Compare commits

Author SHA1 Message Date
TermiNexus 8461a3da47 fix(viperc): typedef 强转补充 stdint/win32base 解析兜底 2026-10-19 19:16:46 +08:00
TermiNexus 08fe5a7b6b fix(viperc): 支持多级模块路径的 typedef 强转(win32.win32base.LPCWSTR) 2026-10-19 19:16:00 +08:00
TermiNexus 37c15c9aa0 修改 README.md 为纯英文 2026-10-19 18:25:15 +08:00
TermiNexus 850f61dc22 refactor(viperc): 清理标记下标推导中的内联类型转换 2026-10-18 13:40:22 +08:00
TermiNexus 82a735c367 refactor(viperc): 完全删除 T1 | T2 类型组合,语法糖改用标记下标 2026-10-18 13:39:38 +08:00
TermiNexus 42b094a575 refactor(viperc): 退役 t/c 模块标记,内建类型改由编译器直接识别 2026-10-18 12:56:57 +08:00
TermiNexus 3242560d02 支持 tls 关键字,全面准备清退 t, c 2026-10-17 23:40:06 +08:00
TermiNexus 6e17716a69 revert(vtable): 恢复按类实际规模动态分配
bf0bbcc 已经把固定上限去掉:收集前按「各父类 VTableMethodCount 之和 + 本类 FunctionDef 数 + 1」一次性分配三张表,越界检查保留为 InternalCompilerError。eff88dd 在并行开发中误把这段覆盖回固定 1024,这里恢复 bf0bbcc 的实现——无任意上限,且符合「能动态就动态」的既定规则。
2026-10-17 19:22:50 +08:00
TermiNexus eff88dd591 fix(vtable): VTBL_METHOD_MAX 32 -> 1024
memhub.MemBuddy 这类工具类的虚方法数远超 32,上一轮加的 fail-fast 直接把它拦下了。三张槽位表由 PermPool 分配(各 8*VTBL_METHOD_MAX),1024 时每类 24 KiB,可接受。
2026-10-17 19:21:44 +08:00
TermiNexus bf0bbcc89b fix(vtable): 虚方法表按类实际规模动态分配,去掉 32 上限
MemBuddy 继承 MemManager 后虚方法约 60+ 个,VTBL_METHOD_MAX=32 会触发 fail-fast(此前静默只收前 32 个,之后的调用点找不到槽位、退化成静态直调)。现在收集前先算容量上界:各父类已收集的 VTableMethodCount 之和 + 本类 FunctionDef 数量 + 1,三个 PermPool 数组按此一次性分配;两处越界检查保留为 InternalCompilerError(理论不可达)。删除 VTBL_METHOD_MAX。注:本次提交同时带上了该文件既有的 t.CPtr → rawptr 迁移与 _generate_default_init 删除等未提交改动。
2026-10-17 19:20:03 +08:00
TermiNexus d46fdf152a refactor(struct): AST vtable 拷贝改为按 uint64 槽显式读写
原写法 *dst_c = vt_ptr_c 把 i8* 赋给结构体类型的左值,TPC 会以「非基本类型禁止指针隐式转值类型」报错。新增 _copy_ast_vtable(src, dst):把两侧 AST 节点都视作一个 uint64 槽(src_vt[0] -> dst_vt[0]),语义不变(只复制首字段 vptr),Constant 与 List 两条深拷贝路径统一调用它。
2026-10-17 19:13:08 +08:00
TermiNexus 3e1e1fc1b3 fix(gen): 消除 ViperC 侧 P0 级静默截断
match:case 数超过 MAX_MATCH_CASES 时 fail-fast(原先 break 会静默少分支,need_case 检查也发现不了)。vtable:初始化串按真实方法表动态分配,_vtable_strcat 溢出改 fatal;VTBL_METHOD_MAX 超限不再 break(原先子类 override 静默失效)。符号名:coro_pool_/__tls_init_done_/__decor_rec_/闭包 fn_ptr 的定义与引用改为按实际长度分配,消除 96 vs 128、64 vs 128 的不对称。Stub:_PyiEmit 溢出改 VLogger.error(半截 .vpi 会被当完整文件解析);VpiReader 参数段/返回注解缓存槽放不下时 fail-fast,不再丢段导致形参注解错位。Includes:文件超过 1MiB 拒绝哈希(SHA1 冲突会让两个模块共用缓存);add_file_entry 失败不再被忽略。f-string:格式串按字面量/转换符实际长度分配,字面量与 format_spec 溢出 fail-fast。
2026-10-17 18:46:18 +08:00
TermiNexus 15952d3852 fix(asm): 内联汇编/LLVMIR 文本缓冲扩容并将所有超限改为 fail-fast
ASM_TEXT_BUF_SIZE 2048→8192(内核 _do_switch 模板已 2245 字节),c.LLVMIR 模板 512→4096。_asm_append_cstr 不再静默截断:装不下时报 TypeError 并给出已用/待写/容量字节数(此前截断会把模板切成半条 mov,最终以 llc invalid operand 的面目出现)。操作数超过 16、clobber 超过 32、符号改写缓冲溢出同样 fail-fast;约束串/参数串/extractvalue 行缓冲改为按实际操作数与类型文本长度动态分配。
2026-10-17 17:41:11 +08:00
TermiNexus 201c881b43 fix(assign): 拦截注解为类值类型但右值取地址的 typo
dirp: devfs_directory = &directories[d](注解漏写 *)此前会静默通过,把栈/表元素地址塞进值语义槽位,导致 vptr/父指针悬垂、对象被就地覆写。在 translate_annassign 中新增 fail-fast:注解解析为 struct(类值类型)而右值是取地址表达式(&x 或 c.Addr(x))时报 TypeError 并提示补 *。新增 _is_addr_of_expr 助手,只认取地址,不含 *p 解引用,避免误伤值拷贝。
2026-10-16 20:19:51 +08:00
TermiNexus 19feb40e8c fix(call): 实参个数与被调方不符时前端 fail-fast - 不再漏到 LLVM verifier(含 EH 参数扣除与可变参数放行) 2026-10-16 19:26:22 +08:00
TermiNexus 2f4031b3d6 chore(typegen): read_all 长度参数去掉多余 ULONG 转换 2026-10-16 16:40:41 +08:00
TermiNexus d8648176e9 feat(asm): op=/clobber= 支持裸枚举名(CLOBBER_MEMORY) - 等价 ASM_DESCR.XXX 2026-10-16 15:26:17 +08:00
TermiNexus e796314a24 fix(gi): 模块级空数组字面量 = [] 改走 zeroinitializer - 修大数组被逐元素展开(元素过多+池耗尽) 2026-10-16 13:02:38 +08:00
TermiNexus 7e9ed22dff revert(main): POOL_SIZE 回退 1GiB - 2GiB 触发 MemBuddy 内部索引溢出(_fl_push 在 __init__ 崩) 2026-10-16 12:13:02 +08:00
TermiNexus c57c931099 fix(cdefine): 支持浮点常量表达式(define X = 160.0 * 4) - 含浮点字面量时按浮点求值登记 double 2026-10-16 11:23:41 +08:00
TermiNexus 832b0a5706 chore(diag): [VAR-DIAG] 增加 alloca 地址打印 - 定位 pos/npos 共享存储(值语义被破坏) 2026-10-16 00:12:06 +08:00
TermiNexus 6196bdfe56 fix(gi): 模块级负浮点字面量按浮点初始化(不再送整型提取器) - 修 double = -100.0 报错 2026-10-16 00:03:21 +08:00
TermiNexus d06e665ebd fix(cdefine): stub 读取对负浮点字面量/浮点强转包裹剥壳后再注册 - 修 X: double = -100.0 走整型提取 fatal 2026-10-15 23:46:25 +08:00
TermiNexus 5460d78ff1 fix(gi): 模块级类全局按值本位存储(仅 __new__ 类用指针) - 修 CAM: V3 被包成 Ptr 致悬垂误报 2026-10-15 23:11:24 +08:00
TermiNexus 9d0a43d5e6 chore(diag): [GVAR-DIAG] 模块级全局类型决策(hasnew/hasvtable/isoop) - 定位 CAM 被包成 Ptr 2026-10-15 23:06:20 +08:00
TermiNexus 15a210bede chore(diag): 变量声明槽类型 [VAR-DIAG] - 定位 class 标注是否被解析成 ptr 2026-10-15 23:00:04 +08:00
TermiNexus 59c032f135 chore(diag): CTOR-DIAG 打印构造类型与目标类型的真实 LLVM 文本 - 定位 CAM 全局值本位 2026-10-15 22:53:23 +08:00
TermiNexus 23236c88ea fix(lifetime): 裸构造判值拷贝不再依赖类型打印比较 - 修 CAM = V3(...) 全局赋值误报 2026-10-15 22:26:17 +08:00
TermiNexus d1dd6de070 fix(lifetime): 目标为值类型即判 LIFE_VALUE(不再要求源非指针) - class 局部变量内部为指针槽,修 stars 误报 2026-10-15 21:59:39 +08:00
TermiNexus cbadc356f9 chore(diag): life 判定加全量诊断 [LIFE-DIAG]/[RT-DIAG] - 定位 return col 误判 2026-10-15 21:54:08 +08:00
TermiNexus 5dc483e256 fix(lifetime): 值类型变量按值拷贝判据改为双非指针(不再依赖类型打印比较),堆/静态登记优先 2026-10-15 21:47:30 +08:00
TermiNexus 6b86f846ca fix(lifetime): 值类型局部变量在同型聚合目标位置按值拷贝(LIFE_VALUE) - 修 return col 被误判 stack address escapes 2026-10-15 21:40:30 +08:00
TermiNexus 217f5f021d fix(viperc): 解引用赋值补位拷贝(*a = *b 按位重解释,不做数值转换)
现场(math.vp sin/cos):
    u: uint64 = 0; xv: double = x
    *&u = *&xv        # 位拷贝,应为 movq %xmm0, %rax
    ef: uint64 = (u >> 52) & 0x7FF
生成:404084 cvttsd2si %xmm0, %rax ← 把 double 数值截断成 int64
代价:u 成了数值截断结果 ⇒ ef/neg 全错;x 为负时(-1.5 → u = -1)ef 命中 0x7FF
⇒ 直接 return x - x = NaN ⇒ sin/cos 负参全废。

VPC 的 `*p = v` 目标路径无条件 coerce_to_type(rhs, pointee);TPC 侧早有该判据
(HandlesAssign.py:592 调 HandlesSpecialCall.EmitRawCopyLlvm,注释明确"位拷贝,绝不转换"),
VPC 漏了 ⇒ 本次按同口径补齐。

- RHS 为 UnaryOp(Deref)(`*p`)且两侧是**等宽标量**时:按**源自身类型** load(类型与槽的
  存储类型一致),再 bitcast 成目的类型存回 —— 不做任何数值转换;
- 不用"按目的类型重解释的 load"(`load i64, ptr %xv`):文本层 T2「store→load 转发」按**名字**
  替换,会把 `store double %x, ptr %xv` 的 %x 顶进这条 i64 行,报
  `use of value '%x' expects different type than prior uses: 'i64' vs 'f64'`;
- 源操作数限定非调用形状,避免与前面 rhs_val 的求值重复;源不是指针 ⇒ fail-fast;
- 两侧位宽不等或含聚合 ⇒ 不做位拷贝,交回 coerce 路径(等价改动前行为,聚合位拷贝未支持)。
2026-10-15 20:16:50 +08:00
TermiNexus 5ea0ce094e fix(viperc): 恢复一元负号字面量按目标类型直出 f32(位宽根因已修,路径不再有风险)
背景:负号字面量(float(-10.0) / x: float = -3.5)此前回退过——当时 MLIR 渲染报
「f32 类型配 16 位文本」,原因是 mlirlite.ConstFloat 用 snprintf 打印位型时,
高位置位的 32 位值会被按符号扩展后的 64 位打印。该根因已在 includes/mlirlite 侧修死
(提交 8dbad70:十六进制位数只由 MlirTypeText(ty) 定,与 MLIR 渲染器同源,构造上一致),
故此路径恢复为「正确路径」:f32 目标下直接物化 f32 常量,去掉 f64 常量 + fptrunc 的绕行。

- ExprGenOps.translate_unaryop 增加 target_ty 形参:目标为 32 位浮点时折叠直接出 f32 常量;
- ExprGenMixin 把 target_ty 透传给 translate_unaryop。
2026-10-15 19:43:15 +08:00
TermiNexus 3e52baa209 fix(viperc): 回退一元负号字面量的 f32 直出,保留正号字面量与 CDefine 的 f32 直出
- 现场二分定位:float(-10.0) / -float(10.0) 走「按目标类型直出 f32 常量」后,
  MLIR 渲染阶段报 float 位模式位宽不匹配(VKernel 整个编不过);同一份 gfx.vp
  只把负号字面量换成正号就渲染通过,故该路径回退为改动前行为(按操作数自身类型
  出常量,f64 常量 + 使用处 fptrunc)。
- 保留两条已验证有效的修复:
  1) 正号浮点字面量在 f32 目标下直接物化 f32 常量(translate_constant);
  2) CDefine 记宽度种类(1=f64 / 2=f32),define PI: float = ... 的使用处直出 f32
     常量(含 -PI —— 折叠按操作数类型出常量,操作数已是 f32,故仍是 f32)。
- define 常量补 ': int' 注解:无注解 define 按值的最小可存宽度物化(1/2 -> i8),
  直接当实参传给 list[int].append(x: int) 会落成 i8 != i32 被 mlir-opt 拦下
  (自举编译 TypeGenCDef 时必现)。
2026-10-15 19:25:49 +08:00
TermiNexus 508428bbc9 fix(viperc): 浮点常量按目标类型/声明宽度直接出 f32,不再先出 double
- 浮点字面量在 f32 目标(float(1.5) / x: float = 1.5 / f32 形参)下直接物化 f32 常量,
  负号折叠(-c)同口径;去掉旧的「f64 常量 + fptrunc」
- CDefine 浮点条目记录宽度种类(1=f64 / 2=f32):define PI: float = ... 的使用处
  直接出 f32 常量,与 f32 字面量混算不再被提升成 double(现场 33 处 cvtss2sd)
- 本模块注册 / pyi·vpi 跨模块重解析 / xmod 缓存三处都按注解裁决宽度,保持跨模块一致
2026-10-15 18:35:48 +08:00
TermiNexus 3a687db0bb feat(viperc): 静态常量算式补上除法/整除/取模折叠(与运行期 sdiv/srem 同口径)
此前纯常量折叠只覆盖 + - * | & ^ << >>,Div/FloorDiv/Mod 一律落到运行期 sdiv/udiv/srem/urem。现按同一维护口径补上:以精确值折叠,再由既有逻辑按结果值自动提升位宽(不无故抬宽,避免把 A/1 这类算式整体抬成 i64)。语义与运行期一致:Div/FloorDiv → sdiv(向零截断)、Mod → srem(余数符号随被除数)——宿主侧 // 与 % 正是这两个语义,无须额外修正;无符号位型常量按规范恒为非负,与运行期 udiv/urem 取值一致。除零不交给宿主(会打挂编译器):先关掉折叠再 fail-fast,诊断文本与 AssignGenMixin 的常量求值器保持一致。
2026-10-14 22:28:43 +08:00
TermiNexus 38a277a914 feat(viperc): 纯常量折叠补上 / // %,与 + - * 同口径(精确值 + 按值自动提升位宽)
口径与 AssignGenMixin 的常量求值器逐字对齐:①除零先关折叠再 fail-fast(同一条诊断文案,避免交给宿主 Python 打挂编译器);②结果按精确值自动提升位宽,不无故抬宽;③负数语义待定 ⇒ 本轮只折叠两侧都非负的情形(此时 sdiv 与 udiv 结果相同、无歧义),含负操作数的算式不折叠,照旧走运行期 sdiv/udiv/srem/urem。
2026-10-14 22:27:55 +08:00
TermiNexus dc4468593c fix(viperc): CDefine 常量按位宽处理时不再把可容正值翻转成负数,修常量算式折错
现场:Gargantua _start 的 string.memset(fb, 0, GW * GH * 4)(GW=160, GH=90)应折叠为 57600,实际生成 movl \(= -34560)作为 memset 长度 → 从 0x457ca0 写 4GB → 越界写撞到映射区末尾 0x467000,用户态 #PF err=0x7(cr2 与反汇编逐字对上)。根因:define GW = 160 的位宽按值推断为 8(无符号可容),而 _cdefine_bits_value/_normalize_cdefine_bits 无条件 '(value<<56)>>56' 重解释为有符号 → 160 变 -96(=-96*90*4=-34560)。现只在两种解释都装不下时才截断;位型不变(const_int 本就按位宽截断),仅不再翻转可容正值的语义。
2026-10-14 22:20:52 +08:00
TermiNexus a143c2de0c fix(stubgen): 装饰器循环变量改名 ddi,避免与默认值处的注解声明 di 冲突
同一个函数里 di 先被 for 循环绑定为 int(i32),后面又用注解声明为 sizet(i64) 且右值是运行期 i64 值:存储类型 i32 收不下,编译器按 fail-fast 报'带注解重声明冲突'(此前 TPC 是静默丢掉 store,生成的 viperc.exe 在 list[AST*].get 越界读)。装饰器循环改用 ddi,di 保持为首次声明。
2026-10-14 21:13:03 +08:00
TermiNexus 7da9d7cb0d fix(viperc): 位宽探测循环改为固定 64 次上界,修主编译器死循环
- 现象:Phase1 编译到 viperlib.vp 时卡死,栈顶停在 ExprGenOps.vp:1362(我新加的位宽探测 while)

- 原因:`while t != 0 { t = t >> 1 }`(t 为 int64)在生成代码里不收敛(疑似 i64 移位/比较被按窄宽度处理)→ 死循环;改为 `while i < 64 and t > 0` 的固定上界写法,两处(ExprGenOps 运行期常量折叠 / AssignGenMixin 静态求值)一起改

- 遗留待查:该 while 为何不收敛(i64 >> / != 0 的代码生成),属同类“最小宽度”问题面
2026-10-14 19:54:29 +08:00
TermiNexus d09b79995d fix(viperc): 运行期纯常量整型算式按精确值折叠并按值定宽,禁止最小宽度回绕
- 现场:`1024 * 1024 * 4` 在表达式侧按最小宽度(i16)运算,1024×1024 先回绕成 0 ⇒ 常量变 0(WINBUF_MAX_BYTES=0 ⇒ 窗口缓冲只剩几字节 ⇒ 内核 memcpy 240KB 越界读 → 三重故障)

- 在 translate_binop 取到操作数类型后加入纯常量折叠:+ - * | & ^ << >> 两侧都是编译期整型常量时按精确值求结果,并按“能精确容纳该值的最小合法宽度(下限取操作数较宽者)”定宽、向上取 8/16/32/64;只有装不下才加宽(与 61f23d9 的静态求值器同口径)

- 与 61f23d9 合起来:静态 define 求值与运行期常量算式都不再按最小宽度回绕
2026-10-14 19:39:11 +08:00
TermiNexus 61f23d99a1 fix(viperc): 静态常量求值对 + - * << 按结果值加宽,杜绝 define 算式静默回绕
- 现象:Gargantua 的 define V3_POOL_SIZE=8192 / V3_POOL_BYTES=V3_POOL_SIZE*24+512 在运行时变成 512(正好是表达式里的 +512),等价于 8192*24=196608 被按 16 位回绕成 0 → 池只剩 512B → 每像素 trace 立刻 OOM 刷屏

- 根因:extract_cdefine_int_value_bits 的 BinOp 位宽规则只“取较宽”,而 + - * << 会增长结果位宽 → define 物化时按 16 位截断

- 现在:操作数的声明宽度仍保持最小(不提升);基准取较宽者;若精确结果装不进基准宽度,则按结果值加宽(向上取 8/16/32/64)—— 静态计算不再回绕;基准未知(纯字面量算式)时保持“未知”,仍由物化端按值推断

- 顺带:未知 BinOp 算子恢复 fail-fast(重构后原落空分支若留着会静默返回 0)
2026-10-14 19:18:52 +08:00
TermiNexus 813b59e25f fix(viperc): 修正 (expr).method() 接收者误入 get_attribute_ptr 导致的 strcmp(_,0x1) 崩溃
- 表达式接收者分支(L1236 设计上可收 BinOp/Subscript/Call)在接收者求值非指针时会调 get_attribute_ptr;该 API 只接受 Attribute,内部直接强转成 ast.Attribute 并读 .attr,对别的节点会命中偏移 9 的字段:BinOp 的 op(OpKind.Sub=1)、Name 的 ctx(ASTCtx.Store=1)等,于是把整数 1 当字符串传给 strcmp → ASan access-violation on 0x1

- 现在仅当接收者确为 ast.Attribute 时才走“取字段地址”,其余节点保持已翻译的接收者值(与旧实现的回退结果一致)

- 现场:Test/OopInheritTest/App/main.vp 的 (a3 * 2.0 - a4).cross(a5),接收者 BinOp 的 op=Sub=1
2026-10-14 17:57:01 +08:00
TermiNexus ee3337f3c4 revert(viperc): 回退闭包变量取址的放行,仅保留“实参级报错定位”
- 回退 a060e1d 中 ExprGenOps 的闭包取址改动与 TypeGenErr 的临时打印移除:&_on_submit 仍按级数不符报 CastError(source level 2 -> target level 1),语义不变、不放行

- 保留 CallGenCall._call_method_on_ptr 的实参级 AST 节点透传:方法/构造实参转换失败时报错定位到具体实参表达式,并显示完整两帧(函数定义帧 + 错误点)
2026-10-14 17:40:53 +08:00
TermiNexus a060e1d76e fix(viperc): 闭包变量取址返回闭包指针,并让方法/构造实参转换报错定位到具体实参
- ExprGenOps 取址:嵌套 def 提升出的闭包变量在父函数里是 i8** 槽,&f 原来返回槽地址(二级),传给 callback: t.CPtr 会因级数不符报 CastError(source level 2 -> target level 1);现按闭包名表判定,返回其值 i8*(一级),与“函数名即地址(同 C)”一致

- CallGenCall._call_method_on_ptr:实参逐个转换原来统一传 node=None,失败时只能退回函数定义行(只剩一帧);现按 arg_nodes 带上每个实参自身的 AST 节点(slot 0 是 self),报错可定位到具体实参表达式并显示完整两帧

- TypeGenErr:移除定位用的 [TEMP-DIAG] 临时打印
2026-10-14 17:38:50 +08:00
TermiNexus 3c6041c97c fix(viperc/diag): 把“当前实参位置”的记录点下沉到 translate_call_arg(覆盖所有调用路径)
TEMP-DIAG 实测:node=0 curarg=0 trace=1 top=11 —— 节点是合成的(无行号),但“当前实参
位置”压根没被记录,说明该构造器调用没走 CallGenLib 的参数循环,兜底只能退回栈帧(函数定义行)。

改为在**所有调用实参的公共入口** CallGenBase.translate_call_arg 顶部记录(它持有 arg_node),
并移除 CallGenLib 里的重复挂点。这样任何调用路径(含构造器)都能把报错定位到具体实参表达式。
2026-10-14 17:13:38 +08:00
TermiNexus ca81c1185c diag(viperc): 放宽报错位置兜底判据 + 临时打印定位决策输入(定位后删除)
兜底判据改为:只要实参位置有效,且节点行号“不深于”栈最内层帧行号(或栈为空),就用实参
位置 —— 覆盖 FunctionDef 节点被传下来的情形(其行号恰好等于函数定义行、并非真实表达式)。
另加一行 [TEMP-DIAG](node/curarg/trace/top/shown),用于一次看清兜底为何未触发;
定位完成后立即删除该行。
2026-10-14 17:08:59 +08:00
TermiNexus 40ea5d6513 fix(viperc/diag): 节点行号等于函数定义行时也用实参位置兜底(定位到具体表达式)
上一版只在 lineno==0 时用 CurArg 兜底;实测该报错的节点自带行号且恰好等于当前函数定义行
(节点其实是 FunctionDef 本身,构造器内联路径把它传了下来),于是兜底没触发,错误位置与
栈帧都在定义行、被去重合并,看起来只剩一帧。

现把“节点位置不可信”判据放宽为:lineno==0,**或** lineno 等于最内层栈帧的行号(即函数
定义行)→ 两条都优先用 CurArg,报错精确指向调用处的实参表达式(如 `&_on_submit`)。
2026-10-14 17:02:16 +08:00
TermiNexus 4f45b391ea fix(viperc/diag): 保留函数定义帧,不覆盖栈帧位置(补齐“定义帧 + 出错语句帧”两帧)
上一版 B 把最内层栈帧的位置改写成“当前语句”,结果它与错误帧落在同一 file:line,
被去重合并 —— 输出只剩一帧,`_start` 那帧(函数定义)反而没了。

改为:栈帧只记录**被调函数的定义位置**(不随语句推进覆盖);“当前语句/具体表达式”
统一由错误帧给出(CurArg 兜底,见 TypeGenErr)。这样能得到完整两帧:
  File "./Apps/HelloWorld/main.vp", line 11   export def _start() -> int:   ← 定义帧
  File "./Apps/HelloWorld/main.vp", line 36   ... &_on_submit)             ← 出错语句帧
同时删除已无调用的 TypeGenBase.update_top_trace_position。
2026-10-14 16:51:51 +08:00
TermiNexus d6b87da063 fix(viperc/attr): 不再要求 import c 才认 @__attribute__(否则函数属性被静默丢弃)
ast/parser.vp 的 _DesugarMagicAttribute 会把用户写的 `@__attribute__(...)` 脱糖成
`@c.Attribute(...)`,此处看到的 `c.` 前缀是编译器自己产生的(用户并未写 `@c.Attribute`)。
原先这里要求 is_module_imported(...,"c"),用户没写 `import c` 时整条装饰器被静默丢弃,
stackrealign / always_inline 等函数属性全部失效。现去掉该守卫,与 extract_func_section
(本就没有此守卫)保持一致。
2026-10-14 16:42:16 +08:00
TermiNexus 0f2675e794 fix(viperc/codegen): Win64 变参提升按实参符号性选择 zext/sext,避免无符号值高位被填脏
变参调用把 <64 位整数提升到 i64 时原先一律 sext:uint8 0x8E 会被填成
0xFFFFFFFFFFFFFF8E,va_arg(i64) 读到脏高位(即 Debugger/VKERNELERR.md 记录的 sext 家族)。
现按实参符号性选择:无符号走 zext、有符号仍走 sext,与
CallGenVa._check_and_promote_variadic_call 同口径(复用 ExprGenOps.should_zext_widen)。
2026-10-14 16:41:58 +08:00
TermiNexus 0678cc8c48 feat(viperc/diag): 报错定位到具体表达式 + 栈帧跟踪当前语句(方案 A+B)
A:报错节点是合成节点(lineno==0,如构造器内联出的参数赋值)时,优先用“正在翻译的实参
   位置”兜底 → 报错精确指向调用处的表达式(如 `&_on_submit`),不再退回函数定义行。
   - TypeGenBase 新增 CurArgFile/CurArgLine/CurArgCol 与 set_cur_arg()
   - CallGenLib 翻译实参前调用 set_cur_arg(当前文件, arg_node 位置)
   - TypeGenErr 兜底顺序改为:节点自身行号 → CurArg → 栈最内层帧
   - 语句边界清空 CurArg,避免兜底串味到上一条语句的实参

B:栈帧跟踪“当前语句”位置(Python traceback 口径)
   - TypeGenBase.update_top_trace_position():更新最内层帧的 lineno/col
   - translate_stmt 每条语句推进时调用 → 报错时栈帧给出的是调用点所在语句,而非函数定义行
   配合 A 即可得到“调用点 + 具体表达式”两帧。
2026-10-14 16:40:35 +08:00
TermiNexus 70c271d2a4 fix(viperc): &func 对函数指针变量应取其值(Ptr(Func) 一级),不再多一层地址
现场:Apps/HelloWorld 里 vpui.Button(..., &_on_submit) 报
  CastError: pointer level differs (source level 2 -> target level 1);
  source: level 2, pointee !llvm.ptr -> target: level 1, pointee i8

根因:嵌套函数名在符号表里是 Ptr(Func) 变量。ExprGenOps 的取址分支有一条
“变量本身就是那个对象”的规则(结构体指针变量要 load 一次、而非取变量地址),
但该 match 只覆盖了 Ptr(Ptr(Struct)),未覆盖 Ptr(Ptr(Func)) —— 于是 `&f` 落到末尾的
`return v_ao`,给出的是变量地址 Ptr(Ptr(Func))(二级),而不是函数地址 Ptr(Func)(一级)。

修改:在该 match 中补上 Ptr(Ptr(Func)) 分支,load 一次后返回 Ptr(Func),
与结构体指针变量同口径。
2026-10-14 16:37:37 +08:00
TermiNexus 9d02a3ac0b improve(viperc/diag): 指针级数不符报错补印“级数 + pointee 类型”
LLVM 不透明指针下 source/target 两边的类型文本都是 !llvm.ptr,看不出差异,现场只剩
一个 “2 -> 1”。现改为同时打印:
  source: level N, pointee <T>  ->  target: level M, pointee <U>
这样一眼能定位是哪个表达式/哪个形参触发的级数不符。
2026-10-14 16:01:09 +08:00
TermiNexus 6b180c8f59 fix(viperc/life): 进程入口豁免补上 _start(ViperOS 应用入口),消除栈逃逸误报
现象:Apps/HelloWorld/main.vp 报 LifetimeError(`master` frame-local,传给构造器参数会
悬垂)。而 `master = &root` 中 root 是 _start 的帧内 Tk,_start 是进程入口
(@__attribute__(section(".text.startup")),结尾 mainloop/process.exit),本帧返回即进程
结束 —— 与 main 同口径,确无逃逸。

_CurFuncIsEntryMain 此前只认 "main",现把 `_start` 一并视为进程入口。
2026-10-14 15:53:28 +08:00
TermiNexus a23c1bda3e fix(viperc/diag): 调用栈帧与错误点同位置时不再重复打印
现象:`_start` 定义行在 Traceback 里被打印两遍 —— 栈内最内层帧与随后的“错误点帧”
落在同一 file:line(错误的 AST 节点 lineno 为 0 时,错误点位置会回退到栈最内层帧,
于是与栈帧完全重合)。

Diag.emit 增加两条去重规则(DIAG_DIR_UP / DIAG_DIR_DOWN 两个方向都生效):
① 连续两个位置完全相同的栈帧只印一次;
② 与紧随其后的“错误点帧”(emit_current 打印的那一帧)位置相同的栈帧不再重复印。
2026-10-14 15:53:05 +08:00
TermiNexus dc24970af2 fix(gen): 修 __before_init__ 字段默认值写入的参数次序(build_store 缺 builder/file 顺序颠倒),修编译期参数缺失报错 2026-10-13 21:46:20 +08:00
TermiNexus ea54d5685d feat(gen): virtual(HasVTable)类豁免「无 def => struct」规则,必须合成 __before_init__/__init__ 且必发 vtable;非泛型构造路径按符号存在性判定调用 2026-10-13 21:29:24 +08:00
TermiNexus 99a021ad67 fix(gen): 类合成默认构造——有方法但无显式 __init__ 的类合成空 __init__(补 undefined 符号);完全无 def 的类按 struct 处理,不合成 __init__/__before_init__ 2026-10-13 20:59:41 +08:00
TermiNexus 12da1cbfc1 fix(gen): define 复用前置声明时按源码形参名对齐签名参数名,修签名与函数体两套参数名导致 MLIR「region entry argument already in use」 2026-10-13 20:33:06 +08:00
TermiNexus bc1eeb6e38 feat(gen): REPL 会话变量的复合赋值下传槽类型作为 target_ty(复用 repl_slot_ty,缺失即 fail-fast),与二元/普通变量路径对齐 2026-10-13 20:04:34 +08:00
TermiNexus c1e5a11962 feat(gen): 复合赋值统一二元运算口径(常量无损适配另一侧、不做整数提升、RHS 按左值类型翻译),修 u16 -= 1 误报窄化 2026-10-13 20:01:16 +08:00
TermiNexus d2138678ef feat(gen): 同型聚合值 -> 同型聚合指针 允许隐式值拷贝(调用者帧溢出临时槽后取址),修裸类形参接收聚合值实参报错 2026-10-13 18:33:49 +08:00
TermiNexus 1c3cdf3493 fix(stub): 消费模块补齐按值返回聚合的具名结构体完整体(_inject_method_declare 补 _ensure_full_value_types、_inject_class_struct_declare 补登完整定义),修 MLIR opaque 报错 2026-10-13 17:48:57 +08:00
TermiNexus 377c0db5d3 fix(gen): life_of_expr 的构造分支按目标类型判定——目标为同型聚合值时判 LIFE_VALUE(值拷贝),不再误判帧逃逸 2026-10-13 17:48:35 +08:00
TermiNexus 6cb1f525d9 feat(gen): 常量操作数隐式适配另一侧位宽(仅另一侧非常量时),修 u16 变量 + 字面量被抬到 i32 导致误报窄化 2026-10-13 17:01:53 +08:00
TermiNexus a01e9280cc fix(stub,phase1): 因取消整数提升,模块属性 CDefine 的窄乘法显式加宽为 sizet(修缓冲区尺寸回绕成 0/负值) 2026-10-13 16:34:39 +08:00
TermiNexus 14d8c05a2a feat(gen): 二元运算取消 C 式整数提升(任何位宽不再默认提升到 i32),改为按 max(两侧)/目标位宽运算 2026-10-13 16:20:02 +08:00
TermiNexus 97c0a1c6fd fix(gen): 取消「宽整型 -> bool(i1)」的隐式真值归一化,改为 fail-fast(仅保留编译期常量豁免) 2026-10-13 16:07:43 +08:00
TermiNexus d7c5af9b80 fix(gen): callable[[]] 省略返回类型按 void 注册(作为值使用时报错,不再静默按 int) 2026-10-13 13:32:47 +08:00
TermiNexus cb2dc4cb3a feat(gen): 字节序 bswap 改为按目标端序判断(BigEndian/LittleEndian 都相对目标),原语改名 apply_byte_order_bswap 2026-10-13 13:29:03 +08:00
TermiNexus 444e87dd7d fix(gen): callable[[]](省略返回类型)按默认 int(i32) 注册函数指针签名,修间接调用签名缺失 fail-fast 2026-10-13 13:26:51 +08:00
TermiNexus d3997e0746 fix(gen): 函数指针变量别名(f2: rawptr = f)继承已注册的返回类型签名,修 rawptr 间接调用的签名缺失 fail-fast 2026-10-13 13:26:11 +08:00
TermiNexus 32599a08c1 fix(stub): .vpi 读取侧复刻字段标记判定(volatile/pointee-volatile/const/字节序),跨模块不再丢字段标记 2026-10-13 12:30:23 +08:00
TermiNexus 37c4b0da3b feat(gen): 字节序 bswap 代码生成——字段/局部变量/数组元素读写处成对 llvm.bswap(仅 BigEndian 且 16/32/64 位整数) 2026-10-13 12:30:05 +08:00
TermiNexus 5d15d01e21 feat(gen): 支持 t.BigEndian / t.LittleEndian 字节序描述符(注解识别 X|marker 并把字节序登记到 FieldEntry.ByteOrder,暂不生成 bswap) 2026-10-13 12:17:02 +08:00
TermiNexus 3ca2165fd9 feat(gen): 指向同型聚合的指针→同型聚合值允许隐式值拷贝(裸类形参 T* ABI 的直接赋值仍按值拷贝) 2026-10-13 10:30:15 +08:00
TermiNexus b859f8a136 fix(gen): CDefine 常量(含 mod.N 属性形态)拓宽方向单独判定——负值 sext、非负 zext,修 zlib 窗口尺寸被 sext 成负数与 Z_DEFAULT_COMPRESSION->255 2026-10-12 21:48:05 +08:00
TermiNexus 22475e0fda fix(gen): 逃逸检查把模块级(根 scope)变量判为 LIFE_STATIC,修模块级数组 return 误判帧逃逸(pyzlib_error_msg) 2026-10-12 21:23:23 +08:00
TermiNexus c0ff5c9d96 fix(gen): 复合赋值取消「右值有符号 vs 左值无符号」拦截,与二元 x = x op y 口径一致(修 sizet += int 误报) 2026-10-12 21:16:50 +08:00
TermiNexus c9562a6e04 feat(gen): 一元 +/-/~ 按符号性提升——u 类不提升(保持原位宽),i 类提升到 i32 防负数陷阱 2026-10-12 21:10:06 +08:00
TermiNexus d206abf3e1 chore: 全仓同步 w32→win32 模块改名与 range(int(...)) 显式收窄适配 2026-10-12 20:59:11 +08:00
TermiNexus ea2af739dd feat(gen): range 边界与循环变量统一提升到至少 i32(取消 u8/u16 循环变量档),u64 边界保持 i64 2026-10-12 20:57:12 +08:00
TermiNexus 00bf7ebf38 fix(gen): 非负 define 常量按最小位宽的无符号位模式参与宽化/比较(修 range(50000) 迭代 0 次) 2026-10-12 20:52:52 +08:00
TermiNexus e3743b185f fix(gen): zip/enumerate 的 range 源工作位宽随参数(仿 for-range),修 range(3) 被 i64 索引误判窄化 2026-10-12 20:24:50 +08:00
TermiNexus 54b239747e chore(gen): 清理「按值物化」残留注释(裸类形参即 T*,非值语义槽) 2026-10-12 20:03:10 +08:00
TermiNexus ae12ae8581 Revert "feat(gen): 裸类形参按值物化(值语义),调用方 ABI 不变"
This reverts commit 87914b0a51.
2026-10-12 19:56:02 +08:00
TermiNexus 001f8afc37 fix(gen): 常量适配限定为位运算,算术一律按 C 语义拓宽(修 i8 乘法回绕致 memhub 不变式误报) 2026-10-12 19:40:13 +08:00
TermiNexus e7b05c6a3d fix(gen): 三元表达式两支统一按常量无损适配,修 i8 表元素 vs 字符字面量被抬成 i32 的窄化误报 2026-10-12 19:27:58 +08:00
TermiNexus 3d638927d5 fix(gen): 移位的工作位宽不计入计数宽度,修 ushort 表达式被抬成 32 位后赋回 ushort 误报窄化 2026-10-12 19:22:36 +08:00
TermiNexus 4a66aa7fd2 feat(gen): 二元运算采用目标感知工作位宽(仿 C:算到目标位宽;移位只提升左操作数),隐式窄化仍禁止 2026-10-12 18:53:50 +08:00
TermiNexus 9d02cc4684 fix(gen): 移位按 C 语义把左操作数提升到 int,且移位数不参与常量适配(修 uchar<<8 误报) 2026-10-12 18:42:54 +08:00
TermiNexus 65c72fd55c fix(gen): 二元运算按常量无损适配另一侧位宽,修 crc(uint)^0xFFFFFFFF 被抬成 64 位 2026-10-12 18:32:55 +08:00
TermiNexus 8453698f66 chore(gen): ExprGenBase 残留清理(range(int()) 与 TPV->VPC 注释) 2026-10-12 18:25:56 +08:00
TermiNexus 74fafb3b56 fix(gen): 整型字面量宽度按值推导(新增 u32 档)+ 显式宽度严格校验 2026-10-12 18:25:38 +08:00
TermiNexus f044d94e90 fix(gen): -x/~x 对编译期常量折叠,修负字面量被隐式窄化误拦 2026-10-12 18:25:24 +08:00
TermiNexus 87914b0a51 feat(gen): 裸类形参按值物化(值语义),调用方 ABI 不变 2026-10-12 18:25:04 +08:00
TermiNexus d7a5f7e68b fix(gen): 值语义聚合槽豁免改为类型级判定,去掉易漏判的槽身份比较 2026-10-12 16:56:03 +08:00
TermiNexus 7c11c589f7 fix(gen): 值语义聚合槽允许隐式值拷贝,修 self.field = param 的 Foo* -> Foo 误报 2026-10-12 16:42:52 +08:00
TermiNexus 3384941cd1 chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换) 2026-10-12 15:06:28 +08:00
TermiNexus d0c7b9c77c chore: 改名同步 TransPyV -> ViperC(.gitignore/文档/脚本) 2026-10-12 15:02:13 +08:00
TermiNexus 9b93586eec docs: README 随改名同步(ViperC/viperc.exe/__vpcbuild__)并纠正过时内容
- w32.* -> win32.*(中文前提条件/对比表、英文 Prerequisites/对比表 共 4 处)
- 规模数字按实测刷新:App 82 文件 63,783 行;Gen 54 文件 47,297 行;
  Phase 6/5,339;Stub 9/6,207;Projectrans 3/712
- 自测套件位置纠正:不在本仓库,位于工作区根 Test\(独立仓库,97 工程 129 个 .vp),
  由 Test\SequentialRegressionTest.py 驱动;删除已不存在的 Test/{App,NegativeTest,Sha1Test} 描述
- 工作区布局补 Test\ 与 exe\,修正 ViperC\ 对齐
2026-10-12 14:48:47 +08:00
TermiNexus 023a22af08 chore: 工程改名 TransPyV -> ViperC(输出 viperc.exe,构建目录 __vpcbuild__) 2026-10-12 14:28:52 +08:00
TermiNexus 4e46a25ab1 fix(gen): define 位宽/整数提升/移位/AugAssign 规则收敛,修 va_list 与 traceback 卡死
- define 位宽:cdefine_decl_bits 单一裁决(注解 → 右侧强转 → 值最小可存宽度),定义方与 VpiReader 两处跨模块注册共用;lookup_cdefine_bits 取最后一条非零记录;存储即截断;使用侧改按声明位宽;通用求值器加编译期除零/移位越界 fail-fast
- 消除对无符号强转加宽方向的依赖:define_min_width 改 (value>>32)==0,截断改左移+算术右移,修 TPC/TPV 判定不一致导致 0x80000000 误判 64 位
- 整数提升:算术/移位、比较、一元 +/-/~ 提升到 i32;位运算保持同宽不提升
- AugAssign:y 有效宽度须 <= x、禁 y 显式有符号而 x 无符号;移位计数静态范围检查
- 函数入口复位 va_list 状态与 willFree 失效槽表(FuncGenDef/FuncGenMixin/VarGenMixin 等)
- BuildPipeline:traceback 逐行扫描改 span 判定,去掉对全文 strlen 的 O(n^2)
2026-10-12 12:37:48 +08:00
TermiNexus be4db7b5d1 fix(narrow): StmtGenMixin exc_code 显式 int(cst_r.int_val)
ast.Constant.int_val 为 int64,exc_code 为 int(并传入 const_int32),改用显式 int(...)
2026-10-11 10:07:37 +08:00
TermiNexus 985983391b fix(narrow): ClassGenBase 方法元数据指针声明 int* 而非 t.CPtr
flags_ptr/flags_ptr2 被显式 (int*)(...) 赋值但声明为 t.CPtr,下标元素被判为 i8,与 i*4 的 32 位步长及位标志语义不符(且 >127 的值会读出符号错误);声明改为 int*
2026-10-11 10:04:19 +08:00
TermiNexus 3b9deb08d8 fix(narrow): ClassGenEnum RenumClassVariantCounts 元素读取改用 int(...)
n_first_v(1029) 与 vcount(1010/1055) 承接 sizet* 元素,原先 64->32 隐式窄化;改用显式 int(...) 保持原截断语义
2026-10-11 10:00:05 +08:00
TermiNexus 7468b007f7 fix(narrow): ClassGenEnum pos_max 加宽为 sizet
pos_max 承接 variant_field_sizes 的 sizet 元素,仅与 sizet 比较并流入 sizet();原 int 触发 64->32 隐式窄化
2026-10-11 09:54:18 +08:00
TermiNexus 3c0d3d13b5 fix(narrow): 清理注解式整数窄化(Converter/TypeGenCType/CallGenLeafKit/ClassGenEnum)
CDefine 查询返回 int64 处按下游用法定型:回传给 int 的用显式 int(...),流入 int64 目标的加宽为 int64;sizet 数组元素赋给 int 的用 int(...)
2026-10-11 09:51:08 +08:00
TermiNexus 966fc3b8f5 fix(narrow): BuildPipeline.ensure_dir 消除隐式窄化(int(path_len) / char(saved))
buf[i] 元素为 i8,saved 由 int 改为 char 并用 char(ch) 显式收窄;plen 由 sizet 显式 int(path_len) 收窄
2026-10-11 09:42:39 +08:00
TermiNexus 77f4a25055 fix(narrow): CallGenFd 的 fd_can 改用 int(len(...)) 消除 64->32 隐式窄化 2026-10-11 09:38:24 +08:00
TermiNexus 82ad5da7c7 fix(narrow): len() 结果赋给 int 的形参/默认值计数改用显式 int(...)
FuncGenDef 的 fd_param_count/fd_default_count/tfd_param_count/tfd_default_count 与 ClassGenVTable 的 md_param_count/md_default_count 原本直接接收 len()(sizet),触发隐式窄化;改 int(len(...)) 保持原有截断语义
2026-10-11 09:34:43 +08:00
TermiNexus d023e9932d fix(narrow): StructGenMixin 守卫候选与归纳区间常量改用 int64,消除 64->32 隐式窄化
kv/lo/hi/k 原本声明为 int 承接 int64(Scratch[...]),触发新禁止的隐式窄化;_AddGuardCand/_PackEscapeClear 的 kv 形参同步拓宽为 int64(打包范围检查需看到完整 64 位值),打包结果处显式 int() 收窄
2026-10-10 22:20:47 +08:00
TermiNexus 41772b4a0c refactor(narrow): 撤销 File.write 实参多余的 ULONG(...) 显式窄化
File.write 形参已改为 sizet,调用点不再需要显式 ULONG 包裹(覆盖 WriteCallSidecar/_phase2_write_cmd/write_ir_to_file/WriteStackMap/TranslateFileGetTrans)
2026-10-10 22:15:49 +08:00
TermiNexus 58951a29a8 fix(narrow): File.write 长度实参加显式 ULONG(...)(避免 64→32 隐式窄化)
fileio.File.write(buf, count: ULONG) 的 count 是 32 位,而实参是 sizet/int64(strlen、pos、ir_len、total 等);规则收紧后为隐式窄化,改为显式 ULONG(...)。本文件其它位置无隐式窄化。
2026-10-10 22:10:01 +08:00
TermiNexus 5bb6538703 feat(types): 禁止隐式窄化(整数 高→低);_tb_read_whole 容量改 sizet
规则收紧(2026-10-10):整数位宽 高→低 隐式窄化一律 fail-fast(CastError),仅两类豁免:① 目标 i1 的真值归一化;② 源是编译期常量且值落进目标位宽。_scalar_convert_reason 的 int↔int 同步改为'目标位宽≥源位宽才 PC_OK'。BuildPipeline._tb_read_whole 去掉 ULONG(sz)+ULONG(2) 的窄化,改 sizet(sz)+2。
2026-10-10 21:57:18 +08:00
TermiNexus 9db3748f68 feat(gen): main 内栈逃逸检查整体豁免(收窄为结构体指针不再采用)
按用户决定:入口 main 的帧生命期=进程生命期,main 内任何栈地址写进长命存储都不报,不再区分结构体/标量。移除上一版为'仅豁免结构体指针'新增的 _TypeIsPtrToStruct/_ExprIsStructPtr,fail_if_life_escapes 只保留 _CurFuncIsEntryMain 短路。嵌套函数不豁免。
2026-10-10 14:46:45 +08:00
TermiNexus 87e0bce5ee feat(gen): 入口 main 内豁免栈上结构体指针的逃逸检查
memhub.MemBuddy(...) 是裸构造(无 __new__),life_of_expr 判定为 LIFE_FRAME;main 里 argparse._mbuddy = mb 把帧内结构体指针写进模块级字段即触发 P3 拦截。入口 main 的帧生命期=进程生命期,其栈上结构体不会被本帧返回后使用,故在 fail_if_life_escapes 放行。仅豁免'结构体指针'(新增 _CurFuncIsEntryMain/_TypeIsPtrToStruct/_ExprIsStructPtr):标量指针(&local、栈数组退让、ret(frame) 返回 int*)仍受检,保持 NewLifetimeFieldEscapeTest / NewLifetimeAnnFieldEscapeTest 等负向工程不回归。
2026-10-10 14:45:07 +08:00
TermiNexus 034293df59 fix(gen): freed_restore/freed_join/freed_capture 补 global 声明,修复野写崩溃
上一提交(eea8111)在 freed_restore 里重新赋值全局指针 _g_freed_slots 却未声明 global,被解析成未初始化的局部变量:is None 判假 → 跳过分配 → 对垃圾地址(0x11)野写,ASan access-violation。为三个新函数统一声明 global _g_freed_slots, _g_freed_count(与 _FreedMark 同口径)。
2026-10-10 14:27:00 +08:00
TermiNexus eea8111e61 fix(gen): willFree 失效槽表补控制流合流,修复分支内 free 泄漏导致的误报
失效表原为平坦单调全局表,无分支合流语义:if 分支体内 free(x) 会把失效态泄漏到合流之后,于是 fileio.vp 的 if ...: free(buf); return None 之后 return buf 误报 LifetimeError(1784eaf 修好 free 的 wf 位后才暴露)。新增 freed_capture/freed_restore/freed_join(pool 快照 + 求交),并在 if(结合 then/else 是否到达合流)与 while/for 的既有 life_* 同点位接入:只有所有到达合流的路径都失效才算失效。
2026-10-10 14:10:53 +08:00
TermiNexus f2cbb658e1 fix(stubgen): vpi 落盘入口统一重建文件级名字窗口,修复 FileFuncNames 悬垂野读崩溃
Phase1 1b-pre / 1b 兜底 / BuildPipeline / Repl 为生成 vpi 各自重新 parse 出独立 AST,但不走 register_file_funcs,导致 is_file_func(range)(_PushInductionEntry)仍读到上一个文件的表 + 已被 reset 的池 → 悬垂指针,ASan access-violation 崩在 HashTable._slot_hash。将 reset_file_name_window + register_file_func_names 收敛到 _GeneratePyiFromAst 唯一入口,覆盖全部 vpi 落盘调用点;Phase2 的重复调用随之移除。
2026-10-10 14:00:41 +08:00
TermiNexus c1d3f62bf0 fix(gen): ret(borrows=...) 支持 0 起序号写法(原来只认形参名)
现场:@life(noescape(a, b), ret(borrows=1)) 报
LifetimeError: @life(ret(borrows=...)) accepts only a parameter name or `none`
—— _RetKindFromTag 的关键字分支只处理 Name(形参名)与 CONST_NONE,遇到整数常量
(形参位置序号)就落到 fail-fast。

修法:Name(形参名 / none)与 Constant(CONST_NONE = 非本帧)先分流,
其余一律交给 _WillFreeArgIndex —— 它本来就同时认"形参名"与"0 起序号"
(与 willFree(...) 同口径,见 includes/stdlib.vp 注释),非法实参在那边 fail-fast。
于是 ret(borrows=x) 与 ret(borrows=0) 等价可用。
2026-10-10 13:27:24 +08:00
TermiNexus 5ec75b2d38 fix(gen): 模块限定函数调用的 ret(borrows=x) 形参位映射错位(A2 跨模块静默失效)
现场:NewLifetimeRetXmodTest 漏检(应拒绝却通过)。诊断(printf 断点,已删)显示
_CallRetKind 完全正确:cls=(none) → 模块分支 → mod_p=retdep2 sha=1 → vpi lookup rk=8。
即契约取到了,错在**下一跳**:life_of_expr 的 borrows 分支用
_CallArgNodeForParam(cl, pi) 把 pi=0 当"接收者"——那是**方法**的语义(形参表含 self)。
`retdep2.IdentBorrow(l)` 是**模块限定函数**(callee 也是 Attribute,但不是方法),
形参 0 就是第一个实参,于是取到了 `retdep2` 这个模块名节点:
life_of_expr(Name retdep2) 查不到变量 → 返回 LIFE_STATIC(≠FRAME)→ 放行。

修法:
- _CallArgNodeForParam 增加 is_method 参数:为 1 时才把 pi=0 映射到接收者、其余偏移一格;
  为 0(模块限定/裸函数)时 pi 直接就是实参下标。
- 新增 _CallIsMethod(trans, cl):接收者是对象变量(_RecvClassName 命中)才算方法调用。
- life_of_expr 的 borrows 分支据此传参。

同模块用例(NewLifetimeRetEscapeTest)与正向用例(NewLifetimeRetOkTest)在此之前"碰巧"
通过/失效——前者走 Name callee 不涉偏移,后者的正例把结果用在本帧内、STATIC 也放行。
2026-10-10 13:00:51 +08:00
TermiNexus 1784eaf906 fix(gen): _WillFreeMergeTag 置位判据写反(is_wf != 0 → == 0),P2 willFree 全面漏检
string.strcmp 相等返回 0,所以 `is_wf != 0` 的含义是"这个标签**不是** willFree"。原来的
`if is_wf != 0: mask |= 1 << idx` 于是:真正标了 willFree 的形参位**永不置位**,而任何别的
标签(noescape/escape/stores/borrows)反而把 willFree 位置 1。

实测证据(本轮编译产物):
- EscapeXmodTest/.tpv_build/.../73b2b3ff01d73ce2.vpi(stdlib)第 9 行
  `def free(p: rawptr) -> t.State: pass` —— 连尾注都没有(wf_mask 为 0 ⇒ 不写 wf=);
- EscapeOkTest/.tpv_build/.../4ccdbfc0e006b3d5.vpi 第 44 行 StoreNS(只标了 noescape)
  却写着 `# wf=1 escn=1`(反向误标)。

后果:跨模块 wf= 通道形同虚设 → 调用点从不"标记实参失效" → P2 全面漏检
(NewLifetimeWillFreeTest / WillFreeKwargTest / WillFreeMethodTest / WillFreeAddrTest 静默通过),
同时 NewLifetimeWillFreeOkTest 是**空过**(根本没有标记发生)。反向误标还可能造成假报。

回归来源:fc797da(A1 把三元 guard 扩成五元时把这行一起改反)。本行判据改为 is_wf == 0,
与同文件 @willFree 兼容分支(strcmp(...) == 0 即相等)的口径一致。
2026-10-10 11:55:57 +08:00
TermiNexus 948e1855a6 fix(gen): 跨模块 vpi 查询只在加载成功时记"已加载"备忘(修漏检回归)
A2 引入的 lookup_ret_kind_via_vpi 会在 life_of_expr(判右值生命期,可能在极早的翻译阶段)
里触发对方模块 vpi 的按需加载,但它沿用 _vpi_escape_packed 的旧写法:**无论加载成功与否**
都把该模块记入 _vpi_escape_module_loaded 备忘。于是当那个时刻 vpi 还没生成/打不开时,
该模块被毒化成"已加载",之后所有共用这条备忘的跨模块查询(逃逸摘要 esc=、willFree wf=)
都跳过真正的加载 → 缓存恒空 → 判据静默失效。

现场:EscapeXmodTest(跨模块 esc)与 NewLifetimeWillFreeTest(跨模块 wf)双双漏检,
即"应拒绝却通过"。同模块用例不受影响,故更隐蔽。

修法:两处加载点都改为 _ensure_vpi_ret_annot_loaded 返回非 0(= 真加载/已缓存)才
_vpi_escape_mark_module;失败直接返回 0,留给后续查询重试。
- lookup_ret_kind_via_vpi(新代码,回归根源)
- _vpi_escape_packed(既有同款隐患,一并修)。
2026-10-10 11:25:43 +08:00
TermiNexus a3a627f513 fix(gen): lambda 返回类型完全跟随 body(整数也跟随,不再一律 i32)
承接上一提交:原来只修正指针/浮点两类,整数仍保留 i32 语义(i64 实参会被 trunc 成
i32,静默有损)。现按用户要求改为**完全跟随 body 的实际类型**:
- func.RetTy = body_val.Ty,然后原样 build_ret,不做任何强转;
- void body(如 lambda: <void 调用>)→ RetTy = Void + build_ret_void
  (原来会因 "a void value cannot take part in any operation" 直接报错)。
副作用(预期内):body 为 bool → i1;body 为 i64/u64/f64 → 同宽返回;指针 → 指针。
调用点从函数签名取返回类型(CallGenMixin 读 local_fn.RetTy),自动适配。
注意:这是 ABI 行为变更,重编后必须回归 ClosureTest / ClosureTest2 / ClosureCallTest。
2026-10-10 10:46:51 +08:00
TermiNexus ea57986312 fix(gen): lambda 提升函数按 body 类型修正返回类型(原先恒 i32)
translate_lambda 第 4 步创建提升函数时只能先写 i32(body 尚未翻译);第 11 步原来把
body 值一律 coerce_to_type 到 i32,于是:
- body 返回指针 → 撞 CastError(implicit pointer-to-integer conversion is forbidden);
- body 返回浮点 → fptosi 成 i32(静默有损)。
现按 body 的实际类型修正 func.RetTy(mlirlite 公开字段;本步之后才发 ret,期间无人读
RetTy;调用点读到的已是修正后的签名):
- 指针   → RetTy = body 类型,直接 ret(不再强转);
- 浮点   → 同上(不再 fptosi);
- 整数/其它 → 保持既有 i32 语义(与"无注解嵌套 def 保持 i32"同口径,避免改动既有行为)。
与嵌套 def 的返回类型缺口同类(见上一提交)。
2026-10-10 10:44:07 +08:00
TermiNexus 5d1e604aac fix(gen): 嵌套 def 的返回类型跟随 -> T 注解(原先写死 i32)
现场:def Inner(p: char*) -> char*: return p 报
CastError: implicit pointer-to-integer conversion is forbidden ...
—— 因为 translate_nested_function_def 把提升后的闭包函数写死成
define i32 @__closure_Inner(i8* %env, i8* %p)(grep 过整个文件,从未读 fd.returns),
return p 于是要把 char* 隐式转 i32。即"嵌套 def 只支持 int 返回",此前被同文件
更早的用例报错掩盖(EscapeMethodTest 的 (A)/(B) 因同命裁剪静音后才露出来)。

改动:
- 提升函数按 fd.returns 解析返回类型(resolve_annotation_type);解析不出 -> fail-fast。
  边界:fd.returns 为 None 或注解是 None / t.CVoid 时**保持 i32**(不改既有 ABI,
  避免打断现有无返回值/无注解的嵌套 def)。
- 隐式收尾的零值改为 ConstZero(ret_ty)(原写死 const_int32(0),签名一变 llc 会拒绝),
  取不到零值则 fail-fast。

已知未覆盖:lambda 的提升路径同样写死 i32(见 FuncGenMixin 中 lambda 分支),
本轮不动(lambda 无返回注解可跟随)。
2026-10-10 10:40:36 +08:00
TermiNexus 41674d125c fix(gen): 修 vpi 生成阶段 is_file_func 读到已回收池的悬垂表(ASan 野读崩溃)
现场:TransPyV.exe(ASan)编译 EscapeOkTest 崩在 Phase A-pre-vpi →
_GeneratePyiFromAst → _PyiFunction → compute_escape_summary → _ScanEscapes →
_PushInductionEntry → is_file_func → HashTable._slot_hash
(access-violation on unknown address 0xffffffffffffffff)。

根因(潜伏已久,非本阶段语义改动引起):
- FileClassNames / FileFuncNames / FileVarNames / FileModuleSha1 是按文件重建的表,
  表与键共用该文件的瞬时池;契约要求跨文件/跨阶段先清空再重建
  (原写在 reset_visible_structs 里那四行)。
- Phase A-pre-vpi 为生成 vpi 会重新 lex/parse 每个文件,但不走 register_file_*、
  也不清表 → _PushInductionEntry 的 is_file_func 只认 range 的判据,落在 Phase1
  最后一个文件的表上,而那个池已被 reset_to(mark1) 回收。非 ASan 下常表现为读到
  0/旧值 的静默错答,ASan 下 0xfd 填充 → 垃圾容量/垃圾 __slots__ → 野读崩溃。

修法:
- 抽出 reset_file_name_window() 作为文件级名字窗口的唯一开启入口(原四行清空搬进去,
  reset_visible_structs 改为调它),并在注释里写死契约。
- 新增轻量 register_file_func_names(pool, tree):只重建本文件顶层函数名(核对过 vpi
  路径只需这一张;FileClassNames/FileModuleSha1/FileVarNames 的读取点全在翻译期)。
- Phase A-pre-vpi 循环里,在 _GeneratePyiFromAst 之前调用
  reset_file_name_window() + register_file_func_names(mb, tree_vp2)。
- register_file_classes / register_file_funcs 改为无条件重建(原来 没有才建 的写法在
  表已被别的阶段建过时会静默复用悬垂表)。
2026-10-10 09:41:15 +08:00
TermiNexus 79d1c86982 fix(gen): return 本帧存储改为声明感知 fail-fast(A2 方案 A)
A2 原本整体移除定义端 return <本帧地址> 的 fail-fast,导致无 @life(ret(...))
声明的函数返回本帧存储时静默通过(调用点只拿到 LIFE_UNKNOWN,悬垂一路无声)。
现改为按被调者自己的 ret 声明判定:
- ret(frame) / ret(borrows=<该形参>) → 放行;
- ret(heap|static|borrows=none) → 报错(声明与返回值矛盾);
- 无声明 → 报错(提示补 @life(ret(frame)) 或 ret(borrows=<param>))。
落点:AssignGenMixin.check_return_frame_decl(判据复用 life_of_expr,target_ty 传
返回类型,与移除前的口径一致);ret_kind 由 StructGenMixin.get_ret_kind_by_func
反查 EscFuncTable → EscDirTable(顶层函数/方法/嵌套 def 一次覆盖,无需猜类名),
当前 FunctionDef 取 StructGenMixin.NllCurFunc。
新检查是旧检查的严格子集 ⇒ 自举无回归。
2026-10-10 09:16:26 +08:00
TermiNexus 6da8e2e095 feat(gen): 落地 @life(ret(...)) 返回值生命期契约(A2)
- GenApi:新增 compute_life_anno / _RetKindFromTag 与 RET_KIND_* 编码,
  提供 get_ret_kind / get_ret_kind_method / lookup_ret_kind_via_vpi;
  _WillFreeMergeTag 不再拦截 ret(改由 compute_life_anno 收集校验)。
- StructGenMixin:EscDirTable 由 3 槽扩到 4 槽(新增 ret_kind 列),
  compute_escape_summary 把契约落在 box[5] 并登记。
- Stub:.vpi 行尾新增 escr=<ret_kind>(Converter 写、VpiReader 读 _g_vpi_esd_r)。
- AssignGenMixin.life_of_expr(Call):@life(ret(...)) 声明优先于任何推断;
  frame/heap/static/borrows=none 直接映射,borrows=x 取对应实参生命期,
  方法调用按接收者静态类型解析类名。
- StmtGenCifMixin:同批移除定义端 return <本帧地址> 的 fail-fast,
  返回值去向改由调用点按被调者契约判定。
2026-10-09 22:58:33 +08:00
TermiNexus 4907e64714 feat(lifetime): _g_new_life 透传——new stack/slife 的构造也算 FRAME(参与构造器同命裁剪)
新增模块级 _g_new_life 与 _g_new_storage 同步维护(set/restore 支持嵌套 ctor),口径与 life_of_expr 的
New 分支一致:stack(含 stack slife)/slife → LIFE_FRAME;heap·memgr + 缺省/hlife → LIFE_HEAP;-1 = 未知。
_translate_struct_ctor 两份副本的 recv_life_ct 改为:裸 T() → FRAME;有 new → 取 _g_new_life。
于是 new stack S(&l) / new heap slife S(&l) 不再误报,new heap hlife S(&l) 仍报。
2026-10-09 22:43:43 +08:00
TermiNexus 953c829e9f feat(lifetime): 构造器路径填 recv_life——裸 T() 构造(入口块 alloca)算 FRAME
_translate_struct_ctor 两份副本都算 recv_life_ct:_g_new_storage == None(无 new 描述符提供存储)→
裸 T() 的存储是入口块 alloca → LIFE_FRAME(参与构造器同命裁剪);非空(存储来自 new …)→ -1 不裁剪。
两处 __init__ 调用点(:171 / :675)均传入。覆盖 CtorHolder(&l) / BoxCtor(&l) 这类裸构造误报。
2026-10-09 22:40:30 +08:00
TermiNexus 357a8bba05 feat(lifetime): 构造器路径同命裁剪的透传管道(recv_life,默认 -1 = 现状不变)
_call_method_on_ptr 新增 recv_life: int = -1(被构造对象的生命期,由调用方 CallGenCtor 算出);
构造器分支在指令最终裁决之后,若 recv_life ∈ {FRAME, VALUE} 则复用 _FrameArgsEscapeClearsMethod
(recv_node=None,接收者已由 recv_life 判定)清"写进形参对象字段"那类位。
下一轮只需在 3 个 __init__ 调用点(CallGenCtor:171/665、ExprGenConst:257)填 recv_life;未填 = 完全现状。
2026-10-09 22:36:09 +08:00
TermiNexus bf5ecd7668 fix(lifetime): 方法路径补上"同命裁剪"(此前缺失)—— h.set_q(l) 不该报
现场:h: S* = new heap slife S()(生命期本就是 FRAME:作用域出口释放)+ l 是栈数组;
h.set_q(l) 却报"escaping method parameter"。根因:同命裁剪只做在模块级函数路径,方法路径一直没接
(此前已明确告知"还差一半")。

新增 CallGenMixin._FrameArgsEscapeClearsMethod:位 0 = 接收者(self)、位 (i+1) = 用户第 i 个实参;
某位可清 ⇔ 该位对应实参是 FRAME/VALUE 且其余全部实参(含接收者)也在本帧。
CallGenCall 方法路径在 refine 之后、指令最终裁决之前套用;硬位仍用 get_param_escapes_hard_method 排除。
构造器路径暂未接(__init__ 的 self 是"正在构造的对象",此处拿不到接收者 AST 变量)。
2026-10-09 22:30:35 +08:00
TermiNexus 6000734704 feat(life): A1——@life 新增 stores(x)/borrows(x)(复用 escape/noescape 契约位);ret/owns 显式报未实现
§14 路线 A 第一步(零语义变化):
- stores(x) 归到"强制逃逸"位、borrows(x) 归到"强制不逃逸"位(同一对契约位,调用点已在最后套用、.vpi 已有 esce=/escn= 通道)
- _WillFreeMergeTag 白名单同步扩 stores/borrows;ret/owns 显式 fail-fast "not implemented yet (planned A2/A3)",不做静默忽略
- 矛盾契约报文改为 escape/stores 与 noescape/borrows 两两对立
2026-10-09 22:14:26 +08:00
TermiNexus 71730d920c chore(dbg): 删除 NLL 临时诊断(§13 已在 EscapeTest 上验证通过)
实测:var=h confined=1,g(l,h) 不再报错;报错前移到真正的泄漏点 `if 1==0 or a == 1: return l`(where=a return value)。
2026-10-09 21:59:48 +08:00
TermiNexus 0f504a9010 chore(dbg): 给 NLL 谓词插桩([NLL-ST] 逐条语句 + [NLL-UNSAFE]/[NLL-MENTION] 拒绝原因),定位后删除 2026-10-09 21:53:44 +08:00
TermiNexus 960ecdfd74 fix(nll): 补 BoolOp/Compare 的字段递归(if 1==0 or a == 1 曾把整条判据否掉)
_init_ast 里 node.children = None 是**默认值**(只有语句体这类显式赋值的才有子树),所以"认不出的表达式
一律保守"这条策略会被任何未列举的表达式种类触发。新用例的 if 条件 BoolOp(values) 与 Compare(left/comparators)
就是这种。补上后 h 应能降级,报错位置回到真正的泄漏点 return l(第 19 行)。
2026-10-09 21:34:17 +08:00
TermiNexus 5a10a2ea10 fix(nll): 谓词改为按字段显式递归(AST 表达式节点不填 children,导致 new stack [2]char 被误判"提及")
根因([NLL-DBG] var=l 已证实):_set_parent_list 只设父指针、不填 children,故 New/Attribute/Call 等表达式节点
的 children 基本都是 None。原实现把"children==None"当作"提及/拿不到子树",于是 k 里第一条
`l = new stack [2]char` 就把整条判据否掉(var=h confined=0)。

现在 _NllMentions / _NllExprUnsafe 均按字段显式递归:Name/Constant/Attribute/Subscript/Call/New/UnaryOp/
BinOp/IfExp/Tuple|List|Set;认不出的种类保守(提及=1 / 不安全=1)。_NllStmts 的"认不出的语句"
分支改为按子节点判(无子节点的 pass 之类放行)。
2026-10-09 21:23:51 +08:00
TermiNexus e82e2ac768 chore(dbg): 加 NLL 判定链临时诊断([NLL-DBG]),定位后删除 2026-10-09 21:05:31 +08:00
TermiNexus 337be883d3 fix(nll): Attribute 节点显式只递归 .value,避免"拿不到子树=算提及"把 stdio.printf(...) 误判成不安全
现场:EscapeTest 的 h = new heap S() 永不降级(g(l,h) 照旧报逃逸)。根因是 _NllMentions 对 Attribute
落进保守分支(children 不含 value 时返回"提及")→ _NllExprUnsafe 把 stdio.printf("%s", h.q) 判为不安全
→ 整个 NLL 判据返回 0。属性名是静态字符串,变量只可能在基对象里,显式递归 .value 即可。
2026-10-09 20:57:31 +08:00
TermiNexus d0ffb094c2 feat(lifetime): §13 NLL——默认生命期的 new heap 证明帧内封闭时降级为帧对象
StructGenMixin: 新增 NllFrameConfined 及遍历器(_NllStmts/_NllExprUnsafe/_NllMentions/_NllInSet/_NllAdd/_NllCalleeEscMask)+
has_param_escapes(区分"有记录=权威不逃逸"与"无记录=判不定");别名集只增不减故单遍即可,if/while/for 全走、
不进嵌套 def/class;认不出的语句"不提及别名才放行";取址/实参位逃逸/return/长命目标一律按外泄拒绝。

CallGenCtor._new_is_nll_demotable:只降级"堆且未显式指定生命期"的 new;AssignGenMixin.propagate_life
判据成立则 life=FRAME(于是调用点同命裁剪放过 g(l,h)),_slife_note_assign 接 target_name、判据成立按托管堆
对象登记 → 复用 slife 的作用域出口释放(手动 free/del 也会被既有检查挡住)。

三条函数体翻译入口记录 NllCurFunc(谓词要遍历当前函数体),与 traceback 推帧同一处。

注:谓词按 §13.2 保守优先,判不定一律维持 HEAP;本提交未编译验证。
2026-10-09 20:49:21 +08:00
TermiNexus 79299ec47d feat(lifetime): 声明式指令契约落 .vpi(esce=/escn=),跨模块调用点也能最终裁决
写侧 Converter:读 compute_escape_summary 落在 box[3]/box[4] 的契约掩码,随函数头行尾写 esce=<强制逃逸位> / escn=<强制不逃逸位>;
只有指令(既无 esc/ret 也无 wf)时也补一个 "# esc=0 ret=0" 注释段——读侧是"见 # 才解析"。

读侧 VpiReader:_cache_vpi_esc_clears_text 同一次行扫描里顺带收 esce=/escn= 进 _g_vpi_esd_*(与 escg 同键 sha1[.Cls].func);
新增 _lookup_vpi_escape_dir(.., noescape);CallGenBase.lookup_escape_dir_via_vpi 包装。

调用点:模块级函数 / 方法 / 构造器三条路径在本文件指令表为空(= 跨模块)时改查对方 .vpi,
仍放在所有推断/回退之后最后套用:esc_mask = (esc_mask | esce) & ~escn。
2026-10-09 20:40:30 +08:00
TermiNexus 2bd37e80e3 feat(lifetime): 方法/构造器调用点也接 @life(escape/noescape) 最终裁决
StructGenMixin: 新增 _DirForceOf / _DirForceMethod(查 "Class.method" 并沿 MRO 回退)与
get_param_escape_force_method / get_param_noescape_force_method;GenApi 加同名委托。

CallGenCall: 构造器分支(esc_mask_ci)与方法分支(esc_mask_mc)在跨模块回退、常量特化之后
最后套用一次 esc_mask = (esc_mask | escape_force) & ~noescape_force,与模块级函数那条口径一致。
2026-10-09 20:34:34 +08:00
TermiNexus fc7c5d5560 feat(lifetime): @life(escape/noescape) 升为调用点最终裁决(契约不可被推断推翻)
StructGenMixin: 新增 EscDirTable(3 槽/项:[名哈希, 强制逃逸位, 强制不逃逸位]),
_RegisterFuncEscapeSummary 把 compute_escape_summary 落在 box[3]/box[4] 的指令掩码单独落表
(与 EscTable 的"推断结果"分开);新增 _DirLookup / get_param_escape_force / get_param_noescape_force。
compute_escape_summary 的指令输出缓冲由 g+68 改为 box[3](同一内存,语义显式化)。

CallGenMixin: 在常量特化、同命裁剪、跨模块回退**全部做完之后**,最后套用一次指令:
esc_mask = (esc_mask | escape_force) & ~noescape_force。这样 escape(...) 不会被"死分支/同命"折掉,
noescape(...) 也不会被任何上游路径重新解释成"逃逸"。
2026-10-09 20:30:03 +08:00
TermiNexus 8881dea2e2 fix(diag): 泄漏点采集改为"确认报错后才跑",并保存/恢复扫描期状态
原先在调用点的位循环里就调 escape_leak_line:该函数会重扫被调者函数体并改 EscConstEnv/EscIndCount,
而循环里可能紧接着发现"该实参其实不是帧生命期"从而不报错 —— 等于白白跑一遍扫描、还留下脏状态。
现在改成把 (被调者, 形参位) 传给检查方,过了 LIFE_FRAME 判定才采集;escape_leak_line 内部
把 EscConstEnv/EscIndCount/EscLeakWant 先存后还。
2026-10-09 19:50:31 +08:00
TermiNexus 2353dca8ec feat(diag): 逃逸类 LifetimeError 追加 File lifetime leak 帧,标出泄漏点
StructGenMixin: 新增 escape_leak_line——错误路径重扫被调者函数体,取指定形参的**首个逃逸点**行号;
_NoteLeak 在三处置位来源采集(字段/全局写、return 形参、传递闭包调用),新增 EscLeakWant/EscLeakLine。

Diag: DiagEntry 增加 LeakFileName/LeakLine + set_leak + emit_leak_frame(标签 File lifetime leak),
打印顺序 = 调用链 → 错误点 → 泄漏点 → 类别: 消息。

TypeGenErr.fatal_error / GenApi.fail_if_stack_addr_escape 增加 leak_file/leak_line 透传;
CallGenMixin 调用点按失败实参位取行号。**仅同模块**:跨模块 EscFuncTable 无 AST → 返回 0,不打该帧。
2026-10-09 19:32:54 +08:00
TermiNexus c98730f717 refactor(diag): 精简 LifetimeError 报错词,去掉冗长从句
逃逸主报文由三段长从句压缩为「frame-local + 悬垂 + 修法」两句:
  stack address escapes: `&x` is frame-local; using it at {where} hands out a dangling pointer
  once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).

同步缩短 where 短语(return value / escaping callee|method parameter / constructor parameter stored
into a field / longer-lived field-element / module-level global)

其余冗长报文一并精简:willFree 失效、slife 手动 free/del、async slife、stack+hlife 矛盾、三处容量上限、
@life 标签实参缺失

测试只断言关键字 LifetimeError,不受文案影响(Test/*.py 已确认)。
2026-10-09 19:22:04 +08:00
TermiNexus 007943142a fix(diag): DiagEntry.File 改名 FileName,避开与 fileio.File 的非限定名相撞
现场:TPC 编译 Diag.vp 时 read_source_line 的 fileio.File(...) 报 llvmlite
"Type of #1 arg mismatch: i8** != %"1f18c0c34662cbfb.File"*"
(1f18c0c34662cbfb = fileio 的模块 sha1,即 self 形参被解析成 i8**,实参本是正确的 fileio.File*)。

Diag.vp 是唯一一个"类字段名 == 同模块 import 的类名"的模块(全仓仅此处有 File: 字段),
而其余 24 个调用 fileio.File(...) 的模块(含 BuildPipeline.vp 的 6 个类)都无此字段且编译正常。
TPC 的 Gen.structs / SymbolTable / _mangle_func_name 均按非限定名索引,故改名规避。
2026-10-09 19:12:10 +08:00
TermiNexus 6769cf0335 refactor(diag): 编译期 traceback 抽为 Diag 模块,单条诊断携带完整调用链
- 新增 Diag.DiagEntry 与 new_entry/begin/set_current/add_frame/emit/free_entry,方向由 DIAG_DIR_* 决定

- TypeGenErr.fatal_error 改走 DiagEntry:填调用链 + 当前帧,emit(UP) 后 free + finish

- 顶层函数体/嵌套函数体/方法体三处翻译入口各 push/pop 一帧(def 位置),报错可回溯所在函数
2026-10-09 18:45:06 +08:00
TermiNexus c791bd5e46 feat(new): new 支持标量/数组与前导 [N]T;类名解析不出时回落基本类型路径
- CallGenCtor._translate_new_primitive:认 Array 元素类型,返回值退化为首元素指针,零初始化按整体类型写
2026-10-09 18:45:01 +08:00
TermiNexus fc797da542 feat(lifetime): @life(escape/noescape) 多形参、硬逃逸位同命精化、归纳变量区间与跨模块常量特化
- GenApi:escape/noescape 标签支持多实参;新增 compute_escape_directives 与若干委托

- StructGenMixin:硬逃逸位(box[2])、常量与归纳变量区间折叠、_PackEscapeClear/compute_escape_clears

- Call 系:调用点同命裁剪(_FrameArgsEscapeClears)+ 跨模块 escg= 查询;写侧 Converter、读侧 VpiReader

- AssignGenMixin:裸 T()(无 new、无 __new__)判为帧生命期
2026-10-09 18:44:51 +08:00
TermiNexus 14a8dcb98b feat(gen): new 支持基本类型 + 跨过程常量特化重算逃逸摘要
- CallGenCtor._translate_new_primitive:
ew [stack|heap|memgr(x)] T(T 为标量)取存储 + 零初始化 → T*;stack 走入口块 alloca、heap 走 malloc、memgr 复用既有通道;标量带实参 fail-fast,不引入第二套标量构造语义

- StructGenMixin:新增 EscFuncTable / EscRefineScratch / EscConstEnv 与常量求值器 _EvalConstInt / _EvalConstCond;_ScanEscapes 的 if 分支按形参常量折叠只扫被走到的分支(死分支里的写不记为逃逸)

- 调用点(普通函数 + 方法,方法沿 MRO 回退)在逃逸检查前用常量实参重算摘要;跨模块拿不到函数体仍保守返回原掩码

- 折叠只会减少逃逸位,不引入新的误报
2026-10-09 14:55:28 +08:00
TermiNexus ab30b264fc feat(gen): P3 覆盖注解形态的属性/下标目标
- translate_annassign 原先对非 Name 目标直接 return 0,obj.f: T = v / rr[i]: T = v 整条语句被静默丢弃(既不报错也不写出)

- 新增 _translate_lvalue_annassign:解析注解(先特化泛型)→ get_attribute_ptr / get_subscript_ptr 求左值指针 → 走同一处 P3 长命左值检查 → RHS 以注解为靶型求值 → coerce_to_type 后写出;const / volatile / f-string 逃逸与普通 Assign 同口径

- 所有失败路径 fail-fast,不再有静默丢语句
2026-10-09 14:55:10 +08:00
TermiNexus d926987d44 chore(dbg): 清除 P4 转移分支的逐点探针
崩点根因已定位(slife 登记表跨文件悬垂),debugger 探针全部移除。
2026-10-09 13:04:40 +08:00
TermiNexus eb0c3dfd31 fix(gen): slife 登记表改为跨文件存活,并修正区间弹回
- _g_slife_owned 原先从 trans.Pool(每文件池)分配,Phase2 每文件 reset_to(mark2) 后表体即悬垂,第二个文件起每次登记都写坏活跃池数据(现场 access-violation on 0x0,崩点随分配漂移、ASan 无法给栈)

- _SlifeRestore 只还了 count、未还 top,top 随函数数量单调增长;上一文件/函数的陈旧条目留在扫描区间内,因每文件池地址复用被误判为新变量,_SlifeShadow 返回悬垂影子槽,_SlifeNoteStore 直接写坏内存

- 新增 _g_slife_save/_g_slife_depth:进入时保存 count/top 两个边界,退出时一起弹回

- _SlifeDropOwnedUnlessSame 补 Pointee==0 守卫(与 _SlifeEmitGuardedFree 同口径)
2026-10-09 13:04:26 +08:00
TermiNexus 1a3ef521b3 chore(dbg): P4 转移分支逐点探针(owned/begin/ensured/drop.begin/dropped)
崩点已后移到 CaseOwnershipTransfer 的 q = p(P4 别名转移),逐点定位该分支内具体崩溃位置。TEMP。
2026-10-09 12:43:59 +08:00
TermiNexus aeb396831a chore(dbg): 清除 CallGenMixin 残留的 call.enter 探针 2026-10-09 12:43:28 +08:00
TermiNexus 6758ecbf69 chore(dbg): 清除最后一处 TEMP 探针(AssignGenMixin slife.note) 2026-10-09 12:03:43 +08:00
TermiNexus 5f92abb7bc fix(gen): 补回 CallGenMixin 的 _FreedMark(修行尾时被 checkout 回滚)
TPC 报 MemberLookupFailFast: 属性 Freed 在 VarEntry 中找不到 —— 该文件的行尾修复用 git checkout 恢复过,把未提交的 _FreedMark 改动一并回滚了,残留 ent_wf.Freed = 1。
2026-10-09 12:03:23 +08:00
TermiNexus dd24dc670f fix(gen): P2 失效状态改存独立槽表,不再给 VarEntry 加字段
现象:任何工程编译都在函数形参注册刚结束处 access-violation(pc=0,逐字节可复现)。定位:逐层探针把崩点压到 FuncGenDef 的 pre_scan 段,而该段对 Check(body 仅 if+return)应为空操作,唯一变量是 VarEntry 这个热结构被加了 Freed 字段。修法:撤销 VarEntry.Freed,改用 VarGenMixin 的独立失效槽表 _g_freed_slots(键 = 变量槽 alloca,整次翻译内唯一,故全局累积、无需按函数 reset/restore);_FreedMark/_FreedCheck/_FreedClearSlot 三个 API 替换字段读写,P2 语义不变。同时清除全部 TEMP 探针。
2026-10-09 11:51:11 +08:00
TermiNexus 95b076581a chore(dbg): pre-scan 与 _SlifeReset 之间补 p1d 探针(TEMP)
p1c 之后仍崩,最后一段只剩 pre-scan 与 _SlifeReset;p1d 把两者分开。
2026-10-09 11:30:33 +08:00
TermiNexus 054dcf0a0b chore(dbg): 形参注册与 pre-scan 之间补 p1c 探针(TEMP)
add_var 三次全过(esz=56),崩点在其后。p1c 夹出 L700-763(状态保存)与 L765-775(pre-scan)两段。
2026-10-09 11:24:29 +08:00
TermiNexus 07ff906338 chore(dbg): add_var 逐行探针 a1..a8(TEMP)
崩点落在形参注册段,而这段里唯一的结构性改动是 VarEntry 加了字段。逐行埋点定位到具体哪一步(esz/cnt 也会打出来,可用于核对 sizeof 是否异常)。
2026-10-09 11:12:31 +08:00
TermiNexus a2e1c651fc fix(gen): 变量表 Freed 不再逐个字段写,统一依赖 init_vars 的整块清零
崩点已定位在普通函数形参注册段(auto_addr 之后、_SlifeReset 之前)。add_var 里对刚加过字段的 VarEntry 末端逐个写 entry.Freed 疑似越界/踩偏移;init_vars 本就 memset 整块,去掉冗余写入。
2026-10-09 11:01:08 +08:00
TermiNexus 9b97470c09 chore(dbg): 函数翻译内段探针 p1/p2/p3
夹住 auto_addr → 形参注册/pre-scan → slife reset → 语句翻译 四段;TEMP,定位后移除。
2026-10-09 10:44:56 +08:00
TermiNexus 20e3f3694f chore(dbg): 补语句级与调用级探针(stmt.kind / call.enter fn)
崩点已收窄到 Check 函数体内的翻译,这两个探针把范围压到具体语句与具体被调者。均为 TEMP,定位后移除。
2026-10-09 10:39:24 +08:00
TermiNexus 400cc6e17b chore(dbg): 再加两个探针(函数名 / slife 赋值 rhs kind)
TR.fn: <name> 定位到具体函数;slife.note rhs_kind=<k> 定位到 _slife_note_assign 的哪条 RHS 路径。均为 TEMP,定位后移除。
2026-10-09 10:19:29 +08:00
TermiNexus be2521fe46 chore(dbg): 临时阶段日志定位 Phase A 崩溃点(定位后移除)
四个探针:PhaseA translating <path> / vpi.gen <rel_path> / TR.enter sha1 / TR.prelude.done。用于把崩溃点夹在 解析→vpi生成→模块前言→函数体翻译 四段之间。
2026-10-09 10:08:43 +08:00
TermiNexus 8b63ae4ca2 fix(gen): 模块前言的声明池逐级判空,避免 PermPool 未就绪时空指针崩溃
现象:TransPyV.exe 编译任何工程都 AddressSanitizer access-violation on address 0x0(pc=0)。根因:_emit_module_prelude 跑在每个模块翻译的开头,此时 CompilerFactory.factory 或其 PermPool 可能尚未就绪,直接 factory.PermPool.alloc(...) 解引用空指针(PermPool 是 factory 的首字段,偏移 0,与 ASan 报的地址 0x0 吻合)。改为 factory / PermPool 逐级判空,不可用时回退模块池。
2026-10-09 09:45:36 +08:00
TermiNexus b71ed43f10 feat(gen): P5 slife 登记表容量提到 256,超限 fail-fast
分配与上限判定共用 _SLIFE_MAX = 256(原 128 且满时静默停止登记)。_SlifeEnsureOwned 超限改为 fatal_error(LifetimeError):静默返回 None 会让该对象永不登记、永不释放(纯泄漏),与编译器自动管理的承诺矛盾。
2026-10-09 08:46:24 +08:00
TermiNexus efb4a54428 fix(gen): P4 自别名重复赋值不得释放共持对象
q = p; q = p 这类自别名重复赋值在覆盖释放时会把两个槽共持的对象提前释放(q 与 p 同时悬垂)。新增 _SlifeDropOwnedUnlessSame:在 IR 里比较 target 与源槽的当前值,相同则跳过整个覆盖释放(含析构调度),不同才走常规保护式释放。P4 转移改用它。
2026-10-09 08:45:09 +08:00
TermiNexus 3209a7a633 feat(gen): P4 别名赋值改为所有权转移(影子标志从源槽移到目标槽)
q = p 时若源槽确实拥有 slife 对象,则把所有权影子标志转移:先按覆盖释放处理目标槽原有对象,再置 q=1 / p=0。于是 q = p; p = None 不再释放对象(q 不悬垂),出口只释放 q 恰好一次。自赋值 p = p 与源不拥有(普通/外来指针)保持原状。
2026-10-09 08:43:48 +08:00
TermiNexus 301a9d3328 feat(gen): P3 左值比右值长命时禁止写入本帧栈地址
新增 AssignGenMixin.life_of_lvalue:Name 目标取变量槽本身的生命期(局部 FRAME / 全局 STATIC),Attribute/Subscript 目标递归其基对象的 life_of_expr(h.p 的生命期 = h 指向的存储)。在 propagate_life 的非 Name 目标出口比较:仅当左值明确长命(LIFE_HEAP/LIFE_STATIC)且右值为 LIFE_FRAME 时报 LifetimeError(复用 fail_if_life_escapes)。h.p = &local 被拒;local_struct.f = &local2 与 p = &local 放行(不误报)。已知缺口:AnnAssign 的属性/下标目标不走该出口。
2026-10-08 22:47:28 +08:00
TermiNexus 2b5ff215c7 fix(gen): 跨模块裸名 CExport/State 函数不再被误清,关键字实参不解析时 fail-fast
现象:stdlib.free(p = p) 生成 llvm.call @free()(零实参),mlir-opt 报 incorrect number of operands (0) for callee。根因:跨模块检查要求函数名前 16 字符等于模块 sha1,而 CExport/t.State 型函数(stdlib.free 等)本来就以裸名定义与声明,于是 found_func 被清成 None → 关键字实参路径整体跳过 → 位置实参循环一个也不翻译。修两处:1) 该检查对 is_cexport_func / _is_state_func_from_vpi 命中的函数放行;2) 带关键字实参却解析不出签名时 fail-fast,不再静默丢弃实参。
2026-10-08 22:38:28 +08:00
TermiNexus 8b2008031d fix(gen): 模块前言 C 库声明名改落 PermPool,避免被回绕的模块池覆盖
现象:@malloc 的声明名被渲染成乱码,mlir-opt 报 llvm.call @malloc does not reference a symbol in the current scope。根因:create_declare 用 _str_dup(pool, name) 把名字拷进传入的池,而前言用的是模块池(大模块上会被复用/回绕),早期分配的声明名被后续分配覆盖;类型是 mlirlite 内部 interned 的,所以只有名字坏、参数类型正常。修法:声明与参数名改落 CompilerFactory.PermPool(never reset)。
2026-10-08 22:30:07 +08:00
TermiNexus 8fb0e38f4c fix(gen): 泛型类构造只在真有 __init__ 时才调 __before_init__/__init__
无方法的泛型类(如 NewTest 的 Box[T])没有 __init__,ClassGenVTable 也就不会生成 __before_init__,但 _translate_generic_ctor_call 无条件发射这两个调用,导致链接期 undefined symbol(1cf2df6387df0bbf.Box[int].__before_init__ / .__init__)。改为与非泛型 _translate_struct_ctor 同判据:有 __init__ 才走 __before_init__ + __init__,否则按字段顺序逐字段 store。
2026-10-08 22:16:23 +08:00
TermiNexus c02aa3a2eb fix(gen): @life/@willFree 不再被当作运行时装饰器
自定义装饰器检测(__decor_wrap_<name> 生成路径)此前把 P2 的编译期标注当成 User 装饰器:会生成 wrapper 调用并不存在的 @life/@willFree,并把被装饰的 extern def(如 stdlib.free)变成本模块 internal 声明,llc 报 invalid linkage for function declaration。把 life/willFree 加入该处标记白名单。
2026-10-08 21:43:47 +08:00
TermiNexus b60ce06335 feat(gen): willFree 覆盖关键字实参、方法调用与取地址三条路径
关键字/混合实参:CallGenMixin._MarkWillFreeKwargs 按形参序(位置实参占前 can 个形参,其余按关键字名匹配)标记。方法调用:CallGenCall._translate_method_call 的 kwargs 与位置两条循环,实参链含 self 故用户第 ai 个实参对应 bit (ai+1);新增 GenApi/StructGenMixin.get_param_willfree_method(查 Class.method 并沿 MRO 回退)。取地址:ExprGenOps 的 AddrOf 分支对已失效变量 fail-fast。
2026-10-08 21:39:53 +08:00
TermiNexus d921dcd9e7 fix(gen): willFree 形参表访问先转 Arguments*,修复 TPC 侧 MemberLookupFailFast
FunctionDef.args 的字段静态类型是 AST*,直接写 fn.args.args 会在 AST 类上查 args(class_members=7 不含 args)。改为先 ast.Arguments*(fn.args) 再取 .args 列表,与 VpiReader / FuncGenMixin / ClassGenVTable 等既有写法一致。
2026-10-08 21:34:14 +08:00
TermiNexus 97313e9e55 feat(gen): P2 生命期标注 @life(willFree(...)) 驱动释放后使用与二次释放检查
标注面:@life(willFree(<形参名|0起序号>)),兼容叠加式 @willFree(...),可混用累加;序号按形参表下标、self/cls 计入。GenApi.compute_willfree_mask 解析标注并对非法标注 fail-fast。本文件表 StructGenMixin.WfTable + 跨模块 .vpi 行尾 wf=<mask>(Converter 写 / VpiReader 读 / CallGenBase 查)。调用点在实参全部翻译之后把对应实参变量槽置 VarEntry.Freed;其后的读取路径报 LifetimeError。重新赋值清除失效态。
2026-10-08 21:28:49 +08:00
TermiNexus 9ff5a3a03e fix(gen): 泛型类构造复用 new 描述符存储,并支持泛型下标 callee
- _translate_generic_ctor_call 之前一律 _alloca_at_entry:new memgr(pool) hlife list[int](...) 会忽略描述符给的内存、把对象构造到栈上、返回栈地址(静默悬垂)。改为与 _translate_struct_ctor 同款:tmp = _g_new_storage,为空才回落 alloca。

- _translate_new_expr 支持泛型下标 callee(Box[int]):新增 _specialize_new_generic_class 先特化模板拿具体类名,再走同一条取存储路径。
2026-10-08 21:04:41 +08:00
TermiNexus b6a9b50f93 fix(gen): 异常与传播出口也释放 slife(P1)
插桩点选在 StmtGenMixin._eh_ret_default —— 异常路径上真正离开函数的唯一汇聚点(translate_raise 的 bare raise / StopIteration / 传播三条分支 + try 的 UnhandledBB 传播分支都走它),发 ret 前 _SlifeEmitFrees。不得改在 raise 语句本身:被本函数 except 捕获时对象仍在用(那一步只 br 到 DispatchBB);EH 返回值也不会是 slife 对象(逃逸检查已拒)。
2026-10-08 21:04:33 +08:00
TermiNexus 70390858d8 feat(gen): 协程体里的 new heap slife fail-fast(释放点无法确定)
AsyncGenMixin 的状态机会把协程 body 拆成多个 state 块并多次转译,编译器无法确定 slife 的释放点;此前是静默漏释放。新增锚点 trans._coro_body_func(Translator 字段 + init 置 None),只在 body 翻译期间由 translate_async_function 设置并保存/恢复旧值;_translate_new_expr 以**函数对象同一性**(_coro_body_func == _cur_func)判定并 fatal_error(LifetimeError)。嵌套普通函数会换掉 _cur_func 故不误报;new stack 与 hlife 不受影响。
2026-10-08 20:42:09 +08:00
TermiNexus 2178970ff7 feat(gen): slife 释放前调度析构 __del__,并禁止手动 del(§3.6 落地)
- 释放统一入口 _SlifeEmitGuardedFree 改为「先析构、再 free」;析构名定案 __del__(对齐 TPC LLVMCG/MemoryOps._emit_local_heap_frees 的释放路径;del obj 那条语句走的仍是 __delete__)。

- 查表沿 MRO / 跨模块 stub 回退(复用 ExprGenOps._resolve_dunder_by_class);类未定义 __del__ 时**不加任何分支**,常见路径零开销。

- 析构用条件分支而不是 select:未拥有的路径必须完全不进析构体(析构体里会解引用 self,传 null 进去会踩空);块标签走 trans._label_counter,避免同函数内重名。

- §2.4 判据5 扩展:对 slife 对象手动 del obj → LifetimeError(否则析构跑两次)。hlife 路径不受影响。

- 为把 trans 传进析构查找,_SlifeEmitFrees / _SlifeEmitGuardedFree / _SlifeDropOwned 加 trans 形参,9 处调用点同步。
2026-10-08 20:34:11 +08:00
TermiNexus 78b2052445 feat(gen): 接线 M3 owned slife 的 RAII 出口释放(函数级 scope guard)
- 所有权影子标志(i8,登记时入口块置 0)+ free(select(影子!=0, load 槽, null)) 保护式释放:赋值在分支/循环里而未执行 → free(NULL);槽里写入外来指针或别名 → 清影子,绝不 free 外来指针;循环反复 new → 赋值前覆盖释放上一轮,不累积。

- 三条函数体转译路径(FuncGenDef 主路径 / FuncGenMixin 嵌套函数提升为 __closure_* / ClassGenVTable 方法体)各自抬升与弹回登记区间;修两处漏 reset 导致内层出口 free 外层对象再被外层 free 一次 = double free(Windows STATUS_HEAP_CORRUPTION)。出口按槽去重做兜底。

- free 由模块前言统一声明(Translator.vp,紧挨 malloc);释放点只查表 + fail-fast,绝不吐指向未声明符号的 llvm.call。

- 赋值处记账:new heap slife → 登记 + 覆盖释放 + 影子置 1;其它 RHS(new stack / hlife / 别名 / 外来指针 / None)→ 已登记则交还所有权。手动 free slife 对象 → LifetimeError。
2026-10-08 20:03:54 +08:00
TermiNexus 934708b206 fix(type): 数组退让豁免提到级数检查之前,修 [N]str 形参被误判 CastError
形参侧 CArray 按 C 语义退让成元素指针 str*(级数 2),实参仍是 [N]str 的数组地址(级数 1)→ _ptr_cast_reason 先查级数就判 PC_DEPTH,_pointee_convert_reason 里那条既有数组退让豁免成了死代码。元素为 i8 时级数不变所以一直没暴露,元素本身是指针才炸(现场:includes/tomllib _parse_key_path 的 segs)。
2026-10-08 20:03:42 +08:00
TermiNexus 1dd53ebff5 feat(gen): M3 owned slife 登记机制(函数级 scope guard 的底座)
登记本函数内由 new heap/memgr slife 直接绑定的变量 alloca,提供 Reset/Register/Owned/MarkAliased/EmitFrees/ReleaseOld 六个 API,供出口插桩与覆盖释放使用。

为什么是函数级而非块级(已核实):Stmt 层(if/while/for/with)不调用 enter_scope/exit_scope,局部存储一律入口块 alloca(函数级寿命)——块级释放会让"块之后仍可达的变量"变悬垂。别名(q = p)在覆盖释放时保守跳过(宁可泄漏不悬垂),函数出口释放不受别名影响。

本提交仅机制,尚未接线(无行为变化);下一步接 4 处钩子:函数入口/收尾、translate_return 的 3 个 build_ret、赋值处的登记与覆盖释放、以及 §4.3 手动 free 判据。
2026-10-08 18:41:52 +08:00
TermiNexus 09b5bf5ac3 docs(gen): _translate_new_expr 注明缺省规则(stack 默认 slife)
缺省规则(§3.2 / D3):来源缺省 = heap;生命期缺省由来源决定——stack → slife(故 new stack T(...) 不写 slife 也等效),heap/memgr → hlife。生命期在 life_of_expr 按同一张表落地(LIFE_FRAME / LIFE_HEAP)。
2026-10-08 18:21:00 +08:00
TermiNexus 9e55e909a8 feat(gen): LifeTag 接到 new 描述符;vpi 文本缓存收敛为单一入口
life_of_expr 新增 ASTKind.New 分支(§2.3 矩阵):stack→LIFE_FRAME;heap/memgr 的 slife 同样按帧生命期(作用域末 RAII 释放,逃逸必悬垂);hlife→LIFE_HEAP。于是 new stack slife Foo() 的结果被赋给变量/返回时会被既有汇点判据 fail_if_life_escapes 拦住(该判据本来就是生命期驱动,无需再加语法特例)。

VpiReader 新增 _cache_vpi_annots_text 单一入口(ret/param/逃逸摘要一次喂齐),两个 vpi 加载点都改调它:从结构上消除"某个加载点漏喂某类缓存"这类静默退化(本次 EscapeXmodTest 漏检的根因类别)。
2026-10-08 18:19:15 +08:00
TermiNexus 8a98051759 fix(gen): new 的分配尺寸常量改用 const_int64,修 mlir-opt 校验失败
ConstInt 的第 4 个参数是常量在 IR 里的文本(不是注释/名字),此前传 "newsize" 会打印成 llvm.mlir.constant(newsize : i64),mlir-opt 报 expected attribute value。已改用项目通用的 mlirlite.const_int64(pool, size_bytes)。
2026-10-08 18:18:03 +08:00
TermiNexus c77d38e923 fix(gen): new heap/memgr 的尺寸改用 ClassGenEnum._get_type_size,修结构体报 cannot determine the size
原先调用 TypeGenResolve.get_type_size_bytes,它只解析 typedef/标量,结构体一律返回 0,于是 new heap Point(3,4) 在语义期 fail-fast。

改用语言级 T.__sizeof__() 的同一实现 ClassGenEnum._get_type_size(struct_ty),保证 new 分配的尺寸与源码手写 __sizeof__() 完全一致(含对齐/填充)。
2026-10-08 18:07:28 +08:00
TermiNexus 14920c0e1e fix(vpi): 逃逸摘要在所有 vpi 加载点都喂缓存,修 EscapeXmodTest 跨模块漏检
_ensure_vpi_ret_annot_loaded 开头有"本模块 ret 注解已缓存则直接 return 1"的短路:若某模块先经 _ParseVpiIntoModuleImpl(stub 注入)加载,而该处只喂了 ret/param 缓存、没喂逃逸缓存,后续跨模块逃逸查询就会永久查空。现场:EscapeXmodTest 漏检——box.vpi 里 `# esc=2 ret=0` 落盘正确,查询侧却命中不到。在该加载点补 _cache_vpi_escape_annots_text。
2026-10-08 17:58:07 +08:00
TermiNexus 64502471e1 feat(gen): M2.4 new 描述符语义落地(stack/heap/memgr 三来源)
CallGenCtor 新增 _translate_new_expr:只替换取存储一步——stack 用帧内 alloca、heap 用 malloc(sizeof)+bitcast、memgr(x) 用 x.alloc(sizeof)+bitcast;其余构造序列(__before_init__ → __init__ / 字段赋值)用合成 Call 复用原路径,因此 __init__ 的逃逸检查对 new 同样生效;结果恒为 T*。

文件级 _g_new_storage 承接描述符算出的存储,两个 ctor 入口的默认 alloca 改为有则复用;_translate_new_expr 前后保存/恢复,支持嵌套 new。

§2.4:stack+hlife 语义矛盾 fail-fast;memgr(x) 拿不到管理器类 fail-fast。ExprGenMixin 分派 ASTKind.New。
2026-10-08 17:45:39 +08:00
TermiNexus cd68bb62ac feat(gen): M2.3 v3 跨模块逃逸摘要 + 全局赋值判据双保险
【v3 跨模块摘要】Converter._PyiFunction 在每个 def 签名行尾写 `# esc=<mask> ret=<ret>`(仅非零时写);VpiReader 新增逃逸摘要缓存(键 {sha1}.{Class}.{method} / {sha1}.{func},值打包 mask|ret<<32),与返回注解缓存同一次加载解析,并加"模块已加载"备忘 + 键前缀过滤避免拖慢编译;StructGenMixin 抽出 compute_escape_summary 供登记与落盘共用;CallGenBase.lookup_escape_via_vpi 按本类 + MRO 祖先模块 sha1 查询,方法/构造器/模块级函数三处调用点在本地表未命中时回退查 vpi。

【修复】Converter._PyiEnsureEscScratch 补 global(漏写导致全局仍 NULL,box 算成 0+520 → ASan WRITE on 0x208);函数内写"本文件已声明的模块全局"且无 global/nonlocal 时必须 fail-fast——原判据只看 SymTab.Root.Vars,部分时序看不到模块全局而静默建局部影子;新增 FileVarNames(register_file_funcs 预扫收集顶层变量名)+ is_file_var() 兜底,普通赋值与注解式重声明两处都接上。
2026-10-08 17:28:06 +08:00
TermiNexus 9f98e683a9 feat(gen): M2.3 覆盖面扩展——类方法与嵌套函数的逃逸摘要
摘要键约定:模块级/嵌套函数用裸名(与调用点 func_name 一致),类方法用 "Class.method",查询沿 MRO 回退;刻意不回退裸名,避免方法名与同名模块函数(error/init 等)串味产生误报。

register_file_funcs 登记类方法与嵌套函数摘要,v2 传递闭包相应覆盖(_RescanEscapes 拆为 _RescanOne/_RescanNested/_RescanClassMethods)。

调用点新增:_translate_method_call 的方法路径检查(形参下标含 self,用户第 ai 个实参对应 bit ai+1)、_call_method_on_ptr 的 __init__ 构造器检查(self.f = p 的逃逸;__new__ 形参可能与 __init__ 共享并裁剪,不查)。

已知边界:摘要表按文件重置,跨模块查询待 v3;_PropagateCallEscapes 只处理裸名被调者,方法调用链的传递待补。
2026-10-08 16:55:34 +08:00
TermiNexus b03e55bb7e fix(gen): 类归属不采信星号导入候选,收口 fatal_error 返回类型与默认值补全
TypeGenResolve._class_true_def_sha1 / CallGenBase._lookup_reexport_from_vpi 新增 allow_star 参数:类归属只采信具名 re-export。此前 `from X import *` 候选被当成类的真实定义模块,而几乎每个文件都 star-import stdint,导致 TypeGenErr 前向声明被建成 "stdint.TypeGenErr";该名字在 StructTable 里只剩一个错误 sha1 的条目,fatal_error 的跨模块返回类型解析全部落空并 fail-fast。

TypeGenErr.fatal_error/fatal_type_error 显式 -> int(留空会解析出退化类型 i0,命中 CallGenCall 的 i0 守卫);_fail_method_ret_ty 增加失败上下文转储并并入同一条 VLogger.error(批处理下 error 即 sys.exit,其后代码全是死代码)。

CallGenMixin 模块级调用:默认值已知但重建不出时 fail-fast,不再静默产出操作数不足的 llvm.call(只会被 MLIR 校验拦下);AssignGenMixin 两处 VLogger.error 补上 category 实参。

M2.3 v1b 收尾:逃逸摘要表与扫描临时区改为只分配一次、传递闭包扫到不动点、u64 改 sizet;VpiReader 不再 free 跨阶段引用的 src_sha1(heap-use-after-free)。
2026-10-08 16:38:35 +08:00
TermiNexus 1fb0f4a4ea feat(gen): 逃逸摘要传递闭包(M2.3 v2)
- walker 认值位置上的调用(Return/Assign/AnnAssign/Expr 的 value 为 Call)。 - 传递规则:g(p,...) 中 g 会逃逸的实参位若绑到本函数形参 p,则 p 也逃逸。 - 注册完再重扫 2 遍(单调 OR-in)。已知:超过 2 层的链仍可能漏,真正收敛需改为扫描到不变为止。
2026-10-08 13:47:54 +08:00
TermiNexus 52fd89decf feat(gen): 调用点逃逸检查(M2.3 v1b 步骤4)
translate_call 里按被调者摘要核对实参:第 k 位为 1(该形参会逃逸/被返回)的形参位上,实参若交出本帧栈地址(&local / 局部数组退让)即报 LifetimeError。此处拿不到被调者形参类型,故传哑指针类型,仅用于让 life_of_expr 判定裸局部数组名处于指针位置=退让。
2026-10-08 13:41:00 +08:00
TermiNexus 4ce577d8fb feat(gen): 函数生命期摘要表 + 逃逸 walker(M2.3 v1b 步骤1-3)
- StructGenMixin:EscTable/EscCount(追加在类末尾,避免偏移漂移);register_file_funcs 每文件重置并逐函数算摘要。 - 逃逸 walker:return p / global 赋值 / obj.f 赋值,递归 if/while/for;不进嵌套 def/class;形参访问逐级显式重转(AST 退化)。 - 表内只存 u64 名哈希(djb2-64),不解引用字符串指针,消除槽内坏指针导致的 strcmp 崩溃。 - 表从 PermPool 分配并 memset。 - GenApi:get_param_escapes/get_ret_borrow 查询入口。调用点检查(步骤4)待接。
2026-10-08 13:29:28 +08:00
TermiNexus a6e1957768 feat(gen): 控制流汇合 Meet(M2.2)
- VarGenMixin:life_capture/life_restore/life_join(作用域链快照,池上 [count,(entry,life)…] 数组)。 - StmtGenCifMixin.translate_if:进分支前快照、then 末快照、else 前复位、出口按 min 合并。 - StmtGenMixin:while/for 统一在语句分派处 capture→run→join(一处覆盖全部迭代形态与解糖,含 0 次迭代路径)。
2026-10-08 12:35:59 +08:00
TermiNexus ec47401151 feat(gen): 生命期标签(LifeTag)与借用传播(M2.1)
- GenTypes:新增 define LIFE_VALUE/FRAME/HEAP/STATIC/UNKNOWN 与 VarEntry.Life。 - VarGenMixin:set_var_life/get_var_life/life_meet;add_var 初始化 Life=UNKNOWN。 - AssignGenMixin:life_of_expr(&local、数组退让、变量 Life 读取)、propagate_life(赋值传播)、fail_if_life_escapes(汇点检查,LifetimeError);在 AnnAssign 与单 Name Assign 处挂传播。 - GenApi.fail_if_stack_addr_escape 改为薄壳并委托 fail_if_life_escapes;原 (a)(b) 语法判据并入 life_of_expr。于是 s: str = l; return s 被拦截。
2026-10-08 12:22:10 +08:00
TermiNexus 7070bc1608 feat(gen): 栈地址逃逸检查(LifetimeError)、按值返回聚合、标量→聚合 fail-fast
- GenApi.fail_if_stack_addr_escape:禁止把本帧栈地址写出帧外(显式 &local,以及局部数组名退让到指针目标,如 return arr with '-> char*');裸聚合名 + 同型聚合目标是值拷贝,不在其列;err_type 记为 LifetimeError。 - StmtGenCifMixin.translate_return:按值返回聚合时,对被指类型与返回聚合类型同一的指针补 load(与多返回值 struct 的 ret 同形),修复 return 局部数组报 CastError。 - ExprGenBase.coerce_to_type:聚合目标不接受标量(旧行为落成 store i32 0, ptr,只写首元素字节并抬高对齐),改为编译期 fail-fast。
2026-10-08 12:10:13 +08:00
TermiNexus 2ab6342d3c fix(gen): arg 糖支持转发形态的 va_list
_translate_arg_sugar 原本在 trans._va_list_ptr 为 None 时(非变参的 vprintf 式包装函数,如 vsnprintf(buf,size,fmt,args))直接返回 0,转发下来的 va_list 因此读不到;现增加回退:按名 args 取形参 alloca 并 load 出槽内的 va_list 指针(自身变参形态仍用 trans._va_list_ptr,不 load)。点号形式 stdarg.arg(...) 经 ExprGenBase.get_func_name 取属性短名,已天然命中同一分派,未改动。
2026-10-07 20:15:40 +08:00
TermiNexus c20bd4734b feat(gen): 值引用自动取址接入虚分派/构造/属性/静态等全部方法调用路径
CallGenBase 新增 _vpi_auto_addr_mode(把原先埋在 _coerce_args_to_def_params 里的 vpi 注解解析抽成唯一实现,两处共用)与 _apply_auto_addr_to_call_args(同模块先查 AutoAddrModes,跨模块回退 vpi 注解)。接入原先完全不查表的路径:虚分派(原在 vpi 取址前 return)、_call_method_on_ptr 覆盖的构造 __new__/__init__(含泛型)与 dunder 分派、属性 getter/setter/deleter、静态/类方法、super()、FakeDuck;属性 setter 由 AssignGenMixin 传入 rhs AST 以支持 &T 取左值地址。
2026-10-07 19:59:52 +08:00
TermiNexus 5c1b882169 chore(gen): c.CIf 家族移除报错覆盖 CElif/CElse/CEndif,注释口径统一为 cif
ExprGenName / CallGenDispatch 的「已删除拼写」守卫原本只认 CIf/CIfdef/CIfndef,现补齐 CElif/CElse/CEndif 六个(与 TPC 侧一致),报文同步;StmtGenCifMixin 的头注释、docstring 与状态注释把语句统一称作 cif,内部标记仍写作 c.@CIf 族。
2026-10-07 19:10:05 +08:00
TermiNexus 2fb648d68a feat(lang): 新增 cif/celif/celse 条件编译与值引用注解 &T/&temp T,assert 改译 testcheck.check
cif 三件套:StmtGenCifMixin 负责常量求值(含 defined(模块宏) 的 Attribute 形式),链式错配与含运行时变量一律 fail-fast;删掉 c.CIf 家族。值引用:TypeGenResolve 让 CRef/CTempRef 与 t.CPtr 走同一解析路径(&void 折叠为 i8*,删除 t.CNeedPtr),CallGenBase 在调用点按形参模式自动取址(ref 只接受左值,右值 fail-fast;temp 物化临时栈),跨模块经 VpiReader/StubMergerBuild 传递模式。assert:StmtGenMixin 合成 testcheck.check(cond, None, msg),Translator 检测到 assert 时自动注入 testcheck 导入(带去重与防环),深递归守卫改用 stdio.printf + stdlib.exit(Translator 无 Gen 成员)。
2026-10-07 19:03:16 +08:00
TermiNexus ce50daa87a refactor(build): 构建产物目录去掉 _mlir 后缀(temp/output)
TempDir/OutputDir 回归 {BuildDir}/temp 与 {BuildDir}/output,与 TPC 及项目脚本预期一致;mlir_dir_suffix() 仅保留给 includes 二进制缓存(其布局为 sha1 子目录,与 TPC 的扁平 obj 缓存不同,合并会混淆)。
2026-10-07 19:03:02 +08:00
TermiNexus 3b4bd1c6dc fix(gen): 跨模块函数作值时不再伪造同名外部全局
`serial.puts` 这类模块属性指向的是函数(elf.vp 的 _resolve_symbol 返回它作
函数指针),属性兜底路径却为它造了一个 llvm.mlir.global external @<sha1>.puts,
与已有的 llvm.func @<sha1>.puts 同名,mlir-opt 报 redefinition of symbol named。
改为在伪造之前先精确匹配函数符号(目标模块 sha1 前缀过的函数名与 Function.Name
逐字符相等,避免误命中他模块同名函数),命中则返回函数符号引用本身
(@<mangled>,类型 Ptr(Func),与同模块 &f 分支同一形态),并让属性读路径跳过
对该值的 load(函数地址即值,load 会去读函数体)。
EOF
2026-10-07 15:59:54 +08:00
TermiNexus edca6c3760 chore(detsite): 类型文本 4096 缓冲补插桩
19 处 TypePrint(..., 4096, ...) 对应的 pool.alloc(4096) 全部改用 alloc_tagged
(AssignGenMixin/ClassGenVTable/FuncGenDef/ExprGenBase/ExprGenCdef/CallGenLeafKit/
TypeGenCType/TypeGenResolve/CallGenCBuiltin/ExprGenOps),tag 33..51 见 memhub.vp。
detection==0 时 alloc_tagged 直接转发,无布局与性能变化。
2026-10-07 15:16:52 +08:00
TermiNexus f3628a797a fix(global): 修复存储结构体字段零值文本的池缓冲溢出
_stg_field_zero_text 分配 160 字节,却用 snprintf(buf, 576, ...) 写出,长类型文本
(如 !llvm.array<8 x i8> zeroinitializer)可越界 415 字节,写穿相邻空闲块的元数据,
触发 [MEM] _fl_pop / [FREEFRAME] 空闲块被写穿(内核编译在 PhaseA 45/50 处中断)。
按实际类型文本长度分配(strlen+24),并把 snprintf 上限与分配对齐。

本文件同时含 3 处 DETSITE 插桩(elem_ty_str_n / ty_str / ty_buf_v)。
2026-10-07 15:16:47 +08:00
TermiNexus 12b15958c6 feat(global): 支持模块级符号地址初始化器
新增 init_kind=5(符号地址):分类阶段识别 UnaryOp(AddrOf, Name),不再把它
误送进整数常量提取器;发射阶段取模块作用域里该全局的引用文本(@mangled /
@mangled 带引号两种形式)作为初始化器,MLIR 层渲染为 llvm.mlir.addressof。
类型/操作数/引用目标不合法时按 fail-fast 报错。

复现:_v: int = 1 / _p: rawptr = &_v
      修复前报 CDefine value extraction failed(该分支原假设所有 UnaryOp 都是负数/取反字面量)
2026-10-07 14:54:44 +08:00
TermiNexus 5993affd9c fix(type): const 等限定联合不再被误判为 str*/bytes*
_is_ptr_element_annotation 原先只要注解联合里出现 str/bytes 就返回 1(按 i8** 处理,
8 字节元素步长),于是 const str(t.CConst | str,实际仍是 i8*)的下标取到的是指针。
现在要求联合必须带 CPtr 标记(真正的 str*/bytes*)才判定;模块级全局的同一处逻辑
(TranslatorGenMixin)同步收紧。新增 _annotation_has_cptr_marker 供两处复用。

复现:def F(p: str) -> int: / path: const str = p / ch: char = path[i]
      -> CastError 指针转整数(形参位置的 const str 一直正常,仅局部/全局错)
2026-10-07 14:35:25 +08:00
TermiNexus fb3d3787e6 refactor(build): linker.cmd 由绝对路径改为裸名,--asan 提示文案去硬编码
- project.vpj / repl.vpj 的 linker.cmd 改为 clang++.exe,交由 PATH 解析

- main.vp 的 --asan 运行库提示改用占位符路径,并说明 clang64 根目录由 PATH 自动探测
2026-10-07 11:25:38 +08:00
TermiNexus 36d23d2f42 feat(build): 实现 Qt 运行时依赖部署(runtime.qt6_path / qt6_dlls / qt6_plugins)
背景:project.vpj 里的 runtime 块此前只是声明(编译器不读取),Qr 程序运行时报 Could not find the Qt platform plugin "windows"。

实现:链接成功后(Phase2 的 PhaseC 成功点)调用 BuildPipeline.deploy_runtime_deps(exe_path)——把 qt6_dlls 各项从 <qt6_path>/bin 复制到 exe 同目录;把新增的可选字段 qt6_plugins 各项从 <qt6_path>/plugins 复制到 <exe 目录>/<相对路径>(父目录不存在则 mkdir)。Config.vp 新增 Qt6Path/Qt6Dlls/Qt6Plugins 读取(口径同 LinkerStaticLibs)。

fail-fast:声明了但源文件不存在 / 复制失败 / mkdir 失败 => VLogger.error 并中止构建;三字段全未声明则整段跳过。

验证:重编 SUCCESS(210.11s);ViperMNIST 产物含 7 个 Qt6*.dll + platforms/qwindows.dll,实跑 stderr 无插件报错;ViperAIChat / ViperQtHelloWorld 同样 OK 并完成部署。
2026-10-07 09:08:35 +08:00
TermiNexus 848dc0ab38 fix(compiler): 跨模块调用补全 int→ptr 转换与别名默认参数;类型文本缓冲 512→4096
1) CallGenMixin.vp(跨模块 extern 实参提升):指针目标形参改为无条件走 coerce_to_type——原守卫按整数位宽比较,而指针类型的 bits 恒为 0,整段提升被跳过,致 VirtualAlloc(0,...) 以 i32 传入指针形参、mlir-opt 报 operand type mismatch(ViperAIChat 失败)。

2) CallGenMixin.vp(默认参数限定键):改用 StructGen._resolve_module_sha1 解析模块 SHA——原只精确匹配模块名,import x as y 这类别名取不到真实 SHA,默认值表落空、跨模块调用实参数目不符(ViperRequestsTest 失败)。

3) 11 个文件的 TypePrint 类型文本缓冲 512→4096(分配与传入 size 成对修改):48 字段类(如 MNIST 的 Net)的 MLIR 结构体类型文本超过 512 被截断并 fail-fast。
2026-10-07 08:48:09 +08:00
TermiNexus 7a9bc5ae75 chore(messages): TransPyV 运行时中文字符串英文化
把 TransPyV/App 下 57 个文件的运行时中文字符串(编译器报错/告警/日志、CLI 帮助与横幅、REPL 提示、Phase1/Phase2 流程日志、Codegen 各 Mixin 的类型与调用报错、Stub/Vpi 诊断等)共 1091 行译为英文。

口径:只改字符串字面量内部文字,行数零变化(+1092/-1092);printf 格式符与转义序列多重集合校验不变(_strcheck 异常 0);译文不引入撇号以免干扰单引号字面量解析。验证:三重重编 SUCCESS(136.20s)+ 全量 77/77 顺序回归通过 + VET 编译 SUCCESS / --search-paths 7 条 exit 0;新产物 help/日志输出已为英文。
2026-10-07 00:03:58 +08:00
TermiNexus 0cf80632c1 feat(gen): typedef 源文本归一 C 式 const/T*(R16)
TPV 的 typedef 注册走裸源码扫描,需与 TPC 同形:_parse_typedef_line 把右值的前导 const/volatile 归一为 t.CConst | / t.CVolatile |、末尾 * 归一为 | t.CPtr;register_ctypedef_from_source 的缓冲 320->512 以容纳展开后的文本。
2026-10-05 20:36:40 +08:00
TermiNexus 374921623b fix(gen): 补全新语法 &/* 的取地址与解引用路径
(*p).attr 属性基址:_get_cderef_attr_base 增加 UnaryOp(Deref) 分支(读写都要求 GEP 指针本身,而非加载后的结构体值)。_is_addrlike_call 增加 AddrOf/Deref 分支:指针型右值不应走模块全局的『静态存储对象拷贝』分支。
2026-10-05 19:58:45 +08:00
TermiNexus df2de5ecfa refactor: 去 t.CPtr(&a) 冗余包装(R15)
TransPyV 4 文件 / 7 行:Phase/BuildPipeline.vp、core/Gen/AssignGenMixin.vp、core/Gen/TranslatorGenMixin.vp。验证:自举三重重编 SUCCESS + 全量 77 项顺序回归通过。
2026-10-05 17:55:52 +08:00
TermiNexus cd55a7e214 refactor: rawptr[T] -> T*(R14)+ 修 REnum 前向占位未就地填充
R14:rawptr[X] -> X*(56 文件 / 834 行改动);保留 6 处 rawptr[t.CPtr](存纯裸指针的规范例外)。 修复:class X(t.REnum) 的变体字段写成自引用 X* 时,字段解析在变体扫描期经 ensure_forward_class 建了一个 opaque 前向占位;末尾 register_struct 按 Ty 指针去重会再建第二个同名 entry,而 find_struct 取首个(占位,0 字段)→ X.__sizeof__() == 0,报「已注册但大小为 0(字段链表未填充)」。改为注册前若命中同模块前向占位则就地填充(与 ensure_forward_class 文档口径一致)。TPC 无此问题,属 TPV 侧缺口。 验证:自举三重重编 SUCCESS;全量 77/77 顺序回归通过(修复前卡在 LLvmLiteTest)。
2026-10-05 17:38:28 +08:00
TermiNexus aeb4e8e54c refactor(gen): R12 收尾同步——12 处计数 while 循环改 for range
与 includes 同步:<= 条件转 range(expr + 1),并放宽初始化行的紧贴要求(同块内最近一次写即为初始化即可)。其余保守守卫不变。

验证:bootstrap 三重重编 SUCCESS;全量 77/77 顺序回归通过。
2026-10-05 16:36:36 +08:00
TermiNexus fb53f88893 refactor(gen): R12/R13 同步——下标 while 循环改 for range、去 c.Set
与 includes 同步执行两批源改写:R12 把「i: int = 0 与 while i < N: … i += 1」形式的计数循环改写为「for i in range(N):」;R13 把 c.Set(*p, v) 迁移为 *p = v、c.Set(a, b) 迁移为 a = b(c.Set 是正在退化的路径)。

验证:bootstrap 三重重编 SUCCESS(无 vast_c 解析回退);全量 77/77 顺序回归通过(WebTest 首轮网络超时,单独重跑通过)。
2026-10-05 15:33:58 +08:00
TermiNexus a7b8118ce8 fix(call): c.Set 支持 Starred 解引用目标(*p 在实参位被解析为 Starred)
新规范把 c.Set(c.Deref(p), v) 改写成 c.Set(*p, v),而实参位的 '*' 由 ast/parser.vp 解析为 Starred(value=p, ctx=Load),translate_c_set 只认 c.Deref/Call 与 Name 形态 ⇒ 整条 store 被静默丢弃。

后果:zlib 侧 includes 全部 47 处 c.Set(*out_len/*out/*length/*cap/*buf/*best_dist/*p_done, ...) 失效 —— 压缩结果长度恒为 0(decompress 报 Z_DATA_ERROR -3),ZlibTest 运行期 0xC0000005。现按 UnaryOp/Deref 与 Starred 两种形态统一走解引用写入,并对非指针目标 fail-fast。
2026-10-05 14:35:50 +08:00
TermiNexus 87b24c2445 fix(type): resolve_typedef_or_map 补齐裸内建别名,修复 int(x, t.CPtr) 类型解析
裸名 str/bytes/int/char/bool/float/double/void 在 map_t_type 中只有 t.CXXX 拼写,导致 _tc_h4 的双参指针包装分支拿不到 bare_ty,落回外部函数返回类型兜底并 fail-fast(argparse.vp:93 c.Deref(int(uint64(ptr), t.CPtr)) 报 TypeError: int)。
2026-10-05 14:14:29 +08:00
TermiNexus 0f4aeeceff docs(tpv): R11 注释与 docstring 英文化并压缩到 2 行内
按 wiki/新规范:注释只讲作用、不掺杂历史原因、避免超长、尽可能英文(含三引号 __doc__)。全文 79 个 .vp:# 注释 16269 行(中文 13808)与 docstring(中文 1381 处)全部改英文;超 2 行的注释块(2093)与 docstring(397)全部压到 2 行。运行时可见文本未动(fatal_error/VLogger 的中文报错文案、stdio.printf 的横幅格式串)。校验:屏蔽注释与三引号后逐行比对各文件代码骨架,0 漂移。验证:自举重建 153.48s 通过(跨模块内联完成);TPV 顺序回归 77/77 全通过。
2026-10-05 13:38:22 +08:00
TermiNexus a2874a14e6 chore(tpv): 移除 --allow-multiple-definition 兜底并同步缺省链接参数
main.vp 的缺省 linker flags 与 ViperBuild 内置 _c_linker 默认值去掉 -Wl,--allow-multiple-definition,与 project.vpj 同口径;Phase2 三处引用该 flag 的注释改为描述链接器选 main / 链接前按路径去重的实际机制。重建验证:跨模块内联优化完成、链接成功 139.17s。
2026-10-05 09:22:58 +08:00
TermiNexus e79ea010b0 fix(tpv): 消除 PYI_BUF_SIZE 跨模块重复全局并去掉 --allow-multiple-definition 兜底
Phase1 与 BuildPipeline 各自把 PYI_BUF_SIZE 声明为模块级全局,模块级全局走裸名符号(类 C extern 全局语义),两模块各发一份强定义 @PYI_BUF_SIZE,llvm-link 报 symbol multiply defined 并回退到单独编译。改为编译期宏 define PYI_BUF_SIZE = 262144(与同文件 define SRC_BUF_SIZE 同口径,不产生符号),同步更新引用该符号的非 CDefine 示例注释。project.vpj 移除 -Wl,--allow-multiple-definition 兜底。验证:跨模块内联优化完成,最终链接无该 flag 成功(161.70s);TransPyV 自举产物顺序回归 77/77 全通过。
2026-10-05 09:16:12 +08:00
TermiNexus f2ef1bb240 refactor(tpv): 新语法 R10 括号内隐式续行并一行(1140 处 / 67 文件) 2026-10-05 07:14:14 +08:00
TermiNexus 4a685d478f refactor(tpv): 新语法 R10 单语句块并一行(7722 处 if/for/while 单语句体) 2026-10-05 03:10:55 +08:00
TermiNexus f923dc4d8d chore(tpv): 删除已废弃的 Test 目录(回归测试保留在仓库外 Test/) 2026-10-04 23:10:44 +08:00
TermiNexus 30a3da4cd4 refactor(tpv): 新语法 R10(反斜杠续行合并)+ 回退 R5(语义不等价)
- 反斜杠续行合并为单行(304 处),呼应"尽可能不换行"。
- 回退 c.DerefAs(p, v) → *p = v 两处(StructGenMixin):原写法是 bitcast 后
  store 指针位,新写法按 pointee 值类型赋值,类型不符(i8* → List 值),
  语义不等价,保留 c.DerefAs。

种子自举通过。
2026-10-04 22:56:27 +08:00
TermiNexus 185a2196ca refactor(tpv): 迁移新语法 R1/R2/R7/R8/R3/R4/R5(指针 T*、短别名、define、&/*、__asm__)
按 wiki/新规范:
- T | t.CPtr → T*(无 | 标准)
- t.CInt/t.CUInt64T/t.CChar/t.CDouble 等 → int/uint64/char/double 等短别名
- X: t.CDefine = Y → define X = Y
- c.Asm → __asm__,c.AsmInp/c.AsmOut → asminp/asmout,t.ASM_DESCR. → ASM_DESCR.
- c.Addr(x) → &x,c.Deref(p) → *p,c.DerefAs(p, v) → *p = v

种子自举通过(96s)。
2026-10-04 22:49:01 +08:00
TermiNexus 9717ce0107 feat(traceback): 运行期调用栈显示 callsite 行号(编译器旁文件 + 链接后 objdump 归一)
MLIR 后端下 mlirlite 只记录 DI 不输出、且 mlir-translate 不产 !dbg(实测),
导致我方 .vp 函数在最终 exe 无任何 .debug_line(<exe>.stackmap 只剩 mingw CRT
的 C 源记录)→ 用户代码帧只能显示 line 0。

改为 Route A:--traceback 时编译器记录每函数(F)与每次调用(C,含调用点行),
写 <sha1>.vpcall;链接后 WriteStackMap 用 llvm-objdump 反汇编,把 callq 的
返回地址 RVA 按 (调用方,被调方,出现序号) 匹配到调用点行,并在函数入口写定义行
兜底;合并进现有 .stackmap 格式。运行期 traceback.vp 不改。

验收:PtrOpProbe --traceback 生成 427 条我方记录(抽查 main 的 malloc/printf
等行号与源码逐一吻合);非 --traceback 构建零旁文件、产物与运行结果不变。
2026-10-04 22:38:02 +08:00
TermiNexus 4236127956 fix(tpv): f-string 隐式 snprintf 声明形参 buf/fmt 用 i8* 而非裸 i8
f-string 降级为 snprintf 时按"隐式提供"原则在使用点补 declare,但三段固定
参数里的 buf/fmt 误写成数组元素类型 i8(应为指针 i8*),与调用侧传入的
buf_ptr/fmt_ptr(i8*)不匹配 → MLIR 'llvm.call' operand type mismatch
'!llvm.ptr' != 'i8'(ZlibDbg 编译失败根因)。改为 Ptr(i8)。
2026-10-04 20:51:38 +08:00
TermiNexus 68cd5f2f90 fix(tpv): __new__ 截断条件放宽到 nn_pc>=1(源侧 __new__ 可无参数)
OopInheritTest 报:
  error: 'llvm.call' op incorrect number of operands (4) for callee (expecting: 1)
      %16 = llvm.call @"...Vec3.__new__(%0, %5, %10, %15) : (!llvm.ptr, f64, f64, f64) -> !llvm.ptr

源码 `def __new__() -> 'Vec3' | CPtr:` 无参数(TransPyV 只注入 self),
__init__(self, x, y, z) 才收 x/y/z。上一提交的截断条件写成 nn_pc > 1,
nn_pc == 1 时被跳过 ⇒ 仍多传 3 个。放宽为 nn_pc >= 1(裁到只传 self)。

验证(均已跑通):
- OopInheritTest:PhaseB 编译完成 → PhaseC 产出 OopInheritTest.exe → 完成
- SlotsBugTest:同上(无回归)
2026-10-04 20:15:37 +08:00
TermiNexus 58cfe2346e fix(tpv): __new__ 调用按被调方签名截掉多余尾参
mlir-opt 报:
  error: 'llvm.call' op incorrect number of operands (8) for callee (expecting: 7)
      %245 = llvm.call @a959d1abdb31a471.FunctionDef.__new__(%35, %239, %240,
             %241, %242, %243, %is_async, %244) : (...) -> !llvm.ptr

定义/声明侧(ast/stmts.vp vpi 核实):
  FunctionDef.__new__(self, pool, name, args, decorator_list, returns, is_async)
      = 7 个形参(非-self 6)
  FunctionDef.__init__(..., is_async, type_params = None)
      = 8 个形参(非-self 7,多出的 type_params 有默认值)

根因:构造器路径把 ctor 实参**全量**转发给 __new__,而 __new__ 的签名比
__init__ 短(无 type_params)⇒ 调用点传了 7 个非-self 实参,声明只收 6 个。
(临时探针实测:mdf_param_count=6 正确、封参逻辑未触发,extra_count=7 才是不匹配源。)

修复:_call_method_on_ptr 中对 method_name == "__new__" 且已找到 declare 时,按被调方
非-self 形参数截掉多余**尾参**(这些尾参 __new__ 本就不接收,语义上由 __init__
消费;__new__ 只负责分配存储)。探针已删除。

验证:python ./TransPyC/Projectrans.py --project ./TransPyV/project.vpj --clean
      --rebuild-includes --clear-cache 重编通过;
      ./TransPyV/output/TransPyV.exe --project ./Test/SlotsBugTest/project.json
      PhaseB 编译完成 → PhaseC 产出 SlotsBugTest.exe → TransPyV 完成。
2026-10-04 20:09:21 +08:00
TermiNexus 6a6cae2a44 fix(tpv): 模块限定调用优先用 vpi 定义侧形参类型,Win32 白名单降为兜底
mlir-opt 报:
  error: 'llvm.call' op operand type mismatch for operand 2: 'i64' != 'i32'
      %5 = llvm.call @VirtualAlloc(%c6, %2, %3, %4) : (!llvm.ptr, i64, i64, i64) -> !llvm.ptr

声明(真值):extern def VirtualAlloc(VOIDPTR, sizet, ULONG, ULONG) -> VOIDPTR
           ⇒ llvm.func @VirtualAlloc(!llvm.ptr, i64, i32, i32)

根因:调用点 found_func 为 None 时用 `_get_external_func_param_ty` 硬编码白名单,
它把 Win32 API 的**所有**非指针参数一律当 i64(注释依据"DWORD/ULONG 在 stubs 里
被映射为 i64"的旧口径),而现实中 ULONG → i32 ⇒ 字面量被 sext 到 i64,与声明
i32 不匹配。

修复:CallGenMixin 的参数 coerce 分支(found_func is None)改为**模块限定调用
先用定义侧 vpi 形参注解**(`_resolve_vpi_param_annot_seg`,模块级函数段下标 ==
形参下标)取真实类型;拿不到注解才回退白名单。CallGenLib 的该函数补注释,
标明它是**兜底专用**且 Win32 口径已过时。
2026-10-04 18:17:51 +08:00
TermiNexus b7e4861e6c fix(tpv): vpi 返回注解纯 marker(t.State 等)解析为 void,修 void 调用被当 i32
调用侧 `_resolve_vpi_ret_annot_str` 只把 `t.CVoid` 映射为 void,纯 `t.State`
(存储类装饰器标记)会解析失败返回 None → 调用侧回退外部函数白名单默认 i32。

现场(MLIRLiteTest):`includes/stdlib.vp` 的 `extern def exit(status: int)`
在 stdlib 的 vpi 中是 `def exit(status: int) -> t.State: pass`,于是生成
  %6 = llvm.call @exit(%c3) : (i32) -> i32
而模块声明是 `llvm.func @exit(%status: i32)`(void),mlir-opt 报
  'llvm.call' op calling function with void result must not produce values

修复:在 `_resolve_vpi_ret_annot_str` 增加"纯 marker 注解 → void"分支,与
declare 注入路径 `_inject_module_declare` 的 is_pure_marker_rt 判据同口径。
仅**纯** marker(`t.State` / `t.CExtern` / `t.CExport`,可带 t./c. 前缀)
才归 void;`HANDLE | t.State` 等含真实类型的注解仍由段循环返回真实类型。
2026-10-03 21:51:13 +08:00
TermiNexus 94b174c8a0 fix(tpv): 补齐 asm_arg_cap 声明与嵌套数组 element 预算漏改
上一条提交(3426139)中两处改动实际未落盘(编辑动作报告成功但文件内容未变),
导致 TransPyC 编译报 NameError: 未定义名称 'asm_arg_cap'。

- CallGenCBuiltin:补 asm_arg_cap 声明(按操作数个数给足内联汇编实参表容量),
  args_buf 改用该容量分配(此前 4 处用法已改为 asm_arg_cap,声明却缺席)
- TranslatorGenMixin:嵌套数组 element 预算 192 → 768(含子数组类型前缀)
2026-10-03 21:17:41 +08:00
TermiNexus 3426139d03 fix(mlir): TypePrint 输出缓冲按 MLIR 类型长度放大;初始化器文本按类型长度给足
MLIR 类型文本内联具名结构体完整字段体(可达数百字节),旧的短缓冲会截断类型
文本、产出畸形 MLIR(mlirlite 侧 TypePrint 现已对截断响亮报错,此处补齐容量)。

- 10 处 TypePrint 调用点及其缓冲统一放大到 512:
  AssignGenMixin / TranslatorGenMixin / CallGenCBuiltin(×2) / CallGenLeafKit /
  ClassGenVTable / ExprGenBase / ExprGenOps(×4) / FuncGenDef / TypeGenResolve(×2)
- TranslatorGenMixin:
  - _stg_field_zero_text 缓冲 160 → 576(元素 = 类型前缀 + 值)
  - _default_val_const_text 缓冲 48 → 576
  - 嵌套数组 element 预算 192 → 768(含子数组类型前缀)
  - storage 结构体 element 预算 160 → 576
- CallGenCBuiltin:内联汇编实参表按操作数个数分配(原固定 512,一个结构体
  操作数即溢出)、汇编行缓冲按实际长度分配
2026-10-03 21:08:30 +08:00
TermiNexus d0434bf114 fix(repl): 片段导出符号探针改用 MLIR 形态 llvm.func;布局恢复文案改指 {sha1}.meta
- Repl.vp ⑧ 探针由 "define i32 @名(i8* %__repl_ctx__" 改为
  "llvm.func @名(%__repl_ctx__: !llvm.ptr) -> i32"(单端后 dump 出的是 MLIR 文本)
- VpiReader.vp REPL 布局恢复的注释/报错/汇总文案 .stub.ll → {sha1}.meta
  (数据源早已是 meta:_repl_stub_collect_module 读 {sha1}.meta,
   _repl_meta_scan_layout 解析 STRUCT/VTABLE;本次只改文案与注释,无逻辑改动)
2026-10-03 19:58:37 +08:00
TermiNexus cbf76cd468 fix(callgen): text.ll 返回类型回退支持 MLIR 形态 llvm.func
- _CacheModuleRetTyTexts 同时认 define 行与 mlirlite 的 llvm.func 行(含缩进/void 省略 ->/变参)

- 缓存值以 '#' 前缀标记 MLIR 形态,命中时用新增 _ParseMlirRetTyStr 解析

- _ParseMlirRetTyStr 覆盖 none/iN/f16..f128/!llvm.ptr/!llvm.array/!llvm.struct(具名按登记表回查,未登记返回 None 不臆造)

- 背景:单端 MLIR 后 text.ll 无 define 行,回退恒空 ⇒ 指针返回值退回默认 i32 被 inttoptr 截断
2026-10-03 19:45:47 +08:00
TermiNexus 7b3e814524 refactor(backend): 单端 MLIR —— 发射器全树改名 mlirlite 并删除 --backend 开关
- App/Test 的 .vp 中 llvmlite 全部改名为 mlirlite(import、限定名、类型注解、注释)

- 删除后端开关:Config.set_backend/is_mlir_backend/alias_module_name、BackendTarget/BackendIsMlir、CLI --backend、options.backend/options.target 读取,以及 IncludesScanner/StubMergerIR 的模块别名调用

- BuildPipeline 的 mlir-opt -> mlir-translate 降级管线去掉开关门,改为恒执行

- backend_dir_suffix 改名 mlir_dir_suffix 并恒返回 _mlir:TempDir/OutputDir/includes 缓存继续落在 *_mlir,避免复用旧 LLVM 后端的 .ll/obj

- project.vpj 删除已失效的 options.target
2026-10-03 19:26:24 +08:00
TermiNexus 1dcd29db9c fix(phaseb): llc 并行失败按阶段定位,不再一律报 llc 编译失败
- LlcTask 追加 FailStage 字段(0=未失败/1=mlir-opt/2=mlir-translate/3=ASan opt/4=llc),worker 在各步失败时写入

- run_llc_parallel 按 FailStage 打印真正失败的阶段名与对应命令:MLIR 后端下 mlir-opt/mlir-translate 失败此前被误指为 llc(现场 .ll 含 LLVM IR 时 mlir-opt 报 unexpected character,日志却指向 llc)

- 字段追加在结尾,所有 stride/memset 均按 LlcTask.__sizeof__() 计算,既有偏移不变
2026-10-03 19:01:50 +08:00
TermiNexus cda4cca805 refactor(generic): 类型实参符号判定复用 spec_sign_of_annot
- 两处 Call 推断由 SpecSignFromTypeNameSpan 改为 TypeGenCType.spec_sign_of_annot,复用注解侧唯一真源(精确名白名单 + CTypedef 链逐层展开)

- 收益:别名(uint64/size_t/WCHAR->WORD->uint16 等)也能判出符号性,不再是裸白名单的 UNKNOWN
2026-10-03 18:20:42 +08:00
TermiNexus 2ac412c11a feat(generic): 泛型实参推断补齐全部内建标量位宽的精确票名
- 整型按 位宽×符号性 全覆盖:i8 -> t.CUInt8T/char、i32 -> t.CUInt32T/int,i16/i64 维持 t.CUInt16T/t.CInt16T、t.CUInt64T/t.CInt64T

- 浮点补 half -> t.CFloat16T、fp128 -> t.CFloat128T(原仅 float/double,其余返回 None)

- 票名与 TypeGenResolve.map_t_type 的可解析名一一对应;无符号一律不走裸名(避免静默变号),位宽无裸名别名且符号未知时返回 None(宁缺毋错)
2026-10-03 18:19:53 +08:00
TermiNexus f9ba391236 feat(generic): 无符号 i32 泛型实参改发精确票名 t.CUInt32T
- _infer_scalar_type_name_from_spec 对 i32 增加符号性分支:无符号(CUInt32T/CSizeT/CUIntPtrT 等)发 t.CUInt32T,有符号/未知保持裸名 int(位宽无误且与历史逐字一致)

- 修复 CUInt32T 实参经泛型推断被静默变号为 int 的问题
2026-10-03 18:18:21 +08:00
TermiNexus 21436b2690 feat(generic): 泛型实参推断为无裸名别名的整型位宽补精确票名
- _infer_scalar_type_name_from_spec 支持 i64/i16 -> t.CUInt64T/t.CInt64T、t.CUInt16T/t.CInt16T;符号性未知时不猜(返回 None)

- infer_scalar_type_arg_from_llvm_ty 增加 sign 参数:LLVM 类型层无法区分 iN/uN,符号性由被调名源码文本经 SpecSignFromTypeNameSpan 判定

- 泛型函数与泛型类构造两处 Call 推断同步传符号性,修复 GenericOopTest fxi2 第 3 个类型实参(CUInt64T)推断失败
2026-10-03 17:12:47 +08:00
TermiNexus ddb1e6cc2a fix(类型): get_type_size_bytes 补浮点位宽,修复浮点泛型实参的 __sizeof__ 尺寸解析
现象:Test/VectorGenericTest/App/vectortest.vp:32 vector.Vector[CFloat64T] 特化时 vector.vp:12 T.__sizeof__() 报 "NameError: 未定义名称 T(当前泛型上下文: 类型形参 1 个 / 类型实参 1 个)" —— 新增诊断证明上下文是好的(T 能换成 CFloat64T),故问题在 __sizeof__ 快径没接住。

根因:get_type_size_bytes 只用 get_llvm_type_bits 取位宽,而该函数只认整数(Float/Double/Half/FP128 一律返回 0)⇒ 浮点 typedef(CFloat64T 经 map_t_type 已正确解析为 Double)被判"尺寸未知"返回 0 ⇒ CallGenDispatch._tc_h5_sizeof_len_isinst 的尺寸表也不含 CFloat64T(只有 CInt/CDouble/CFloat 等)⇒ 落回"回退 2"把接收者 Name(T) 当实例翻译 → NameError。同文件第 15 行 Vector[CInt] 走整数路径正常,故只有浮点特化暴露(不对称现象由此解释)。

修复:整数位宽为 0 时再取 get_llvm_float_bits(浮点位宽),两者皆 0 才返回 0。
2026-10-03 16:50:42 +08:00
TermiNexus 3a4323e288 feat(generic): 特化上下文自检 fail-fast + NameError 附泛型上下文诊断
背景:VectorGenericTest 在第 32 行 vector.Vector[CFloat64T] 特化时仍报 "NameError: 未定义名称 T"(8af967d 的类模板 tp 名补缺只能覆盖 tp 名缺失那一种,这里 tp 名在场)。静态读完 6 个 _specialize_generic_class 调用点与 ClassGenDispatch 的 __sizeof__ 快径后,无法确定是"上下文没建"还是"建了装不上",故加两处诊断。

1) ClassGenGeneric._specialize_generic_class 在 set_generic_context 之后立刻自检:类型实参个数必须等于模板类型形参个数,且首形参必须能解析成具体类型名;否则 fatal_error 点出类名与特化键(含实参),替代下游 12 行之外那句脱节的未定义名称。

2) ExprGenName 未定义名称报文追加当前泛型上下文(类型形参 N 个 / 类型实参 M 个):形参 0 个=上下文没建,形参>0 且实参 0 个=实参表丢了,皆>0=上下文在但该名不在其中,一眼可分。
2026-10-03 16:38:01 +08:00
TermiNexus da18dd39a4 fix(classdef): 已注册类早退路径补字段类型完整定义
上一条(96dad30)只改了"全新定义"路径的字段扫描循环,但 includes 在 Phase A 预注册、Phase B 重新翻译时走的是 existing 早退分支(ClassGenDef 117-167,只 module_add_named_type 后即 return 0),字段循环根本不执行 ⇒ includes/condition.vp 的 Condition 仍内嵌 opaque 成员、自身 unsized,llc 仍报 base element of getelementptr must be sized。

物证:同一构建里 ThreadingTest 的 main.vp(fb785af4a2aa1a46.ll)因按需调 ensure_struct_def_in_module,已正确拷入 CRITICAL_SECTION/CONDITION_VARIABLE/SRWLOCK 的完整定义;而定义它们的 condition.vp 自身(de351f55b29d33a3.ll)没有 ⇒ 只有定义模块自身体内 GEP 时才暴露。

修复:早退分支在 module_add_named_type 后补调 _ensure_field_types_in_module(Struct/Array 值类型递归补完整定义、Ptr 补 opaque),与全新定义路径同口径。
2026-10-03 16:05:24 +08:00
TermiNexus 96dad309ac fix(classdef): 值类型字段补完整结构体定义,修复跨模块嵌套结构体 unsized 导致 llc GEP 报错
现场:Test/ThreadingTest 编译 includes/condition.vp 时 llc 报「base element of getelementptr must be sized」:getelementptr %Condition, %Condition* %1, i32 0, i32 1。

根因:ClassGenDef 定义类时对字段类型一律只调 module_ensure_opaque_for_type(仅 opaque 声明)。当字段是**值类型**跨模块结构体时,本类体内嵌的成员在 IR 中仍是 opaque ⇒ 成员 unsized ⇒ 本类 unsized ⇒ 本模块内对本类做带字段下标的 GEP 时 llc 报错。现场物证:condition.vp 的 .ll 里 CONDITION_VARIABLE/CRITICAL_SECTION = type opaque,而 Condition = type {CV, CS}。

修复:指针字段维持 opaque(指向物无需在本模块有体);非指针(值类型结构体/结构体数组)改调 ensure_struct_def_in_module 复制完整定义,并配合 _ensure_field_types_in_module 覆盖数组元素。
2026-10-03 15:58:48 +08:00
TermiNexus 28b7437e21 fix(generic): 泛型类构造 Call 实参推断同步收紧,抽出共用精确映射
CallGenCtor._infer_generic_ctor_type_arg 的 Call 分支此前与泛型函数路径同款名字子串猜测(含 Int→int、Float→double),把 CUInt64T/CInt16T 静默归成 int=i32、CFloat32T 猜成 double。改为:把被调表达式交给注解解析器,命中标量才据精确位宽映射;非标量/不可解析仍 fail-fast(保持原有"明确的类型意图却无法解析"报错)。

新增 ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty 作为两处共用的唯一入口(内部走 _infer_scalar_type_name_from_spec),泛型函数路径一并改为调用它,消除重复的 arena/SpecFromLLVMType 样板,保证两处同口径。
2026-10-03 15:51:12 +08:00
TermiNexus 9a80f1df6c fix(generic): 泛型函数 Call 实参推断改为精确类型解析,移除名字子串猜测
原实现按被调名子串猜类型实参(含 Int→int、Float→double 等),把 CUInt64T/CInt16T 一律归成 int=i32,静默窄化/扩宽位宽(禁止静默默认原则);CFloat32T 也会被猜成 double。

改为:把被调表达式本身交给注解解析器 resolve_annotation_type,命中**标量**才据其精确位宽经 _infer_scalar_type_name_from_spec(唯一真源)映射;非标量(构造/普通函数调用)或不可解析一律返回 None,由调用方汇总后 fail-fast 提示显式标注。行为变化:CUInt64T(x)/CInt16T(x) 等无等位宽裸名的转换实参不再被静默猜成 int。
2026-10-03 15:49:58 +08:00
TermiNexus e196d6a28a fix(类型): 模块限定注解支持 from-import 重导出解析
现场:Test/ThreadingTest/App/main.vp:184 `lock: vthreading.Lock = vthreading.Lock()` 报「变量类型注解解析失败」。vthreading.Lock 是重导出(includes/vthreading.vp `from lock import Lock`),Lock 真定义在 lock.vp;普通模块的 from-import 重导出不像包 __init__ 那样登记别名 entry(VpiReader._inject_reexport_class_declare 仅对包 __init__ 触发),故按 vthreading sha1 查 Lock 恒落空。

resolve_annotation_type 的 Attribute 模块限定分支此前未做真定义模块校正:先按原 sha1 查(既有命中情形逐字不变),未命中时再经 _class_true_def_sha1(唯一真源,已在 _normalize_type_name 规则 4b 使用)校正后重查一次,无证据时原样返回不改变行为。
2026-10-03 15:44:49 +08:00
TermiNexus c035a82394 fix(类型): str/bytes 聚合指针检查豁免字符数组衰减,并统一判据真源
误报现场:includes/os/_win32.vp:207 `name: str = find_data.cFileName`。WIN32_FIND_DATAA.cFileName 是 [260 x i8],字段访问得到 Ptr(Array(i8,260)),其指针在 C 语义下天然衰减为 char*/str,属合法赋值,原判据把 Ptr(Array) 一律当真而误杀。

新增 ExprGenBase._str_rejects_ptr_kind(单一真源:1=结构体/类/联合指针、2=非字符数组指针、3=函数指针、0=可接受;[N x i8] 豁免)供 fail_if_str_annot_agg_ptr 与 CallGenLib 跨模块实参检查共用,消除两处重复的 kind 判定,避免同规则两处演化。
2026-10-03 15:40:01 +08:00
TermiNexus bf2f38acd2 fix(generic): 修正 SpecToLLVMText 返回值判据倒置,恢复内建标量实参的泛型类型实参推断
_infer_scalar_type_name_from_spec 把 SpecToLLVMText 的『0=成功』误判为失败(写成 == 0 就 return None),使该函数对任何内建标量都恒返回 None —— 泛型函数隐式推断中『实参来自内建标量形参/字段』的路径(Name/Attribute 分支)永远推不出,报『无法从实参推断泛型函数 ... 禁止静默默认 int』(现场:GenericOopTest fxi 体内 fxi2(a, b, ...),a: double)。判据改为 != 0(与 ClassGenVTable 同约定)。

同时收紧位宽映射:i16/i64/f16 在 Viper 没有等位宽裸名别名(裸 int=int32、裸 float=float32),原映射 i16/i64->int、half->float 会静默窄化/扩宽位宽(i64->int 即 i64->i32),违反『禁止静默默认/兜底』原则;改为只映射位宽精确对应的形态(i1->bool、i8->char、i32->int、f32->float、f64->double),其余返回 None 交给调用方 fail-fast 提示显式标注。
2026-10-03 15:28:02 +08:00
TermiNexus 09ea7283f9 fix(类型): str/bytes 不再作通用指针通配,补齐实参/赋值/返回三处隐式转换 fail-fast
规则:void*/t.CPtr/rawptr 允许隐式转为任意指针类型,str/bytes 不允许(即便两者在 IR 层同为 i8*)。此前 coerce_to_type 的规则 1(通用指针通配)会把指向结构体/类/数组/函数的指针静默 bitcast 到 str 目标(反向亦然)。

- ExprGenBase:新增 _annot_name_is_bare_str/_annot_is_bare_str(裸 str/bytes 判定,组合段如 str|t.CPtr 不受影响)、fail_if_str_annot_agg_ptr(值是指向聚合类型的指针则 fail-fast,消息 CastError)、fail_if_str_annot_ast_agg_ptr(AST 注解入口)。

- 返回路径 StmtGenCifMixin.translate_return:在类型转换分支之前用 trans.CurFuncRetAnnot 判定,避免被规则 1 静默 bitcast。

- 赋值路径 AssignGenMixin.translate_annassign:四个分支(global / nonlocal / mod_glob / 局部)在 coerce 前用 aa.annotation 判定。

- 同模块实参 CallGenMixin.translate_call:在参数循环前一次性确保本模块 .vpi 参数注解缓存就绪(同跨模块 CallGenLib 键式:模块级函数 cls=None、段下标==形参下标),循环内按 _vpi_param_annot_seg_is 判定裸 str/bytes。
2026-10-03 14:46:57 +08:00
TermiNexus 8af967db7f fix(generic): 泛型类模板类型参数名补缺 + 空上下文 fail-fast,修复特化方法体 T 变 NameError 2026-10-03 14:34:55 +08:00
TermiNexus 98ee53340f feat(call): 跨模块实参加 str/bytes 形参的 fail-fast(str 不作通用指针通配),并加 vpi 注解段等值判定 2026-10-03 14:23:12 +08:00
TermiNexus feb29fa7a3 fix(generic-func): 发射特化调用改传符号名(callee: str)而非 Function*,修复生成空 @ 的非法 IR 2026-10-03 14:18:14 +08:00
TermiNexus df1ffa4c2a refactor(subscript): 删除调用侧下标 zext 与 _zext_index_to_i32,统一交给 build_gep(单一真源) 2026-10-03 13:35:36 +08:00
TermiNexus bcbc7142be fix(subscript): 指针下标 GEP 补 zext,修复 i8 下标被符号扩展(uint8ptr[200] -> p[-56] 越界),zlib 漏计字节>=128 根因 2026-10-03 13:20:11 +08:00
TermiNexus ab0ee723b3 fix(classgen): 补 import llvmlite,修复 llvmlite.LLVMType/Int32 解析不一致导致的指针隐式转换 fast-fail 2026-10-03 13:01:42 +08:00
TermiNexus 2868798f58 fix(classgen): 补声明 GenericFunc* 五个类字段,修复 GenericFuncKeys 成员查找 fast-fail 2026-10-03 12:54:56 +08:00
TermiNexus 63acb2bf4d fix(fstring): 使用点按需补 @snprintf declare,f-string 不再依赖显式 import stdio 2026-10-03 12:46:30 +08:00
TermiNexus f2b65f57ec fix(match): case A | B 的 OR 模式注册全部备选(switch 仅取首个)
_handle_simple_match 的 MatchOr 分支此前只把 pats[0] 译成 case 值 → switch value: case 1 | 2 中 2 落 default(LogicFlowTest SwitchMultiValueTest(2) != 10)。改为按臂记录 MatchOr 节点,建 sw 链时逐备选 new_switch_case 挂同一 body(等价 C 的 case 1: case 2:);_handle_guarded_simple_match 同款:cond 改为各备选 subj==cv 的 OR 再与 guard 取与。非 MatchOr 路径逐字不变;非值备选改为 fail-fast(原为静默丢弃)。
2026-10-03 12:11:43 +08:00
TermiNexus 3b465121cd fix(call): kwargs 调用路径补齐省略形参的默认值
_translate_call_with_kwargs 此前对「位置/关键字均未提供的形参」直接跳过 → 调用实参数 < define 形参数,被调函数按 ABI 读未写寄存器/栈槽(remote.one(x = 1) 的 cap 读成 0,DefaultArgTest 断言失败)。改为:按 found_func mangled 名前缀取定义模块 SHA1,用全局默认表 find_func_defaults 取定义处默认值快照补齐(右对齐索引);取不到默认值时 fail-fast,绝不静默漏参/填零。跨模块默认值通道本就通(vpi 带默认值),无需改产物格式。
2026-10-03 12:08:32 +08:00
TermiNexus 692998820c feat(generic): 泛型函数类型实参推断与单态化
支持 def f[T](x: T) -> T 及 f(x)/f[T](x):模板注册(Phase1Scan/Phase2 顶层 FunctionDef,键 {sha1}.{name})、按形参注解对齐从实参推断类型实参(无来源即 fail-fast,不静默默认 int)、克隆模板并 set_generic_context 后翻译生成特化体(名字 {sha1}.f[int],规范化复用 _mangle_generic_class_name)、同签名去重、跨模块按模板签名补 declare。泛型函数模板不再发 vpi(模板签名含未绑定 T),也不再发射 int 兜底版本。已知限制(未静默):bound [T: bound] 解析期已被丢弃;t.CExport/t.CExtern/t.State 泛型函数、关键字/默认参数调用、mod.f[T](x) 显式下标等暂 fail-fast 或走既有报错。
2026-10-03 11:59:26 +08:00
TermiNexus 1fbebb2a62 fix(type): 普通模块 from-import 重导出类的归属模块校正
_class_true_def_sha1 在 cand 模块下查不到该类时原样返回 cand_sha1;而普通模块(非包 __init__)的 from X import Y 重导出不登记别名 entry(VpiReader._inject_reexport_class_declare 仅对包 __init__ 触发)。于是 vthreading.Lock(vthreading 仅 from lock import Lock)的前向声明被错误建在 vthreading 模块下 → 方法返回类型查询去读 vthreading 的 text.ll/vpi(无 Lock.acquire)→ fail-fast(ThreadingTest 根因)。补:先查全局 re-export 内存表 lookup_reexport,未命中再按需解析 .vpi 的 from-import 定位源模块,取其真定义 SHA1(源为包时按同一 IR 名前缀口径继续校正);无证据时行为不变。
2026-10-03 11:37:27 +08:00
TermiNexus 18f4c2326f fix(match): REnum match 变体字段绑定前确保跨模块结构体完整定义
_bind_variant_fields 对 renum_ty 做带字段下标的 GEP,LLVM 要求被指结构体 sized;而模块内对跨模块 REnum(如 llvmlite.__types.LLVMType)只有签名扫描产生的 type opaque,作 GEP base element 时 llc 报 base element of getelementptr must be sized(LLvmLiteTest 根因)。其它取字段路径在 GEP 前都会调 ensure_struct_def_in_module,唯独 REnum match 绑定漏了。补上该调用(同名原地替换 opaque,非追加;本地非跨模块类型自动跳过)。
2026-10-03 11:21:41 +08:00
TermiNexus 105456e56e feat(type): 泛型类注解必须带类型实参,裸名 fail-fast
resolve_annotation_type 入口集中守卫:注解(或其 A | t.CPtr 一侧)的基名(Name.id / Attribute.attr)是已注册泛型模板却没写 [...] 时 fatal_error,报文给出正确写法示例(vector.Vector[CInt]、GSList[G5Node])。判据复用 _find_generic_template(按项目注册),非泛型同名类(struct_test 的裸 Box、dict)不误报;Subscript 形态不受影响,仍走 _resolve_generic_subscript_ty 与 _fail_on_degenerated_generic_annotation。自举源码 includes/** 与 TransPyV/App/** 已确认无裸泛型注解。
2026-10-03 11:16:42 +08:00
TermiNexus 8da4550d79 fix(vpi): 全局变量注入补自注入守卫,消除同模块 external global 与定义重名
循环 import(os/__init__ 导入 os._win32/os._posix,而二者又导入包 os)使 imported_modules 含本模块包名 → LoadDepVpiIntoModule 命中本模块自身 sha1 → _inject_global_variable_declare 为本模块自己的模块级全局发射 @<sha1>.<name> = external global,随后定义端 handle_module_level_var 再发 = global … → llc redefinition of global(SysLibTest 根因)。补上 _inject_function_declare / _inject_method_declare 已有的同款自注入守卫(_declare_only==0 且 src_sha1==trans.ModuleSha1 时早退)。
2026-10-03 11:08:58 +08:00
TermiNexus 19d7a90c28 feat(match): 支持 match/switch 守卫(case X if cond)
通配计数只统计无守卫的 case _,允许多个带守卫的 case _ if ...;_handle_simple_match 预扫描 has_guard,有守卫时改走 _handle_guarded_simple_match(按书写顺序的 if-else 比较链,guard 归一为 i1 后与值比较取与,链尾落 default/end),无守卫时保持原 switch 分派路径逐字不变(零回归);_handle_renum_match 对带守卫臂 fail-fast,避免 switch(tag) 静默忽略 guard。
2026-10-03 10:58:47 +08:00
TermiNexus 67667b10db fix(class): __before_init__ 定义时复用同名前向声明,消除同模块 declare+define
本类方法体内构造自身(如 Vec2.__add__ 里 return Vec2(...))时,构造路径先经 _call_method_on_ptr → _ensure_method_declare 在本模块建好同名 __before_init__ 前向声明(IsDeclared=1);随后 _generate_before_init 又 create_function 追加一条同名 define,使同一模块 IR 同时出现 declare 与 define → llc 报 invalid redefinition of function(OopInheritTest 根因;④ 改用 full.ll 直喂 llc 后不再被旧 BuildCombinedIR 去重掩盖)。改为按名复用该前向声明(命名首参 %self、校准类型、清 IsDeclared 转 define),与 _generate_method/FuncGenDef 同构。
2026-10-03 10:26:57 +08:00
TermiNexus 4c114d8875 fix(call): 泛型模板的前向声明占位不再阻断构造期类型实参推断
注解 vector.Vector | CPtr(无实参)会经 TypeGenResolve 登记不透明前向声明占位 {sha}.Vector(IsForwardDecl=1)。模块限定构造 vector.Vector(...) 在 CallGenMixin 命中该占位后,gen_ctor_entry != None 使泛型推断分支(struct_ty is None and gen_ctor_entry is None)被整段跳过,静默按普通结构体/函数构造并产出裸泛型基类对象 → 后续方法调用触发 D25 接收者 fail-fast(VectorGenericTest 根因)。改为:命中项是前向声明占位且类名为已注册泛型模板时,不锁定 struct_ty/gen_ctor_entry,放行泛型推断按实参(_hint: T)特化;裸名分支与模块限定分支同口径。
2026-10-03 10:18:29 +08:00
TermiNexus 53ba4e48bd fix(expr): 修正 not 取反的 const_int 参数顺序(bits 在前)
上一提交 df9a5a9 误把 const_int 参数写成 (value, bits),实际签名为 const_int(pool, bits, val)。改为 const_int(pool, 1, 0)(i1 false),使 not x 真正降级为 (bool(x)==0),不再与 1 比较而恒等。对照本文件 663/680 行的 const_int(pool, 1, 0) 用法可确认参数顺序。
2026-10-03 09:58:35 +08:00
TermiNexus 9bd3ac3dfb refactor(sidecar): 删除 BuildCombinedIR 文本合并死代码(④-d)
在线模型方案(④-a~④-c)已由 LoadFullIR 取代文本合并,BuildCombinedIR 及其专属辅助(_Seen* 去重表、_RegisterStubLines、_LoadAndAppendStub、_LoadAndAppendTextDeclares、_IsTypeDefLine/_IsIrHeaderLine/HoistTypeDefsBeforeDeclares、_collect_stub_sha1s、_stub_scan_cache_*)已无调用方,一并删除(约 1348 行);同步改写 #35/TranslatorGenMixin 中引用旧函数的注释。
2026-10-03 09:13:17 +08:00
TermiNexus df9a5a9beb fix(expr): 修复 not 一元运算未取反(i1 条件与 0 比较)
not x 的降级此前写成 (bool(x) == 1),在 i1 上与恒等等价,导致 not r2 不取反(FuncTest Test 7 bool return 失败)。注释本意即 not x = (bool(x) == 0),改为与 i1 常量 0 比较。
2026-10-03 09:07:04 +08:00
TermiNexus cc8ea01476 feat(sidecar): 翻译点增写 {sha1}.full.ll,Phase B 改用在线 FULL 产物(④-b/④-c)
- 翻译点(Phase1Main / Phase2 PhaseA / Phase2 includes 重试)在 dump stub/text/meta 后
  追加 dump_ir(OUTPUT_FULL) → {sha1}.full.ll(FULL 缓冲 16MB = stub+text 2× 余量)
- Phase1 增量跳转新增要求 full.ll 存在(缺失即重译补写)
- 新增 StubMergerBuild.LoadFullIR(与 BuildCombinedIR 同契约:读文件到 out_buf)
- Phase B 三处调用点 BuildCombinedIR → LoadFullIR;COMBINED_IR_SIZE 4MB → 16MB
- 诊断报文/注释同步(读 full.ll 失败)

full.ll 为模型在线时产出的自足单模块 IR(类型定义在声明之前,天然满足 llc 顺序);
BuildCombinedIR 暂保留(④-d 再删)。未编译验证。
2026-10-03 08:43:03 +08:00
TermiNexus ec9e950185 fix(gen): 全局初始化器引用跨模块 vtable 时补 external 声明(④-a)
模块级对象全局的 storage 初始化器以符号名写入依赖类 vtable
(i8* @<dep_sha1>.<Cls>.vtable),此前不建 GlobalVariable ⇒ 单模块
OUTPUT_FULL 缺该符号声明,只能靠 BuildCombinedIR 合并依赖 stub 补入。

现于写初始化器前:跨模块(ModuleSha1 != 当前模块)且模块内无该全局时,
new_global_variable(..., Ptr(i8)) + module_add_global 补 external 声明
(对齐 CallGenLeafKit._make_vtable_ref_i8)。仅跨模块补,本模块类由
_generate_vtable 产出定义,避免同模块重复定义。

去 IR 化步骤④-a(生产者自足,FULL IR 自足化前置);未编译验证。
2026-10-03 08:39:13 +08:00
TermiNexus 324a22b6d1 refactor(call): CallGenFd 返回类型精确化改以 vpi 为权威
返回类型「精确化」原只读定义模块 text.ll;改为先查 vpi 返回注解缓存
(_lookup_method_ret_ty_via_vpi),未命中再回退 text.ll,与 ② 的
vpi→text.ll 口径一致。

去 IR 化遗留收尾;未编译验证。
2026-10-03 08:28:07 +08:00
TermiNexus 3b0ed04e70 chore(stubmerger): 删除 StubMergerIR 中 6 个无调用方的旧 IR 文本扫描助手
删除 _IsFuncDeclaredOrDefined / _CommentOutDeclareInBuf / _CommentOutGlobalInBuf /
_HasGlobalInBuf / _CommentOutOpaqueTypeInBuf / _HasFullTypeDefinition —— 全仓无调用方
(去重判定早已迁移到 StubMergerBuild 的 _seen_* 哈希表),保留在用中的
_is_submodule_of_imported。顺带更新本模块头注释(现为 meta sidecar 工具)。

去 IR 化收尾清理;未编译验证。
2026-10-03 08:26:02 +08:00
TermiNexus 611f49cfb6 refactor(repl): 会话函数签名改从翻译期模型取,去除片段 IR 文本解析
- 新增 _repl_ty_ptr_token(LLVMType* → tag*1000+bits)与 _repl_model_func_sig
  (按裸导出名查 tr.Module 的 Function,读 RetTy/Params;含 ctx 首形参与 EH 出参判定)
- _repl_precheck_session_funcs / _repl_register_session_funcs 增 mod 形参;
  两处调用点传 tr.Module
- 删除旧 IR 文本解析:_repl_ir_func_sig / _repl_ir_find_define_at /
  _repl_llvm_ty_token / _repl_span_str 及其 scratch ReplTySpan / ReplTySpan3 /
  ReplIrDefOff / _REPL_FN_TY_CAP

去 IR 化步骤⑤;返回码(1/2/3/4/5)与旧实现一致。未编译验证。
2026-10-03 08:23:45 +08:00
TermiNexus 35f2070d3b feat(sidecar): meta 增写 STRUCT/VTABLE,REPL 布局恢复改读 meta
- WriteModuleFuncMeta 更名 WriteModuleMeta:追加 STRUCT <name> FIELDS n PACKED 0|1
  (opaque → STRUCT <name> OPAQUE)与 VTABLE <class> SLOTS n;数据源改为
  Module.NamedTypeHead / GlobalHead,不再有任何 IR 文本反推
- VpiReader REPL 布局改读 {sha1}.meta:新增 _meta_atoi / _repl_meta_scan_layout
  按前缀精确匹配解析 STRUCT/VTABLE 行
- 删除旧 .stub.ll 文本解析辅助 _repl_stub_take_name / _repl_stub_body_count
  及其 scratch 缓冲 ReplStubNameBuf / _REPL_STUB_NAME_CAP

去 IR 化步骤③;未编译验证。
2026-10-03 08:18:15 +08:00
TermiNexus 78d3941f7d refactor(call): 跨模块返回类型解析改以 .vpi 为权威,text.ll 降为回退
将 8 处跨模块方法/函数返回类型解析的 text.ll → vpi 顺序对调为 vpi → text.ll:
- CallGenProp:属性方法调用
- CallGenMixin:跨模块裸名 CExport、from-import 函数
- CallGenCall:跨模块方法、类方法调用、self 方法调用(含 MRO 祖先链重试段整体搬移)
- CallGenDispatch:__len__ 跨模块、泛型 Mixin 方法

本模块 found_func / local_fn 取当前 IR 声明类型的路径保持不变。
去 IR 化步骤②;未编译验证。
2026-10-03 08:10:34 +08:00
TermiNexus 94d2bf5047 feat(sidecar): 落盘 {sha1}.meta 函数定义清单,声明文件判定改读结构化 sidecar
- 新增 WriteModuleFuncMeta:翻译期从 Module.FuncHead + Function.IsDeclared 产出 FUNC <mangled> DEF|DECL(与 dump_ir 的 stub/text 判据同源)
- 新增 _MetaHasDef / 重写 IsDeclOnlyModule:不再扫 text.ll 的 define 行,改读 meta
- Phase1/Phase2 落盘点紧随 text.ll 写出 meta;Phase1 增量跳转额外要求 meta 存在
- Phase2 内联 is_decl_mi 与 PhaseC 判定统一走 IsDeclOnlyModule

去 IR 化步骤①(sidecar 最小闭环),全后端统一,默认行为不变。
2026-10-03 08:06:39 +08:00
TermiNexus 0ebe2e0752 feat(backend): 新增 MLIR 后端开关/产物隔离/降级管线/模块别名(默认 llvm 逐字不变)
- Config: BackendTarget/BackendIsMlir/set_backend/is_mlir_backend/backend_dir_suffix/alias_module_name;读取 options.backend(回退 options.target)

- main.vp: 新增 --backend llvm|mlir(CLI 优先级最高,默认 llvm),加载工程后、resolve_paths 前生效

- 产物隔离: temp/output 追加 _mlir 后缀;includes 缓存追加后端后缀(.binary[_asan][_mlir])

- BuildPipeline: MLIR 降级 mlir-opt(校验) -> mlir-translate --mlir-to-llvmir,覆盖串行/并行/--phase2command,且置于 ASan opt 之前;LlcTask 增 MlirOptCmd/MlirTranslateCmd 字段

- 别名: ResolveModuleToPath 与 _FindSha1ByModName 经 Config.alias_module_name 把 llvmlite 重定向到 mlirlite(调用点零改动)
2026-10-03 07:53:04 +08:00
TermiNexus 4eec3f0908 feat(dunder): 补齐 dunder 协议(__bool__/__call__/__str__/__abs__/__del__/**/增量/反向)
- __bool__:ExprGenOps.coerce_cond_to_bool 统一 if/while/and/or/not/三元/协程条件的真值归一,优先调度 __bool__

- __call__:obj(args) 分发到 obj.__call__(CallGenMixin 裸名分支)

- __str__:str()/print/f-string 调度(CallGenDispatch/CallGenLeafKit/ExprGenFstr)

- __abs__:abs(obj) 调度(CallGenMixin)

- __del__/__delete__:del obj → __delete__;del obj.attr 无 property deleter 时回退对象 __del__

- ** / __pow__:新增 llvm.pow 内联(整数按常量指数回写),_binop_to_dunder 补 Pow

- 增量赋值:__iX__ 优先、缺失回退 __X__(新增 _aug_op_to_dunder)

- 反向运算符:新增 _rbinop_to_dunder,lhs 无 dunder 时调度 rhs.__rX__
2026-10-03 00:40:00 +08:00
TermiNexus a3bb62f2c3 feat(switch): switch 允许重复 case 标签(match 仍禁止)
- HandlesMatch / MatchGenMixin:值标签去重仅对 match 生效;switch 允许重复 case
  标签(按书写顺序,先出现者生效);通配臂多个(default / case _)仍 fail-fast
- wiki/07-control-flow.md 同步该语义
2026-10-03 00:18:55 +08:00
TermiNexus fabd92adc7 feat(switch): 新增 switch 语句并禁止 match 内 fallthrough
- MatchGenMixin:case 体按 node.is_switch 置 _in_switch_case;_has_no_break 只认 FallThrough 节点(去掉 c.NoBreak() 兼容路径)
- StmtGenDeclMixin:translate_fallthrough 只在 switch 的 case 体内合法,match/switch 之外 fail-fast
- Translator:_in_match_case 更名 _in_switch_case(语义 = 当前 case 体属于 switch)
- FuncGenDef / FuncGenMixin:函数体 / 嵌套函数 / lambda 入口隔离并恢复 _in_switch_case
- 因 match 改为硬关键字,重命名同名局部变量 match -> Matched(StubMergerStore / ImportsGenMixin / ExprGenAttr / CallGenBase)
2026-10-02 23:15:43 +08:00
TermiNexus 38860a6313 feat(match): 重名 case 标签 fail-fast
MatchGenMixin._translate_match_impl 新增前置检查(任何 IR 产出之前):新增
_case_label_same / _value_label_same 比较 switch 标签(常量 / 名字 / 属性链 /
MatchSingleton / MatchClass 变体名 / MatchOr 子模式),标签重复或出现多个
`case _` 时 fatal_error 终止(C switch 不允许重复 case 标签)。
2026-10-02 22:44:34 +08:00
TermiNexus d633a5786f fix(flow): 函数体入口清空 break/continue 目标与 match-case 标记
- FuncGenDef.translate_function_def:进函数体前保存并清空 _break_bb / _continue_bb /
  _in_match_case,退出恢复
- FuncGenMixin:嵌套函数体与 lambda 体两处同样处理——原实现下嵌套函数体内的
  break/continue 会继承外层函数的跳转目标(跨函数 br,非法 IR)
2026-10-02 22:40:04 +08:00
TermiNexus fb4175736f fix(flow): 循环/match 外的 break/continue 改为 fail-fast
- StmtGenDeclMixin:translate_break / translate_continue 增加 node 形参,目标块
  缺失(_break_bb / _continue_bb 为 None)时 fatal_error,不再静默不发跳转
- StmtGenMixin:Break / Continue 分派传入 node(供错误定位)
2026-10-02 22:35:26 +08:00
TermiNexus 43d25e58b9 feat(match): case 内 break 跳出 match,并落地 fallthrough
- Translator:新增 _in_match_case(FallThrough 合法性判定用);_break_bb 注释补充
  case 内 break 语义
- StmtGenDeclMixin:新增 translate_fallthrough(本身不产出 IR;非 case 体内 fail-fast)
- StmtGenMixin:FallThrough 语句分派
- MatchGenMixin:进出 case 体(含 default 臂)时暂存/覆盖 _break_bb = match end_bb,
  使 case 内 break 跳出 match 而非外层循环;_has_no_break 兼容 FallThrough 标记;
  REnum / simple 两条 switch 路径的 fallthrough 由"只留无终结符块"改为显式 br
  下一个 case body(原实现产出非法 IR)
2026-10-02 22:33:15 +08:00
TermiNexus 0b7f7a50c7 docs(types): 同步 list 相关注释,list[T, N] 不是定长数组
- TypeGenResolve._is_count_arg_container:说明把 list 纳入判据只是为了历史写法
  list[E, N] 的第 2 实参不被误当类名归一;list 本体是 _list.vp 的堆上 STL 容器
- AssignGenMixin:注释示例由 list[type] / list[type, N] 改为 CArray 形态
2026-10-02 22:16:30 +08:00
TermiNexus 8d01c01afd feat(viperbuild): 落地 --src 工程模式配置面,并补齐循环 else 与整型 None 比较
- 新增 lib/core/builtin/ViperBuild.vp:--src Pass 0 依赖闭包扫描 + 宏配置提取 +
  默认值填充 + 写回 Config;main.vp 路由改为工程模式并删除单文件分支
- Translator.vp:写入 triple / datalayout,翻译期擦除 viperbuild 宏语句;
  ImportsGenMixin.vp:对 viperbuild 豁免 SHA1 校验
- StmtGenCifMixin.vp:for / while 的 else 子句在 11 条循环生成路径全部接通
- ExprGenOps.vp:None 比较按 None 所在侧分派,整型 None 隐式为 == 0 / != 0
2026-10-02 22:07:01 +08:00
131 changed files with 56095 additions and 69345 deletions

No files matched your search

+1 -1
View File
@@ -28,4 +28,4 @@ __pycache__/
# 编译
**/output/
**/temp/
**/.tpv_build/
**/__vpcbuild__/
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+169 -260
View File
@@ -1,109 +1,63 @@
import t, c
from stdint import *
import string
import stdlib
import memhub
import viperlib
import sys
import w32.fileio as fileio
import w32.win32file
import w32.win32base
import win32.fileio as fileio
import ast
import llvmlite
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.CompilerFactory as CompilerFactory
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.Stub.StubGen.Converter as StubConverter
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
import lib.Phase.Phase1 as Phase1
import lib.core.Gen.GenApi as GenApi
# ============================================================
# Phase1Scan - 扫描类继承关系、泛型类标记与容器使用
#
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
# ============================================================
# Phase1Scan - scan class inheritance, generic-class markers and container usage.
# Functions live in this module; globals are accessed cross-module via Phase1._xxx.
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 Phase1.CLASS_NAME_LEN 字节),
# 定义 SHA1 存储在 def_sha1s_buf(每个 Phase1.SHA1_LEN 字节)。
# Topological sort stores class info/deps in flat byte arrays (names in
# class_names_buf/parent_names_buf, SHA1s in def_sha1s_buf) to avoid NULL str fields.
# ============================================================
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
#
# 遍历所有可达 includes 文件解析 AST,收集 ClassDef 的类名与父类名存入扁平字节数组,返回收集到的类数量。
#
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否使用可达集合过滤;
# sha1_set SHA1 集合(use_reachable=0 时使用);set_count SHA1 集合数量;class_names_buf 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节);
# parent_names_buf 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0');def_sha1s_buf 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
# Returns: 收集到的类数量,-1 失败
# ============================================================
def _ScanClassInheritance(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult],
reachable_set: str, reachable_count: int,
use_reachable: int,
sha1_set: str, set_count: int,
class_names_buf: bytes,
parent_names_buf: bytes,
def_sha1s_buf: bytes,
generic_file_indices: t.CPtr,
generic_file_count_box: t.CPtr) -> int:
"""预扫描 includes 文件,收集类继承关系"""
if result is None:
return -1
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配(Phase1Main 预扫描前),本函数把其地址直接作为 strcpy 目的缓冲;
# 分配失败返回 NULL 时目的指针为近零地址 → strcpy 写零页(ASan access-violation),故按 fail-fast 显式报错终止(VLogger.error 内部 sys.exit(1)),不静默返回 -1。
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: rawptr, generic_file_count_box: rawptr) -> int:
"""Pre-scan includes files and collect class inheritance info."""
if result is None: return -1
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
# strcpy write the zero page, so fail-fast abort instead of returning -1 silently.
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed (class_names_buf/parent_names_buf/def_sha1s_buf is NULL)", "Phase1")
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0
for i in range(result.Count):
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes 全量 parse 的 AST 若驻留到 mark1 会耗尽 1GB 主池 OOM);
# class_names_buf/parent_names_buf/def_sha1s_buf 与 generic_file_indices 在调用前分配、位于本 mark 之前,reset 不影响。
for i in range(int(result.Count)):
# Per-file mark: reclaim this file's AST right after collecting inheritance
# info (keeping all ASTs alive until mark1 would OOM the 1GB main pool).
scan_mark: sizet = mb.mark()
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
if entry is None or entry.Sha1 is None: continue
sha1_e: str = entry.Sha1
file_has_generic: int = 0
# 按需翻译过滤
# on-demand translation filter
in_set: int = 0
if use_reachable != 0:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0: continue
# 读取文件
# read the file
file_path: str = entry.Path
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f.closed:
continue
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
if f.closed: continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path}", "Phase1")
f.close()
continue
@@ -117,10 +71,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
if bytes_read <= 0:
stdlib.free(src_buf)
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
# 解析 AST
# parse the AST
lx: ast.Lexer* = ast.new_lexer(mb)
if lx is None:
stdlib.free(src_buf)
@@ -132,150 +85,141 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
stdlib.free(src_buf)
continue
# 遍历 AST 顶层节点,收集 ClassDef
# walk top-level AST nodes, collect ClassDef
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn: sizet = len(ch)
for ci in range(cn):
for ci in range(int(cn)):
child: ast.AST* = ch.get(ci)
if child is None:
continue
if child is None: continue
kd: int = child.kind()
if kd != ast.ASTKind.ClassDef:
continue
if kd != ast.ASTKind.ClassDef: continue
cd: ast.ClassDef* = ast.ClassDef*(child)
if cd is None or cd.name is None:
continue
if cd is None or cd.name is None: continue
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
# detect generic classes (type_params); record the file index for Phase 1a
if file_has_generic == 0:
tp_scan: list[str]* = cd.type_params
if tp_scan is not None:
file_has_generic = 1
if generic_file_count_box is not None:
gfc_ptr_scan: t.CPtr = (int | t.CPtr)(generic_file_count_box)
gfc_ptr_scan: int* = (int*)(generic_file_count_box)
if gfc_ptr_scan is not None:
gfc_scan: int = gfc_ptr_scan[0]
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
gi_ptr_scan: t.CPtr = (int | t.CPtr)(gi_addr_scan)
if gi_ptr_scan is not None:
gi_ptr_scan[0] = i
gi_ptr_scan: int* = (int*)(gi_addr_scan)
if gi_ptr_scan is not None: gi_ptr_scan[0] = i
gfc_ptr_scan[0] = gfc_scan + 1
if class_count >= Phase1.MAX_CLASSES:
break
if class_count >= Phase1.MAX_CLASSES: break
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
# fill flat array: class name (offset = class_count * Phase1.CLASS_NAME_LEN)
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
nm_len: sizet = string.strlen(cd.name)
if nm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(name_dst, cd.name)
else: string.strcpy(name_dst, cd.name)
# 填充扁平数组:父类名(无父类填 '\0')
# fill flat array: parent name (no parent -> '\0')
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
parent_dst[0] = '\0'
parent_nm: str = GenApi._get_parent_class(cd, None)
# parent_nm 是 AST 节点内部指针,不需要释放
# parent_nm is an internal AST pointer; no need to free
if parent_nm is not None:
pnm_len: sizet = string.strlen(parent_nm)
if pnm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(parent_dst, parent_nm)
else: string.strcpy(parent_dst, parent_nm)
# 填充扁平数组:定义 SHA1(偏移 = class_count * Phase1.SHA1_LEN)
# fill flat array: defining SHA1 (offset = class_count * Phase1.SHA1_LEN)
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
string.strcpy(sha1_dst, sha1_e)
class_count += 1
stdlib.free(src_buf)
# 回收本文件 AST/lexer/token(类名/父类/SHA1 已拷入扁平数组)
# reclaim this file's AST/lexer/token (names/SHA1 already copied into flat arrays)
mb.reset_to(scan_mark)
return class_count
# ============================================================
# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记
#
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部 98 个 includes 致 AST 驻留 mark1 前把 pool 推到 OOM。
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);漏报有害(泛型模板未预注册 → 悬空复发),故保守只匹配单行形式。
# ============================================================
# _BufHasGenericClassMarker - text scan for a generic-class marker (PEP 695 "class
# Name["). Used to pre-filter files before registering generic templates.
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
if buf is None or blen <= 6:
return 0
"""Return 1 if buf contains a 'class identifier[' generic-class marker, else 0."""
if buf is None or blen <= 6: return 0
pos: sizet = 0
while pos + 5 < blen:
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' \
and buf[pos + 3] == 's' and buf[pos + 4] == 's':
# class 前是词边界(行首或非标识符字符)
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' and buf[pos + 3] == 's' and buf[pos + 4] == 's':
# 'class' preceded by a word boundary (line start or non-identifier)
prev_ok: int = 1
if pos > 0:
pc: char = buf[pos - 1]
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') \
or(pc >= '0' and pc <= '9') or pc == '_':
prev_ok = 0
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
if prev_ok != 0:
p: sizet = pos + 5
# 跳过空白
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
# 读类名标识符
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
or(buf[p] >= 'A' and buf[p] <= 'Z')
or(buf[p] >= '0' and buf[p] <= '9')
or buf[p] == '_'):
# skip whitespace
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
# read the class-name identifier
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
p += 1
# 类名后非空白是 '[' → 泛型类
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
if p < blen and buf[p] == '[':
return 1
pos += 1
else:
# non-space after the name is '[' -> generic class
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
if p < blen and buf[p] == '[': return 1
pos += 1
else: pos += 1
return 0
# ============================================================
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
#
# 泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/GenericClassFiles + 模板节点)是懒分配的跨文件持久表,
# 首次注册必须落在 mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2))存活;否则表与节点被 reset 回收,
# _find_generic_template 遍历悬空内存 strcmp 崩溃。本函数先文本扫描(_BufHasGenericClassMarker,不 parse)筛选出含泛型类的文件,
# 仅对命中文件用**永久池(perm_pool)**解析(模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool);重复注册由 _register_generic_template 内部查重跳过。
# ============================================================
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult]) -> int:
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
if result is None or mb is None:
return 0
# _BufHasGenericFuncMarker - text scan for a generic-function marker (PEP 695
# "def name["); cheap pre-filter before parsing for generic function templates.
def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
"""Return 1 if buf contains a 'def identifier[' generic-function marker, else 0."""
if buf is None or blen <= 4: return 0
pos: sizet = 0
while pos + 3 < blen:
if buf[pos] == 'd' and buf[pos + 1] == 'e' and buf[pos + 2] == 'f':
# 'def' preceded by a word boundary (line start or non-identifier)
prev_ok: int = 1
if pos > 0:
pc: char = buf[pos - 1]
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
if prev_ok != 0:
p: sizet = pos + 3
# skip whitespace
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
# read the function-name identifier
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
p += 1
# non-space after the name is '[' -> generic function
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
if p < blen and buf[p] == '[': return 1
pos += 1
else: pos += 1
return 0
# _PreRegisterAllGenericTemplates - pre-register includes generic templates before
# mark1 so the lazy tables survive later resets; parses only marker-matched files.
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*) -> int:
"""Pre-register generic templates from all includes files and return the count."""
if result is None or mb is None: return 0
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None:
return 0
if perm is None: return 0
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
handled_count: int = 0
for i in range(result.Count):
for i in range(int(result.Count)):
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
entry_pg: rawptr[IncludesScanner.FileEntry] = (
rawptr[IncludesScanner.FileEntry])(entry_addr_pg)
if entry_pg is None or entry_pg.Sha1 is None:
continue
# 读取文件
entry_pg: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(entry_addr_pg)
if entry_pg is None or entry_pg.Sha1 is None: continue
# read the file
file_path_pg: str = entry_pg.Path
f_pg: rawptr[fileio.File] = fileio.File(file_path_pg, fileio.MODE.R)
if f_pg.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
f_pg: fileio.File* = fileio.File(file_path_pg, fileio.MODE.R)
if f_pg.closed: continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path_pg}", "Phase1")
f_pg.close()
continue
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
@@ -287,13 +231,13 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
if bytes_read_pg <= 0:
stdlib.free(src_buf_pg)
continue
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
src_buf_pg[bytes_read_pg] = 0
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0:
if bytes_read_pg < Phase1.SRC_BUF_SIZE: src_buf_pg[bytes_read_pg] = 0
# Text-scan filter: skip non-generic files (no parse, no residency, avoids
# OOM). A class or function marker hit selects the file for parsing.
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0 and _BufHasGenericFuncMarker(src_buf_pg, bytes_read_pg) == 0:
stdlib.free(src_buf_pg)
continue
# 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活)
# generic file: parse in perm_pool and register templates (survives reset)
lx_pg: ast.Lexer* = ast.new_lexer(perm)
if lx_pg is None:
stdlib.free(src_buf_pg)
@@ -304,96 +248,75 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
if tree_pg is None:
stdlib.free(src_buf_pg)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
# walk top-level ClassDef/FunctionDef, register generic templates (type_params != None)
ch_pg: list[ast.AST*]* = tree_pg.children
if ch_pg is not None:
cn_pg: sizet = len(ch_pg)
for ci_pg in range(cn_pg):
for ci_pg in range(int(cn_pg)):
child_pg: ast.AST* = ch_pg.get(ci_pg)
if child_pg is None or child_pg.kind() != ast.ASTKind.ClassDef:
continue
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
if cd_pg is None or cd_pg.name is None:
continue
tp_pg: list[str]* = cd_pg.type_params
if tp_pg is None:
continue
GenApi.set_current_file_type(file_path_pg)
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
handled_count += 1
if child_pg is None: continue
ck_pg: int = child_pg.kind()
if ck_pg == ast.ASTKind.ClassDef:
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
if cd_pg is None or cd_pg.name is None: continue
tp_pg: list[str]* = cd_pg.type_params
if tp_pg is None: continue
GenApi.set_current_file_type(file_path_pg)
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
handled_count += 1
elif ck_pg == ast.ASTKind.FunctionDef:
fd_pg: ast.FunctionDef* = ast.FunctionDef*(child_pg)
if fd_pg is None or fd_pg.name is None: continue
tf_pg: list[str]* = fd_pg.type_params
if tf_pg is None: continue
GenApi.set_current_file_type(file_path_pg)
GenApi._register_generic_func_template(perm, fd_pg, entry_pg.Sha1)
handled_count += 1
stdlib.free(src_buf_pg)
if handled_count > 0:
VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型类模板", "Phase1")
else:
VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
if handled_count > 0: VLogger.info(f"pre-registered {handled_count} includes generic templates (classes and functions) in the permanent pool before mark1", "Phase1")
else: VLogger.info(f"pre-registered 0 generic templates in the permanent pool before mark1 (scanned {result.Count} files; the marker may not have matched or the full scan failed)", "Phase1")
return handled_count
# ============================================================
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
#
# Args: class_names_buf 类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;class_count 类数量;class_name 要查找的类名
# Returns: SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
# ============================================================
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
class_count: int, class_name: str) -> str:
"""查找类名对应的定义文件 SHA1"""
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
return None
for i in range(class_count):
# _FindClassDefSha1 - look up a class name's defining file SHA1 in the flat arrays;
# returns a pointer into def_sha1s_buf, or None if not found.
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, class_name: str) -> str:
"""Find a class name's defining file SHA1 in the flat arrays."""
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
for i in range(int(class_count)):
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
if string.strcmp(name_ptr, class_name) == 0:
return def_sha1s_buf + i * Phase1.SHA1_LEN
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
return None
# ============================================================
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
#
# TPV 的 AST 中语句块挂在 children,而表达式/注解树全部以结构体字段挂载(value/slice/func/args/annotation/targets/returns/bases/left/right 等);
# 只递归 children 会漏掉 list[...] | t.CPtr、list[...](...)、字段注解等非语句块位置,故每个节点先递归表达式承载字段、再递归 children。
# 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。
# ============================================================
# _ScanTreeForContainers - recursively scan the AST for list/dict container usage:
# recurse expression/annotation struct fields first, then statement-block children.
def _AnnIsContainer(ann: ast.AST*) -> int:
"""注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...])
数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _ScanTreeForContainers
把数组字面量误判为 list 容器使用(FIX 2026-08-30,联动 Translator.vp 同修)。
"""
"""Return 1 if the annotation is a list/dict container, else 0."""
while ann is not None:
ak_c: int = ann.kind()
if ak_c == ast.ASTKind.Name:
nm_c: ast.Name* = ast.Name*(ann)
if nm_c.id is not None:
if string.strcmp(nm_c.id, "list") == 0:
return 1
if string.strcmp(nm_c.id, "dict") == 0:
return 1
if string.strcmp(nm_c.id, "list") == 0: return 1
if string.strcmp(nm_c.id, "dict") == 0: return 1
return 0
elif ak_c == ast.ASTKind.Subscript:
sub_c: ast.Subscript* = ast.Subscript*(ann)
ann = sub_c.value
else:
return 0
else: return 0
return 0
def _ScanTreeForContainers(node: ast.AST*,
need_list_box: t.CPtr,
need_dict_box: t.CPtr,
depth: int = 0):
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
if depth > 128:
VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
if node is None:
return
def _ScanTreeForContainers(node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr, depth: int = 0):
"""Recursively scan the AST for list/dict container usage."""
# Recursion depth guard: a self-referential AST cycle would recurse until the
# stack is exhausted; fail-fast abort.
if depth > 128: VLogger.error("container scan recursion too deep (AST cycle or nesting over 128 levels)", "Phase1")
if node is None: return
k_s: int = node.kind()
if k_s == ast.ASTKind.Name:
nm_s: ast.Name* = ast.Name*(node)
if nm_s.id is not None:
if string.strcmp(nm_s.id, "list") == 0:
need_list_box[0] = 1
elif string.strcmp(nm_s.id, "dict") == 0:
need_dict_box[0] = 1
if string.strcmp(nm_s.id, "list") == 0: need_list_box[0] = 1
elif string.strcmp(nm_s.id, "dict") == 0: need_dict_box[0] = 1
elif k_s == ast.ASTKind.Subscript:
sub_s: ast.Subscript* = ast.Subscript*(node)
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
@@ -409,16 +332,14 @@ def _ScanTreeForContainers(node: ast.AST*,
if cl_args_s is not None:
na_s: sizet = len(cl_args_s)
i_a2: sizet
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
for i_a2 in range(int(na_s)): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
if cl_kws_s is not None:
nk_s: sizet = len(cl_kws_s)
i_k2: sizet
for i_k2 in range(nk_s):
kw_s: rawptr[ast.Keyword] = rawptr[ast.Keyword](cl_kws_s.get(i_k2))
if kw_s is not None:
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
for i_k2 in range(int(nk_s)):
kw_s: ast.Keyword* = ast.Keyword*(cl_kws_s.get(i_k2))
if kw_s is not None: _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Assign:
asn_s: ast.Assign* = ast.Assign*(node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
@@ -426,8 +347,7 @@ def _ScanTreeForContainers(node: ast.AST*,
if tgts_s is not None:
nt_s: sizet = len(tgts_s)
i_t2: sizet
for i_t2 in range(nt_s):
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
for i_t2 in range(int(nt_s)): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.AnnAssign:
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
@@ -441,18 +361,16 @@ def _ScanTreeForContainers(node: ast.AST*,
if fa_s is not None:
nf_s: sizet = len(fa_s)
i_f2: sizet
for i_f2 in range(nf_s):
for i_f2 in range(int(nf_s)):
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
if arg_s is not None:
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
if arg_s is not None: _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: ast.ClassDef* = ast.ClassDef*(node)
bases_s: list[ast.AST*]* = cd_s.bases
if bases_s is not None:
nb_s: sizet = len(bases_s)
i_b2: sizet
for i_b2 in range(nb_s):
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
for i_b2 in range(int(nb_s)): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Attribute:
at_s: ast.Attribute* = ast.Attribute*(node)
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
@@ -462,44 +380,35 @@ def _ScanTreeForContainers(node: ast.AST*,
if elts_s is not None:
ne_s: sizet = len(elts_s)
i_e2: sizet
for i_e2 in range(ne_s):
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
for i_e2 in range(int(ne_s)): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.UnaryOp:
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Expr:
ex_s: rawptr[ast.Expr] = rawptr[ast.Expr](node)
ex_s: ast.Expr* = ast.Expr*(node)
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Return:
rt_s: rawptr[ast.Return] = rawptr[ast.Return](node)
rt_s: ast.Return* = ast.Return*(node)
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Compare:
cp_s: rawptr[ast.Compare] = rawptr[ast.Compare](node)
cp_s: ast.Compare* = ast.Compare*(node)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
cps_s: list[ast.AST*]* = cp_s.comparators
if cps_s is not None:
nc_s: sizet = len(cps_s)
i_c2: sizet
for i_c2 in range(nc_s):
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
for i_c2 in range(int(nc_s)): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
bo_s: ast.BoolOp* = ast.BoolOp*(node)
bvs_s: list[ast.AST*]* = bo_s.values
if bvs_s is not None:
nv_s: sizet = len(bvs_s)
i_v2: sizet
for i_v2 in range(nv_s):
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数)与数组数据初始化,并非容器使用;
# 容器的真实使用由类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。
# elif k_s == ast.ASTKind.List:
# need_list_box[0] = 1
# elif k_s == ast.ASTKind.Dict:
# need_dict_box[0] = 1
# 递归语句块子节点
for i_v2 in range(int(nv_s)): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
# `[...]`/`{}` literals do not trigger _list/_dict imports (compile-time constant lists, array init);
# recurse into statement-block children.
if node.children is not None:
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
ci_s: sizet
for ci_s in range(n_s):
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
for ci_s in range(int(n_s)): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
+132 -214
View File
@@ -1,160 +1,109 @@
import t, c
from stdint import *
import string
import stdlib
import memhub
import viperlib
import w32.fileio as fileio
import w32.win32file
import w32.win32base
import ast
import llvmlite
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.CompilerFactory as CompilerFactory
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.Stub.StubGen.Converter as StubConverter
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
import lib.Phase.Phase1 as Phase1
# ============================================================
# Phase1Topo - 按类继承与 import 依赖对源文件做拓扑排序
#
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
# ============================================================
# Phase1Topo - topologically sort source files by class inheritance and import dependencies.
# Functions live in this module (accessed via its prefix); globals go through Phase1._xxx.
# ============================================================
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序(Kahn)
#
# 步骤:1) 从扁平类信息数组构建「文件 SHA1 → 依赖文件 SHA1 集合」映射;2) 计算每个文件入度(DepCount,初始化 InDegree = DepCount);
# 3) 入度为 0 的先处理,处理后减少依赖它的文件入度;4) 重复直到全部处理完成。
# 依赖方向是「父类所在文件 → 子类所在文件」,即父类文件必须先处理。
#
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否用可达集合过滤;
# sha1_set SHA1 集合;set_count SHA1 集合数量;class_names_buf 类名扁平数组;parent_names_buf 父类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;
# class_count 类数量;out_order 输出拓扑顺序的文件索引数组(调用者分配,int 数组);out_count 输出文件数量
# Returns: 0 成功,-1 失败
# ============================================================
def _TopoSortFiles(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult],
reachable_set: str, reachable_count: int,
use_reachable: int,
sha1_set: str, set_count: int,
class_names_buf: bytes, parent_names_buf: bytes,
def_sha1s_buf: bytes, class_count: int,
out_order: t.CPtr, out_count: t.CPtr,
temp_dir: str, td_len_ts: sizet) -> int:
"""根据类继承关系对文件进行拓扑排序"""
if result is None or out_order is None or out_count is None:
return -1
# _TopoSortFiles - topologically sort files by class inheritance (Kahn).
# Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1.
def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: rawptr, out_count: rawptr, temp_dir: str, td_len_ts: sizet) -> int:
"""Topologically sort files by class inheritance and import dependencies."""
if result is None or out_order is None or out_count is None: return -1
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
# 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags.
# Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing.
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
file_deps_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_indegrees: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
# 取消整数提升后,模块属性 CDefine(Phase1.MAX_*) 按最小位宽物化为 i16/i8;三段乘积 256*16*17=69632 会在 i16 回绕,
# 首乘数显式 sizet() 加宽到 64 位。上一行 256*17=4352 未超 i16,故无需改。
file_deps_buf: bytes = mb.alloc(sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
file_dep_counts: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_indegrees: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_processed: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
if file_sha1s_buf is not None:
mb.free(file_sha1s_buf)
if file_deps_buf is not None:
mb.free(file_deps_buf)
if file_dep_counts is not None:
mb.free(file_dep_counts)
if file_indegrees is not None:
mb.free(file_indegrees)
if file_processed is not None:
mb.free(file_processed)
if file_sha1s_buf is not None: mb.free(file_sha1s_buf)
if file_deps_buf is not None: mb.free(file_deps_buf)
if file_dep_counts is not None: mb.free(file_dep_counts)
if file_indegrees is not None: mb.free(file_indegrees)
if file_processed is not None: mb.free(file_processed)
return -1
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
string.memset(file_deps_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
string.memset(file_deps_buf, 0, sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4)
string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4)
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
file_count: int = 0
# sha1→file_index 映射(简单线性查找,文件数不多)
for i in range(result.Count):
# sha1 -> file_index map (simple linear search; file count is small)
for i in range(int(result.Count)):
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
if entry is None or entry.Sha1 is None: continue
sha1_e: str = entry.Sha1
in_set: int = 0
if use_reachable != 0:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0: continue
if file_count >= Phase1.MAX_FILE_DEPS:
break
if file_count >= Phase1.MAX_FILE_DEPS: break
# 初始化扁平数组:文件 SHA1(偏移 = file_count * Phase1.SHA1_LEN)
# DepCount/InDegree/Processed 已由 memset 清零
# Initialize the flat array: file SHA1 (offset = file_count * Phase1.SHA1_LEN).
# DepCount/InDegree/Processed are already zeroed by memset.
sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN
string.strcpy(sha1_dst, sha1_e)
file_count += 1
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
for ci in range(class_count):
# 读取父类名(偏移 = ci * Phase1.CLASS_NAME_LEN)
# 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge
for ci in range(int(class_count)):
# Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN)
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
if parent_ptr[0] == '\0':
continue
if parent_ptr[0] == '\0': continue
# 读取子类定义 SHA1(偏移 = ci * Phase1.SHA1_LEN)
# Read child class definition SHA1 (offset = ci * Phase1.SHA1_LEN)
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
# 查找父类定义所在文件的 SHA1
# Find the SHA1 of the file defining the parent class
parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
if parent_sha1 is None:
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
continue # parent not in includes (e.g. Object, CEnum marker base classes)
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
if string.strcmp(parent_sha1, child_def_sha1) == 0:
continue
# The parent file SHA1 must differ from the child's (same-file inheritance needs no dependency)
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
for fi in range(file_count):
# Find the child file's index and add the parent file SHA1 to its dependency list
for fi in range(int(file_count)):
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
if string.strcmp(fsha1, child_def_sha1) != 0:
continue
if string.strcmp(fsha1, child_def_sha1) != 0: continue
# 读取当前依赖数量
# Read the current dependency count
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
cnt_ptr: t.CPtr = (int | t.CPtr)(cnt_addr)
if cnt_ptr is None:
break
cnt_ptr: int* = (int*)(cnt_addr)
if cnt_ptr is None: break
cur_cnt: int = cnt_ptr[0]
# 检查依赖是否已添加(去重)
# Check whether the dependency is already present (dedupe)
already: int = 0
for di in range(cur_cnt):
for di in range(int(cur_cnt)):
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
if string.strcmp(dep_slot, parent_sha1) == 0:
already = 1
break
if already != 0:
break
if already != 0: break
if cur_cnt < Phase1.MAX_DEP_EDGES:
dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN
@@ -162,59 +111,44 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
cnt_ptr[0] = cur_cnt + 1
break
# 2.5 补充 import 依赖边:被 import 的模块先完整翻译。
#
# 仅按类继承建边不够:跨模块全局变量 stub、跨模块 vtable TypeStr、泛型模板都要求依赖模块先翻译,而它们之间可能无类继承关系。
# deps(_includes_deps_*)由 Phase 1a-pre 填充(含容器按需注入的 _list/_dict);用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组
# (与 _BuildReachableSha1Set 的 BFS 一致),避免直接跨函数传全局 store 返回值导致地址异常。
# 2.5 Add import dependency edges: imported modules must be fully translated first.
# Class inheritance alone is insufficient (cross-module globals/vtable TypeStr/generics need deps); deps come from Phase 1a-pre, mapped via _BuildIncludesSha1Map.
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
map_count_ts: int = 0
if sha1_arr_ts is not None and mod_arr_ts is not None:
map_count_ts = StubMergerIR._BuildIncludesSha1Map(
temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
for fi_imp in range(file_count):
if map_count_ts <= 0:
break
map_count_ts = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
for fi_imp in range(int(file_count)):
if map_count_ts <= 0: break
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
if deps_imp is None:
continue
if deps_imp is None: continue
dl_imp: sizet = string.strlen(deps_imp)
dpos_imp: sizet = 0
while dpos_imp < dl_imp:
dstart_imp: sizet = dpos_imp
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ':
dpos_imp += 1
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ': dpos_imp += 1
dlen_imp: sizet = dpos_imp - dstart_imp
if dpos_imp < dl_imp:
dpos_imp += 1
if dlen_imp == 0:
continue
if dpos_imp < dl_imp: dpos_imp += 1
if dlen_imp == 0: continue
dname_imp: bytes = stdlib.malloc(dlen_imp + 1)
if dname_imp is None:
continue
if dname_imp is None: continue
string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp)
dname_imp[dlen_imp] = '\0'
dsha1_imp: str = StubMergerIR._FindSha1ByModName(
sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
dsha1_imp: str = StubMergerIR._FindSha1ByModName(sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
stdlib.free(dname_imp)
if dsha1_imp is None:
continue
# 找依赖文件在列表中的索引(跳过自身)
for fj_imp in range(file_count):
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0:
continue
if fj_imp == fi_imp:
break
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
if dsha1_imp is None: continue
# Find the dependency file's index in the list (skip self)
for fj_imp in range(int(file_count)):
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
if fj_imp == fi_imp: break
# Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++)
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
cnt_ptr_imp: t.CPtr = (int | t.CPtr)(cnt_addr_imp)
if cnt_ptr_imp is None:
break
cnt_ptr_imp: int* = (int*)(cnt_addr_imp)
if cnt_ptr_imp is None: break
cur_cnt_imp: int = cnt_ptr_imp[0]
already_imp: int = 0
for di_imp in range(cur_cnt_imp):
for di_imp in range(int(cur_cnt_imp)):
dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN
if string.strcmp(dep_slot_imp, dsha1_imp) == 0:
already_imp = 1
@@ -225,117 +159,104 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
cnt_ptr_imp[0] = cur_cnt_imp + 1
break
# 释放 import 边用的局部映射数组
if sha1_arr_ts is not None:
stdlib.free(sha1_arr_ts)
if mod_arr_ts is not None:
stdlib.free(mod_arr_ts)
# Free the local mapping arrays used for import edges
if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts)
if mod_arr_ts is not None: stdlib.free(mod_arr_ts)
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
for fi in range(file_count):
# 3. Kahn: InDegree = DepCount (number of parent files depended on).
# Process zero-dependency files first, then decrement the in-degree of dependents.
for fi in range(int(file_count)):
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
cnt_ptr3: t.CPtr = (int | t.CPtr)(cnt_addr3)
cnt_ptr3: int* = (int*)(cnt_addr3)
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr3: t.CPtr = (int | t.CPtr)(ind_addr3)
if cnt_ptr3 is not None and ind_ptr3 is not None:
ind_ptr3[0] = cnt_ptr3[0]
ind_ptr3: int* = (int*)(ind_addr3)
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
# 4. 拓扑排序输出
# 4. Topological sort output
out_idx: int = 0
changed: int = 1
while changed != 0:
changed = 0
for fi in range(file_count):
# 检查处理状态
for fi in range(int(file_count)):
# Check processed flag
proc_addr4: uint64 = uint64(file_processed) + fi * 4
proc_ptr4: t.CPtr = (int | t.CPtr)(proc_addr4)
if proc_ptr4 is None or proc_ptr4[0] != 0:
continue
# 检查入度
proc_ptr4: int* = (int*)(proc_addr4)
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
# Check in-degree
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr4: t.CPtr = (int | t.CPtr)(ind_addr4)
if ind_ptr4 is None or ind_ptr4[0] > 0:
continue
ind_ptr4: int* = (int*)(ind_addr4)
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
# 入度为 0,可以处理
# 找到对应的 result.Entries 索引
# In-degree 0: can be processed
# Find the matching result.Entries index
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
for ri in range(result.Count):
for ri in range(int(result.Count)):
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
ent_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r)
ent_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r)
if ent_r is not None and ent_r.Sha1 is not None:
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
# 写入输出索引数组
# Write into the output index array
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr: t.CPtr = (int | t.CPtr)(out_idx_addr)
if out_int_ptr is not None:
out_int_ptr[0] = ri
out_int_ptr: int* = (int*)(out_idx_addr)
if out_int_ptr is not None: out_int_ptr[0] = ri
out_idx += 1
break
# 标记已处理
# Mark as processed
proc_ptr4[0] = 1
changed = 1
# 减少依赖该文件的其他文件的入度
for fj in range(file_count):
# Decrement the in-degree of files that depend on this one
for fj in range(int(file_count)):
proc_addr5: uint64 = uint64(file_processed) + fj * 4
proc_ptr5: t.CPtr = (int | t.CPtr)(proc_addr5)
if proc_ptr5 is None or proc_ptr5[0] != 0:
continue
# 读取 fj 的依赖数量
proc_ptr5: int* = (int*)(proc_addr5)
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
# Read fj's dependency count
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
cnt_ptr5: t.CPtr = (int | t.CPtr)(cnt_addr5)
if cnt_ptr5 is None:
continue
cnt_ptr5: int* = (int*)(cnt_addr5)
if cnt_ptr5 is None: continue
fj_dep_cnt: int = cnt_ptr5[0]
for di in range(fj_dep_cnt):
for di in range(int(fj_dep_cnt)):
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
if string.strcmp(dep_slot5, target_sha1) == 0:
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
ind_ptr5: t.CPtr = (int | t.CPtr)(ind_addr5)
if ind_ptr5 is not None:
ind_ptr5[0] = ind_ptr5[0] - 1
ind_ptr5: int* = (int*)(ind_addr5)
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
break
# 处理循环依赖:环内优先输出被依赖者(被其余剩余文件依赖数量多者先)。
#
# 容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import 会形成伪环,Kahn 无法完全归零;若按扫描顺序追加,
# 依赖者会先于被依赖者翻译 → 跨模块 vtable TypeStr 未注册 → 误杀。
# Handle cycles: within a cycle emit the more-depended-upon files first.
# On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr.
if out_idx < file_count:
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
rem_idx: int* = (int*)(stdlib.malloc(4 * file_count))
rem_depcnt: int* = (int*)(stdlib.malloc(4 * file_count))
rem_count: int = 0
if rem_idx is not None and rem_depcnt is not None:
for fi in range(file_count):
for fi in range(int(file_count)):
proc_addr7: uint64 = uint64(file_processed) + fi * 4
proc_ptr7: t.CPtr = (int | t.CPtr)(proc_addr7)
proc_ptr7: int* = (int*)(proc_addr7)
if proc_ptr7 is not None and proc_ptr7[0] == 0:
rem_idx[rem_count] = fi
rem_depcnt[rem_count] = 0
rem_count += 1
# 计算每个剩余文件的被依赖数(被其余剩余文件依赖)
for i_a in range(rem_count):
# Compute each remaining file's dependents count (among the remaining files)
for i_a in range(int(rem_count)):
fi_a: int = rem_idx[i_a]
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
for i_b in range(rem_count):
for i_b in range(int(rem_count)):
fi_b: int = rem_idx[i_b]
if fi_b == fi_a:
continue
if fi_b == fi_a: continue
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
cnt_ptr_b: t.CPtr = (int | t.CPtr)(cnt_addr_b)
if cnt_ptr_b is None:
continue
cnt_ptr_b: int* = (int*)(cnt_addr_b)
if cnt_ptr_b is None: continue
cnt_b: int = cnt_ptr_b[0]
for di_b in range(cnt_b):
for di_b in range(int(cnt_b)):
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
break
# 冒泡降序(被依赖数多者在前)
for i_a in range(rem_count):
for j_a in range(rem_count - 1):
# Bubble sort descending (more dependents first)
for i_a in range(int(rem_count)):
for j_a in range(int(rem_count - 1)):
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
tmp_i: int = rem_idx[j_a]
rem_idx[j_a] = rem_idx[j_a + 1]
@@ -343,35 +264,32 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
tmp_d: int = rem_depcnt[j_a]
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
rem_depcnt[j_a + 1] = tmp_d
# 按序输出剩余文件
for i_a in range(rem_count):
# Emit the remaining files in order
for i_a in range(int(rem_count)):
fi_a2: int = rem_idx[i_a]
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
proc_ptr8: t.CPtr = (int | t.CPtr)(proc_addr8)
if proc_ptr8 is None or proc_ptr8[0] != 0:
continue
proc_ptr8: int* = (int*)(proc_addr8)
if proc_ptr8 is None or proc_ptr8[0] != 0: continue
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
for ri in range(result.Count):
for ri in range(int(result.Count)):
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
ent_r2: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r2)
ent_r2: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r2)
if ent_r2 is not None and ent_r2.Sha1 is not None:
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr2: t.CPtr = (int | t.CPtr)(out_idx_addr2)
if out_int_ptr2 is not None:
out_int_ptr2[0] = ri
out_int_ptr2: int* = (int*)(out_idx_addr2)
if out_int_ptr2 is not None: out_int_ptr2[0] = ri
out_idx += 1
break
proc_ptr8[0] = 1
stdlib.free(rem_idx)
stdlib.free(rem_depcnt)
# 写入输出数量
out_count_ptr: t.CPtr = (int | t.CPtr)(out_count)
if out_count_ptr is not None:
out_count_ptr[0] = out_idx
# Write the output count
out_count_ptr: int* = (int*)(out_count)
if out_count_ptr is not None: out_count_ptr[0] = out_idx
# 释放扁平数组
# Free the flat arrays
mb.free(file_sha1s_buf)
mb.free(file_deps_buf)
mb.free(file_dep_counts)
+12 -12
View File
@@ -1,19 +1,19 @@
import t, c
from stdint import *
import memhub
# 全局 mbuddy 指针
# Global mbuddy pointer
_mbuddy: memhub.MemBuddy*
# 源代码缓冲区大小(1MB)
SRC_BUF_SIZE: t.CDefine = 1048576
# Source buffer size (1MB)
define SRC_BUF_SIZE = 1048576
# vpi 缓冲区大小(256KB)
PYI_BUF_SIZE: sizet = 262144
# vpi buffer size (256KB).
# Compile-time constant via define: a module-level global would emit a strong symbol per module, causing llvm-link errors.
define PYI_BUF_SIZE = 262144
# 拓扑排序相关常量
MAX_CLASSES: t.CDefine = 512
MAX_FILE_DEPS: t.CDefine = 256
MAX_DEP_EDGES: t.CDefine = 16
SHA1_LEN: t.CDefine = 17
CLASS_NAME_LEN: t.CDefine = 64
# Topological sort constants
define MAX_CLASSES = 512
define MAX_FILE_DEPS = 256
define MAX_DEP_EDGES = 16
define SHA1_LEN = 17
define CLASS_NAME_LEN = 64
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+73 -140
View File
@@ -1,57 +1,38 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import viperlib
import hashlib
import w32.win32file
import w32.win32base
import w32.fileio as fileio
import win32.win32file
import win32.win32base
import win32.fileio as fileio
# ============================================================
# Utils - 工程工具函数(SHA1 计算、目录清理等)
# ============================================================
# Utils - project utility functions (SHA1 hashing, directory cleanup)
# 全局 mbuddy 指针
# Global mbuddy pointer
_mbuddy: memhub.MemBuddy*
# ============================================================
# nibble_to_hex_char - 半字节转十六进制字符
# ============================================================
# nibble_to_hex_char - convert a nibble to a hex character
def nibble_to_hex_char(n: int, uppercase: int) -> char:
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
if n < 10: return '0' + n
if uppercase != 0: return 'A' + (n - 10)
return 'a' + (n - 10)
"""Convert a 0-15 nibble to a hex character (uppercase=1 uses 'A'-'F')."""
if n < 10: return char('0' + n)
if uppercase != 0: return char('A' + (n - 10))
return char('a' + (n - 10))
# ============================================================
# bytes_to_hex - 字节序列转十六进制字符串
# ============================================================
def bytes_to_hex(pool: memhub.MemBuddy*,
digest: bytes,
nbytes: sizet,
uppercase: int,
use_malloc: int) -> str:
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
"""
if digest is None or nbytes <= 0:
return None
# bytes_to_hex - convert a byte sequence to a hex string
def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase: int, use_malloc: int) -> str:
"""Convert the first nbytes of digest to a null-terminated hex string."""
if digest is None or nbytes <= 0: return None
hex_len: sizet = nbytes * 2 + 1
hex_buf: str = None
if use_malloc != 0:
hex_buf = stdlib.malloc(hex_len)
else:
hex_buf = pool.alloc(hex_len)
if hex_buf is None:
return None
for i in range(nbytes):
if use_malloc != 0: hex_buf = stdlib.malloc(hex_len)
else: hex_buf = pool.alloc(hex_len)
if hex_buf is None: return None
for i in range(int(nbytes)):
hi: int = (digest[i] >> 4) & 0xF
lo: int = digest[i] & 0xF
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
@@ -60,52 +41,38 @@ def bytes_to_hex(pool: memhub.MemBuddy*,
return hex_buf
# ============================================================
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符(与 Projectrans.py 一致)
# ============================================================
# compute_sha1 - compute a string SHA1, return the first 16 hex chars
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
if content is None or pool is None:
return None
"""Compute the SHA1 of a string, returning its first 16 hex chars."""
if content is None or pool is None: return None
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
# ctx is stack-allocated (alloca); no heap allocation needed
ctx: hashlib.sha1* = hashlib.sha1()
if ctx is None: return None
# 计算摘要
# Compute the digest
ctx.update(content)
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
if digest is None: return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
# Convert to a hex string (first 8 bytes = 16 hex chars)
return bytes_to_hex(pool, digest, 8, 0, 0)
# ============================================================
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
# ============================================================
# is_vp_at - check whether a path segment ends with .vp
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
"""
if p is None or plen <= 3:
return 0
"""Return 1 if p[start, start+plen) ends with the .vp suffix."""
if p is None or plen <= 3: return 0
base: sizet = start + plen - 3
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
return 1
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p': return 1
return 0
# ============================================================
# JoinPath - 拼接两个路径段为 a/b
# ============================================================
# JoinPath - join two path segments as a/b
def JoinPath(a: str, b: str) -> str:
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
if a is None or b is None:
return None
"""Join two path segments as "a/b"; caller frees the malloc'd result."""
if a is None or b is None: return None
a_len: sizet = string.strlen(a)
b_len: sizet = string.strlen(b)
total_len: sizet = a_len + b_len + 2
@@ -115,17 +82,11 @@ def JoinPath(a: str, b: str) -> str:
return buf
# ============================================================
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
# ============================================================
# read_file_to_buf - read a whole file into a null-terminated buffer
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
"""
"""Read a whole file into a null-terminated malloc buffer, or None."""
if path is None or buf_size <= 0: return None
sf: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
sf: fileio.File* = fileio.File(path, fileio.MODE.R)
if sf.closed: return None
buf: bytes = stdlib.malloc(buf_size)
if buf is None:
@@ -136,25 +97,16 @@ def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
if br <= 0:
stdlib.free(buf)
return None
if br < buf_size:
buf[br] = 0
if br < buf_size: buf[br] = 0
return buf
# ============================================================
# PathToModuleName - 文件路径转模块名
# ============================================================
# PathToModuleName - convert a file path to a module name
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
"""
if path is None:
return None
"""Convert a file path to a dotted module name; caller frees the result."""
if path is None: return None
plen: sizet = string.strlen(path)
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
# Skip the includes/ or includes\ prefix
prefix_len: sizet = 9
path_start: sizet = 0
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
@@ -162,59 +114,45 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
if psep_ch == '/' or psep_ch == '\\':
path_start = prefix_len
plen = plen - prefix_len
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
if is_vp_at(path, path_start, plen) != 0:
plen -= 3
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
# Strip the .vp suffix
if is_vp_at(path, path_start, plen) != 0: plen -= 3
if plen == 0: return None
# Check for a trailing /__init__ or \__init__ (package __init__.vp)
if plen > 9:
tail_off: sizet = path_start + plen - 9
sep_char: char = path[tail_off]
if (sep_char == '/' or sep_char == '\\') \
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
if (sep_char == '/' or sep_char == '\\') and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
# Drop the separator + __init__ suffix
plen = tail_off - path_start
if plen == 0:
return None
# 复制并替换 / 为 .
if plen == 0: return None
# Copy and replace / with .
result: str = stdlib.malloc(plen + 1)
if result is None:
return None
for ri in range(plen):
if result is None: return None
for ri in range(int(plen)):
ch: char = path[path_start + ri]
if ch == '/' or ch == '\\':
result[ri] = '.'
else:
result[ri] = ch
if ch == '/' or ch == '\\': result[ri] = '.'
else: result[ri] = ch
result[plen] = '\0'
return result
# ============================================================
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身;用于 --clean)
# ============================================================
# CleanDir - recursively delete all files/subdirs under dir_path (for --clean)
def CleanDir(dir_path: str) -> int:
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
if dir_path is None:
return -1
"""Recursively delete files/subdirs under dir_path, returning the count."""
if dir_path is None: return -1
dir_len: sizet = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 8)
if pattern is None:
return -1
if pattern is None: return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
stdlib.free(pattern)
return -1
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
string.memset(find_data, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
handle: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(pattern, find_data)
if handle == win32.win32base.INVALID_HANDLE_VALUE:
stdlib.free(pattern)
stdlib.free(find_data)
return 0
@@ -223,34 +161,29 @@ def CleanDir(dir_path: str) -> int:
while True:
fname: str = find_data.cFileName
if fname is not None:
# 跳过 . 和 ..
# Skip . and ..
if fname[0] == '.':
if fname[1] == '\0':
fname = None
elif fname[1] == '.' and fname[2] == '\0':
fname = None
if fname[1] == '\0': fname = None
elif fname[1] == '.' and fname[2] == '\0': fname = None
if fname is not None:
full_path: bytes = JoinPath(dir_path, fname)
if full_path is not None:
# 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
is_dir: uint32 = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
# Check whether this is a directory
is_dir: uint32 = find_data.dwFileAttributes & win32.win32file.FILE_ATTRIBUTE_DIRECTORY
if is_dir != 0:
# 递归清理子目录中的文件
# Recurse into the subdirectory
sub_deleted: int = CleanDir(full_path)
if sub_deleted > 0:
deleted += sub_deleted
# 删除空子目录(RemoveDirectoryA 要求目录为空)
w32.win32file.RemoveDirectoryA(full_path)
if sub_deleted > 0: deleted += sub_deleted
# Remove the now-empty subdirectory
win32.win32file.RemoveDirectoryA(full_path)
else:
# 删除文件
if w32.win32file.DeleteFileA(full_path) != 0:
deleted += 1
# Delete the file
if win32.win32file.DeleteFileA(full_path) != 0: deleted += 1
stdlib.free(full_path)
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
w32.win32file.FindClose(handle)
win32.win32file.FindClose(handle)
stdlib.free(pattern)
stdlib.free(find_data)
return deleted
+1 -7
View File
@@ -1,12 +1,6 @@
import t, c
from stdint import *
from .Config import Load_project_config, resolve_paths
from .Utils import compute_sha1, CleanDir
__all__ = [
'Load_project_config',
'resolve_paths',
'compute_sha1',
'CleanDir',
]
__all__ = ['Load_project_config', 'resolve_paths', 'compute_sha1', 'CleanDir',]
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+124 -254
View File
@@ -1,254 +1,124 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerCollect - 切片路径构建与 stub 文件 SHA1 收集
#
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
# ============================================================
# ============================================================
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free;level>0 时自动调 BuildPipeline.ensure_dir 建子目录)
#
# 按 Config.Sha1SliceLevel 分散到 SHA1 前缀子目录:level=0 → {temp_dir}/{sha1}.{ext};level=1 → {temp_dir}/b7/{sha1}.{ext};level=2 → {temp_dir}/b7/90/{sha1}.{ext}
#
# Args: temp_dir 基础目录;td_len temp_dir 长度(避免重复 strlen);sha1 SHA1 字符串;ext 文件扩展名(如 "stub.ll")
# Returns: 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
# ============================================================
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
level>0 时自动调用 ensure_dir 创建子目录。
"""
if temp_dir is None or sha1 is None or ext is None:
return None
sha1_len: sizet = string.strlen(sha1)
ext_len: sizet = string.strlen(ext)
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
sub_len: sizet = 0
if subdir_buf is not None:
sub_len = string.strlen(subdir_buf)
# 确保子目录存在: {temp_dir}/{subdir}
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
# 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。
dir_path_len: sizet = td_len + sub_len + 2
dir_path: bytes = stdlib.malloc(dir_path_len)
if dir_path is not None:
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
BuildPipeline.ensure_dir(dir_path)
stdlib.free(dir_path)
# 构建完整路径
path: str = None
path_len: sizet = 0
if sub_len > 0:
path_len = td_len + sub_len + sha1_len + ext_len + 4
path = stdlib.malloc(path_len)
if path is not None:
viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
else:
path_len = td_len + sha1_len + ext_len + 3
path = stdlib.malloc(path_len)
if path is not None:
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
return path
# ============================================================
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1(从文件名提取)
#
# level=0 → 扫描 {base_dir}/*.stub.ll;level>0 → 扫描 {base_dir}/* 的子目录并对每个子目录递归调用。
#
# Args: base_dir 当前扫描目录;bd_len base_dir 长度;level 剩余递归层数;out_arr 输出数组(每个 SHA1 17 字节 = 16字符 + null);out_count 当前已收集数量;max_count 最大数量
# Returns: 收集后的总数量
# ============================================================
def _collect_stub_sha1s(base_dir: str, bd_len: sizet, level: int,
out_arr: str, out_count: int, max_count: int) -> int:
"""递归扫描子目录,收集 .stub.ll 文件的 SHA1"""
if base_dir is None or out_arr is None:
return out_count
if out_count >= max_count:
return out_count
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
return out_count
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
if level <= 0:
# 扫描 {base_dir}/*.stub.ll
pattern: bytes = stdlib.malloc(bd_len + 16)
if pattern is None:
stdlib.free(find_data)
return out_count
viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir)
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
stdlib.free(pattern)
if handle != win32base.INVALID_HANDLE_VALUE:
while True:
if out_count >= max_count:
break
fname: str = find_data.cFileName
if fname is not None:
if fname[0] != '\0':
# 从文件名提取 SHA1(前 16 字符)
string.strncpy(out_arr + out_count * 17, fname, 16)
out_arr[out_count * 17 + 16] = '\0'
out_count += 1
if win32file.FindNextFileA(handle, find_data) == 0:
break
win32file.FindClose(handle)
else:
# 扫描 {base_dir}/* 的子目录
pattern = stdlib.malloc(bd_len + 4)
if pattern is None:
stdlib.free(find_data)
return out_count
viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir)
handle = win32file.FindFirstFileA(pattern, find_data)
stdlib.free(pattern)
if handle != win32base.INVALID_HANDLE_VALUE:
while True:
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
attrs: uint32 = find_data.dwFileAttributes
if (attrs & 16) != 0:
fname = find_data.cFileName
if fname is not None:
# 跳过 "." 和 ".."
is_dot: int = 0
if fname[0] == '.':
if fname[1] == '\0':
is_dot = 1
elif fname[1] == '.' and fname[2] == '\0':
is_dot = 1
if is_dot == 0:
# 构建子目录路径并递归扫描
sub_dir: str = Utils.JoinPath(base_dir, fname)
if sub_dir is not None:
sub_dir_len: sizet = string.strlen(sub_dir)
out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
stdlib.free(sub_dir)
if win32file.FindNextFileA(handle, find_data) == 0:
break
win32file.FindClose(handle)
stdlib.free(find_data)
return out_count
# ============================================================
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
#
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
# ============================================================
def _load_includes_sha1_set(pool: memhub.MemBuddy*,
temp_dir: str,
sha1_set: str) -> int:
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
返回找到的 includes SHA1 数量,-1 表示错误"""
if sha1_set is None:
return -1
if StubMerger._sha1_store_arr is None:
return -1
count: int = 0
for i in range(StubMerger._sha1_store_count):
if count >= StubMerger.MAX_INCLUDES_SHA1:
break
idx: sizet = sizet(i) * 17
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
count += 1
return count
# ============================================================
# _is_in_sha1_set - 检查 SHA1 是否在集合中
# ============================================================
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
"""检查 sha1 是否在集合中"""
if sha1 is None or sha1_set is None:
return 0
for i in range(set_count):
if string.strcmp(sha1_set + i * 17, sha1) == 0:
return 1
return 0
# ============================================================
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(仅人类可读的调试输出,程序内部不读取,
# 机器分析用 PopulateSha1MapStore 填充的全局内存存储器)
#
# Args: mb 内存池;temp_dir 临时目录(_sha1_map.txt 写入位置);scan_result IncludesScanner 扫描结果
# Returns: 0 成功,非 0 失败
# ============================================================
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str,
scan_result: rawptr[IncludesScanner.ScanResult],
filter_set: str,
filter_count: int) -> int:
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
"""
if temp_dir is None or scan_result is None:
return 1
# 构造路径 temp_dir/_sha1_map.txt
dir_len: sizet = string.strlen(temp_dir)
map_path: bytes = stdlib.malloc(dir_len + 32)
if map_path is None:
return 1
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
# 打开文件写入(CREATE_ALWAYS)
f: rawptr[fileio.File] = fileio.File(map_path, fileio.MODE.W)
if f.closed:
return 1
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
line_buf: bytes = stdlib.malloc(512)
if line_buf is None:
f.close()
return 1
written_count: int = 0
for i in range(scan_result.Count):
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
ent_w: rawptr[IncludesScanner.FileEntry] = (rawptr[IncludesScanner.FileEntry])(ea_w)
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None:
continue
# 若提供过滤集合,只写入集合中的条目
if filter_set is not None and filter_count > 0:
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0:
continue
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
line_len_w: sizet = string.strlen(line_buf)
f.write(line_buf, line_len_w)
written_count += 1
f.close()
return 0
from stdint import *
import memhub
import string
import stdlib
import mlirlite
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
if temp_dir is None or sha1 is None or ext is None: return None
sha1_len: sizet = string.strlen(sha1)
ext_len: sizet = string.strlen(ext)
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
sub_len: sizet = 0
if subdir_buf is not None:
sub_len = string.strlen(subdir_buf)
# Ensure subdir {temp_dir}/{subdir} exists.
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
dir_path_len: sizet = td_len + sub_len + 2
dir_path: bytes = stdlib.malloc(dir_path_len)
if dir_path is not None:
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
BuildPipeline.ensure_dir(dir_path)
stdlib.free(dir_path)
# Build the full path
path: str = None
path_len: sizet = 0
if sub_len > 0:
path_len = td_len + sub_len + sha1_len + ext_len + 4
path = stdlib.malloc(path_len)
if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
else:
path_len = td_len + sha1_len + ext_len + 3
path = stdlib.malloc(path_len)
if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
return path
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking ViperC's own lib/* stubs.
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
if sha1_set is None: return -1
if StubMerger._sha1_store_arr is None: return -1
count: int = 0
for i in range(int(StubMerger._sha1_store_count)):
if count >= StubMerger.MAX_INCLUDES_SHA1: break
idx: sizet = sizet(i) * 17
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
count += 1
return count
# _is_in_sha1_set - check whether a SHA1 is in the set.
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
"""Whether sha1 is present in the set."""
if sha1 is None or sha1_set is None: return 0
for i in range(int(set_count)):
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
return 0
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
if temp_dir is None or scan_result is None: return 1
# Build the path temp_dir/_sha1_map.txt
dir_len: sizet = string.strlen(temp_dir)
map_path: bytes = stdlib.malloc(dir_len + 32)
if map_path is None: return 1
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
# Open the file for writing (CREATE_ALWAYS)
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
if f.closed: return 1
# Write each include entry: {sha1}:includes/{rel_path}\n
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
line_buf: bytes = stdlib.malloc(512)
if line_buf is None:
f.close()
return 1
written_count: int = 0
for i in range(int(scan_result.Count)):
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w)
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
# When a filter set is provided, write only its entries
if filter_set is not None and filter_count > 0:
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
line_len_w: sizet = string.strlen(line_buf)
f.write(line_buf, line_len_w)
written_count += 1
f.close()
return 0
+171 -313
View File
@@ -1,13 +1,12 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import mlirlite
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import viperlib
import sys
import ast
@@ -26,329 +25,194 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerIR - IR 文本去重/注释与模块名-SHA1 映射工具
# ============================================================
# StubMergerIR - module structured sidecar ({sha1}.meta) write/read, submodule checks and module-name/SHA1 mapping helpers
# ============================================================
# IsDeclOnlyModule - 判断模块是否为纯声明文件(读 {temp_dir}/{sha1}.text.ll 扫描是否含 "define " 行)
#
# 返回 1 = 纯声明(无函数体,如全 pass 的模块,无需 .obj);0 = 实现文件(含 define 函数体);-1 = text.ll 不存在/无法判断。
# 与 Phase B+ 编译缺失 includes 时的内联判断逻辑一致,供 Phase C 链接时区分「声明文件无需 .obj」与「实现文件缺失」。
# ============================================================
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
"""判断模块是否为纯声明文件(text.ll 无 define 行)"""
if temp_dir is None or sha1 is None:
return -1
tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll")
if tpath is None:
return -1
tf_d: fileio.File = fileio.File(tpath, fileio.MODE.R)
stdlib.free(tpath)
if tf_d.closed:
return -1
tbuf_d: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
tbr_d: int64 = 0
if tbuf_d is not None:
tbr_d = tf_d.read_all(tbuf_d, StubMerger.STUB_READ_BUF_SIZE)
tf_d.close()
if tbuf_d is None:
return -1
if tbr_d <= 0:
# 空 text.ll:无函数体,纯声明
stdlib.free(tbuf_d)
# WriteModuleMeta - write {sha1}.meta (structured sidecar: funcs + struct layout + vtables).
# Lines: FUNC <mangled> DEF|DECL, STRUCT <sha1>.<name> FIELDS <n> PACKED <0|1>|OPAQUE, VTABLE <sha1>.<name> SLOTS <n>.
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha1: str) -> int:
"""Write the module's func defines, struct layouts and vtables to {sha1}.meta."""
if mod is None or temp_dir is None or sha1 is None:
VLogger.error("meta write: null argument", "Meta")
return 1
if tbr_d < StubMerger.STUB_READ_BUF_SIZE:
tbuf_d[tbr_d] = '\0'
# 逐行扫描:找 "define " 行即为实现文件
tpos_d: sizet = 0
while tpos_d < tbr_d:
tls_d: sizet = tpos_d
while tpos_d < tbr_d:
if tbuf_d[tpos_d] == '\n':
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
if mpath is None:
VLogger.error("meta write: path construction failed", "Meta")
return 1
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
if mf.closed:
VLogger.error(f"meta write: cannot open {mpath}", "Meta")
return 1
cur: mlirlite.Function* = mod.FuncHead
steps: sizet = 0
while cur is not None:
steps += 1
if steps > 100000:
VLogger.error("meta write: function list has a cycle or exceeded the limit", "Meta")
break
nm: str = mlirlite.function_get_name(cur)
if nm is not None:
nlen: sizet = string.strlen(nm)
line: bytes = stdlib.malloc(nlen + 16)
if line is None:
VLogger.error("meta write: line buffer allocation failed", "Meta")
break
tpos_d += 1
tll_d: sizet = tpos_d - tls_d
if tpos_d < tbr_d:
tpos_d += 1
if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0:
stdlib.free(tbuf_d)
return 0
stdlib.free(tbuf_d)
mark: str = "DECL"
if mlirlite.function_is_declared(cur) == 0: mark = "DEF"
viperlib.snprintf(line, nlen + 16, "FUNC %s %s\n", nm, mark)
mf.write(line, string.strlen(line))
stdlib.free(line)
cur = mlirlite.function_get_next(cur)
# 2) Struct layout: STRUCT <name> FIELDS <n> PACKED <0|1> (opaque -> STRUCT <name> OPAQUE)
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
nt_steps: sizet = 0
while nt is not None:
nt_steps += 1
if nt_steps > 100000:
VLogger.error("meta write: named-type list has a cycle or exceeded the limit", "Meta")
break
tn: str = mlirlite.get_struct_name(nt.Ty)
if tn is not None and tn[0] != '\0':
tl: sizet = string.strlen(tn)
lb: bytes = stdlib.malloc(tl + 48)
if lb is None:
VLogger.error("meta write: line buffer allocation failed", "Meta")
break
fc: int = mlirlite.get_struct_fcount(nt.Ty)
if fc < 0: viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn)
else: viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked)
mf.write(lb, string.strlen(lb))
stdlib.free(lb)
nt = nt.Next
# 3) Vtable slots: VTABLE <class> SLOTS <n> (global <class>.vtable; Ty anonymous struct fcount = slots)
gv: mlirlite.GlobalVariable* = mod.GlobalHead
gv_steps: sizet = 0
while gv is not None:
gv_steps += 1
if gv_steps > 100000:
VLogger.error("meta write: global-variable list has a cycle or exceeded the limit", "Meta")
break
gn: str = gv.Name
if gn is not None:
gnl: sizet = string.strlen(gn)
if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0:
base_len: sizet = gnl - 7
base: bytes = stdlib.malloc(base_len + 1)
if base is not None:
string.strncpy(base, gn, base_len)
base[base_len] = '\0'
ar: int = mlirlite.get_struct_fcount(gv.Ty)
if ar < 0: ar = 0
lb2: bytes = stdlib.malloc(base_len + 40)
if lb2 is None: VLogger.error("meta write: line buffer allocation failed", "Meta")
else:
viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar)
mf.write(lb2, string.strlen(lb2))
stdlib.free(lb2)
stdlib.free(base)
gv = gv.Next
mf.close()
stdlib.free(mpath)
return 0
# _MetaHasDef - read {sha1}.meta to check for a function definition ("FUNC ... DEF" line).
# Returns: 1=has DEF (impl file); 0=readable but no DEF (decl-only); -1=missing/unreadable.
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
"""Read {sha1}.meta: 1 if it has a FUNC ... DEF line, 0 if none, -1 if unreadable."""
if temp_dir is None or sha1 is None: return -1
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
if mpath is None: return -1
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
stdlib.free(mpath)
if mf.closed: return -1
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
mbr: int64 = 0
if mbuf is not None: mbr = mf.read_all(mbuf, StubMerger.STUB_READ_BUF_SIZE)
mf.close()
if mbuf is None: return -1
if mbr <= 0:
# Empty meta: no DEF, decl-only
stdlib.free(mbuf)
return 0
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
# Scan line by line: a trailing " DEF" marks an impl file
mpos: sizet = 0
while mpos < mbr:
mls: sizet = mpos
while mpos < mbr:
if mbuf[mpos] == '\n': break
mpos += 1
mll: sizet = mpos - mls
if mpos < mbr: mpos += 1
if mll > 0 and mbuf[mls + mll - 1] == '\r': mll -= 1
if mll >= 10 and string.strncmp(mbuf + mls, "FUNC ", 5) == 0:
if mbuf[mls + mll - 4] == ' ' and string.strncmp(mbuf + mls + mll - 3, "DEF", 3) == 0:
stdlib.free(mbuf)
return 1
stdlib.free(mbuf)
return 0
# IsDeclOnlyModule - whether a module is decl-only (reads the structured definition list in {sha1}.meta).
# 1=decl-only (no FUNC ... DEF, e.g. all-pass modules, no .obj needed); 0=impl; -1=unknown/missing.
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
"""Whether the module is decl-only (its meta has no FUNC ... DEF line)."""
h: int = _MetaHasDef(temp_dir, td_len, sha1)
if h < 0: return -1
if h == 1: return 0
return 1
# ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
#
# 逐字节 strncmp 搜索 func_sig(如 @"sha1.func"(/ @printf(),命中后检查行首是否为 "declare "/"define ",跳过 call 语句
# (避免 strstr 子串匹配误命中 call ... @"func"(...)。
#
# Args: out_buf 已有 IR 文本缓冲区;func_sig 函数签名片段,如 @"sha1.func"(/ @printf(
# Returns: 1=已存在 declare/define, 0=不存在
# ============================================================
def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
"""检查 out_buf 中是否已有函数的 declare 或 define 行"""
if out_buf is None or func_sig is None:
return 0
sig_len: sizet = string.strlen(func_sig)
out_len: sizet = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: sizet = 0
for pos in range(0, out_len - sig_len + 1):
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: sizet = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
return 1
if string.strncmp(out_buf + ls, "define ", 7) == 0:
return 1
return 0
# ============================================================
# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉
#
# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖
# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';'
# 使其成为注释,然后追加 define 行。
# ============================================================
def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int:
"""在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉"""
if out_buf is None or func_sig is None:
return 0
sig_len: sizet = string.strlen(func_sig)
out_len: sizet = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: sizet = 0
while pos + sig_len <= out_len:
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: sizet = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
out_buf[ls] = ';'
return 1
pos += 1
return 0
# ============================================================
# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉
#
# 解决 stub/text 全局变量重复定义问题:stub 中可能是 external 声明
# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。
# ============================================================
def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int:
"""在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';'
注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与
argparse.vp 都声明 StubMerger._mbuddy)时,本地 stub external + 依赖 stub external +
依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 →
合成 IR 重复定义 → llc "redefinition of global '@StubMerger._mbuddy'"(ArgparseTest
回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终
只保留最后一个定义。
"""
if out_buf is None or global_name is None:
return 0
name_len: sizet = string.strlen(global_name)
out_len: sizet = string.strlen(out_buf)
if name_len == 0 or out_len == 0 or name_len > out_len:
return 0
pos: sizet = 0
commented: int = 0
while pos + name_len <= out_len:
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
# 确认是行首(前一个是换行或 pos==0)
if pos == 0 or out_buf[pos - 1] == '\n':
out_buf[pos] = ';'
commented = 1
pos += 1
return commented
def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int:
"""检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释)
用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明,
依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方
会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。
"""
if out_buf is None or global_name is None:
return 0
name_len: sizet = string.strlen(global_name)
out_len: sizet = string.strlen(out_buf)
if name_len == 0 or out_len == 0 or name_len > out_len:
return 0
pos: sizet = 0
while pos + name_len <= out_len:
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
# 确认是行首(前一个是换行或 pos==0)
if pos == 0 or out_buf[pos - 1] == '\n':
return 1
pos += 1
return 0
# ============================================================
# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉
#
# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明
#(如 %"sha1.Widget" = type opaque),而依赖 stub 包含完整定义
#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明
# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。
# ============================================================
def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
"""在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';'
type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容)
返回 1=已注释, 0=未找到
"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: sizet = string.strlen(type_name_prefix)
out_len: sizet = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: sizet = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0)
if pos == 0 or out_buf[pos - 1] == '\n':
# 检查后面是否是 " = type opaque"
p: sizet = pos + prefix_len
# 跳过空格
while p < out_len and out_buf[p] == ' ':
p += 1
if p < out_len and out_buf[p] == '=':
p += 1
while p < out_len and out_buf[p] == ' ':
p += 1
if p + 11 <= out_len:
if string.strncmp(out_buf + p, "type opaque", 11) == 0:
# 找到 opaque 声明,将行首改为 ';'
out_buf[pos] = ';'
return 1
pos += 1
return 0
# ============================================================
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
#
# deps_buf 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer
# 子模块类型;模块名含 '.' 时检查其顶层包名是否在 deps_buf 中(deps_buf = "ast stdio string" 时 "ast.lexer" → 1,"foo.bar" → 0)。
# ============================================================
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
if deps_buf is None or mod_name is None:
return 0
# 查找第一个 '.',提取顶层包名
"""Whether mod_name is a submodule of a package imported in deps_buf."""
if deps_buf is None or mod_name is None: return 0
# Find the first '.' and extract the top-level package name
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
if dot_pos is None:
return 0
if dot_pos is None: return 0
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
if pkg_len == 0 or pkg_len >= 256:
return 0
# 构造顶层包名(截断到 '.' 之前)
# 直接在 mod_name 上临时截断检查,避免分配
if pkg_len == 0 or pkg_len >= 256: return 0
# Build the top-level package name (truncated before '.') by temporary in-place
# truncation to avoid an allocation
saved_ch: char = mod_name[pkg_len]
mod_name[pkg_len] = '\0'
result: int = GenApi.is_module_imported(deps_buf, mod_name)
mod_name[pkg_len] = saved_ch
return result
# ============================================================
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque)
#
# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=')
# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } >
# 返回 1=已有完整定义, 0=无完整定义
# ============================================================
def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
"""检查 out_buf 中是否已有 type_name_prefix 的完整定义"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: sizet = string.strlen(type_name_prefix)
out_len: sizet = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: sizet = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';')
is_line_start: int = 0
if pos == 0:
is_line_start = 1
elif out_buf[pos - 1] == '\n':
is_line_start = 1
if is_line_start == 1:
# 检查后面是否是 " = type {" 或 " = type <"
p: sizet = pos + prefix_len
while p < out_len and out_buf[p] == ' ':
p += 1
if p < out_len and out_buf[p] == '=':
p += 1
while p < out_len and out_buf[p] == ' ':
p += 1
if p + 5 < out_len:
if string.strncmp(out_buf + p, "type ", 5) == 0:
p += 5
if p < out_len:
if out_buf[p] == '{' or out_buf[p] == '<':
return 1
pos += 1
return 0
# ============================================================
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
#
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
# ============================================================
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet,
sha1_list: str,
mod_list: str) -> int:
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
"""
if sha1_list is None or mod_list is None:
return 0
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
return 0
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
"""Copy the sha1 to module_name map from the global store into caller arrays."""
if sha1_list is None or mod_list is None: return 0
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
count: int = 0
for i in range(StubMerger._sha1_store_count):
if count >= StubMerger.MAX_INCLUDES:
break
for i in range(int(StubMerger._sha1_store_count)):
if count >= StubMerger.MAX_INCLUDES: break
idx: sizet = sizet(i) * 17
idx2: sizet = sizet(i) * 64
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
count += 1
return count
# ============================================================
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配;供依赖图分析「模块名 → SHA1」)
#
# Args: sha1_arr SHA1 数组(每个 17 字节);mod_arr 模块名数组(每个 64 字节);count 映射条目数;mod_name 要查找的模块名
# Returns: SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
# ============================================================
def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
count: int, mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退)
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
"""
if sha1_arr is None or mod_arr is None or mod_name is None:
return None
if count <= 0:
return None
# 第一遍:精确匹配
for i in range(count):
# _FindSha1ByModName - look up a SHA1 by module name (exact match) for dependency graph analysis.
# sha1_arr holds 17-byte entries, mod_arr 64-byte names; returns a pointer into sha1_arr, or None.
def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -> str:
"""Look up a SHA1 by module name, falling back to the .__init__ package form."""
if sha1_arr is None or mod_arr is None or mod_name is None: return None
if count <= 0: return None
# Pass 1: exact match
for i in range(int(count)):
idx: sizet = sizet(i) * 64
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
# (热点:本函数全表逐条 strcmp 查名)。
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
# (hot spot: this function strcmp-scans the whole table per lookup).
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
sidx: sizet = sizet(i) * 17
return sha1_arr + sidx
# 第二遍:包名回退("ast" → "ast.__init__")
# Pass 2: package fallback ("ast" -> "ast.__init__")
nm_len: sizet = string.strlen(mod_name)
init_buf: bytes = stdlib.malloc(nm_len + 12)
if init_buf is not None:
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
for i in range(count):
for i in range(int(count)):
idx2: sizet = sizet(i) * 64
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
sidx2: sizet = sizet(i) * 17
@@ -356,20 +220,14 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
return sha1_arr + sidx2
stdlib.free(init_buf)
return None
# ============================================================
# _AddSha1ToSet - 将 SHA1 加入集合(去重;已在集合中则 count 不变)
#
# Args: set_buf SHA1 集合缓冲区(每个 17 字节);count 当前集合中的 SHA1 数量;sha1 要加入的 SHA1
# Returns: 新的 count
# ============================================================
# _AddSha1ToSet - add a SHA1 to a set (deduped; count unchanged if already present).
# set_buf holds 17-byte entries; returns the new count.
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
"""将 SHA1 加入集合,返回新 count"""
if set_buf is None or sha1 is None:
return count
for i in range(count):
"""Add a SHA1 to the set (deduped) and return the new count."""
if set_buf is None or sha1 is None: return count
for i in range(int(count)):
idx: sizet = sizet(i) * 17
if string.strcmp(set_buf + idx, sha1) == 0:
return count
if string.strcmp(set_buf + idx, sha1) == 0: return count
if count < StubMerger.MAX_INCLUDES_SHA1:
idx: sizet = sizet(count) * 17
string.strcpy(set_buf + idx, sha1)
+214 -426
View File
@@ -1,12 +1,11 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import mlirlite
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import viperlib
import sys
import ast
@@ -26,44 +25,26 @@ import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerStore - 全局 SHA1 映射存储器(sha1→模块名/相对路径)读写
# ============================================================
# StubMergerStore - global SHA1 map store (sha1 -> module name / rel path) read/write
# ============================================================
# 导入上下文记账(单一真源)
#
# 「AST 导入语句 → imported_modules / from_imports」的记账口径必须唯一。
# 翻译期(ImportsGenMixin.translate_import)与 vpi 注入期(VpiReader 顶层
# 遍历)此前各写一份:注入期漏掉 `import X as Y → from_imports "Y:X"`(别名
# 反查模块真名的唯一入口,见 LookupModNameByAlias)以及 `import a.b.c →
# imported_modules "a"`(顶层包名放行 is_module_imported)。前者使注解里的
# `TSpec.TypeSpec*` 在 _forward_declare_ptr_from_node 4a'/4b 全落空 → 退化
# 为 Ptr(i8)(现场:GenTypes.VarEntry.Spec.PtrLevel 属性访问失败);后者使
# 包限定解析在 stub 上下文里被误判"未导入"。
#
# 现统一到本模块,两侧只保留各自的特有副作用(SHA1 校验 / reexport 注册 /
# add_visible_struct),字符串记账一律走下面这组函数。ImportCtx 只承载两个
# 上下文串(Viper 无多返回值,用 pool 分配的小 struct 传出)。
# ============================================================
# Import-context accounting, single source of truth: AST import statements map to
# imported_modules / from_imports; both translate-time and vpi-injection use these helpers.
class ImportCtx:
"""导入上下文容器:两个空格分隔串(ImportedModules / FromImports)"""
"""Import context container: two space-separated lists (ImportedModules / FromImports)"""
ImportedModules: str
FromImports: str
# ============================================================
# AppendImportedModule - 追加模块名到空格分隔列表(纯记账,无索引副作用)
# ============================================================
# AppendImportedModule - append module name to space-separated list (pure bookkeeping)
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
"""追加模块名到空格分隔列表(pool 分配),返回新列表"""
if name is None:
return cur
"""Append a module name to a space-separated list (pool-allocated); returns the new list"""
if name is None: return cur
if cur is None:
nlen: sizet = string.strlen(name)
buf: str = pool.alloc(nlen + 1)
if buf is not None:
string.strcpy(buf, name)
if buf is not None: string.strcpy(buf, name)
return buf
old_len: sizet = string.strlen(cur)
name_len: sizet = string.strlen(name)
@@ -75,18 +56,11 @@ def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
return buf2
# ============================================================
# AppendFromImport - 追加 from-import 映射("local:mod[:orig]" 空格分隔)
#
# [D7·截断纪律] 旧实现固定 256 缓冲 + snprintf:三者合计超长即被静默截断,
# 该条目在查找侧仍被当作合法条目解析出错误的 local/mod/orig ⇒ 跨模块类名/
# 别名还原取到错名字(污染 Spec.SourceName)。此处按精确长度分配,无上限。
# ============================================================
def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
local: str, mod: str, orig: str) -> str:
"""追加 from-import 映射(pool 分配),返回新列表"""
if local is None or mod is None:
return cur
# AppendFromImport - append a from-import mapping ("local:mod[:orig]") to a space-separated list.
# Exact-size allocation (no fixed 256 buffer) so overlong entries are never silently truncated.
def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, orig: str) -> str:
"""Append a from-import mapping (pool-allocated); returns the new list"""
if local is None or mod is None: return cur
llen: sizet = string.strlen(local)
mlen: sizet = string.strlen(mod)
use_orig: int = 0
@@ -96,19 +70,16 @@ def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
olen = string.strlen(orig)
# local ':' mod [':' orig] '\0'
need: sizet = llen + 1 + mlen + 1
if use_orig != 0:
need += 1 + olen
if use_orig != 0: need += 1 + olen
entry: str = pool.alloc(need)
if entry is None:
return cur
if entry is None: return cur
string.strcpy(entry, local)
entry[llen] = ':'
string.strcpy(entry + llen + 1, mod)
if use_orig != 0:
entry[llen + 1 + mlen] = ':'
string.strcpy(entry + llen + 1 + mlen + 1, orig)
if cur is None:
return entry
if cur is None: return entry
old_len: sizet = string.strlen(cur)
entry_len: sizet = string.strlen(entry)
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
@@ -120,32 +91,21 @@ def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
# ============================================================
# AllocImportCtx - 用两个既有上下文串构造记账容器
# ============================================================
def AllocImportCtx(pool: memhub.MemBuddy*,
imported_modules: str, from_imports: str) -> ImportCtx*:
"""构造导入上下文容器(pool 分配);两串可为 None"""
# AllocImportCtx - build the accounting container from two existing context strings
def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports: str) -> ImportCtx*:
"""Build an import context container (pool-allocated); either list may be None"""
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
if ctx is None:
return None
if ctx is None: return None
ctx.ImportedModules = imported_modules
ctx.FromImports = from_imports
return ctx
# ============================================================
# NotePlainImport - 记账 `import X` / `import X as Y`(就地更新 ctx)
#
# 有别名:追加别名 `Y`,并写 from-import 映射 "Y:X"——这就是 `LookupModNameByAlias`
# 与 `_forward_declare_ptr_from_node` 4a' 反查模块真名的唯一来源,绝不可省。
# 无别名:`import a.b.c` 时把顶层包名 `a` 也追加(使 a.b.c 作为属性访问 base
# 时 is_module_imported("a") 放行)。
# ============================================================
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*,
name: str, asname: str) -> int:
"""记账单条 import(就地更新 ctx),返回 0"""
if ctx is None or name is None:
return 1
# NotePlainImport - record `import X` / `import X as Y` in place on ctx.
# Aliased: append alias Y + from-import "Y:X" (sole source for reverse module lookup).
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname: str) -> int:
"""Record a single import in place on ctx; returns 0"""
if ctx is None or name is None: return 1
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
if asname is not None:
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
@@ -159,58 +119,42 @@ def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*,
if pkg_buf is not None:
string.strncpy(pkg_buf, name, pkg_len)
pkg_buf[pkg_len] = '\0'
ctx.ImportedModules = AppendImportedModule(
pool, ctx.ImportedModules, pkg_buf)
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, pkg_buf)
return 0
# ============================================================
# PopulateSha1MapStore - 遍历 ScanResult.Entries 构建 sha1→module_name 映射,填充全局存储器
#
# 模块名通过 _PathToModuleName(includes/rel_path) 计算;内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
# Args: scan_result IncludesScanner 扫描结果
# Returns: 填充的条目数,-1 表示失败
# ============================================================
def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int:
"""从扫描结果填充全局 SHA1 映射存储器
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
模块名通过 _PathToModuleName(includes/rel_path) 计算。
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
"""
if scan_result is None:
return -1
# 释放旧存储器
if StubMerger._sha1_store_arr is not None:
stdlib.free(StubMerger._sha1_store_arr)
if StubMerger._sha1_store_mod is not None:
stdlib.free(StubMerger._sha1_store_mod)
if StubMerger._sha1_store_rel is not None:
stdlib.free(StubMerger._sha1_store_rel)
if StubMerger._sha1_store_incdir is not None:
stdlib.free(StubMerger._sha1_store_incdir)
# PopulateSha1MapStore - build sha1 -> module_name map from ScanResult.Entries into the global store.
# Memory via stdlib.malloc (global, not freed with mbuddy). Returns entry count, -1 on failure.
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
"""Populate the global SHA1 store from a scan result; returns the entry count or -1"""
if scan_result is None: return -1
# free the old store
if StubMerger._sha1_store_arr is not None: stdlib.free(StubMerger._sha1_store_arr)
if StubMerger._sha1_store_mod is not None: stdlib.free(StubMerger._sha1_store_mod)
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
return -1
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
StubMerger._sha1_store_count = 0
p_bad: int = 0
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
for i in range(scan_result.Count):
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
VLogger.error("includes SHA1 存储超上限,终止", "StubMerger")
for i in range(int(scan_result.Count)):
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: VLogger.error("includes SHA1 store exceeded the limit; aborting", "StubMerger")
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
ent: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea)
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea)
if ent is None or ent.Sha1 is None or ent.RelPath is None:
p_bad += 1
continue
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
# build full path includes/{RelPath} (needed by _PathToModuleName)
rp_len: sizet = string.strlen(ent.RelPath)
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
if full_path_buf is None:
@@ -224,212 +168,161 @@ def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
else:
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
stdlib.free(mod_name)
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
# save rel_path (used by Phase2 3.5 to compile missing includes)
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
else:
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用)
# save owning includes root dir (Phase2 pre-register / on-demand path building)
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
if ent.IncDir is not None:
icd_len: sizet = string.strlen(ent.IncDir)
if icd_len < StubMerger.MAX_INC_DIR_LEN:
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
else:
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, StubMerger.MAX_INC_DIR_LEN - 1)
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
StubMerger._sha1_store_count += 1
if p_bad > 0:
VLogger.error(f"includes SHA1 存储有 {p_bad} 个条目失败", "StubMerger")
if p_bad > 0: VLogger.error(f"includes SHA1 store has {p_bad} failed entries", "StubMerger")
return StubMerger._sha1_store_count
# ============================================================
# AppendToSha1MapStore - 追加 App 模块条目到全局 SHA1 映射存储器(Phase1 先用 PopulateSha1MapStore 填 includes,
# Phase2 扫描 App 源文件后调用本函数追加,否则编译 App 自身时跨模块 CDefine 查找命中不到 App 模块)
#
# 存储器未初始化时自动初始化;SHA1 已存在(去重)则跳过。
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对路径(如 "lib/core/StubMerger.vp",不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
# Returns: 0 成功(含去重跳过),1 失败
# ============================================================
# AppendToSha1MapStore - append an App module entry to the global SHA1 store (Phase2 use);
# auto-initializes if empty, skips on duplicate SHA1. Returns 0 ok, 1 failed.
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
if sha1 is None or rel_path is None:
return 1
# 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用)
"""Append an entry to the global SHA1 store (Phase2)"""
if sha1 is None or rel_path is None: return 1
# init store if empty (Phase2 may run before Phase1)
if StubMerger._sha1_store_arr is None:
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
return 1
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
StubMerger._sha1_store_count = 0
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
return 1
# 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger"
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
# module name: rel_path "lib/core/StubMerger.vp" -> "lib.core.StubMerger"
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
if full_path_buf is None:
return 1
if full_path_buf is None: return 1
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
stdlib.free(full_path_buf)
if mod_name is None:
return 1
# 去重:SHA1 与模块名都相同才跳过——同一 SHA1 可能先以全路径模块名注册(App 源文件在 includes 目录下),
# App 追加的短模块名必须保留,否则 import 该模块的精确查找失败、后缀匹配会劫持到其它同名模块。
for i in range(StubMerger._sha1_store_count):
if mod_name is None: return 1
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
# under a full-path name; the App short name must stay or exact lookup breaks.
for i in range(int(StubMerger._sha1_store_count)):
sidx_d: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
midx_d: sizet = sizet(i) * 64
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
stdlib.free(mod_name)
return 0
# 写入 SHA1
# write SHA1
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
# 写入模块名
# write module name
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
else:
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
stdlib.free(mod_name)
# 写入相对路径
# write rel path
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
rp_len: sizet = string.strlen(rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数)
# App sources belong to no includes dir: leave IncDir empty (Phase2 falls back to includes_dir)
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
StubMerger._sha1_store_count += 1
return 0
# ============================================================
# AddIncludeToSha1MapStore - 单条登记 includes 条目到全局 SHA1 映射存储器
#
# 依赖图按需扫描(_BuildReachableSha1Set)解析到可达 includes 文件后逐条登记,未引用的 includes 不进 store;
# App 源文件仍用 AppendToSha1MapStore(inc_dir 留空)。
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对 includes 根目录的路径(如 "ast/parser.vp");inc_dir 所属 includes 根目录;mod_name 模块名(如 "ast.parser")
# Returns: 0 成功(含去重跳过),1 失败
# ============================================================
# AddIncludeToSha1MapStore - register one includes entry into the global SHA1 store (on-demand).
# Unreferenced includes stay out; App sources still use AppendToSha1MapStore (empty inc_dir).
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
"""单条登记 includes 条目到全局 SHA1 映射存储器(依赖图按需扫描用)"""
if sha1 is None or rel_path is None or mod_name is None:
return 1
# 存储器未初始化:自动初始化
"""Register one includes entry in the global SHA1 store (on-demand scan)"""
if sha1 is None or rel_path is None or mod_name is None: return 1
# init store if empty
if StubMerger._sha1_store_arr is None:
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
return 1
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
StubMerger._sha1_store_count = 0
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
return 1
# 去重:SHA1 与模块名都相同才跳过(对齐 AppendToSha1MapStore)
for i in range(StubMerger._sha1_store_count):
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
for i in range(int(StubMerger._sha1_store_count)):
sidx_d: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
midx_d: sizet = sizet(i) * 64
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
return 0
# 写 SHA1
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: return 0
# write SHA1
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
# 写模块名
# write module name
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
else:
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
# 写相对路径
# write rel path
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
rp_len: sizet = string.strlen(rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# 写所属 includes 根目录(非空 = includes 条目;App 条目留空)
# write owning includes root (non-empty = includes entry; App entries empty)
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
if inc_dir is not None:
icd_len: sizet = string.strlen(inc_dir)
if icd_len < StubMerger.MAX_INC_DIR_LEN:
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
else:
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
else:
StubMerger._sha1_store_incdir[idx_inc] = '\0'
else: StubMerger._sha1_store_incdir[idx_inc] = '\0'
StubMerger._sha1_store_count += 1
return 0
# ============================================================
# GetSha1StoreCount - 获取全局存储器条目数
# ============================================================
# GetSha1StoreCount - return the global store entry count
def GetSha1StoreCount() -> int:
"""返回全局 SHA1 存储器的条目数"""
"""Return the global SHA1 store entry count"""
return StubMerger._sha1_store_count
# ============================================================
# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器)
#
# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
#
# Returns:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
# LookupSha1ByModName - find SHA1 by module name; falls back to "{mod_name}.__init__".
# Returns a pointer into the store (do not free), None if not found.
def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
"""Look up a SHA1 by module name (exact then package fallback); delegates to _FindSha1ByModName"""
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
# ============================================================
# 泛型类归属表(泛型类名 → 定义模块 SHA1)——仅 REPL 会话登记/查询
#
# 为什么需要:REPL 的 includes 会话初始化只注入 `.vpi` 的类(泛型类被跳过),
# 不跑批处理的 _PreRegister*GenericTemplates ⇒ 会话内泛型模板表为空 ⇒ 注解里的
# `list[rawptr[AST]]` 等泛型下标解析(`TypeGenResolve._resolve_generic_subscript_ty`)
# 恒 miss。会话侧的最后兜底要按**定义侧特化命名** "%{模板模块 sha1}.{mangled}"
# 造 opaque 具名类型(实证:`%"b56d2f995104690d.list[rawptr[AST]]"`),因此需要
# "泛型类定义在哪个模块"这一事实——由注入期扫 `.vpi` 的 ClassDef 登记(见
# VpiReader._repl_stub_collect_module)。
#
# 归属模块 sha1 无法从类名反推(模块名取自文件路径,class list ← _list.vp;用户
# 泛型类更是名字与模块名无关),故必须显式登记,不做"按名字猜"。
# 批处理不登记(表恒空 ⇒ 查询恒 None ⇒ 批处理路径一字不改)。
# 容量:includes 现有 6 个泛型类定义(list/dict 系 + Vector/GSList/ndarray),取 32 槽。
# ============================================================
GENERIC_OWNER_MAX: t.CDefine = 32
# Generic-class owner table (generic class name -> defining module SHA1), REPL-session only;
# needed because REPL skips generic templates, so generic subscript resolution would otherwise miss.
define GENERIC_OWNER_MAX = 32
_generic_owner_names: bytes = None
_generic_owner_sha1s: bytes = None
_generic_owner_count: int = 0
def ResetGenericOwners() -> None:
"""清空泛型类归属表(REPL 会话初始化开始时调用一次)"""
"""Clear the generic-class owner table (called once at REPL session start)"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if _generic_owner_names is not None:
stdlib.free(_generic_owner_names)
@@ -441,31 +334,23 @@ def ResetGenericOwners() -> None:
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
"""登记"泛型类名 → 定义模块 SHA1";1=已登记(含重复)/ 0=失败(参数/满/同名异 sha1)
同名异 sha1(跨模块同名泛型类)保留首个,与泛型模板表按类名去重的口径一致。
"""
"""Register generic-class-name to defining-module SHA1; 1 = registered, 0 = failed"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if cls_name is None or mod_sha1 is None:
return 0
if cls_name[0] == '\0' or mod_sha1[0] == '\0':
return 0
if cls_name is None or mod_sha1 is None: return 0
if cls_name[0] == '\0' or mod_sha1[0] == '\0': return 0
if _generic_owner_names is None:
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
if _generic_owner_names is None or _generic_owner_sha1s is None:
return 0
if _generic_owner_names is None or _generic_owner_sha1s is None: return 0
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
_generic_owner_count = 0
i_go: int
for i_go in range(_generic_owner_count):
for i_go in range(int(_generic_owner_count)):
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0:
return 1
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0: return 1
return 0
if _generic_owner_count >= GENERIC_OWNER_MAX:
return 0
if _generic_owner_count >= GENERIC_OWNER_MAX: return 0
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
_generic_owner_count += 1
@@ -473,51 +358,40 @@ def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
def LookupGenericOwnerSha1(cls_name: str) -> str:
"""按泛型类名取定义模块 SHA1(返回内部指针,无需释放;未登记 None)"""
if cls_name is None or _generic_owner_names is None:
return None
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
if cls_name is None or _generic_owner_names is None: return None
i_lg: int
for i_lg in range(_generic_owner_count):
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0:
return _generic_owner_sha1s + sizet(i_lg) * 17
for i_lg in range(int(_generic_owner_count)):
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
return None
# ============================================================
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1(用于 from .X import Y:impf.module 是 ".X",而映射表键是 "pkg.X")
#
# 包含三种匹配方式:精确、后缀、反向后缀。
# Returns: SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
# LookupSha1ByModNameSuffix - find SHA1 by suffix; tries exact, suffix, reverse-suffix. None if absent.
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1"""
if mod_suffix is None:
return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
return None
if StubMerger._sha1_store_count <= 0:
return None
"""Look up a module SHA1 by suffix match"""
if mod_suffix is None: return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
if StubMerger._sha1_store_count <= 0: return None
suffix_len_sf: sizet = string.strlen(mod_suffix)
if suffix_len_sf == 0:
return None
if suffix_len_sf == 0: return None
i_sf: int
for i_sf in range(StubMerger._sha1_store_count):
for i_sf in range(int(StubMerger._sha1_store_count)):
idx_sf: sizet = sizet(i_sf) * 64
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
mod_len_sf: sizet = string.strlen(mod_name_sf)
if mod_len_sf == 0:
continue
# 精确匹配
if mod_len_sf == 0: continue
# exact match
if mod_len_sf == suffix_len_sf:
if string.strcmp(mod_name_sf, mod_suffix) == 0:
sidx_sf: sizet = sizet(i_sf) * 17
return StubMerger._sha1_store_arr + sidx_sf
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
# suffix match: mod_name ends with "." + mod_suffix
if mod_len_sf > suffix_len_sf:
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
sidx2_sf: sizet = sizet(i_sf) * 17
return StubMerger._sha1_store_arr + sidx2_sf
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
# reverse suffix match: mod_suffix ends with "." + mod_name
if suffix_len_sf > mod_len_sf:
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
@@ -526,47 +400,34 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
return StubMerger._sha1_store_arr + sidx_rev_sf
return None
# ============================================================
# LookupSha1ByAlias - 从 from_imports 别名映射解析模块 SHA1
#
# from_imports 条目格式 "local:module" 或 "local:module:orig"(空格分隔,由 ImportsGenMixin.add_from_import 写入);
# 命中 local==alias 时对 module 执行 LookupSha1ByModName。
# Returns: SHA1 字符串指针(指向全局存储器内部),None 未找到
# ============================================================
# LookupSha1ByAlias - resolve SHA1 from from_imports alias "local:module[:orig]" entries.
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
"""从 from_imports 别名映射解析模块 SHA1(改名别名 import X as Y 场景)"""
if from_imports is None or alias is None:
return None
"""Resolve a module SHA1 from a from-import alias mapping (import X as Y)"""
if from_imports is None or alias is None: return None
alias_len: sizet = string.strlen(alias)
if alias_len == 0:
return None
if alias_len == 0: return None
total_len: sizet = string.strlen(from_imports)
ci: sizet = 0
while ci < total_len:
# 跳过前导空格
while ci < total_len and from_imports[ci] == ' ':
ci += 1
if ci >= total_len:
break
# 条目起点 ci,找 ':'(local_name 结束)
# skip leading spaces
while ci < total_len and from_imports[ci] == ' ': ci += 1
if ci >= total_len: break
# entry starts at ci; find ':' (end of local_name)
colon: sizet = ci
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
colon += 1
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
local_len: sizet = colon - ci
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
# 比较 local_name == alias
match: int = 1
li: sizet = 0
while li < alias_len:
# compare local_name == alias
Matched: int = 1
for li in range(int(alias_len)):
if from_imports[ci + li] != alias[li]:
match = 0
Matched = 0
break
li += 1
if match == 1:
# 提取 module(colon+1 到下一个 ':' 或空格)
if Matched == 1:
# extract module (from colon+1 to next ':' or space)
m_start: sizet = colon + 1
m_end: sizet = m_start
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
m_end += 1
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
if m_end > m_start:
mbuf: str = stdlib.malloc(m_end - m_start + 1)
if mbuf is not None:
@@ -576,52 +437,38 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
stdlib.free(mbuf)
return res
return None
# 跳到下一条目
while ci < total_len and from_imports[ci] != ' ':
ci += 1
# advance to next entry
while ci < total_len and from_imports[ci] != ' ': ci += 1
return None
# ============================================================
# LookupModNameByAlias - 从 from_imports 别名映射解析模块真名
#
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,X=模块真名);
# 本函数返回别名 Y 对应的模块真名 X(供限定名解析 "Y.Class" → 按 X 模块归属匹配)。
# Returns: 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
# ============================================================
# LookupModNameByAlias - return real module name X for alias Y (from_imports "Y:X"; caller frees).
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
"""从 from_imports 别名映射解析模块真名(import X as Y → Y:X)"""
if from_imports is None or alias is None:
return None
"""Resolve the real module name for an alias from from_imports (import X as Y -> Y:X)"""
if from_imports is None or alias is None: return None
alias_len: sizet = string.strlen(alias)
if alias_len == 0:
return None
if alias_len == 0: return None
total_len: sizet = string.strlen(from_imports)
ci: sizet = 0
while ci < total_len:
# 跳过前导空格
while ci < total_len and from_imports[ci] == ' ':
ci += 1
if ci >= total_len:
break
# 条目起点 ci,找 ':'(local_name 结束)
# skip leading spaces
while ci < total_len and from_imports[ci] == ' ': ci += 1
if ci >= total_len: break
# entry starts at ci; find ':' (end of local_name)
colon: sizet = ci
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
colon += 1
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
local_len: sizet = colon - ci
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
# 比较 local_name == alias
match: int = 1
li: sizet = 0
while li < alias_len:
# compare local_name == alias
Matched: int = 1
for li in range(int(alias_len)):
if from_imports[ci + li] != alias[li]:
match = 0
Matched = 0
break
li += 1
if match == 1:
# 提取 module(colon+1 到下一个 ':' 或空格)
if Matched == 1:
# extract module (from colon+1 to next ':' or space)
m_start: sizet = colon + 1
m_end: sizet = m_start
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
m_end += 1
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
if m_end > m_start:
mbuf: str = stdlib.malloc(m_end - m_start + 1)
if mbuf is not None:
@@ -629,127 +476,85 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
mbuf[m_end - m_start] = '\0'
return mbuf
return None
# 跳到下一条目
while ci < total_len and from_imports[ci] != ' ':
ci += 1
# advance to next entry
while ci < total_len and from_imports[ci] != ' ': ci += 1
return None
# ============================================================
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
#
# Phase B+ 前用 declare_only 翻译 includes 文件、提取 _imported_modules 后调用本函数存入内存;_BuildReachableSha1Set 从内存读取,不读 deps.txt。
# Args: sha1 includes 文件的 SHA1(16字符);deps_str 依赖模块名字符串(空格分隔,如 "t c stdint string")
# Returns: 0 成功,非 0 失败
# ============================================================
# PopulateIncludesDeps - store includes deps in memory (deps_str space-separated). 0 ok, non-0 fail.
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
"""将 includes 文件的依赖存入内存"""
if sha1 is None:
return 1
# 延迟初始化
"""Store includes-file dependencies in memory"""
if sha1 is None: return 1
# lazy init
if StubMerger._includes_deps_sha1 is None:
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
StubMerger._includes_deps_str = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None:
return 1
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(MAX_DEPS_STR_LEN) 会回绕成 0,显式加宽到 64 位
StubMerger._includes_deps_str = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 1
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
string.memset(StubMerger._includes_deps_str, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
string.memset(StubMerger._includes_deps_str, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
StubMerger._includes_deps_count = 0
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES:
return 1
# 去重:若 SHA1 已存在则跳过
for i in range(StubMerger._includes_deps_count):
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
# dedup: skip if SHA1 already present
for i in range(int(StubMerger._includes_deps_count)):
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
return 0
# 写入 SHA1
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
# write SHA1
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
# 写入依赖字符串
# write deps string
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
if deps_str is not None:
dl: sizet = string.strlen(deps_str)
if dl < StubMerger.MAX_DEPS_STR_LEN:
string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
else:
string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
else:
StubMerger._includes_deps_str[idx2] = '\0'
if dl < StubMerger.MAX_DEPS_STR_LEN: string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
else: string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
else: StubMerger._includes_deps_str[idx2] = '\0'
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
return 0
# ============================================================
# LookupIncludesDeps - 从内存查找 includes 文件的依赖
#
# Args:
# sha1: includes 文件的 SHA1(16字符)
#
# Returns:
# 依赖模块名字符串指针(空格分隔),None 表示未找到
# ============================================================
# LookupIncludesDeps - look up includes deps by SHA1 in memory; returns space-separated deps or None.
def LookupIncludesDeps(sha1: str) -> str:
"""从内存查找 includes 文件的依赖"""
if sha1 is None or StubMerger._includes_deps_sha1 is None:
return None
if StubMerger._includes_deps_count <= 0:
return None
for i in range(StubMerger._includes_deps_count):
"""Look up includes-file dependencies in memory"""
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
if StubMerger._includes_deps_count <= 0: return None
for i in range(int(StubMerger._includes_deps_count)):
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
return StubMerger._includes_deps_str + idx2
return None
def GetSha1StoreArrPtr() -> bytes:
"""返回 SHA1 数组指针(每个条目 17 字节)"""
"""Return the SHA1 array pointer (17 bytes per entry)"""
return StubMerger._sha1_store_arr
def GetSha1StoreModArrPtr() -> bytes:
"""返回模块名数组指针(每个条目 64 字节)"""
"""Return the module-name array pointer (64 bytes per entry)"""
return StubMerger._sha1_store_mod
def GetSha1StoreRelArrPtr() -> bytes:
"""返回相对路径数组指针(每个条目 StubMerger.MAX_REL_PATH_LEN 字节)"""
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
return StubMerger._sha1_store_rel
def GetSha1StoreIncdirArrPtr() -> bytes:
"""返回所属 includes 根目录数组指针(每个条目 StubMerger.MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)"""
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
return StubMerger._sha1_store_incdir
# ============================================================
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
#
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
# 内存归全局存储器所有,调用者不要 free
# ============================================================
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
"""获取全局存储器数组指针
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 槽的地址(t.CPtr*),
函数把存储器数组指针写入这些槽。返回条目数,-1 失败
"""
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
return -1
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None:
return -1
"""Get the global store array pointers and entry count; returns -1 on failure"""
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
sp: rawptr[t.CPtr] = out_sha1_arr
if sp is not None:
sp[0] = StubMerger._sha1_store_arr
if sp is not None: sp[0] = StubMerger._sha1_store_arr
mp: rawptr[t.CPtr] = out_mod_arr
if mp is not None:
mp[0] = StubMerger._sha1_store_mod
if mp is not None: mp[0] = StubMerger._sha1_store_mod
rp: rawptr[t.CPtr] = out_rel_arr
if rp is not None:
rp[0] = StubMerger._sha1_store_rel
if rp is not None: rp[0] = StubMerger._sha1_store_rel
return StubMerger._sha1_store_count
# ============================================================
# LookupSha1RelPath - 通过 SHA1 查找相对路径
#
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free),用于把 sha1 转成人类可读的文件路径。
# Args: sha1 SHA1 字符串(16 字符)
# Returns: 相对路径字符串指针(只读),None=未找到
# ============================================================
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
def LookupSha1RelPath(sha1: str) -> str:
"""通过 SHA1 查找相对路径,返回只读指针,None=未找到"""
if sha1 is None:
return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None:
return None
for i in range(StubMerger._sha1_store_count):
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
if sha1 is None: return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
for i in range(int(StubMerger._sha1_store_count)):
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
@@ -758,29 +563,12 @@ def LookupSha1RelPath(sha1: str) -> str:
# ============================================================
# LookupModNameBySha1 - 通过 SHA1 反查模块名
#
# 用途:判定"本 stub 是否某包的 __init__"(模块名以 ".__init__" 结尾)——该事实
# **不依赖 sha1→rel_path 字段**:rel_path 反查在部分运行形态下拿不到条目 ⇒
# VpiReader 原来的 `strstr(rel_path, "__init__.vp")` 判定恒 false ⇒ 包重导出类的
# 别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,
# 与构造函数侧(真定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的
# CastError;幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物 opaque)。
# 模块名与 rel_path 是同一次登记写入的(PopulateSha1MapStore /
# AppendToSha1MapStore),但模块名可用即足以作出判定与取包前缀。
#
# 同一 SHA1 可能有多条(App 源文件按全路径模块名与短模块名各登记一次):返回
# **第一条命中**;各条的包前缀一致,调用方只取"去 .__init__ 后的前缀"。
# Args: sha1 SHA1 字符串(16 字符)
# Returns: 模块名字符串指针(只读,不要 free),None=未找到
# ============================================================
# LookupModNameBySha1 - reverse lookup module name by SHA1 (read-only ptr, no free); first hit wins.
def LookupModNameBySha1(sha1: str) -> str:
"""通过 SHA1 查找模块名,返回只读指针,None=未找到"""
if sha1 is None or sha1[0] == '\0':
return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
return None
for i in range(StubMerger._sha1_store_count):
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
if sha1 is None or sha1[0] == '\0': return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
for i in range(int(StubMerger._sha1_store_count)):
sidx_mb: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
midx_mb: sizet = sizet(i) * 64
+48 -95
View File
@@ -1,163 +1,116 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerTopo - 对可达 includes 做逆拓扑排序(被依赖者先编译)
#
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
# ============================================================
# StubMergerTopo - reverse-topological sort of reachable includes (dependencies first).
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
# ============================================================
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
#
# 跨模块 vtable 生成要求被依赖模块先完整翻译(子类 vtable 槽位需父类方法的类型已注册到全局表)。
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中);入度为 0(不依赖任何可达模块)者先输出,
# 输出后对依赖它的模块减入度,入度归 0 者入队。reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
#
# Args: store_count / store_sha1_arr / store_mod_arr store 元数据;reachable_set / reachable_count 可达集合(_BuildReachableSha1Set 输出);sorted_idx 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
# Returns: 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
# ============================================================
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
store_mod_arr: bytes, reachable_set: bytes,
reachable_count: int,
sorted_idx: sizet*) -> int:
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None:
return 0
indegree: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
queue: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
rel_idx: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
# BuildTopoSortedIndices - reverse-topological sort of reachable includes (dependencies first).
# Uses Kahn's algorithm; reachable_count <= 0 sorts all stores; cycles are appended in store order.
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_arr: bytes, reachable_set: bytes, reachable_count: int, sorted_idx: sizet*) -> int:
"""Reverse-topologically sort reachable includes (dependencies first); store indices go to sorted_idx."""
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: return 0
indegree: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
queue: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
rel_idx: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
if indegree is None or queue is None or rel_idx is None:
if indegree is not None:
stdlib.free(indegree)
if queue is not None:
stdlib.free(queue)
if rel_idx is not None:
stdlib.free(rel_idx)
if indegree is not None: stdlib.free(indegree)
if queue is not None: stdlib.free(queue)
if rel_idx is not None: stdlib.free(rel_idx)
return 0
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
# Zero indegree/queue/rel_idx (malloc memory is uninitialized)
zi: int = 0
for zi in range(store_count + 1):
for zi in range(int(store_count + 1)):
indegree[zi] = 0
queue[zi] = 0
rel_idx[zi] = 0
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
# Collect reachable store indices (all stores when reachable_count<=0)
rel_count: int = 0
i_t: int
if reachable_set is None or reachable_count <= 0:
for i_t in range(store_count):
for i_t in range(int(store_count)):
rel_idx[rel_count] = i_t
rel_count += 1
else:
for i_t in range(store_count):
for i_t in range(int(store_count)):
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
rel_idx[rel_count] = i_t
rel_count += 1
# 计算入度:m 在可达集合内的直接依赖数
for i_r in range(rel_count):
si: int = rel_idx[i_r]
# Compute indegree: number of direct deps of m within the reachable set
for i_r in range(int(rel_count)):
si: int = int(rel_idx[i_r])
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
if deps_si is None:
continue
if deps_si is None: continue
dl_si: sizet = string.strlen(deps_si)
dpos: sizet = 0
while dpos < dl_si:
dstart: sizet = dpos
while dpos < dl_si and deps_si[dpos] != ' ':
dpos += 1
while dpos < dl_si and deps_si[dpos] != ' ': dpos += 1
dlen: sizet = dpos - dstart
if dpos < dl_si:
dpos += 1
if dlen == 0:
continue
if dpos < dl_si: dpos += 1
if dlen == 0: continue
dname_buf: bytes = stdlib.malloc(dlen + 1)
if dname_buf is None:
continue
if dname_buf is None: continue
string.strncpy(dname_buf, deps_si + dstart, dlen)
dname_buf[dlen] = '\0'
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
stdlib.free(dname_buf)
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
# 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug
indg: int = indegree[si]
# Use an intermediate int to work around a union-pointer arithmetic codegen bug
indg: int = int(indegree[si])
indg = indg + 1
indegree[si] = indg
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
# Kahn: nodes with indegree 0 (no reachable deps) are output first
q_tail: int = 0
for i_r in range(rel_count):
si2: int = rel_idx[i_r]
for i_r in range(int(rel_count)):
si2: int = int(rel_idx[i_r])
if indegree[si2] == 0:
queue[q_tail] = si2
q_tail += 1
out_count: int = 0
# Kahn 主循环必须用 while 而非 for range:循环体内会动态入队(for range 上界只求值一次,
# 动态入队的模块永远不会被处理,且 indegree>0 兜底也捞不回 → 拓扑漏项)
# The Kahn main loop must use while, not for range: the body enqueues dynamically,
# and a for range bound is evaluated once, so newly queued modules would be skipped.
q_head: int = 0
while q_head < q_tail:
m: int = queue[q_head]
m: int = int(queue[q_head])
sorted_idx[out_count] = m
out_count += 1
# 对依赖 m 的可达模块减入度
# Decrement indegree of reachable modules that depend on m
mod_m: str = store_mod_arr + sizet(m) * 64
mod_m_len: sizet = string.strlen(mod_m)
for i_r in range(rel_count):
si3: int = rel_idx[i_r]
if indegree[si3] == 0:
continue
for i_r in range(int(rel_count)):
si3: int = int(rel_idx[i_r])
if indegree[si3] == 0: continue
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
if deps_si3 is None:
continue
if deps_si3 is None: continue
dl_si3: sizet = string.strlen(deps_si3)
dpos3: sizet = 0
found_dep: int = 0
while dpos3 < dl_si3 and found_dep == 0:
dstart3: sizet = dpos3
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
dpos3 += 1
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ': dpos3 += 1
dlen3: sizet = dpos3 - dstart3
if dpos3 < dl_si3:
dpos3 += 1
if dlen3 == 0:
continue
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0:
found_dep = 1
if dpos3 < dl_si3: dpos3 += 1
if dlen3 == 0: continue
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1
if found_dep == 1:
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
indg3: int = indegree[si3]
# Use an intermediate int decrement to work around a union-pointer arithmetic codegen bug
indg3: int = int(indegree[si3])
indg3 = indg3 - 1
indegree[si3] = indg3
if indegree[si3] == 0:
queue[q_tail] = si3
q_tail += 1
q_head += 1
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
for i_r in range(rel_count):
si4: int = rel_idx[i_r]
# On cycles, append remaining entries in store order to avoid skipping compilation
for i_r in range(int(rel_count)):
si4: int = int(rel_idx[i_r])
if indegree[si4] > 0:
sorted_idx[out_count] = si4
out_count += 1
+31 -60
View File
@@ -1,71 +1,42 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
# ============================================================
# StubMerger - stub 声明合并模块(Phase2 核心)
#
# 从 temp 目录加载所有 .stub.ll,把依赖模块的声明合并到主 IR 的 target triple 之后,使链接器能解析跨模块符号引用。
# 合并策略:扫描 temp 目录收集 {sha1}.stub.ll → 读取内容 → 过滤去重(避免重复声明)→ 插入主 IR。
# ============================================================
# StubMerger - merges dependency stub declarations into the main IR (Phase2 core):
# collect {sha1}.stub.ll from temp, dedup, then insert after the target triple so cross-module symbols link.
# 全局 mbuddy 指针
# Global mbuddy pointer
_mbuddy: memhub.MemBuddy*
# 最大 includes SHA1 数(用于过滤)
MAX_INCLUDES_SHA1: t.CDefine = 1024
# stub 读取缓冲区大小
# 1MB 不够:ast/parser.vp 的 text.ll >1MB,BuildCombinedIR 读取本地 text.ll
# 单次截断 → 合并 IR 尾部丢字(opt: "end of file in global variable name")。
STUB_READ_BUF_SIZE: t.CDefine = 8388608
# 最大 includes 条目数
MAX_INCLUDES: t.CDefine = 1024
# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir
# 后 worklist 含全部 App 文件 imports 的拼接 + BFS 依赖展开,追加82 溢出)
REACH_WORKLIST_BUF: t.CDefine = 65536
# Max includes SHA1 entries (for filtering)
define MAX_INCLUDES_SHA1 = 1024
# Stub/text read buffer size. 1MB is too small: text.ll files exceed 1MB, so a single
# truncated read drops trailing data (opt: "end of file in global variable name").
define STUB_READ_BUF_SIZE = 8388608
# Max includes entries
define MAX_INCLUDES = 1024
# Reachability worklist/processed buffer size. 8192 is too small: after scanning
# source_dir it holds all App imports plus BFS expansion, overflowing on append 82.
define REACH_WORKLIST_BUF = 65536
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
# Phase1 扫描完成后填充,供所有函数使用
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个)
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
_sha1_store_incdir: bytes # 所属 includes 根目录数组(每个 512 字节,MAX_INCLUDES 个,多 includes 支持)
_sha1_store_count: int # 条目数
# Global SHA1 map store (in memory, no dependency on _sha1_map.txt).
# Filled after the Phase1 scan and shared by all functions.
_sha1_store_arr: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
_sha1_store_mod: bytes # Module-name array (64 bytes each, MAX_INCLUDES entries)
_sha1_store_rel: bytes # Relative-path array (256 bytes each, MAX_INCLUDES entries)
_sha1_store_incdir: bytes # Owning includes-root array (512 bytes each, MAX_INCLUDES entries, multi-includes)
_sha1_store_count: int # Entry count
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个)
_includes_deps_count: int # 条目数
# In-memory includes dependency store (no dependency on the deps.txt file).
# Before Phase B+ all includes are translated in declare_only mode; _BuildReachableSha1Set reads deps from here.
_includes_deps_sha1: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
_includes_deps_str: bytes # Dependency-string array (512 bytes each, MAX_INCLUDES entries)
_includes_deps_count: int # Entry count
# BuildCombinedIR 全目录扫描缓存:Phase B 每个源文件调用一次 BuildCombinedIR,而 _collect_stub_sha1s 会递归枚举
# temp_dir 全部切片子目录(O(N×S) 目录枚举很慢);按 temp_dir 缓存扫描结果,首次扫描后复用,temp_dir 变化时自动重建。
_stub_scan_cache_dir: str # 缓存对应的 temp_dir(None=未初始化)
_stub_scan_cache_arr: str # 扫描结果数组(每 17 字节一个 SHA1,MAX_INCLUDES_SHA1 个)
_stub_scan_cache_count: int # 收集数量
# Max length of a single dependency string
define MAX_DEPS_STR_LEN = 512
# 单条依赖字符串最大长度
MAX_DEPS_STR_LEN: t.CDefine = 512
# rel_path 最大长度
MAX_REL_PATH_LEN: t.CDefine = 256
# includes 根目录最大长度(绝对路径,多 includes 支持)
MAX_INC_DIR_LEN: t.CDefine = 512
# Max rel_path length
define MAX_REL_PATH_LEN = 256
# Max includes-root path length (absolute path, multi-includes)
define MAX_INC_DIR_LEN = 512
+3 -8
View File
@@ -1,13 +1,8 @@
import t, c
from stdint import *
import memhub
import lib.core.VLogger as VLogger
# ============================================================
# lib 包入口 - 声明全局 _mbuddy 内存池指针
# ============================================================
# lib package entry - declares the global _mbuddy memory-pool pointer.
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
# Global mbuddy memory-pool pointer (injected by App/main.vp after init).
# Uses MemBuddy, not MemManager, to avoid returning None when virtual dispatch is unavailable.
_mbuddy: memhub.MemBuddy*
+7 -10
View File
@@ -1,23 +1,20 @@
import t, c
import memhub
# ============================================================
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
# ============================================================
# CompilerFactory - compiler struct factory (aligned with TPC Translator assembly).
novirtual class CompilerFactory:
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
Pool: memhub.MemBuddy* # 主内存池(瞬时数据,mark/reset 回收)
PermPool: memhub.MemBuddy* # 永久内存池(持久数据,从不 reset)
"""Compiler struct factory holding shared objects such as the main and permanent pools."""
Pool: memhub.MemBuddy* # Main memory pool (transient data; reclaimed by mark/reset)
PermPool: memhub.MemBuddy* # Permanent memory pool (persistent data; never reset)
# ---- REnum 变体注册表(编译期单例持有,与 perm_pool 的 struct entry 同寿命)----
# ---- REnum variant registry (compile-time singleton; same lifetime as perm_pool struct entries)----
RenumClassNames: sizet*
RenumClassVariantCounts: sizet*
RenumVariantNames: sizet*
RenumVariantTags: sizet*
RenumClassCount: int
# ---- REnum 变体字段别名表(同上,必须同寿命;_bind_variant_fields 查字段真实类型)----
# ---- REnum variant field alias table (same lifetime; _bind_variant_fields resolves real field types)----
RenumAliasClass: sizet*
RenumAliasField: sizet*
RenumAliasIndex: sizet*
@@ -30,5 +27,5 @@ novirtual class CompilerFactory:
self.Pool = pool
self.PermPool = perm_pool
# 全局工厂单例
# Global factory singleton
factory: CompilerFactory* = None
+334
View File
@@ -0,0 +1,334 @@
from stdint import *
import string
import stdio
import stdlib
import win32.fileio as fileio
import lib.core.VLogger as VLogger
# ============================================================
# Diag - 编译期诊断(traceback 系统):源码行读取 + 统一打印 + 终止策略
# ============================================================
# 抽出来的原因:格式化 / 源码行读取 / 终止策略原先都散在 TypeGenErr 里(连同它的
# TraceCount 调用栈遍历),导致"多错误栈"(错误收集 + 每单元去重 + 上限)无处安放。
# 现在 TypeGenErr / GenApi 的 `fatal_error` / `dump_parse_traceback` 全部**委托**到这里;
# 保留在 TypeGenErr 里的只有两件"它才知道"的东西:
# ① 调用栈帧的遍历(`TypeGenBase._get_trace_frame`);
# ② 当前文件名(`TypeGenResolve.get_current_file`)。
#
# **行为与抽离前逐字一致**:报文顺序、缩进、caret 规则、fflush、repl_abort 全不变。
# 诊断模型:**单条 + 立即终止**(不收集多条错误)—— 但单条自带**整条调用链**(DiagEntry),
# 由调用方(TypeGenErr)填好 entry 后 emit,方向由 DIAG_DIR_* 决定。所有打印都拆成独立的
# emit_*,便于在不改调用点的前提下调整格式。
# ============================================================
# ---- traceback 打印方向 ----
define DIAG_DIR_UP = 0 # 外→内:最外层触发点在上,错误点最后(Python "most recent call last")
define DIAG_DIR_DOWN = 1 # 内→外:错误点最先,越往外越下
define DIAG_MAX_FRAMES = 32 # 调用链上限(与 TypeGenMixin.TRACE_MAX 对齐)
# ============================================================
# DiagEntry - 单条编译期诊断(= 一次 traceback 要打印的全部内容)
# ============================================================
# **不是**错误收集容器:任何时刻只处理"出错的那一条"(打印完即终止),
# 但这一条自带**整条调用链** —— 编译器逐层下潜进各源码上下文
# (import 触发依赖加载 / 泛型特化 / 函数体翻译)时的触发点,
# 用于"从错误发生点向外展示触发过程"。
# 所有字符串都用 stdlib 深拷贝:编译期池会回绕,不能借用池内指针。
struct DiagEntry:
# 字段名不要与"本模块 import 的类名"重名:TPC 的类型/符号表按**非限定名**索引
# (Gen.structs / SymbolTable / _mangle_func_name),曾把本字段的 `File` 与
# `win32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
# `File.__before_init__` 的 self 形参解析成 `i8**`(llvmlite: "Type of #1 arg
# mismatch: i8** != %"...fileio.File"*")。故此处用 FileName 而非 File。
FileName: str # 当前帧(错误点)文件
Line: int # 当前帧行号
ColStart: int # caret 起始列(1-based;0 = 不画)
ColEnd: int # caret 结束列
Category: str # LifetimeError / SyntaxError / TypeError ...
Msg: str # 消息
Hint: str # 可选提示(可 None)
FrameCount: int # 调用链帧数
FrameFiles: sizet* # 调用链每帧文件名(深拷贝)
FrameLines: sizet* # 调用链每帧行号(与 FrameFiles 同容量)
# 泄漏点(可选):被调者那边**造成逃逸的写入点**。只有逃逸类 LifetimeError 会有值;
# 打印成独立一帧 `File lifetime leak "<file>", line N`,与调用栈帧(`File "..."`)区分。
LeakFileName: str
LeakLine: int
Cap: int # 内部:FrameFiles/FrameLines 容量(不参与打印)
# dup_str - 深拷贝到 stdlib 堆(诊断需跨池回绕点存活)
def dup_str(s: str) -> str:
if s is None: return None
n: sizet = string.strlen(s)
b: str = str(stdlib.malloc(n + 1))
if b is None: return None
string.strncpy(b, s, n)
b[n] = '\0'
return b
# new_entry - 分配一条诊断(调用方持有;打印完用 free_entry 释放)
def new_entry(max_frames: int) -> DiagEntry*:
ent: DiagEntry* = (DiagEntry*)(stdlib.malloc(DiagEntry.__sizeof__()))
if ent is None: return None
if max_frames <= 0: max_frames = 1
ent.FileName = None
ent.Line = 0
ent.ColStart = 0
ent.ColEnd = 0
ent.Category = None
ent.Msg = None
ent.Hint = None
ent.FrameCount = 0
ent.LeakFileName = None
ent.LeakLine = 0
ent.Cap = max_frames
ent.FrameFiles = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
ent.FrameLines = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
return ent
# begin - 初始化一条诊断(随后用 add_frame/set_current 填调用链与当前位置)
def begin(ent: DiagEntry*, category: str, msg: str, hint: str = None) -> int:
if ent is None: return 0
ent.Category = dup_str(category)
ent.Msg = dup_str(msg)
ent.Hint = dup_str(hint)
ent.FileName = None
ent.Line = 0
ent.ColStart = 0
ent.ColEnd = 0
ent.FrameCount = 0
ent.LeakFileName = None
ent.LeakLine = 0
return 0
# set_current - 设置当前帧(错误点)位置
def set_current(ent: DiagEntry*, file_name: str, lineno: int, col_start: int, col_end: int) -> int:
if ent is None: return 0
ent.FileName = dup_str(file_name)
ent.Line = lineno
ent.ColStart = col_start
ent.ColEnd = col_end
return 0
# set_leak - 设置"泄漏点"帧(File lifetime leak);仅逃逸类 LifetimeError 使用
def set_leak(ent: DiagEntry*, file_name: str, lineno: int) -> int:
if ent is None or lineno <= 0: return 0
ent.LeakFileName = dup_str(file_name)
ent.LeakLine = lineno
return 0
# add_frame - 追加调用链一帧(按"自外向内"顺序追加)
def add_frame(ent: DiagEntry*, file_name: str, lineno: int) -> int:
if ent is None or ent.FrameFiles is None or ent.FrameLines is None: return 0
if ent.FrameCount >= ent.Cap: return 0
ent.FrameFiles[ent.FrameCount] = sizet(dup_str(file_name))
ent.FrameLines[ent.FrameCount] = sizet(lineno)
ent.FrameCount += 1
return 0
# emit - 打印一条诊断:头 → 调用链(按 direction) → 当前帧(caret) → 类别: 消息
# 去重:① 连续两个位置完全相同的栈帧只印一次;② 与“错误点帧”位置相同的栈帧不再重复印
#(此前常见 `_start` 定义行被打印两遍:栈内最内层帧与错误点帧落在同一行)。
def emit(ent: DiagEntry*, direction: int) -> int:
if ent is None: return 0
emit_header()
last_file: str = ""
last_line: int = -1
if ent.FrameFiles is not None and ent.FrameLines is not None:
if direction == DIAG_DIR_DOWN:
i_d: int = ent.FrameCount - 1
while i_d >= 0:
ff: str = str(ent.FrameFiles[i_d])
ll: int = int(ent.FrameLines[i_d])
dup: int = 1 if last_file != "" and string.strcmp(ff, last_file) == 0 and ll == last_line else 0
if dup == 0:
emit_frame(ff, ll)
last_file = ff
last_line = ll
i_d -= 1
else:
i_u: int = 0
while i_u < ent.FrameCount:
ff2: str = str(ent.FrameFiles[i_u])
ll2: int = int(ent.FrameLines[i_u])
dup2: int = 1 if last_file != "" and string.strcmp(ff2, last_file) == 0 and ll2 == last_line else 0
cur_same: int = 1 if ent.FileName is not None and string.strcmp(ff2, ent.FileName) == 0 and ll2 == ent.Line else 0
if dup2 == 0 and cur_same == 0:
emit_frame(ff2, ll2)
last_file = ff2
last_line = ll2
i_u += 1
emit_current(ent.FileName, ent.Line, ent.ColStart, ent.ColEnd)
# 泄漏点帧紧跟在"错误点"之后(往下再一行),再印类别: 消息
if ent.LeakLine > 0: emit_leak_frame(ent.LeakFileName, ent.LeakLine)
emit_category(ent.Category, ent.Msg)
return 0
# free_entry - 释放一条诊断(含深拷贝的字符串)
def free_entry(ent: DiagEntry*) -> int:
if ent is None: return 0
if ent.FileName is not None: stdlib.free(ent.FileName)
if ent.LeakFileName is not None: stdlib.free(ent.LeakFileName)
if ent.Category is not None: stdlib.free(ent.Category)
if ent.Msg is not None: stdlib.free(ent.Msg)
if ent.Hint is not None: stdlib.free(ent.Hint)
if ent.FrameFiles is not None:
i_f: int = 0
while i_f < ent.FrameCount:
if ent.FrameFiles[i_f] != 0: stdlib.free(str(ent.FrameFiles[i_f]))
i_f += 1
stdlib.free(ent.FrameFiles)
if ent.FrameLines is not None: stdlib.free(ent.FrameLines)
stdlib.free(ent)
return 0
# read_source_line - 读源文件第 lineno 行,供 traceback 展示
def read_source_line(file_path: str, lineno: int) -> str:
"""读第 lineno 行;返回 stdlib 分配的缓冲(调用方 free),读不到返回 None"""
if file_path is None or lineno <= 0: return None
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
if f.closed: return None
# Allocate by actual file size: a fixed 256KB buffer truncated large files so late line
# numbers were unreadable. The 64MB cap guards against reading abnormally huge files.
fsize: LONGLONG = f.size()
if fsize <= 0 or fsize > 67108864:
f.close()
return None
src_size: sizet = sizet(fsize)
buf: bytes = stdlib.malloc(src_size + 1)
if buf is None:
f.close()
return None
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
f.close()
if bytes_read <= 0:
stdlib.free(buf)
return None
# Scan line by line for the target lineno
cur_line: int = 1
line_start: sizet = 0
for i in range(int(bytes_read)):
if buf[i] == '\n':
if cur_line == lineno:
line_len: sizet = i - line_start
if line_len > 0 and buf[line_start + line_len - 1] == '\r': line_len -= 1
line_buf: bytes = stdlib.malloc(line_len + 1)
if line_buf is not None:
string.strncpy(line_buf, buf + line_start, line_len)
line_buf[line_len] = '\0'
stdlib.free(buf)
return line_buf
stdlib.free(buf)
return None
cur_line += 1
line_start = i + 1
stdlib.free(buf)
return None
# emit_header - traceback 头(解析失败与语义错误共用同一行文案)
def emit_header() -> int:
stdio.printf("Traceback (most recent call last, compile-time error):\n")
return 0
# emit_frame - 调用栈里的一帧(只印位置与源码行,不印 caret)
def emit_frame(file_name: str, lineno: int) -> int:
if file_name is None: return 0
if lineno > 0:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
src: str = read_source_line(file_name, lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
else: stdio.printf(' File "%s"\n', file_name)
return 0
# emit_leak_frame - "泄漏点"帧:标签用 `File lifetime leak`,与调用栈帧(`File "..."`)区分
def emit_leak_frame(file_name: str, lineno: int) -> int:
if file_name is None: return 0
if lineno > 0:
stdio.printf(' File lifetime leak "%s", line %d\n', file_name, lineno)
src: str = read_source_line(file_name, lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
else: stdio.printf(' File lifetime leak "%s"\n', file_name)
return 0
# emit_current - 当前帧(带 caret 列范围;行读不出时印警告)
def emit_current(file_name: str, lineno: int, col_start: int, col_end: int) -> int:
# Sanity defense: read the line first to verify lineno belongs to the current file.
# If unreadable the file/line is untrustworthy (e.g. reused AST); print a warning instead.
src_line: str = None
line_ok: int = 1
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line = read_source_line(file_name, lineno)
if src_line is None: line_ok = 0
if line_ok == 0:
stdio.printf(' File "%s", line %d (!! cannot read that source line: file does not exist or line number out of range; the error may come from a reused AST tree, rely on the error message)\n', file_name, lineno)
else:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
if src_line is not None:
stdio.printf(" %s\n", src_line)
# Column-range marker: use ^ under the source line to mark the error range
if col_start > 0 and col_end >= col_start:
stdio.printf(" ")
j: int
for j in range(int(col_end)):
if j >= col_start - 1 and j < col_end: stdio.printf("^")
else: stdio.printf(" ")
stdio.printf("\n")
stdlib.free(src_line)
return 0
# emit_category - `类别: 消息`,随后立即 fflush
def emit_category(err_type: str, msg: str) -> int:
if err_type is None or err_type[0] == 0: err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
# Force flush before exiting: stdout redirected to a file is fully buffered, so terminating
# directly drops the whole traceback (observed: 2.log kept only one VLogger line).
stdio.fflush(None)
return 0
# emit_parse - 解析失败的单帧 traceback(Python 风格,单个 ^ 指向 token 起始列)
def emit_parse(file_path: str, err_line: int, err_col: int, err_msg: str) -> int:
emit_header()
if file_path is None or file_path[0] == 0: file_path = "<unknown>"
if err_line > 0:
stdio.printf(' File "%s", line %d\n', file_path, err_line)
src: str = read_source_line(file_path, err_line)
if src is not None:
stdio.printf(" %s\n", src)
# Column marker: put ^ under the error token's start column (parser col_offset is
# 0-based, so 4-space indent + err_col spaces lands on the token's first char)
if err_col > 0:
stdio.printf(" ")
for _ in range(int(err_col)): stdio.printf(" ")
stdio.printf("^\n")
stdlib.free(src)
else: stdio.printf(' File "%s"\n', file_path)
if err_msg is None or err_msg[0] == 0: err_msg = "syntax error"
stdio.printf("SyntaxError: %s\n", err_msg)
return 0
# finish - 终止策略:REPL 里 longjmp 回循环(报文已印、进程存活),批处理里 sys.exit(1)
def finish() -> None:
VLogger.repl_abort()
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+15 -19
View File
@@ -1,39 +1,35 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import memhub
import llvmlite
import mlirlite
# ============================================================
# BaseGenMixin - 生成器基座(状态容器)
# ============================================================
# BaseGenMixin - generator base (state container).
novirtual class BaseGenMixin:
"""生成器状态容器(基座)"""
"""Generator state container (base)."""
# 内存/IR 状态
# Memory / IR state
Gen: Generator.Generator*
Pool: memhub.MemBuddy* # 内存池
Module: llvmlite.LLVMModule* # 当前 LLVM 模块
Builder: llvmlite.IRBuilder* # 当前 IRBuilder
# 符号表/函数表
SymTab: rawptr[GenTypes.SymbolTable]
Pool: memhub.MemBuddy* # Memory pool
Module: mlirlite.LLVMModule* # Current LLVM module
Builder: mlirlite.IRBuilder* # Current IRBuilder
# Symbol / function tables
SymTab: GenTypes.SymbolTable*
Funcs: GenTypes.FuncEntry*
FuncCount: int
# 导入/模块信息
# Import / module info
ImportedModules: str
FromImports: str
ModuleSha1: str
# 当前翻译上下文
CurFunc: llvmlite.Function*
CurBuilder: llvmlite.IRBuilder*
# Current translation context
CurFunc: mlirlite.Function*
CurBuilder: mlirlite.IRBuilder*
CurClassName: str
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
"""初始化生成器状态(默认值;子类构造时调用)"""
if self is None:
return 1
"""Initialize generator state to defaults; called from subclass constructors."""
if self is None: return 1
self.Pool = pool
self.Module = None
self.Builder = None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+117 -167
View File
@@ -1,8 +1,7 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import viperlib
@@ -12,107 +11,91 @@ import lib.core.Gen.GenApi as GenApi
from . import CallGenBase
# ============================================================
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
# ============================================================
# CallGenFd - split from CallGenMixin (stage B): FakeDuck / requires field injection / provider stack
novirtual class CallGenFd(CallGenBase.CallGenBase):
"""FakeDuck / requires 字段注入 / provider 栈"""
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str,
method_name: str,
cl: ast.Call*,
trans: HT.Translator*) -> llvmlite.Value*:
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
"""FakeDuck / requires field injection / provider stack"""
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
"""FakeDuck: resolve a str variable's method call to extension class _Type/_str, return the call result or None"""
if pool is None or builder is None or cl is None or trans is None: return None
if method_name is None: return None
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
# cls_name 为 None(表达式接收者解析失败)时也尝试
# Only trigger for str (AnnotClassName == "str", object is char*);
# also try when cls_name is None (receiver resolution failed).
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
if string.strcmp(fd_at.attr, method_name) != 0: return None
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
fd_func: llvmlite.Function* = None
# Candidate extension classes: _Type first (a.type_id()), then _str (a.upper() etc.)
fd_func: mlirlite.Function* = None
fd_cls: str = None
fd_key: str = pool.alloc(160)
if fd_key is not None:
for fd_ci in range(2):
fd_cn: str = "_Type"
if fd_ci == 1:
fd_cn = "_str"
if fd_ci == 1: fd_cn = "_str"
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
fd_func = self.find_func_in_module(mod, fd_key)
if fd_func is not None:
fd_cls = fd_cn
break
if fd_func is None or fd_cls is None: return None
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
fd_sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
# The extension class struct must be registered (GEP/temp shadow needs a type)
fd_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
if fd_sty is None: return None
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
# Extension class field count: with fields (_str) self uses a shadow struct pointer;
# without fields (_Type) self bitcasts the object value to the extension pointer (matches TPC L2856-2876).
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
fd_has_fields: int = 0
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0:
fd_has_fields = 1
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
fd_self: llvmlite.Value* = None
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0: fd_has_fields = 1
# self arg: prefer an existing shadow {var}__meta__ (created by fd2, only when the extension class has fields)
fd_self: mlirlite.Value* = None
if fd_at.value.kind() == ast.ASTKind.Name:
fd_onm: ast.Name* = ast.Name*(fd_at.value)
if fd_onm.id is not None:
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
# Pool-persistent buffer (an f-string stack buffer would expire after return)
fd_mn: str = pool.alloc(256)
if fd_mn is not None:
viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
fd_meta: llvmlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
if fd_meta is not None and fd_has_fields != 0:
fd_self = fd_meta
if fd_mn is not None: viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
fd_meta: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
if fd_meta is not None and fd_has_fields != 0: fd_self = fd_meta
if fd_self is None:
if fd_has_fields != 0:
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
# Fielded extension class with no shadow (string literal etc.): create and init a temp
# shadow at entry; first ensure full _str definition (cross-module structs default to opaque and alloca would report unsized).
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
if fd_self is None: return None
fd_obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
fd_obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_val is not None:
fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__")
if fd_dfe is not None and fd_dfe.Ty is not None:
fd_dp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
if fd_dp is not None:
llvmlite.build_store(builder, fd_obj_val, fd_dp)
fd_dp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
if fd_dp is not None: mlirlite.build_store(builder, fd_obj_val, fd_dp)
fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
if fd_bfe is not None and fd_bfe.Ty is not None:
fd_bp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, fd_bfe.Ty, "null")
if fd_bp is not None and fd_null is not None:
llvmlite.build_store(builder, fd_null, fd_bp)
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
fd_bp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, fd_bfe.Ty, "null")
if fd_bp is not None and fd_null is not None: mlirlite.build_store(builder, fd_null, fd_bp)
# FakeDuck: auto-inject requires fields from the with context (e.g. __mbuddy__)
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
else:
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
fd_obj_v: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
# Fieldless extension class (_Type): bitcast the object value to the extension pointer
fd_obj_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_v is None: return None
fd_self = llvmlite.build_bitcast(builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty))
fd_self = mlirlite.build_bitcast(builder, fd_obj_v, mlirlite.Ptr(pool, fd_sty))
if fd_self is None: return None
# 构建参数链表: self → 其余参数
llvmlite.value_set_next(fd_self, None)
fd_head: llvmlite.Value* = fd_self
fd_tail: llvmlite.Value* = fd_self
# Build the argument list: self -> remaining args
mlirlite.value_set_next(fd_self, None)
fd_head: mlirlite.Value* = fd_self
fd_tail: mlirlite.Value* = fd_self
fd_args: list[ast.AST*]* = cl.args
fd_can: int = 0
if fd_args is not None:
fd_can = len(fd_args)
for fd_ai in range(fd_can):
if fd_args is not None: fd_can = int(len(fd_args))
for fd_ai in range(int(fd_can)):
fd_arg: ast.AST* = fd_args.get(fd_ai)
if fd_arg is not None:
fd_av: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
# char 常量参数 → 单字符字符串(char 隐式转 str,
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
fd_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
# char constant arg -> single-char string (implicit char->str, e.g. s1.split(',') where
# ',' is i32 44); passing it raw to a str param makes strlen/strncmp deref 44 and crash.
if fd_arg.kind() == ast.ASTKind.Constant:
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
if fd_ac.const_kind == ast.CONST_CHAR:
@@ -122,175 +105,142 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
ch_buf[1] = '\0'
fd_av = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, ch_buf, trans)
if fd_av is not None:
llvmlite.value_set_next(fd_av, None)
llvmlite.value_set_next(fd_tail, fd_av)
mlirlite.value_set_next(fd_av, None)
mlirlite.value_set_next(fd_tail, fd_av)
fd_tail = fd_av
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
fd_ret_ty: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(fd_func)
if fd_ret_ty is None:
fd_ret_ty = llvmlite.Int32(pool)
fd_fname: str = llvmlite.function_get_name(fd_func)
# Return type and call name (from the function definition hit by find_func_in_module)
fd_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fd_func)
if fd_ret_ty is None: fd_ret_ty = mlirlite.Int32(pool)
fd_fname: str = mlirlite.function_get_name(fd_func)
if fd_fname is None: return None
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标路径而非 list 内联 __getitem__(printf %s 崩溃)。从定义模块 text.ll 读取精确返回类型;仅当函数名确为 "{16hex}.{func}"(判定走唯一真源 GenApi.sha1_prefix_len)才提取模块 sha1
# Refine return type: cross-module declare may simplify a list[T] specialization return type to Ptr(i8), degrading the caller's alloca type -> parts[0] takes the char-index path instead of list inline __getitem__ (printf %s crash). Authority is the defining module's vpi (fallback text.ll); extract the module sha1 only when the name is "{16hex}.{func}" (via GenApi.sha1_prefix_len)
if GenApi.sha1_prefix_len(fd_fname) != 0:
fd_sha1: str = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
fd_exact: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None:
fd_ret_ty = fd_exact
fd_exact: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, fd_sha1, fd_cls, method_name)
if fd_exact is None: fd_exact = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None: fd_ret_ty = fd_exact
fd_total: int = 1 + fd_can
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
# ============================================================
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
#
# 对齐 TPC HandlesExprCall.py L4021-4079:
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
# 用户已手动设置的值)。_find_provider 未注册(未 import
# _withcontent)时跳过注入,不影响非 with 场景。
# ============================================================
# Value-reference (&T / &temp T) params of the resolved extension method: auto-address the
# user args (the shadow self pointer occupies slot 0).
fd_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, fd_fname, fd_args, fd_head, fd_total, 1)
return mlirlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
# _inject_requires_fields - FakeDuck: inject requires fields from the with context.
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
# ============================================================
def _inject_requires_fields(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
meta_ptr: llvmlite.Value*,
class_name: str) -> int:
"""在 with 上下文注入 requires 字段,返回注入字段数"""
if trans is None or pool is None or builder is None or mod is None:
return 0
if meta_ptr is None or class_name is None:
return 0
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
fd_fn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_find_provider")
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: str) -> int:
"""Inject requires fields from the with context, return the injected field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if meta_ptr is None or class_name is None: return 0
# _withcontent._find_provider (cross-module): inject only when visible in this module
# (matches TPC L4048-4050); otherwise non-with projects would emit undefined {sha1}._find_provider calls.
fd_fn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_find_provider")
if fd_fn_probe is None: return 0
fd_fname: str = llvmlite.function_get_name(fd_fn_probe)
fd_fname: str = mlirlite.function_get_name(fd_fn_probe)
if fd_fname is None: return 0
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
if cnt <= 0: return 0
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
if sty is None: return 0
i8_ty_j: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ty_j: mlirlite.LLVMType* = mlirlite.Int8(pool)
if i8_ty_j is None: return 0
i8_ptr_ty_j: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_j)
i8_ptr_ty_j: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_j)
if i8_ptr_ty_j is None: return 0
cur_func: llvmlite.Function* = trans._cur_func
cur_func: mlirlite.Function* = trans._cur_func
if cur_func is None: return 0
injected: int = 0
for ji in range(cnt):
for ji in range(int(cnt)):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
if fname is None: continue
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
if fe is None or fe.Ty is None: continue
# 参数:字段名字符串(i8*)
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
# Arg: field name string (i8*)
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
if fd_fstr is None: continue
llvmlite.value_set_next(fd_fstr, None)
mlirlite.value_set_next(fd_fstr, None)
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
fd_ptr: llvmlite.Value* = llvmlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
fd_ptr: mlirlite.Value* = mlirlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
if fd_ptr is None: continue
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty_j, "null")
# Conditional inject: store only when ptr != NULL (preserve user-set values)
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty_j, "null")
if fd_null is None: continue
fd_cmp: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, fd_ptr, fd_null)
fd_cmp: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, fd_ptr, fd_null)
if fd_cmp is None: continue
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
# 冲突 → llc "Terminator found in the middle of a basic block")
# Unique block names (create_block does not auto-number; duplicate labels in textual IR
# conflict -> llc "Terminator found in the middle of a basic block").
vtc2: int = trans._label_counter
trans._label_counter = vtc2 + 1
fd_bb_buf2: str = pool.alloc(64)
if fd_bb_buf2 is None: continue
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
fd_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
fd_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
fd_inject_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
fd_inject_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
if fd_skip_bb is None or fd_inject_bb is None: continue
llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
# inject 块:bitcast + GEP + store,跳回 skip
llvmlite.position_at_end(builder, fd_inject_bb)
fd_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, fd_ptr, fe.Ty)
mlirlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
# inject block: bitcast + GEP + store, then jump back to skip
mlirlite.position_at_end(builder, fd_inject_bb)
fd_cast: mlirlite.Value* = mlirlite.build_bitcast(builder, fd_ptr, fe.Ty)
if fd_cast is not None:
fd_fp: llvmlite.Value* = llvmlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
if fd_fp is not None: llvmlite.build_store(builder, fd_cast, fd_fp)
llvmlite.build_br(builder, fd_skip_bb)
# skip 块:恢复 CurBlock,继续后续代码生成
llvmlite.position_at_end(builder, fd_skip_bb)
fd_fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
if fd_fp is not None: mlirlite.build_store(builder, fd_cast, fd_fp)
mlirlite.build_br(builder, fd_skip_bb)
# skip block: restore CurBlock and continue codegen
mlirlite.position_at_end(builder, fd_skip_bb)
injected += 1
return injected
# ============================================================
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
# provider push/pop(__provides__ 元数据,fd1 注册)
#
# 对齐 TPC HandlesWith.py L138-174:
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
# ============================================================
# _emit_provider_push / _emit_provider_pop - FakeDuck: with-context provider push/pop (__provides__ metadata).
# Matches TPC HandlesWith.py L138-174: push _push_provider(field, ctx_i8) on entry and equal _pop_provider() on exit.
# ============================================================
def _emit_provider_push(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str,
ctx_val: llvmlite.Value*) -> int:
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, ctx_val: mlirlite.Value*) -> int:
"""Emit a _push_provider(field, ctx_i8) call per provided field, return the field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None or ctx_val is None: return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0: return 0
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pfn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_push_provider")
# _withcontent._push_provider (cross-module, same visibility guard as _find_provider:
# skip when this module does not import _withcontent, to avoid undefined symbol).
fd_pfn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_push_provider")
if fd_pfn_probe is None: return 0
fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe)
fd_pfn: str = mlirlite.function_get_name(fd_pfn_probe)
if fd_pfn is None: return 0
i8_ty_p: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ty_p: mlirlite.LLVMType* = mlirlite.Int8(pool)
if i8_ty_p is None: return 0
i8_ptr_ty_p: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_p)
i8_ptr_ty_p: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_p)
if i8_ptr_ty_p is None: return 0
ret_i32_p: llvmlite.LLVMType* = llvmlite.Int32(pool)
ret_i32_p: mlirlite.LLVMType* = mlirlite.Int32(pool)
if ret_i32_p is None: return 0
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
fd_ctx_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
# Bitcast the ctx value to i8* (provider stack stores void*)
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
if fd_ctx_i8 is None: return 0
for pi in range(fd_pc):
for pi in range(int(fd_pc)):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
if fname is None: continue
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
if fd_fstr is None: continue
llvmlite.value_set_next(fd_fstr, fd_ctx_i8)
llvmlite.value_set_next(fd_ctx_i8, None)
mlirlite.value_set_next(fd_fstr, fd_ctx_i8)
mlirlite.value_set_next(fd_ctx_i8, None)
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
mlirlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
return fd_pc
def _emit_provider_pop(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str) -> int:
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str) -> int:
"""Emit a _pop_provider() call per provided field, return the field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None: return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0: return 0
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pon_probe: llvmlite.Function* = self.find_func_in_module(mod, "_pop_provider")
# _withcontent._pop_provider (cross-module, same visibility guard as _find_provider:
# skip when this module does not import _withcontent, to avoid undefined symbol).
fd_pon_probe: mlirlite.Function* = self.find_func_in_module(mod, "_pop_provider")
if fd_pon_probe is None: return 0
fd_pon: str = llvmlite.function_get_name(fd_pon_probe)
fd_pon: str = mlirlite.function_get_name(fd_pon_probe)
if fd_pon is None: return 0
ret_i32_q: llvmlite.LLVMType* = llvmlite.Int32(pool)
ret_i32_q: mlirlite.LLVMType* = mlirlite.Int32(pool)
if ret_i32_q is None: return 0
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
for _pi in range(fd_pc):
llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
for _pi in range(int(fd_pc)): mlirlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
return fd_pc
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+163 -293
View File
@@ -1,9 +1,8 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import stdio
@@ -13,57 +12,35 @@ import lib.core.Gen.Translator as HT
from . import CallGenBase
# ============================================================
# CallGenProp - CallGenMixin 拆分(阶段 B):属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)
# CallGenProp - split from CallGenMixin (stage B): property access (getter/setter/deleter / static class calls / list builtins)
# ============================================================
novirtual class CallGenProp(CallGenBase.CallGenBase):
"""属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)"""
"""Property access (getter/setter/deleter / static class calls / list builtins)"""
# ============================================================
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
#
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
# ============================================================
# Property methods (@property/@x.setter/@x.deleter) and static/class call helpers.
# Translate obj.prop / obj.prop=v / del obj.prop / Class.method() into the matching {sha1}.ClassName.prop call.
# ============================================================
# _translate_property_call — 翻译属性方法调用
#
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
#
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
# ============================================================
def _translate_property_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
class_name: str,
method_name: str,
call_head: llvmlite.Value*,
call_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译属性/静态方法调用,返回 call 结果 Value"""
# _translate_property_call - translate a property/static method call.
# Resolve the call name like _translate_method_call; call_head/call_count is the prebuilt arg list.
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*, arg_nodes: list[ast.AST*]* = None, start_idx: int = 0) -> mlirlite.Value*:
"""Translate a property/static method call, return the call result Value"""
if pool is None or builder is None or class_name is None or method_name is None: return None
# 构造查找名: ClassName.method_name
# Build the lookup name: ClassName.method_name
lookup_name: str = pool.alloc(128)
if lookup_name is None: return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
# 查找函数(精确/后缀匹配)
found_func: llvmlite.Function* = self.find_func_in_module(mod, lookup_name)
# Look up the function (exact/suffix match)
found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name)
# 确定调用名
# Determine the call name
call_name: str = lookup_name
if found_func is not None:
found_name: str = llvmlite.function_get_name(found_func)
if found_name is not None:
call_name = found_name
found_name: str = mlirlite.function_get_name(found_func)
if found_name is not None: call_name = found_name
else:
# 未找到:用类所属模块的 SHA1 构造调用名
# Not found: build the call name from the class's owning module SHA1
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
if cls_sha1_pc is not None:
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
@@ -72,223 +49,154 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
call_name = mangled_pc
# 确定返回类型
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
call_ret_ty: llvmlite.LLVMType* = i32_ty
# Determine the return type
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
call_ret_ty: mlirlite.LLVMType* = i32_ty
if found_func is not None:
frt: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(found_func)
if frt is not None:
call_ret_ty = frt
frt: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(found_func)
if frt is not None: call_ret_ty = frt
else:
sp_ty1: llvmlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
if sp_ty1 is not None:
call_ret_ty = sp_ty1
sp_ty1: mlirlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
if sp_ty1 is not None: call_ret_ty = sp_ty1
else:
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
looked_up_ty: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
if looked_up_ty is not None:
call_ret_ty = looked_up_ty
# Authoritative source: vpi stub return-annotation cache (in place, no hardcoded whitelist)
vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty
else:
# 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单)
vpi_ret_ty: llvmlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
if vpi_ret_ty is not None:
call_ret_ty = vpi_ret_ty
# Fallback: parse the module text.ll define line (legacy form not covered by vpi)
looked_up_ty: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
if looked_up_ty is not None: call_ret_ty = looked_up_ty
else:
# Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成):
# 保持默认 i32(build_call(Void) 返回 None 会破坏表达式翻译)。
if cls_sha1_rt is None \
or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0:
call_ret_ty = llvmlite.Int32(pool)
else:
call_ret_ty = self._fail_method_ret_ty(method_name)
# Phase A deps not ready (cls_sha1_rt None / the defining module's text.ll missing):
# keep the i32 default (build_call(Void) returning None would break expression translation).
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
else: call_ret_ty = self._fail_method_ret_ty(method_name, None, class_name, None, cls_sha1_rt)
# 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
tgt_func_pc: llvmlite.Function* = found_func
if tgt_func_pc is None:
tgt_func_pc = self.find_func_in_module(mod, call_name)
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
tgt_func_pc: mlirlite.Function* = found_func
if tgt_func_pc is None: tgt_func_pc = self.find_func_in_module(mod, call_name)
if tgt_func_pc is not None:
prev_pc: llvmlite.Value* = None
cur_arg_pc: llvmlite.Value* = call_head
cur_param_pc: llvmlite.Param* = llvmlite.function_get_param_head(tgt_func_pc)
prev_pc: mlirlite.Value* = None
cur_arg_pc: mlirlite.Value* = call_head
cur_param_pc: mlirlite.Param* = mlirlite.function_get_param_head(tgt_func_pc)
while cur_arg_pc is not None and cur_param_pc is not None:
param_ty_pc: llvmlite.LLVMType* = llvmlite.param_get_ty(cur_param_pc)
param_ty_pc: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param_pc)
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
coerced_pc: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
coerced_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
if coerced_pc is not None and coerced_pc != cur_arg_pc:
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
llvmlite.value_set_next(coerced_pc, cur_arg_pc.Next)
if prev_pc is None:
call_head = coerced_pc
else:
llvmlite.value_set_next(prev_pc, coerced_pc)
# Keep the list intact: the new Value inherits the node's Next; predecessor points to it
mlirlite.value_set_next(coerced_pc, cur_arg_pc.Next)
if prev_pc is None: call_head = coerced_pc
else: mlirlite.value_set_next(prev_pc, coerced_pc)
cur_arg_pc = coerced_pc
prev_pc = cur_arg_pc
cur_arg_pc = cur_arg_pc.Next
cur_param_pc = llvmlite.param_get_next(cur_param_pc)
cur_param_pc = mlirlite.param_get_next(cur_param_pc)
# 确保 declare 存在,然后调用
# Ensure the declare exists, then call
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
return llvmlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
# ============================================================
# _resolve_obj_class_name — 解析对象表达式的类名(短名)
#
# 优先从变量注解(AnnotClassName)解析(如 "memhub.MemBuddy" → "MemBuddy"),
# 失败时从 LLVM 类型解析(Ptr(Struct) → 类名)。
# 供属性访问/赋值/删除识别 property 时使用。
# ============================================================
return mlirlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
# _resolve_obj_class_name - resolve an object expression's class short name.
# Prefer the variable annotation (AnnotClassName); fall back to the LLVM type (Ptr(Struct)).
# ============================================================
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*,
trans: HT.Translator*,
obj_node: ast.AST*) -> str:
"""解析对象表达式的类名(短名),失败返回 None"""
if pool is None or trans is None or obj_node is None:
return None
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, obj_node: ast.AST*) -> str:
"""Resolve an object expression's class short name, None on failure"""
if pool is None or trans is None or obj_node is None: return None
if obj_node.kind() == ast.ASTKind.Name:
obj_nm: ast.Name* = ast.Name*(obj_node)
if obj_nm.id is not None:
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
if annot_val is not None:
# [D32] 取「模块限定名」末段时只按**括号外**的 '.' 切分。
# 此前用 string.strrchr 在整个串上找最后一个 '.':泛型特化名
# "list[e94ef9e139461915.AST|t.CPtr]" 的 '.' 落在 '[' 内 ⇒
# 返回 "AST|t.CPtr]" 这种垃圾短名 ⇒ 后续按它查表必落空,
# 进而可能被兜底成任意特化(.get() 误分派)。
# [D32] Split the qualified name only on '.' outside brackets: the old strrchr over the
# whole string picked the '.' inside "list[sha1.AST*]", yielding a bogus short name.
n_av: sizet = string.strlen(annot_val)
last_dot_av: sizet = n_av
depth_av: int = 0
ai_av: sizet = 0
while ai_av < n_av:
for ai_av in range(int(n_av)):
ch_av: int = annot_val[ai_av]
if ch_av == '[':
depth_av += 1
elif ch_av == ']':
depth_av -= 1
elif ch_av == '.' and depth_av == 0:
last_dot_av = ai_av
ai_av += 1
if last_dot_av < n_av:
return annot_val + last_dot_av + 1
if ch_av == '[': depth_av += 1
elif ch_av == ']': depth_av -= 1
elif ch_av == '.' and depth_av == 0: last_dot_av = ai_av
if last_dot_av < n_av: return annot_val + last_dot_av + 1
return annot_val
return None
# ============================================================
# _try_property_getter_attr — 尝试将 obj.attr 翻译为 property getter 调用
#
# 检查 obj 的类是否有 @property/@x.getter 的 attr 方法;
# 若有则生成 call Class.attr(obj) 并返回调用结果 Value;否则返回 None。
# ============================================================
# _try_property_getter_attr - try to translate obj.attr into a property getter call.
# If the class has an @property/@x.getter for attr, emit call Class.attr(obj); else return None.
# ============================================================
def _try_property_getter_attr(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
at_node: ast.Attribute*,
obj_ptr: llvmlite.Value*,
trans: HT.Translator*,
fallback_class_name: str = None) -> llvmlite.Value*:
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
"""
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None:
return None
def _try_property_getter_attr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, at_node: ast.Attribute*, obj_ptr: mlirlite.Value*, trans: HT.Translator*, fallback_class_name: str = None) -> mlirlite.Value*:
"""Try a property getter access, return the call result Value; None if not a getter."""
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None: return None
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
if class_name is None:
class_name = fallback_class_name
if class_name is None:
return None
if class_name is None: class_name = fallback_class_name
if class_name is None: return None
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
if meta & GenTypes.META_PROP_GETTER == 0:
return None
# 单参数调用: obj → getter
llvmlite.value_set_next(obj_ptr, None)
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
# ============================================================
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
#
# 检查 obj 的类是否有 @x.setter 的 attr 方法;
# 若有则生成 call Class.attr$set(obj, v),返回 1;否则返回 0。
# ============================================================
if meta & GenTypes.META_PROP_GETTER == 0: return None
# Single-arg call: obj -> getter (no user args to auto-address; obj is slot 0)
mlirlite.value_set_next(obj_ptr, None)
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans, None, 1)
# _try_property_setter - try to translate obj.attr = v into a property setter call.
# If the class has an @x.setter for attr, emit call Class.attr$set(obj, v) and return 1; else 0.
# ============================================================
def _try_property_setter(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
target_attr: ast.Attribute*,
rhs_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*, rhs_node: ast.AST* = None) -> int:
"""Try a property setter assignment, return 1=handled (call emitted)"""
if pool is None or builder is None or mod is None or trans is None: return 0
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None: return 0
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
if meta & GenTypes.META_PROP_SETTER == 0: return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
# Translate the object value (self argument)
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None: return 0
# 构建参数链表: obj → rhs
llvmlite.value_set_next(obj_val, None)
llvmlite.value_set_next(rhs_val, None)
llvmlite.value_set_next(obj_val, rhs_val)
# setter 函数名带 $set 后缀
# Build the argument list: obj -> rhs
mlirlite.value_set_next(obj_val, None)
mlirlite.value_set_next(rhs_val, None)
mlirlite.value_set_next(obj_val, rhs_val)
# The setter function name carries the $set suffix
setter_name_buf: str = pool.alloc(128)
if setter_name_buf is None: return 0
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
self._translate_property_call(pool, builder, mod, class_name,
setter_name_buf, obj_val, 2, trans)
# Value-reference (&T / &temp T) setter value param: expose the rhs AST as user arg 0
# (obj is slot 0) so the helper can take the lvalue address / build a temp copy.
set_arg_nodes: list[ast.AST*]* = None
if rhs_node is not None:
set_arg_nodes = list[ast.AST*](pool, 1)
if set_arg_nodes is not None: set_arg_nodes.append(rhs_node)
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans, set_arg_nodes, 1)
return 1
# ============================================================
# _try_property_deleter — 尝试将 del obj.attr 翻译为 property deleter 调用
#
# 检查 obj 的类是否有 @x.deleter 的 attr 方法;
# 若有则生成 call Class.attr$del(obj),返回 1;否则返回 0。
# ============================================================
# _try_property_deleter - try to translate del obj.attr into a property deleter call.
# If the class has an @x.deleter for attr, emit call Class.attr$del(obj) and return 1; else 0.
# ============================================================
def _try_property_deleter(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
target_attr: ast.Attribute*,
trans: HT.Translator*) -> int:
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
def _try_property_deleter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, trans: HT.Translator*) -> int:
"""Try a property deleter, return 1=handled (call emitted)"""
if pool is None or builder is None or mod is None or trans is None: return 0
if target_attr is None or target_attr.attr is None: return 0
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None: return 0
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
if meta & GenTypes.META_PROP_DELETER == 0: return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
# Translate the object value (self argument)
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None: return 0
llvmlite.value_set_next(obj_val, None)
# deleter 函数名带 $del 后缀
mlirlite.value_set_next(obj_val, None)
# The deleter function name carries the $del suffix
del_name_buf: str = pool.alloc(128)
if del_name_buf is None: return 0
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
self._translate_property_call(pool, builder, mod, class_name,
del_name_buf, obj_val, 1, trans)
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans, None, 1)
return 1
# ============================================================
# _translate_static_class_call — 翻译 ClassName.method(args) 静态/类方法调用
#
# @staticmethod: 直接 call Class.method(args...),无 self
# @classmethod: 在参数前插入栈上分配的类实例指针(与 TPC 一致)
# ============================================================
# _translate_static_class_call - translate ClassName.method(args) for static/class methods.
# @staticmethod: call Class.method(args...). @classmethod: prepend a stack-allocated class pointer.
# ============================================================
def _translate_static_class_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
class_name: str,
method_name: str,
cl: ast.Call*,
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
def _translate_static_class_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
"""Translate a ClassName static/class method call, return the call result Value; None otherwise"""
if pool is None or builder is None or mod is None or trans is None: return None
if class_name is None or method_name is None or cl is None: return None
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
@@ -298,67 +206,55 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
if meta & GenTypes.META_CLASS != 0: is_class = 1
if is_static == 0 and is_class == 0: return None
# 构建参数链表
head: llvmlite.Value* = None
# Build the argument list
head: mlirlite.Value* = None
total_count: int = 0
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
# @classmethod: prepend a stack-allocated class pointer as the cls argument
if is_class != 0:
struct_ty_cc: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
struct_ty_cc: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
if struct_ty_cc is None: return None
cls_ptr: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
cls_ptr: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
if cls_ptr is None: return None
llvmlite.value_set_next(cls_ptr, None)
mlirlite.value_set_next(cls_ptr, None)
head = cls_ptr
total_count = 1
# 翻译调用参数
# Translate the call arguments
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
tail: llvmlite.Value* = head
for ai in range(can):
if cargs is not None: can = len(cargs)
tail: mlirlite.Value* = head
for ai in range(int(can)):
arg_node: ast.AST* = cargs.get(ai)
if arg_node is None: continue
arg_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
if arg_val is None: continue
llvmlite.value_set_next(arg_val, None)
mlirlite.value_set_next(arg_val, None)
if head is None:
head = arg_val
tail = arg_val
else:
llvmlite.value_set_next(tail, arg_val)
mlirlite.value_set_next(tail, arg_val)
tail = arg_val
total_count += 1
# 零参 @staticmethod(如 PyListObj.new()):head 保持 None 是合法的
# 无参调用,照常走 _translate_property_call(SHA1 前缀名 + 精确返回类型)。
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans)
# ============================================================
# _is_list_builtin_method - 判断是否是 list 泛型内置容器方法
#
# list[T] 是内置泛型容器(_list.vp),其方法(append/get/set/pop/clear/
# __iter__/__len__/__getitem__/__setitem__)由 translate_call 的 list
# 内置分支内联 GEP 处理,不注册为方法函数。若 obj.method() 检测把
# list 特化 struct(如 list[AST | CPtr])误当普通结构体走
# _translate_method_call,会查不到 append 返回类型 → fail-fast
# (NewAstTest body.append 根因)。此判断用于跳过 method_call 路径。
#
# 返回: 1=list 内置方法(应落到 list 内置分支),0=普通方法
# ============================================================
# Zero-arg @staticmethod (e.g. PyListObj.new()): head=None is a valid no-arg call, so still route
# through _translate_property_call; returning None would fall back to bare-name lookup and fail.
# Value-reference (&T / &temp T) user args: static methods have no self slot (start_idx=0),
# classmethods carry the cls pointer at slot 0 (start_idx=1).
sc_start_idx: int = 0
if is_class != 0: sc_start_idx = 1
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans, cargs, sc_start_idx)
# _is_list_builtin_method - test whether (class_name, method_name) is a list generic builtin.
# list[T] methods are inlined by translate_call's list branch, not registered as functions.
# ============================================================
def _is_list_builtin_method(self, class_name: str,
method_name: str) -> int:
"""判断 (class_name, method_name) 是否是 list 泛型内置方法"""
def _is_list_builtin_method(self, class_name: str, method_name: str) -> int:
"""Test whether (class_name, method_name) is a list generic builtin method"""
if class_name is None or method_name is None: return 0
# 类名必须以 "list" 开头(list / list[AST | CPtr] 特化名)
# Class name must start with "list" (list / list[AST*] specializations)
if string.strncmp(class_name, "list", 4) != 0: return 0
# 方法名必须是 list 内置方法集合
# Method name must be one of the list builtin methods
if string.strcmp(method_name, "append") == 0: return 1
if string.strcmp(method_name, "get") == 0: return 1
if string.strcmp(method_name, "set") == 0: return 1
@@ -370,76 +266,50 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
if string.strcmp(method_name, "__setitem__") == 0: return 1
return 0
# ============================================================
# _strip_module_sha1_prefix - 去跨模块特化名的 sha1 前缀
#
# 接收者实际结构体名常带 "{16位小写hex}." 前缀(如
# "e94ef9e139461915.list[ast.ASTptr]");判定基名 | 特化名前必须先剥离。
# 前缀长度判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
# ============================================================
# _strip_module_sha1_prefix - strip the cross-module specialization sha1 prefix.
# Receiver struct names often carry a "{16 hex}." prefix; use GenApi.sha1_prefix_len (C10).
def _strip_module_sha1_prefix(self, name: str) -> str:
"""去除 "{16hex}." 前缀(无前缀则原样返回;返回原串内部子串)"""
if name is None:
return None
"""Strip the "{16hex}." prefix (return the input unchanged if absent; returns a substring of the input)"""
if name is None: return None
pfx: int = GenApi.sha1_prefix_len(name)
return name + pfx if pfx != 0 else name
# _resolve_generic_receiver_name:解析接收者实际泛型特化名(IR 真值)——接收者实际 LLVM 结构体类型名比可能退化的注解/注册名可靠,故泛型判定以此为准:去 sha1 前缀取 '[' 前基名,基名是已注册泛型模板时含类型实参 ⇒ 返回实际特化名,无类型实参 ⇒ fail-fast(D25);基名非泛型模板或实际类型不可得 ⇒ 返回 None(调用方各自回退)。与 CallGenCall._translate_method_call 的 D25 分派修复共用本函数(唯一口径)
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*,
struct_ty: llvmlite.LLVMType*,
method_name: str,
cl: ast.Call*) -> str:
"""返回实际泛型特化名(去 sha1)或 None;裸泛型基类 fail-fast"""
if struct_ty is None:
return None
ty_nm: str = llvmlite.get_struct_name(struct_ty)
if ty_nm is None or ty_nm[0] == '\0':
return None
# _resolve_generic_receiver_name - resolve the receiver's actual generic specialization name (IR truth).
# Strip the sha1 prefix, take the base before '['; bare generic base => fail-fast (D25), else None.
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*, struct_ty: mlirlite.LLVMType*, method_name: str, cl: ast.Call*) -> str:
"""Return the actual generic specialization name (sha1 stripped) or None; a bare generic base fails fast"""
if struct_ty is None: return None
ty_nm: str = mlirlite.get_struct_name(struct_ty)
if ty_nm is None or ty_nm[0] == '\0': return None
short_nm: str = self._strip_module_sha1_prefix(ty_nm)
lb: str = string.strchr(short_nm, '[')
bare_len: sizet = 0
if lb is not None:
bare_len = sizet(uint64(lb) - uint64(short_nm))
else:
bare_len = string.strlen(short_nm)
if bare_len == 0:
return None
if lb is not None: bare_len = sizet(uint64(lb) - uint64(short_nm))
else: bare_len = string.strlen(short_nm)
if bare_len == 0: return None
base_nm: str = pool.alloc(bare_len + 1)
if base_nm is None:
return None
if base_nm is None: return None
string.strncpy(base_nm, short_nm, bare_len)
base_nm[bare_len] = '\0'
# 基名非已注册泛型模板(普通类接收者)⇒ 非泛型,交由调用方原口径处理
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None:
return None
if lb is not None:
return short_nm
# 裸泛型基类(无类型实参):接收者元素类型未知 → fail-fast(D25 口径)
self.Gen.TypeGenErr.fatal_error(
cl,
f"方法 '{method_name}' 的接收者实际结构体类型是裸泛型基类 "
f"'{short_nm}*'(丢失类型实参),元素类型未知;"
f"禁止静默按默认元素类型处理(元素按 i32 处理会截断指针),"
f"请让该变量的注解/类型带上实参(如 list[ast.AST*])",
"TypeError")
# Base is not a registered generic template (plain class receiver) => not generic; return None
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None: return None
if lb is not None: return short_nm
# Bare generic base (no type args): element type unknown -> fail-fast (D25)
self.Gen.TypeGenErr.fatal_error(cl, f"the actual struct type of the receiver of method '{method_name}' is a bare generic base " f"'{short_nm}*' (type arguments lost), so the element type is unknown; " f"silently using the default element type is forbidden (treating elements as i32 would truncate pointers); " f"make the variable annotation/type carry type arguments (e.g. list[ast.AST*])", "TypeError")
return None
# _is_list_builtin_receiver:基于接收者实际类型判定 list 内建方法,判据与 _resolve_generic_receiver_name 同源——优先采用接收者实际 LLVM 结构体类型名(去 sha1 判基名),不可得时回退注解名/注册名判定。返回 1=list 内置方法 / 0=非
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*,
recv_val: llvmlite.Value*,
fallback_name: str,
method_name: str,
cl: ast.Call*) -> int:
"""基于接收者实际类型判定 list 内建方法(实际类型不可得回退注解名)"""
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
# _is_list_builtin_receiver - test list builtin methods from the receiver's actual type.
# Prefer the actual LLVM struct name (sha1 stripped); fall back to the annotation/registered name.
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*, recv_val: mlirlite.Value*, fallback_name: str, method_name: str, cl: ast.Call*) -> int:
"""Test list builtin methods from the receiver's actual type (fall back to the annotation name if unavailable)"""
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
if sty is not None:
ty_nm: str = llvmlite.get_struct_name(sty)
ty_nm: str = mlirlite.get_struct_name(sty)
if ty_nm is not None and ty_nm[0] != '\0':
# 泛型模板接收者:裸基类 fail-fast;含实参用实际特化名
# Generic template receiver: bare base fail-fast; with type args use the actual specialization
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
if act_nm is not None:
return self._is_list_builtin_method(act_nm, method_name)
# 非泛型模板接收者:用去 sha1 后的实际短名判定(不复用退化注解名)
if act_nm is not None: return self._is_list_builtin_method(act_nm, method_name)
# Non-generic template receiver: judge by the actual short name (sha1 stripped)
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
# 实际类型不可得 → 回退注解名/注册名判定(信息缺失下的既有行为)
# Actual type unavailable -> fall back to the annotation/registered name
return self._is_list_builtin_method(fallback_name, method_name)
+153 -285
View File
@@ -1,7 +1,6 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import lib.core.Gen.Translator as HT
@@ -9,378 +8,247 @@ import lib.core.Gen.Translator as HT
from . import CallGenBase
# ============================================================
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
# CallGenVa - CallGenMixin split (stage B): va_arg variadics (start/arg/end / variadic call promotion)
# ============================================================
novirtual class CallGenVa(CallGenBase.CallGenBase):
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
"""va_arg variadics (start/arg/end and variadic call promotion)."""
# ============================================================
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
#
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
# _func_has_eh_params - whether callee signature has try-except EH out params (main excluded)
# ============================================================
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
arg_node: ast.AST*,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""获取 va_list 变量的 alloca 指针(不 load)
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
"""
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Return the alloca pointer of a va_list variable without loading it."""
if arg_node is None: return None
# Name 节点:直接查符号表获取 alloca 指针(不 load)
# Name node: look up the alloca pointer from the symbol table (no load)
if arg_node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is not None:
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is not None:
return alloca
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is not None: return alloca
# Fallback: translate_value (may return a loaded value; only valid when already a pointer)
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*,
type_node: ast.AST*,
trans: HT.Translator*) -> llvmlite.LLVMType*:
"""解析 va_arg/arg 的类型参数为 LLVMType
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*, type_node: ast.AST*, trans: HT.Translator*) -> mlirlite.LLVMType*:
"""Resolve the va_arg/arg type argument to an LLVMType, defaulting to i32."""
if type_node is None: return mlirlite.Int32(pool)
# Resolve the type expression via HandlesType.resolve_annotation_type
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None: return resolved
# Type node present but unresolvable: fail-fast, never fall back to i32 (would read the vararg slot at the wrong width)
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg type resolution failed", "TypeError")
return mlirlite.Int32(pool)
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit a va_arg instruction reading the next variadic argument from the va_list."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
支持的类型表达式:
int / int32 → i32
int64 → i64
t.CDouble → double
t.CPtr / str → i8*
str / bytes → i8*
无类型参数时默认 i32(vsnprintf 中 va_arg(ap) 常用于读取 i32)
"""
if type_node is None:
return llvmlite.Int32(pool)
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
return resolved
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
return llvmlite.Int32(pool)
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
"""
if can == 0 or cargs is None:
return llvmlite.const_int32(pool, 0)
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
# First arg: va_list (evaluate to an i8* address; aligned with TPC _HandleVaArgLlvm). In own *args
# translate_value returns the buffer address; in forwarding (sprintf -> vsnprintf) it loads the param.
ap_node: ast.AST* = cargs.get(0)
ap_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
if ap_val is None:
return llvmlite.const_int32(pool, 0)
ap_ptr: llvmlite.Value* = ap_val
ap_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
if ap_val is None: return mlirlite.const_int32(pool, 0)
ap_ptr: mlirlite.Value* = ap_val
# 第二个参数:目标类型(可选,默认 i32)
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
# Second arg: target type (optional, defaults to i32)
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
if can >= 2:
type_node: ast.AST* = cargs.get(1)
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73),
# 再 trunc 回目标宽度——否则返回 8 字节含高位垃圾,%d/%i 语义错误
# (viperlib vsnprintf %d 非 long 分支 `int64(va_arg(ap, int))`
# 编译成直接 va_arg i64,高 32 位未定义 → 负数当大正数 A6 根因)。
read_ty: llvmlite.LLVMType* = arg_ty
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (TPC emit_va_arg L72-73)
# then trunc to target width; otherwise high bits are garbage (%d/%i wrong, A6 root cause).
read_ty: mlirlite.LLVMType* = arg_ty
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
if abits_va != 0 and abits_va < 64:
read_ty = llvmlite.Int64(pool)
va_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, ap_ptr, read_ty)
if va_raw is None:
return llvmlite.const_int32(pool, 0)
# 8 字节槽 → 目标宽度(低 bits 位);调用方 int64(...) 会再 sext/zext
return llvmlite.build_trunc(builder, va_raw, arg_ty)
read_ty = mlirlite.Int64(pool)
va_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, ap_ptr, read_ty)
if va_raw is None: return mlirlite.const_int32(pool, 0)
# 8-byte slot -> target width (low bits); caller int64(...) re-applies sext/zext
return mlirlite.build_trunc(builder, va_raw, arg_ty)
# 生成 va_arg 指令
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
def _translate_arg_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
# Emit the va_arg instruction
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit va_arg reading from the enclosing variadic's own va_list or a forwarded va_list."""
# arg() has two forms:
# (1) Own-variadic form: inside `def f(a, *args)`, trans._va_list_ptr is the i8* address of
# the function's implicit va_list buffer; read the next vararg straight from it.
# (2) Forwarding form: in a NON-variadic wrapper such as vsnprintf(buf, size, fmt, args),
# the caller's va_list pointer is passed by value through the 4th param named "args"
# (vprintf style, see includes/viperlib.vp); there is no implicit va_list here, so
# trans._va_list_ptr is None and the pointer must be recovered from that param.
va_ptr: mlirlite.Value* = trans._va_list_ptr
if va_ptr is None:
# The "args" param's alloca is an i8** slot holding the forwarded i8* va_list value, so it
# MUST be loaded once: va_arg needs the pointer value itself (i8*), not the slot address.
args_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "args")
if args_alloca is None: return mlirlite.const_int32(pool, 0)
if args_alloca.Ty is None: return mlirlite.const_int32(pool, 0)
va_ptr = mlirlite.build_load(builder, args_alloca.Ty.Pointee, args_alloca)
if va_ptr is None: return mlirlite.const_int32(pool, 0)
使用当前函数的隐式 va_list(trans._va_list_ptr),
简化 *args 函数体内的 va_arg 调用。
"""
if trans._va_list_ptr is None:
return llvmlite.const_int32(pool, 0)
# 第一个参数:目标类型(可选,默认 i32)
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
# First arg: target type (optional, defaults to i32)
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
if can >= 1 and cargs is not None:
type_node: ast.AST* = cargs.get(0)
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致),
# 再 trunc 回目标宽度(%d/%i 高位垃圾 A6 修复,见 _translate_va_arg_sugar)
read_ty: llvmlite.LLVMType* = arg_ty
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (same as _translate_va_arg_sugar),
# then trunc to target width (%d/%i high-bit garbage fix, see _translate_va_arg_sugar).
read_ty: mlirlite.LLVMType* = arg_ty
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
if abits_arg != 0 and abits_arg < 64:
read_ty = llvmlite.Int64(pool)
arg_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
if arg_raw is None:
return llvmlite.const_int32(pool, 0)
return llvmlite.build_trunc(builder, arg_raw, arg_ty)
read_ty = mlirlite.Int64(pool)
arg_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, va_ptr, read_ty)
if arg_raw is None: return mlirlite.const_int32(pool, 0)
return mlirlite.build_trunc(builder, arg_raw, arg_ty)
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
return llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
# Emit va_arg, using va_ptr as the va_list address (own buffer or forwarded pointer)
return mlirlite.build_va_arg(builder, va_ptr, read_ty)
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
"""
if can == 0 or cargs is None:
return llvmlite.const_int32(pool, 0)
# 第一个参数:va_list 指针(获取 alloca,不 load)
# First arg: va_list pointer (get its alloca, no load)
ap_node: ast.AST* = cargs.get(0)
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None:
return llvmlite.const_int32(pool, 0)
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
i8_ty_vs: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_vs: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_vs)
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
if ap_i8 is None:
return llvmlite.const_int32(pool, 0)
# bitcast to i8* (@llvm.va_start takes i8*)
i8_ty_vs: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty_vs: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_vs)
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
void_ty_vs: llvmlite.LLVMType* = llvmlite.Void(pool)
llvmlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
return llvmlite.const_int32(pool, 0)
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
void_ty_vs: mlirlite.LLVMType* = mlirlite.Void(pool)
mlirlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
return mlirlite.const_int32(pool, 0)
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit llvm.va_end to clean up the va_list (a no-op on most platforms)."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
"""
if can == 0 or cargs is None:
return llvmlite.const_int32(pool, 0)
# 第一个参数:va_list 指针(获取 alloca,不 load)
# First arg: va_list pointer (get its alloca, no load)
ap_node: ast.AST* = cargs.get(0)
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None:
return llvmlite.const_int32(pool, 0)
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
i8_ty_ve: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_ve: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_ve)
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
if ap_i8 is None:
return llvmlite.const_int32(pool, 0)
# bitcast to i8* (@llvm.va_end takes i8*)
i8_ty_ve: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty_ve: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_ve)
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
void_ty_ve: llvmlite.LLVMType* = llvmlite.Void(pool)
llvmlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
return llvmlite.const_int32(pool, 0)
def _get_func_fixed_param_count(self, func: llvmlite.Function*) -> int:
"""获取函数的固定参数数量(遍历 Params 链表)"""
if func is None:
return 0
void_ty_ve: mlirlite.LLVMType* = mlirlite.Void(pool)
mlirlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
return mlirlite.const_int32(pool, 0)
def _get_func_fixed_param_count(self, func: mlirlite.Function*) -> int:
"""Return the count of fixed params by walking the Params list."""
if func is None: return 0
count: int = 0
param: llvmlite.Param* = llvmlite.function_get_param_head(func)
param: mlirlite.Param* = mlirlite.function_get_param_head(func)
while param is not None:
count += 1
param = llvmlite.param_get_next(param)
param = mlirlite.param_get_next(param)
return count
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
"""已知变参 C 库函数固定参数个数(-1=未知)
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
"""
if name is None:
return -1
if string.strcmp(name, "snprintf") == 0:
return 3
if string.strcmp(name, "sprintf") == 0:
return 2
if string.strcmp(name, "vsprintf") == 0:
return 2
"""Return the fixed-param count for known variadic C functions (-1 if unknown)."""
if name is None: return -1
if string.strcmp(name, "snprintf") == 0: return 3
if string.strcmp(name, "sprintf") == 0: return 2
if string.strcmp(name, "vsprintf") == 0: return 2
return -1
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*,
name: str, idx: int) -> llvmlite.LLVMType*:
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
"""
if pool is None or name is None or idx < 0:
return None
i8_ptr_kv: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_kv: llvmlite.LLVMType* = llvmlite.Int64(pool)
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*, name: str, idx: int) -> mlirlite.LLVMType*:
"""Return the type of the idx-th fixed param of a known variadic C function."""
if pool is None or name is None or idx < 0: return None
i8_ptr_kv: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
i64_kv: mlirlite.LLVMType* = mlirlite.Int64(pool)
if string.strcmp(name, "snprintf") == 0:
if idx == 0:
return i8_ptr_kv
if idx == 1:
return i64_kv
if idx == 2:
return i8_ptr_kv
if idx == 0: return i8_ptr_kv
if idx == 1: return i64_kv
if idx == 2: return i8_ptr_kv
return None
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
if idx == 0:
return i8_ptr_kv
if idx == 1:
return i8_ptr_kv
if idx == 0: return i8_ptr_kv
if idx == 1: return i8_ptr_kv
return None
return None
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
call_name: str,
head: llvmlite.Value*,
can: sizet,
arg_nodes: list[ast.AST*]* = None,
self_offset: int = 0,
trans: HT.Translator* = None) -> int:
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
返回: is_variadic (1=变长调用, 0=普通调用)
副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升)
arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断
self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0)
trans: 供 _node_type_unsigned 查询变量注解
"""
if mod is None or call_name is None or head is None:
return 0
# 查找函数对象
func: llvmlite.Function* = self.find_func_in_module(mod, call_name)
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, can: sizet, arg_nodes: list[ast.AST*]* = None, self_offset: int = 0, trans: HT.Translator* = None) -> int:
"""Promote variadic args of a callee to the C ABI; returns 1 if variadic else 0."""
if mod is None or call_name is None or head is None: return 0
# Look up the function object
func: mlirlite.Function* = self.find_func_in_module(mod, call_name)
fixed_count: int = -1
if func is not None and func.IsVarArg == 1:
fixed_count = self._get_func_fixed_param_count(func)
if func is not None and func.IsVarArg == 1: fixed_count = self._get_func_fixed_param_count(func)
else:
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):func 为 None(跨模块 stub 未注入,翻译时 found_func 恒 None);func 存在但 IsVarArg=0(该 declare 由 _ensure_method_declare 按实参类型临时创建,无 ... 标记,不代表真实签名)。否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → va_arg(i64) 高 4 字节读残留栈垃圾
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
# or IsVarArg=0 (ad hoc declare); otherwise i32 varargs aren't promoted and the Win64 8-byte slot keeps only 4 bytes.
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
if fixed_count < 0:
return 0
if fixed_count < 0: return 0
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
if can <= fixed_count:
return 1
# No varargs (arg count <= fixed count) but still report is_variadic=1
if can <= fixed_count: return 1
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
cur: llvmlite.Value* = head
prev: llvmlite.Value* = None
# Walk to the fixed_count-th node (the last fixed param)
cur: mlirlite.Value* = head
prev: mlirlite.Value* = None
idx: int = 0
while cur is not None and idx < fixed_count:
prev = cur
cur = cur.Next
idx += 1
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
# 对变长参数进行 C ABI 提升
i32_ty_promote: llvmlite.LLVMType* = llvmlite.Int32(pool)
double_ty_promote: llvmlite.LLVMType* = llvmlite.Double(pool)
# cur now points at the first vararg; apply C ABI promotion to all varargs
i32_ty_promote: mlirlite.LLVMType* = mlirlite.Int32(pool)
double_ty_promote: mlirlite.LLVMType* = mlirlite.Double(pool)
v_idx: int = fixed_count
while cur is not None:
next_node: llvmlite.Value* = cur.Next
promoted: llvmlite.Value* = cur
next_node: mlirlite.Value* = cur.Next
promoted: mlirlite.Value* = cur
# C ABI integer promotion: i8/i16 → i32
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
if arg_bits != 0 and arg_bits < 32:
# 无符号整型(uint8/CUInt16T 等)→ zext;有符号/未知 → sext。
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
# Unsigned ints (uint8/CUInt16T etc.) -> zext; signed/unknown -> sext.
# Fix: printf("%02x", buf[i]) with CUInt8T must zext to 0xF3, not sext to 0xFFFFFFF3.
is_u_va: int = 0
if arg_nodes is not None and trans is not None:
ast_idx_va: int = v_idx - self_offset
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
if an_va is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0:
is_u_va = 1
if is_u_va != 0:
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0: is_u_va = 1
if is_u_va != 0: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
else:
# is_u_va 判断失败(arg_nodes/trans 缺失或变量注解查不到)时,
# i8 按系统编程惯例默认 zext(对齐 should_zext_widen 的
# "i8 → zext(系统编程中 i8 几乎总是无符号字节)"策略),
# 否则 printf("%02x", buf[i]) 中 uint8 字节被 sext 成
# 0xFFFFFFF3,哈希摘要输出 ffffffXX 垃圾。
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8:
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
else:
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
# If is_u_va is unknown (missing arg_nodes/trans or no annotation), default i8 to zext
# (aligned with should_zext_widen); sext would print uint8 bytes as ffffffXX garbage.
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
else: promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
if promoted is not None:
promoted.Next = next_node
if prev is not None:
prev.Next = promoted
if prev is not None: prev.Next = promoted
# C ABI float promotion: float → double
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
if arg_fbits != 0 and arg_fbits < 64:
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
if promoted is not None:
promoted.Next = next_node
if prev is not None:
prev.Next = promoted
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
# L798-811 的 found_func.IsVarArg 提升)。
i64_ty_promote: llvmlite.LLVMType* = llvmlite.Int64(pool)
if prev is not None: prev.Next = promoted
# Win64 vararg slots are 8 bytes: i8/i16/i32 -> i64. The va_arg read side always reads i64
# (_translate_va_arg_sugar), so callers must fill all 8 bytes or stale stack data remains.
i64_ty_promote: mlirlite.LLVMType* = mlirlite.Int64(pool)
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
if pb_va != 0 and pb_va < 64:
promoted_va2: llvmlite.Value* = None
promoted_va2: mlirlite.Value* = None
is_u_va2: int = 0
if arg_nodes is not None and trans is not None:
ast_idx_va2: int = v_idx - self_offset
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
if an_va2 is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
is_u_va2 = 1
if is_u_va2 != 0:
promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote)
else:
promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote)
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0: is_u_va2 = 1
if is_u_va2 != 0: promoted_va2 = mlirlite.build_zext(builder, promoted, i64_ty_promote)
else: promoted_va2 = mlirlite.build_sext(builder, promoted, i64_ty_promote)
if promoted_va2 is not None:
promoted_va2.Next = next_node
if prev is not None:
prev.Next = promoted_va2
if prev is not None: prev.Next = promoted_va2
promoted = promoted_va2
prev = promoted
cur = next_node
+64 -98
View File
@@ -1,7 +1,6 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import viperlib
@@ -11,167 +10,134 @@ import lib.core.Gen.GenTypes as GenTypes
from . import CallGenBase
# ============================================================
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
# CallGenVt - CallGenMixin split (phase B): vtable slot writing
# ============================================================
novirtual class CallGenVt(CallGenBase.CallGenBase):
"""vtable 槽位写入"""
"""vtable slot writing."""
# _write_vtable_slot / _write_vtable_attr:把模块级函数指针写入对象 vtable 的方法槽位(devfs 驱动注册模式,对齐 TPC HandlesAssign.py L905-949 写时复制)——vtable 方法不是数据字段,get_attribute_ptr 查不到;对象 vtable 仍指向类默认 vtable 时先复制一份(internal 全局 + llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store i8*。多次写入互不覆盖
# _write_vtable_slot / _write_vtable_attr: write a module-level function pointer into an object vtable slot via copy-on-write (vtable slots are not data fields, so get_attribute_ptr cannot find them).
# ============================================================
def _write_vtable_slot(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
obj_ptr: llvmlite.Value*,
class_name: str,
method_index: int,
func_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
def _write_vtable_slot(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, obj_ptr: mlirlite.Value*, class_name: str, method_index: int, func_val: mlirlite.Value*, trans: HT.Translator*) -> int:
"""Emit a copy-on-write vtable slot write."""
if pool is None or builder is None or mod is None or trans is None: return 0
if obj_ptr is None or class_name is None or func_val is None: return 0
if method_index < 0 or trans._cur_func is None: return 0
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
if i8_ty is None: return 0
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None: return 0
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
if i8_ptr_ptr_ty is None: return 0
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
if method_count <= 0: return 0
# 构造 vtable 类型 { i8*, i8*, ... }
# vtable 类型的字段链表是 llvmlite.ParamNode(new_param_node / paramnode_set_next /
# Struct 形参均为 rawptr[ParamNode])——原先误注解为 Function*。
vt_first: llvmlite.ParamNode* = None
vt_prev: llvmlite.ParamNode* = None
for vi in range(method_count):
pn: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i8_ptr_ty)
# Build the vtable type { i8*, i8*, ... }; its field list uses mlirlite.ParamNode, not Function*.
vt_first: mlirlite.ParamNode* = None
vt_prev: mlirlite.ParamNode* = None
for vi in range(int(method_count)):
pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
if pn is None: continue
if vt_first is None:
vt_first = pn
if vt_prev is not None:
llvmlite.paramnode_set_next(vt_prev, pn)
if vt_first is None: vt_first = pn
if vt_prev is not None: mlirlite.paramnode_set_next(vt_prev, pn)
vt_prev = pn
vtable_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, vt_first, method_count, None)
if vtable_ty is None:
return 0
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
vtable_slot: llvmlite.Value* = llvmlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
vtable_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, vt_first, method_count, None)
if vtable_ty is None: return 0
# Object vtable slot (object header -> i8**; the vtable pointer is always the first field).
vtable_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
if vtable_slot is None: return 0
vtable_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
vtable_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
if vtable_ptr is None: return 0
# 类默认 vtable 地址(比较用)
class_vt: llvmlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
# Address of the class default vtable (for comparison).
class_vt: mlirlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
if class_vt is None: return 0
cmp_vt: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt)
cmp_vt: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, vtable_ptr, class_vt)
if cmp_vt is None: return 0
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
# devfs open/close/read/write 连续赋值)同名 label 冲突)
# Unique block names: create_block does not auto-number, so repeated vtable writes in one function would collide.
vtc3: int = trans._label_counter
trans._label_counter = vtc3 + 1
vt_bb_buf3: str = pool.alloc(64)
if vt_bb_buf3 is None: return 0
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
vt_copy_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
vt_copy_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
vt_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
vt_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
if vt_copy_bb is None or vt_skip_bb is None: return 0
llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
llvmlite.position_at_end(builder, vt_copy_bb)
mlirlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
# copy block: create internal copy global (zeroinit) + memcpy + repoint object at it.
mlirlite.position_at_end(builder, vt_copy_bb)
vtc: int = trans._label_counter
trans._label_counter = vtc + 1
vcopy_buf: str = pool.alloc(256)
if vcopy_buf is not None:
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
vcopy_gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, vcopy_buf, vtable_ty)
vcopy_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, vcopy_buf, vtable_ty)
if vcopy_gv is not None:
llvmlite.module_add_global(mod, vcopy_gv)
mlirlite.module_add_global(mod, vcopy_gv)
vcopy_gv.Linkage = None
vcopy_gv.Initializer = "zeroinitializer"
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
# 输出时加的,引用侧不自动加)
# Reference names need the @ prefix: SSAValue emits the name verbatim (unlike new_global_variable, it adds no @).
vcopy_ref_nm: str = pool.alloc(272)
if vcopy_ref_nm is None:
vcopy_ref_nm = vcopy_buf
else:
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
vcopy_ref: llvmlite.Value* = llvmlite.SSAValue(pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
if vcopy_ref_nm is None: vcopy_ref_nm = vcopy_buf
else: viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
vcopy_ref: mlirlite.Value* = mlirlite.SSAValue(pool, mlirlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
if vcopy_ref is not None:
vcopy_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
vcopy_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
if vcopy_i8 is not None:
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
# → llc "expected value token"(devfs memcpy 根因)
# ConstInt's name must be numeric text (e.g. "56"); an identifier would emit verbatim without % and break llc.
vtsz_buf: str = pool.alloc(32)
if vtsz_buf is not None:
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
vt_size: llvmlite.Value* = llvmlite.ConstInt(pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
if vt_size is not None:
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
llvmlite.build_br(builder, vt_skip_bb)
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
llvmlite.position_at_end(builder, vt_skip_bb)
vt_ptr2: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
vt_size: mlirlite.Value* = mlirlite.ConstInt(pool, mlirlite.Int64(pool), method_count * 8, vtsz_buf)
if vt_size is not None: self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
mlirlite.build_store(builder, vcopy_i8, vtable_slot)
mlirlite.build_br(builder, vt_skip_bb)
# skip block: reload vtable -> GEP the slot -> store the function pointer (i8*).
mlirlite.position_at_end(builder, vt_skip_bb)
vt_ptr2: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
if vt_ptr2 is None: return 0
vt_typed: llvmlite.Value* = llvmlite.build_bitcast(builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty))
vt_typed: mlirlite.Value* = mlirlite.build_bitcast(builder, vt_ptr2, mlirlite.Ptr(pool, vtable_ty))
if vt_typed is None: return 0
vt_slotp: llvmlite.Value* = llvmlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
vt_slotp: mlirlite.Value* = mlirlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
if vt_slotp is None: return 0
fptr_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, func_val, i8_ptr_ty)
if fptr_i8 is not None:
llvmlite.build_store(builder, fptr_i8, vt_slotp)
fptr_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, func_val, i8_ptr_ty)
if fptr_i8 is not None: mlirlite.build_store(builder, fptr_i8, vt_slotp)
return 1
def _write_vtable_attr(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
rhs_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
def _write_vtable_attr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
"""Handle obj.method = func writes that target a vtable slot."""
if node is None or builder is None or pool is None or mod is None: return 0
if rhs_val is None or trans is None: return 0
at: ast.Attribute* = ast.Attribute*(node)
if at is None or at.value is None or at.attr is None: return 0
# 解析类名(AnnotClassName;去模块前缀)
# Resolve the class name (AnnotClassName; strip the module prefix).
class_nm_v: str = None
if at.value.kind() == ast.ASTKind.Name:
nm_v: ast.Name* = ast.Name*(at.value)
if nm_v.id is not None:
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
if ve_v is not None:
class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
if ve_v is not None: class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
elif at.value.kind() == ast.ASTKind.Attribute:
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
# Cross-mixin: _resolve_attr_field_class lives in ExprGenAttr, called via self.Gen.
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
if class_nm_v is None: return 0
fd_dot_v: str = string.strrchr(class_nm_v, '.')
if fd_dot_v is not None:
class_nm_v = fd_dot_v + 1
if fd_dot_v is not None: class_nm_v = fd_dot_v + 1
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
if vm_idx_v < 0: return 0
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
obj_ptr_v: llvmlite.Value* = None
if at.value.kind() == ast.ASTKind.Name:
obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
else:
obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
# Resolve obj_ptr (same as the first half of get_attribute_ptr: Name looks up the alloca directly).
obj_ptr_v: mlirlite.Value* = None
if at.value.kind() == ast.ASTKind.Name: obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
else: obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
# X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针
# X* variable (Ptr(Ptr)): load-dereference to get the object pointer.
match obj_ptr_v.Ty:
case llvmlite.LLVMType.Ptr(inner_v):
case mlirlite.LLVMType.Ptr(inner_v):
match inner_v:
case llvmlite.LLVMType.Ptr(_):
obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v)
case mlirlite.LLVMType.Ptr(_):
obj_ptr_v = mlirlite.build_load(builder, inner_v, obj_ptr_v)
case _:
pass
case _:
pass
if obj_ptr_v is None:
return 0
if obj_ptr_v is None: return 0
return self._write_vtable_slot(pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -5
View File
@@ -1,9 +1,6 @@
import t, c
from stdint import *
from . import ClassGenBase
# ============================================================
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
# ============================================================
# ClassGenMixin - class definition / inheritance / generics / vtable / enum / union / renum.
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
"""Class-definition mixin (top level after the stage-D split; methods live in sub-mixins)."""
File diff suppressed because it is too large. Load diff
+137 -230
View File
@@ -1,77 +1,54 @@
import t, c
from stdint import *
import llvmlite
import mlirlite
import memhub
import string
import stdlib
import viperlib
import w32.win32file as w32file
import win32.win32file as win32file
import lib.core.VLogger as VLogger
# ============================================================
# DebugInfoGen - function-level DWARF metadata emitter (llvmlite text IR)
#
# Node layout mirrors TPC lib/core/LLVMCG/DebugInfo.py node-for-node:
# !DIFile -> !{ null } -> !DISubroutineType -> distinct !DICompileUnit
# -> distinct !DISubprogram (per define) -> !DILocation (per statement)
# module tail: !llvm.dbg.cu + !llvm.module.flags (Debug Info Version 3)
#
# Enabled only while --traceback is on: the Phase layer mirrors
# BuildPipeline._traceback_mode here via SetEnabled(). While disabled every
# entry point returns before touching any llvmlite DI API, so the emitted IR
# stays byte-identical to the pre-DWARF output.
#
# Scope/statement hooks are driven from the Gen layer:
# Translator.translate -> BeginModule / Finalize
# FuncGenDef.translate_function_def / ClassGenVTable._translate_method /
# FuncGenMixin (nested + lambda) -> PushFunction / PopFunction
# StmtGenMixin.translate_stmt -> SetStatement
# ============================================================
# DebugInfoGen - emits function-level DWARF metadata (mlirlite text IR), mirroring TPC LLVMCG/DebugInfo.py node-for-node.
# Active only under --traceback (SetEnabled); hooks come from the Gen layer. When disabled every entry point returns before touching DI APIs, keeping IR byte-identical.
# ---- module-scope state (reset per module by BeginModule) ----
_g_enabled: int = 0
_g_finalized: int = 0
_g_mod: llvmlite.LLVMModule* = None
_g_mod: mlirlite.LLVMModule* = None
_g_file_path: str = None
_g_abs_file_path: str = None
_g_file_id: int = -1
_g_null_id: int = -1
_g_subtype_id: int = -1
_g_cu_id: int = -1
_g_cur_sp: int = -1
_g_cur_entry_ref: str = None
_g_cur_builder: llvmlite.IRBuilder* = None
_g_cur_builder: mlirlite.IRBuilder* = None
_g_depth: int = 0
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder)
# per level. +1 so 0 means "no enclosing function" (sp ids are >= 0). The saved
# builder is what makes SetStatement reject a builder that is not the one the
# current DISubprogram was pushed on (e.g. an async state machine emitted inside
# a bracketed function): such instructions must carry no !dbg, otherwise LLVM's
# verifier rejects "!dbg attachment points at wrong subprogram for function".
DBG_STACK_MAX: t.CDefine = 256
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder) per level; +1 lets 0 mean "no enclosing function".
# The saved builder makes SetStatement reject a builder not pushed by the current DISubprogram (e.g. an async state machine in a bracketed function); such instrs must carry no !dbg or LLVM's verifier fails.
define DBG_STACK_MAX = 256
_g_stk_sp: sizet* = None
_g_stk_ref: str = None
_g_stk_bld: str = None
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
DBG_LOC_SLOTS: t.CDefine = 8192
DBG_LOC_PROBE: t.CDefine = 8
DBG_LOC_KEY_STRIDE: t.CDefine = 3
define DBG_LOC_SLOTS = 8192
define DBG_LOC_PROBE = 8
define DBG_LOC_KEY_STRIDE = 3
_g_loc_key: sizet* = None
_g_loc_ref: str = None
_g_loc_used: bytes = None
# ============================================================
# SetEnabled / IsEnabled - mirror the --traceback switch
# ============================================================
# SetEnabled / IsEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
def SetEnabled(flag: bool) -> int:
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
global _g_enabled
if flag:
_g_enabled = 1
else:
_g_enabled = 0
if flag: _g_enabled = 1
else: _g_enabled = 0
# Forward the same switch to mlirlite's debug call-site recorder (under --traceback we emit a <sha1>.vpcall side file for WriteStackMap+objdump).
mlirlite.DebugSetEnabled(flag)
return 0
@@ -79,13 +56,10 @@ def IsEnabled() -> int:
return _g_enabled
# ============================================================
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers
# ============================================================
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers.
def _EnsureStorage() -> int:
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
if _g_stk_sp is not None:
return 0
if _g_stk_sp is not None: return 0
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
@@ -93,7 +67,7 @@ def _EnsureStorage() -> int:
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
VLogger.error("--traceback: DWARF 元数据缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DWARF metadata buffer, aborting compilation", "DWARF")
return 1
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
@@ -113,25 +87,23 @@ def _EnsureStorage() -> int:
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
"""Allocate a persistent "!N" reference string for a DI node id."""
buf: str = pool.alloc(24)
if buf is None:
return None
if buf is None: return None
viperlib.snprintf(buf, 24, "!%d", node_id)
return buf
# ============================================================
# BeginModule - start of one module translation
# ============================================================
def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
# BeginModule - start of one module translation.
def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
"""Reset per-module DI state. Call once at the start of each module."""
global _g_mod, _g_file_path, _g_finalized, _g_file_id, _g_null_id
global _g_mod, _g_file_path, _g_abs_file_path, _g_finalized, _g_file_id, _g_null_id
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _EnsureStorage() != 0:
return 1
if _g_enabled == 0: return 0
if _EnsureStorage() != 0: return 1
_g_mod = mod
_g_file_path = file_path
_g_abs_file_path = None
# Module-boundary fallback: clear any un-Pop'd debug function context (mid-translation failure) so the previous module's caller is not attributed here.
mlirlite.DebugResetFunc()
_g_finalized = 0
_g_file_id = -1
_g_null_id = -1
@@ -141,62 +113,46 @@ def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
_g_cur_entry_ref = None
_g_cur_builder = None
_g_depth = 0
if _g_loc_used is not None:
string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
if _g_loc_used is not None: string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
return 0
# ============================================================
# _EnsureModuleNodes - DIFile / !{ null } / DISubroutineType / DICompileUnit
# (created lazily at the first define, once per module)
# ============================================================
# _EnsureModuleNodes - lazily create DIFile / !{ null } / DISubroutineType / DICompileUnit at the first define (once per module).
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id
if _g_cu_id >= 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None:
return 1
# DIFile: **绝对**路径(正斜杠),filename 保留整条路径。
# 为什么必须绝对:本工程给 CurrentFile 的是**项目相对**路径(如
# "./Test/LLvmLiteTest/App/main.vp"),LLVM 会把该目录写成 CU 的 DW_AT_comp_dir,
# 而主文件条目用 `dir_index: 0` 指向 comp_dir;stackmap 写入端只解析
# `llvm-dwarfdump --debug-line`(其中**没有** comp_dir)⇒ 行记录只能退化成裸文件名
# "main.vp":运行期既显示不全,也无法按路径重读源码行(实证:曾整段栈都打印
# `File "main.vp", line N` 而无源码行)。
# 绝对化后,无论 LLVM 保留整条 name(写入端 `_tb_is_abs_path` 命中 ⇒ 原样采用)
# 还是把它拆成 dir+basename(dir_index 指向同一个绝对目录 ⇒ 拼回绝对路径),
# 结果都是绝对路径 —— 故本改动不需要动写入端。
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id, _g_abs_file_path
if _g_cu_id >= 0: return 0
mod: mlirlite.LLVMModule* = _g_mod
if mod is None: return 1
# DIFile: absolute path (forward slashes), filename keeps the whole path.
# Must be absolute: relative paths make LLVM's DWARF line records degrade to a bare "main.vp" that runtime cannot reload; absolutizing works whether LLVM keeps the full name or splits dir+basename.
src: str = _g_file_path
if src is None:
src = ""
if src is None: src = ""
slen: sizet = string.strlen(src)
cap_n: sizet = slen + 4112
nbuf: str = pool.alloc(cap_n)
if nbuf is None:
VLogger.error("--traceback: DIFile 路径缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DIFile path buffer, aborting compilation", "DWARF")
return 1
if slen > 0:
string.strncpy(nbuf, src, slen)
if slen > 0: string.strncpy(nbuf, src, slen)
nbuf[slen] = '\0'
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
# 相对路径 → 前置当前工作目录。用 w32file 低层调用(同 Repl.vp),
# 不经 os._mbuddy:编译器进程未必初始化过那个内存管理器。
# Relative path -> prefix CWD via a win32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
CwdBuf: bytes = pool.alloc(4112)
if CwdBuf is not None:
CwdBuf[0] = '\0'
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
nc: ULONG = win32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
if nc > 0:
# 防御:缓冲区不足时 GetCurrentDirectoryA 的写入内容未定义,强制补 NUL
# Defensive: when the buffer is too small GetCurrentDirectoryA writes undefined content; force a NUL.
CwdBuf[4110] = '\0'
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
# 归一所有反斜杠(CWD 段同样可能含 '\')
# Normalize all backslashes (the CWD segment may contain '\' too).
flen: sizet = string.strlen(nbuf)
ni: sizet = 0
for ni in range(flen):
if nbuf[ni] == '\\':
nbuf[ni] = '/'
# filename 取整条路径;directory 另存其目录前缀(两段各自 NUL 结尾,互不污染)
for ni in range(int(flen)):
if nbuf[ni] == '\\': nbuf[ni] = '/'
# Save the absolute path for mlirlite's debug call-site recorder (.vpcall file paths must match DIFile: absolute + forward slash).
_g_abs_file_path = nbuf
# filename = whole path; directory = its prefix (both NUL-terminated, non-overlapping).
fname: str = nbuf
directory: str = ""
slash: str = string.strrchr(nbuf, '/')
@@ -210,87 +166,74 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
dbuf: str = pool.alloc(dsize)
if dbuf is None:
VLogger.error("--traceback: DIFile 节点缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DIFile node buffer, aborting compilation", "DWARF")
return 1
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
fid: int = llvmlite.module_add_di_node(mod, pool, dbuf)
fid: int = mlirlite.module_add_di_node(mod, pool, dbuf)
if fid < 0:
VLogger.error("--traceback: DIFile 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DIFile metadata node, aborting compilation", "DWARF")
return 1
_g_file_id = fid
# types: !{ null } placeholder list, then DISubroutineType referencing it
nid: int = llvmlite.module_add_di_node(mod, pool, "!{ null }")
nid: int = mlirlite.module_add_di_node(mod, pool, "!{ null }")
if nid < 0:
VLogger.error("--traceback: DISubroutineType 类型表分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubroutineType type table, aborting compilation", "DWARF")
return 1
_g_null_id = nid
tbuf: str = pool.alloc(64)
if tbuf is None:
VLogger.error("--traceback: DISubroutineType 缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubroutineType buffer, aborting compilation", "DWARF")
return 1
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
tid: int = llvmlite.module_add_di_node(mod, pool, tbuf)
tid: int = mlirlite.module_add_di_node(mod, pool, tbuf)
if tid < 0:
VLogger.error("--traceback: DISubroutineType 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubroutineType metadata node, aborting compilation", "DWARF")
return 1
_g_subtype_id = tid
cbuf: str = pool.alloc(256)
if cbuf is None:
VLogger.error("--traceback: DICompileUnit 缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DICompileUnit buffer, aborting compilation", "DWARF")
return 1
viperlib.snprintf(cbuf, 256,
"distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)",
_g_file_id)
cid: int = llvmlite.module_add_di_node(mod, pool, cbuf)
viperlib.snprintf(cbuf, 256, "distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)", _g_file_id)
cid: int = mlirlite.module_add_di_node(mod, pool, cbuf)
if cid < 0:
VLogger.error("--traceback: DICompileUnit 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DICompileUnit metadata node, aborting compilation", "DWARF")
return 1
_g_cu_id = cid
return 0
# ============================================================
# _LocRefAt - read a cached DILocation ref string pointer
# ============================================================
# _LocRefAt - read a cached DILocation ref string pointer.
def _LocRefAt(slot: int) -> str:
slotp: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot) * 8)
slotp: str* = (str*)(uint64(_g_loc_ref) + sizet(slot) * 8)
r: str = slotp[0]
return r
# ============================================================
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once
# ============================================================
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once.
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
if line <= 0:
return _g_cur_entry_ref
if line <= 0: return _g_cur_entry_ref
c: int = col
if c <= 0:
c = 1
if _g_loc_used is None or _g_mod is None:
return None
if c <= 0: c = 1
if _g_loc_used is None or _g_mod is None: return None
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
pi: int = 0
while pi < DBG_LOC_PROBE:
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
if _g_loc_used[slot] == 0:
break
if _g_loc_used[slot] == 0: break
kbase: int = slot * DBG_LOC_KEY_STRIDE
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line \
and int(_g_loc_key[kbase + 2]) == c:
return _LocRefAt(slot)
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line and int(_g_loc_key[kbase + 2]) == c: return _LocRefAt(slot)
pi += 1
lbuf: str = pool.alloc(96)
if lbuf is None:
return None
if lbuf is None: return None
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
lid: int = llvmlite.module_add_di_node(_g_mod, pool, lbuf)
lid: int = mlirlite.module_add_di_node(_g_mod, pool, lbuf)
if lid < 0:
VLogger.error("--traceback: DILocation 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DILocation metadata node, aborting compilation", "DWARF")
return None
lref: str = _RefOf(pool, lid)
if lref is None:
VLogger.error("--traceback: DILocation 引用缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DILocation reference buffer, aborting compilation", "DWARF")
return None
if pi < DBG_LOC_PROBE:
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
@@ -298,174 +241,138 @@ def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str
_g_loc_key[kbase2] = sizet(sp)
_g_loc_key[kbase2 + 1] = sizet(line)
_g_loc_key[kbase2 + 2] = sizet(c)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot2) * 8)
rslot: str* = (str*)(uint64(_g_loc_ref) + sizet(slot2) * 8)
rslot[0] = lref
_g_loc_used[slot2] = 1
return lref
# ============================================================
# PushFunction - create the DISubprogram for one define
# ============================================================
def PushFunction(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
func: llvmlite.Function*, line: int, col: int) -> int:
"""Attach a DISubprogram to one define and set the builder's entry DILocation.
Returns 1 when pushed (caller must call PopFunction), 0 when DI is disabled
or not applicable. Every instruction emitted afterwards on `builder` carries
", !dbg !N" until the next SetStatement.
"""
# PushFunction - create the DISubprogram for one define.
def PushFunction(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, func: mlirlite.Function*, line: int, col: int) -> int:
"""Attach a DISubprogram to one define and set the builder's entry DILocation."""
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if pool is None or builder is None or func is None or _g_mod is None:
return 0
if _g_enabled == 0: return 0
if pool is None or builder is None or func is None or _g_mod is None: return 0
if _g_depth >= DBG_STACK_MAX:
VLogger.error("--traceback: DWARF 函数嵌套深度超上限,终止编译", "DWARF")
VLogger.error("--traceback: DWARF function nesting depth exceeds the limit, aborting compilation", "DWARF")
return 0
if _EnsureModuleNodes(pool) != 0:
return 0
name: str = llvmlite.function_get_name(func)
if _EnsureModuleNodes(pool) != 0: return 0
name: str = mlirlite.function_get_name(func)
if name is None:
VLogger.error("--traceback: 函数名为空,无法发射 DISubprogram,终止编译", "DWARF")
VLogger.error("--traceback: function name is empty, cannot emit DISubprogram, aborting compilation", "DWARF")
return 0
dline: int = line
if dline <= 0:
dline = 1
if dline <= 0: dline = 1
dcol: int = col
if dcol <= 0:
dcol = 1
if dcol <= 0: dcol = 1
ssize: sizet = string.strlen(name) + 320
spbuf: str = pool.alloc(ssize)
if spbuf is None:
VLogger.error("--traceback: DISubprogram 缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubprogram buffer, aborting compilation", "DWARF")
return 0
viperlib.snprintf(spbuf, ssize,
"distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)",
name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
sp_id: int = llvmlite.module_add_di_node(_g_mod, pool, spbuf)
viperlib.snprintf(spbuf, ssize, "distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)", name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
sp_id: int = mlirlite.module_add_di_node(_g_mod, pool, spbuf)
if sp_id < 0:
VLogger.error("--traceback: DISubprogram 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubprogram metadata node, aborting compilation", "DWARF")
return 0
sp_ref: str = _RefOf(pool, sp_id)
if sp_ref is None:
VLogger.error("--traceback: DISubprogram 引用缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DISubprogram reference buffer, aborting compilation", "DWARF")
return 0
llvmlite.function_set_dbg(func, sp_ref)
mlirlite.function_set_dbg(func, sp_ref)
# save the enclosing function's DI context (LIFO), then switch to this one
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
rslot[0] = _g_cur_entry_ref
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
bslot[0] = _g_cur_builder
_g_depth += 1
_g_cur_sp = sp_id
_g_cur_builder = builder
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
if entry_ref is None:
VLogger.error("--traceback: 入口 DILocation 生成失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to generate entry DILocation, aborting compilation", "DWARF")
return 0
_g_cur_entry_ref = entry_ref
llvmlite.builder_set_debug_location(builder, entry_ref)
mlirlite.builder_set_debug_location(builder, entry_ref)
# Enter this define: tell mlirlite's call-site recorder the current symbol/file/def-line and write an F record (for the .vpcall file + WriteStackMap entry fallback line).
mlirlite.DebugNoteFunc(name, _g_abs_file_path, dline)
return 1
# ============================================================
# PopFunction - restore the enclosing function's DI context
# ============================================================
def PopFunction(builder: llvmlite.IRBuilder*) -> int:
# PopFunction - restore the enclosing function's DI context.
def PopFunction(builder: mlirlite.IRBuilder*) -> int:
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _g_depth <= 0:
return 0
if _g_enabled == 0: return 0
if _g_depth <= 0: return 0
_g_depth -= 1
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
_g_cur_entry_ref = rslot[0]
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
_g_cur_builder = (llvmlite.IRBuilder*)(bslot[0])
if builder is not None:
llvmlite.builder_clear_debug_location(builder)
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
_g_cur_builder = (mlirlite.IRBuilder*)(bslot[0])
if builder is not None: mlirlite.builder_clear_debug_location(builder)
# Leave this define: restore mlirlite's outer function context (for nested defines).
mlirlite.DebugClearFunc()
return 0
# ============================================================
# SetStatement - point the current builder at a statement DILocation
# ============================================================
def SetStatement(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
line: int, col: int) -> int:
"""Update the builder's DILocation to the current statement (line precision).
No-op outside a DI-attached define (module-level code / synthetic defines),
so instructions outside a function body never reference a function's scope.
"""
if _g_enabled == 0 or pool is None or builder is None:
return 0
if _g_cur_sp < 0:
return 0
# SetStatement - point the current builder at a statement DILocation.
def SetStatement(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, line: int, col: int) -> int:
"""Update the builder's DILocation to the current statement (line precision)."""
if _g_enabled == 0 or pool is None or builder is None: return 0
# Record the current statement line: C records from build_call in this statement use it as the call-site line.
mlirlite.DebugNoteLine(line)
if _g_cur_sp < 0: return 0
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
if ref is None:
return 0
llvmlite.builder_set_debug_location(builder, ref)
if ref is None: return 0
mlirlite.builder_set_debug_location(builder, ref)
return 1
# ============================================================
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
# ============================================================
def _ModuleHasDefine(mod: llvmlite.LLVMModule*) -> int:
if mod is None:
return 0
cur: llvmlite.Function* = mod.FuncHead
def _ModuleHasDefine(mod: mlirlite.LLVMModule*) -> int:
if mod is None: return 0
cur: mlirlite.Function* = mod.FuncHead
steps: int = 0
while cur is not None:
steps += 1
if steps > 100000:
break
if llvmlite.function_is_declared(cur) == 0:
return 1
cur = llvmlite.function_get_next(cur)
if steps > 100000: break
if mlirlite.function_is_declared(cur) == 0: return 1
cur = mlirlite.function_get_next(cur)
return 0
# ============================================================
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir
# ============================================================
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir.
def Finalize(pool: memhub.MemBuddy*) -> int:
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
global _g_finalized
if _g_enabled == 0:
return 0
if _g_finalized != 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None or pool is None:
return 0
if _g_enabled == 0: return 0
if _g_finalized != 0: return 0
mod: mlirlite.LLVMModule* = _g_mod
if mod is None or pool is None: return 0
_g_finalized = 1
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
# define but carries no DI at all loses line info for its frames.
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0:
VLogger.warning("--traceback: 本模块发射了 define 但 IR 未携带 DWARF (!llvm.dbg.cu)——该模块帧在 Ctrl+C 栈里将只显示地址", "DWARF")
if _g_cu_id < 0:
return 0
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0: VLogger.warning("--traceback: this module emits a define but its IR carries no DWARF (!llvm.dbg.cu); its frames will show only addresses in the Ctrl+C stack", "DWARF")
if _g_cu_id < 0: return 0
cu_ref: str = _RefOf(pool, _g_cu_id)
if cu_ref is None:
VLogger.error("--traceback: DICompileUnit 引用缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate DICompileUnit reference buffer, aborting compilation", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
mlirlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
fbuf: str = pool.alloc(64)
if fbuf is None:
VLogger.error("--traceback: 模块级调试标志缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate module-level debug flags buffer, aborting compilation", "DWARF")
return 1
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
flags_id: int = llvmlite.module_add_di_node(mod, pool, fbuf)
flags_id: int = mlirlite.module_add_di_node(mod, pool, fbuf)
if flags_id < 0:
VLogger.error("--traceback: llvm.module.flags 元数据节点分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate llvm.module.flags metadata node, aborting compilation", "DWARF")
return 1
fl_ref: str = _RefOf(pool, flags_id)
if fl_ref is None:
VLogger.error("--traceback: llvm.module.flags 引用缓冲分配失败,终止编译", "DWARF")
VLogger.error("--traceback: failed to allocate llvm.module.flags reference buffer, aborting compilation", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
mlirlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
return 0
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+108 -186
View File
@@ -1,7 +1,6 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import stdlib
@@ -15,42 +14,31 @@ import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# ============================================================
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,存疑
# ============================================================
# docstring registry capacity constants (kept in sync with ExprGenMixin)
define DOC_MAX = 512
define DOC_KEY_SZ = 192
define DOC_VAL_SZ = 4096
# ============================================================
# ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记
# ============================================================
# ExprGenCls - split of ExprGenMixin (stage D): class-name inference / docstring registry
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
"""类名推断/docstring 登记"""
"""Expression class inference and docstring registry."""
# ============================================================
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
#
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
# ============================================================
# docstring extract/register/lookup (__doc__ support): pre-registration scans
# ASTs into a global "{module_sha1}.{symbol}" table; lookups inline the string.
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
"""Extract a docstring from a body when body[0] is an Expr(Constant str)."""
if body is None or len(body) == 0: return None
first: ast.AST* = body.get(0)
if first is None or first.kind() != ast.ASTKind.Expr: return None
ex: rawptr[ast.Expr] = rawptr[ast.Expr](first)
ex: ast.Expr* = ast.Expr*(first)
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: return None
cn: ast.Constant* = ast.Constant*(ex.value)
if cn is None or cn.const_kind != ast.CONST_STR: return None
return cn.str_val
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
"""Register a docstring under key "{module_sha1}.{symbol}", deduplicated."""
if module_sha1 is None or symbol is None or doc is None: return
if string.strlen(doc) == 0: return
if self.DocKeys is None:
@@ -65,7 +53,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None: return
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
for i in range(self.DocCount):
for i in range(int(self.DocCount)):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
return
@@ -74,26 +62,26 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
self.DocCount += 1
stdlib.free(key_buf)
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
"""Look up a docstring by "{module_sha1}.{symbol}", or None on miss."""
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
ml: sizet = string.strlen(module_sha1)
sl: sizet = string.strlen(symbol)
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None: return None
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
for i in range(self.DocCount):
for i in range(int(self.DocCount)):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
return self.DocValues + i * DOC_VAL_SZ
stdlib.free(key_buf)
return None
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
"""Scan a module AST and register docstrings of its functions, classes and methods."""
if tree is None or module_sha1 is None: return
ch: list[ast.AST*]* = tree.children
if ch is None: return
cn: sizet = len(ch)
for ci in range(cn):
for ci in range(int(cn)):
child: ast.AST* = ch.get(ci)
if child is None: continue
kd: int = child.kind()
@@ -102,20 +90,18 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
if fd is not None and fd.name is not None:
fd_body: list[ast.AST*]* = fd.children
fdoc: str = self._extract_docstring_from_body(fd_body)
if fdoc is not None:
self.register_docstring(module_sha1, fd.name, fdoc)
if fdoc is not None: self.register_docstring(module_sha1, fd.name, fdoc)
elif kd == ast.ASTKind.ClassDef:
cd: ast.ClassDef* = ast.ClassDef*(child)
if cd is None or cd.name is None: continue
cd_body0: list[ast.AST*]* = cd.children
cdoc: str = self._extract_docstring_from_body(cd_body0)
if cdoc is not None:
self.register_docstring(module_sha1, cd.name, cdoc)
# 类方法 docstring(key="{ClassName}.{method}")
if cdoc is not None: self.register_docstring(module_sha1, cd.name, cdoc)
# class method docstring (key="{ClassName}.{method}")
cd_body: list[ast.AST*]* = cd.children
if cd_body is not None:
cbn: sizet = len(cd_body)
for mi in range(cbn):
for mi in range(int(cbn)):
m: ast.AST* = cd_body.get(mi)
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
@@ -129,53 +115,39 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
self.register_docstring(module_sha1, sym_buf, mdoc)
stdlib.free(sym_buf)
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str,
out_mod: str, out_sym: str) -> int:
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
"""
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str, out_mod: str, out_sym: str) -> int:
"""Resolve a Name symbol's docstring owner into out_mod/out_sym; return 1 on success."""
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
if trans.ModuleSha1 is None: return 0
# 1. 本地变量(函数变量/全局变量)
# 1. local variable (function/global)
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
# 2. struct class (entry.ModuleSha1 distinguishes cross-module pre-registration)
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
if se is not None:
if se.ModuleSha1 is not None:
string.strcpy(out_mod, se.ModuleSha1)
else:
string.strcpy(out_mod, trans.ModuleSha1)
if se.ModuleSha1 is not None: string.strcpy(out_mod, se.ModuleSha1)
else: string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
# 跨 Mixin self 调用生成不存在符号)
# 3. module name (rare); use StubMerger module-level functions to avoid a
# cross-Mixin self call that would emit a nonexistent symbol.
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
if mod_sha1 is None:
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
if mod_sha1 is not None:
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, name_id)
return 1
# 4. 兜底当前模块
# 4. fall back to the current module
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
def _resolve_doc_symbol_attr(self, trans: HT.Translator*,
at: ast.Attribute*,
out_mod: str, out_sym: str) -> int:
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
支持形态:remote.remote_func / remote.RemoteClass /
remote.RemoteClass.remote_method / LocalClass.local_method 等。
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
"""
def _resolve_doc_symbol_attr(self, trans: HT.Translator*, at: ast.Attribute*, out_mod: str, out_sym: str) -> int:
"""Resolve a nested Attribute chain's docstring owner into out_mod/out_sym."""
if trans is None or at is None or out_mod is None or out_sym is None: return 0
if trans.ModuleSha1 is None: return 0
# 收集 Attribute 链(最外层 attr 在前)
# collect the Attribute chain (outermost attr first)
DOC_CHAIN_MAX: int = 8
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
if items is None: return 0
@@ -187,7 +159,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
string.strcpy(items + n_items * 128, cat.attr)
n_items += 1
cur = cat.value
# 最左应为 Name
# leftmost must be a Name
head_id: str = None
if cur is not None and cur.kind() == ast.ASTKind.Name:
hnm: ast.Name* = ast.Name*(cur)
@@ -195,85 +167,63 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
if head_id is None:
stdlib.free(items)
return 0
# head 判定:模块名 or 变量/类
# head decision: module name or variable/class
is_mod: int = 0
mod_sha1: str = None
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id)
if mod_sha1 is None:
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
if mod_sha1 is not None:
is_mod = 1
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
if mod_sha1 is not None: is_mod = 1
if is_mod != 0:
# 模块限定:symbol = items 反转(不含 head_id)
# module-qualified: symbol = items reversed (excluding head_id)
if n_items == 0:
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, head_id)
stdlib.free(items)
return 1
total: sizet = 0
for j in range(n_items):
total += string.strlen(items + j * 128) + 1
for j in range(int(n_items)): total += string.strlen(items + j * 128) + 1
sym_buf: bytes = stdlib.malloc(total + 1)
if sym_buf is None:
stdlib.free(items)
return 0
sym_buf[0] = '\0'
for j in range(n_items):
if j > 0:
string.strcat(sym_buf, ".")
for j in range(int(n_items)):
if j > 0: string.strcat(sym_buf, ".")
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, sym_buf)
stdlib.free(sym_buf)
stdlib.free(items)
return 1
# 变量/类:symbol = head_id + '.' + items 反转
# variable/class: symbol = head_id + '.' + items reversed
total2: sizet = string.strlen(head_id)
for j in range(n_items):
total2 += string.strlen(items + j * 128) + 1
for j in range(int(n_items)): total2 += string.strlen(items + j * 128) + 1
sym2: bytes = stdlib.malloc(total2 + 1)
if sym2 is None:
stdlib.free(items)
return 0
sym2[0] = '\0'
string.strcat(sym2, head_id)
for j in range(n_items):
for j in range(int(n_items)):
string.strcat(sym2, ".")
string.strcat(sym2, items + (n_items - 1 - j) * 128)
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
if se2 is not None and se2.ModuleSha1 is not None:
string.strcpy(out_mod, se2.ModuleSha1)
else:
string.strcpy(out_mod, trans.ModuleSha1)
if se2 is not None and se2.ModuleSha1 is not None: string.strcpy(out_mod, se2.ModuleSha1)
else: string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, sym2)
stdlib.free(sym2)
stdlib.free(items)
return 1
# ============================================================
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
# ============================================================
# P3 migrated methods (from HandlesExprOps.vp: operator overload + binary ops)
# ============================================================
# infer_expr_class - 从表达式推断结构体类名(共享推断)
#
# 无注解赋值(HandlesAssign)与方法调用分派(CallGenMixin)共用:
# 联合类型 X|t.CPtr 简化为 Ptr(i8),不含类名,需按表达式结构推断:
# Call → 类实例化(func 为已注册结构体名);func 为 Attribute
# 时是方法调用,经 vpi 返回注解缓存解析返回类名
# Name → 源变量的 AnnotClassName
# BinOp/UnaryOp → lhs 类名 + 存在对应 dunder 方法(返回同类对象)
# Attribute → 字段注解类名(_resolve_attr_field_class)
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
# ============================================================
def infer_expr_class(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator*) -> str:
"""从表达式推断结构体类名,无法推断返回 None"""
# infer_expr_class - infer the struct class name from an expression (shared by
# unannotated assignment and method dispatch); returns None if undecidable.
def infer_expr_class(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator*) -> str:
"""Infer the struct class name from an expression, or None if undecidable."""
if node is None or trans is None: return None
k: int = node.kind()
# Call: 类实例化 / 方法调用
# Call: class instantiation / method call
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node)
if cl is None or cl.func is None: return None
@@ -283,48 +233,39 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
if self.Gen.StructGen.find_struct(cand) is not None: return cand
return None
if cl.func.kind() == ast.ASTKind.Attribute:
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
# Method call a.cross(b): resolve the return class from the vpi return
# annotation cache (may differ from the receiver class).
ca: ast.Attribute* = ast.Attribute*(cl.func)
if ca is None or ca.attr is None or ca.value is None: return None
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
# module-level call (Module.func()): receiver is an imported module name
if ca.value.kind() == ast.ASTKind.Name:
mod_nm2: str = (ast.Name*(ca.value)).id
if mod_nm2 is not None and trans._from_imports is not None:
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(
pool, trans._from_imports, mod_nm2)
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, mod_nm2)
if mod_sha1_2 is not None:
temp_dir2: str = None
if self.Gen.CallGenBase.TempDir is not None:
temp_dir2 = self.Gen.CallGenBase.TempDir
if temp_dir2 is not None:
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(
mod_sha1_2, None, ca.attr)
if self.Gen.CallGenBase.TempDir is not None: temp_dir2 = self.Gen.CallGenBase.TempDir
if temp_dir2 is not None: VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(mod_sha1_2, None, ca.attr)
if annot_ret2 is not None:
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
if ret_cls2 is not None:
return ret_cls2
if ret_cls2 is not None: return ret_cls2
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
if obj_cls is None: return None
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
if cls_sha1 is None and trans.ModuleSha1 is not None:
cls_sha1 = trans.ModuleSha1
if cls_sha1 is None and trans.ModuleSha1 is not None: cls_sha1 = trans.ModuleSha1
ret_cls: str = None
if cls_sha1 is not None:
temp_dir: str = None
# 跨子对象访问经 self.Gen 取 CallGenBase.TempDir(双向引用)
if self.Gen.CallGenBase.TempDir is not None:
temp_dir = self.Gen.CallGenBase.TempDir
if temp_dir is not None:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
# cross sub-object access via self.Gen for CallGenBase.TempDir
if self.Gen.CallGenBase.TempDir is not None: temp_dir = self.Gen.CallGenBase.TempDir
if temp_dir is not None: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
if annot_ret is not None:
ret_cls = self._extract_annot_class_name(pool, annot_ret)
if annot_ret is not None: ret_cls = self._extract_annot_class_name(pool, annot_ret)
return ret_cls
return None
# Name: 源变量注解
# Name: source variable annotation
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm is None or nm.id is None: return None
@@ -332,16 +273,12 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
if ve is None: return None
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
if acn_n is None: return None
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
# get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名
# (list[str] 含 '[')不截断(方括号内可能有点号)。
# Strip the module prefix (json.JsonValue -> JsonValue): the lookups use
# bare names; generic names containing '[' are not truncated.
acn_dot_n: str = string.strrchr(acn_n, 46)
if acn_dot_n is not None and string.strchr(acn_n, 91) is None:
acn_n = acn_dot_n + 1
if acn_dot_n is not None and string.strchr(acn_n, 91) is None: acn_n = acn_dot_n + 1
return acn_n
# BinOp/UnaryOp: 运算符重载返回同类对象
# BinOp/UnaryOp: operator overload returns the same class object
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if bop is None or bop.left is None: return None
@@ -358,25 +295,19 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
return opr_cls
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
if k == ast.ASTKind.Attribute:
return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
# Attribute: field annotation class name (recursive chain, e.g. p = r.origin)
if k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
return None
# ============================================================
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder/方法
#
# 与 try_operator_overload 的查找一致:当前模块内定义或跨模块类有
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
# 避免把原生运算误标为类类型。
# ============================================================
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, class_name: str, dunder: str) -> int:
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
# _cls_has_dunder - whether a class (or its MRO ancestors) has the dunder/method;
# defined in this module or declared in a cross-module stub SHA1.
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, class_name: str, dunder: str) -> int:
"""Return 1 if the class or its MRO ancestors define the dunder method."""
if class_name is None or dunder is None or mod is None: return 0
lookup_name: str = pool.alloc(128)
if lookup_name is None: return 0
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
# 继承链祖先查找
# walk MRO ancestors
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
@@ -385,22 +316,15 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
anc_lookup: str = pool.alloc(128)
if anc_lookup is not None:
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None:
return 1
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None: return 1
mi += 1
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
# cross-module: a class with a SHA1 (method declared in a stub) counts as existing
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
return 0
# ============================================================
# _extract_annot_class_name - 从返回注解文本提取结构体类名
#
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
# 多候选时取首个命中段。
# ============================================================
# _extract_annot_class_name - extract a registered struct class name from a vpi
# return annotation, splitting on '|' and taking the first hit.
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
"""Extract a registered struct class name from return annotation text, or None."""
if pool is None or annot is None: return None
alen: sizet = string.strlen(annot)
if alen == 0: return None
@@ -408,54 +332,52 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
if seg is None: return None
start: sizet = 0
i: sizet
for i in range(alen):
for i in range(int(alen)):
if annot[i] == '|':
s: sizet = start
e: sizet = i
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
e -= 1
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
seglen: sizet = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None:
return seg
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中
# 时去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
# Unwrap the CPtr[T] pointer marker: the class name is inside.
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
seg = seg + 5
seglen -= 6
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None: return seg
# On a find_struct miss, retry with the last '.'-prefix (module
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
seg_dot2: str = string.strrchr(seg, 46)
if seg_dot2 is not None and seg_dot2 != seg:
seg_bare2: str = seg_dot2 + 1
if self.Gen.StructGen.find_struct(seg_bare2) is not None:
return seg_bare2
if self.Gen.StructGen.find_struct(seg_bare2) is not None: return seg_bare2
start = i + 1
# 最后一段(无 '|' 后缀)
# last segment (no '|' suffix)
s: sizet = start
e: sizet = alen
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
e -= 1
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
seglen: sizet = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None:
return seg
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中时
# 去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
# Unwrap the CPtr[T] pointer marker: the class name is inside.
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
seg = seg + 5
seglen -= 6
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None: return seg
# On a find_struct miss, retry with the last '.'-prefix (module
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
seg_dot3: str = string.strrchr(seg, 46)
if seg_dot3 is not None and seg_dot3 != seg:
seg_bare3: str = seg_dot3 + 1
if self.Gen.StructGen.find_struct(seg_bare3) is not None:
return seg_bare3
if self.Gen.StructGen.find_struct(seg_bare3) is not None: return seg_bare3
return None
+175 -230
View File
@@ -1,7 +1,6 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import stdio
@@ -13,286 +12,239 @@ import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
# ============================================================
# ExprGenConst - split from ExprGenMixin (stage D): constants / global strings / list literals
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
"""常量/全局字符串/list 字面量翻译"""
"""Translation of constants, global strings and list literals."""
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
str_val: str,
trans: HT.Translator*) -> llvmlite.Value*:
"""创建全局字符串常量并返回 i8* bitcast"""
# Create global string constant
def create_global_string(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, str_val: str, trans: HT.Translator*) -> mlirlite.Value*:
"""Create a global string constant and return its i8* bitcast."""
escaped: str = self.escape_llvm_string(pool, str_val)
if escaped is None: return None
slen: sizet = string.strlen(str_val)
count: int = slen + 1
count: int = int(slen + 1)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, count)
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
arr_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, count)
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
# Use the module-level counter (trans._label_counter) to avoid cross-function naming conflicts
str_idx: int = trans._label_counter
trans._label_counter += 1
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
# Prefix string names with the SHA1 (same rule as function export) to avoid cross-module collisions
gv_name: str = pool.alloc(48)
if gv_name is None:
return None
if trans.ModuleSha1 is not None:
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
else:
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
if gv_name is None: return None
if trans.ModuleSha1 is not None: viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
else: viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None:
return None
llvmlite.module_add_global(mod, gv)
gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None: return None
mlirlite.module_add_global(mod, gv)
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
llvmlite.global_set_unnamed_addr(gv, 1)
llvmlite.global_set_constant(gv, 1)
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
# Drop private linkage, same as funcs: declare in stub, define in text
mlirlite.global_set_unnamed_addr(gv, 1)
mlirlite.global_set_constant(gv, 1)
# LLVM 22+ forbids initializers on external-linkage globals, so set internal
gv.Linkage = "internal"
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, arr_ty)
arr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, arr_ty)
gv_ref_name: str = pool.alloc(64)
if gv_ref_name is None:
return None
if gv_ref_name is None: return None
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
gv_ref: llvmlite.Value* = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
gv_ref: mlirlite.Value* = mlirlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
bc: llvmlite.Value* = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
bc: mlirlite.Value* = mlirlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
return bc
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None) -> llvmlite.Value*:
"""翻译常量(int/str/bool)"""
# _explicit_width_int_const - 显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`):
# 按声明宽度出常量;值必须落在该宽度的可表示范围(有符号 [-2^(w-1), 2^(w-1)-1];无符号 [0, 2^w-1]),
# 越界编译期 fail-fast(不允许隐式截断)。w==64 时 int64 全域都合法。
def _explicit_width_int_const(self, pool: memhub.MemBuddy*, node: ast.AST*, w: int, is_unsigned: int, iv: int64) -> mlirlite.Value*:
"""Explicit-width integer literal: strict range check, then a constant of exactly w bits."""
ok: int = 1
if is_unsigned != 0:
if w < 64 and iv < 0: ok = 0
elif w <= 62 and iv > (int64(1) << w) - 1: ok = 0
else:
if w < 64:
lo: int64 = -(int64(1) << (w - 1))
hi: int64 = (int64(1) << (w - 1)) - 1
if iv < lo or iv > hi: ok = 0
if ok == 0:
wname: str = "int"
if is_unsigned != 0: wname = "uint"
self.Gen.TypeGenErr.fatal_error(node, f"integer literal {iv} does not fit the explicit width {wname}{w} (it would truncate); use a wider suffix or an explicit cast", "OverflowError")
return mlirlite.const_int(pool, w, iv)
# Translate constant expressions
def translate_constant(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
"""Translate a constant node (int/str/bool)."""
cn: ast.Constant* = ast.Constant*(node)
if cn is None:
return None
if cn is None: return None
ck: int = cn.const_kind
if cn.const_kind == ast.CONST_INT:
# 位宽选择:
# 1) 目标上下文是 >32 位整数时直接取 i64 —— 必需:词法层 atoll 把
# `0xFFFFFFFFFFFFFFFF` 按有符号 int64 回绕成 -1,仅按"是否超出
# i32 范围"判定会把它物化成 i32 -1,随后强转路径 zext 到 i64
# → 0x00000000FFFFFFFF(N1:THREAD_CREATE 失败码错)。
# const_int 按十进制值打印,故 i64 -1 即全 1,语义正确。
# 2) 否则维持旧规则:超出 i32 范围用 i64,其余 i32。
# Bit width: (1) if the target context is >32-bit int, use i64 directly - required
# since atoll wraps 0xFFFF... to -1 as signed int64; (2) else i64 if out of i32 range, otherwise i32.
iv: int64 = cn.int_val
if target_ty is not None and self.is_ptr_type(target_ty) == 0 \
and self.get_llvm_type_bits(target_ty) > 32:
return llvmlite.const_int64(pool, iv)
if iv > 2147483647 or iv < -2147483648:
return llvmlite.const_int64(pool, iv)
return llvmlite.const_int32(pool, iv)
# 显式宽度后缀(_int<N>/_uint<N>/_i<N>/_u<N>):按声明宽度出常量并严格校验不截断
if cn.int_width > 0: return self._explicit_width_int_const(pool, node, cn.int_width, cn.int_unsigned, iv)
if target_ty is not None and self.is_ptr_type(target_ty) == 0 and self.get_llvm_type_bits(target_ty) > 32: return mlirlite.const_int64(pool, iv)
# 位宽按值推导:i32 放不下、但 u32 放得下的字面量归为 32 位(u32)常量,而不是一律落 64 位
# (否则 `crc ^ 0xFFFFFFFF` 的异或会变成 64 位,赋值回 32 位 uint 时报“隐式窄化禁止”)。
# 文本用有符号等价(LLVM/MLIR 惯例,避免越界字面量);IntVal 保留无符号真值,
# 使窄化检查按真实数值判定,不静默截断。
if iv > 2147483647 and iv <= 4294967295:
ty32: mlirlite.LLVMType* = mlirlite.Int32(pool)
nm32: str = pool.alloc(32)
if nm32 is None: return mlirlite.const_int32(pool, iv)
viperlib.snprintf(nm32, 32, "%lld", iv - 4294967296)
return mlirlite.ConstInt(pool, ty32, iv, nm32)
if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv)
return mlirlite.const_int32(pool, iv)
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
double_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
# 目标类型是 32 位浮点(float(1.5) / x: float = 1.5 / f32 形参)→ 直接出 f32 常量。
# 旧实现无视 target_ty 一律先出 double 常量、再由 coerce_to_type fptrunc,
# 现场:float(2.0) / float(1.0) 生成 f64 常量 + fptrunc,与双精度常量混算时又多出 fpext。
# 其余情况(无目标类型 / double / 非浮点)保持 double 常量,由 coerce_to_type 负责转换。
if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32:
return mlirlite.ConstFloat(pool, target_ty, cn.float_val)
double_ty: mlirlite.LLVMType* = mlirlite.Double(pool)
return mlirlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_CHAR:
# 单引号单字符字面量(CONST_CHAR):默认按字符值(i32,coerce 截断为 i8);但当目标类型是指针(str/bytes/t.CPtr)时须物化为 1 字符字符串,否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→ 运行期 strlen 访问低地址崩溃。1a 阶段(declare_only=1)依赖函数 found_func=None,target_ty 未对齐即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
if target_ty is not None and (uint64(target_ty) & 7) == 0 \
and self.is_ptr_type(target_ty) != 0:
# Single-char literal (CONST_CHAR): default as char value (i32, coerce truncates to i8); if the target type is a pointer (str/bytes/t.CPtr) materialize a 1-char string, else inttoptr treats the char code as a pointer -> strlen crash. In 1a stage (declare_only=1) found_func=None so a misaligned target_ty is treated as garbage and skipped
if target_ty is not None and (uint64(target_ty) & 7) == 0 and self.is_ptr_type(target_ty) != 0:
cbuf: bytes = stdlib.malloc(2)
if cbuf is None:
return llvmlite.const_int32(pool, 0)
if cbuf is None: return mlirlite.const_int32(pool, 0)
cbuf[0] = char(cn.int_val)
cbuf[1] = '\0'
r_cs: llvmlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
r_cs: mlirlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
stdlib.free(cbuf)
return r_cs
cv: int64 = cn.int_val
return llvmlite.const_int32(pool, cv)
return mlirlite.const_int32(pool, cv)
elif cn.const_kind == ast.CONST_STR:
sv: str = cn.str_val
if sv is None:
return None
r: llvmlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
if sv is None: return None
r: mlirlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
return r
elif cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
if cn.int_val != 0: return mlirlite.const_int32(pool, 1)
return mlirlite.const_int32(pool, 0)
elif cn.const_kind == ast.CONST_NONE:
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
if i8_ty is None:
return None
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None:
return None
rv_none: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
# None -> i8* null (null pointer constant) for `p is None` / `p is not None` comparisons
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
if i8_ty is None: return None
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None: return None
rv_none: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty, "null")
return rv_none
return None
# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*,
value: llvmlite.Value*) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
"""
if value is None or value.Ty is None:
return None
ty: llvmlite.LLVMType* = value.Ty
# _infer_elem_type_from_value - infer the list element type name from an LLVM Value.
# Used for element type inference of list literals [a, b, c].
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*, value: mlirlite.Value*) -> str:
"""Infer the list element type name from an LLVM Value; None if unmappable."""
if value is None or value.Ty is None: return None
ty: mlirlite.LLVMType* = value.Ty
bits: int = self.get_llvm_type_bits(ty)
if bits != 0:
if bits == 32:
return "int"
if bits == 64:
return "CSizeT"
if bits == 8:
return "CInt8T"
if bits == 16:
return "CInt16T"
if bits == 32: return "int"
if bits == 64: return "CSizeT"
if bits == 8: return "CInt8T"
if bits == 16: return "CInt16T"
return None
if self.is_ptr_type(ty) != 0:
return "str"
if self.is_ptr_type(ty) != 0: return "str"
fbits: int = self.get_llvm_float_bits(ty)
if fbits != 0:
if fbits == 64:
return "CDouble"
if fbits == 32:
return "CFloat"
if fbits == 64: return "CDouble"
if fbits == 32: return "CFloat"
return None
# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(self, trans: HT.Translator*) -> llvmlite.Value*:
"""查找上下文中的 pool 变量,返回 alloca 或 None
查找顺序: pool → _mbuddy → mbuddy → mb
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
"""
if trans is None:
return None
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
# _find_pool_var - find the pool variable in the context.
# Tries "pool", "_mbuddy", "mbuddy", "mb" in order.
def _find_pool_var(self, trans: HT.Translator*) -> mlirlite.Value*:
"""Find the pool variable in context (pool/_mbuddy/mbuddy/mb), else fall back."""
if trans is None: return None
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
if alloca is not None: return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
if alloca is not None:
return alloca
if alloca is not None: return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
if alloca is not None:
return alloca
if alloca is not None: return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
# 兜底: 创建当前模块 mangled 的 @_mbuddy 定义(linkonce_odr + zeroinitializer)
# 对齐模块级全局带 SHA1 前缀命名:裸名 @_mbuddy 依赖链接合并共享
# (--allow-multiple-definition),带前缀后每模块独立。linkonce_odr 允许
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
if alloca is not None: return alloca
# Fallback: create a module-mangled @_mbuddy definition (linkonce_odr + zeroinitializer),
# matching the SHA1-prefixed global naming; a real crash exposes an uninitialized pool (fail-fast).
pool: memhub.MemBuddy* = trans.Pool
if pool is None or trans.Module is None or trans.SymTab is None:
return None
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
existing: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
if existing is not None:
return existing
# 创建 i8* 类型(t.CPtr = i8*)
i8_ty_fb: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_fb)
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
if pool is None or trans.Module is None or trans.SymTab is None: return None
# First check whether it is already registered in the module scope (avoid duplicate globals)
existing: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
if existing is not None: return existing
# Create the i8* type (t.CPtr = i8*)
i8_ty_fb: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_fb)
# Module-mangled name (@<module-sha1>._mbuddy)
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
gv_ext: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
# Create a linkonce_odr definition (linker-mergeable, self-contained for linking)
gv_ext: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
if gv_ext is not None:
gv_ext.Linkage = "linkonce_odr"
gv_ext.Initializer = "zeroinitializer"
llvmlite.module_add_global(trans.Module, gv_ext)
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
i8_ptr_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty_fb)
mlirlite.module_add_global(trans.Module, gv_ext)
# Create the Value reference (@<module-sha1>._mbuddy, type i8**)
i8_ptr_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty_fb)
ref_name_fb: str = pool.alloc(96)
if ref_name_fb is not None:
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0:
viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
else:
viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
gv_ref_fb: llvmlite.Value* = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# 注册到模块作用域,避免重复创建
# Mangled names start with a digit (SHA1 prefix), so the reference must be quoted (see make_global_ref)
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0: viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
else: viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
gv_ref_fb: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# Register in the module scope to avoid duplicate creation
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
return gv_ref_fb
# translate_list_literal:把 list 字面量 [a, b, c] 翻译为 list[T](pool) 构造 + append 调用序列。elem_type_name 为元素类型名(如 "str"/"int"),None 表示从元素推断。返回 list 对象指针(Ptr(list[T] struct)),失败返回 None
def translate_list_literal(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
elem_type_name: str,
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
if node is None or trans is None:
return None
# translate_list_literal - lower [a, b, c] to list[T](pool) construction + append calls; elem_type_name None means infer from elements; returns Ptr(list[T] struct) or None
def translate_list_literal(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, elem_type_name: str, trans: HT.Translator*) -> mlirlite.Value*:
"""Lower a list literal to list[T](pool) construction plus append calls."""
if node is None or trans is None: return None
list_node: ast.List* = ast.List*(node)
if list_node is None:
return None
if list_node is None: return None
elts: list[ast.AST*]* = list_node.elts
elts_count: sizet = 0
if elts is not None:
elts_count = len(elts)
if elts is not None: elts_count = len(elts)
# 推断元素类型
# Infer element type
inferred_type_name: str = elem_type_name
first_elem_val: llvmlite.Value* = None
first_elem_val: mlirlite.Value* = None
if inferred_type_name is None:
if elts_count > 0:
first_elem_node: ast.AST* = elts.get(0)
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
if first_elem_val is None or first_elem_val.Ty is None:
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
# fail-fast: first-element translation failed/Value type missing -> abort now (surface root cause)
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
if inferred_type_name is None:
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
# fail-fast: first-element Value type cannot map to a list element type -> abort now
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
else:
# 空 list 无元素可推断,保持默认 str(list[str])
# Empty list has no element to infer, keep default str (list[str])
inferred_type_name = "str"
# 查找 pool 变量
pool_alloca: llvmlite.Value* = self._find_pool_var(trans)
# Find the pool variable
pool_alloca: mlirlite.Value* = self._find_pool_var(trans)
if pool_alloca is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
return None
# load pool 变量值(i8*)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None:
return None
# Load the pool variable value (i8*)
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
pool_val: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None: return None
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
# Specialize the list[T] class (elem_size=8: str is a pointer; 4 would overlap elements)
type_args: list[str]* = list[str](pool, 8)
type_args.append(inferred_type_name)
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
@@ -300,55 +252,48 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
return None
# 查找 struct entry
# Find the struct entry
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if struct_entry is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
return None
struct_ty: llvmlite.LLVMType* = struct_entry.Ty
if struct_ty is None:
return None
struct_ty: mlirlite.LLVMType* = struct_entry.Ty
if struct_ty is None: return None
# alloca 临时 list 空间
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
# alloca temporary list storage; ensure the cross-module struct definition is
# fully available in the current module (so alloca can size it).
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return None
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None: return None
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
# 避免为无 __new__ 的类生成过度声明)
storage_ptr: llvmlite.Value* = tmp
# Storage pointer: alloca by default, or the pointer returned by __new__ if present.
# __new__ is optional and only called when the generic template defines it (consistent with ctor lowering).
storage_ptr: mlirlite.Value* = tmp
has_new_flag: int = 0
if struct_entry is not None:
has_new_flag = struct_entry.HasNew
if struct_entry is not None: has_new_flag = struct_entry.HasNew
if has_new_flag != 0:
new_args: sizet* = pool.alloc(8 * 32)
if new_args is None:
return None
if new_args is None: return None
string.memset(new_args, 0, 8 * 32)
new_args[0] = sizet(pool_val)
new_ptr: llvmlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if new_ptr is not None:
storage_ptr = new_ptr
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if new_ptr is not None: storage_ptr = new_ptr
# 调用 __before_init__(storage_ptr)
# Call __before_init__(storage_ptr)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
# Call __init__(storage_ptr, pool_val, elem_size=0)
init_args: sizet* = pool.alloc(8 * 32)
if init_args is None: return None
string.memset(init_args, 0, 8 * 32)
init_args[0] = sizet(pool_val)
elem_size_val: llvmlite.Value* = llvmlite.const_int64(pool, 0)
elem_size_val: mlirlite.Value* = mlirlite.const_int64(pool, 0)
init_args[1] = sizet(elem_size_val)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
# 对每个元素调用 append(storage_ptr, elem_val)
# Call append(storage_ptr, elem_val) for each element
if elts_count > 0:
# 第一个元素可能已经翻译过
# The first element may already be translated
if first_elem_val is not None:
append_args: sizet* = pool.alloc(8 * 32)
if append_args is not None:
@@ -356,10 +301,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
append_args[0] = sizet(first_elem_val)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
# 翻译剩余元素
for i in range(1, elts_count):
# Translate the remaining elements
for i in range(1, int(elts_count)):
elem_node_i: ast.AST* = elts.get(i)
elem_val_i: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
elem_val_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
if elem_val_i is not None:
ap_args: sizet* = pool.alloc(8 * 32)
if ap_args is not None:
+194 -252
View File
@@ -1,254 +1,237 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import lib.core.Gen.Translator as HT
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
# ============================================================
# f-string translation constants (local copy of ExprGenMixin's to avoid a cross-module
# CDefine lookup bug: ExprGenFstr compiles before ExprGenMixin, so the table is unregistered).
define FSTR_BUF_SIZE = 1024
define FSTR_SPEC_MAX = 64
define FSTR_FMT_MAX = 64
# ============================================================
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
# ============================================================
# ExprGenFstr - ExprGenMixin split (stage D): f-string/formatted-value translation
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
"""f-string/格式化值翻译"""
"""f-string and formatted-value translation."""
# ============================================================
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
#
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
# 栈缓冲(每站点独立,线程安全);结果是**函数内临时值,禁止逃逸**
# (return / 存字段 / 存全局 由 GenApi.fail_if_fstring_escape 拦;
# 需逃逸用 GenApi.escape_fstring_copy 显式拷贝)。
# 之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
# ============================================================
def translate_joined_str(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""f-string → snprintf 生成 i8* 栈缓冲(函数内临时值,禁止逃逸)"""
# translate_joined_str - f-string translation (aligned with TPC _HandleJoinedStrLlvm):
# join literals and format specs, snprintf into a per-site i8* stack buffer (no escape).
def translate_joined_str(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Lower an f-string to snprintf into a per-site i8* stack buffer."""
if node is None or builder is None or trans is None: return None
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](node)
js: ast.JoinedStr* = ast.JoinedStr*(node)
if js is None or js.values is None: return None
# 编译期格式串缓冲(pool 分配)
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None: return None
fmt_buf[0] = '\0'
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
arg_head: llvmlite.Value* = None
arg_tail: llvmlite.Value* = None
arg_count: int = 0
parts: list[ast.AST*]* = js.values
pn: sizet = len(parts)
for pi in range(pn):
# Size the format buffer from the actual parts: each literal may double ('%' -> '%%') and
# each conversion contributes a short spec. A fixed 1024 silently dropped trailing
# literals/converters while their arguments were still appended -> wrong runtime output.
fmt_cap: sizet = 16
for pi0 in range(int(pn)):
part0: ast.AST* = parts.get(pi0)
if part0 is None: continue
if part0.kind() == ast.ASTKind.Constant:
pc0: ast.Constant* = ast.Constant*(part0)
if pc0.const_kind == ast.CONST_STR and pc0.str_val is not None:
fmt_cap = fmt_cap + string.strlen(pc0.str_val) * 2
elif part0.kind() == ast.ASTKind.FormattedValue:
fmt_cap = fmt_cap + FSTR_SPEC_MAX + 16
# Compile-time format string buffer (pool-allocated)
fmt_buf: str = pool.alloc(fmt_cap)
if fmt_buf is None: return None
fmt_buf[0] = '\0'
# Runtime argument list (linked by Value.Next, matching the printf call)
arg_head: mlirlite.Value* = None
arg_tail: mlirlite.Value* = None
arg_count: int = 0
for pi in range(int(pn)):
part: ast.AST* = parts.get(pi)
if part is None: continue
if part.kind() == ast.ASTKind.Constant:
pcn: ast.Constant* = ast.Constant*(part)
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
self._fstr_append_literal(fmt_buf, pcn.str_val)
self._fstr_append_literal(fmt_buf, fmt_cap, pcn.str_val)
continue
if part.kind() != ast.ASTKind.FormattedValue: continue
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part)
fv: ast.FormattedValue* = ast.FormattedValue*(part)
if fv.value is None: continue
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
# fail-fast: a failed formatted-value translation must abort compilation
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
continue
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
# Object defines __str__ and no format is given -> use its returned string (like TPC)
if fv.format_spec is None:
str_ov_fs: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
if str_ov_fs is not None: val = str_ov_fs
# Determine the C format specifier (format_spec first, else infer from the value type)
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
# 追加格式说明符
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
# fail-fast: cannot infer the f-string format (missing value type); abort
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
# Append the format specifier
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
# Convert the argument type (i8/i16->i32, bool->i32, f32->f64)
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None: cast_val = val
cast_val.Next = None
if arg_head is None:
arg_head = cast_val
else:
arg_tail.Next = cast_val
if arg_head is None: arg_head = cast_val
else: arg_tail.Next = cast_val
arg_tail = cast_val
arg_count += 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def translate_formatted_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
if node is None or builder is None or trans is None:
return None
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](node)
if fv is None or fv.value is None:
return None
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
def translate_formatted_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Translate a bare FormattedValue without any literal parts."""
if node is None or builder is None or trans is None: return None
fv: ast.FormattedValue* = ast.FormattedValue*(node)
if fv is None or fv.value is None: return None
# A bare FormattedValue contributes exactly one conversion spec (bounded by FSTR_SPEC_MAX
# plus the C prefix/width); size the buffer from that instead of the fixed 1024.
fmt_cap: sizet = FSTR_SPEC_MAX + 64
fmt_buf: str = pool.alloc(fmt_cap)
if fmt_buf is None: return None
fmt_buf[0] = '\0'
arg_head: llvmlite.Value* = None
arg_tail: llvmlite.Value* = None
arg_head: mlirlite.Value* = None
arg_tail: mlirlite.Value* = None
arg_count: int = 0
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
# fail-fast: a failed bare FormattedValue translation must abort compilation
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
else:
# Object defines __str__ and no format is given -> use its returned string (like TPC)
if fv.format_spec is None:
str_ov_sf: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
if str_ov_sf is not None: val = str_ov_sf
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
# fail-fast: cannot infer the f-string format (missing value type); abort
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None: cast_val = val
cast_val.Next = None
arg_head = cast_val
arg_tail = cast_val
arg_count = 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def _fstr_finalize(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
fmt_buf: str,
arg_head: llvmlite.Value*,
arg_tail: llvmlite.Value*,
arg_count: int,
trans: HT.Translator*) -> llvmlite.Value*:
"""f-string 尾部:全局格式串 + 每站点栈缓冲 + snprintf,返回 i8*(函数内临时值)"""
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
def _fstr_finalize(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, fmt_buf: str, arg_head: mlirlite.Value*, arg_tail: mlirlite.Value*, arg_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit the final snprintf over the global format and per-site buffer."""
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
if fmt_buf is None or fmt_buf[0] == '\0':
# 空 f-string(f"")→ 空字符串常量
# Empty f-string (f"") -> empty string constant
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
# 格式串 → 全局字符串常量(i8*)
fmt_ptr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
# Format string -> global string constant (i8*)
fmt_ptr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None: return None
# 结果缓冲:**每站点独立栈缓冲**(entry 块 alloca;每次翻译本站点各建一个,
# 站点之间不共享 ⇒ 同函数多个 f-string 不再互相覆盖)。
#
# 语义(用户 2026-09-27 决定):f-string 结果是**当前函数内的临时值**。
# · 栈缓冲 ⇒ 线程安全(每个线程各自一份栈帧,同站点并发也不串味)
# + 零运行时依赖(内核没有 C-ABI malloc,也不能假设任何分配器);
# · 函数返回即失效 ⇒ **禁止逃逸**:return / 存字段 / 存全局 / 存容器一律
# 由 GenApi.fail_if_fstring_escape 在翻译期 fail-fast 拦下。历史事故:
# 旧实现让调用方把该栈指针 return 出去,再作 %s 喂 snprintf ⇒
# ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。
# · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy
# (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法
# (AssignGenMixin.const_assign_msg)。
buf_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
# Result buffer: a per-site entry alloca (thread-safe, no runtime dependency), so the
# f-string result is a function-local temporary that must not escape; copy to escape.
buf_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
if buf_alloca is None: return None
zero_i32: llvmlite.Value* = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value* = llvmlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
zero_i32: mlirlite.Value* = mlirlite.const_int32(pool, 0)
buf_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None: return None
# f-string lowers to snprintf, declared in includes/stdio.vp. If the source imports no
# module declaring it, add the declare here (implicit-provide); i8*, i64, i8* + varargs.
if self.Gen.CallGenBase.find_func_in_module(mod, "snprintf") is None:
snprintf_decl: mlirlite.Function* = mlirlite.create_declare(pool, mod, "snprintf", i32_ty)
if snprintf_decl is not None:
snprintf_decl.IsVarArg = 1
# buf/fmt are pointer params (i8*), not the element type i8: a bare i8 declare
# would cause an MLIR 'llvm.call' operand type mismatch ('!llvm.ptr' != 'i8').
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "buf")
mlirlite.add_param(pool, snprintf_decl, mlirlite.Int64(pool), "size")
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "fmt")
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
size_val: llvmlite.Value* = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
size_val: mlirlite.Value* = mlirlite.const_int64(pool, FSTR_BUF_SIZE)
buf_ptr.Next = size_val
size_val.Next = fmt_ptr
fmt_ptr.Next = arg_head
if arg_tail is not None:
arg_tail.Next = None
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
if arg_tail is not None: arg_tail.Next = None
mlirlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
return buf_ptr
def _fstr_append_literal(self, fmt_buf: str,
lit: str) -> None:
"""追加字面量到格式串(% 转义为 %%)"""
def _fstr_append_literal(self, fmt_buf: str, cap: sizet, lit: str) -> None:
"""Append a literal to the format string, escaping percent signs; fail-fast on overflow."""
if fmt_buf is None or lit is None: return
cur_len: sizet = string.strlen(fmt_buf)
rem: sizet = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0: return
src_len: sizet = string.strlen(lit)
# Worst case: every character expands to "%%".
if cur_len + src_len * 2 + 1 > cap:
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + up to {src_len * 2} escaped + 1 NUL); shorten the literal", "OverflowError")
si: sizet = 0
while si < src_len and rem > 0:
while si < src_len:
ch: char = lit[si]
if ch == '%':
if rem < 2:
break
fmt_buf[cur_len] = '%'
fmt_buf[cur_len + 1] = '%'
cur_len += 2
rem -= 2
else:
fmt_buf[cur_len] = ch
cur_len += 1
rem -= 1
si += 1
fmt_buf[cur_len] = '\0'
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
"""追加原始字符串(不做 % 转义)"""
if fmt_buf is None or s is None:
return
def _fstr_append_raw(self, fmt_buf: str, cap: sizet, s: str) -> None:
"""Append a raw string without percent escaping; fail-fast on overflow."""
if fmt_buf is None or s is None: return
cur_len: sizet = string.strlen(fmt_buf)
src_len: sizet = string.strlen(s)
if cur_len + src_len < FSTR_BUF_SIZE:
string.strcpy(fmt_buf + cur_len, s)
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*,
spec_node: ast.AST*,
val: llvmlite.Value*,
arg_node: ast.AST*) -> str:
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
if spec_node is None or pool is None or val is None:
return None
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
if cur_len + src_len + 1 > cap:
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + {src_len} to append + 1 NUL); shorten the format spec", "OverflowError")
i: sizet = 0
while i < src_len:
fmt_buf[cur_len + i] = s[i]
i += 1
fmt_buf[cur_len + src_len] = '\0'
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*, spec_node: ast.AST*, val: mlirlite.Value*, arg_node: ast.AST*) -> str:
"""Convert a Python format_spec into a C printf format string."""
if spec_node is None or pool is None or val is None: return None
# 1. Extract the spec string (JoinedStr with constants only)
spec: str = pool.alloc(FSTR_SPEC_MAX)
if spec is None:
return None
if spec is None: return None
spec[0] = '\0'
sk: int = spec_node.kind()
if sk == ast.ASTKind.JoinedStr:
jss: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](spec_node)
jss: ast.JoinedStr* = ast.JoinedStr*(spec_node)
if jss.values is None: return None
vn: sizet = len(jss.values)
for vi in range(vn):
for vi in range(int(vn)):
vpart: ast.AST* = jss.values.get(vi)
if vpart is None: continue
if vpart.kind() != ast.ASTKind.Constant: return None
vcn: ast.Constant* = ast.Constant*(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
string.strcat(spec, vcn.str_val)
# fail-fast: a truncated spec can still look valid (e.g. ">10.5f" cut to ">10."),
# which silently changes the output format.
if string.strlen(spec) + string.strlen(vcn.str_val) >= FSTR_SPEC_MAX - 1:
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
string.strcat(spec, vcn.str_val)
elif sk == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(spec_node)
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
return None
if cn.const_kind != ast.CONST_STR or cn.str_val is None: return None
if string.strlen(cn.str_val) >= FSTR_SPEC_MAX - 1:
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
else: return None
slen: sizet = string.strlen(spec)
if slen == 0: return None
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
# 2. Parse segment by segment: [fill]align / sign / # / 0 / width / , / .precision / type
pos: sizet = 0
align_char: char = 0
if pos < slen:
@@ -270,10 +253,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
c_prec[0] = '\0'
# sign
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
if spec[pos] == '+':
string.strcat(c_flags, "+")
elif spec[pos] == ' ':
string.strcat(c_flags, " ")
if spec[pos] == '+': string.strcat(c_flags, "+")
elif spec[pos] == ' ': string.strcat(c_flags, " ")
pos += 1
# alt '#'
if pos < slen and spec[pos] == '#':
@@ -283,9 +264,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
if pos < slen and spec[pos] == '0':
string.strcat(c_flags, "0")
pos += 1
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
if align_char == '<' or align_char == '^':
string.strcat(c_flags, "-")
# Left align '<'/'^' -> '-' (C semantics give '-' priority over '0')
if align_char == '<' or align_char == '^': string.strcat(c_flags, "-")
# width digits
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
wl: sizet = string.strlen(c_width)
@@ -293,9 +273,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
c_width[wl] = spec[pos]
c_width[wl + 1] = '\0'
pos += 1
# comma ',' → C printf 不支持千分位,忽略
if pos < slen and spec[pos] == ',':
pos += 1
# comma ',' -> C printf has no thousands separator, ignore
if pos < slen and spec[pos] == ',': pos += 1
# .precision
if pos < slen and spec[pos] == '.':
string.strcat(c_prec, ".")
@@ -307,28 +286,26 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
c_prec[pl + 1] = '\0'
pos += 1
if string.strlen(c_prec) == 1:
return None # 只有 '.' 无效
return None # a lone '.' is invalid
# type char
fmt_type: char = 0
if pos < slen:
fmt_type = spec[pos]
pos += 1
if pos != slen: return None # 多余字符无法解析
if pos != slen: return None # extra characters cannot be parsed
# 3. 长度修饰(按值类型位宽)
# 3. Length modifier (by value type bit width)
len_mod: str = pool.alloc(4)
if len_mod is None: return None
len_mod[0] = '\0'
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
# 32 位**不写**长度修饰符(N14):`{x:08X}` 的实参是 i32,此前推导出
# `l` → 格式串 `%08lX`(long)与实参宽度不符(LP64 下 printf 按 64 位
# 读参 → 变参错位)。与同文件无 spec 路径 `_fstr_infer_c_fmt`(32 位用
# `%u/%d`)以及 TPC 侧 HandlesExprFormat.py 保持一致。
# Do not write a length modifier for 32-bit (N14): `{x:08X}` passes an i32, but `l`
# would make %08lX (long) mismatch on LP64 and misread the varargs.
if ibits == 64: string.strcpy(len_mod, "ll")
elif ibits == 16: string.strcpy(len_mod, "h")
elif ibits == 8: string.strcpy(len_mod, "hh")
# 4. 组装 C 格式串
# 4. Assemble the C format string
out: str = pool.alloc(FSTR_FMT_MAX)
if out is None: return None
out[0] = '\0'
@@ -337,58 +314,43 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
string.strcat(out, c_width)
string.strcat(out, c_prec)
if fmt_type == 0:
# 无 type:按值类型推断
# No type: infer from the value type
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if ibits != 0:
if ibits == 8:
string.strcat(out, "c")
elif ibits == 1:
string.strcat(out, "d")
if ibits == 8: string.strcat(out, "c")
elif ibits == 1: string.strcat(out, "d")
else:
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fbits2 != 0:
string.strcat(out, "f")
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
string.strcat(out, "s")
else:
string.strcat(out, "d")
elif fmt_type == 'c':
string.strcat(out, "c")
elif fmt_type == 'f' or fmt_type == 'F':
string.strcat(out, "f")
elif fbits2 != 0: string.strcat(out, "f")
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: string.strcat(out, "s")
else: string.strcat(out, "d")
elif fmt_type == 'c': string.strcat(out, "c")
elif fmt_type == 'f' or fmt_type == 'F': string.strcat(out, "f")
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
tmp_ch: str = pool.alloc(2)
if tmp_ch is None:
return None
if tmp_ch is None: return None
tmp_ch[0] = fmt_type
tmp_ch[1] = '\0'
string.strcat(out, tmp_ch)
elif fmt_type == 's':
string.strcat(out, "s")
elif fmt_type == '%':
string.strcat(out, "%")
elif fmt_type == 's': string.strcat(out, "s")
elif fmt_type == '%': string.strcat(out, "%")
elif fmt_type == 'b':
return None # 二进制不支持
return None # binary is unsupported
elif fmt_type == 'd' or fmt_type == 'i':
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
string.strcat(out, len_mod)
tmp_ch2: str = pool.alloc(2)
if tmp_ch2 is None:
return None
if tmp_ch2 is None: return None
tmp_ch2[0] = fmt_type
tmp_ch2[1] = '\0'
string.strcat(out, tmp_ch2)
else:
return None
else: return None
return out
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*,
val: llvmlite.Value*,
arg_node: ast.AST*,
trans: HT.Translator*) -> str:
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, arg_node: ast.AST*, trans: HT.Translator*) -> str:
"""Infer the C printf format from the value type when no format_spec is given."""
if val is None or pool is None: return None
out: str = pool.alloc(16)
if out is None: return None
@@ -396,70 +358,50 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if ibits == 1: return "%d" # bool
if ibits == 8: return "%c" # 字符
if ibits == 8: return "%c" # character
if ibits != 0:
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
if ibits == 64:
string.strcpy(out, "%llu")
else:
string.strcpy(out, "%u")
if ibits == 64: string.strcpy(out, "%llu")
else: string.strcpy(out, "%u")
else:
if ibits == 64:
string.strcpy(out, "%lld")
else:
string.strcpy(out, "%d")
if ibits == 64: string.strcpy(out, "%lld")
else: string.strcpy(out, "%d")
return out
if fbits != 0:
return "%f"
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
return "%s"
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
if fbits != 0: return "%f"
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: return "%s"
# fail-fast: missing value type (not int/float/pointer); cannot infer the C format
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string format cannot be inferred (value type missing)", "TypeError")
return None
def _fstr_cast_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
val: llvmlite.Value*,
c_fmt: str,
arg_node: ast.AST*,
trans: HT.Translator*) -> llvmlite.Value*:
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
if val is None or c_fmt is None:
return None
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
def _fstr_cast_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, val: mlirlite.Value*, c_fmt: str, arg_node: ast.AST*, trans: HT.Translator*) -> mlirlite.Value*:
"""Cast the value to the type required by the C format specifier."""
if val is None or c_fmt is None: return None
# Find the format type char (last letter, ignoring l/h length modifiers)
fmt_char: char = 0
flen: sizet = string.strlen(c_fmt)
fi: sizet = flen
while fi > 0:
fi -= 1
ch: char = c_fmt[fi]
if ch == '%':
break
if ch == '%': break
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
if ch != 'l' and ch != 'h':
fmt_char = ch
break
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if fmt_char == 's':
return val
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
if fmt_char == 's': return val
if fmt_char == 'c':
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits != 0 and ibits < 32:
return llvmlite.build_zext(builder, val, i32_ty)
if ibits != 0 and ibits < 32: return mlirlite.build_zext(builder, val, i32_ty)
return val
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits == 1:
return llvmlite.build_zext(builder, val, i32_ty)
if ibits == 1: return mlirlite.build_zext(builder, val, i32_ty)
if ibits == 8 or ibits == 16:
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
return llvmlite.build_zext(builder, val, i32_ty)
return llvmlite.build_sext(builder, val, i32_ty)
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0: return mlirlite.build_zext(builder, val, i32_ty)
return mlirlite.build_sext(builder, val, i32_ty)
return val
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
or fmt_char == 'g' or fmt_char == 'G':
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' or fmt_char == 'g' or fmt_char == 'G':
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if fbits == 32:
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
if fbits == 32: return mlirlite.build_fpext(builder, val, mlirlite.Double(pool))
return val
return val
+81 -127
View File
@@ -1,127 +1,81 @@
import t, c
from stdint import *
import ast
import memhub
import llvmlite
import lib.core.Gen.Translator as HT
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# docstring 登记表容量(__doc__ 魔法属性支持)
#
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
# 函数/类/方法 docstring,幂等查重)
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
# DOC_VAL_SZ: docstring 内容缓冲宽度
# ============================================================
define DOC_MAX = 512
define DOC_KEY_SZ = 192
define DOC_VAL_SZ = 4096
# ============================================================
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
#
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
# ============================================================
define FSTR_BUF_SIZE = 1024
define FSTR_SPEC_MAX = 64
define FSTR_FMT_MAX = 64
# ============================================================
# ExprGenMixin - 表达式处理 Mixin(对象化 P3)
# ============================================================
# ============================================================
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
# ============================================================
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
# ============================================================
# 翻译值表达式(RHS 分派)— 模块级版本
# ============================================================
def translate_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None,
use_ctx: str = None) -> llvmlite.Value*:
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
use_ctx: 使用处**真实站点种类**标签("局部变量注解" / "模块级全局" /
"形参" / "返回注解" …),只被 CDefine 使用处宽度不足报文读取;不参与判定。
"""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
rv: llvmlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
if trans is not None:
trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None,
use_ctx: str = None) -> llvmlite.Value*:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
return rv
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Await:
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
aw: rawptr[ast.Await] = rawptr[ast.Await](node)
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
aw_cl: ast.Call* = ast.Call*(aw.value)
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
if aw_nm.id is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0:
return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
return None
elif k == ast.ASTKind.Starred:
# *x 在值上下文(如 printf("%d", *k) 实参):解析器把调用参数位的
# '*' 按 Python *args 解包语义归类为 Starred,但 Viper 未实现解包,
# 全代码库无调用点使用;按 C 语义视为单目解引用(*ptr → load pointee,
# 对齐 UnaryOp(Deref))。构造 Deref UnaryOp 复用既有翻译/错误路径。
st: rawptr[ast.Starred] = rawptr[ast.Starred](node)
if st is None or st.value is None: return None
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
if deref_node is None: return None
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
return None
from stdint import *
import ast
import memhub
import mlirlite
import lib.core.Gen.Translator as HT
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# docstring registry capacities (__doc__ magic attribute support).
# DOC_MAX: max entries; DOC_KEY_SZ: key "{module_sha1}.{symbol}" width; DOC_VAL_SZ: content width.
define DOC_MAX = 512
define DOC_KEY_SZ = 192
define DOC_VAL_SZ = 4096
# f-string translation constants (aligned with TPC HandlesExprFormat).
# FSTR_BUF_SIZE: runtime stack buffer; FSTR_SPEC_MAX: format_spec parse width; FSTR_FMT_MAX: printf format width.
define FSTR_BUF_SIZE = 1024
define FSTR_SPEC_MAX = 64
define FSTR_FMT_MAX = 64
# ExprGenMixin - expression handling mixin (objectified P3).
# ExprGenMixin - expressions / constants / subscripts / attributes / operators.
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
"""Expression-handling mixin (pure methods, no state fields)."""
# Translate a value expression (RHS dispatch) - module-level version.
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
"""Translate a value expression to an LLVM Value (depth-guard wrapper; see _translate_value_impl).
target_ty = assignment/parameter type context; use_ctx = real call site for CDefine width checks."""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "expression translation nested too deep (suspected AST self-reference cycle or translation bug)", "RecursionError")
trans.TranslateDepth += 1
rv: mlirlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
if trans is not None: trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
"""Translate a value expression to an LLVM Value."""
if node is None: return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: mlirlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
return rv
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans, target_ty)
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
# M2.4 `new stack|heap|memgr(x) [slife|hlife] T(args)`:只换"取存储",其余复用构造序列
elif k == ast.ASTKind.New: return self.Gen.CallGenCtor._translate_new_expr(pool, builder, mod, ast.New*(node), trans)
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans, target_ty)
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Await:
# Coroutine await (VPC-M1): inline-translate when the await target is an async call.
# Returns the child coroutine's i32 ret; top-level await is handled by AsyncGen.
aw: ast.Await* = ast.Await*(node)
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
aw_cl: ast.Call* = ast.Call*(aw.value)
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
if aw_nm.id is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0: return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
return None
elif k == ast.ASTKind.Starred:
# *x in a value context: the parser tags '*' as Starred, but Viper has no unpacking.
# Treat it as C unary deref (*ptr -> load pointee) by building a Deref UnaryOp.
st: ast.Starred* = ast.Starred*(node)
if st is None or st.value is None: return None
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
if deref_node is None: return None
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
return None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+281 -370
View File
@@ -1,371 +1,282 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Call.CallGenBase as CallGenBase
# ============================================================
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
# ============================================================
novirtual class FuncGenBase(CallGenBase.CallGenBase):
"""函数属性 / 命名 / mangle / create_function 基础工具"""
# ============================================================
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
#
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
#
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
# ============================================================
def extract_func_attrs(self, pool: memhub.MemBuddy*,
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
if decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
attrs_buf: str = pool.alloc(256)
if attrs_buf is None: return None
attrs_buf[0] = '\0'
found_any: int = 0
for di in range(dn):
deco: ast.AST* = decorator_list.get(di)
if deco is None: continue
dk: int = deco.kind()
# 直接形式 `@t.attr.<name>`(与类侧 ClassGenBase._has_decorator 同口径,
# 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态,
# 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件
# 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略
# (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。
if dk == ast.ASTKind.Attribute:
dan: str = self._get_attr_name_from_node(deco)
if dan is not None and string.strcmp(dan, "section") != 0:
if self._append_llvm_attr(attrs_buf, dan) != 0:
found_any = 1
continue
if dk != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None: continue
cfk: int = call_node.func.kind()
if cfk != ast.ASTKind.Attribute: continue
# 检测 c.Attribute
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
if func_attr.attr is None: continue
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
if func_attr.value is None: continue
fvk: int = func_attr.value.kind()
if fvk != ast.ASTKind.Name: continue
c_name: ast.Name* = ast.Name*(func_attr.value)
if c_name.id is None: continue
if string.strcmp(c_name.id, "c") != 0: continue
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
if imp_c == 0: continue
# 遍历参数提取属性
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None: continue
# B5:section 字符串属性(t.attr.section(".text.startup"))不能内联
# 在函数签名行(llc: expected string constant),必须走属性组
# #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。
if string.strcmp(attr_name, "section") == 0: continue
# 映射到 LLVM 属性名并追加
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
if ar != 0:
found_any = 1
if found_any == 0: return None
return attrs_buf
def extract_func_section(self, pool: memhub.MemBuddy*,
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。
LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾
attributes #N = { ... }),不能内联(llc: expected string constant)。
main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。
"""
if pool is None or decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
for di in range(dn):
deco: ast.AST* = decorator_list.get(di)
if deco is None or deco.kind() != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
sec_val: str = self._get_attr_call_arg_str(arg_node)
if sec_val is None: continue
sec_buf: str = pool.alloc(128)
if sec_buf is None: return None
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
return sec_buf
return None
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
"""从 AST 节点提取属性名
支持:
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
- Attribute: t.attr.packed -> "packed"
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
"""
if node is None: return None
k: int = node.kind()
# Call 节点: t.attr.always_inline()
if k == ast.ASTKind.Call:
call: ast.Call* = ast.Call*(node)
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
at: ast.Attribute* = ast.Attribute*(call.func)
return at.attr
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
if k == ast.ASTKind.Attribute:
at2: ast.Attribute* = ast.Attribute*(node)
return at2.attr
return None
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
"""取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))"""
if node is None or node.kind() != ast.ASTKind.Call: return None
cl: ast.Call* = ast.Call*(node)
if cl.args is None or len(cl.args) == 0: return None
a0: ast.AST* = cl.args.get(0)
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
cn: ast.Constant* = ast.Constant*(a0)
if cn.const_kind != ast.CONST_STR: return None
return cn.str_val
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
"""将属性名映射到 LLVM 属性名并追加到缓冲区
返回 1 表示已追加,0 表示不支持该属性
"""
if attrs_buf is None or attr_name is None: return 0
# 先确定映射的 LLVM 属性名
llvm_name: str = None
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
else:
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
return 0
# 支持的属性:如果缓冲区非空,先追加空格分隔
if attrs_buf[0] != '\0':
self._append_str(attrs_buf, " ", 256)
self._append_str(attrs_buf, llvm_name, 256)
return 1
def _append_str(self, buf: str, s: str,
max_len: sizet = 256) -> None:
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
if buf is None or s is None: return
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
if max_len == 0:
max_len = 256
cur_len: sizet = string.strlen(buf)
s_len: sizet = string.strlen(s)
if cur_len + s_len + 1 > max_len: return # 无剩余容量:丢弃本次追加,避免越界写 存疑
string.strcpy(buf + cur_len, s)
# ============================================================
# _mangle_name - 为名称添加 SHA1 前缀
#
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
# SHA1 前缀(16 hex + '.'),则原样返回。
# ============================================================
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
if trans is None or name is None: return name
if trans.ModuleSha1 is None: return name
name_len: sizet = string.strlen(name)
# 已带 SHA1 前缀("{16hex}.")则跳过
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`。旧判据是 `name_len > 17`,
# 与真源的 `>= 17` 只差"前缀之后为空串"这一退化形态(无实际意义),一并对齐。
if GenApi.sha1_prefix_len(name) != 0: return name
sha1: str = trans.ModuleSha1
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
#
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
# 而非当前模块的 SHA1。
# ============================================================
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
if sha1 is None or name is None: return name
name_len: sizet = string.strlen(name)
# 已带 SHA1 前缀("{16hex}.")则跳过
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`(口径对齐同 _mangle_name)。
if GenApi.sha1_prefix_len(name) != 0: return name
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
#
# 规则:
# - main 函数不加前缀(程序入口点)
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
# - 已带 SHA1 前缀的不再加
# - ModuleSha1 为 None 时不加前缀
# ============================================================
def _mangle_func_name(self, trans: HT.Translator*, name: str,
has_export: int) -> str:
"""为函数名添加 SHA1 前缀(检查 main/export)"""
if trans is None or name is None: return name
if has_export != 0: return name
if string.strcmp(name, "main") == 0: return name
return self._mangle_name(trans, name)
# ============================================================
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
#
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
# ============================================================
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
if fd is None or fd.children is None:
return 0
body_f: list[ast.AST*]* = fd.children
bn_f: sizet = len(body_f)
for bi_f in range(bn_f):
stmt_f: ast.AST* = body_f.get(bi_f)
if stmt_f is None: continue
if stmt_f.kind() != ast.ASTKind.Raise: continue
rn_f: rawptr[ast.Raise] = rawptr[ast.Raise](stmt_f)
if rn_f is None or rn_f.exc is None: continue
ek_f: int = rn_f.exc.kind()
is_stop_f: int = 0
if ek_f == ast.ASTKind.Name:
enm_f: ast.Name* = ast.Name*(rn_f.exc)
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0:
is_stop_f = 1
elif ek_f == ast.ASTKind.Call:
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0:
is_stop_f = 1
if is_stop_f == 0:
return 1
return 0
# ============================================================
# RejectCReturnDecorator - @c.CReturn 装饰器已移除,遇到即编译期报错
#
# 多返回值唯一入口是返回注解 `-> tuple[ty1, ty2, ...]`(tuple → struct 通路
# 逐字保留)。任何装饰器形态出现 CReturn 一律 fail-fast,不静默忽略。
# 供 FuncGenDef(pre_scan 前向声明 / translate_function_def 定义两阶段)复用;
# Converter 为模块级函数,同报文另写一份(见 StubGen/Converter.vp)。
# 无 CReturn 返回 0。
# ============================================================
def RejectCReturnDecorator(self, node: ast.AST*,
decorators: list[ast.AST*]*) -> int:
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
if decorators is None: return 0
rcr_dn: sizet = len(decorators)
for rcr_i in range(rcr_dn):
rcr_deco: ast.AST* = decorators.get(rcr_i)
if rcr_deco is None: continue
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
rcr_attr: ast.Attribute* = None
if rcr_deco.kind() == ast.ASTKind.Call:
rcr_call: ast.Call* = ast.Call*(rcr_deco)
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute:
rcr_attr = ast.Attribute*(rcr_call.func)
elif rcr_deco.kind() == ast.ASTKind.Attribute:
rcr_attr = ast.Attribute*(rcr_deco)
if rcr_attr is None or rcr_attr.attr is None: continue
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct 返回)", "TypeError")
return 1
return 0
# ============================================================
# 创建 LLVM 函数(简单版本,仅声明)
# ============================================================
def create_function(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
name: str,
args_node: ast.AST*,
ret_ty: llvmlite.LLVMType*) -> llvmlite.Function*:
"""创建 LLVM 函数并添加参数"""
if name is None or mod is None: return None
func: llvmlite.Function* = llvmlite.create_function(pool, mod, name, ret_ty)
if func is None: return None
if args_node is not None:
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
for ai in range(an):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
# t\.CVoid 表示空参:跳过
if arg.annotation is not None:
if arg.annotation.kind() == ast.ASTKind.Attribute:
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue
param_ty: llvmlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
if resolved is None:
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32。
# 本函数不传 trans/imports 上下文,解析失败即调用方未补齐
# 上下文(暴露上游,不由本函数兜底)。
self.Gen.TypeGenErr.fatal_error(arg, f"函数 '{name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
return None
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
llvmlite.add_param(pool, func, param_ty, pname)
from stdint import *
import ast
import mlirlite
import memhub
import string
import viperlib
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Call.CallGenBase as CallGenBase
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
novirtual class FuncGenBase(CallGenBase.CallGenBase):
"""Shared helpers for function attributes, naming, mangling and creation."""
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
if decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
attrs_buf: str = pool.alloc(256)
if attrs_buf is None: return None
attrs_buf[0] = '\0'
found_any: int = 0
for di in range(int(dn)):
deco: ast.AST* = decorator_list.get(di)
if deco is None: continue
dk: int = deco.kind()
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
if dk == ast.ASTKind.Attribute:
dan: str = self._get_attr_name_from_node(deco)
if dan is not None and string.strcmp(dan, "section") != 0:
if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1
continue
if dk != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None: continue
cfk: int = call_node.func.kind()
# Detect the builtin Attribute(...) wrapper emitted by @__attribute__(...).
if cfk != ast.ASTKind.Name: continue
func_nm: ast.Name* = ast.Name*(call_node.func)
if func_nm.id is None: continue
if string.strcmp(func_nm.id, "Attribute") != 0: continue
# Iterate args and extract attrs
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(int(an)):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None: continue
# section string attr cannot be inlined on the signature line (llc: expected
# string constant); it must go through attr group #N, handled by FuncGenDef.
if string.strcmp(attr_name, "section") == 0: continue
# Map to LLVM attr name and append
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
if ar != 0: found_any = 1
if found_any == 0: return None
return attrs_buf
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
"""Extract @Attribute(attr.section(...)) as the attr-group text."""
if pool is None or decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
for di in range(int(dn)):
deco: ast.AST* = decorator_list.get(di)
if deco is None or deco.kind() != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Name: continue
func_nm: ast.Name* = ast.Name*(call_node.func)
if func_nm.id is None or string.strcmp(func_nm.id, "Attribute") != 0: continue
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(int(an)):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
sec_val: str = self._get_attr_call_arg_str(arg_node)
if sec_val is None: continue
sec_buf: str = pool.alloc(128)
if sec_buf is None: return None
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
return sec_buf
return None
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
"""Extract the attribute name from a Call or Attribute AST node."""
if node is None: return None
k: int = node.kind()
# Call node: t.attr.always_inline()
if k == ast.ASTKind.Call:
call: ast.Call* = ast.Call*(node)
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
at: ast.Attribute* = ast.Attribute*(call.func)
return at.attr
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
if k == ast.ASTKind.Attribute:
at2: ast.Attribute* = ast.Attribute*(node)
return at2.attr
return None
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
"""Return the first positional string constant of a t.attr.xxx(...) call."""
if node is None or node.kind() != ast.ASTKind.Call: return None
cl: ast.Call* = ast.Call*(node)
if cl.args is None or len(cl.args) == 0: return None
a0: ast.AST* = cl.args.get(0)
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
cn: ast.Constant* = ast.Constant*(a0)
if cn.const_kind != ast.CONST_STR: return None
return cn.str_val
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
if attrs_buf is None or attr_name is None: return 0
# First determine the mapped LLVM attr name
llvm_name: str = None
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
else:
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
return 0
# Supported attr: append a space separator if the buffer is non-empty
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
self._append_str(attrs_buf, llvm_name, 256)
return 1
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
"""Append a string to buf with a bounded write to avoid overflow."""
if buf is None or s is None: return
# Defensive: VPC default-arg registration is buggy (missing max_len falls to 0);
# fall back to 256 so the bounded-write check does not drop all attr text.
if max_len == 0: max_len = 256
cur_len: sizet = string.strlen(buf)
s_len: sizet = string.strlen(s)
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
string.strcpy(buf + cur_len, s)
# _mangle_name - prepend a SHA1 prefix to a name.
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
"""Prepend the SHA1 prefix to a name (no main/export check)."""
if trans is None or name is None: return name
if trans.ModuleSha1 is None: return name
name_len: sizet = string.strlen(name)
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
if GenApi.sha1_prefix_len(name) != 0: return name
sha1: str = trans.ModuleSha1
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
"""Prepend the given SHA1 to a name (no main/export check)."""
if sha1 is None or name is None: return name
name_len: sizet = string.strlen(name)
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
if GenApi.sha1_prefix_len(name) != 0: return name
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
if trans is None or name is None: return name
if has_export != 0: return name
if string.strcmp(name, "main") == 0: return name
return self._mangle_name(trans, name)
# _body_has_raise - whether the body contains raise (non-StopIteration).
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
if fd is None or fd.children is None: return 0
body_f: list[ast.AST*]* = fd.children
bn_f: sizet = len(body_f)
for bi_f in range(int(bn_f)):
stmt_f: ast.AST* = body_f.get(bi_f)
if stmt_f is None: continue
if stmt_f.kind() != ast.ASTKind.Raise: continue
rn_f: ast.Raise* = ast.Raise*(stmt_f)
if rn_f is None or rn_f.exc is None: continue
ek_f: int = rn_f.exc.kind()
is_stop_f: int = 0
if ek_f == ast.ASTKind.Name:
enm_f: ast.Name* = ast.Name*(rn_f.exc)
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1
elif ek_f == ast.ASTKind.Call:
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
if is_stop_f == 0: return 1
return 0
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
if decorators is None: return 0
rcr_dn: sizet = len(decorators)
for rcr_i in range(int(rcr_dn)):
rcr_deco: ast.AST* = decorators.get(rcr_i)
if rcr_deco is None: continue
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
rcr_attr: ast.Attribute* = None
if rcr_deco.kind() == ast.ASTKind.Call:
rcr_call: ast.Call* = ast.Call*(rcr_deco)
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
if rcr_attr is None or rcr_attr.attr is None: continue
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
return 1
return 0
# Create LLVM function (simple version, declaration only)
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
"""Create an LLVM function and add its parameters."""
if name is None or mod is None: return None
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
if func is None: return None
if args_node is not None:
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
for ai in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
# CVoid / void means no args: skip
if arg.annotation is not None:
if arg.annotation.kind() == ast.ASTKind.Name:
at_cn: ast.Name* = ast.Name*(arg.annotation)
if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
if resolved is None:
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
# This fn lacks trans/imports context, so failure surfaces upstream.
self.Gen.TypeGenErr.fatal_error(arg, f"function '{name}' param '{arg.arg}' type annotation failed to resolve", "NameError")
return None
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
mlirlite.add_param(pool, func, param_ty, pname)
return func
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+813 -483
View File
File diff suppressed because it is too large. Load diff
+226 -328
View File
@@ -1,434 +1,332 @@
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
# 别名用 TSpec:模块内类名同为 TypeSpec,别名同名会被解析器误判为类静态方法。
# TypeSpec: unified type descriptor (Debugger/_Type.md section 6).
# Alias TSpec because the in-module class name is also TypeSpec (same name would be a static method).
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
#
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
# ============================================================
# GenTypes - Gen-side shared data types and constants (migrated out of the Handles layer)
# ============================================================
# 类编译期元数据(原 HandlesType.CLASS_META_*)
# ============================================================
CLASS_META_PROVIDES: t.CDefine = 1
CLASS_META_REQUIRES: t.CDefine = 2
CLASS_META_REQUIRE_MUST: t.CDefine = 3
# Class compile-time metadata (formerly HandlesType.CLASS_META_*)
define CLASS_META_PROVIDES = 1
define CLASS_META_REQUIRES = 2
define CLASS_META_REQUIRE_MUST = 3
# ============================================================
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
# ============================================================
META_STATIC: t.CDefine = 1
META_CLASS: t.CDefine = 2
META_PROP_GETTER: t.CDefine = 4
META_PROP_SETTER: t.CDefine = 8
META_PROP_DELETER: t.CDefine = 16
# Attribute/static/class-method metadata (formerly HandlesClassDef.META_*)
define META_STATIC = 1
define META_CLASS = 2
define META_PROP_GETTER = 4
define META_PROP_SETTER = 8
define META_PROP_DELETER = 16
# ============================================================
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
# ============================================================
FIELD_MAX: t.CDefine = 256
FIELD_NAME_MAX: t.CDefine = 64
PARENT_MAX: t.CDefine = 64
MRO_MAX: t.CDefine = 64
# Struct capacities (formerly HandlesStruct.FIELD_MAX/...)
define FIELD_MAX = 256
define FIELD_NAME_MAX = 64
define PARENT_MAX = 64
define MRO_MAX = 64
# ============================================================
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
# ============================================================
SEQ_KIND_RANGE: t.CDefine = 1
SEQ_KIND_STR: t.CDefine = 2
SEQ_KIND_LIST: t.CDefine = 3
# for-iteration sequence kinds (formerly HandlesFor.SEQ_KIND_*)
define SEQ_KIND_RANGE = 1
define SEQ_KIND_STR = 2
define SEQ_KIND_LIST = 3
# ============================================================
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
# ============================================================
# TraceFrame - Python-Traceback-like call frame (formerly HandlesType.TraceFrame)
class TraceFrame:
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
"""Python-Traceback-like call frame used by push_trace/pop_trace"""
file_name: str
lineno: int
col_start: int
col_end: int
# ---- 生命期标签(LifeTag):有序,越小越"短命" ----
# 逃逸检查只在 LIFE_FRAME 上触发;控制流汇合取更短者(Meet = min)。
# 详见 Debugger/ownership-and-new.md §11。
define LIFE_VALUE = 0 # 值语义(标量/聚合按值拷贝),不参与逃逸
define LIFE_FRAME = 1 # 本帧栈存储:局部变量地址、数组退让、形参副本地址
define LIFE_HEAP = 2 # 堆,hlife(释放靠手动/区域)
define LIFE_STATIC = 3 # 模块级全局 / 字符串常量 / 函数地址
define LIFE_UNKNOWN = 4 # 未知(extern 返回等),保守放行
# ============================================================
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
# ============================================================
# VarEntry - variable table entry (formerly HandlesVar.VarEntry)
class VarEntry:
"""变量表条目"""
"""Variable table entry"""
Name: str
Alloca: llvmlite.Value*
Alloca: mlirlite.Value*
Used: int
# TypeSpec(S3 起为**唯一类型承载**):由 add_var 用 SpecFromLLVMType 从
# alloca.Ty 的 pointee 填充(PermPool 分配 ⇒ 跨文件安全),注解侧再由
# set_var_annot_class_name 经 SpecWithSign / SpecWithSource 补整数符号性与
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入),由
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
# IsPtrElement 字段已并入 Spec.ElemBits)。
# TypeSpec (sole type carrier since S3): filled by add_var via SpecFromLLVMType from
# alloca.Ty's pointee (PermPool, cross-file safe); annotation side augments sign/source/elem.
Spec: TSpec.TypeSpec*
# 1=该变量的 load/store 带 LLVM volatile 限定符(注解联合含 t.CVolatile)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
# 1=load/store of this variable carries LLVM volatile (annotation union has t.CVolatile).
# Appended at the end so existing field offsets are unchanged.
IsVolatile: int
# 1=该变量是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):解引用(p[i] / p.field / c.Deref(p))的 load/store 带 LLVM
# volatile,禁止优化器重排序/消除对所指对象的每次访问。注意与 IsVolatile
# 的区别:IsVolatile 限定"指针变量自身"的读写,本字段限定"指针所指物"。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
# 1=this variable is rawptr[t.CVolatile | T] (volatile pointee, like C's volatile T*):
# its dereferences carry volatile. Unlike IsVolatile, which limits the pointer variable itself.
PointeeVolatile: int
# 1=该变量是 **const 对象**(注解含 t.CConst 且声明类型**不是指针**):
# 整个作用域内禁止对该变量名赋值 / 增量赋值 / 元素赋值(fail-fast)。
# 与 TPC 的 Gen.var_const_flags / global_const_flags 同判据、同报文,见
# Debugger/type-descriptors.md。指针类型(如 `p: t.CConst | T | t.CPtr`)的
# CConst 只约束 pointee,不使指针变量自身不可写,故不置 1。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
# 1=this variable is a const object (annotation has t.CConst and the type is not a
# pointer): assignment to it anywhere in scope fails fast. Pointer CConst only limits the pointee.
IsConst: int
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
Life: int
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
ByteOrder: int
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
# ============================================================
# Scope - 作用域节点(原 HandlesVar.Scope)
# ============================================================
# Scope - scope node (formerly HandlesVar.Scope)
novirtual class Scope:
"""作用域节点"""
Parent: Scope | t.CPtr
Vars: VarEntry | t.CPtr
"""Scope node"""
Parent: Scope*
Vars: VarEntry*
VarCount: int
Kind: int
# ============================================================
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
# ============================================================
# SymbolTable - nested-scope symbol table (formerly HandlesVar.SymbolTable)
novirtual class SymbolTable:
"""嵌套作用域符号表"""
"""Nested-scope symbol table"""
Pool: memhub.MemBuddy*
Root: Scope | t.CPtr
Current: Scope | t.CPtr
Root: Scope*
Current: Scope*
# ============================================================
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
# ============================================================
# FieldEntry - field info entry (formerly HandlesStruct.FieldEntry)
class FieldEntry:
Name: str # 字段名(字符串)
Index: int # 字段在结构体中的索引
Ty: llvmlite.LLVMType* # 字段的 LLVM 类型
DefaultVal: ast.AST* # 默认值 AST 节点(None=无默认值)
# TypeSpec(S3 起为**唯一类型承载**):由 add_field 用 SpecFromLLVMType 从
# field_ty 填充(PermPool 分配 ⇒ 跨 1b reset 安全,无需 DeepCopyType),
# 注解侧再经 SpecWithSign / SpecWithSource 补符号性与注解来源名
# (Spec.SourceName,原 AnnotClassName 字段已并入)。
Name: str # Field name (string)
Index: int # Field index within the struct
Ty: mlirlite.LLVMType* # Field LLVM type
DefaultVal: ast.AST* # Default-value AST node (None=no default)
# TypeSpec (sole type carrier since S3): filled by add_field via SpecFromLLVMType from
# field_ty (PermPool, safe across 1b reset); annotation side adds sign/source.
Spec: TSpec.TypeSpec*
# 1=该字段经对象访问的 load/store 带 LLVM volatile 限定符
# (字段注解联合含 t.CVolatile,如 Spinlock.locked: t.CVolatile | t.CInt)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
# 1=load/store of this field via an object carries LLVM volatile (field annotation union
# has t.CVolatile, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
IsVolatile: int
# 1=该字段是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
# 带 LLVM volatile。与 IsVolatile 的区别同 VarEntry.PointeeVolatile。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
# 1=this field is rawptr[t.CVolatile | T] (volatile pointee): dereferences via it
# (self.ptr[i] / self.ptr.field) carry volatile. Same distinction as VarEntry.PointeeVolatile.
PointeeVolatile: int
# 1=该字段是 **const 对象**(注解含 t.CConst 且字段类型**不是指针**):
# 经对象给该字段赋值(obj.f = ... / self.f = ...)即 fail-fast,与
# VarEntry.IsConst 同判据、同报文(TPC 侧对应 Gen.field_const_flags)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
# 1=this field is a const object (annotation has t.CConst and the type is not a pointer):
# assigning it via an object fails fast, same as VarEntry.IsConst.
IsConst: int
# Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation
# union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets
# are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only).
ByteOrder: int
# ============================================================
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
# ============================================================
# StructEntry - struct registration entry (formerly HandlesStruct.StructEntry)
novirtual class StructEntry:
Name: str # 类名
Ty: llvmlite.LLVMType* # LLVM StructType
FieldCount: int # 字段数量
Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位)
IsUnion: int # 1=联合体 / 0=普通结构体
IsPacked: int # 1=@c.Attribute(t.attr.packed) 打包结构体(无字段填充)
Align: int # @c.Attribute(t.attr.aligned(N)) 对齐字节数(0=默认,如 fpu_context=16)
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
HasInit: int # 1=有__init__方法 / 0=无
HasNew: int # 1=有__new__方法 / 0=无
HasVTable: int # 1=有虚表 / 0=无虚表
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
ParentNames: sizet* # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
ParentCount: int # 父类数量(0=无父类,1=单继承,>1=多继承)
VTableMethodCount: int # 虚表中的方法数量
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
IsForwardDecl: int # 1=前向声明占位条目(Ty=不透明具名结构体 {sha}.Class,FieldCount=0;
# 定义侧随后**原位填充**同一 Ty 对象并清零本标记)。
# 语义对齐 TPC:未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充。
Name: str # Class name
Ty: mlirlite.LLVMType* # LLVM StructType
FieldCount: int # Field count
Fields: FieldEntry* # Field array (FIELD_MAX slots)
IsUnion: int # 1=union / 0=normal struct
IsPacked: int # 1=@c.Attribute(t.attr.packed) packed struct (no padding)
Align: int # @c.Attribute(t.attr.aligned(N)) alignment in bytes (0=default)
IsOOP: int # 1=OOP struct (has methods) / 0=plain memory struct
HasInit: int # 1=has __init__ / 0=no
HasNew: int # 1=has __new__ / 0=no
HasVTable: int # 1=has vtable / 0=no vtable
IsNoVTable: int # 1=explicitly @t.NoVTable / 0=not marked
ParentName: str # Parent name (None=none; first parent for multiple inheritance)
ParentNames: sizet* # Multi-inheritance parent name array (str pointers; stdlib-deep-copied)
ParentCount: int # Parent count (0=none, 1=single, >1=multiple)
VTableMethodCount: int # Number of methods in the vtable
VTableMethods: str # Vtable method name array (VTableMethodCount str)
ModuleSha1: str # Module SHA1 defining this class (None=no SHA1)
MethodsEmitted: int # 1=method bodies translated / 0=not (Phase 1a mark_only; Phase B fills in)
SpecTriggerSha1: str # Trigger-file SHA1 for a generic specialization (None=not specialized).
# ModuleSha1 is the template sha1; reset_module_methods_emitted resets
# MethodsEmitted by trigger sha1 to avoid leftover 1 / undefined symbol.
IsForwardDecl: int # 1=forward-declaration placeholder (Ty is opaque named struct, FieldCount=0).
# The definition side later fills the same Ty in place and clears this flag.
# ============================================================
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
# ============================================================
# EnumMember - enum member (formerly HandlesEnum.EnumMember)
class EnumMember:
Name: str # 成员名(字符串)
Value: int64 # 成员的整数值
Ty: llvmlite.LLVMType* # 成员的 LLVM 类型(用于混用类型场景)
Name: str # Member name (string)
Value: int64 # Member integer value
Ty: mlirlite.LLVMType* # Member LLVM type (for mixed-type cases)
# ============================================================
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
# ============================================================
# EnumEntry - enum type entry (formerly HandlesEnum.EnumEntry)
novirtual class EnumEntry:
Name: str # 枚举类名
BaseTy: llvmlite.LLVMType* # 基准类型(所有成员类型中最大的)
MemberCount: int # 成员数量
Members: EnumMember | t.CPtr # 成员数组(ENUM_MEMBER_MAX 个槽位)
Name: str # Enum class name
BaseTy: mlirlite.LLVMType* # Base type (largest among member types)
MemberCount: int # Member count
Members: EnumMember* # Member array (ENUM_MEMBER_MAX slots)
# ============================================================
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
# ============================================================
# AsmOperand - inline-asm operand entry (formerly HandlesExprCall.AsmOperand)
class AsmOperand:
"""内联汇编操作数条目"""
Value: llvmlite.Value* # 输入值或输出 alloca
Constraint: str # 约束字符串 (如 "r", "=r")
IsOutput: int # 1=输出, 0=输入
"""Inline-asm operand entry"""
Value: mlirlite.Value* # Input value or output alloca
Constraint: str # Constraint string (e.g. "r", "=r")
IsOutput: int # 1=output, 0=input
# ============================================================
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
# ============================================================
# FuncEntry - function table entry (formerly HandlesExprCall.FuncEntry)
novirtual class FuncEntry:
"""函数表条目"""
"""Function table entry"""
Name: str
Func: llvmlite.Function*
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
DefaultCount: int # 默认值数量
ParamCount: int # 形参数量(不含 self)
# TypeSpec(S3 条目双写):Kind=TS_FUNC 的**整签名**(Ret/Params/ParamCount/IsVarArg
# 都在其内),由 update_global_func_spec 在形参 add_param 完成后用 NewTypeFunc 构造。
# PermPool 驻留 ⇒ 跨 1b reset 安全。单字段即可完整表达签名,与 VarEntry/FieldEntry
# 的 Spec 命名一致,避免参数数组重复。
Func: mlirlite.Function*
Defaults: t.CPtr # Defaults list node pointer (list[AST*]); None=no defaults
DefaultCount: int # Default count
ParamCount: int # Param count (excluding self)
# TypeSpec (dual-write since S3): the full signature of Kind=TS_FUNC (Ret/Params/...)
# built by update_global_func_spec after add_param; PermPool-resident, safe across 1b reset.
Spec: TSpec.TypeSpec*
# ============================================================
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
# ============================================================
# FuncDefaultsEntry - default-argument entry (formerly HandlesExprCall.FuncDefaultsEntry)
novirtual class FuncDefaultsEntry:
"""默认参数表条目"""
"""Default-argument table entry"""
Name: str
ModuleSha1: str # 定义该函数的模块 SHA1(限定名去重,避免跨模块同名串味)
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
ModuleSha1: str # Module SHA1 defining this function (dedupes qualified names)
Defaults: t.CPtr # Defaults list node pointer (list[AST*]) (in pool, may be reset)
DefaultCount: int
ParamCount: int # 形参数量(不含 self)
# 常量快照(stdlib 分配,跨文件永久存活):
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
ConstKinds: int | t.CPtr # const_kind 数组(DefaultCount 个 i32)
ConstInts: int64 | t.CPtr # int_val 数组(DefaultCount 个 i64)
ConstStrs: sizet* # str_val 指针数组(DefaultCount 个 i8*)
ParamCount: int # Param count (excluding self)
# Constant snapshot (stdlib-allocated, permanent): extracted at registration so
# cross-module calls rebuild constant IR without touching a reset AST.
ConstKinds: int* # const_kind array (DefaultCount i32)
ConstInts: int64* # int_val array (DefaultCount i64)
ConstStrs: sizet* # str_val pointer array (DefaultCount i8*)
# ============================================================
# REPL 会话(M2/M3):ctx 缓冲区布局 + 会话表条目 + 结果区头
#
# ctx 缓冲区由宿主(lib/core/Repl.vp)在 PermPool 一次性分配,片段函数以首形参
# 接收(注解 bytes ⇒ i8*),同一进程内宿主与翻译器共享同一块内存。
#
# 布局(用户拍板子约定;字节全小端,无符号/有符号按位模式):
#
# 偏移 0 magic(u32 = 'REPL' = 0x5245504C) 4 version(u32 = 2)
# 偏移 8 result_kind(i32) 12 保留(0)
# 偏移 16 result_a(u64) 24 result_b(u64)
# 偏移 32 var_count(i32) 36 last_frag(i32)
# 偏移 40..63 保留(0)
# 偏移 64 + i*16 第 i 个会话变量的槽(步长 16 字节),i < REPL_VAR_CAP
# 偏移 1088 + j*16 第 j 个会话函数的指针槽(步长 16 字节),j < REPL_FUNC_CAP
#
# 版本与向后兼容(version 1 → 2,**纯尾部追加**):
# v1 = 头 64B + 变量槽区 1024B(共 1088B);v2 在 1088 之后追加函数指针槽区
# (REPL_FUNC_CAP*REPL_FUNC_SLOT_SIZE)。前 1088 字节的字段偏移/宽度/语义逐字
# 未变 ⇒ 按 v1 生成的旧片段 DLL 与 v2 宿主混用仍然正确(旧片段只读写
# < 1088 的字节);新片段读函数指针槽时宿主必为 v2 ⇒ 无版本协商需求。
# 头 +4 写 2 仅作"含函数指针槽区"的显式标记(片段侧不校验该字段)。
#
# result_kind:
# 0 = none 本片段无结果(声明类片段)
# 1 = int/uint result_a = 值(补码位模式,宿主按有符号 64 位回显)
# 2 = float64 result_a = IEEE754 double 位模式(宿主重解释后回显)
# 3 = str/bytes result_a = 指针;result_b = 长度(0 = 以 NUL 结尾,宿主按
# C 字符串处理——片段侧不额外调 strlen,故长度未知时写 0)
# 4 = struct/其它 result_a = 值地址(结构体值装箱后的盒子地址 / 非字符串指针)
#
# 槽内存放形态(ReplVarEntry.Kind):
# SCALAR — 标量/指针(≤8 字节)的值直接存槽;
# BOX — 结构体值:片段侧 malloc 盒子(值存盒内),槽内存盒子指针。
# 槽步长 16 字节(结构体/字符串统一走指针 ⇒ 宿主无需知道类型);负载一律
# ≤8 字节且 8 字节对齐即合法(MemBuddy 用户指针实测 8 字节对齐)。
# 槽数上限 REPL_VAR_CAP ⇒ 变量区尺寸 = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE。
#
# 函数指针槽(M3):槽内是会话函数在**定义片段 DLL** 里的入口地址(void*,8 字节;
# DLL 常驻不卸载 ⇒ 地址在会话期内有效)。宿主在片段 DLL 加载成功后
# GetProcAddress(裸名) 取得地址并写入槽;后续片段的调用点从槽 load 地址后
# 间接调用 ⇒ 函数代码只有一份(在定义片段里),调用点不复制代码。
# ============================================================
REPL_CTX_MARKER: t.CDefine = 1397571916 # 宿主侧对象标记(自比对;非 ctx 头字段)
REPL_CTX_MAGIC: t.CDefine = 0x5245504C # 'REPL'(ctx 头 +0,小端字节序为 R E P L)
REPL_CTX_VERSION: t.CDefine = 2 # ctx 头 +4(1 → 2:尾部追加函数指针槽区)
REPL_SLOT_SIZE: t.CDefine = 16 # 每个会话变量占用字节数(步长)
REPL_SLOT_BASE: t.CDefine = 64 # 槽区起点 = 固定头字节数(= +40..63 保留之后)
REPL_CTX_PARAM: t.CDefine = "__repl_ctx__" # 包裹模板里 ctx 形参名(与 Repl.vp 同源)
REPL_VAR_CAP: t.CDefine = 64 # 会话变量上限(变量区 = 64 + 64*16 = 1088 字节)
# 会话函数指针槽区(M3):起点紧跟变量区,步长与变量槽一致(16B,低 8 字节存地址)
REPL_FUNC_BASE: t.CDefine = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
REPL_FUNC_SLOT_SIZE: t.CDefine = 16 # 每个会话函数占用字节数(步长)
REPL_FUNC_CAP: t.CDefine = 32 # 会话函数上限(函数区 = 32*16 = 512 字节)
# ctx 总字节数 = 64 + 64*16 + 32*16 = 1600(宿主 Repl._REPL_CTX_SIZE 必须一致)
REPL_CTX_SIZE: t.CDefine = 1600
# 会话函数的形参上限(签名 token 数组定长;超出 ⇒ 注册期明确报错)
REPL_FUNC_PARAM_MAX: t.CDefine = 8
# 片段入口函数名前缀(Repl.vp 生成 "__repl_frag_%d";用于区分"片段入口函数"与
# 被提升的会话函数本体——前者是会话变量写入的作用域,后者只读会话槽)
REPL_ENTRY_PREFIX: t.CDefine = "__repl_frag_"
# 会话模块 SHA1(M4):片段翻译器的 ModuleSha1 取此**稳定**值 ⇒ 片段内定义的类型
# 名形如 "<16位hex>.<Type>",且**跨片段逐字一致**——这是"先前片段定义的类在后续
# 片段可见/可用"的基础(类型名是跨片段查找的唯一键)。
# 派生:sha1("TransPyV-REPL-Session").hexdigest()[:16]
# 得 71f67b7cb405f0e4(一次性计算,见 repl-design.md §M4 的可复算命令)。
# 形态:16 位小写 hex 与 includes 模块 SHA1 同形 ⇒ 被既有判定
# GenApi.is_sha1_hex16 / StructGen.find_struct 的"模块段即 SHA1"分支
# 直接当作模块限定名处理(无需新增解析规则)。
# 反例(为什么不给每个片段各设一个 sha1):类型名会随片段漂移,先前片段注册的
# 结构体/方法在后续片段的 find_struct_by_module 全部落空。
# 与裸名约定的关系:`export def`(含片段入口与会话函数)走 has_export 分支取裸名
# (FuncGenDef.vp:482-485),**不受**该值影响;受影响的只有类/方法/模块级全局
# 等经 _mangle_name/_mangle_name_with_sha1 命名的符号。
REPL_SESSION_SHA1: t.CDefine = "71f67b7cb405f0e4"
# REPL session (M2/M3): ctx buffer layout, session table entries, and result header.
define REPL_CTX_MARKER = 1397571916 # Host-side object marker (self-comparison; not a ctx header field)
define REPL_CTX_MAGIC = 0x5245504C # 'REPL' (ctx header +0, little-endian bytes R E P L)
define REPL_CTX_VERSION = 2 # ctx header +4 (1 -> 2: function pointer slot area appended at the tail)
define REPL_SLOT_SIZE = 16 # Bytes per session variable (stride)
define REPL_SLOT_BASE = 64 # Slot area start = fixed header bytes (after the +40..63 reserved area)
define REPL_CTX_PARAM = "__repl_ctx__" # Wrapper-template ctx parameter name (same source as Repl.vp)
define REPL_VAR_CAP = 64 # Max session variables (var area = 64 + 64*16 = 1088 bytes)
# Session function pointer slot area (M3): starts right after the var area, 16B stride (low 8 bytes hold the address)
define REPL_FUNC_BASE = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
define REPL_FUNC_SLOT_SIZE = 16 # Bytes per session function (stride)
define REPL_FUNC_CAP = 32 # Max session functions (func area = 32*16 = 512 bytes)
# Total ctx bytes = 64 + 64*16 + 32*16 = 1600 (host Repl._REPL_CTX_SIZE must match)
define REPL_CTX_SIZE = 1600
# Max session function params (fixed-size signature token array; overflow errors at registration)
define REPL_FUNC_PARAM_MAX = 8
# Fragment entry function name prefix (Repl.vp generates "__repl_frag_%d"); distinguishes
# the fragment entry function (writes session vars) from the hoisted session function body.
define REPL_ENTRY_PREFIX = "__repl_frag_"
# Session module SHA1 (M4): fragments use this stable value so type names are byte-identical
# across fragments, which is the key for classes defined earlier to be visible later.
define REPL_SESSION_SHA1 = "71f67b7cb405f0e4"
# ctx 头字段偏移(与 ReplResult 字段顺序/宽度一一对应)
REPL_RES_KIND_OFF: t.CDefine = 8 # i32
REPL_RES_A_OFF: t.CDefine = 16 # u64
REPL_RES_B_OFF: t.CDefine = 24 # u64
REPL_RES_VARCOUNT_OFF: t.CDefine = 32 # i32
REPL_RES_LASTFRAG_OFF: t.CDefine = 36 # i32
# ctx header field offsets (one-to-one with ReplResult field order/width)
define REPL_RES_KIND_OFF = 8 # i32
define REPL_RES_A_OFF = 16 # u64
define REPL_RES_B_OFF = 24 # u64
define REPL_RES_VARCOUNT_OFF = 32 # i32
define REPL_RES_LASTFRAG_OFF = 36 # i32
# 结果 Kind(宿主按此回显)
REPL_RES_NONE: t.CDefine = 0 # 本片段无结果(如仅声明)
REPL_RES_INT: t.CDefine = 1 # 整数(读 result_a 位模式)
REPL_RES_FLOAT: t.CDefine = 2 # float64(读 result_a 的 double 位模式)
REPL_RES_STR: t.CDefine = 3 # str/bytes(读 result_a 指针 + result_b 长度)
REPL_RES_OBJ: t.CDefine = 4 # 结构体/其它指针(读 result_a 地址)
# Result kind (the host echoes accordingly)
define REPL_RES_NONE = 0 # No result for this fragment (e.g. declaration only)
define REPL_RES_INT = 1 # Integer (read result_a bit pattern)
define REPL_RES_FLOAT = 2 # float64 (read result_a's double bit pattern)
define REPL_RES_STR = 3 # str/bytes (read result_a pointer + result_b length)
define REPL_RES_OBJ = 4 # Struct/other pointer (read result_a address)
# 会话变量类型标签(ReplVarEntry.TyTag)
REPL_TY_INT: t.CDefine = 1
REPL_TY_FLOAT: t.CDefine = 2
REPL_TY_STR: t.CDefine = 3 # i8*(str/bytes)
REPL_TY_PTR: t.CDefine = 4 # 其他指针(Bits=pointee 位宽 / TyName=结构体名)
REPL_TY_STRUCT: t.CDefine = 5 # 结构体值(按声明类型内联在槽里;≤ 槽容量)
# Session variable type tags (ReplVarEntry.TyTag)
define REPL_TY_INT = 1
define REPL_TY_FLOAT = 2
define REPL_TY_STR = 3 # i8* (str/bytes)
define REPL_TY_PTR = 4 # Other pointer (Bits=pointee width / TyName=struct name)
define REPL_TY_STRUCT = 5 # Struct value (inlined in the slot per declared type; <= slot capacity)
# 会话变量存放形态(ReplVarEntry.Kind)
REPL_KIND_UNTYPED: t.CDefine = 0 # 已登记槽位但尚未定型(未赋值/无注解)
REPL_KIND_SCALAR: t.CDefine = 1 # 值内联存槽(标量/指针/≤16B 结构体值)
# Session variable storage kind (ReplVarEntry.Kind)
define REPL_KIND_UNTYPED = 0 # Slot registered but not yet typed (unassigned / no annotation)
define REPL_KIND_SCALAR = 1 # Value inlined in the slot (scalar/pointer/<=16B struct value)
# 会话函数签名的返回类型 token(M3;只用于 ReplFuncEntry.RetTag)
REPL_TY_VOID: t.CDefine = 0 # 无返回值(void;不作为形参 token)
# Session function signature return-type token (M3; used only for ReplFuncEntry.RetTag)
define REPL_TY_VOID = 0 # No return value (void; not used as a param token)
# ============================================================
# ReplVarEntry - REPL 会话变量条目(跨片段存活,表本体在 PermPool)
# ============================================================
# ReplVarEntry - REPL session variable entry (survives across fragments; table in PermPool)
novirtual class ReplVarEntry:
"""REPL 会话变量条目:名字 + ctx 槽偏移 + 定型信息
Name/TyName 必须是 PermPool 深拷贝——宿主的 AST 名字串属于片段 pool,
行末 reset_to 后即失效,直接存指针会读到已回收内存。
"""
Name: str # 变量名(PermPool 深拷贝,NUL 结尾)
TyName: str # 结构体名(TyTag=PTR 且 pointee 为结构体 / TyTag=STRUCT);否则 None
Offset: int # ctx 内槽偏移(REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
"""REPL session variable entry: name + ctx slot offset + type info"""
Name: str # Variable name (PermPool deep copy, NUL-terminated)
TyName: str # Struct name (TyTag=PTR with struct pointee / TyTag=STRUCT); else None
Offset: int # Slot offset within ctx (REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
Kind: int # REPL_KIND_*
TyTag: int # REPL_TY_*
Bits: int # 标量位宽;TyTag=PTR 时是 pointee 位宽(0=未知)
Bits: int # Scalar bit width; for TyTag=PTR the pointee width (0=unknown)
# ============================================================
# ReplFuncEntry - REPL 会话函数条目(M3;跨片段存活,表本体在 PermPool)
#
# 语义:片段 N 里 `def f(...)` 被提升为片段模块里的**顶层 export def**(裸名导出),
# 该 DLL 加载后宿主 GetProcAddress 取到地址并写入 ctx 的函数指针槽;本条目记录
# "名字 → 符号名 + 定义片段号 + 槽偏移 + 签名 token",后续片段的调用点据此
# 从槽 load 地址并间接调用(与 M2 的会话变量槽同理:单一真相 + 间接寻址)。
#
# 签名 token(ParamTags/ParamBits 每形参 1 字节、RetTag/RetBits)与来源:
# 定义片段翻译出的 IR 里 `define <ret> @f(i8* %__repl_ctx__, <P1> %p1, ...)`
# 是 ABI 的唯一真相(注解 → LLVM 类型由 codegen 解析),宿主在注册前解析该
# define 行并**只接受**可往返重建的 token 集合:
# REPL_TY_INT(bits=1/8/16/32/64) / REPL_TY_FLOAT(bits=32/64) / REPL_TY_STR(i8*)
# 其余类型(结构体值、数组、多级指针等)⇒ 注册期明确报错,绝不静默按 i32 兜底。
# ============================================================
# ReplFuncEntry - REPL session function entry (M3; survives fragments, in PermPool).
# Maps name -> symbol + fragment no + slot offset + signature token for indirect calls.
novirtual class ReplFuncEntry:
"""REPL 会话函数条目:名字 + 导出符号名 + ctx 函数指针槽 + 签名 token"""
Name: str # 会话函数名(= 用户写的裸名;PermPool 深拷贝,NUL 结尾)
SymName: str # 片段 DLL 里的导出符号名(PermPool 深拷贝;M3 = 裸名)
FragNo: int # 定义该函数的片段号(重定义时更新为最新片段号)
SlotOff: int # ctx 内函数指针槽偏移(REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
"""REPL session function entry: name + export symbol + ctx function-pointer slot + signature token"""
Name: str # Session function name (= the bare name the user wrote; PermPool copy)
SymName: str # Export symbol name in the fragment DLL (PermPool copy; M3 = bare name)
FragNo: int # Fragment number defining this function (updated on redefinition)
SlotOff: int # Function pointer slot offset within ctx (REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
RetBits: int # 对齐 ReplVarEntry.Bits 的口径(VOID 时为 0)
ParamCount: int # 形参数(**不含**注入的 ctx 首形参)
ParamTags: str # PermPool 字节数组(每形参 1 字节 tag,REPL_FUNC_PARAM_MAX 个)
ParamBits: str # PermPool 字节数组(每形参 1 字节 bits,REPL_FUNC_PARAM_MAX 个)
RetBits: int # Same measure as ReplVarEntry.Bits (0 for VOID)
ParamCount: int # Param count (NOT including the injected ctx first param)
ParamTags: str # PermPool byte array (1 tag per param, REPL_FUNC_PARAM_MAX entries)
ParamBits: str # PermPool byte array (1 bits byte per param, REPL_FUNC_PARAM_MAX entries)
# ============================================================
# ReplCtx - REPL 会话上下文(宿主建、翻译器读)
#
# 开关语义:trans.ReplCtx != None ⇒ REPL 模式(仅 Repl.vp 翻译片段前置位,
# 翻译后清空)。Gen 侧全部拦截点都以此字段为唯一守卫——普通编译路径下该字段
# 恒为 None,逐字走原路径。
# ============================================================
# ReplCtx - REPL session context (host-built, translator-read). trans.ReplCtx != None
# enables REPL mode and guards all Gen-side interception points; otherwise the old path is used.
novirtual class ReplCtx:
"""REPL 模式上下文(宿主持有;翻译器只读)"""
Marker: int # REPL_CTX_MARKER(防误传非 ReplCtx 对象)
Table: rawptr[ReplVarEntry] # 会话表数组(PermPool,容量 Cap)
Count: int # 已登记条目数(= 已分配槽数)
Cap: int # 容量(REPL_VAR_CAP)
CtxParam: str # 片段函数 ctx 形参名(PermPool 深拷贝)
SlotSize: int # 槽步长(REPL_SLOT_SIZE)
# 影子条目(宿主建、PermPool):仅供"裸表达式结果"做类型分类用,不占会话槽、
# 不进表(Count 不计入),Name 固定为 "<expr>" 以便错误消息可读。
Scratch: rawptr[ReplVarEntry]
# 宿主提供的永久池:会话表条目名/结构体名等**跨片段存活**的数据必须在此池
# 深拷贝(片段的 trans.Pool 行末 reset_to 即回收,直接存其指针会悬垂)。
"""REPL mode context (host-owned, translator-read-only)"""
Marker: int # REPL_CTX_MARKER (guards against a non-ReplCtx object)
Table: ReplVarEntry* # Session table array (PermPool, capacity Cap)
Count: int # Registered entry count (= allocated slots)
Cap: int # Capacity (REPL_VAR_CAP)
CtxParam: str # Fragment function ctx param name (PermPool copy)
SlotSize: int # Slot stride (REPL_SLOT_SIZE)
# Shadow entry (host-built, PermPool): only classifies bare-expression results; it
# occupies no session slot and is not counted; Name is fixed to "<expr>".
Scratch: ReplVarEntry*
# Permanent pool provided by the host: cross-fragment data (session names, struct
# names) must be deep-copied here, since a fragment's trans.Pool is reset each line.
Perm: memhub.MemBuddy*
# 会话函数表(M3;宿主写、Gen 只读):数组容量 FuncCap,已登记 FuncCount 条。
# 条目只在"定义片段 DLL 加载成功且 GetProcAddress 命中"后由宿主追加 ⇒ 片段
# 翻译期看到的表恒为"先前片段的已就绪函数"(本片段新定义的同名函数走直接调用)。
FuncTable: rawptr[ReplFuncEntry]
# Session function table (M3; host-writes, Gen-reads): capacity FuncCap, FuncCount
# registered. Entries are appended only after a fragment DLL loads and GetProcAddress hits.
FuncTable: ReplFuncEntry*
FuncCount: int
FuncCap: int # 容量(REPL_FUNC_CAP)
FuncCap: int # Capacity (REPL_FUNC_CAP)
# ============================================================
# ReplResult - ctx 头(宿主按字段读;片段按 REPL_RES_*_OFF 字节偏移写)
#
# 字段顺序/宽度即 ctx 布局:Magic(+0,i32) Version(+4,i32) Kind(+8,i32)
# Reserved0(+12,i32) A(+16,i64) B(+24,i64) VarCount(+32,i32) LastFrag(+36,i32)。
# 宿主另有"同一头的浮点视图"(把 +16 的 8 字节按 double 读回,见 Repl.vp)。
# ============================================================
# ReplResult - ctx header (host reads by field; a fragment writes by REPL_RES_*_OFF).
# Field order/width is the ctx layout: Magic, Version, Kind, Reserved0, A, B, VarCount, LastFrag.
novirtual class ReplResult:
"""ctx 固定头(前 64 字节中的前 40 字节;+40..63 为保留)"""
Magic: int # REPL_CTX_MAGIC('REPL')
"""Fixed ctx header (first 40 of the 64 header bytes; +40..63 reserved)"""
Magic: int # REPL_CTX_MAGIC ('REPL')
Version: int # REPL_CTX_VERSION
Kind: int # REPL_RES_*
Reserved0: int # 保留(0)
A: t.CInt64T # result_a
B: t.CInt64T # result_b
VarCount: int # 已分配槽数(宿主写,供检视)
LastFrag: int # 最近执行的片段号(宿主写,供检视)
Reserved0: int # Reserved (0)
A: int64 # result_a
B: int64 # result_b
VarCount: int # Allocated slot count (host-written, for inspection)
LastFrag: int # Most recent fragment number (host-written, for inspection)
+102 -108
View File
@@ -1,11 +1,10 @@
import string
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import llvmlite
import mlirlite
import lib.core.Gen.BaseGenMixin as BaseGenMixin
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
@@ -55,388 +54,385 @@ import lib.core.Gen.VarGenMixin as VarGenMixin
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
# ============================================================
# Generator - 生成器(双向引用组合架构)
# ============================================================
# Generator - generator (bidirectional-reference composition architecture)
novirtual class Generator:
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
"""Generator holding pointers to all GenMixin sub-objects (bidirectional composition)."""
# ---- 子对象指针(46 个,永久池) ----
BaseGen: rawptr[BaseGenMixin.BaseGenMixin]
TypeGenBase: rawptr[TypeGenBase.TypeGenBase]
TypeGen: rawptr[TypeGenMixin.TypeGenMixin]
TypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef]
TypeGenCType: rawptr[TypeGenCType.TypeGenCType]
TypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve]
TypeGenErr: rawptr[TypeGenErr.TypeGenErr]
StructGen: rawptr[StructGenMixin.StructGenMixin]
ExprGenBase: rawptr[ExprGenBase.ExprGenBase]
ExprGen: rawptr[ExprGenMixin.ExprGenMixin]
ExprGenConst: rawptr[ExprGenConst.ExprGenConst]
ExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr]
ExprGenOps: rawptr[ExprGenOps.ExprGenOps]
ExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef]
ExprGenName: rawptr[ExprGenName.ExprGenName]
ExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr]
ExprGenCls: rawptr[ExprGenCls.ExprGenCls]
CallGenBase: rawptr[CallGenBase.CallGenBase]
CallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin]
CallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit]
CallGenCtor: rawptr[CallGenCtor.CallGenCtor]
CallGenCall: rawptr[CallGenCall.CallGenCall]
CallGen: rawptr[CallGenMixin.CallGenMixin]
CallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch]
CallGenLib: rawptr[CallGenLib.CallGenLib]
CallGenVt: rawptr[CallGenVt.CallGenVt]
CallGenVa: rawptr[CallGenVa.CallGenVa]
CallGenProp: rawptr[CallGenProp.CallGenProp]
CallGenFd: rawptr[CallGenFd.CallGenFd]
FuncGenBase: rawptr[FuncGenBase.FuncGenBase]
FuncGenDef: rawptr[FuncGenDef.FuncGenDef]
FuncGen: rawptr[FuncGenMixin.FuncGenMixin]
ClassGenBase: rawptr[ClassGenBase.ClassGenBase]
ClassGen: rawptr[ClassGenMixin.ClassGenMixin]
ClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum]
ClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric]
ClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable]
ClassGenDef: rawptr[ClassGenDef.ClassGenDef]
ImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin]
StmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin]
StmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin]
StmtGen: rawptr[StmtGenMixin.StmtGenMixin]
MatchGen: rawptr[MatchGenMixin.MatchGenMixin]
AssignGen: rawptr[AssignGenMixin.AssignGenMixin]
AsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin]
VarGen: rawptr[VarGenMixin.VarGenMixin]
TranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin]
# ---- sub-object pointers (46, permanent pool) ----
BaseGen: BaseGenMixin.BaseGenMixin*
TypeGenBase: TypeGenBase.TypeGenBase*
TypeGen: TypeGenMixin.TypeGenMixin*
TypeGenCDef: TypeGenCDef.TypeGenCDef*
TypeGenCType: TypeGenCType.TypeGenCType*
TypeGenResolve: TypeGenResolve.TypeGenResolve*
TypeGenErr: TypeGenErr.TypeGenErr*
StructGen: StructGenMixin.StructGenMixin*
ExprGenBase: ExprGenBase.ExprGenBase*
ExprGen: ExprGenMixin.ExprGenMixin*
ExprGenConst: ExprGenConst.ExprGenConst*
ExprGenFstr: ExprGenFstr.ExprGenFstr*
ExprGenOps: ExprGenOps.ExprGenOps*
ExprGenCdef: ExprGenCdef.ExprGenCdef*
ExprGenName: ExprGenName.ExprGenName*
ExprGenAttr: ExprGenAttr.ExprGenAttr*
ExprGenCls: ExprGenCls.ExprGenCls*
CallGenBase: CallGenBase.CallGenBase*
CallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin*
CallGenLeafKit: CallGenLeafKit.CallGenLeafKit*
CallGenCtor: CallGenCtor.CallGenCtor*
CallGenCall: CallGenCall.CallGenCall*
CallGen: CallGenMixin.CallGenMixin*
CallGenDispatch: CallGenDispatch.CallGenDispatch*
CallGenLib: CallGenLib.CallGenLib*
CallGenVt: CallGenVt.CallGenVt*
CallGenVa: CallGenVa.CallGenVa*
CallGenProp: CallGenProp.CallGenProp*
CallGenFd: CallGenFd.CallGenFd*
FuncGenBase: FuncGenBase.FuncGenBase*
FuncGenDef: FuncGenDef.FuncGenDef*
FuncGen: FuncGenMixin.FuncGenMixin*
ClassGenBase: ClassGenBase.ClassGenBase*
ClassGen: ClassGenMixin.ClassGenMixin*
ClassGenEnum: ClassGenEnum.ClassGenEnum*
ClassGenGeneric: ClassGenGeneric.ClassGenGeneric*
ClassGenVTable: ClassGenVTable.ClassGenVTable*
ClassGenDef: ClassGenDef.ClassGenDef*
ImportsGen: ImportsGenMixin.ImportsGenMixin*
StmtGenCif: StmtGenCifMixin.StmtGenCifMixin*
StmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin*
StmtGen: StmtGenMixin.StmtGenMixin*
MatchGen: MatchGenMixin.MatchGenMixin*
AssignGen: AssignGenMixin.AssignGenMixin*
AsyncGen: AsyncGenMixin.AsyncGenMixin*
VarGen: VarGenMixin.VarGenMixin*
TranslatorGen: TranslatorGenMixin.TranslatorGenMixin*
def __init__(self, pool: memhub.MemBuddy*):
if self is None:
return
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
SubBaseGen: rawptr[BaseGenMixin.BaseGenMixin] = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
if self is None: return
# ---- alloc 46 sub-objects + back-pointers (permanent pool, fail-fast) ----
SubBaseGen: BaseGenMixin.BaseGenMixin* = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
if SubBaseGen is None:
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
SubBaseGen.Gen = self
self.BaseGen = SubBaseGen
SubTypeGenBase: rawptr[TypeGenBase.TypeGenBase] = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
SubTypeGenBase: TypeGenBase.TypeGenBase* = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
if SubTypeGenBase is None:
stdio.printf("FAIL: TypeGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
SubTypeGenBase.Gen = self
self.TypeGenBase = SubTypeGenBase
SubTypeGen: rawptr[TypeGenMixin.TypeGenMixin] = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
SubTypeGen: TypeGenMixin.TypeGenMixin* = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
if SubTypeGen is None:
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
SubTypeGen.Gen = self
self.TypeGen = SubTypeGen
SubTypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef] = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
SubTypeGenCDef: TypeGenCDef.TypeGenCDef* = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
if SubTypeGenCDef is None:
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
SubTypeGenCDef.Gen = self
self.TypeGenCDef = SubTypeGenCDef
SubTypeGenCType: rawptr[TypeGenCType.TypeGenCType] = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
SubTypeGenCType: TypeGenCType.TypeGenCType* = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
if SubTypeGenCType is None:
stdio.printf("FAIL: TypeGenCType alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
SubTypeGenCType.Gen = self
self.TypeGenCType = SubTypeGenCType
SubTypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve] = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
SubTypeGenResolve: TypeGenResolve.TypeGenResolve* = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
if SubTypeGenResolve is None:
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
SubTypeGenResolve.Gen = self
self.TypeGenResolve = SubTypeGenResolve
SubTypeGenErr: rawptr[TypeGenErr.TypeGenErr] = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
SubTypeGenErr: TypeGenErr.TypeGenErr* = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
if SubTypeGenErr is None:
stdio.printf("FAIL: TypeGenErr alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
SubTypeGenErr.Gen = self
self.TypeGenErr = SubTypeGenErr
SubStructGen: rawptr[StructGenMixin.StructGenMixin] = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
SubStructGen: StructGenMixin.StructGenMixin* = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
if SubStructGen is None:
stdio.printf("FAIL: StructGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
SubStructGen.Gen = self
self.StructGen = SubStructGen
SubExprGenBase: rawptr[ExprGenBase.ExprGenBase] = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
SubExprGenBase: ExprGenBase.ExprGenBase* = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
if SubExprGenBase is None:
stdio.printf("FAIL: ExprGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
SubExprGenBase.Gen = self
self.ExprGenBase = SubExprGenBase
SubExprGen: rawptr[ExprGenMixin.ExprGenMixin] = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
SubExprGen: ExprGenMixin.ExprGenMixin* = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
if SubExprGen is None:
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
SubExprGen.Gen = self
self.ExprGen = SubExprGen
SubExprGenConst: rawptr[ExprGenConst.ExprGenConst] = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
SubExprGenConst: ExprGenConst.ExprGenConst* = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
if SubExprGenConst is None:
stdio.printf("FAIL: ExprGenConst alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
SubExprGenConst.Gen = self
self.ExprGenConst = SubExprGenConst
SubExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr] = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
SubExprGenFstr: ExprGenFstr.ExprGenFstr* = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
if SubExprGenFstr is None:
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
SubExprGenFstr.Gen = self
self.ExprGenFstr = SubExprGenFstr
SubExprGenOps: rawptr[ExprGenOps.ExprGenOps] = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
SubExprGenOps: ExprGenOps.ExprGenOps* = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
if SubExprGenOps is None:
stdio.printf("FAIL: ExprGenOps alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
SubExprGenOps.Gen = self
self.ExprGenOps = SubExprGenOps
SubExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef] = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
SubExprGenCdef: ExprGenCdef.ExprGenCdef* = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
if SubExprGenCdef is None:
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
SubExprGenCdef.Gen = self
self.ExprGenCdef = SubExprGenCdef
SubExprGenName: rawptr[ExprGenName.ExprGenName] = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
SubExprGenName: ExprGenName.ExprGenName* = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
if SubExprGenName is None:
stdio.printf("FAIL: ExprGenName alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
SubExprGenName.Gen = self
self.ExprGenName = SubExprGenName
SubExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr] = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
SubExprGenAttr: ExprGenAttr.ExprGenAttr* = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
if SubExprGenAttr is None:
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
SubExprGenAttr.Gen = self
self.ExprGenAttr = SubExprGenAttr
SubExprGenCls: rawptr[ExprGenCls.ExprGenCls] = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
SubExprGenCls: ExprGenCls.ExprGenCls* = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
if SubExprGenCls is None:
stdio.printf("FAIL: ExprGenCls alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
SubExprGenCls.Gen = self
self.ExprGenCls = SubExprGenCls
SubCallGenBase: rawptr[CallGenBase.CallGenBase] = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
SubCallGenBase: CallGenBase.CallGenBase* = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
if SubCallGenBase is None:
stdio.printf("FAIL: CallGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
SubCallGenBase.Gen = self
self.CallGenBase = SubCallGenBase
SubCallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin] = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
SubCallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin* = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
if SubCallGenCBuiltin is None:
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
SubCallGenCBuiltin.Gen = self
self.CallGenCBuiltin = SubCallGenCBuiltin
SubCallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit] = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
SubCallGenLeafKit: CallGenLeafKit.CallGenLeafKit* = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
if SubCallGenLeafKit is None:
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
SubCallGenLeafKit.Gen = self
self.CallGenLeafKit = SubCallGenLeafKit
SubCallGenCtor: rawptr[CallGenCtor.CallGenCtor] = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
SubCallGenCtor: CallGenCtor.CallGenCtor* = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
if SubCallGenCtor is None:
stdio.printf("FAIL: CallGenCtor alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
SubCallGenCtor.Gen = self
self.CallGenCtor = SubCallGenCtor
SubCallGenCall: rawptr[CallGenCall.CallGenCall] = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
SubCallGenCall: CallGenCall.CallGenCall* = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
if SubCallGenCall is None:
stdio.printf("FAIL: CallGenCall alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
SubCallGenCall.Gen = self
self.CallGenCall = SubCallGenCall
SubCallGen: rawptr[CallGenMixin.CallGenMixin] = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
SubCallGen: CallGenMixin.CallGenMixin* = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
if SubCallGen is None:
stdio.printf("FAIL: CallGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
SubCallGen.Gen = self
self.CallGen = SubCallGen
SubCallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch] = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
SubCallGenDispatch: CallGenDispatch.CallGenDispatch* = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
if SubCallGenDispatch is None:
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
SubCallGenDispatch.Gen = self
self.CallGenDispatch = SubCallGenDispatch
SubCallGenLib: rawptr[CallGenLib.CallGenLib] = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
SubCallGenLib: CallGenLib.CallGenLib* = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
if SubCallGenLib is None:
stdio.printf("FAIL: CallGenLib alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
SubCallGenLib.Gen = self
self.CallGenLib = SubCallGenLib
SubCallGenVt: rawptr[CallGenVt.CallGenVt] = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
SubCallGenVt: CallGenVt.CallGenVt* = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
if SubCallGenVt is None:
stdio.printf("FAIL: CallGenVt alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
SubCallGenVt.Gen = self
self.CallGenVt = SubCallGenVt
SubCallGenVa: rawptr[CallGenVa.CallGenVa] = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
SubCallGenVa: CallGenVa.CallGenVa* = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
if SubCallGenVa is None:
stdio.printf("FAIL: CallGenVa alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
SubCallGenVa.Gen = self
self.CallGenVa = SubCallGenVa
SubCallGenProp: rawptr[CallGenProp.CallGenProp] = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
SubCallGenProp: CallGenProp.CallGenProp* = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
if SubCallGenProp is None:
stdio.printf("FAIL: CallGenProp alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
SubCallGenProp.Gen = self
self.CallGenProp = SubCallGenProp
SubCallGenFd: rawptr[CallGenFd.CallGenFd] = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
SubCallGenFd: CallGenFd.CallGenFd* = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
if SubCallGenFd is None:
stdio.printf("FAIL: CallGenFd alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
SubCallGenFd.Gen = self
self.CallGenFd = SubCallGenFd
SubFuncGenBase: rawptr[FuncGenBase.FuncGenBase] = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
SubFuncGenBase: FuncGenBase.FuncGenBase* = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
if SubFuncGenBase is None:
stdio.printf("FAIL: FuncGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
SubFuncGenBase.Gen = self
self.FuncGenBase = SubFuncGenBase
SubFuncGenDef: rawptr[FuncGenDef.FuncGenDef] = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
SubFuncGenDef: FuncGenDef.FuncGenDef* = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
if SubFuncGenDef is None:
stdio.printf("FAIL: FuncGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
SubFuncGenDef.Gen = self
self.FuncGenDef = SubFuncGenDef
SubFuncGen: rawptr[FuncGenMixin.FuncGenMixin] = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
SubFuncGen: FuncGenMixin.FuncGenMixin* = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
if SubFuncGen is None:
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
SubFuncGen.Gen = self
self.FuncGen = SubFuncGen
SubClassGenBase: rawptr[ClassGenBase.ClassGenBase] = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
SubClassGenBase: ClassGenBase.ClassGenBase* = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
if SubClassGenBase is None:
stdio.printf("FAIL: ClassGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
SubClassGenBase.Gen = self
self.ClassGenBase = SubClassGenBase
SubClassGen: rawptr[ClassGenMixin.ClassGenMixin] = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
SubClassGen: ClassGenMixin.ClassGenMixin* = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
if SubClassGen is None:
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
SubClassGen.Gen = self
self.ClassGen = SubClassGen
SubClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum] = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
SubClassGenEnum: ClassGenEnum.ClassGenEnum* = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
if SubClassGenEnum is None:
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
SubClassGenEnum.Gen = self
self.ClassGenEnum = SubClassGenEnum
SubClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric] = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
SubClassGenGeneric: ClassGenGeneric.ClassGenGeneric* = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
if SubClassGenGeneric is None:
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
SubClassGenGeneric.Gen = self
self.ClassGenGeneric = SubClassGenGeneric
SubClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable] = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
SubClassGenVTable: ClassGenVTable.ClassGenVTable* = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
if SubClassGenVTable is None:
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
SubClassGenVTable.Gen = self
self.ClassGenVTable = SubClassGenVTable
SubClassGenDef: rawptr[ClassGenDef.ClassGenDef] = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
SubClassGenDef: ClassGenDef.ClassGenDef* = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
if SubClassGenDef is None:
stdio.printf("FAIL: ClassGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
SubClassGenDef.Gen = self
self.ClassGenDef = SubClassGenDef
SubImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin] = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
SubImportsGen: ImportsGenMixin.ImportsGenMixin* = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
if SubImportsGen is None:
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
SubImportsGen.Gen = self
self.ImportsGen = SubImportsGen
SubStmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
SubStmtGenCif: StmtGenCifMixin.StmtGenCifMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
if SubStmtGenCif is None:
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
SubStmtGenCif.Gen = self
self.StmtGenCif = SubStmtGenCif
SubStmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
SubStmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
if SubStmtGenDecl is None:
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
SubStmtGenDecl.Gen = self
self.StmtGenDecl = SubStmtGenDecl
SubStmtGen: rawptr[StmtGenMixin.StmtGenMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
SubStmtGen: StmtGenMixin.StmtGenMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
if SubStmtGen is None:
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
SubStmtGen.Gen = self
self.StmtGen = SubStmtGen
SubMatchGen: rawptr[MatchGenMixin.MatchGenMixin] = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
SubMatchGen: MatchGenMixin.MatchGenMixin* = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
if SubMatchGen is None:
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
SubMatchGen.Gen = self
self.MatchGen = SubMatchGen
SubAssignGen: rawptr[AssignGenMixin.AssignGenMixin] = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
SubAssignGen: AssignGenMixin.AssignGenMixin* = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
if SubAssignGen is None:
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
SubAssignGen.Gen = self
self.AssignGen = SubAssignGen
SubVarGen: rawptr[VarGenMixin.VarGenMixin] = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
SubVarGen: VarGenMixin.VarGenMixin* = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
if SubVarGen is None:
stdio.printf("FAIL: VarGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
SubVarGen.Gen = self
self.VarGen = SubVarGen
SubTranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin] = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
SubTranslatorGen: TranslatorGenMixin.TranslatorGenMixin* = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
if SubTranslatorGen is None:
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
SubTranslatorGen.Gen = self
self.TranslatorGen = SubTranslatorGen
SubAsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin] = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
SubAsyncGen: AsyncGenMixin.AsyncGenMixin* = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
if SubAsyncGen is None:
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
stdlib.exit(1)
@@ -444,7 +440,7 @@ novirtual class Generator:
SubAsyncGen.Gen = self
self.AsyncGen = SubAsyncGen
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
# ---- initialize sub-object fields (Init* methods called inline) ----
SubBaseGen.InitBaseGen(pool)
SubTypeGenBase.InitTypeGen()
SubCallGenBase.InitCallGen()
@@ -456,7 +452,5 @@ novirtual class Generator:
SubAssignGen.InitAssignGen()
# ============================================================
# 全局生成器单例(组合架构兼容层)
# ============================================================
# global generator singleton (composition-architecture compatibility layer)
_g_gen: Generator* = None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+226 -348
View File
@@ -1,8 +1,7 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import stdlib
@@ -17,531 +16,419 @@ import lib.core.Gen.GenApi as GenApi
# ============================================================
# StmtGenDeclMixin - StmtGen 拆分 2/3:声明/枚举/nonlocal 访问
# ============================================================
# StmtGenDeclMixin - StmtGen split 2/3: declaration/enum/nonlocal access
# ============================================================
# 常量
# ============================================================
# Constants
define ENUM_MAX = 64
define ENUM_MEMBER_MAX = 128
# ============================================================
# StmtGenDeclMixin - 声明/枚举/nonlocal(中层级)
# ============================================================
# StmtGenDeclMixin - declaration/enum/nonlocal (mid layer)
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
"""声明/枚举/nonlocal 访问(StmtGen 拆分 2/3,继承 Cif)"""
"""Declaration/enum/nonlocal access (StmtGen split 2/3, extends Cif)"""
def translate_global(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 global 语句:记录 global 变量名"""
gn: rawptr[ast.Global] = rawptr[ast.Global](node)
if gn is None:
return 0
def translate_global(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate a global statement and record the global variable names"""
gn: ast.Global* = ast.Global*(node)
if gn is None: return 0
names: list[ast.AST*]* = gn.names
if names is None:
return 0
if names is None: return 0
n: sizet = len(names)
for i in range(n):
for i in range(int(n)):
nm_node: ast.AST* = names.get(i)
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(nm_node)
if nm.id is not None:
GenApi.add_global_name(trans, nm.id)
if nm.id is not None: GenApi.add_global_name(trans, nm.id)
return 0
def translate_nonlocal(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 nonlocal 语句:记录 nonlocal 变量名及其类型
类型信息通过查找父作用域的 alloca 获取(用于 env 指针数组方案的类型恢复)。
"""
nl: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](node)
if nl is None:
return 0
def translate_nonlocal(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate a nonlocal statement and record names with their types"""
nl: ast.Nonlocal* = ast.Nonlocal*(node)
if nl is None: return 0
names: list[ast.AST*]* = nl.names
if names is None:
return 0
if names is None: return 0
n: sizet = len(names)
for i in range(n):
for i in range(int(n)):
nm_node: ast.AST* = names.get(i)
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(nm_node)
if nm.id is not None:
# 查找父作用域中的变量 alloca,获取类型信息
var_ty: llvmlite.LLVMType* = None
var_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
# Look up the variable alloca in the parent scope to get its type
var_ty: mlirlite.LLVMType* = None
var_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if var_alloca is not None and var_alloca.Ty is not None:
var_ty = var_alloca.Ty.Pointee
# Void 类型视为无效(Pointee 访问异常时可能返回 Void),
# 类型无效直接报错终止(fail-fast),避免生成
# "bitcast ... to void*" / "load void, void*" 非法 IR
# Treat Void as invalid (Pointee access can return Void); report and abort
# rather than emit illegal IR like "bitcast ... to void*" / "load void, void*".
if var_ty is not None:
match var_ty:
case llvmlite.LLVMType.Void():
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal 变量 '{nm.id}' 类型无效(Void/无法解析)", "TypeError")
case mlirlite.LLVMType.Void():
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal variable {nm.id} has an invalid type (Void/unresolvable)", "TypeError")
case _:
pass
GenApi.add_nonlocal_name(trans, nm.id, var_ty)
return 0
def translate_expr_stmt(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译表达式语句(printf 调用等)"""
ex: rawptr[ast.Expr] = rawptr[ast.Expr](node)
if ex is None:
return 0
def translate_expr_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate an expression statement (e.g. a printf call)"""
ex: ast.Expr* = ast.Expr*(node)
if ex is None: return 0
call_node: ast.AST* = ex.value
if call_node is None:
return 0
if call_node is None: return 0
ck: int = call_node.kind()
if ck != ast.ASTKind.Call:
# REPL 模式(M2):片段函数本体内的裸表达式语句(如 1+2 / x / x + 1)
# 求值后写入 ctx 结果区,由宿主回显。调用表达式仍走下方原路径
# (执行 + 丢弃返回值,同 print/printf 语义)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ "表达式语句除调用外一律忽略"
# 的原行为逐字不变。
if trans is not None and trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
self.Gen.VarGen.repl_expr_result(trans, call_node)
# REPL mode (M2): bare expression statements in a fragment body (e.g. 1+2 / x / x + 1)
# are written to the ctx result area. Guarded by trans.ReplCtx; non-REPL behavior unchanged.
if trans is not None and trans.ReplCtx is not None and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0: self.Gen.VarGen.repl_expr_result(trans, call_node)
return 0
cl: ast.Call* = ast.Call*(call_node)
func_node: ast.AST* = cl.func
if func_node is None:
return 0
if func_node is None: return 0
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
if func_name is None:
# 函数名解析失败:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(call_node, "表达式语句的函数名解析失败", "SyntaxError")
# Function name resolution failed: report and abort (fail-fast)
self.Gen.TypeGenErr.fatal_error(call_node, "failed to resolve the function name of the expression statement", "SyntaxError")
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
if string.strcmp(func_name, "printf") == 0:
self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
elif string.strcmp(func_name, "print") == 0:
self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
# printf / print take special paths (print is type-aware formatting, not mapped to printf)
if string.strcmp(func_name, "printf") == 0: self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
elif string.strcmp(func_name, "print") == 0: self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
else:
# 通用函数调用(返回值丢弃)
# M3:裸名调用**会话函数**(跨片段)时额外把返回值写进 ctx 结果区回显
# (M2 的限制条目"调用表达式的结果不回显"对会话函数放开——用户在
# `>>> f(41)` 后期望看到 `=> 42`)。结果区的类型分类直接用会话函数表里
# 记录的返回类型 token(宿主从定义片段 IR 解析而来,是定义端 ABI 真相),
# 不用影子条目推断 ⇒ 与调用点签名同源。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 原"执行 + 丢弃返回值"逐字不变;
# 仅裸名(Name)且表中登记的会话函数走本分支(printf/print 等在上方已分流)。
if trans is not None and trans.ReplCtx is not None \
and func_node.kind() == ast.ASTKind.Name \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
sfn_ent: rawptr[GenTypes.ReplFuncEntry] = \
self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
# Generic call (result discarded). M3: a bare-name call to a session function also
# writes the result to ctx (classified by the session table's return token).
if trans is not None and trans.ReplCtx is not None and func_node.kind() == ast.ASTKind.Name and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
sfn_ent: GenTypes.ReplFuncEntry* = self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
sfn_v: llvmlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node,
trans._funcs, trans._func_count, trans)
sfn_v: mlirlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
if sfn_v is not None:
ctx_v: llvmlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
ctx_v: mlirlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
if ctx_v is not None:
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits,
call_node)
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits, call_node)
return 0
self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
return 0
def pre_scan_allocas(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""预扫描语句中的 AnnAssign,提前创建 alloca
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c)
在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的
i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 →
跨函数 SSA 错乱,llc "instruction expected to be numbered")。
返回新增的变量数(调用方均忽略返回值)。
"""
if node is None:
return 0
def pre_scan_allocas(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Pre-scan AnnAssign statements and create their entry allocas early"""
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.AnnAssign:
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
if k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
if k == ast.ASTKind.If:
ifn: rawptr[ast.If] = rawptr[ast.If](node)
ifn: ast.If* = ast.If*(node)
if ifn.children is not None:
bn_pre_i: sizet = len(ifn.children)
for bi_pre_i in range(bn_pre_i):
self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
for bi_pre_i in range(int(bn_pre_i)): self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
if ifn.orelse is not None:
bn_pre_e: sizet = len(ifn.orelse)
for bi_pre_e in range(bn_pre_e):
self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
for bi_pre_e in range(int(bn_pre_e)): self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
return 0
if k == ast.ASTKind.For:
for_n: ast.For* = ast.For*(node)
if for_n.children is not None:
bn_pre_f: sizet = len(for_n.children)
for bi_pre_f in range(bn_pre_f):
self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
for bi_pre_f in range(int(bn_pre_f)): self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
if for_n.orelse is not None:
bn_pre_fe: sizet = len(for_n.orelse)
for bi_pre_fe in range(bn_pre_fe):
self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
for bi_pre_fe in range(int(bn_pre_fe)): self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
return 0
if k == ast.ASTKind.While:
wh_n: rawptr[ast.While] = rawptr[ast.While](node)
wh_n: ast.While* = ast.While*(node)
if wh_n.children is not None:
bn_pre_w: sizet = len(wh_n.children)
for bi_pre_w in range(bn_pre_w):
self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
for bi_pre_w in range(int(bn_pre_w)): self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
if wh_n.orelse is not None:
bn_pre_we: sizet = len(wh_n.orelse)
for bi_pre_we in range(bn_pre_we):
self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
for bi_pre_we in range(int(bn_pre_we)): self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
return 0
if k == ast.ASTKind.Try:
tr_n: ast.Try* = ast.Try*(node)
if tr_n.children is not None:
bn_pre_t: sizet = len(tr_n.children)
for bi_pre_t in range(bn_pre_t):
self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
for bi_pre_t in range(int(bn_pre_t)): self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
if tr_n.handlers is not None:
bn_pre_th: sizet = len(tr_n.handlers)
for bi_pre_th in range(bn_pre_th):
for bi_pre_th in range(int(bn_pre_th)):
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
if ehc_pre.children is not None:
bn_pre_ehb: sizet = len(ehc_pre.children)
for bi_pre_ehb in range(bn_pre_ehb):
self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
for bi_pre_ehb in range(int(bn_pre_ehb)): self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
if tr_n.orelse is not None:
bn_pre_to: sizet = len(tr_n.orelse)
for bi_pre_to in range(bn_pre_to):
self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
for bi_pre_to in range(int(bn_pre_to)): self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
if tr_n.finalbody is not None:
bn_pre_tf: sizet = len(tr_n.finalbody)
for bi_pre_tf in range(bn_pre_tf):
self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
for bi_pre_tf in range(int(bn_pre_tf)): self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
return 0
if k == ast.ASTKind.With:
wth: ast.With* = ast.With*(node)
if wth.children is not None:
bn_pre_wb: sizet = len(wth.children)
for bi_pre_wb in range(bn_pre_wb):
self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
for bi_pre_wb in range(int(bn_pre_wb)): self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
return 0
if k == ast.ASTKind.Match:
mt_n: ast.Match* = ast.Match*(node)
if mt_n.cases is not None:
bn_pre_mc: sizet = len(mt_n.cases)
for bi_pre_mc in range(bn_pre_mc):
for bi_pre_mc in range(int(bn_pre_mc)):
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
mcc_pre: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_pre)
mcc_pre: ast.MatchCase* = ast.MatchCase*(mc_pre)
if mcc_pre.children is not None:
bn_pre_mcb: sizet = len(mcc_pre.children)
for bi_pre_mcb in range(bn_pre_mcb):
self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
for bi_pre_mcb in range(int(bn_pre_mcb)): self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
return 0
return 0
def pre_scan_scope_decls(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""预扫描 Global/Nonlocal 声明,提前注册作用域名称"""
if node is None:
return 0
def pre_scan_scope_decls(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Pre-scan Global/Nonlocal declarations and register scope names early"""
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.Global:
return self.translate_global(trans, node)
if k == ast.ASTKind.Nonlocal:
return self.translate_nonlocal(trans, node)
if k == ast.ASTKind.Global: return self.translate_global(trans, node)
if k == ast.ASTKind.Nonlocal: return self.translate_nonlocal(trans, node)
return 0
def translate_break(self, trans: HT.Translator*) -> int:
"""翻译 break 语句:跳转到循环 end 块"""
builder: llvmlite.IRBuilder* = trans._cur_builder
func: llvmlite.Function* = trans._cur_func
def translate_break(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate a break statement: branch to the break target"""
builder: mlirlite.IRBuilder* = trans._cur_builder
func: mlirlite.Function* = trans._cur_func
pool: memhub.MemBuddy* = trans.Pool
if builder is None or func is None:
return 0
if builder is None or func is None: return 0
# 发射 br 到 break 目标
if trans._break_bb is not None:
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
llvmlite.build_br(builder, trans._break_bb)
# Emit br to the break target (_break_bb set by the loop / switch / match case side)
if trans._break_bb is None:
# fail-fast: with no loop / switch / match there is nowhere to jump; never silently no-op
self.Gen.TypeGenErr.fatal_error(node, "break is only allowed inside a loop or a switch/match case body (there is no target to break out of)", "SyntaxError")
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._break_bb)
# 创建死代码 BB,用于后续语句(break 后面的代码不可达)
# Create a dead-code BB for following statements (code after break is unreachable)
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
llvmlite.position_at_end(builder, dead_bb)
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
mlirlite.position_at_end(builder, dead_bb)
return 0
def translate_continue(self, trans: HT.Translator*) -> int:
"""翻译 continue 语句:跳转到循环 cond/incr 块"""
builder: llvmlite.IRBuilder* = trans._cur_builder
func: llvmlite.Function* = trans._cur_func
def translate_fallthrough(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate a fallthrough statement: emits no IR, consumed by the switch side"""
if trans is None: return 0
if trans._in_switch_case == 0:
self.Gen.TypeGenErr.fatal_error(node, "fallthrough is only allowed inside a switch case body (fallthrough is completely forbidden in match; " "the semantics are falling through to the next case)", "SyntaxError")
return 0
def translate_continue(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate a continue statement: branch to the loop cond/incr block"""
builder: mlirlite.IRBuilder* = trans._cur_builder
func: mlirlite.Function* = trans._cur_func
pool: memhub.MemBuddy* = trans.Pool
if builder is None or func is None:
return 0
if builder is None or func is None: return 0
# 发射 br 到 continue 目标
if trans._continue_bb is not None:
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
llvmlite.build_br(builder, trans._continue_bb)
# Emit br to the continue target
if trans._continue_bb is None:
# fail-fast: a continue outside a loop has nowhere to jump; never silently no-op
self.Gen.TypeGenErr.fatal_error(node, "continue is only allowed inside a loop body (there is no enclosing loop to continue)", "SyntaxError")
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._continue_bb)
# 创建死代码 BB,用于后续语句
# Create a dead-code BB for following statements
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
llvmlite.position_at_end(builder, dead_bb)
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
mlirlite.position_at_end(builder, dead_bb)
return 0
def get_stmt_kind_name(self, node: ast.AST*) -> str:
"""获取语句类型名"""
if node is None:
return None
"""Return the statement type name"""
if node is None: return None
return node.type_name()
# ============================================================
# 簇 D:nonlocal/闭包 env 访问(迁移自 HandlesNonlocal.vp)
# ============================================================
# Cluster D: nonlocal/closure env access (migrated from HandlesNonlocal.vp)
def _is_void_type(self, ty: llvmlite.LLVMType*) -> int:
"""判断是否为 Void 类型,是则返回 1,否则 0"""
if ty is None:
return 0
def _is_void_type(self, ty: mlirlite.LLVMType*) -> int:
"""Return 1 if the type is Void, else 0"""
if ty is None: return 0
match ty:
case llvmlite.LLVMType.Void():
case mlirlite.LLVMType.Void():
return 1
case _:
return 0
def get_env_ptr_var(self, trans: HT.Translator*) -> llvmlite.Value*:
"""获取或创建 _env_ptr 变量(存储闭包 env 指针)"""
env_var: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
if env_var is not None:
return env_var
# 创建 alloca 存储 env
def get_env_ptr_var(self, trans: HT.Translator*) -> mlirlite.Value*:
"""Get or create the _env_ptr variable holding the closure env pointer"""
env_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
if env_var is not None: return env_var
# Create an alloca to store env
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
alloca: llvmlite.Value* = llvmlite.build_alloca(builder, i8_ptr_ty)
if alloca is None:
return None
builder: mlirlite.IRBuilder* = trans._cur_builder
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
alloca: mlirlite.Value* = mlirlite.build_alloca(builder, i8_ptr_ty)
if alloca is None: return None
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
return alloca
def _load_raw_var_ptr(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""从闭包 env 加载 nonlocal 变量的原始指针(i8*)
返回 i8* 类型的 Value,调用方需 bitcast 为具体类型指针后 load/store。
"""
def _load_raw_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
"""Load the raw pointer (i8*) of a nonlocal variable from the closure env"""
idx: int = GenApi.get_nonlocal_index(trans, name)
if idx < 0:
return None
if idx < 0: return None
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
builder: mlirlite.IRBuilder* = trans._cur_builder
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
# 1. 加载 env_ptr
env_alloca: llvmlite.Value* = self.get_env_ptr_var(trans)
if env_alloca is None:
return None
env_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
if env_ptr is None:
return None
# 1. Load env_ptr
env_alloca: mlirlite.Value* = self.get_env_ptr_var(trans)
if env_alloca is None: return None
env_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, env_alloca)
if env_ptr is None: return None
# 2. GEP to offset (idx * 8) — env 存储指针数组,每槽 8 字节
offset_val: llvmlite.Value* = llvmlite.const_int32(pool, idx * 8)
addr: llvmlite.Value* = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
if addr is None:
return None
# 2. GEP to offset (idx * 8) - env is an array of pointers, 8 bytes per slot
offset_val: mlirlite.Value* = mlirlite.const_int32(pool, idx * 8)
addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, env_ptr, offset_val)
if addr is None: return None
# 3. Bitcast to i8** and load the raw pointer
ptr_addr: llvmlite.Value* = llvmlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
if ptr_addr is None:
return None
return llvmlite.build_load(builder, i8_ptr_ty, ptr_addr)
ptr_addr: mlirlite.Value* = mlirlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
if ptr_addr is None: return None
return mlirlite.build_load(builder, i8_ptr_ty, ptr_addr)
def load_nonlocal_var(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""从闭包 env 加载 nonlocal 变量值
通过 HT.get_nonlocal_type 获取变量类型,bitcast 原始指针后 load。
"""
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None:
return None
def load_nonlocal_var(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
"""Load the value of a nonlocal variable from the closure env"""
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None: return None
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
builder: mlirlite.IRBuilder* = trans._cur_builder
# 获取变量类型(从注册时存储的类型信息)
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
# Get the variable type (from the type info stored at registration)
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
if var_ty is None or self._is_void_type(var_ty) != 0:
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "load void, void*" 非法 IR
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
# fail-fast on a missing/invalid type, avoiding illegal IR like "load void, void*"
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
# bitcast raw i8* to var_ty*
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
var_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
if var_ptr is None:
return None
return llvmlite.build_load(builder, var_ty, var_ptr)
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
var_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
if var_ptr is None: return None
return mlirlite.build_load(builder, var_ty, var_ptr)
def get_nonlocal_var_ptr(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""获取 nonlocal 变量的存储指针(var_ty*),用于 store 操作
返回的指针指向父函数的 alloca,store 操作对父函数可见。
"""
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None:
return None
def get_nonlocal_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
"""Get the storage pointer (var_ty*) of a nonlocal variable for store operations"""
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None: return None
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
builder: mlirlite.IRBuilder* = trans._cur_builder
# 获取变量类型
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
# Get the variable type
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
if var_ty is None or self._is_void_type(var_ty) != 0:
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "bitcast ... to void*" 非法 IR
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
# Missing/invalid type aborts (fail-fast) to avoid illegal "bitcast ... to void*" IR
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
# bitcast raw i8* to var_ty*
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
return llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
return mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
# ============================================================
# 簇 E:枚举注册表(迁移自 HandlesEnum.vp)
# ============================================================
# Cluster E: enum registry (migrated from HandlesEnum.vp)
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
"""初始化枚举注册表,返回 1 成功
表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是
跨文件持久表,pool 分配会被 reset 回收导致 Phase B 读垃圾,
enum_count 出现 512 等垃圾值 / entry.Name 解引用崩溃)。
"""
if self.EnumTable is not None:
return 1
"""Initialize the enum registry; returns 1 on success"""
if self.EnumTable is not None: return 1
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
self.EnumTable = CompilerFactory.factory.PermPool.alloc(entry_size * ENUM_MAX)
if self.EnumTable is None:
return 0
if self.EnumTable is None: return 0
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
self.EnumCount = 0
return 1
def _get_enum_entry(self, i: int) -> rawptr[GenTypes.EnumEntry]:
"""获取第 i 个枚举条目"""
if self.EnumTable is None or i < 0 or i >= ENUM_MAX:
return None
def _get_enum_entry(self, i: int) -> GenTypes.EnumEntry*:
"""Get the i-th enum entry"""
if self.EnumTable is None or i < 0 or i >= ENUM_MAX: return None
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
addr: uint64 = uint64(self.EnumTable) + i * entry_size
return rawptr[GenTypes.EnumEntry](addr)
return GenTypes.EnumEntry*(addr)
def _get_enum_member(self, enum_entry: rawptr[GenTypes.EnumEntry],
i: int) -> rawptr[GenTypes.EnumMember]:
"""获取枚举中第 i 个成员条目"""
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX:
return None
def _get_enum_member(self, enum_entry: GenTypes.EnumEntry*, i: int) -> GenTypes.EnumMember*:
"""Get the i-th enum member entry"""
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX: return None
member_size: sizet = GenTypes.EnumMember.__sizeof__()
addr: uint64 = uint64(enum_entry.Members) + i * member_size
return rawptr[GenTypes.EnumMember](addr)
return GenTypes.EnumMember*(addr)
def register_enum(self, pool: memhub.MemBuddy*,
name: str,
base_ty: llvmlite.LLVMType*) -> rawptr[GenTypes.EnumEntry]:
"""注册枚举类型,返回 EnumEntry 指针"""
if self.init_enum_table(pool) == 0:
return None
def register_enum(self, pool: memhub.MemBuddy*, name: str, base_ty: mlirlite.LLVMType*) -> GenTypes.EnumEntry*:
"""Register an enum type and return its EnumEntry pointer"""
if self.init_enum_table(pool) == 0: return None
# 检查是否已注册
existing: rawptr[GenTypes.EnumEntry] = self.find_enum(name)
if existing is not None:
return existing
# Check whether it is already registered
existing: GenTypes.EnumEntry* = self.find_enum(name)
if existing is not None: return existing
if self.EnumCount >= ENUM_MAX:
VLogger.error(f"table full, cannot register {name}", "ENUM")
return None
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(self.EnumCount)
if entry is None:
return None
entry: GenTypes.EnumEntry* = self._get_enum_entry(self.EnumCount)
if entry is None: return None
# 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化)
# Allocate the member array (permanent pool: survives mark/reset like EnumTable)
member_size: sizet = GenTypes.EnumMember.__sizeof__()
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
if entry.Members is None:
return None
if entry.Members is None: return None
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
# 复制类名(永久池深拷贝)
# Copy the class name (permanent-pool deep copy)
name_len: sizet = string.strlen(name)
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if name_buf is not None:
string.strcpy(name_buf, name)
entry.Name = name_buf
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1 时 reset 回收悬空 → Phase B
# 枚举访问 ConstInt(悬空 BaseTy) 生成 void 值(llc 报错)。Phase1 1a(mark1 前)
# 保持 pool 存活,无需拷贝。
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接 self.StdlibAlloc
# (NoVTable 按本 Mixin 字段布局编译),走模块级委托。
# Deep-copy BaseTy: LLVMType is pool-allocated and would dangle after reset under
# StdlibAlloc=1. StdlibAlloc is a StructGenMixin field, so delegate via the module.
if self.Gen.StructGen.get_stdlib_alloc() != 0:
base_ty_copy: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
if base_ty_copy is not None:
entry.BaseTy = base_ty_copy
else:
entry.BaseTy = base_ty
else:
entry.BaseTy = base_ty
base_ty_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
if base_ty_copy is not None: entry.BaseTy = base_ty_copy
else: entry.BaseTy = base_ty
else: entry.BaseTy = base_ty
entry.MemberCount = 0
self.EnumCount += 1
return entry
def add_enum_member(self, pool: memhub.MemBuddy*,
enum_entry: rawptr[GenTypes.EnumEntry],
member_name: str,
member_val: int64,
member_ty: llvmlite.LLVMType*) -> int:
"""向枚举添加成员,返回成员索引(-1 失败)"""
if enum_entry is None or member_name is None or member_ty is None:
return -1
def add_enum_member(self, pool: memhub.MemBuddy*, enum_entry: GenTypes.EnumEntry*, member_name: str, member_val: int64, member_ty: mlirlite.LLVMType*) -> int:
"""Add a member to an enum; returns the member index or -1 on failure"""
if enum_entry is None or member_name is None or member_ty is None: return -1
# 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块
# 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT)
for ei_d in range(enum_entry.MemberCount):
ex_d: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, ei_d)
# Dedup: skip an existing member with the same name (cross-module enum merging, e.g.
# win32.fileio and fat32_types both define FRESULT).
for ei_d in range(int(enum_entry.MemberCount)):
ex_d: GenTypes.EnumMember* = self._get_enum_member(enum_entry, ei_d)
if ex_d is not None and ex_d.Name is not None:
if string.strcmp(ex_d.Name, member_name) == 0:
return ei_d
if string.strcmp(ex_d.Name, member_name) == 0: return ei_d
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
return -1
idx: int = enum_entry.MemberCount
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, idx)
if me is None:
return -1
me: GenTypes.EnumMember* = self._get_enum_member(enum_entry, idx)
if me is None: return -1
# 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活)
# Copy the member name (permanent-pool deep copy: the member table survives mark/reset)
name_len: sizet = string.strlen(member_name)
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if name_buf is not None:
@@ -554,43 +441,34 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
enum_entry.MemberCount = idx + 1
return idx
def find_enum(self, name: str) -> rawptr[GenTypes.EnumEntry]:
"""按枚举类名查找,返回 EnumEntry 或 None"""
if name is None or self.EnumTable is None:
return None
for i in range(self.EnumCount):
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(i)
def find_enum(self, name: str) -> GenTypes.EnumEntry*:
"""Look up an enum by class name; returns EnumEntry or None"""
if name is None or self.EnumTable is None: return None
for i in range(int(self.EnumCount)):
entry: GenTypes.EnumEntry* = self._get_enum_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
if string.strcmp(entry.Name, name) == 0: return entry
return None
def lookup_enum_member(self, enum_name: str,
member_name: str) -> rawptr[GenTypes.EnumMember]:
"""按枚举类名和成员名查找,返回 EnumMember 或 None"""
if enum_name is None or member_name is None:
return None
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
if entry is None:
return None
for mi in range(entry.MemberCount):
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(entry, mi)
def lookup_enum_member(self, enum_name: str, member_name: str) -> GenTypes.EnumMember*:
"""Look up a member by enum class name and member name; returns EnumMember or None"""
if enum_name is None or member_name is None: return None
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
if entry is None: return None
for mi in range(int(entry.MemberCount)):
me: GenTypes.EnumMember* = self._get_enum_member(entry, mi)
if me is not None and me.Name is not None:
if string.strcmp(me.Name, member_name) == 0:
return me
if string.strcmp(me.Name, member_name) == 0: return me
return None
def is_enum_class(self, name: str) -> int:
"""检查类名是否为已注册枚举,返回 1=是 / 0=否"""
if name is None:
return 0
if self.find_enum(name) is not None:
return 1
"""Return 1 if the class name is a registered enum, else 0"""
if name is None: return 0
if self.find_enum(name) is not None: return 1
return 0
def get_enum_base_type(self, enum_name: str) -> llvmlite.LLVMType*:
"""按枚举类名获取基准类型"""
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
if entry is not None:
return entry.BaseTy
def get_enum_base_type(self, enum_name: str) -> mlirlite.LLVMType*:
"""Get the base type for an enum class name"""
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
if entry is not None: return entry.BaseTy
return None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+86 -107
View File
@@ -1,132 +1,120 @@
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import hashtable
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
# TypeSpec 模块导入别名用 TSpec(模块内类名也叫 TypeSpec,别名同名会被解析器
# 当成"类 TypeSpec 的静态方法",导致跨模块调用被 mangled 成 <sha1>.TypeSpec.<fn>
# 并隐式补 self 实参 → undefined symbol)。
# Use alias TSpec for the TypeSpec module (its class is also named TypeSpec; a same-name alias
# would be parsed as a static method -> mangled cross-module call with implicit self -> undefined symbol).
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# TypeGenBase - 类型表基础设施:字段 + InitTypeGen + trace/临时目录/generic 上下文
# ============================================================
# TypeGenBase - type-table infrastructure: fields + InitTypeGen + trace/temp-dir/generic context
novirtual class TypeGenBase:
"""字段 + InitTypeGen + trace/临时目录/generic 上下文"""
"""Field storage, InitTypeGen, and trace/temp-dir/generic context helpers."""
# --- 仿 Python Traceback 调用栈(原 _g_trace_frames/_g_trace_count) ---
# --- Python-Traceback-like call stack (was _g_trace_frames/_g_trace_count) ---
Gen: Generator.Generator*
TraceFrames: rawptr[GenTypes.TraceFrame] # malloc 数组(TypeGenMixin.TRACE_MAX 槽)
TraceFrames: GenTypes.TraceFrame* # malloc array (TypeGenMixin.TRACE_MAX slots)
TraceCount: int
# --- 当前实参位置:翻译调用实参时记录。报错节点是**合成**的(lineno==0,如构造器内联出的
# 参数赋值)时,用它兜底定位到具体表达式(如 `&_on_submit`),而不是回退到函数定义行。---
CurArgFile: str
CurArgLine: int
CurArgCol: int
# --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) ---
# --- Global generic specialization context (was _g_generic_tp_names/_g_generic_type_args) ---
GenericTpNames: list[str]*
GenericTypeArgs: list[str]*
# 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出
# 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None →
# 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest
# 特化 Vector[int].__init__ 回归根因)。
GenericCtxStack: sizet* # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针)
# Nesting-safe stack: set_generic_context pushes the outer value, clear_generic_context pops it.
# (A single slot would clear the outer context to None on exit, losing T substitution -> NameError.)
GenericCtxStack: sizet* # fixed i64* array: 2 slots per level (tp_names, type_args pointers)
GenericCtxDepth: int
# --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) ---
# --- Global CDefine compile-time constant table (was _g_cdefine_*) ---
CDefineNames: list[str]*
CDefineValues: list[str]*
CDefineDoubleFlags: list[int] | t.CPtr
CDefineStrFlags: list[int] | t.CPtr # 1=字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2")
# 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。
# 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32:
# `define PI: float = 3.14159...` 必须直接出 f32 常量,否则与 f32 字面量混算会多出 fpext。
CDefineDoubleFlags: list[int]*
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
CDefineBits: list[int]*
CDefineIsDouble: int
CDefineIsStr: int
CDefineFound: int
# --- CDefine 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
# 与 CDefineNames 同池分配、同 clear_cdefine_constants 复位;CDefineNames 只
# 追加不重排 ⇒ 存槽下标稳定;Built == len(CDefineNames) 才可信,不可信/未命中
# 一律回退线性扫描(正确性保证)。
# --- CDefine name -> slot index (open addressing; slot stores index+1, 0=empty) ---
# Same pool/reset as CDefineNames; valid only when Built == len(CDefineNames), else fall back to linear scan.
CDefineNameIdx: sizet*
CDefineNameIdxCap: int
CDefineNameIdxBuilt: int
# --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) ---
# 缓存 reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用,
# CONST_INT 递归查找只做一次。
# --- Cross-module CDefine lookup cache (PermPool-allocated, reused across files) ---
# Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once.
XmodCdefNames: list[str]*
XmodCdefValues: list[str]*
XmodCdefDoubleFlags: list[int] | t.CPtr
XmodCdefStrFlags: list[int] | t.CPtr
# 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。
XmodCdefDoubleFlags: list[int]*
XmodCdefStrFlags: list[int]*
# --- 当前模块 SHA1(原 _g_current_module_sha1) ---
# --- Current module SHA1 (was _g_current_module_sha1) ---
CurrentModuleSha1: str
# --- 全局 CTypedef 表(原 _g_ctypedef_*) ---
# --- Global CTypedef table (was _g_ctypedef_*) ---
CTypedefNames: list[str]*
CTypedefTypeStrs: list[str]*
CTypedefFound: int
# CTypedef 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)。
# 与 CTypedefNames 同池分配、同 clear_ctypedefs 复位;CTypedefNames 只追加
# 不重排(同名注册原地更新类型串)⇒ 存槽下标稳定;Built == len(CTypedefNames)
# 才可信,不可信/未命中一律回退线性扫描(正确性保证)。
# CTypedef name -> slot index (open addressing; slot stores index+1, 0=empty).
# Same pool/reset as CTypedefNames; valid only when Built == len(CTypedefNames), else fall back to linear scan.
CTypedefNameIdx: sizet*
CTypedefNameIdxCap: int
CTypedefNameIdxBuilt: int
CTypedefResolveDepth: int
CTypedefVisiting: list[str]*
# builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef)
# 每文件最多读一次 includes/builtins.vp;随 clear_ctypedefs 复位
# builtins source scan flag: "last match" fallback for annotation resolution (_lookup_builtins_typedef).
# Reads includes/builtins.vp at most once per file; reset by clear_ctypedefs.
BuiltinsTypedefScanned: int
# builtins 最后匹配诊断("变量类型注解解析失败"详细报错用):
# LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult =
# 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。
# 两个被指串由以下自有缓冲承载(stdlib.malloc,clear_ctypedefs 释放):
# 调用方常传临时缓冲(VpiReader._resolve_single_vpi_type_str 的段缓冲
# malloc 后立刻 free),直接存指针会悬垂 → _build_ann_fail_msg 的
# snprintf 读已释放内存(ASan heap-use-after-free)。
# Builtins last-match diagnostics (for detailed "variable annotation resolve failed" errors):
# the two strings are held by their own buffers (stdlib.malloc, freed by clear_ctypedefs) because callers pass temporaries that would dangle (ASan use-after-free).
LastBuiltinsTypedefName: str
LastBuiltinsTypedefResult: str
LastBuiltinsTypedefNameBuf: bytes
LastBuiltinsTypedefResultBuf: bytes
# builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段):
# _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块限定
# 引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。
# 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空;malloc 跨 pool
# 生命周期存活,clear_ctypedefs 释放置空。
# builtins import alias buffer (stdlib.malloc, format "alias\0target\0").
# Filled by scanning builtins.vp import lines to restore real module names from aliases; malloc (not pool) so it survives per-file reset.
ImportAliasBuf: bytes
# --- 库 typedef 按模块一次性门禁(_lookup_library_typedef 性能优化) ---
# 库模块 typedef 缓存(PermPool 永久分配,跨文件/Phase 存活):
# LibTypedefScannedMods: key=库模块名 → 已扫描标记(命中=该模块源已读并入缓存);
# LibTypedefTypeMap: key="模块名|typedef 名" → 源中原始类型表达式。
# 每次查询都读 64KB 库源全文扫描会使单文件 translate 耗时 7–12s;库源单次编译内
# 不变,故缓存命中结果与逐次读源等价。
# --- Per-module one-shot gate for library typedefs (perf optimization) ---
# PermPool caches (across files/phases): LibTypedefScannedMods (key=module -> scanned) and LibTypedefTypeMap (key="module|typedef" -> raw type expr); scanning 64KB per query costs 7-12s.
LibTypedefScannedMods: hashtable.HashTable*
LibTypedefTypeMap: hashtable.HashTable*
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
# --- Const integer expression eval success flag (was _g_const_eval_ok) ---
ConstEvalOk: int
# --- 全局当前文件名(原 _g_current_filename) ---
# --- Global current filename (was _g_current_filename) ---
CurrentFile: str
# --- TypeSpec 驻留表(统一类型描述符,Debugger/_Type.md 第 6 节) ---
# 惰性创建(ensure_spec_arena);由类型子系统自身持有,避免模块级全局表。
# 实例与内部数组均 PermPool 分配 ⇒ 跨文件/跨 mark 存活。
# --- TypeSpec arena (unified type descriptors; Debugger/_Type.md section 6) ---
# Lazily created (ensure_spec_arena); instance and arrays are PermPool-allocated, so they survive across files/marks.
Specs: TSpec.TypeSpecArena*
def InitTypeGen(self) -> int:
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
"""Initialize the type mixin fields (inlined from subclass constructors)."""
if self is None: return 1
self.TraceFrames = None
self.TraceCount = 0
self.CurArgFile = None
self.CurArgLine = 0
self.CurArgCol = 0
self.GenericTpNames = None
self.GenericTypeArgs = None
self.GenericCtxStack = None
@@ -135,6 +123,7 @@ novirtual class TypeGenBase:
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
self.CDefineBits = None
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
self.CDefineFound = 0
@@ -160,7 +149,7 @@ novirtual class TypeGenBase:
self.LastBuiltinsTypedefNameBuf = None
self.LastBuiltinsTypedefResultBuf = None
self.ImportAliasBuf = None
# 库 typedef 持久缓存字段(不随 clear_ctypedefs 复位 → 跨文件/Phase 存活)
# Persistent library typedef cache fields (not reset by clear_ctypedefs, so they survive across files/phases)
self.LibTypedefScannedMods = None
self.LibTypedefTypeMap = None
self.ConstEvalOk = 0
@@ -168,14 +157,10 @@ novirtual class TypeGenBase:
self.Specs = None
return 0
# ============================================================
# ensure_spec_arena - 惰性创建 TypeSpec 驻留表(幂等)
#
# 用 PermPool 分配 arena 本体;表内部缓冲由 TSpec.SpecArenaInit 分配
# (同样 PermPool)。失败返回 None(调用方按 fail-fast 处理,不静默退化)。
# ============================================================
# ensure_spec_arena - lazily create the TypeSpec arena (idempotent).
# PermPool-allocated; returns None on failure (callers fail-fast, no silent degradation).
def ensure_spec_arena(self) -> TSpec.TypeSpecArena*:
"""确保 TypeSpec 驻留表已创建,返回 arena;失败返回 None"""
"""Ensure the TypeSpec arena exists and return it; None on failure."""
if self.Specs is not None: return self.Specs
size: sizet = TSpec.TypeSpecArena.__sizeof__()
arena: TSpec.TypeSpecArena* = TSpec.TypeSpecArena*(CompilerFactory.factory.PermPool.alloc(size))
@@ -184,30 +169,28 @@ novirtual class TypeGenBase:
if TSpec.SpecArenaInit(arena, 0) != 0: return None
self.Specs = arena
return arena
# ============================================================
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
# ============================================================
# P2 migrated methods (bodies from HandlesType.vp; globals -> self fields, same-module calls -> self methods).
# Signatures match the original HandlesType functions.
def _ensure_trace_init(self) -> int:
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
"""Ensure the trace stack is initialized (lazy, malloc-based)."""
if self.TraceFrames is not None: return 1
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
self.TraceFrames = CompilerFactory.factory.PermPool.alloc(entry_size * TypeGenMixin.TRACE_MAX)
if self.TraceFrames is None: return 0
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
return 1
def _get_trace_frame(self, i: int) -> rawptr[GenTypes.TraceFrame]:
"""获取第 i 个 TraceFrame 槽位"""
def _get_trace_frame(self, i: int) -> GenTypes.TraceFrame*:
"""Return the i-th TraceFrame slot."""
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX: return None
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
addr: uint64 = uint64(self.TraceFrames) + i * entry_size
return rawptr[GenTypes.TraceFrame](addr)
return GenTypes.TraceFrame*(addr)
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
"""Push a translation context frame."""
if self._ensure_trace_init() == 0: return
if self.TraceCount >= TypeGenMixin.TRACE_MAX: return
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
frame: GenTypes.TraceFrame* = self._get_trace_frame(self.TraceCount)
if frame is None: return
frame.file_name = file_name
frame.lineno = lineno
@@ -215,19 +198,25 @@ novirtual class TypeGenBase:
frame.col_end = col_end
self.TraceCount += 1
def pop_trace(self) -> None:
"""弹出栈顶帧(退出翻译作用域时调用)"""
"""Pop the top trace frame."""
if self.TraceCount <= 0: return
self.TraceCount -= 1
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
frame: GenTypes.TraceFrame* = self._get_trace_frame(self.TraceCount)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
# set_cur_arg - 记录正在翻译的实参位置(file/line/col),供“合成节点无行号”时兜底定位。
def set_cur_arg(self, file_name: str, lineno: int, col_start: int) -> None:
"""Remember the argument position currently being translated."""
self.CurArgFile = file_name
self.CurArgLine = lineno
self.CurArgCol = col_start
def clear_trace(self) -> None:
"""清空调用栈(每个文件翻译开始前调用)"""
for i in range(self.TraceCount):
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(i)
"""Clear the trace stack (called before translating each file)."""
for i in range(int(self.TraceCount)):
frame: GenTypes.TraceFrame* = self._get_trace_frame(i)
if frame is not None:
frame.file_name = None
frame.lineno = 0
@@ -235,25 +224,19 @@ novirtual class TypeGenBase:
frame.col_end = 0
self.TraceCount = 0
def set_temp_dir(self, temp_dir: str) -> None:
"""设置全局 temp_dir(统一写入 CallGenBase.TempDir,跨子对象经 Gen 指针)"""
"""Set the global temp_dir via CallGenBase.TempDir."""
self.Gen.CallGenBase.SetTempDir(temp_dir)
def get_temp_dir(self) -> str:
"""获取全局 temp_dir(从 CallGenBase 读取)"""
"""Get the global temp_dir from CallGenBase."""
return self.Gen.CallGenBase.TempDir
def set_current_module_sha1(self, sha1: str) -> None:
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
"""Set the SHA1 of the module currently being compiled."""
self.CurrentModuleSha1 = sha1
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*,
tree: ast.AST*, tag: str):
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*, tree: ast.AST*, tag: str):
"""Removed debug hook; signature kept as a placeholder for external callers."""
pass
def set_generic_context(self, tp_names: list[str]*,
type_args: list[str]*):
"""设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全
先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复,
避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。
"""
def set_generic_context(self, tp_names: list[str]*, type_args: list[str]*):
"""Push and set the generic specialization context (nesting-safe)."""
if self.GenericCtxStack is None:
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
if self.GenericCtxStack is None:
@@ -269,7 +252,7 @@ novirtual class TypeGenBase:
self.GenericTypeArgs = type_args
def clear_generic_context(self):
"""清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None"""
"""Pop the generic specialization context, restoring the outer one."""
if self.GenericCtxDepth > 0:
self.GenericCtxDepth -= 1
self.GenericTpNames = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2])
@@ -278,17 +261,13 @@ novirtual class TypeGenBase:
self.GenericTpNames = None
self.GenericTypeArgs = None
def resolve_generic_type_name(self, name: str) -> str:
"""如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身
用于表达式中的 T 替换(如 T.__sizeof__() → int.__sizeof__())
"""
if name is None:
return name
"""Return the concrete type name for a generic type parameter, else name itself."""
if name is None: return name
rv_d: str = name
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
rg_n: sizet = len(self.GenericTpNames)
rg_i: sizet
for rg_i in range(rg_n):
for rg_i in range(int(rg_n)):
rg_tpn: str = self.GenericTpNames.get(rg_i)
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
rg_ta: str = self.GenericTypeArgs.get(rg_i)
+200 -288
View File
@@ -1,48 +1,40 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
# ============================================================
# CDefine 名索引(名 → 槽下标,开放寻址 | 线性探测)
#
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
# 槽存槽下标 + 1(0=空槽)。CDefineNames 只追加不重排 ⇒ 槽下标稳定;
# Built == len(CDefineNames) 才可信。
# ============================================================
# CDefine name index (name -> slot index; open addressing with linear probing).
# 4096 power-of-two slots; slot stores index+1 (0=empty); valid only when Built == len(CDefineNames).
define CDEFINE_IDX_CAP = 4096
# 索引哈希常量(32 位 FNV-1a)
# Index hash constants (32-bit FNV-1a)
define CDEF_IDX_FNV_OFFSET = 2166136261
define CDEF_IDX_FNV_PRIME = 16777619
define CDEF_IDX_FNV_MASK = 4294967295
# CDefine 浮点常量的宽度种类(CDefineDoubleFlags / XmodCdefDoubleFlags 条目编码)。
# 1=f64(double,默认),2=f32(float);0 表示非浮点。既有读取处一律按 `!= 0` 判定
# "是浮点常量",故 f32 天然兼容;只有浮点常量的物化处需要区分 64/32。
# 必须写明 `: int`:无注解 define 按**值的最小可存宽度**物化(1/2 → i8),
# 直接当实参调用 `list[int].append(x: int)` 会落成 `i8 != i32`(mlir-opt 校验失败)。
define CDEFINE_FLOAT_KIND_F64: int = 1
define CDEFINE_FLOAT_KIND_F32: int = 2
# ============================================================
# TypeGenCDef - TypeGenMixin 拆分(阶段 D):CDefine 编译期常量表(register/lookup/is/set/clear)
# ============================================================
# TypeGenCDef - split from TypeGenMixin (stage D): CDefine compile-time constant table (register/lookup/is/set/clear)
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
"""CDefine 编译期常量表(register/lookup/is/set/clear)"""
"""CDefine compile-time constant table (register/lookup/is/set/clear)."""
# ============================================================
# CDefine 名索引(开放寻址;槽存 index+1,0=空槽)
# ============================================================
# CDefine name index (open addressing; slot stores index+1, 0=empty)
def _cdef_idx_hash(self, name: str) -> uint64:
"""名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
"""64-bit FNV-1a hash of the name, computed byte by byte until NUL."""
h: uint64 = uint64(CDEF_IDX_FNV_OFFSET)
if name is None:
return h
if name is None: return h
i: sizet = 0
while name[i] != '\0':
h = (h ^ uint64(int(name[i]))) * uint64(CDEF_IDX_FNV_PRIME)
@@ -51,11 +43,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return h
def _cdef_idx_slot(self, si: sizet) -> sizet*:
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
"""Return index slot si (slot stores index+1; 0 means empty)."""
return (sizet*)(uint64(self.CDefineNameIdx) + uint64(si) * 8)
def _cdef_idx_init(self, pool: memhub.MemBuddy*) -> None:
"""创建/清零名索引(与 CDefineNames 同池);失败则标记不可信"""
"""Create and zero the name index in the same pool; mark it untrusted on failure."""
idx: sizet* = pool.alloc(sizet(CDEFINE_IDX_CAP) * 8)
if idx is None:
self.CDefineNameIdx = None
@@ -68,118 +60,81 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
self.CDefineNameIdxBuilt = 0
def _cdef_idx_find(self, name: str) -> int:
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
return -1
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)):
return -1
"""Probe the name index for name; return the slot index, or -1 if missing/unusable."""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return -1
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)): return -1
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CDefineNameIdxCap:
for i in range(int(self.CDefineNameIdxCap)):
slot: sizet* = self._cdef_idx_slot(si)
sv: sizet = slot[0]
if sv == 0:
return -1
if sv == 0: return -1
se: int = int(sv) - 1
nm: str = self.CDefineNames.get(sizet(se))
if nm is not None and string.strcmp(nm, name) == 0:
return se
if nm is not None and string.strcmp(nm, name) == 0: return se
si = (si + 1) & mask
i += 1
return -1
def _cdef_idx_insert(self, slot_index: int, name: str) -> int:
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
return 1
"""Record name -> slot_index in the index; 0=success, 1=failure."""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return 1
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CDefineNameIdxCap:
for i in range(int(self.CDefineNameIdxCap)):
slot: sizet* = self._cdef_idx_slot(si)
if slot[0] == 0:
slot[0] = sizet(slot_index + 1)
return 0
si = (si + 1) & mask
i += 1
return 1
def _cdef_idx_sync_append(self, name: str) -> None:
"""CDefineNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
"""Sync the index after CDefineNames.append (slot = len-1); keep it untrusted if so."""
n: int = int(len(self.CDefineNames))
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0:
self.CDefineNameIdxBuilt = n
else:
self.CDefineNameIdxBuilt = -1
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0: self.CDefineNameIdxBuilt = n
else: self.CDefineNameIdxBuilt = -1
# ============================================================
# CDefine 表本体的池归一
#
# 表指针挂在**进程级单例** _g_gen.TypeGenCDef 上,不在任何 pool 的管辖
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
# (Phase1 1a/1b、Phase2 PhaseA、StubMerger 扫描、Repl 每片段都按 mark/reset
# 轮转)。表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None`
# 守卫失效 ⇒ 下一次 register_cdefine_* 的 append 写进已回收内存(堆损坏;
# 检测模式下该区被 free 填成 0xCD ⇒ append 的接收者 = 0xcdcdcdcdcdcdcdcd)。
# 故表本体(四个 list + 名索引)一律落 PermPool(进程级永久池,从不 reset),
# 与"文件级/片段级 reset"彻底解耦。
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
# ============================================================
def _cdefine_table_pool(self,
pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
"""CDefine 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
if CompilerFactory.factory is not None \
and CompilerFactory.factory.PermPool is not None:
return CompilerFactory.factory.PermPool
# Pool discipline for the CDefine table body.
# Body (4 lists + name index) always lives in PermPool so it survives per-file reset_to(mark); falls back to the passed pool if unavailable.
def _cdefine_table_pool(self, pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
"""Pool for the CDefine table body: PermPool, or the passed pool as a fallback."""
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
return pool
def register_cdefine_constant(self, pool: memhub.MemBuddy*,
name: str, value: int64) -> None:
"""注册 CDefine 编译期常量到本地表
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None:
"""Register an integer CDefine constant; store the value as a decimal string.
值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。
名称深拷贝到 pool(与 register_xmod_cdefine 对 key 深拷贝同一教训):
调用方传入的 name 可能指向临时缓冲——VpiReader 注入 .vpi 时 Name.id 是
零拷贝指向 vpi_buf(见 ParseVpiIntoModule 的 token 零拷贝注释),而
vpi_buf 在注入函数末尾即 stdlib.free。只存指针会让 CDefineNames 持有
悬空名:之后 lookup 的 strcmp 若命中被回收/复用内存里恰好出现的同名文本,
就会返回别的条目的值(Phase1.SHA1_LEN=17 被读成 0 的根因)。
bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。
"""
if name is None:
return
if name is None: return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
# 落到 None 或未初始化地址(原实现无检查 ⇒ append 写进 NULL/0xCD 区崩)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表分配失败(表本体池不可用或耗尽)", "MemoryError")
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
# Table build failed (pool unusable/exhausted): report and return, never let the
# append below hit None or an uninitialized address (would write NULL/0xCD).
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# 表就绪守卫:表本体缺失/半初始化(建表失败或外部置空)⇒ fail-fast,
# 不写垃圾(append 之前必有非空断言)
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
# (assert non-null before append).
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy: str = pool.alloc(string.strlen(name) + 1)
if name_copy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 名称缓冲区分配失败", "MemoryError")
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' name buffer allocation failed", "MemoryError")
return
string.strcpy(name_copy, name)
self.CDefineNames.append(name_copy)
@@ -188,105 +143,93 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if val_buf is not None:
viperlib.snprintf(val_buf, 32, "%lld", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
# fail-fast:CDefine 值缓冲区分配失败必须终止编译
# (与浮点/字符串分支口径一致,不再静默写 "0" 污染常量表)
if val_buf is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 值缓冲区分配失败", "MemoryError")
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
def register_cdefine_double(self, pool: memhub.MemBuddy*,
name: str, value: double) -> None:
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
else: self.CDefineValues.append("0")
# fail-fast: a failed CDefine value buffer must abort compilation
# (consistent with the double/string branches; do not silently write "0").
if val_buf is None: self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' value buffer allocation failed", "MemoryError")
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
if self.CDefineBits is not None: self.CDefineBits.append(bits)
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None:
"""Register a float CDefine constant; store the value as %f text and flag it.
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
名称同样深拷贝到 pool(理由见 register_cdefine_constant:注入 .vpi 时
Name.id 指向的 vpi_buf 会被释放,只存指针会留下悬空名)。
is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`),
物化时直接出 f32 常量;0(默认)表示 double。
"""
if name is None:
return
if name is None: return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表分配失败(表本体池不可用或耗尽)", "MemoryError")
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
if name_copy_d is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 浮点 '{name}' 名称缓冲区分配失败", "MemoryError")
self.Gen.TypeGenErr.fatal_error(None, f"CDefine float '{name}' name buffer allocation failed", "MemoryError")
return
string.strcpy(name_copy_d, name)
self.CDefineNames.append(name_copy_d)
self._cdef_idx_sync_append(name_copy_d)
val_buf: str = pool.alloc(32)
if val_buf is not None:
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
# %g 浮点路径有 bug(%.17g 输出错误值,如 pi 显示 3.1397985 而非 3.1415926,
# 位模式 %a 正确),会导致 CDefine 表存错文本 → lookup 生成错 LLVM 常量。
# Use CRT stdio.snprintf, not viperlib.snprintf: viperlib's %g float path is buggy
# (wrong %.17g, e.g. pi prints 3.1397985 not 3.1415926), which would corrupt CDefine text.
stdio.snprintf(val_buf, 32, "%.17g", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
else: self.CDefineValues.append("0")
# 浮点宽度种类:f32=2,f64=1(判定处一律 `!= 0`,故与旧数据兼容)。
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(1)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
# fail-fast:CDefine 值缓冲区分配失败必须终止编译,
if is_f32 != 0: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F32)
else: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F64)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
if self.CDefineBits is not None: self.CDefineBits.append(0)
# fail-fast: a failed CDefine value buffer must abort compilation
if val_buf is None:
VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG")
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError")
def register_cdefine_string(self, pool: memhub.MemBuddy*,
name: str, value: str) -> None:
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
与浮点 CDefine 的恢复同理)。名称同样深拷贝(理由见
register_cdefine_constant:注入 .vpi 的 Name.id 指向会被释放的 vpi_buf)。
"""
if pool is None or name is None:
return
VLogger.error("CDefine float value buffer allocation failed", "CDEF-DBG")
self.Gen.TypeGenErr.fatal_error(None, "CDefine float value buffer allocation failed", "MemoryError")
def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str) -> None:
"""Register a string CDefine constant, deep-copying name and value into pool."""
if pool is None or name is None: return
if value is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string value missing: {name}", "InternalCompilerError")
return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表分配失败(表本体池不可用或耗尽)", "MemoryError")
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
if ncopy_s is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串 '{name}' 名称缓冲区分配失败", "MemoryError")
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string '{name}' name buffer allocation failed", "MemoryError")
return
string.strcpy(ncopy_s, name)
self.CDefineNames.append(ncopy_s)
@@ -294,166 +237,145 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
vlen: sizet = string.strlen(value)
vcopy: str = pool.alloc(vlen + 1)
if vcopy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值分配失败: {name}", "MemoryError")
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string value allocation failed: {name}", "MemoryError")
return
string.strcpy(vcopy, value)
self.CDefineValues.append(vcopy)
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(1)
# 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。
# 跨模块字符串 CDefine 读回只调本函数 + 尾部 set_cdefine_found;不设标志会让
# 属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 → IR 生成 inttoptr i32 0
# → 运行期按 NULL 字符串处理崩溃。
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1)
if self.CDefineBits is not None: self.CDefineBits.append(0)
# Also set the "last result is a string" flag (read by is_cdefine_str()).
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
self.CDefineIsStr = 1
self.CDefineFound = 1
# ============================================================
# 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用)
# ============================================================
def register_xmod_cdefine(self, name: str, value_str: str,
is_double: int, is_str: int) -> None:
"""注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用)
# Cross-module CDefine lookup cache (PermPool-allocated, reused across files)
def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None:
"""Cache a cross-module CDefine lookup result in the perm_pool global cache.
Args:
name: CDefine 常量名(如 CONST_INT)
value_str: 值文本(十进制/浮点/字符串)
is_double: 1=浮点值
is_str: 1=字符串值
is_double 沿用 CDefineDoubleFlags 的编码(1=f64,2=f32,0=非浮点),
这样跨模块读取 float 声明的常量时也能保持 f32。
"""
if name is None or value_str is None:
return
# 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定)
if name is None or value_str is None: return
# Dedupe: skip if cached (first lookup wins; CDefines are globally unique)
if self.XmodCdefNames is not None:
x_n: sizet = len(self.XmodCdefNames)
x_i: sizet
for x_i in range(x_n):
for x_i in range(int(x_n)):
x_nm: str = self.XmodCdefNames.get(x_i)
if x_nm is not None and string.strcmp(x_nm, name) == 0:
return
if x_nm is not None and string.strcmp(x_nm, name) == 0: return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if self.XmodCdefNames is None:
self.XmodCdefNames = list[str](perm, 64)
self.XmodCdefValues = list[str](perm, 64)
self.XmodCdefDoubleFlags = list[int](perm, 64)
self.XmodCdefStrFlags = list[int](perm, 64)
# key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在
# reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活
# Deep-copy key/value into perm_pool: the inputs (pool.alloc temporaries) dangle after
# reset_to(mark2), so appending pointers alone would not survive the reset.
klen: sizet = string.strlen(name)
kcopy: str = perm.alloc(klen + 1)
if kcopy is None:
return
if kcopy is None: return
string.strcpy(kcopy, name)
vlen: sizet = string.strlen(value_str)
vcopy: str = perm.alloc(vlen + 1)
if vcopy is None:
return
if vcopy is None: return
string.strcpy(vcopy, value_str)
self.XmodCdefNames.append(kcopy)
self.XmodCdefValues.append(vcopy)
if self.XmodCdefDoubleFlags is not None:
self.XmodCdefDoubleFlags.append(is_double)
if self.XmodCdefStrFlags is not None:
self.XmodCdefStrFlags.append(is_str)
if self.XmodCdefDoubleFlags is not None: self.XmodCdefDoubleFlags.append(is_double)
if self.XmodCdefStrFlags is not None: self.XmodCdefStrFlags.append(is_str)
def lookup_xmod_cdefine(self, name: str) -> str:
"""查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志
Returns:
值文本(命中),未命中返回 None
"""
"""Look up the cross-module CDefine cache and set the found/is_double/is_str flags."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if name is None or self.XmodCdefNames is None:
return None
if name is None or self.XmodCdefNames is None: return None
n: sizet = len(self.XmodCdefNames)
i: sizet
for i in range(n):
for i in range(int(n)):
nm: str = self.XmodCdefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.XmodCdefValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1:
self.CDefineIsStr = 1
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1: self.CDefineIsStr = 1
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) != 0:
self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i))
return val_str
return None
def lookup_cdefine_constant(self, name: str) -> int64:
"""查找 CDefine 编译期常量
返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。
由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
调用方应在调用后检查 _g_cdefine_found。
浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。
"""
"""Look up an integer CDefine constant; 64-bit exact, found flag distinguishes -1."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0
if self.CDefineNames is None: return 0
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return 0
if cd_hit < 0: return 0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
# 字符串 CDefine(如 ZLIB_VERSION="1.3.2"):整数查询路径
# 标记 is_str,调用方改用 lookup_cdefine_string 取字符串。
# String CDefine (e.g. ZLIB_VERSION="1.3.2"): mark is_str on the int path;
# callers should use lookup_cdefine_string instead.
self.CDefineIsStr = 1
return 0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
# 浮点常量(f64=1 / f32=2):把种类原样带给 is_cdefine_double()/is_cdefine_float32()
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
return string.atoll(val_str)
return string.atoll(val_str)
return 0
def lookup_cdefine_double(self, name: str) -> double:
"""查找 CDefine 编译期常量,返回 double 值(浮点/整数统一)
def lookup_cdefine_bits(self, name: str) -> int:
"""Look up a CDefine constant's value-expression bit width; 0 = unknown (width inferred by value).
与 lookup_cdefine_constant 共享 _g_cdefine_found / _g_cdefine_is_double
(found=1 且 is_double=1 → 浮点 CDefine,应返回浮点常量)。
同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi
等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录,
于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。
"""
if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0
n: sizet = len(self.CDefineNames)
# 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值),
# 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录,
# 而不是"命中即返回"或"第一条非零"——否则早退的 stub 记录会盖掉真实位宽。
found: int = 0
for i in range(int(n)):
nm: str = self.CDefineNames.get(i)
if nm is None or string.strcmp(nm, name) != 0: continue
if sizet(i) >= len(self.CDefineBits): continue
b: int = self.CDefineBits.get(i)
if b != 0: found = b
return found
def lookup_cdefine_double(self, name: str) -> double:
"""Look up a CDefine constant and return its double value (float or int)."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0.0
if self.CDefineNames is None: return 0.0
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0.0(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return 0.0
if cd_hit < 0: return 0.0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs.
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
@@ -461,40 +383,32 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
self.CDefineIsStr = 1
return 0.0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
# 种类原样带回(1=f64 / 2=f32),物化处据此决定出 f64 还是 f32 常量。
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
ret_d: double = string.atof(val_str)
return ret_d
ret_i2: double = double(string.atoll(val_str))
return ret_i2
return 0.0
def lookup_cdefine_string(self, name: str) -> str:
"""查找 CDefine 编译期字符串值(found=1 且 is_str=1 时有效)
返回的字符串指针位于 pool 内(当前模块生命周期内有效),
调用方应尽快用于 create_global_string 拷贝到 LLVM 常量。
"""
"""Look up a string CDefine value (valid when found=1 and is_str=1)."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return None
if self.CDefineNames is None: return None
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 None(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return None
if cd_hit < 0: return None
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
@@ -506,42 +420,40 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return val_str
return None
def is_cdefine_str(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是字符串常量"""
"""Whether the last CDefine hit was a string constant."""
return self.CDefineIsStr
def is_cdefine_double(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是浮点常量"""
"""Whether the last CDefine hit was a float constant."""
return self.CDefineIsDouble
def is_cdefine_float32(self) -> int:
"""Whether the last CDefine hit was an f32 (`float`) constant; 0 = f64/非浮点。
物化浮点常量时必须先看本函数:`define PI: float = ...` 要直接出 f32 常量,
否则与 f32 字面量混算会被提升成 double(现场:33 处 cvtss2sd)。
"""
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return 1
return 0
def xmod_float_kind(self) -> int:
"""跨模块缓存写入用的浮点种类(1=f64 / 2=f32):把编码留在本模块,写入方不必跨模块引用 define。"""
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return CDEFINE_FLOAT_KIND_F32
return CDEFINE_FLOAT_KIND_F64
def is_cdefine_found(self) -> int:
"""检查上次 lookup_cdefine_constant 是否找到值"""
"""Whether the last CDefine lookup found a value."""
return self.CDefineFound
def set_cdefine_found(self, val: int) -> None:
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
"""Set the cdefine_found flag (used by cross-module lookup functions)."""
self.CDefineFound = val
def clear_cdefine_constants(self) -> None:
"""清空 CDefine 常量表(模块切换时调用)
表本体自首次创建起常驻 PermPool(见 _cdefine_table_pool)⇒ 此处只
**清空内容**、不丢弃表指针:
- 丢指针会退回"下一次 register_cdefine_* 按**调用方 pool** 重建",
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
初始化内存的根因);
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
register 首次 append 即写回第 0 槽。
"""
if self.CDefineNames is not None:
self.CDefineNames.clear()
if self.CDefineValues is not None:
self.CDefineValues.clear()
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.clear()
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.clear()
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
# 与"新建表后首次 register"的索引状态逐字一致)
"""Clear the CDefine table body in place, keeping the PermPool table pointers."""
if self.CDefineNames is not None: self.CDefineNames.clear()
if self.CDefineValues is not None: self.CDefineValues.clear()
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
if self.CDefineBits is not None: self.CDefineBits.clear()
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
# in sync, matching a freshly built table before its first register).
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
string.memset(self.CDefineNameIdx, 0,
sizet(self.CDefineNameIdxCap) * 8)
string.memset(self.CDefineNameIdx, 0, sizet(self.CDefineNameIdxCap) * 8)
self.CDefineNameIdxBuilt = 0
else:
self.CDefineNameIdx = None
File diff suppressed because it is too large. Load diff
+197 -364
View File
@@ -1,327 +1,209 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import mlirlite
import memhub
import string
import viperlib
import stdio
import stdlib
import sys
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Diag as Diag
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# TypeGenErr - 错误上报/源行读取/类型推断(fatal_error/infer_* 等)
# TypeGenErr - error reporting / source line reading / type inference (fatal_error, infer_*, ...)
# ============================================================
novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
"""错误上报/源行读取/类型推断(fatal_error/infer_* 等)"""
"""Error reporting / source-line reading / type inference (fatal_error/infer_* etc.)"""
def _read_source_line(self, file_path: str, lineno: int) -> str:
"""从源文件读取第 lineno 行内容"""
if file_path is None or lineno <= 0: return None
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f.closed: return None
# 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 →
# 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第
# 8390 行)。64MB 上限防御异常超大文件整体读取。
fsize: LONGLONG = f.size()
if fsize <= 0 or fsize > 67108864:
f.close()
return None
src_size: sizet = sizet(fsize)
buf: bytes = stdlib.malloc(src_size + 1)
if buf is None:
f.close()
return None
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
f.close()
if bytes_read <= 0:
stdlib.free(buf)
return None
# 逐行查找第 lineno 行
cur_line: int = 1
line_start: sizet = 0
for i in range(bytes_read):
if buf[i] == '\n':
if cur_line == lineno:
line_len: sizet = i - line_start
if line_len > 0 and buf[line_start + line_len - 1] == '\r':
line_len -= 1
line_buf: bytes = stdlib.malloc(line_len + 1)
if line_buf is not None:
string.strncpy(line_buf, buf + line_start, line_len)
line_buf[line_len] = '\0'
stdlib.free(buf)
return line_buf
stdlib.free(buf)
return None
cur_line += 1
line_start = i + 1
stdlib.free(buf)
return None
"""Read line lineno from the source file (delegates to the Diag module)"""
return Diag.read_source_line(file_path, lineno)
def read_source_line(self, file_path: str, lineno: int) -> str:
"""公开包装:从源文件读取第 lineno 行内容(供 traceback 展示)"""
"""Public wrapper: read line lineno from the source file for traceback display"""
return self._read_source_line(file_path, lineno)
def dump_parse_traceback(self, file_path: str) -> None:
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
err_line: int = ast.get_parse_err_line()
err_col: int = ast.get_parse_err_col()
err_msg: str = ast.get_parse_err_msg()
stdio.printf("Traceback (most recent call last, compile-time error):\n")
if file_path is None or file_path[0] == 0:
file_path = "<unknown>"
if err_line > 0:
stdio.printf(' File "%s", line %d\n', file_path, err_line)
src: str = self._read_source_line(file_path, err_line)
if src is not None:
stdio.printf(" %s\n", src)
# 列标记:在出错 token 起始列下方标 ^(parser 的 col_offset 是 0-based,
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
if err_col > 0:
stdio.printf(" ")
for _ in range(err_col):
stdio.printf(" ")
stdio.printf("^\n")
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', file_path)
if err_msg is None or err_msg[0] == 0:
err_msg = "语法错误"
stdio.printf("SyntaxError: %s\n", err_msg)
def fatal_error(self, node: ast.AST*, msg: str,
err_type: str = "SyntaxError",
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
"""On parse failure, print a Python-style traceback (delegates to the Diag module)"""
Diag.emit_parse(file_path, ast.get_parse_err_line(), ast.get_parse_err_col(), ast.get_parse_err_msg())
# 注:返回类型必须显式给一个**真实类型**(int),不能留空、也不能用 None。
# 本方法是"必定终止"的 fail-fast,从不返回;但留空/None 都会让跨模块 call site 解析出
# 退化类型 i0,命中 CallGenCall.vp 的 i0 守卫 → _fail_method_ret_ty("fatal_error") 中止编译。
# 给 int 后解析为 i32,守卫不再命中;因从不返回,该类型不会被真正使用。
def fatal_error(self, node: ast.AST*, msg: str, err_type: str = "SyntaxError", col_start: int = 0, col_end: int = 0, leak_file: str = None, leak_line: int = 0) -> int:
"""Report an error in Python style and exit (single diagnostic carrying its full call chain).
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
leak_line > 0 时额外打印一帧 `File lifetime leak "<file>", line N`(逃逸类错误的泄漏点)。
"""
stdio.printf("Traceback (most recent call last, compile-time error):\n")
# 输出调用栈帧(从底到顶)
for i in range(self.Gen.TypeGenBase.TraceCount):
frame: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(i)
if frame is not None and frame.file_name is not None:
if frame.lineno > 0:
stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno)
src: str = self._read_source_line(frame.file_name, frame.lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', frame.file_name)
# 输出当前帧(node 对应的位置)
# 当前帧(错误点)位置:优先 node,其次调用栈最内层有行号的帧,最后 current_file。
lineno: int = 0
if node is not None:
lineno = node.lineno
if node is not None: lineno = node.lineno
raw_line: int = lineno # [TEMP-DIAG] 兜底前的原始节点行号(定位完删除)
file_name: str = self.Gen.TypeGenResolve.get_current_file()
if file_name is None:
file_name = "<unknown>"
# 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的
# ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个
# 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。
if file_name is None: file_name = "<unknown>"
# 报错位置兜底(“节点位置不可信”时优先用正在翻译的实参位置,精确定位到
# `&_on_submit` 这类表达式;见 TypeGenBase.set_cur_arg)。判据取宽:节点无行号、
# 或**没有更深的可用位置**(节点行号不深于栈最内层帧的行号 —— 函数体里的真实
# 表达式行号必然大于函数定义行,而 FunctionDef 自身恰好等于定义行/更浅),
# 或栈为空(顶层函数体翻译时可能没有帧)时,都采用实参位置。
deeper_known: int = 0
if lineno > 0 and self.Gen.TypeGenBase.TraceCount > 0:
tfc: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
if tfc is not None and tfc.lineno > 0 and lineno > tfc.lineno: deeper_known = 1
if (lineno <= 0 or deeper_known == 0) and self.Gen.TypeGenBase.CurArgLine > 0:
if self.Gen.TypeGenBase.CurArgFile is not None:
file_name = self.Gen.TypeGenBase.CurArgFile
lineno = self.Gen.TypeGenBase.CurArgLine
col_start = self.Gen.TypeGenBase.CurArgCol
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
while ti_b >= 0:
tfb: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(ti_b)
tfb: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(ti_b)
if tfb is not None and tfb.file_name is not None and tfb.lineno > 0:
file_name = tfb.file_name
lineno = tfb.lineno
break
ti_b -= 1
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
src_line: str = None
line_ok: int = 1
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line = self._read_source_line(file_name, lineno)
if src_line is None:
line_ok = 0
if line_ok == 0:
stdio.printf(' File "%s", line %d (!! 无法读取该行源码:文件不存在或行号超出范围,错误可能来自被复用的 AST 树,请以错误消息为准)\n',
file_name, lineno)
else:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
if src_line is not None:
stdio.printf(" %s\n", src_line)
# 列范围标记:在源代码行下方用 ^ 标记出错范围
if col_start > 0 and col_end >= col_start:
stdio.printf(" ")
j: int
for j in range(col_end):
if j >= col_start - 1 and j < col_end:
stdio.printf("^")
else:
stdio.printf(" ")
stdio.printf("\n")
stdlib.free(src_line)
# 输出错误类型 + 消息
if err_type is None or err_type[0] == 0:
err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,直接终止进程会把整段
# traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
stdio.fflush(None)
# 终止点收敛:REPL 会话内经 VLogger.repl_abort longjmp 回 REPL 循环
# (Traceback 已打印,进程存活);批处理下该出口即 sys.exit(1),逐字不变。
VLogger.repl_abort()
def fatal_type_error(self, node: ast.AST*, msg: str,
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(TypeError 版本)"""
self.fatal_error(node, msg, "TypeError", col_start, col_end)
def try_extract_callable_ret(self, pool: memhub.MemBuddy*,
annot: ast.AST*,
imported_modules: str,
from_imports: str,
trans: HT.Translator*) -> llvmlite.LLVMType*:
"""提取函数指针注解(t.Callable[[A,B], R] / callable[[A,B], R])的返回类型
LLVMType;非 Callable 注解或无签名形态([]、[[]])返回 None"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return None
sub_cr: ast.Subscript* = ast.Subscript*(annot)
# 构造子判据走 TypeGenResolve 单一真源(Attribute 形态 t.Callable 与
# 裸名形态 callable / Callable 等价)
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0:
return None
# slice 必须是 Tuple(elts=[args_list, ret_node])
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple:
return None
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
if tp_cr.elts is None or len(tp_cr.elts) != 2:
return None
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
if ret_node_cr is None:
return None
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
def has_decorator_marker(self, node: ast.AST*,
marker_name: str,
depth: int = 0) -> int:
"""检测类型注解中是否包含指定的装饰器标记"""
if node is None or marker_name is None:
# 单条诊断(自带到错误的完整调用链);打印交给 Diag。
ent: Diag.DiagEntry* = Diag.new_entry(Diag.DIAG_MAX_FRAMES)
if ent is None:
# 极端情况(分配失败):退回单帧输出,保证错误不被静默吞掉。
Diag.emit_header()
Diag.emit_current(file_name, lineno, col_start, col_end)
if leak_line > 0: Diag.emit_leak_frame(leak_file, leak_line)
Diag.emit_category(err_type, msg)
Diag.finish()
return 0
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
if depth > 128:
self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
# [TEMP-DIAG] 定位“报错位置”问题用(定位完删除):把决策输入打出来
# node=节点原始行号(0=合成) curarg=当前实参行号 trace=栈深 top=栈最内层行号
ti_top: int = -1
if self.Gen.TypeGenBase.TraceCount > 0:
tf_d: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
if tf_d is not None: ti_top = tf_d.lineno
stdio.printf("[TEMP-DIAG] node=%d curarg=%d trace=%d top=%d -> shown=%d\n",
raw_line, self.Gen.TypeGenBase.CurArgLine, self.Gen.TypeGenBase.TraceCount, ti_top, lineno)
Diag.begin(ent, err_type, msg)
# 调用链帧(按"自外向内"顺序)**由本类遍历**(只有它拿得到 TraceFrame),填入 entry。
for i in range(int(self.Gen.TypeGenBase.TraceCount)):
frame: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(i)
if frame is not None and frame.file_name is not None:
Diag.add_frame(ent, frame.file_name, frame.lineno)
Diag.set_current(ent, file_name, lineno, col_start, col_end)
# 泄漏点帧(可选):逃逸类错误指出"值是从被调者哪一行漏出去的"
Diag.set_leak(ent, leak_file, leak_line)
# 默认"向上":最外层触发点在上、错误点最后(Python most recent call last)。
Diag.emit(ent, Diag.DIAG_DIR_UP)
Diag.free_entry(ent)
# Termination convergence: in REPL, longjmp back to the loop via VLogger.repl_abort (the
# traceback is printed and the process survives); in batch this exit is sys.exit(1) unchanged.
Diag.finish()
# 不可达:finish 要么 longjmp 回 REPL 循环、要么 sys.exit(1)。
# 显式 return 只为满足上面声明的 -> int 返回类型(避免"缺返回路径")。
return 0
# 同 fatal_error:跨模块调用需要**真实**返回类型,否则解析出 i0 会命中守卫中止编译。
def fatal_type_error(self, node: ast.AST*, msg: str, col_start: int = 0, col_end: int = 0) -> int:
"""Report an error in Python style and exit (TypeError variant)"""
return self.fatal_error(node, msg, "TypeError", col_start, col_end)
def try_extract_callable_ret(self, pool: memhub.MemBuddy*, annot: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator*) -> mlirlite.LLVMType*:
"""Extract the return type of a function-pointer annotation (t.Callable[[A,B], R]); None for non-Callable or signature-less forms."""
if annot is None or annot.kind() != ast.ASTKind.Subscript: return None
sub_cr: ast.Subscript* = ast.Subscript*(annot)
# The subscript test uses TypeGenResolve as the single source of truth (the t.Callable
# Attribute form is equivalent to the bare-name callable / Callable forms)
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0: return None
# slice must be Tuple(elts=[args_list, ret_node]); the one-element form
# `callable[[A, B]]` omits the return type and means the default int (i32) — the same
# degradation a missing annotation gets elsewhere. It must still register, otherwise the
# indirect call fails fast with "Return type ... is not registered".
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple: return None
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
if tp_cr.elts is None: return None
# The one-element form `callable[[A, B]]` omits the return type, which means **void**
# (no return value). It must still register, so the indirect call `f()` is statically
# typed as void and using it as a value (e.g. `v: int = f()`) is rejected instead of
# silently assuming int.
if len(tp_cr.elts) == 1: return mlirlite.Void(pool)
if len(tp_cr.elts) != 2: return None
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
if ret_node_cr is None: return None
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
def has_decorator_marker(self, node: ast.AST*, marker_name: str, depth: int = 0) -> int:
"""Check whether a type annotation contains the given decorator marker"""
if node is None or marker_name is None: return 0
# Recursion depth guard: vpi stub injection calls this for every function's returns, and
# deep vpi nodes may be corrupted into back-pointing cycles (VpiReader L810-813); fail fast.
if depth > 128: self.fatal_type_error(node, "decorator marker detection recursion too deep (AST cycle or nesting over 128 levels)")
k: int = node.kind()
# Attribute 节点: t.CExtern, t.State, t.CExport, t.CInline
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
if at.attr is not None:
if string.strcmp(at.attr, marker_name) == 0:
return 1
# Name node: a bare builtin marker (CExtern / State / CExport / CInline / CStatic / CConst ...)
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None and string.strcmp(nm.id, marker_name) == 0: return 1
return 0
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0:
return 1
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0:
return 1
# Subscript node: a marker wrapper `CConst[T]` / descriptor chain; the marker may be on this
# level or nested inside the wrapped type.
if k == ast.ASTKind.Subscript:
sub_m: ast.Subscript* = ast.Subscript*(node)
if sub_m.value is not None and sub_m.value.kind() == ast.ASTKind.Name:
mv: ast.Name* = ast.Name*(sub_m.value)
if mv.id is not None and string.strcmp(mv.id, marker_name) == 0: return 1
if sub_m.slice is not None:
return self.has_decorator_marker(sub_m.slice, marker_name, depth + 1)
return 0
return 0
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
"""检测指针注解 rawptr[...] 的下标实参是否含 t.CVolatile 标记,1=是 / 0=否
语义:rawptr[t.CVolatile | T] 等价 C 的 volatile T*,即"指针所指对象按
volatile 访问"——解引用(p[i] / p.field / c.Deref(p))的 load/store 带
LLVM volatile。与 has_decorator_marker 的区别:后者检测变量/字段**自身**
的可见性限定(如 `locked: t.CVolatile | t.CInt`),对 Subscript 节点返回 0;
本函数专检 rawptr 的 pointee 侧标记(下标实参子树),两者互不串味。
标注形态(vast.parse 实证):
rawptr[t.CVolatile | t.CInt]
→ Subscript(value=Name('rawptr'),
slice=BinOp(BitOr, Attribute(t,CVolatile), Attribute(t,CInt)))
多级指针 rawptr[rawptr[t.CVolatile|T]] 的 slice 是 Subscript,
has_decorator_marker 不递归 Subscript ⇒ 返回 0(明确不支持,见限制清单)。
"""
if node is None or node.kind() != ast.ASTKind.Subscript:
return 0
"""Whether a rawptr[...] subscript argument carries the t.CVolatile marker (pointee-side volatile), 1=yes / 0=no."""
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
sub: ast.Subscript* = ast.Subscript*(node)
if sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return 0
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
rp_nm: ast.Name* = ast.Name*(sub.value)
if rp_nm.id is None or string.strcmp(rp_nm.id, "rawptr") != 0:
return 0
if sub.slice is None:
return 0
if rp_nm.id is None: return 0
if string.strcmp(rp_nm.id, "rawptr") != 0 and string.strcmp(rp_nm.id, "CPtr") != 0: return 0
if sub.slice is None: return 0
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
def is_const_object_marker(self, node: ast.AST*,
var_ty: llvmlite.LLVMType*) -> int:
"""变量/字段本身是否 **const 对象**(1=是 / 0=否)。
判据与 TPC 的 BaseGen._IsConstObject **逐条一致**(见 Debugger/type-descriptors.md):
① 注解子树含 t.CConst(has_decorator_marker,与 TPC 的 _AnnotationHasQualifier 同口径);
② 声明类型的 LLVM 形态**不是指针**(TPC 用 PtrCount==0 表达同一件事)。
`t.CConst` 在本语言里承载「指向的数据 const」(对齐 TPC 的 DataConst 语义),
对非指针类型该限定退化为「该对象自身 const」⇒ 登记为 const 变量/字段;
对指针类型(`p: t.CConst | T | t.CPtr`)限定只作用于 pointee ⇒ 指针变量自身
仍可重新赋值,不登记。
"""
if self.has_decorator_marker(node, "CConst", 0) == 0:
return 0
if var_ty is None:
return 0
if var_ty.kind() == llvmlite.LLVMType.Ptr:
return 0
def is_const_object_marker(self, node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
"""Whether a variable/field is itself a const object (1=yes / 0=no): annotation carries t.CConst and the declared LLVM type is not a pointer."""
if self.has_decorator_marker(node, "CConst", 0) == 0: return 0
if var_ty is None: return 0
if var_ty.kind() == mlirlite.LLVMType.Ptr: return 0
return 1
def is_pass_body(self, body: list[ast.AST*]*) -> int:
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
if body is None:
return 1
"""Test whether a function body is pass (empty or only a pass statement)"""
if body is None: return 1
bn: sizet = len(body)
if bn == 0:
return 1
if bn == 0: return 1
if bn == 1:
stmt: ast.AST* = body.get(0)
if stmt is not None and stmt.kind() == ast.ASTKind.Pass:
return 1
if stmt is not None and stmt.kind() == ast.ASTKind.Pass: return 1
return 0
# 获取 LLVM 整数类型的位宽
# 实现已收拢到 TSpec.SpecTypeBits(该块在 TypeSpec 模块中的唯一实现),此处只做委托。
# 私有调用点(self.get_llvm_type_bits)保持原样不受影响。
def get_llvm_type_bits(self, ty: llvmlite.LLVMType*) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
# Get the LLVM integer type bit width. Delegates to TSpec.SpecTypeBits (the sole implementation
# in TypeSpec); private call sites (self.get_llvm_type_bits) are unaffected.
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
"""Get an LLVM integer type's bit width, 0 for non-integers (delegates to SpecTypeBits)"""
return TSpec.SpecTypeBits(ty)
def infer_expr_type(self, pool: memhub.MemBuddy*,
node: ast.AST*,
param_types: str) -> llvmlite.LLVMType*:
"""推断表达式的 LLVM 类型
param_types 格式: "name:bits,name:bits,..."
"""
if node is None:
return llvmlite.Int32(pool)
def infer_expr_type(self, pool: memhub.MemBuddy*, node: ast.AST*, param_types: str) -> mlirlite.LLVMType*:
"""Infer an expression's LLVM type; param_types format is "name:bits,name:bits,..."."""
if node is None: return mlirlite.Int32(pool)
k: int = node.kind()
if k == ast.ASTKind.Constant:
return llvmlite.Int32(pool)
if k == ast.ASTKind.Constant: return mlirlite.Int32(pool)
elif k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None and param_types is not None:
# 在 param_types 中查找: "name:bits" 格式
# 键按精确长度分配:固定 64 缓冲 + snprintf("%s:") 会静默截断超长参数名,
# 截断后的键可能与**别的参数前缀**匹配成功 ⇒ 取到错误的位宽(类型误判)。
# Look up "name:bits" in param_types. Allocate the key at exact length: a fixed 64
# buffer truncated long names and could match another param's prefix, giving wrong bits.
id_len: sizet = string.strlen(nm.id)
search_key: str = pool.alloc(id_len + 2)
if search_key is not None:
@@ -335,158 +217,109 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
bits: int = 0
for bi in range(4):
ch: char = bits_str[bi]
if ch >= '0' and ch <= '9':
bits = bits * 10 + (ch - '0')
else:
break
if bits == 64:
return llvmlite.Int64(pool)
elif bits == 16:
return llvmlite.Int16(pool)
elif bits == 8:
return llvmlite.Int8(pool)
return llvmlite.Int32(pool)
if ch >= '0' and ch <= '9': bits = bits * 10 + (ch - '0')
else: break
if bits == 64: return mlirlite.Int64(pool)
elif bits == 16: return mlirlite.Int16(pool)
elif bits == 8: return mlirlite.Int8(pool)
return mlirlite.Int32(pool)
elif k == ast.ASTKind.BinOp:
binop: ast.BinOp* = ast.BinOp*(node)
lhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
rhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
lhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
rhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
if lhs_bits >= rhs_bits:
return lhs_ty
if lhs_bits >= rhs_bits: return lhs_ty
return rhs_ty
return llvmlite.Int32(pool)
def infer_return_type(self, pool: memhub.MemBuddy*,
body: list[ast.AST*]*,
param_types: str) -> llvmlite.LLVMType*:
"""扫描函数体中的 return 语句,推断返回类型"""
if body is None:
return llvmlite.Int32(pool)
return mlirlite.Int32(pool)
def infer_return_type(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*, param_types: str) -> mlirlite.LLVMType*:
"""Scan a function body's return statements to infer the return type"""
if body is None: return mlirlite.Int32(pool)
ret_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
ret_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
bn: sizet = len(body)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
rt: ast.Return* = ast.Return*(stmt)
if rt.value is not None:
expr_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
expr_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
if expr_ty is not None:
cur_bits: int = self.get_llvm_type_bits(ret_ty)
new_bits: int = self.get_llvm_type_bits(expr_ty)
if new_bits > cur_bits:
ret_ty = expr_ty
if new_bits > cur_bits: ret_ty = expr_ty
return ret_ty
def body_has_value_return(self, pool: memhub.MemBuddy*,
body: list[ast.AST*]*) -> int:
"""扫描函数体,判断是否存在返回非 None 值的 return
递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过
嵌套函数/类定义(FunctionDef/ClassDef 由自身翻译时独立检查,避免外层
检查误伤嵌套作用域)。裸 return / return None(CONST_NONE)/ 无 return
均不算返回非 None 值。
供无返回类型注解函数的 fail-fast 检查使用:返回具体值必须有显式 ->
注解,编译器不替用户推断返回类型(推断位宽会掩盖返回契约)。
"""
if body is None:
return 0
def body_has_value_return(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*) -> int:
"""Whether the body has a return of a non-None value, recursively checking control-flow statements but skipping nested defs."""
if body is None: return 0
bn: sizet = len(body)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is None:
continue
if stmt is None: continue
k: int = stmt.kind()
if k == ast.ASTKind.Return:
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
rt: ast.Return* = ast.Return*(stmt)
if rt.value is not None:
# return None(CONST_NONE)不算返回非 None 值
# return None (CONST_NONE) does not count as returning a non-None value
if rt.value.kind() == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(rt.value)
if cn.const_kind == ast.CONST_NONE:
continue
if cn.const_kind == ast.CONST_NONE: continue
return 1
elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef:
continue
elif k == ast.ASTKind.If or k == ast.ASTKind.While or \
k == ast.ASTKind.For or k == ast.ASTKind.Try or \
k == ast.ASTKind.With or k == ast.ASTKind.Match:
elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef: continue
elif k == ast.ASTKind.If or k == ast.ASTKind.While or k == ast.ASTKind.For or k == ast.ASTKind.Try or k == ast.ASTKind.With or k == ast.ASTKind.Match:
sub: list[ast.AST*]* = stmt.children
if sub is not None and len(sub) > 0:
if self.body_has_value_return(pool, sub) != 0:
return 1
if self.body_has_value_return(pool, sub) != 0: return 1
return 0
def build_param_types_str(self, pool: memhub.MemBuddy*,
args_node: ast.AST*) -> str:
"""构建参数类型编码字符串 "name:bits,name:bits,..."
[D5·截断纪律] 旧实现按 `an * 32 + 1` 预估缓冲,再用 `_strappend` 以
`remain - 1` 截断且**不报错**:参数名偏长(或参数偏多)时编码串被静默
截断 ⇒ `infer_expr_type` 的 `strstr(param_types, "name:")` 查找失配 /
命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**上界预估 + 单遍就地
拼接**:段一是只扫参数名的上界(name + ':' + 位宽 + ',' + NUL),段二是
解析类型后逐条目就地格式化并立即拼接 —— 缓冲恒够,不存在截断;
`_strappend` 一并删除(唯一调用点在本函数)。
"""
if args_node is None:
return None
def build_param_types_str(self, pool: memhub.MemBuddy*, args_node: ast.AST*) -> str:
"""Build the param type encoding string "name:bits,name:bits,..." with upper-bound sizing and single-pass in-place concatenation."""
if args_node is None: return None
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is None:
return None
if ags.args is None: return None
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
if an == 0:
return None
if an == 0: return None
# 1) 上界估长(**只扫参数名**,不解析类型,也不暂存结果指针):
# 每条 = name + ':' + 位宽(int32 十进制最长 11 字符含符号)+ ',';再加 NUL。
# 必须"上界"而非"精确":把条目指针存进 sizet 数组再取出转回指针 strcat 会出错;
# 上界缓冲不需要数组往返,每条目就地格式化、立即拼接。
bound: sizet = 1 # 末尾 NUL
# 1) Upper-bound sizing (scan only param names, no type resolution): each entry is
# name + ':' + bits + ',', plus NUL. Use an upper bound, not an exact size.
bound: sizet = 1 # trailing NUL
pre_i: sizet
for pre_i in range(an):
for pre_i in range(int(an)):
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
if pre_arg is None or pre_arg.arg is None:
continue
if pre_arg is None or pre_arg.arg is None: continue
bound += string.strlen(pre_arg.arg) + 1 + 12 + 1
buf: str = pool.alloc(bound)
if buf is None:
return None
if buf is None: return None
buf[0] = '\0'
# 2) 单遍:解析类型 → 就地格式化条目 → 立即拼接(缓冲为上界,不会截断)
# 2) Single pass: resolve type -> format entry in place -> append immediately (no truncation)
cnt: sizet = 0
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
for ai in range(an):
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
for ai in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is None or arg.arg is None:
continue
param_ty: llvmlite.LLVMType* = i32_ty
if arg is None or arg.arg is None: continue
param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None:
# 统一口径:CTypedef 动态表优先,miss 回退 map_t_type
# (阶段 2:删除 map_t_type 硬编码别名后,BYTE 等靠库 typedef)
# Unified: prefer the CTypedef dynamic table, fall back to map_builtin_type
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
if arg.annotation.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(arg.annotation)
if at.attr is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
if resolved is not None:
param_ty = resolved
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
if resolved is not None: param_ty = resolved
bits: int = self.get_llvm_type_bits(param_ty)
if bits == 0:
bits = 32
if bits == 0: bits = 32
nlen: sizet = string.strlen(arg.arg)
# 条目缓冲:name + ':' + 位宽(预留 16 字节,足够 %d 任意取值)+ NUL
# Entry buffer: name + ':' + bits (16 bytes reserved, enough for any %d) + NUL
ent: str = pool.alloc(nlen + 1 + 16 + 1)
if ent is None:
continue
if ent is None: continue
string.strcpy(ent, arg.arg)
ent[nlen] = ':'
viperlib.snprintf(ent + nlen + 1, 16, "%d", bits)
if cnt > 0:
string.strcat(buf, ",")
if cnt > 0: string.strcat(buf, ",")
string.strcat(buf, ent)
cnt += 1
if cnt == 0:
return None
if cnt == 0: return None
return buf
+5 -26
View File
@@ -1,37 +1,16 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import sys
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
# ============================================================
# TypeGenMixin - 类型映射与解析 Mixin(Generator 继承链入口)
#
# 方法已全部拆至 TypeGenBase/CDef/CType/Resolve/Err,本文件保留类声明。
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
# TypeGenMixin - type-mapping/parse mixin entry (@t.NoVTable: static binding, no vtable).
# Methods live in TypeGenBase/CDef/CType/Resolve/Err; this file keeps only the class decl.
# ============================================================
# 常量(与 HandlesType 保持一致)
# ============================================================
# Constants (kept in sync with HandlesType).
define TRACE_MAX = 32
define MAX_CTYPEDEF_DEPTH = 10
# ============================================================
# TypeGenMixin - 类型映射/注解解析/编译期常量表/错误上报
# ============================================================
# TypeGenMixin - type mapping, annotation parsing, compile-time constants, error reporting.
novirtual class TypeGenMixin(TypeGenBase.TypeGenBase):
"""类型映射与解析 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
"""Type mapping and parsing mixin; all methods live in the sub-mixins."""
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -4
View File
@@ -1,4 +1,2 @@
# ============================================================
# Gen - 对象化生成器(P1 骨架 + P2 TypeGenMixin)
# Generator: 翻译状态容器
# ============================================================
# Gen - objectified generator (P1 skeleton + P2 TypeGenMixin)
# Generator: translation state container
+404 -507
View File
@@ -1,507 +1,404 @@
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import w32.win32file as win32file
import w32.win32base as win32base
import hashlib
import viperlib
import lib.Projectrans.Utils as Utils
import lib.core.VLogger as VLogger
# ============================================================
# IncludesScanner - includes 目录递归扫描(Win32 FindFirstFileA/FindNextFileA)
# 递归收集 .vp 文件路径与 SHA1,供 Phase1 stub 生成 / Phase2 stub 合并使用
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy*
# 文件路径最大长度
define MAX_PATH_LEN = 512
# 单次扫描最大文件数
define MAX_FILES = 1024
# ============================================================
# FileEntry - 文件条目(路径 + SHA1)
# ============================================================
novirtual class FileEntry:
"""扫描到的文件条目"""
Path: str # 文件绝对路径
Sha1: str # SHA1 前16字符(16字节+null)
RelPath: str # 相对于 includes 根目录的路径
ModuleName: str # 模块名(如 "ast.parser")
IncDir: str # 所属 includes 根目录(多 includes 目录支持)
# ============================================================
# ScanResult - 扫描结果
# ============================================================
novirtual class ScanResult:
"""扫描结果集合"""
Entries: FileEntry* # FileEntry 数组
Count: int
Capacity: int
# ============================================================
# create_scan_result - 创建扫描结果容器
# ============================================================
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
"""创建扫描结果容器,预分配 MAX_FILES 个槽位"""
if pool is None: return None
size: sizet = MAX_FILES * FileEntry.__sizeof__()
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(与 StubMerger 保持一致)
entries: FileEntry* = stdlib.malloc(size)
if entries is None: return None
string.memset(entries, 0, size)
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
if result is None: return None
string.memset(result, 0, ScanResult.__sizeof__())
result.Entries = entries
result.Count = 0
result.Capacity = MAX_FILES
return result
# ============================================================
# add_file_entry - 向扫描结果添加文件条目
# ============================================================
def add_file_entry(result: ScanResult*,
pool: memhub.MemBuddy*,
abs_path: str, rel_path: str,
sha1: str, inc_dir: str) -> int:
"""添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)"""
if result is None or pool is None: return 1
if result.Count >= result.Capacity: return 1
# 计算模块名:rel_path 中 / 替换为 .,去掉 .vp/.py 扩展名(__init__.vp 特判由 Utils 统一处理)
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
if mod_buf is None: return 1
# 获取条目地址
entry_size: sizet = FileEntry.__sizeof__()
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is None: return 1
# 复制路径字符串
abs_len: sizet = string.strlen(abs_path)
abs_buf: str = stdlib.malloc(abs_len + 1)
if abs_buf is None: return 1
string.strcpy(abs_buf, abs_path)
entry.Path = abs_buf
# 复制 SHA1
sha1_buf: str = stdlib.malloc(17)
if sha1_buf is None: return 1
string.strcpy(sha1_buf, sha1)
entry.Sha1 = sha1_buf
# 复制相对路径
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
if rel_buf is None: return 1
string.strcpy(rel_buf, rel_path)
entry.RelPath = rel_buf
# 复制模块名
entry.ModuleName = mod_buf
# 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用)
if inc_dir is not None:
inc_len: sizet = string.strlen(inc_dir)
inc_buf: str = stdlib.malloc(inc_len + 1)
if inc_buf is None:
return 1
string.strcpy(inc_buf, inc_dir)
entry.IncDir = inc_buf
result.Count += 1
return 0
# ============================================================
# compute_file_sha1 - 读取文件内容并计算 SHA1(CRLF→LF 后计算,与 Projectrans.py 一致)
# ============================================================
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
"""读取文件内容并计算 SHA1 前16字符(CRLF→LF 后计算,与 Projectrans.py 一致)"""
if pool is None or file_path is None: return None
# 读取文件内容(stdlib.malloc 分配,避免 mbuddy 池耗尽);BUF_SIZE 须与翻译侧 SRC_BUF_SIZE(1MB)一致,否则尾部变更不改 SHA1 导致缓存永不失效
BUF_SIZE: sizet = 1048576
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
if buf is None: return None
# 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致
buf_len: sizet = string.strlen(buf)
write_pos: sizet = 0
for ch in buf:
if ch != '\r':
buf[write_pos] = ch
write_pos += 1
# 添加 null 终止符
buf[write_pos] = 0
# 计算 SHA1
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
if ctx is None:
stdlib.free(buf)
return None
ctx.update(buf)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None:
stdlib.free(buf)
return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符,stdlib 分配保持原行为)
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
stdlib.free(buf)
stdlib.free(digest)
return hex_buf
# ============================================================
# scan_directory_recursive - 递归扫描目录(FindFirstFileA/FindNextFileA 遍历目录树)
# ============================================================
def scan_directory_recursive(pool: memhub.MemBuddy*,
root_dir: str,
rel_prefix: str,
result: ScanResult*,
inc_dir: str) -> int:
"""递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)"""
if pool is None or root_dir is None or result is None:
return 1
# 子目录递归失败计数(不静默忽略,汇总处 fail-fast 终止)
subdir_failed: int = 0
# 构造搜索模式: root_dir/*
root_len: sizet = string.strlen(root_dir)
pattern: bytes = stdlib.malloc(root_len + 4)
if pattern is None:
return 1
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
# find_data 缓冲 +16 余量:TPV 的 WIN32_FIND_DATAA.__sizeof__() 小于 Windows 实际写入,越界写会破坏相邻堆块
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(fd_size_s + 16)
if find_data is None:
return 1
string.memset(find_data, 0, fd_size_s + 16)
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
return 1
# 遍历所有文件和子目录
while True:
# 跳过 . 和 ..
fname: str = find_data.cFileName
if fname is not None:
fname0: char = fname[0]
if fname0 == '.':
fname1: char = fname[1]
if fname1 == '\0':
# "."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
elif fname1 == '.':
fname2: char = fname[2]
if fname2 == '\0':
# ".."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
# 检查是否为目录
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
# 构造完整路径
fname_len: sizet = string.strlen(fname)
full_path: bytes = Utils.JoinPath(root_dir, fname)
if full_path is None:
break
# 构造相对路径
prefix_len: sizet = 0
if rel_prefix is not None:
prefix_len = string.strlen(rel_prefix)
rel_path: bytes = None
if rel_prefix is not None and prefix_len > 0:
rel_path = Utils.JoinPath(rel_prefix, fname)
else:
rel_path = stdlib.malloc(fname_len + 1)
if rel_path is None:
break
string.strcpy(rel_path, fname)
if rel_path is None:
break
if is_dir != 0:
# 递归扫描子目录
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0:
subdir_failed += 1
else:
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
is_py: int = 0
if Utils.is_vp_at(fname, 0, fname_len) != 0:
is_py = 1
if is_py != 0:
# 跳过 __pycache__ 目录下的文件
is_pycache: int = 0
if rel_prefix is not None:
if string.strcmp(rel_prefix, "__pycache__") == 0:
is_pycache = 1
if is_pycache == 0:
# 计算 SHA1
sha1: str = compute_file_sha1(pool, full_path)
if sha1 is not None:
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
VLogger.debug(f"扫描: {rel_path} -> {sha1}", "scan")
else:
VLogger.warning(f"SHA1 计算失败: {full_path}", "scan")
# 继续搜索下一个文件
if win32file.FindNextFileA(handle, find_data) == 0:
break
win32file.FindClose(handle)
if subdir_failed > 0:
VLogger.error(f"子目录扫描失败 {subdir_failed} 个", "scan")
return 0
# ============================================================
# scan_includes - 扫描 includes 目录入口(返回所有 .vp 文件路径与 SHA1)
# ============================================================
def scan_includes(pool: memhub.MemBuddy*,
includes_dirs: str) -> ScanResult*:
"""扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果"""
if pool is None or includes_dirs is None:
return None
result: ScanResult* = create_scan_result(pool)
if result is None:
return None
# 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并
dirs_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < dirs_total:
while d_i < dirs_total and includes_dirs[d_i] == ' ':
d_i += 1
if d_i >= dirs_total:
break
d_start: sizet = d_i
while d_i < dirs_total and includes_dirs[d_i] != ' ':
d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0:
continue
dir_buf: bytes = stdlib.malloc(d_len + 1)
if dir_buf is None:
VLogger.error("includes 目录分配失败,终止扫描", "scan")
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
dir_buf[d_len] = '\0'
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
stdlib.free(dir_buf)
return result
# ============================================================
# find_entry_by_module - 按模块名查找文件条目
# ============================================================
def find_entry_by_module(result: ScanResult*,
module_name: str) -> FileEntry*:
"""按模块名查找文件条目"""
if result is None or module_name is None:
return None
entry_size: sizet = FileEntry.__sizeof__()
for i in range(result.Count):
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is not None:
if entry.ModuleName is not None:
if string.strcmp(entry.ModuleName, module_name) == 0:
return entry
return None
# ============================================================
# _PathExistsA - 路径存在性检查(GetFileAttributesA)
# ============================================================
def _PathExistsA(path: str) -> int:
"""路径是否存在(1=存在,0=不存在/参数错误)
用 GetFileAttributesA(无输出缓冲)替代 FindFirstFileA:后者写
WIN32_FIND_DATAA 结构,而 TPV 该结构 sizeof 小于 Windows 实际写入
(约 592 字节,差距 >270 字节),会越界写坏相邻堆块(BFS 输出槽
0x2e 崩溃根因)。GetFileAttributesA 只返回属性值,无越界风险。
"""
if path is None:
return 0
attr: ULONG = win32file.GetFileAttributesA(path)
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
return 0
return 1
# ============================================================
# _TryResolveModuleFile - 尝试单个候选路径并写输出
#
# Args:
# inc_dir: includes 根目录
# rel_seg: 模块名转出的相对路径段(ast/parser)
# rel_len: rel_seg 长度
# suffix: 候选后缀(".vp" 或 "/__init__.vp")
# out_abs_path/out_rel_path/out_inc_dir: 输出槽(t.CPtr 指针)
#
# Returns:
# 1 命中(已写输出),0 未命中
# ============================================================
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet,
suffix: str,
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
out_inc_dir: t.CPtr) -> int:
if inc_dir is None or rel_seg is None or suffix is None:
return 0
inc_len: sizet = string.strlen(inc_dir)
suf_len: sizet = string.strlen(suffix)
# 候选绝对路径: {inc}/{rel}{suffix}(手写拼接,避免 %. *s 精度修饰符)
cand_len: sizet = inc_len + 1 + rel_len + suf_len
cand: bytes = stdlib.malloc(cand_len + 1)
if cand is None:
return 0
string.strcpy(cand, inc_dir)
cand[inc_len] = '/'
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
if _PathExistsA(cand) == 0:
stdlib.free(cand)
return 0
# 相对 includes 根目录的路径: {rel}{suffix}
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
if rel_path is None:
stdlib.free(cand)
return 0
string.strncpy(rel_path, rel_seg, rel_len)
rel_path[rel_len] = '\0'
string.strcat(rel_path, suffix)
# 所属 includes 根目录深拷贝:先全部分配就绪再统一写槽,
# 避免 inc 分配失败时 abs/rel 已转移所有权造成的半写状态
inc_buf: bytes = None
if out_inc_dir is not None:
inc_buf = stdlib.malloc(inc_len + 1)
if inc_buf is None:
stdlib.free(cand)
stdlib.free(rel_path)
return 0
string.strcpy(inc_buf, inc_dir)
# 写输出槽(stdlib 深拷贝,调用方负责 free)
# 槽联合必须用 (sizet*):t.CPtr|t.CPtr 会被折叠成 t.CPtr,
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
# 8 字节,写入用 sizet(ptr) 直接转换;读取用 t\.CVoid(u64, t.CPtr)
# 转回指针(t\.CVoid 仅配合 t.CPtr 表示 void* 强转)。
if out_abs_path is not None:
abs_slot: sizet* = (sizet*)(out_abs_path)
abs_slot[0] = sizet(cand)
else:
stdlib.free(cand)
if out_rel_path is not None:
rel_slot: sizet* = (sizet*)(out_rel_path)
rel_slot[0] = sizet(rel_path)
else:
stdlib.free(rel_path)
if out_inc_dir is not None and inc_buf is not None:
inc_slot: sizet* = (sizet*)(out_inc_dir)
inc_slot[0] = sizet(inc_buf)
return 1
# ============================================================
# ResolveModuleToPath - 模块名→文件路径(按文件系统按需解析,不做全量目录遍历)
#
# 解析规则(多 includes 目录逐个尝试,模块优先于包):
# {inc_dir}/{mod.replace('.','/')}.vp (文件模块)
# {inc_dir}/{mod.replace('.','/')}/__init__.vp (包模块)
#
# Args:
# includes_dirs: 空格分隔的多个 includes 根目录
# mod_name: 绝对模块名
# out_abs_path: 输出绝对路径(stdlib.malloc,调用方负责 free)
# out_rel_path: 输出相对 includes 根目录的路径(同上,如 ast/parser.vp)
# out_inc_dir: 输出所属 includes 根目录(同上)
#
# Returns:
# 0 找到;1 未找到/参数错误
# ============================================================
def ResolveModuleToPath(includes_dirs: str, mod_name: str,
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
out_inc_dir: t.CPtr) -> int:
if includes_dirs is None or mod_name is None:
return 1
mn_len: sizet = string.strlen(mod_name)
if mn_len == 0:
return 1
# mod_name → 相对路径段(. → /):ast.parser → ast/parser
rel_seg: bytes = stdlib.malloc(mn_len + 1)
if rel_seg is None:
return 1
rel_len: sizet = 0
for mi in range(mn_len):
ch_m: char = mod_name[mi]
if ch_m == '.':
rel_seg[rel_len] = '/'
else:
rel_seg[rel_len] = ch_m
rel_len += 1
rel_seg[rel_len] = '\0'
found: int = 0
d_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < d_total:
while d_i < d_total and includes_dirs[d_i] == ' ':
d_i += 1
if d_i >= d_total:
break
d_start: sizet = d_i
while d_i < d_total and includes_dirs[d_i] != ' ':
d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0:
continue
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
if inc_dir_buf is None:
continue
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
inc_dir_buf[d_len] = '\0'
# 候选 1: {inc}/{rel}.vp(文件模块)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp",
out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
break
# 候选 2: {inc}/{rel}/__init__.vp(包模块)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp",
out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
if found != 0:
break
stdlib.free(rel_seg)
if found != 0:
return 0
return 1
from stdint import *
import memhub
import string
import stdlib
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import hashlib
import viperlib
import lib.Projectrans.Utils as Utils
import lib.core.VLogger as VLogger
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
# FindNextFileA); collects .vp paths and SHA1s for Phase1 stub gen / Phase2 merge.
# global mbuddy pointer
_mbuddy: memhub.MemBuddy*
# max file path length
define MAX_PATH_LEN = 512
# max files per scan
define MAX_FILES = 1024
# FileEntry - file entry (path + SHA1)
novirtual class FileEntry:
"""A scanned file entry."""
Path: str # absolute file path
Sha1: str # first 16 SHA1 chars (16 bytes + null)
RelPath: str # path relative to the includes root
ModuleName: str # module name (e.g. "ast.parser")
IncDir: str # owning includes root dir (multi-root support)
# ScanResult - scan result
novirtual class ScanResult:
"""A collection of scan results."""
Entries: FileEntry* # FileEntry array
Count: int
Capacity: int
# create_scan_result - create the scan result container
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
"""Create the scan result container with MAX_FILES preallocated slots."""
if pool is None: return None
size: sizet = MAX_FILES * FileEntry.__sizeof__()
# use stdlib.malloc to avoid exhausting the mbuddy pool (consistent with StubMerger)
entries: FileEntry* = stdlib.malloc(size)
if entries is None: return None
string.memset(entries, 0, size)
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
if result is None: return None
string.memset(result, 0, ScanResult.__sizeof__())
result.Entries = entries
result.Count = 0
result.Capacity = MAX_FILES
return result
# add_file_entry - append a file entry to the scan result
def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, rel_path: str, sha1: str, inc_dir: str) -> int:
"""Append a file entry to the scan result; inc_dir is the owning includes root."""
if result is None or pool is None: return 1
if result.Count >= result.Capacity: return 1
# compute module name: '/' -> '.', strip .vp/.py (Utils handles __init__.vp)
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
if mod_buf is None: return 1
# get the entry address
entry_size: sizet = FileEntry.__sizeof__()
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is None: return 1
# copy the path string
abs_len: sizet = string.strlen(abs_path)
abs_buf: str = stdlib.malloc(abs_len + 1)
if abs_buf is None: return 1
string.strcpy(abs_buf, abs_path)
entry.Path = abs_buf
# copy the SHA1
sha1_buf: str = stdlib.malloc(17)
if sha1_buf is None: return 1
string.strcpy(sha1_buf, sha1)
entry.Sha1 = sha1_buf
# copy the relative path
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
if rel_buf is None: return 1
string.strcpy(rel_buf, rel_path)
entry.RelPath = rel_buf
# copy the module name
entry.ModuleName = mod_buf
# copy the owning includes root dir (used by Phase2 path joining)
if inc_dir is not None:
inc_len: sizet = string.strlen(inc_dir)
inc_buf: str = stdlib.malloc(inc_len + 1)
if inc_buf is None: return 1
string.strcpy(inc_buf, inc_dir)
entry.IncDir = inc_buf
result.Count += 1
return 0
# compute_file_sha1 - read file content and compute SHA1 (CRLF->LF, matching Projectrans.py)
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
"""Read a file, normalize CRLF to LF, and return the first 16 SHA1 hex chars."""
if pool is None or file_path is None: return None
# read file content; BUF_SIZE must match SRC_BUF_SIZE (1MB) or trailing edits won't change SHA1
BUF_SIZE: sizet = 1048576
# Refuse larger files: hashing only the first 1 MiB gives two files that differ later the same
# SHA1, and the SHA1 keys the stub/cache files (colliding modules would reuse each other's IR).
sf_sz: fileio.File* = fileio.File(file_path, fileio.MODE.R)
if sf_sz.closed: return None
too_big: int = 0
if sf_sz.size() >= BUF_SIZE: too_big = 1
sf_sz.close()
if too_big != 0:
VLogger.error(f"source file exceeds the {BUF_SIZE}-byte limit and cannot be hashed completely: {file_path}", "IncludesScanner")
return None
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
if buf is None: return None
# strip \r in place (CRLF -> LF), matching Projectrans.py text-mode reads
buf_len: sizet = string.strlen(buf)
write_pos: sizet = 0
for ch in buf:
if ch != '\r':
buf[write_pos] = ch
write_pos += 1
# add a null terminator
buf[write_pos] = 0
# compute SHA1
ctx: hashlib.sha1* = hashlib.sha1()
if ctx is None:
stdlib.free(buf)
return None
ctx.update(buf)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None:
stdlib.free(buf)
return None
ctx.final(digest)
# convert to hex (first 8 bytes = 16 hex chars; stdlib allocation unchanged)
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
stdlib.free(buf)
stdlib.free(digest)
return hex_buf
# scan_directory_recursive - recursively scan a directory (FindFirstFileA/FindNextFileA)
def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix: str, result: ScanResult*, inc_dir: str) -> int:
"""Recursively collect .vp/.py files under a directory; inc_dir is the includes root."""
if pool is None or root_dir is None or result is None: return 1
# subdir recursion failure count (not ignored; fail-fast at the summary)
subdir_failed: int = 0
# build search pattern: root_dir/*
root_len: sizet = string.strlen(root_dir)
pattern: bytes = stdlib.malloc(root_len + 4)
if pattern is None: return 1
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
# find_data buffer +16 slack: VPC's WIN32_FIND_DATAA.__sizeof__() undercounts Windows writes
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: win32file.WIN32_FIND_DATAA* = stdlib.malloc(fd_size_s + 16)
if find_data is None: return 1
string.memset(find_data, 0, fd_size_s + 16)
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE: return 1
# iterate all files and subdirectories
while True:
# skip . and ..
fname: str = find_data.cFileName
if fname is not None:
fname0: char = fname[0]
if fname0 == '.':
fname1: char = fname[1]
if fname1 == '\0':
# "."
if win32file.FindNextFileA(handle, find_data) == 0: break
continue
elif fname1 == '.':
fname2: char = fname[2]
if fname2 == '\0':
# ".."
if win32file.FindNextFileA(handle, find_data) == 0: break
continue
# check whether it is a directory
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
# build the full path
fname_len: sizet = string.strlen(fname)
full_path: bytes = Utils.JoinPath(root_dir, fname)
if full_path is None: break
# build the relative path
prefix_len: sizet = 0
if rel_prefix is not None: prefix_len = string.strlen(rel_prefix)
rel_path: bytes = None
if rel_prefix is not None and prefix_len > 0: rel_path = Utils.JoinPath(rel_prefix, fname)
else:
rel_path = stdlib.malloc(fname_len + 1)
if rel_path is None: break
string.strcpy(rel_path, fname)
if rel_path is None: break
if is_dir != 0:
# recurse into the subdirectory
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1
else:
# check for a .vp source file (Viper uses .vp only; no longer .py)
is_py: int = 0
if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1
if is_py != 0:
# skip files under __pycache__
is_pycache: int = 0
if rel_prefix is not None:
if string.strcmp(rel_prefix, "__pycache__") == 0: is_pycache = 1
if is_pycache == 0:
# compute SHA1
sha1: str = compute_file_sha1(pool, full_path)
if sha1 is not None:
# fail-fast: a full table used to drop the file silently, so it never got a
# SHA1 entry / dependency edge and was therefore never compiled.
if add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir) != 0:
VLogger.error(f"includes scan could not register '{full_path}' (the {MAX_FILES}-file capacity or an allocation failed); raise MAX_FILES", "scan")
VLogger.debug(f"scan: {rel_path} -> {sha1}", "scan")
else: VLogger.warning(f"SHA1 computation failed: {full_path}", "scan")
# continue to the next file
if win32file.FindNextFileA(handle, find_data) == 0: break
win32file.FindClose(handle)
if subdir_failed > 0: VLogger.error(f"subdirectory scan failed for {subdir_failed} entries", "scan")
return 0
# scan_includes - scan entry point: return all .vp file paths and SHA1s
def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
"""Scan space-separated includes dirs and return all scanned .vp/.py files."""
if pool is None or includes_dirs is None: return None
result: ScanResult* = create_scan_result(pool)
if result is None: return None
# parse space-separated includes dirs, recursively scanning and merging each
dirs_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < dirs_total:
while d_i < dirs_total and includes_dirs[d_i] == ' ': d_i += 1
if d_i >= dirs_total: break
d_start: sizet = d_i
while d_i < dirs_total and includes_dirs[d_i] != ' ': d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0: continue
dir_buf: bytes = stdlib.malloc(d_len + 1)
if dir_buf is None: VLogger.error("failed to allocate the includes directory; aborting scan", "scan")
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
dir_buf[d_len] = '\0'
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
stdlib.free(dir_buf)
return result
# find_entry_by_module - find a file entry by module name
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
"""Find a file entry by module name."""
if result is None or module_name is None: return None
entry_size: sizet = FileEntry.__sizeof__()
for i in range(int(result.Count)):
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is not None:
if entry.ModuleName is not None:
if string.strcmp(entry.ModuleName, module_name) == 0: return entry
return None
# _PathExistsA - path existence check (GetFileAttributesA)
def _PathExistsA(path: str) -> int:
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
if path is None: return 0
attr: ULONG = win32file.GetFileAttributesA(path)
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
return 0
return 1
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
# out_abs_path/out_rel_path/out_inc_dir are rawptr output slots.
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
if inc_dir is None or rel_seg is None or suffix is None: return 0
inc_len: sizet = string.strlen(inc_dir)
suf_len: sizet = string.strlen(suffix)
# candidate absolute path: {inc}/{rel}{suffix} (hand-built to avoid %. *s)
cand_len: sizet = inc_len + 1 + rel_len + suf_len
cand: bytes = stdlib.malloc(cand_len + 1)
if cand is None: return 0
string.strcpy(cand, inc_dir)
cand[inc_len] = '/'
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
if _PathExistsA(cand) == 0:
stdlib.free(cand)
return 0
# path relative to the includes root: {rel}{suffix}
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
if rel_path is None:
stdlib.free(cand)
return 0
string.strncpy(rel_path, rel_seg, rel_len)
rel_path[rel_len] = '\0'
string.strcat(rel_path, suffix)
# Deep-copy the owning includes root dir; allocate everything before writing
# slots to avoid a half-written state when inc allocation fails.
inc_buf: bytes = None
if out_inc_dir is not None:
inc_buf = stdlib.malloc(inc_len + 1)
if inc_buf is None:
stdlib.free(cand)
stdlib.free(rel_path)
return 0
string.strcpy(inc_buf, inc_dir)
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
# (sizet*): rawptr* folds to rawptr and [0] becomes a 1-byte access.
if out_abs_path is not None:
abs_slot: sizet* = (sizet*)(out_abs_path)
abs_slot[0] = sizet(cand)
else: stdlib.free(cand)
if out_rel_path is not None:
rel_slot: sizet* = (sizet*)(out_rel_path)
rel_slot[0] = sizet(rel_path)
else: stdlib.free(rel_path)
if out_inc_dir is not None and inc_buf is not None:
inc_slot: sizet* = (sizet*)(out_inc_dir)
inc_slot[0] = sizet(inc_buf)
return 1
# ResolveModuleToPath - module name -> file path, resolved on demand (no full scan).
# Tries each includes dir, preferring file modules over packages. Returns 0 found, 1 not.
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
if includes_dirs is None or mod_name is None: return 1
mn_len: sizet = string.strlen(mod_name)
if mn_len == 0: return 1
# mod_name -> relative path segment ('.' -> '/'): ast.parser -> ast/parser
rel_seg: bytes = stdlib.malloc(mn_len + 1)
if rel_seg is None: return 1
rel_len: sizet = 0
for mi in range(int(mn_len)):
ch_m: char = mod_name[mi]
if ch_m == '.': rel_seg[rel_len] = '/'
else: rel_seg[rel_len] = ch_m
rel_len += 1
rel_seg[rel_len] = '\0'
found: int = 0
d_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < d_total:
while d_i < d_total and includes_dirs[d_i] == ' ': d_i += 1
if d_i >= d_total: break
d_start: sizet = d_i
while d_i < d_total and includes_dirs[d_i] != ' ': d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0: continue
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
if inc_dir_buf is None: continue
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
inc_dir_buf[d_len] = '\0'
# candidate 1: {inc}/{rel}.vp (file module)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
break
# candidate 2: {inc}/{rel}/__init__.vp (package module)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
if found != 0: break
stdlib.free(rel_seg)
if found != 0: return 0
return 1
+2107 -2970
View File
File diff suppressed because it is too large. Load diff
+246 -275
View File
@@ -1,275 +1,246 @@
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import sys
import w32.win32console as w32cmd
import w32.win32file as w32file
import w32.win32base as win32base
# ============================================================
# VLogger - 原生日志系统(Windows 控制台彩色输出)
# 输出格式: ├ LEVEL: message / ├ LEVEL[category]: message(与 TPC lib/core/VLogger.py 对齐)
# ============================================================
# 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。
# mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为
# _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。
extern def longjmp(buf: rawptr, val: int)
# REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。
# 生命周期 = 整个 REPL 会话:Repl 每轮片段都会重新武装(Repl.run 每轮 _setjmp +
# Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll →
# llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。
# 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。
_repl_jmpbuf: rawptr = None
# ============================================================
# repl_abort - TPV 内"诊断后终止"的唯一出口
#
# 约定:调用方**已打印**诊断(Traceback / VLogger.error / [OOM] 等),本函数只决定
# "跳回 REPL 继续"还是"终止进程":
# * REPL 会话(_repl_jmpbuf 已注册):fflush 后 longjmp 回 Repl.run 的恢复点,
# 进程存活、提示符继续(宽容 Traceback);
# * 批处理 / 未武装:sys.exit(1),与改动前的 fail-fast 行为逐字一致。
# 收敛点:Logger.error、模块级 error、TypeGenErr.fatal_error、
# ImportsGenMixin._report_import_error、CallGenBase/VpiReader 的链表哨兵。
# ============================================================
def repl_abort() -> int:
"""诊断后的统一出口:已注册恢复点则 longjmp 回 REPL,否则终止进程。"""
if _repl_jmpbuf is not None:
stdio.fflush(None)
longjmp(_repl_jmpbuf, 1)
sys.exit(1)
return 0
def set_repl_jmpbuf(buf: t.CPtr) -> int:
"""注册 REPL 的 jmp_buf(会话开始;每片段入口可幂等重注册)。"""
global _repl_jmpbuf
_repl_jmpbuf = buf
return 0
def clear_repl_jmpbuf() -> int:
"""清除已注册的 jmp_buf(REPL 会话结束时调用;中途不清)。"""
global _repl_jmpbuf
_repl_jmpbuf = None
return 0
# 日志级别
enum LogLevel:
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
# Win32 控制台前景色属性
define FOREGROUND_BLUE = 0x0001
define FOREGROUND_GREEN = 0x0002
define FOREGROUND_RED = 0x0004
define FOREGROUND_INTENSITY = 0x0008
define FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN
define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN
define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE
# Win32 控制台背景色属性
define BACKGROUND_RED = 0x0040
define BACKGROUND_GREEN = 0x0020
define BACKGROUND_BLUE = 0x0010
define BACKGROUND_INTENSITY = 0x0080
define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
novirtual class Logger:
_level: int
_console_handle: win32base.HANDLE
_use_color: int
jit_flag: bool
__mbuddy__: memhub.MemBuddy*
def __init__(self, level: int = 1):
self._level = level
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
self._use_color = 1
self.jit_flag = False
self.__mbuddy__ = _mbuddy
def _set_color(self, attr: WORD) -> int:
uc: int = self._use_color
if uc:
ch: win32base.HANDLE = self._console_handle
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
return ret
return 0
def _reset_color(self) -> int:
if self._use_color:
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
return 0
# ============================================================
# _log - 核心日志输出(分段着色:前缀符号 ├ / LEVEL 标签 / 消息内容)
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
# ============================================================
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
sym_color: WORD, level_color: WORD,
category: str = "") -> int:
# 前缀符号着色
self._set_color(sym_color)
stdio.printf("%s ", prefix_sym)
# LEVEL 标签着色
self._set_color(level_color)
stdio.printf("%s", level_str)
self._reset_color()
# category 可选
if category is not None and category[0] != 0:
stdio.printf(" [%s]", category)
stdio.printf(": %s\n", msg)
return 0
def debug(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
def info(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
return ret
def warning(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
def error(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
# JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程)
if self.jit_flag:
repl_abort()
return ret
# 批处理模式:错误立即终止进程(行为与改动前逐字一致)
sys.exit(1)
return ret
def success(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
def critical(self, msg: str, category: str = "") -> int:
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category)
def set_level(self, level: int) -> int:
self._level = level
return 0
def set_jit_flag(self, v: bool) -> int:
"""设置 JIT/REPL 模式标志:True 时 error 改为 longjmp 回 REPL 循环。"""
self.jit_flag = v
return 0
# ============================================================
# 高级日志方法
# ============================================================
def banner(self, msg: str) -> int:
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
stdio.printf("\n=== %s ===\n\n", msg)
self._reset_color()
return 0
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
stdio.printf(" 编译错误 ")
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 错误: %s\n", msg)
self._reset_color()
return 0
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf(" 编译警告 ")
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 警告: %s\n", msg)
self._reset_color()
return 0
# 全局 mbuddy 指针(由 lib.InitLib 注入)
_mbuddy: memhub.MemBuddy*
# 全局 logger 指针
logger: Logger*
log: Logger*
def init_logger():
"""初始化全局 logger 实例。"""
global logger, log
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
# if raw is None: return None 存疑,如何去管理内存?
logger = raw
logger.__before_init__()
logger.__init__(LogLevel.INFO)
log = logger
# if fmtbuffer is None: return None
# ============================================================
# --nowarn-register 开关:抑制 Phase A 依赖类 WARN 输出(仅抑制输出,不影响内部逻辑)
# ============================================================
_nowarn_register: int = 0
# ============================================================
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐,无需先 get_logger())
# ============================================================
def info(msg: str, category: str = "") -> int:
"""输出 INFO 级别日志。"""
if log is not None: return log.info(msg, category)
return 0
def warning(msg: str, category: str = "") -> int:
"""输出 WARN 级别日志。"""
if log is not None: return log.warning(msg, category)
return 0
def error(msg: str, category: str = "") -> int:
"""输出 ERROR 级别日志:批处理模式终止进程;JIT/REPL 模式 longjmp 回 REPL。"""
if log is not None: return log.error(msg, category)
# logger 不可用时也走统一出口(REPL 已注册恢复点时仍可恢复)
repl_abort()
return 0
def success(msg: str, category: str = "") -> int:
"""输出 SUCCESS 级别日志。"""
if log is not None: return log.success(msg, category)
return 0
def critical(msg: str, category: str = "") -> int:
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
if log is not None: return log.critical(msg, category)
return 0
def debug(msg: str, category: str = "") -> int:
"""输出 DEBUG 级别日志。"""
if log is not None: return log.debug(msg, category)
return 0
from stdint import *
import stdio
import memhub
import sys
import win32.win32console as win32cmd
import win32.win32file as win32file
import win32.win32base as win32base
# VLogger - native logger with colored Windows console output.
# Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py).
# Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point.
# mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL).
extern def longjmp(buf: rawptr, val: int)
# REPL recovery-point pointer: registered at session start, cleared at session end.
# Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1).
_repl_jmpbuf: rawptr = None
# repl_abort - the single "terminate after diagnosis" exit in VPC.
# Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1).
def repl_abort() -> int:
"""Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit."""
if _repl_jmpbuf is not None:
stdio.fflush(None)
longjmp(_repl_jmpbuf, 1)
sys.exit(1)
return 0
def set_repl_jmpbuf(buf: rawptr) -> int:
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
global _repl_jmpbuf
_repl_jmpbuf = buf
return 0
def clear_repl_jmpbuf() -> int:
"""Clear the registered jmp_buf at REPL session end."""
global _repl_jmpbuf
_repl_jmpbuf = None
return 0
# Log levels
enum LogLevel:
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
# Win32 console foreground color attributes
# 值用 WORD(...) 显式定宽为 uint16(与 _log 的 sym_color/level_color 形参一致)。
# 注意:仅写 `define X: WORD = 0x0002` 不够 —— define 的宽度由**右侧表达式的类型**
# 决定(win32file.vp 的 `define STD_OUTPUT_HANDLE: DWORD = DWORD(-10)` 同款),
# 否则常量仍是 i32 → 传给 WORD 形参即 32→16 隐式窄化。
define FOREGROUND_BLUE: WORD = WORD(0x0001)
define FOREGROUND_GREEN: WORD = WORD(0x0002)
define FOREGROUND_RED: WORD = WORD(0x0004)
define FOREGROUND_INTENSITY: WORD = WORD(0x0008)
define FOREGROUND_WHITE: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
define FOREGROUND_CYAN: WORD = WORD(FOREGROUND_BLUE | FOREGROUND_GREEN)
define FOREGROUND_YELLOW: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN)
define FOREGROUND_MAGENTA: WORD = WORD(FOREGROUND_RED | FOREGROUND_BLUE)
# Win32 console background color attributes
define BACKGROUND_RED: WORD = WORD(0x0040)
define BACKGROUND_GREEN: WORD = WORD(0x0020)
define BACKGROUND_BLUE: WORD = WORD(0x0010)
define BACKGROUND_INTENSITY: WORD = WORD(0x0080)
define BACKGROUND_WHITE: WORD = WORD(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
novirtual class Logger:
_level: int
_console_handle: win32base.HANDLE
_use_color: int
jit_flag: bool
__mbuddy__: memhub.MemBuddy*
def __init__(self, level: int = 1):
self._level = level
self._console_handle = win32file.GetStdHandle(win32file.STD_OUTPUT_HANDLE)
self._use_color = 1
self.jit_flag = False
self.__mbuddy__ = _mbuddy
def _set_color(self, attr: WORD) -> int:
uc: int = self._use_color
if uc:
ch: win32base.HANDLE = self._console_handle
ret: int = win32cmd.SetConsoleTextAttribute(ch, attr)
return ret
return 0
def _reset_color(self) -> int:
if self._use_color: return win32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
return 0
# _log - core log output with segmented coloring (prefix symbol / level tag / message).
# Format: ├ LEVEL: msg or ├ LEVEL[category]: msg.
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int:
# Color the prefix symbol
self._set_color(sym_color)
stdio.printf("%s ", prefix_sym)
# Color the level tag
self._set_color(level_color)
stdio.printf("%s", level_str)
self._reset_color()
# Optional category
if category is not None and category[0] != 0: stdio.printf(" [%s]", category)
stdio.printf(": %s\n", msg)
return 0
def debug(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
def info(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
return ret
def warning(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
def error(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, WORD(FOREGROUND_RED | FOREGROUND_INTENSITY), category)
# JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit.
if self.jit_flag:
repl_abort()
return ret
# Batch mode: terminate immediately on error.
sys.exit(1)
return ret
def success(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
def critical(self, msg: str, category: str = "") -> int:
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, WORD(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY), category)
def set_level(self, level: int) -> int:
self._level = level
return 0
def set_jit_flag(self, v: bool) -> int:
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
self.jit_flag = v
return 0
# Advanced log methods.
def banner(self, msg: str) -> int:
"""Print a blue-highlighted section banner."""
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
stdio.printf("\n=== %s ===\n\n", msg)
self._reset_color()
return 0
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
"""Format a compile error with a red-background header, location and message."""
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
stdio.printf(" COMPILE ERROR ")
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
elif file[0] != 0: stdio.printf(" file: %s\n", file)
stdio.printf(" error: %s\n", msg)
self._reset_color()
return 0
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
"""Format a compile warning with a yellow background, location and message."""
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf(" COMPILE WARNING ")
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
elif file[0] != 0: stdio.printf(" file: %s\n", file)
stdio.printf(" warning: %s\n", msg)
self._reset_color()
return 0
# Global mbuddy pointer (injected by lib.InitLib).
_mbuddy: memhub.MemBuddy*
# Global logger pointer
logger: Logger*
log: Logger*
def init_logger():
"""Initialize the global logger instance."""
global logger, log
raw: rawptr = _mbuddy.alloc(Logger.__sizeof__())
# if raw is None: return None (memory management still unclear)
logger = raw
logger.__before_init__()
logger.__init__(LogLevel.INFO)
log = logger
# if fmtbuffer is None: return None
# --nowarn-register switch: suppresses Phase A dependency WARN output (output only).
_nowarn_register: int = 0
# Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed).
def info(msg: str, category: str = "") -> int:
"""Log at INFO level."""
if log is not None: return log.info(msg, category)
return 0
def warning(msg: str, category: str = "") -> int:
"""Log at WARN level."""
if log is not None: return log.warning(msg, category)
return 0
def error(msg: str, category: str = "") -> int:
"""Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL."""
if log is not None: return log.error(msg, category)
# When the logger is unavailable, still use the unified exit (REPL can recover if registered)
repl_abort()
return 0
def success(msg: str, category: str = "") -> int:
"""Log at SUCCESS level."""
if log is not None: return log.success(msg, category)
return 0
def critical(msg: str, category: str = "") -> int:
"""Log at CRITICAL level (fatal, red background)."""
if log is not None: return log.critical(msg, category)
return 0
def debug(msg: str, category: str = "") -> int:
"""Log at DEBUG level."""
if log is not None: return log.debug(msg, category)
return 0
+1 -3
View File
@@ -1,3 +1 @@
# ============================================================
# core 包入口
# ============================================================
# core package entry
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
# builtin package entry - compiler-side implementation of builtin libs (e.g. viperbuild).
+480 -710
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -619,7 +619,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
TransPyV Copyright (C) 2026 TermiNexus
ViperC Copyright (C) 2026 TermiNexus
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
+1
View File
@@ -0,0 +1 @@
Viper's original sin is that the person who invented it wasn't smart enough; its abilities don't match its ideals.
+38 -250
View File
@@ -1,223 +1,10 @@
# TransPyV
[English](#english) | [中文](#中文)
The **Viper language compiler, written in Viper itself** — and it is **self-hosted**: TransPyV compiles its own source and produces `TransPyV.exe`. It translates Viper sources to LLVM IR, then drives `llc` + `clang++` to link a native Windows executable. The first-generation CPython compiler ([TransPyC](https://git.gvsds.com/GVSDS/TransPyC)) is now only kept as the bootstrapping **seed**.
**用 Viper 语言编写的 Viper 编译器**,且**已完成自举**:TransPyV 能编译自身源码并产出 `TransPyV.exe`。它把 Viper 源码翻译为 LLVM IR,再调用 `llc` + `clang++` 链接为 Windows 原生可执行文件。第一代 CPython 编译器([TransPyC](https://git.gvsds.com/GVSDS/TransPyC))现在只作为自举**种子**保留。
This is a request to remove the Chinese portions from the ViperC README. Here is the resulting document with all Chinese sections stripped, keeping only the English content.
---
<a id="中文"></a>
# ViperC
## 中文
### 这是什么
`TransPyV` 是 Viper 的第二代编译器:**用 Viper 自己写、也被 Viper 自己编译**。整条链路上没有任何 Python 运行时参与编译 —— 词法/语法分析走 `includes/ast`(自研解析器,9 个文件),IR 生成走 `App/lib/core/Gen/`,目标代码由 `llc` 与 `clang++` 产出。
自举链路:
```text
TransPyC(种子,CPython 实现)
│ 只做一次:把 TransPyV 源码编译成 TransPyV.exe
▼
TransPyV.exe(第一阶段产物)
│ 自举:用这个 exe 再编译 TransPyV 自身
▼
TransPyV.exe(自举产物,当前仓库随包/随 exe\ 提供的那份)
│
└─ 用它编译任意 Viper 工程(ViperApps\*、Test\* …)
```
现状与规模(本机实测):
| 项 | 值 |
| --- | --- |
| 编译器源码 | `App/` 下 **76 个 `.vp` 文件,64,656 行** |
| 其中翻译器核心 | `App/lib/core/Gen/` **52 个文件,49,978 行** |
| 阶段与合并 | `App/lib/Phase/` 6 文件 5,309 行;`App/lib/Stub/` 9 文件 6,877 行 |
| 工程与工具 | `App/lib/Projectrans/` 3 文件 1,008 行 |
| 自测用例 | `Test/` 下 **39 个 `.vp`**(`App/` 36 + `NegativeTest/` 2 + `Sha1Test/` 1) |
| 平台 | Windows x64(源码直接 `import w32.*`) |
> 自举可行性分析与历史数据见 [`wiki/13-bootstrapping.md`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)(该文标注:Phase A → Phase B/C 已跑通,编译器已脱离 Python 运行时)。
### 目录结构
```text
TransPyV/
├── project.vpj # 工程配置(source_dir=./App, build_dir=./.tpv_build, 输出 TransPyV.exe)
├── App/ # 编译器源码(Viper)
│ ├── main.vp # CLI 入口:内存池初始化、单例注入、参数解析、Phase 调度
│ └── lib/
│ ├── Projectrans/
│ │ ├── Config.vp # project.vpj 加载与路径解析(含 includes 缓存目录)
│ │ └── Utils.vp # SHA1 / 目录清理 / 杂项
│ ├── core/
│ │ ├── VLogger.vp # 日志(info/warn/error/banner/success)
│ │ ├── IncludesScanner.vp # includes 目录扫描(决定哪些需重编译)
│ │ ├── CompilerFactory.vp # 工厂:持有 mb + perm_pool 两个 MemBuddy 单例
│ │ └── Gen/ # 翻译器(52 文件)—— 把 AST 变为 LLVM IR
│ │ ├── Generator.vp / Translator.vp / GenApi.vp / GenTypes.vp
│ │ ├── BaseGenMixin / AssignGenMixin / VarGenMixin / ImportsGenMixin
│ │ ├── MatchGenMixin / AsyncGenMixin / StructGenMixin / TranslatorGenMixin
│ │ ├── Call/ (12) 调用生成:普通调用 / C 内建 / 构造 / 虚表 / 变参 / 内联汇编 …
│ │ ├── Class/ (6) 类:定义 / 泛型 / 虚表 / 枚举 / Mixin
│ │ ├── Expr/ (9) 表达式:名字 / 常量 / 运算 / 属性 / f-string / CDef …
│ │ ├── Func/ (3) 函数定义
│ │ ├── Stmt/ (3) 语句:声明 / C-if / Mixin
│ │ └── Type/ (6) 类型解析与 C 类型映射
│ ├── Phase/
│ │ ├── BuildPipeline.vp # .ll → llc → .obj → clang++ → .exe(含 ASan 插桩分支)
│ │ ├── Phase1/ # Phase 1:按需翻译 includes + stub 分离(.stub.ll / .text.ll)
│ │ └── Phase2/ # Phase 2:多文件工程翻译、IR 组合、编译
│ └── Stub/
│ ├── StubGen/ # .vpi 读取 + 声明转换
│ └── StubMerger/ # 多份 .stub.ll / .text.ll 依赖排序与合并
└── Test/ # 编译器自测(Viper)
├── project.vpj
├── App/ # 36 个 .vp(多为 *_test.vp),入口 test_main.vp 聚合全部用例
├── NegativeTest/ # 负向测试(故意编译失败)
└── Sha1Test/ # SHA1 命名空间测试
```
### 前提条件
| 工具 | 说明 |
| --- | --- |
| Windows 10/11 x64 | 编译器源码直接 `import w32.*`,当前仅 Windows |
| `llc` | LLVM 静态编译器(本机 22.1.8),由 `project.vpj` 的 `compiler.cmd` 指定 |
| `clang++` | mingw-w64 工具链,`-fuse-ld=lld` 链接(本机 `D:/msys64/mingw64/bin/clang++.exe`) |
| ≥ 1.5 GiB 空闲内存 | 启动即 `malloc` 1 GiB 主池 + 512 MiB 常驻池 |
| [TransPyC](https://git.gvsds.com/GVSDS/TransPyC) | **仅当需要重建自举种子时**才需要 |
目录布局(编译时需要能找到 `includes/`,`project.vpj` 里写的是相对路径 `../includes`):
```text
TransPyC\ # 工作区根
├── TransPyC\ # 第一代编译器(种子)
├── TransPyV\ # 本仓库
└── includes\ # Viper 标准库 + includes/ast 解析器
```
### 快速开始
```bat
:: 日常:直接用自举产物编译任意工程
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean
:: 编译并立即运行
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
:: 只有在需要重建编译器本体(自举种子)时,才动用 TransPyC
python TransPyC\Projectrans.py --project TransPyV\project.vpj --clean
```
产物位置 = `<工程目录>\<build_dir>\output\<linker.output>`,即默认 `<工程>\.tpv_build\output\app.exe`;
TransPyV 自身因 `linker.output` 为 `TransPyV.exe`,产物是 `TransPyV\.tpv_build\output\TransPyV.exe`。
跑编译器自测套件:
```bat
exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
```
### 命令行参数
| 参数 | 含义 |
| --- | --- |
| `--project <path>` | `project.vpj` 路径(不给则在当前目录、上级目录找) |
| `--src <dir>` | 单文件模式源目录(覆盖配置;入口取 `<dir>/main.vp`) |
| `--temp <dir>` | 声明接口临时目录(覆盖配置,默认 `<build_dir>/temp`) |
| `--output <dir>` | 输出目录(覆盖配置,默认 `<build_dir>/output`) |
| `--phase 1\|2\|all` | 1=只做 stub 分离;2=只做 IR 组合+编译;all=全部(默认) |
| `--cc <cmd>` | LLVM 编译器命令(覆盖配置) |
| `--build-dir <dir>` | 覆盖 `project.vpj` 的 `build_dir`(相对路径仍基于工程目录解析) |
| `--clean` | 清理 temp / output / includes 预编译缓存 |
| `--rebuild-includes` | 只删 includes 预编译缓存(`includes.binary`),强制重编 includes |
| `--clear-cache` | 只清 temp 缓存(`.vpi` / `.stub.ll` / `.text.ll`),强制重生成 stub |
| `--run` | 编译成功后立即执行产物 |
| `--repl` | 交互式 REPL(M1:表达式片段 → DLL → 调用回显);未给 `--project` 时按 cwd → `TransPyV.exe` 同目录查找 `repl.vpj` 模板工程(找不到则报错并提示 `--project`);与 `--asan` / `--phase2command` 互斥 |
| `--asan` | ASan 模式:先 `opt -passes=asan` 插桩;运行库须由 `project.vpj` 的 `linker.static_libs` 声明,否则 fail-fast |
| `--phase2command <bat>` | 不执行 `llc`/链接,只把命令写进指定 `.bat` |
| `--nowarn-register` | 抑制 Phase A 依赖类 WARN(跨模块 `text.ll` 未生成,下遍收敛) |
| `--no-llvmpeephole` | 关闭 FunctionPrint 的本地窥孔(T2 store/load 转发、T3 空 bitcast) |
| `--detection` | 开启 MemBuddy 分配检测(`0xCDCD` 标记 + 记录表探针,查越界写) |
| `--show-args` | 打印全部解析结果 |
| `--version`, `-v` | 打印 Viper / VRTE / TransPyV 版本、提交号、目标三元组与 LLVM 版本 |
| `--help`, `-h` | 打印帮助 |
### 架构
```text
source_dir 下全部 .vp/.py(多文件模式:递归扫描所有源文件)
│
├─ Phase 1(stub 分离)
│ IncludesScanner 按需翻译 includes → Phase1/*
│ Translator + Gen/* 生成 IR,再分别落盘:
│ <sha1>.stub.ll 声明
│ <sha1>.text.ll 实现
│
├─ Phase 2(组合 + 编译)
│ StubMerger* 按拓扑序合并 本地 stub + 依赖 stub + 本地 text → 完整 IR
│ BuildPipeline:完整 IR → llc → .obj → clang++(附加 includes 预编译 .obj)→ .exe
│
└─ <build_dir>/output/<linker.output>
```
**核心模块分工:**
| 模块 | 职责 |
| --- | --- |
| `App/main.vp` | CLI 入口:内存池与单例初始化、参数解析、Phase 调度、`--run` |
| `lib/Projectrans/Config.vp` | `project.vpj` 加载、路径解析、includes 缓存目录 |
| `lib/core/CompilerFactory.vp` | 持有 `mb`(主池)与 `perm_pool`(常驻池)两个 `MemBuddy` |
| `lib/core/Gen/Generator.vp` | 项目级 `Generator` 单例,Phase1/Phase2/BuildPipeline 全程复用 |
| `lib/core/Gen/Translator.vp` | 翻译器主体,`translate()` 与 `dump_ir()`(`.stub.ll` / `.text.ll` / 完整 IR) |
| `lib/core/Gen/GenApi.vp` | 翻译器对外接口与全局状态(如 temp 目录) |
| `lib/Phase/Phase1/` | 扫描 includes、拓扑排序、按需生成声明 |
| `lib/Phase/Phase2/` | 多文件工程:递归扫描源文件、翻译、IR 组合、调度编译 |
| `lib/Phase/BuildPipeline.vp` | `.ll` → `llc` → `.obj` → `clang++` → `.exe`(ASan 走插桩分支) |
| `lib/Stub/StubGen/` | 读取 `.vpi`,转换为 stub 声明 |
| `lib/Stub/StubMerger/` | `stub.ll` / `text.ll` 的收集、依赖拓扑、IR 拼接与落盘 |
| `lib/core/IncludesScanner.vp` | 扫描 `includes/`,决定哪些文件需要重编译 |
| `lib/core/VLogger.vp` | 日志系统(含 `--nowarn-register` 开关) |
> 自举相关注意点:自举后模块级全局变量会带 **SHA1 前缀**(模块隔离),因此像 `_g_gen`
> 这类单例必须**逐模块赋值**(`GenApi._g_gen = gen` …),不能只在 `main.vp` 里赋裸名 ——
> 否则部分模块读到自己的 `None`,表现为"全局名判定恒为 0"之类的误报。
### 内存模型
全程无系统 `malloc` 抖动,只有两个 `MemBuddy` 内存池(`lib/memhub` 的伙伴分配器):
- `POOL_SIZE = 1 GiB`(`1073741824`)—— 主池,`mb`,翻译过程的常规分配。
- `PERM_POOL_SIZE = 536871176`(≈512 MiB 可用)—— 常驻池,`perm_pool`,跨阶段存活的对象。
- 两者由 `CompilerFactory.factory` 单例持有;`mb` 另以 `sys._mbuddy` / `argparse._mbuddy` /
`ast._mbuddy` / `Config._mbuddy` … 的形式逐模块注入。
`--detection` 会把两个池都切到检测模式(`0xCDCD` 填充 + 记录表探针),用于排查越界写。
### 与 TransPyC 的关系
| 维度 | TransPyC | TransPyV |
| --- | --- | --- |
| 实现语言 | CPython | Viper |
| 定位 | 自举**种子**(只用来生成稳定的 TransPyV) | **现行编译器** |
| 自举状态 | — | ✅ 已完成,能编译自身 |
| 入口 | `TransPyC/Projectrans.py` | `App/main.vp` |
| AST 解析 | Python `ast` / 自研 vast | `includes/ast` 自研(9 文件) |
| 工程配置 | `project.json` | `project.vpj` |
| 平台 | Windows / Linux | Windows(`w32.*`) |
### 进一步阅读
- [TransPyC 编译器仓库(种子)](https://git.gvsds.com/GVSDS/TransPyC)
- [Viper 语言文档(wiki 目录)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
- [自举可行性分析与进展](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
- [工程配置 `project.vpj` 说明](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
- [ViperOS 操作系统(用 Viper 写的 OS)](https://git.gvsds.com/GVSDS/ViperOS)
The **Viper language compiler, written in Viper itself** — and it is **self-hosted**: ViperC compiles its own source and produces `viperc.exe`. It translates Viper sources to LLVM IR, then drives `llc` + `clang++` to link a native Windows executable. The first-generation CPython compiler ([TransPyC](https://git.gvsds.com/GVSDS/TransPyC)) is now only kept as the bootstrapping **seed**.
---
@@ -227,18 +14,18 @@ source_dir 下全部 .vp/.py(多文件模式:递归扫描所有源文件)
### What is this
`TransPyV` is the second-generation Viper compiler: **written in Viper and compiled by Viper**. No Python runtime takes part in the compilation path — lexing/parsing goes through `includes/ast` (a self-implemented parser, 9 files), IR generation through `App/lib/core/Gen/`, and machine code through `llc` + `clang++`.
`ViperC` is the second-generation Viper compiler: **written in Viper and compiled by Viper**. No Python runtime takes part in the compilation path — lexing/parsing goes through `includes/ast` (a self-implemented parser, 9 files), IR generation through `App/lib/core/Gen/`, and machine code through `llc` + `clang++`.
The bootstrap chain:
```text
TransPyC (seed, implemented in CPython)
│ one-time only: compile TransPyV sources into TransPyV.exe
│ one-time only: compile ViperC sources into viperc.exe
▼
TransPyV.exe (stage-one artifact)
│ bootstrap: this exe compiles TransPyV itself
viperc.exe (stage-one artifact)
│ bootstrap: this exe compiles ViperC itself
▼
TransPyV.exe (self-hosted artifact — the one shipped in exe\)
viperc.exe (self-hosted artifact — the one shipped in exe\)
│
└─ used to build any Viper project (ViperApps\*, Test\* …)
```
@@ -247,18 +34,18 @@ Current state and size (measured on the dev machine):
| Item | Value |
| --- | --- |
| Compiler sources | **76 `.vp` files, 64,656 lines** under `App/` |
| Translator core | `App/lib/core/Gen/` — **52 files, 49,978 lines** |
| Phases & merging | `App/lib/Phase/` 6 files 5,309 lines; `App/lib/Stub/` 9 files 6,877 lines |
| Project & utils | `App/lib/Projectrans/` 3 files 1,008 lines |
| Self-tests | **39 `.vp`** under `Test/` (`App/` 36 + `NegativeTest/` 2 + `Sha1Test/` 1) |
| Platform | Windows x64 (sources `import w32.*` directly) |
| Compiler sources | **82 `.vp` files, 63,783 lines** under `App/` |
| Translator core | `App/lib/core/Gen/` — **54 files, 47,297 lines** |
| Phases & merging | `App/lib/Phase/` 6 files 5,339 lines; `App/lib/Stub/` 9 files 6,207 lines |
| Project & utils | `App/lib/Projectrans/` 3 files 712 lines |
| Self-tests | workspace root `Test\` (**separate repo**): **97 projects, 129 `.vp`**, driven by `Test\SequentialRegressionTest.py` |
| Platform | Windows x64 (sources `import win32.*` directly) |
### Directory layout
```text
TransPyV/
├── project.vpj # project config (source_dir=./App, build_dir=./.tpv_build, output TransPyV.exe)
ViperC/
├── project.vpj # project config (source_dir=./App, build_dir=./__vpcbuild__, output viperc.exe)
├── App/ # compiler sources (Viper)
│ ├── main.vp # CLI entry: pools, singleton injection, arg parsing, phase dispatch
│ └── lib/
@@ -286,18 +73,17 @@ TransPyV/
│ └── Stub/
│ ├── StubGen/ # .vpi reader + declaration converter
│ └── StubMerger/ # dependency-ordered merge of .stub.ll / .text.ll
└── Test/ # compiler self-tests (Viper)
├── project.vpj
├── App/ # 36 .vp (mostly *_test.vp); entry point test_main.vp aggregates them all
├── NegativeTest/ # negative tests (must fail to compile)
└── Sha1Test/ # SHA1 namespace test
```
> The self-test/regression suite is not in this repo: it lives at the workspace root `Test\`
> (a separate repo, 97 projects, 129 `.vp`), driven by `Test\SequentialRegressionTest.py`
> (`--exe exe\viperc.exe`).
### Prerequisites
| Tool | Notes |
| --- | --- |
| Windows 10/11 x64 | sources `import w32.*`; Windows-only for now |
| Windows 10/11 x64 | sources `import win32.*`; Windows-only for now |
| `llc` | LLVM static compiler (22.1.8 here), set by `compiler.cmd` in `project.vpj` |
| `clang++` | mingw-w64 toolchain, `-fuse-ld=lld` (here `D:/msys64/mingw64/bin/clang++.exe`) |
| ≥ 1.5 GiB free RAM | allocates a 1 GiB main pool + 512 MiB permanent pool at startup |
@@ -308,7 +94,9 @@ Required layout (`project.vpj` refers to `../includes`):
```text
TransPyC\ # workspace root
├── TransPyC\ # first-generation compiler (seed)
├── TransPyV\ # this repo
├── ViperC\ # this repo
├── Test\ # regression suite (separate repo)
├── exe\ # shipped self-hosted artifact (viperc.exe)
└── includes\ # Viper standard library + includes/ast parser
```
@@ -316,23 +104,23 @@ TransPyC\ # workspace root
```bat
:: everyday use: build any project with the self-hosted compiler
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean
:: build and run immediately
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
:: only when rebuilding the compiler itself (bootstrap seed), use TransPyC
python TransPyC\Projectrans.py --project TransPyV\project.vpj --clean
python TransPyC\Projectrans.py --project ViperC\project.vpj --clean
```
Artifacts land in `<project dir>\<build_dir>\output\<linker.output>` — by default
`<project>\.tpv_build\output\app.exe`. TransPyV itself sets `linker.output` to `TransPyV.exe`,
so its own product is `TransPyV\.tpv_build\output\TransPyV.exe`.
`<project>\__vpcbuild__\output\app.exe`. ViperC itself sets `linker.output` to `viperc.exe`,
so its own product is `ViperC\__vpcbuild__\output\viperc.exe`.
Run the compiler self-test suite:
Run the regression suite (workspace root `Test\`, a separate repo):
```bat
exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
python Test\SequentialRegressionTest.py --exe exe\viperc.exe
```
### CLI flags
@@ -350,14 +138,14 @@ exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
| `--rebuild-includes` | delete only the includes cache (`includes.binary`), forcing a full includes rebuild |
| `--clear-cache` | clear only temp (`.vpi` / `.stub.ll` / `.text.ll`), forcing stub regeneration |
| `--run` | execute the produced binary on success |
| `--repl` | interactive REPL (M1: expression fragment → DLL → call and echo); without `--project` it looks for the `repl.vpj` template project in cwd, then next to `TransPyV.exe` (errors out with a `--project` hint if neither exists); mutually exclusive with `--asan` / `--phase2command` |
| `--repl` | interactive REPL (M1: expression fragment → DLL → call and echo); without `--project` it looks for the `repl.vpj` template project in cwd, then next to `viperc.exe` (errors out with a `--project` hint if neither exists); mutually exclusive with `--asan` / `--phase2command` |
| `--asan` | ASan mode: `opt -passes=asan` first; runtimes must be declared in `project.vpj` `linker.static_libs`, else fail-fast |
| `--phase2command <bat>` | do not run `llc`/link; write the commands into the given `.bat` |
| `--nowarn-register` | suppress Phase A dependency WARNs (cross-module `text.ll` not yet emitted, converges next pass) |
| `--no-llvmpeephole` | disable FunctionPrint local peepholes (T2 store/load forwarding, T3 no-op bitcast) |
| `--detection` | enable MemBuddy allocation detection (`0xCDCD` fill + record-table probe for out-of-bounds writes) |
| `--show-args` | dump all parsed arguments |
| `--version`, `-v` | print Viper / VRTE / TransPyV versions, commit ids, target triple and LLVM version |
| `--version`, `-v` | print Viper / VRTE / ViperC versions, commit ids, target triple and LLVM version |
| `--help`, `-h` | print help |
### Architecture
@@ -415,20 +203,20 @@ out-of-bounds writes.
### Relation to TransPyC
| Dimension | TransPyC | TransPyV |
| Dimension | TransPyC | ViperC |
| --- | --- | --- |
| Implementation | CPython | Viper |
| Role | bootstrap **seed** (only used to produce a stable TransPyV) | **the current compiler** |
| Role | bootstrap **seed** (only used to produce a stable ViperC) | **the current compiler** |
| Self-hosted? | — | ✅ yes, it compiles itself |
| Entry | `TransPyC/Projectrans.py` | `App/main.vp` |
| AST parsing | Python `ast` / in-house vast | `includes/ast` (9 files, in-house) |
| Project config | `project.json` | `project.vpj` |
| Platform | Windows / Linux | Windows (`w32.*`) |
| Platform | Windows / Linux | Windows (`win32.*`) |
### Further reading
- [TransPyC compiler repo (the seed)](https://git.gvsds.com/GVSDS/TransPyC)
- [Viper language docs (wiki)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
- [Bootstrapping analysis and progress](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
- [Project config `project.vpj`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
- [ViperOS (an OS written in Viper)](https://git.gvsds.com/GVSDS/ViperOS)
- [Compilation system (`project.vpj` / `viperbuild` metaprogramming)](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-compilation-system.md)
- [ViperOS (an OS written in Viper)](https://git.gvsds.com/GVSDS/ViperOS)
-47
View File
@@ -1,47 +0,0 @@
import stdio
import t, c
# ============================================================
# c.Asm 内联汇编测试
# ============================================================
def asm_test() -> int:
# 1. 简单无操作数汇编(nop + clobber)
c.Asm("nop", op=[t.ASM_DESCR.CLOBBER_MEMORY])
stdio.printf("asm: nop ok\n")
# 2. 仅汇编文本无 clobber
c.Asm("nop")
stdio.printf("asm: nop2 ok\n")
# 3. 多 clobber
c.Asm("nop", op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_CC])
stdio.printf("asm: multi-clobber ok\n")
# 4. 带输入操作数的汇编(读取 CPU ID)
# 使用 f-string 内联 c.AsmInp
# mov eax, 输入值; nop(保持简单,不使用会产生异常的指令)
val: int = 42
c.Asm(f"""mov eax, {c.AsmInp(val, t.ASM_DESCR.REG_ANY)}
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
stdio.printf("asm: input ok val=%d\n", val)
# 5. 带输出操作数的汇编
# c.AsmOut 获取结果
result: int = 0
c.Asm(f"""mov {c.AsmOut(result, t.ASM_DESCR.OUTPUT_REG)}, 123
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
stdio.printf("asm: output result=%d\n", result)
# 6. 带输入和输出操作数
# result2 = val + 1
result2: int = 0
input_val: int = 10
c.Asm(f"""mov eax, {c.AsmInp(input_val, t.ASM_DESCR.REG_ANY)}
add eax, 1
mov {c.AsmOut(result2, t.ASM_DESCR.OUTPUT_REG)}, eax
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
stdio.printf("asm: in+out result2=%d (expect 11)\n", result2)
return 0
-58
View File
@@ -1,58 +0,0 @@
import t, c
from stdint import *
import stdio
import coro
# TPV-M3~M7 协程综合测试:
# M3: For 内 await(range→While)、coro_sleep 挂起、coro_destroy 帧回池
# M4: raise 存码、await 子帧异常传播、coro_exception 查询
# M5/M6: try/except(链式匹配 + as e 绑定异常码 + 类型码)
# M7: try/finally、异常消息 msg 槽、自定义异常类、帧池复用
class MyError(Exception):
pass
async def leaf(x: int) -> int:
return x * 2
async def task(n: int) -> int:
s: int = 0
for i in range(3): # M3:For 内 await
s = s + await leaf(i)
if s > 4:
await coro.coro_sleep(5) # M3:coro_sleep 挂起(wake 槽 + 轮询)
try:
raise ValueError("boom") # M4/M6:raise 类型 + M7 消息
except ValueError as e: # M5/M6:as e 绑定异常码
s = s + e
return s
async def raiser() -> int:
try:
raise MyError("fin_msg") # M7:自定义异常类 + 消息
finally:
x: int = 99 # M7:finally 正常路径执行
return 0
def main() -> int:
tt: t.CPtr = task(3)
r: int = coro.coro_run(tt)
e: int = coro.coro_exception(tt)
stdio.printf("coro result = %d exc = %d\n", r, e)
coro.coro_destroy(tt) # M3/M7:destroy 回池
t1: t.CPtr = raiser()
r1: int = coro.coro_run(t1)
e1: int = coro.coro_exception(t1)
m1: t.CPtr = coro.coro_exception_msg(t1)
stdio.printf("coro result = %d exc = %d msg = %s\n", r1, e1, m1)
coro.coro_destroy(t1)
t2: t.CPtr = raiser() # 帧池复用
r2: int = coro.coro_run(t2)
e2: int = coro.coro_exception(t2)
stdio.printf("coro result = %d exc = %d\n", r2, e2)
coro.coro_destroy(t2)
return 0
-234
View File
@@ -1,234 +0,0 @@
import t, c
from stdint import *
import stdio
import string
# ============================================================
# c.Attribute 装饰器测试
#
# 测试 @c.Attribute(...) 对函数属性的设置:
# - t.attr.always_inline() -> alwaysinline
# - t.attr.noinline() -> noinline
# - t.attr.noreturn() -> noreturn
# - t.attr.pure() -> readonly (LLVM 函数属性)
# - t.attr.llvm.nounwind -> nounwind
# ============================================================
# Test 1: always_inline 属性
@c.Attribute(t.attr.always_inline())
def AlwaysInlineFunc(x: int) -> int:
return x * 2
# Test 2: noinline 属性
@c.Attribute(t.attr.noinline())
def NoInlineFunc(x: int) -> int:
return x + 100
# Test 3: noreturn 属性(函数确实不返回)
@c.Attribute(t.attr.noreturn())
def NoReturnFunc() -> int:
stdio.printf("NoReturnFunc called (does not return)\n")
return 0
# Test 4: 普通函数对照(无装饰器)
def NormalFunc(x: int) -> int:
return x * 3
# Test 5: 多属性组合
@c.Attribute(t.attr.always_inline(), t.attr.pure())
def MultiAttrFunc(x: int) -> int:
return x + 1
# Test 6: 无括号属性引用 t.attr.packed
@c.Attribute(t.attr.packed)
def PackedAttrFunc(x: int) -> int:
return x - 1
def test_always_inline() -> int:
stdio.printf("--- Test 1: c.Attribute(always_inline) ---\n")
r: int = AlwaysInlineFunc(21)
stdio.printf("AlwaysInlineFunc(21)=%d (expect 42)\n", r)
if r == 42:
stdio.printf("AlwaysInlineFunc OK\n")
else:
stdio.printf("AlwaysInlineFunc FAIL\n")
return 0
def test_noinline() -> int:
stdio.printf("--- Test 2: c.Attribute(noinline) ---\n")
r: int = NoInlineFunc(5)
stdio.printf("NoInlineFunc(5)=%d (expect 105)\n", r)
if r == 105:
stdio.printf("NoInlineFunc OK\n")
else:
stdio.printf("NoInlineFunc FAIL\n")
return 0
def test_noreturn() -> int:
stdio.printf("--- Test 3: c.Attribute(noreturn) ---\n")
stdio.printf("NoReturnFunc declared with noreturn attr\n")
stdio.printf("NoReturnFunc OK (not called to avoid UB)\n")
return 0
def test_normal() -> int:
stdio.printf("--- Test 4: normal function (no attr) ---\n")
r: int = NormalFunc(7)
stdio.printf("NormalFunc(7)=%d (expect 21)\n", r)
if r == 21:
stdio.printf("NormalFunc OK\n")
else:
stdio.printf("NormalFunc FAIL\n")
return 0
def test_multi_attr() -> int:
stdio.printf("--- Test 5: c.Attribute(always_inline, pure) ---\n")
r: int = MultiAttrFunc(10)
stdio.printf("MultiAttrFunc(10)=%d (expect 11)\n", r)
if r == 11:
stdio.printf("MultiAttrFunc OK\n")
else:
stdio.printf("MultiAttrFunc FAIL\n")
return 0
def test_packed_attr() -> int:
stdio.printf("--- Test 6: c.Attribute(packed) ---\n")
r: int = PackedAttrFunc(10)
stdio.printf("PackedAttrFunc(10)=%d (expect 9)\n", r)
if r == 9:
stdio.printf("PackedAttrFunc OK\n")
else:
stdio.printf("PackedAttrFunc FAIL\n")
return 0
# Test 7: t.attr.llvm.nounwind 属性
@c.Attribute(t.attr.llvm.nounwind)
def NoUnwindFunc(x: int) -> int:
return x + 1
# Test 8: t.attr.llvm.noredzone 属性
@c.Attribute(t.attr.llvm.noredzone)
def NoRedZoneFunc(x: int) -> int:
return x + 2
# Test 9: t.attr.llvm.willreturn 属性
@c.Attribute(t.attr.llvm.willreturn)
def WillReturnFunc(x: int) -> int:
return x + 3
# Test 10: t.attr.llvm.mustprogress 属性
@c.Attribute(t.attr.llvm.mustprogress)
def MustProgressFunc(x: int) -> int:
return x + 4
# Test 11: t.attr.const() -> readnone
@c.Attribute(t.attr.const())
def ConstFunc(x: int) -> int:
return x * 0 + 42
# Test 12: 多属性组合(alwaysinline + nounwind + noredzone)
@c.Attribute(t.attr.always_inline(), t.attr.llvm.nounwind, t.attr.llvm.noredzone)
def TripleAttrFunc(x: int) -> int:
return x + 5
def test_nounwind() -> int:
stdio.printf("--- Test 7: c.Attribute(llvm.nounwind) ---\n")
r: int = NoUnwindFunc(10)
stdio.printf("NoUnwindFunc(10)=%d (expect 11)\n", r)
if r == 11:
stdio.printf("NoUnwindFunc OK\n")
else:
stdio.printf("NoUnwindFunc FAIL\n")
return 0
def test_noredzone() -> int:
stdio.printf("--- Test 8: c.Attribute(llvm.noredzone) ---\n")
r: int = NoRedZoneFunc(10)
stdio.printf("NoRedZoneFunc(10)=%d (expect 12)\n", r)
if r == 12:
stdio.printf("NoRedZoneFunc OK\n")
else:
stdio.printf("NoRedZoneFunc FAIL\n")
return 0
def test_willreturn() -> int:
stdio.printf("--- Test 9: c.Attribute(llvm.willreturn) ---\n")
r: int = WillReturnFunc(10)
stdio.printf("WillReturnFunc(10)=%d (expect 13)\n", r)
if r == 13:
stdio.printf("WillReturnFunc OK\n")
else:
stdio.printf("WillReturnFunc FAIL\n")
return 0
def test_mustprogress() -> int:
stdio.printf("--- Test 10: c.Attribute(llvm.mustprogress) ---\n")
r: int = MustProgressFunc(10)
stdio.printf("MustProgressFunc(10)=%d (expect 14)\n", r)
if r == 14:
stdio.printf("MustProgressFunc OK\n")
else:
stdio.printf("MustProgressFunc FAIL\n")
return 0
def test_const_attr() -> int:
stdio.printf("--- Test 11: c.Attribute(const) ---\n")
r: int = ConstFunc(99)
stdio.printf("ConstFunc(99)=%d (expect 42)\n", r)
if r == 42:
stdio.printf("ConstFunc OK\n")
else:
stdio.printf("ConstFunc FAIL\n")
return 0
def test_triple_attr() -> int:
stdio.printf("--- Test 12: c.Attribute(always_inline, nounwind, noredzone) ---\n")
r: int = TripleAttrFunc(10)
stdio.printf("TripleAttrFunc(10)=%d (expect 15)\n", r)
if r == 15:
stdio.printf("TripleAttrFunc OK\n")
else:
stdio.printf("TripleAttrFunc FAIL\n")
return 0
def attr_test() -> int:
stdio.printf("=== attr_test: c.Attribute 装饰器测试 ===\n\n")
test_always_inline()
test_noinline()
test_noreturn()
test_normal()
test_multi_attr()
test_packed_attr()
test_nounwind()
test_noredzone()
test_willreturn()
test_mustprogress()
test_const_attr()
test_triple_attr()
stdio.printf("\n=== attr_test 完成 ===\n")
return 0
-58
View File
@@ -1,58 +0,0 @@
import stdio
import t, c
# ============================================================
# AugAssign 测试:+= -= *= /= %= &= |= ^= <<= >>=
# ============================================================
g_count: int = 0
def test_local() -> int:
x: int = 10
x += 5
stdio.printf("local +=: %d\n", x)
x -= 3
stdio.printf("local -=: %d\n", x)
x *= 2
stdio.printf("local *=: %d\n", x)
x /= 4
stdio.printf("local /=: %d\n", x)
x %= 7
stdio.printf("local %%=: %d\n", x)
return 0
def test_global_aug() -> int:
global g_count
g_count += 1
stdio.printf("global +=: %d\n", g_count)
g_count += 10
stdio.printf("global +=: %d\n", g_count)
g_count -= 3
stdio.printf("global -=: %d\n", g_count)
return 0
def test_bitops() -> int:
b: int = 0xFF
b &= 0x0F
stdio.printf("bit &=: %d\n", b)
b |= 0x30
stdio.printf("bit |=: %d\n", b)
b ^= 0xFF
stdio.printf("bit ^=: %d\n", b)
b = 1
b <<= 4
stdio.printf("bit <<=: %d\n", b)
b >>= 2
stdio.printf("bit >>=: %d\n", b)
return 0
def augassign_test() -> int:
test_local()
test_global_aug()
test_bitops()
return 0
-29
View File
@@ -1,29 +0,0 @@
import t, c
import stdlib
import circ_b
# ============================================================
# 循环引用测试:模块 A
#
# circ_a imports circ_b, circ_b imports circ_a — 循环引用
# ClassA.MakeB() 返回 circ_b.ClassB 实例(跨模块构造 + 类型注解)
# ============================================================
class ClassA:
x: int
def __new__(self, val: int):
r: ClassA | t.CPtr = stdlib.malloc(ClassA.__sizeof__())
return r
def __init__(self, val: int):
self.x = val
def GetValue(self) -> int:
return self.x
def MakeB(self) -> circ_b.ClassB | t.CPtr:
b: circ_b.ClassB | t.CPtr = circ_b.ClassB(self.x)
return b
-29
View File
@@ -1,29 +0,0 @@
import t, c
import stdlib
import circ_a
# ============================================================
# 循环引用测试:模块 B
#
# circ_b imports circ_a, circ_a imports circ_b — 循环引用
# ClassB.MakeA() 返回 circ_a.ClassA 实例(跨模块构造 + 类型注解)
# ============================================================
class ClassB:
y: int
def __new__(self, val: int):
r: ClassB | t.CPtr = stdlib.malloc(ClassB.__sizeof__())
return r
def __init__(self, val: int):
self.y = val
def GetValue(self) -> int:
return self.y
def MakeA(self) -> circ_a.ClassA | t.CPtr:
a: circ_a.ClassA | t.CPtr = circ_a.ClassA(self.y)
return a
-66
View File
@@ -1,66 +0,0 @@
import t, c
import stdio
import circ_a
import circ_b
# ============================================================
# 循环引用测试入口
#
# 验证 A → B → A 的循环引用能正确编译和运行:
# - Test 1: ClassA.MakeB() 创建 ClassB 实例
# - Test 2: ClassB.MakeA() 创建 ClassA 实例
# - Test 3: 值传递链 A(20) → B → A
# ============================================================
def circ_test() -> int:
stdio.printf("circ: === Test Start ===\n")
# ============================================================
# Test 1: 创建 ClassA,调用 MakeB 获取 ClassB 实例
# ============================================================
stdio.printf("circ: === Test 1: A.MakeB() ===\n")
a: circ_a.ClassA | t.CPtr = circ_a.ClassA(10)
av: int = a.GetValue()
stdio.printf("circ: a.GetValue()=%d (expected 10)\n", av)
if av != 10:
stdio.printf("[FAIL] a.GetValue()=%d expected 10\n", av)
return 1
b: circ_b.ClassB | t.CPtr = a.MakeB()
bv: int = b.GetValue()
stdio.printf("circ: b.GetValue()=%d (expected 10)\n", bv)
if bv != 10:
stdio.printf("[FAIL] b.GetValue()=%d expected 10\n", bv)
return 1
# ============================================================
# Test 2: 从 B 调用 MakeA 获取 ClassA 实例
# ============================================================
stdio.printf("circ: === Test 2: B.MakeA() ===\n")
a2: circ_a.ClassA | t.CPtr = b.MakeA()
av2: int = a2.GetValue()
stdio.printf("circ: a2.GetValue()=%d (expected 10)\n", av2)
if av2 != 10:
stdio.printf("[FAIL] a2.GetValue()=%d expected 10\n", av2)
return 1
# ============================================================
# Test 3: 值传递链 A(20) → B → A
# ============================================================
stdio.printf("circ: === Test 3: value chain A(20)->B->A ===\n")
a3: circ_a.ClassA | t.CPtr = circ_a.ClassA(20)
b3: circ_b.ClassB | t.CPtr = a3.MakeB()
a4: circ_a.ClassA | t.CPtr = b3.MakeA()
final: int = a4.GetValue()
stdio.printf("circ: final=%d (expected 20)\n", final)
if final != 20:
stdio.printf("[FAIL] final=%d expected 20\n", final)
return 1
stdio.printf("circ: === All Tests Passed ===\n")
return 0
-45
View File
@@ -1,45 +0,0 @@
import stdio
import t, c
# ============================================================
# 闭包 + nonlocal/global 测试
# ============================================================
# 全局变量
g_count: int = 0
# global 关键字测试:修改全局变量
def test_global() -> int:
global g_count
g_count = g_count + 1
return g_count
# nonlocal 关键字测试:嵌套函数修改外层局部变量
def make_counter() -> t.CPtr:
count: int = 0
# 内部函数(闭包)捕获 count
def counter() -> int:
nonlocal count
count = count + 1
return count
return counter
def closure_test() -> int:
# global 测试
stdio.printf("global: %d\n", test_global())
stdio.printf("global: %d\n", test_global())
stdio.printf("global: %d\n", test_global())
# nonlocal/闭包测试
f: t.CPtr = make_counter()
stdio.printf("closure: %d\n", f())
stdio.printf("closure: %d\n", f())
stdio.printf("closure: %d\n", f())
return 0
-183
View File
@@ -1,183 +0,0 @@
import stdio
import t, c
import string
# ============================================================
# deco_test - t/c 装饰器测试
#
# 测试 t.CDefine / t.CExport / t.CInline / t.CExtern / t.State
# ============================================================
# Test 1: t.CDefine - 常量定义
MAX_VALUE: t.CDefine = 256
PI_APPROX: t.CDefine = 314
# Test 10: t.CDefine 作为 t.CArray 的 count
ARR_SIZE: t.CDefine = 8
# Test 2: t.CExport - 导出函数标记
def ExportedAdd(a: int, b: int) -> int | t.CExport:
return a + b
# Test 3: t.CInline - 内联函数标记
def InlineSquare(x: int) -> int | t.CInline:
return x * x
# Test 4: 普通函数(无装饰器,作为对照)
def NormalSub(a: int, b: int) -> int:
return a - b
# Test 5: t.CExport 与 t.CInline 组合
def ExportedInlineCube(x: int) -> int | t.CExport | t.CInline:
return x * x * x
# Test 6: t.CExport 返回指针
def ExportedFindChar(s: str, ch: int) -> str | t.CExport:
return string.strchr(s, ch)
def test_cdefine():
stdio.printf("--- Test 1: t.CDefine ---\n")
stdio.printf("MAX_VALUE=%d (expect 256)\n", MAX_VALUE)
stdio.printf("PI_APPROX=%d (expect 314)\n", PI_APPROX)
if MAX_VALUE == 256:
stdio.printf("MAX_VALUE OK\n")
else:
stdio.printf("MAX_VALUE FAIL\n")
if PI_APPROX == 314:
stdio.printf("PI_APPROX OK\n")
else:
stdio.printf("PI_APPROX FAIL\n")
def test_cexport():
stdio.printf("--- Test 2: t.CExport ---\n")
r: int = ExportedAdd(3, 4)
stdio.printf("ExportedAdd(3,4)=%d (expect 7)\n", r)
if r == 7:
stdio.printf("ExportedAdd OK\n")
else:
stdio.printf("ExportedAdd FAIL\n")
def test_cinline():
stdio.printf("--- Test 3: t.CInline ---\n")
r: int = InlineSquare(5)
stdio.printf("InlineSquare(5)=%d (expect 25)\n", r)
if r == 25:
stdio.printf("InlineSquare OK\n")
else:
stdio.printf("InlineSquare FAIL\n")
def test_deco_normal():
stdio.printf("--- Test 4: normal function ---\n")
r: int = NormalSub(10, 3)
stdio.printf("NormalSub(10,3)=%d (expect 7)\n", r)
if r == 7:
stdio.printf("NormalSub OK\n")
else:
stdio.printf("NormalSub FAIL\n")
def test_export_inline():
stdio.printf("--- Test 5: t.CExport | t.CInline ---\n")
r: int = ExportedInlineCube(3)
stdio.printf("ExportedInlineCube(3)=%d (expect 27)\n", r)
if r == 27:
stdio.printf("ExportedInlineCube OK\n")
else:
stdio.printf("ExportedInlineCube FAIL\n")
def test_export_ptr():
stdio.printf("--- Test 6: t.CExport return ptr ---\n")
p: str = ExportedFindChar("Hello", 108) # 'l' = 108
if p is not None:
stdio.printf("ExportedFindChar found, char=%d (expect 108)\n", c.Deref(p))
if c.Deref(p) == 108:
stdio.printf("ExportedFindChar OK\n")
else:
stdio.printf("ExportedFindChar FAIL\n")
else:
stdio.printf("ExportedFindChar NOT FOUND (FAIL)\n")
# Test 7: t.CExtern | t.CExport - 外部声明函数(pass 体)
# 声明一个外部函数,翻译器应生成 declare 而非 define
def ExternalDecl(x: int) -> int | t.CExtern | t.CExport: pass
# Test 8: t.State - 状态声明(等价于 CExtern | CExport)
def StateDecl(x: int) -> int | t.State: pass
# Test 9: t.State 返回 void
def StateVoidDecl() -> t.State: pass
def test_extern_decl():
stdio.printf("--- Test 7: t.CExtern | t.CExport ---\n")
# 外部声明函数,不调用(应由链接器解析符号)
stdio.printf("ExternalDecl declared (not called)\n")
stdio.printf("ExternalDecl OK\n")
def test_state_decl():
stdio.printf("--- Test 8: t.State ---\n")
stdio.printf("StateDecl declared (not called)\n")
stdio.printf("StateDecl OK\n")
def test_state_void_decl():
stdio.printf("--- Test 9: t.State (void) ---\n")
stdio.printf("StateVoidDecl declared (not called)\n")
stdio.printf("StateVoidDecl OK\n")
# Test 10: t.CDefine 作为 t.CArray count
# ARR_SIZE: t.CDefine = 8 已在文件顶部定义
def test_cdefine_array_count():
stdio.printf("--- Test 10: t.CArray[elem, CDefine] ---\n")
# 使用 CDefine 常量名作为数组 count
arr: t.CArray[int, ARR_SIZE]
i: int
# 初始化: arr[i] = i * 10
for i in range(ARR_SIZE):
arr[i] = i * 10
# 求和验证: 0+10+20+...+70 = 280
total: int = 0
for i in range(ARR_SIZE):
total += arr[i]
stdio.printf("ARR_SIZE=%d (expect 8)\n", ARR_SIZE)
stdio.printf("arr sum=%d (expect 280)\n", total)
stdio.printf("arr[0]=%d (expect 0)\n", arr[0])
stdio.printf("arr[7]=%d (expect 70)\n", arr[7])
if ARR_SIZE == 8 and total == 280 and arr[0] == 0 and arr[7] == 70:
stdio.printf("CArray CDefine count OK\n")
else:
stdio.printf("CArray CDefine count FAIL\n")
def deco_test() -> int:
stdio.printf("=== deco_test: t/c 装饰器测试 ===\n\n")
test_cdefine()
test_cexport()
test_cinline()
test_deco_normal()
test_export_inline()
test_export_ptr()
test_extern_decl()
test_state_decl()
test_state_void_decl()
test_cdefine_array_count()
stdio.printf("\n=== deco_test 完成 ===\n")
return 0
-9
View File
@@ -1,9 +0,0 @@
import stdio
import t, c
def deref_min_test() -> int:
s: str = "hello"
v: int = c.Deref(s)
stdio.printf("deref: %d\n", v)
return 0
-13
View File
@@ -1,13 +0,0 @@
import stdio
import t, c
def deref_test() -> int:
s: str = "hello"
sp: str = s
slen: int = 0
while c.Deref(sp) != 0:
slen = slen + 1
sp = sp + 1
stdio.printf("ptr: len(hello)=%d\n", slen)
return 0
-144
View File
@@ -1,144 +0,0 @@
import stdio
import t, c
import string
# ============================================================
# eq_test - ==、!=、is、is not、None 比较测试
#
# 验证比较表达式的翻译,特别是 None 常量和 is/is not 操作符
# ============================================================
def test_int_eq():
stdio.printf("--- Test 1: int == / != ---\n")
a: int = 42
b: int = 42
c3: int = 99
if a == b:
stdio.printf("int == int (same) OK\n")
else:
stdio.printf("int == int (same) FAIL\n")
if a != c3:
stdio.printf("int != int (diff) OK\n")
else:
stdio.printf("int != int (diff) FAIL\n")
if not (a == c3):
stdio.printf("not (int == diff) OK\n")
else:
stdio.printf("not (int == diff) FAIL\n")
def test_int_cmp():
stdio.printf("--- Test 2: int < > <= >= ---\n")
a: int = 5
b: int = 10
if a < b:
stdio.printf("int < OK\n")
else:
stdio.printf("int < FAIL\n")
if b > a:
stdio.printf("int > OK\n")
else:
stdio.printf("int > FAIL\n")
if a <= 5:
stdio.printf("int <= OK\n")
else:
stdio.printf("int <= FAIL\n")
if b >= 10:
stdio.printf("int >= OK\n")
else:
stdio.printf("int >= FAIL\n")
def test_ptr_is_none():
stdio.printf("--- Test 3: ptr is None / is not None ---\n")
# strchr 找到字符返回非空指针,找不到返回 None
p: str = string.strchr("Hello", 108) # 'l' = 108
if p is not None:
stdio.printf("strchr found, is not None OK\n")
else:
stdio.printf("strchr found, is not None FAIL\n")
p2: str = string.strchr("Hello", 122) # 'z' = 122, not found
if p2 is None:
stdio.printf("strchr not found, is None OK\n")
else:
stdio.printf("strchr not found, is None FAIL\n")
def test_ptr_assign_none():
stdio.printf("--- Test 4: assign None and compare ---\n")
p: str = None
if p is None:
stdio.printf("None assign, is None OK\n")
else:
stdio.printf("None assign, is None FAIL\n")
if p is not None:
stdio.printf("None assign, is not None FAIL\n")
else:
stdio.printf("None assign, is not None OK\n")
def test_ptr_eq_none():
stdio.printf("--- Test 5: ptr == None / != None ---\n")
p: str = string.strstr("Hello World", "World")
if p != None:
stdio.printf("strstr found, != None OK\n")
else:
stdio.printf("strstr found, != None FAIL\n")
p2: str = string.strstr("Hello", "xyz")
if p2 == None:
stdio.printf("strstr not found, == None OK\n")
else:
stdio.printf("strstr not found, == None FAIL\n")
def test_bool_logic():
stdio.printf("--- Test 6: bool and/or ---\n")
a: int = 1
b: int = 0
if a and b:
stdio.printf("1 and 0 = true FAIL\n")
else:
stdio.printf("1 and 0 = false OK\n")
if a or b:
stdio.printf("1 or 0 = true OK\n")
else:
stdio.printf("1 or 0 = true FAIL\n")
if not b:
stdio.printf("not 0 = true OK\n")
else:
stdio.printf("not 0 = true FAIL\n")
def eq_test() -> int:
stdio.printf("=== eq_test: ==、!=、is、None 比较测试 ===\n\n")
test_int_eq()
test_int_cmp()
test_ptr_is_none()
test_ptr_assign_none()
test_ptr_eq_none()
test_bool_logic()
stdio.printf("\n=== eq_test 完成 ===\n")
return 0
-84
View File
@@ -1,84 +0,0 @@
import stdio
import t, c
# ============================================================
# 联合体(t.CUnion)含浮点字段
# ============================================================
class DataUnion(t.CUnion):
i: int
f: t.CFloat
l: int64
# ============================================================
# 主函数
# ============================================================
def float_test() -> int:
# ============================================================
# 测试 1: 基本浮点变量(float / double)
# ============================================================
f: t.CFloat = 3.14
stdio.printf("float: f=%f\n", f)
d: t.CDouble = 3.141592653589793
stdio.printf("double: d=%lf\n", d)
# 重新赋值
f = 2.5
stdio.printf("float: f=%f\n", f)
# ============================================================
# 测试 2: 联合体浮点字段
# ============================================================
u: DataUnion
u.f = 3.14
stdio.printf("union: u.f=%f\n", u.f)
# 写入 i 字段后,f 的值已被覆盖
u.i = 42
stdio.printf("union: u.i=%d (after f overwritten)\n", u.i)
# ============================================================
# 测试 3: 联合体共享内存验证(float 与 int 共享)
# ============================================================
u2: DataUnion
u2.i = 0
u2.f = 1.0
# 写入 f 后,i 的值应不再是 0
if u2.i != 0:
stdio.printf("union: float overwrites int verified\n")
# ============================================================
# 测试 4: 浮点算术运算(fadd/fsub/fmul/fdiv/frem)
# ============================================================
a: t.CFloat = 1.5
b: t.CFloat = 2.5
stdio.printf("arith: %.2f + %.2f = %.2f\n", a, b, a + b)
stdio.printf("arith: %.2f - %.2f = %.2f\n", a, b, a - b)
stdio.printf("arith: %.2f * %.2f = %.2f\n", a, b, a * b)
stdio.printf("arith: %.2f / %.2f = %.2f\n", a, b, a / b)
# ============================================================
# 测试 5: double 算术运算
# ============================================================
x: t.CDouble = 10.0
y: t.CDouble = 3.0
stdio.printf("arith: %.4lf + %.4lf = %.4lf\n", x, y, x + y)
stdio.printf("arith: %.4lf - %.4lf = %.4lf\n", x, y, x - y)
stdio.printf("arith: %.4lf * %.4lf = %.4lf\n", x, y, x * y)
stdio.printf("arith: %.4lf / %.4lf = %.4lf\n", x, y, x / y)
# ============================================================
# 测试 6: 浮点取模 (frem)
# ============================================================
stdio.printf("arith: 10.0 %% 3.0 = %.4lf\n", x % y)
# ============================================================
# 测试 7: int 与 float 混合运算(int 自动转 float)
# ============================================================
n: int = 3
stdio.printf("arith: %d + %.2f = %.2f\n", n, a, n + a)
return 0
-23
View File
@@ -1,23 +0,0 @@
import stdio
import t, c
def flow_test() -> int:
i: int = 0
total: int = 0
while i < 5:
total = total + i
i = i + 1
stdio.printf("while: total=%d i=%d\n", total, i)
for k in range(10):
if k == 3:
break
stdio.printf("break: k=%d\n", k)
for m in range(5):
if m == 2:
continue
stdio.printf("continue: m=%d\n", m)
return 0
-8
View File
@@ -1,8 +0,0 @@
import stdio
import t, c
def for_test() -> int:
for j in range(5):
stdio.printf("for: j=%d\n", j)
return 0
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more