@calcit/procs 0.12.18 → 0.12.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/editing-history/202504160117-wasm-codegen-and-catalog-update.md +40 -0
- package/editing-history/202507160207-wasm-encoder-migration.md +48 -0
- package/editing-history/202507161633-wasm-data-structures.md +61 -0
- package/editing-history/202604151211-record-nth-optimization.md +44 -0
- package/editing-history/20260416-1936-predicate-narrowing-expansion.md +31 -0
- package/editing-history/202604160131-wasm-math-and-test-integration.md +28 -0
- package/editing-history/202604161507-wasm-data-structures-and-rfc-rename.md +27 -0
- package/editing-history/202604161520-wasm-bitwise-and-match.md +21 -0
- package/editing-history/202604161542-wasm-cross-ns-host-imports.md +38 -0
- package/editing-history/20260417-0026-wasm-rest-args.md +62 -0
- package/editing-history/202604170048-wasm-type-of.md +70 -0
- package/editing-history/202604170051-wasm-derived-predicates.md +34 -0
- package/editing-history/202604170132-monomorphize-map-filter.md +50 -0
- package/editing-history/202604170135-monomorphize-includes-reverse.md +31 -0
- package/editing-history/202604170140-fold-type-predicates.md +44 -0
- package/editing-history/202604170154-generic-dispatch-records-tuples.md +52 -0
- package/lib/calcit.procs.mjs +39 -6
- package/lib/package.json +4 -3
- package/package.json +4 -3
- package/rfc/02-04-runtime-traits-plan.md +613 -0
- package/rfc/02-14-project-modernization-roadmap.md +229 -0
- package/rfc/02-17-register-platform-api-rfc.md +115 -0
- package/rfc/02-18-language-theory-evolution-plan.md +367 -0
- package/rfc/02-23-optional-record-macro-plan.md +30 -0
- package/rfc/03-05-function-schema-dual-track-rfc.md +162 -0
- package/rfc/03-16-runtime-boundary-refactor-plan.md +546 -0
- package/rfc/03-18-query-def-tree-show-chunked-display-plan.md +301 -0
- package/rfc/04-13-call-arg-literal-rewrite-rfc.md +205 -0
- package/rfc/04-13-type-slot-mechanism-rfc.md +194 -0
- package/rfc/04-15-match-syntax-rfc.md +175 -0
- package/rfc/04-15-type-directed-optimization-catalog.md +170 -0
- package/rfc/04-15-wasm-compilation-feasibility.md +236 -0
- package/rfc/04-16-wasm-data-structures.md +192 -0
- package/rfc/README.md +40 -0
- package/ts-src/calcit.procs.mts +33 -6
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 202504160117 — 优化目录更新 + WASM codegen demo
|
|
2
|
+
|
|
3
|
+
## 优化目录更新
|
|
4
|
+
|
|
5
|
+
更新 `drafts/04-15-type-directed-optimization-catalog.md`:
|
|
6
|
+
|
|
7
|
+
- 标记 P0、P1、P2、P5、P7 为已完成,附 commit hash
|
|
8
|
+
- 标记 P3、P4 为推迟,附推迟原因
|
|
9
|
+
- 更新 Scope 行:从 `TernaryTreeList<ScopePair>` 改为 `Vec<ScopePair>`
|
|
10
|
+
- P5 描述更新为实际采用的 Vec 方案(而非原规划的 O(1) 方案)
|
|
11
|
+
- 新增「执行记录」节替代原「推荐执行顺序」,含基准数据(836ms→718ms,~14%)
|
|
12
|
+
|
|
13
|
+
## WASM codegen 实验性功能
|
|
14
|
+
|
|
15
|
+
### 新增文件
|
|
16
|
+
|
|
17
|
+
- `src/codegen/emit_wasm.rs` — ~440 行 WAT 代码生成器
|
|
18
|
+
- `demos/wasm-demo.cirru` — 示例程序(fibo、factorial、add-two)
|
|
19
|
+
- `docs/wasm-codegen.md` — 使用文档 + 未来改进路线
|
|
20
|
+
|
|
21
|
+
### 修改文件
|
|
22
|
+
|
|
23
|
+
- `src/codegen.rs` — 注册 `emit_wasm` 模块
|
|
24
|
+
- `src/cli_args.rs` — 新增 `EmitWasmCommand`(`cr <entry> wasm`)
|
|
25
|
+
- `src/bin/cr.rs` — 新增 `run_wasm_codegen` 入口
|
|
26
|
+
|
|
27
|
+
### 设计要点
|
|
28
|
+
|
|
29
|
+
- **纯 WAT 文本输出** — 零额外依赖,不引入 wasm-encoder 等 crate
|
|
30
|
+
- **All-f64 类型策略** — 所有值统一为 f64,Bool 用 1.0/0.0
|
|
31
|
+
- **支持子集** — defn、if、let、算术(&+/&-/&\*/&/)、比较(&</&>/&=)、recur、函数调用
|
|
32
|
+
- **不支持** — 字符串、Record/Map/Set、method dispatch、IO、可变参数
|
|
33
|
+
|
|
34
|
+
### 验证
|
|
35
|
+
|
|
36
|
+
- wasmtime compile 验证 WAT 合法
|
|
37
|
+
- wasmtime run 验证 fibo(10)=89、factorial(10)=3628800、add-two(3.5,2.5)=6
|
|
38
|
+
- cargo test 246/246 pass
|
|
39
|
+
- cargo clippy -- -D warnings clean
|
|
40
|
+
- yarn check-all pass
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# wasm-encoder 迁移:WAT 文本 → 二进制 .wasm
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
将 WASM codegen 从手工拼接 WAT 文本字符串改为使用 `wasm-encoder` crate 直接生成二进制 `.wasm`,
|
|
6
|
+
同时将测试运行时从 wasmtime CLI 替换为 Node.js 内置的 `WebAssembly` API。
|
|
7
|
+
|
|
8
|
+
## 动机
|
|
9
|
+
|
|
10
|
+
- wasmtime 是重量级依赖,CI 需要额外安装步骤,且跨版本 CLI 接口不稳定(v14/v25 breaking changes)
|
|
11
|
+
- WAT 字符串拼接容易出错且难以调试
|
|
12
|
+
- Node.js 自带 WebAssembly 运行时,零额外依赖
|
|
13
|
+
|
|
14
|
+
## 关键知识点
|
|
15
|
+
|
|
16
|
+
### wasm-encoder 用法
|
|
17
|
+
|
|
18
|
+
- `wasm-encoder = "0.246.2"`,仅 1 个传递依赖 `leb128fmt`
|
|
19
|
+
- TypeSection → FunctionSection → ExportSection → CodeSection → module.finish()
|
|
20
|
+
- `F64Const` 接受 `Ieee64` 而非 `f64`,需 `Ieee64::from(val)` 包装
|
|
21
|
+
- `Function::new(locals_vec)` + `instruction(&Instruction::...)` + `instruction(&Instruction::End)`
|
|
22
|
+
|
|
23
|
+
### WASM block depth 追踪
|
|
24
|
+
|
|
25
|
+
- `br N` 中 N 是相对深度(0 = 当前块)
|
|
26
|
+
- `if` 块会增加一层嵌套,所以在 `if` 内部 `br` 到外层 `loop` 需要 depth=1
|
|
27
|
+
- 解决方案:`WasmGenCtx` 添加 `block_depth` 字段,`emit_if` 时 +1,`recur` 使用 `Br(ctx.block_depth)`
|
|
28
|
+
|
|
29
|
+
### 两阶段编译
|
|
30
|
+
|
|
31
|
+
- 函数互调需要预知 fn_index,所以:
|
|
32
|
+
1. 第一遍:收集函数签名和索引映射 `HashMap<String, u32>`
|
|
33
|
+
2. 第二遍:编译函数体,失败的函数用 stub `[f64_const(0.0)]` 保持索引稳定
|
|
34
|
+
|
|
35
|
+
### 比较运算的 i32→f64 转换
|
|
36
|
+
|
|
37
|
+
- WASM 比较指令(f64.lt/gt/eq)返回 i32
|
|
38
|
+
- 用 `select(1.0, 0.0, cmp_result)` 转为 f64
|
|
39
|
+
- `if` 条件用 `f64.ne(val, 0.0)` 转回 i32
|
|
40
|
+
|
|
41
|
+
## 修改文件
|
|
42
|
+
|
|
43
|
+
- `src/codegen/emit_wasm.rs` — 完全重写
|
|
44
|
+
- `scripts/test-wasm.mjs` — 新增 Node.js 测试运行器
|
|
45
|
+
- `scripts/test-wasm.sh` — 简化为生成 + node 调用
|
|
46
|
+
- `.github/workflows/test.yaml` — 移除 wasmtime 安装步骤
|
|
47
|
+
- `Cargo.toml` / `Cargo.lock` — 添加 wasm-encoder
|
|
48
|
+
- `docs/wasm-codegen.md` — 更新文档
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# WASM Data Structures: List, Map, Set + Optional Args + Raise
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Comprehensive data structure support for WASM codegen: list (16 ops), map (9 ops), set (6 ops), plus `raise` and optional argument handling.
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
### 1. Optional Args (`CalcitArgLabel::OptionalMark`)
|
|
10
|
+
- `compile_fn`: Skip `OptionalMark` markers — they aren't parameters, just annotations. The actual Idx labels become WASM params.
|
|
11
|
+
- Added `fn_arity` map alongside `fn_index` to track each function's WASM arity (counting only `Idx` labels).
|
|
12
|
+
- At call sites (`Calcit::Import`, `Calcit::Symbol`), pad missing optional args with `f64.const 0` (nil) to match target arity.
|
|
13
|
+
- **Key insight**: The Calcit preprocessor doesn't always fill nil for omitted optional args in the code tree; WASM codegen must handle the arity mismatch at the call site.
|
|
14
|
+
|
|
15
|
+
### 2. Raise
|
|
16
|
+
- `CalcitProc::Raise` → evaluate and drop all args, emit `Instruction::Unreachable`.
|
|
17
|
+
|
|
18
|
+
### 3. Memory Layouts
|
|
19
|
+
- **List**: `[count:f64] [elem0:f64] [elem1:f64] ...` — size `(1+count)*8`
|
|
20
|
+
- **Map**: `[count:f64] [key0:f64] [val0:f64] [key1:f64] [val1:f64] ...` — size `(1+count*2)*8`
|
|
21
|
+
- **Set**: Same as list — `[count:f64] [elem0:f64] ...`
|
|
22
|
+
|
|
23
|
+
### 4. Helper Functions
|
|
24
|
+
- `emit_bump_alloc_dynamic(ctx, size_local, ptr_local)` — bump allocator with dynamic size
|
|
25
|
+
- `emit_ptr_to_i32(ctx, expr)` — evaluate expression to i32 pointer in a local
|
|
26
|
+
- `emit_load_count_i32(ctx, ptr)` — load count from first f64 slot as i32
|
|
27
|
+
- `emit_addr_offset(ctx, base, offset)` — compute base+offset into new local
|
|
28
|
+
- `emit_copy_f64_loop(ctx, dst, src, n)` — copy N f64 slots between addresses
|
|
29
|
+
- `emit_ds_count(ctx, args)` — shared count accessor (list/map/set)
|
|
30
|
+
- `emit_ds_empty(ctx, args)` — shared empty? check
|
|
31
|
+
- `emit_alloc_with_count(ctx, count, total_slots)` — allocate and write count header
|
|
32
|
+
|
|
33
|
+
### 5. List Operations (16)
|
|
34
|
+
- Constructor: `emit_list_new` — `[] elem0 elem1 ...`
|
|
35
|
+
- Access: `emit_list_nth`, `emit_list_first`, `emit_list_rest`
|
|
36
|
+
- Mutation: `emit_list_append`, `emit_list_prepend`, `emit_list_butlast`
|
|
37
|
+
- Slicing: `emit_list_slice`, `emit_list_reverse`, `emit_list_concat`
|
|
38
|
+
- Update: `emit_list_assoc`, `emit_list_dissoc`
|
|
39
|
+
- Query: `emit_list_contains`, `emit_list_includes`
|
|
40
|
+
|
|
41
|
+
### 6. Map Operations (9)
|
|
42
|
+
- Constructor: `emit_map_new` — `&{} key val ...`
|
|
43
|
+
- Access: `emit_map_get_op` — linear key scan, return value or nil
|
|
44
|
+
- Mutation: `emit_map_assoc` (scan for existing key, branch: update vs append), `emit_map_dissoc` (scan + copy-with-skip)
|
|
45
|
+
- Query: `emit_map_contains`, `emit_map_includes`
|
|
46
|
+
- Transform: `emit_map_to_pairs` — creates list of 2-elem lists (nested allocation in loop)
|
|
47
|
+
|
|
48
|
+
### 7. Set Operations (6)
|
|
49
|
+
- Constructor: `emit_set_new` — `#{} elem ...`
|
|
50
|
+
- Access: `emit_set_includes` (delegates to list_includes — same layout)
|
|
51
|
+
- Mutation: `emit_set_include` (scan + conditional append), `emit_set_exclude` (scan + copy-with-skip)
|
|
52
|
+
|
|
53
|
+
## WASM Patterns Used
|
|
54
|
+
- **Copy loop**: `block/loop/br_if/br` with i32 counter for bulk f64 element copying
|
|
55
|
+
- **Scan loop**: Same pattern with `f64.eq` comparison for key/value lookup; `found_idx` local with -1 sentinel
|
|
56
|
+
- **Conditional allocation**: `if/else` for map_assoc (new key vs update) and set_include (already present vs append)
|
|
57
|
+
- **Select instruction**: For boolean returns (`contains?`, `empty?`) — `select(true_val, false_val, condition)`
|
|
58
|
+
|
|
59
|
+
## Test Results
|
|
60
|
+
- 90 WASM checks (55 existing + 35 new) — all pass
|
|
61
|
+
- 246 cargo tests — all pass
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# `&record:nth` — Compile-Time Record Field Index Optimization
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
新增 `NativeRecordNth` / `&record:nth` 过程,在预处理阶段将 record 字段访问优化为 O(1) 索引访问,绕过运行时的类型分发链和二分查找。
|
|
6
|
+
|
|
7
|
+
## 优化前访问路径
|
|
8
|
+
|
|
9
|
+
- `(:field record)` → 重写为 `(get arg :field)` → 运行时 6 次类型判断 (`nil?` → `string?` → `map?` → `list?` → `tuple?` → `record?`) → `&record:get` → `index_of()` O(log n) 二分查找
|
|
10
|
+
- `&record:get record :field` → 运行时 `index_of()` O(log n) 二分查找
|
|
11
|
+
|
|
12
|
+
## 优化后访问路径
|
|
13
|
+
|
|
14
|
+
当预处理阶段可以通过 `resolve_type_value` + `as_struct()` 解析出 record 的 struct 定义时:
|
|
15
|
+
|
|
16
|
+
- `(:field record)` → 直接重写为 `(&record:nth record <compile-time-idx>)` → 运行时 O(1) `values[idx]`
|
|
17
|
+
- `&record:get record :field` → 重写为 `(&record:nth record <compile-time-idx>)` → 运行时 O(1)
|
|
18
|
+
|
|
19
|
+
## 触发条件
|
|
20
|
+
|
|
21
|
+
优化仅在类型信息明确时激活:
|
|
22
|
+
- record 由 `%Struct ...` 构造器在本地 `let` 中创建
|
|
23
|
+
- 函数参数有显式 struct 类型的 schema 标注
|
|
24
|
+
|
|
25
|
+
若类型不明,回 fallback 到原有路径,不影响运行时行为。
|
|
26
|
+
|
|
27
|
+
## 修改文件
|
|
28
|
+
|
|
29
|
+
1. **`src/calcit/proc_name.rs`** — 新增 `NativeRecordNth` 变体 (`&record:nth`),签名 `(record, number) → dynamic`
|
|
30
|
+
2. **`src/builtins/records.rs`** — 实现 `record_nth(xs)` 运行时函数,直接 `values[idx]` 访问
|
|
31
|
+
3. **`src/builtins.rs`** — 分发表连线 `NativeRecordNth => records::record_nth`
|
|
32
|
+
4. **`src/runner/preprocess/mod.rs`** — 两个重写点:
|
|
33
|
+
- Tag-call (`Calcit::Tag` as head):解析 arg 类型后索引重写
|
|
34
|
+
- `NativeRecordGet` proc call:解析 record arg 类型后索引重写
|
|
35
|
+
5. **`src/runner/preprocess/type_inference.rs`** — `infer_record_nth_type()` 根据 struct 的 `field_types[idx]` 推断返回类型
|
|
36
|
+
6. **`src/runner/preprocess/type_checking.rs`** — 将 `NativeRecordNth` 加入 core arg type check 跳过列表
|
|
37
|
+
7. **`src/codegen/emit_js.rs`** — JS 内联代码生成 `record.values[idx]`,无函数调用开销
|
|
38
|
+
|
|
39
|
+
## 知识点
|
|
40
|
+
|
|
41
|
+
- `CalcitStruct.index_of(field_str)` 在编译期使用,用字段名解析排序位置
|
|
42
|
+
- `resolve_type_value()` 返回 `None` 时优化不触发,安全回退
|
|
43
|
+
- JS CalcitRecord 的 `.values` 数组和 Rust 端的 `values` Vec 索引一致(均按字段名字母序排序)
|
|
44
|
+
- 对于真实项目(如 respo),函数参数通常缺少 struct 类型标注,优化暂不触发;后续若 schema 推断增强,无需改动 rewrite 逻辑即可自动生效
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 扩展 extract_predicate_binding 支持更多类型谓词
|
|
2
|
+
|
|
3
|
+
## 改动概要
|
|
4
|
+
|
|
5
|
+
将 `extract_predicate_binding` 重构为 `extract_predicate_bindings`,支持双向窄化(true/false 分支),并新增 6 个类型谓词的窄化支持。
|
|
6
|
+
|
|
7
|
+
## 修改文件
|
|
8
|
+
|
|
9
|
+
### src/runner/preprocess/mod.rs
|
|
10
|
+
|
|
11
|
+
- 重构 `extract_predicate_binding` → `extract_predicate_bindings`,返回 `PredicateNarrowing` 结构体(包含 `true_binding` 和 `false_binding`)
|
|
12
|
+
- `preprocess_if` 中 false 分支也应用窄化信息
|
|
13
|
+
- 新增谓词:`tag?` → `:tag`、`bool?` → `:bool`、`symbol?` → `:symbol`、`fn?` → `:fn`
|
|
14
|
+
- 新增 `nil?`/`some?`:当变量已知为 `Optional(T)` 时,nil? 的 false 分支窄化为 `T`,some? 的 true 分支窄化为 `T`
|
|
15
|
+
|
|
16
|
+
### src/calcit/proc_name.rs
|
|
17
|
+
|
|
18
|
+
- `TurnTag` 的参数签名从 `some_tag("string")` 改为 `dynamic_tag()`,因为 `turn-tag` 在运行时接受 tag/symbol/string,旧签名在 `tag?` 窄化后误报
|
|
19
|
+
|
|
20
|
+
## 知识点
|
|
21
|
+
|
|
22
|
+
1. **双向窄化**:`if (nil? x) ... else ...`,false 分支可从 `Optional(T)` 解包为 `T`,这在链式 if 模式中可以传播类型信息
|
|
23
|
+
2. **新窄化暴露的类型签名问题**:`tag?` 窄化后,`(turn-tag x)` 触发了 arg-type-mismatch 警告,因为 `turn-tag` 签名过严。类似 coercion 函数应使用 `dynamic_tag()`
|
|
24
|
+
3. **核心库中 `if (tag? ...)` 出现 5 次、`if (nil? ...)` 26 次、`if (list? ...)` 41 次**——窄化覆盖面广
|
|
25
|
+
4. **JS 端 tuple `.nth` 缺失**:prior session 简化了多态函数(移除 tuple? 分支),但 JS runtime 的 `invoke_method` 没有对应的 core-tuple-methods 注册,导致 JS 测试 `test_refs` 失败。这是 JS procs 层的问题,不是预处理阶段的问题
|
|
26
|
+
|
|
27
|
+
## 测试结果
|
|
28
|
+
|
|
29
|
+
- Rust: 246 tests pass(67 cirru + 179 unit)
|
|
30
|
+
- Clippy: clean
|
|
31
|
+
- JS: 已知 pre-existing failure(tuple .nth in JS runtime)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 202604160131 — WASM codegen 扩展: 数学函数 + 测试集成
|
|
2
|
+
|
|
3
|
+
## 新增 WASM 操作
|
|
4
|
+
|
|
5
|
+
- `floor` → `f64.floor`
|
|
6
|
+
- `ceil` → `f64.ceil`
|
|
7
|
+
- `round` → `f64.nearest`
|
|
8
|
+
- `sqrt` → `f64.sqrt`
|
|
9
|
+
- `identical?` → `f64.eq`
|
|
10
|
+
- `sin`/`cos`/`pow` 明确报错(WASM 无对应指令)
|
|
11
|
+
|
|
12
|
+
## 新增文件
|
|
13
|
+
|
|
14
|
+
- `calcit/test-wasm.cirru` — 17 个纯数值函数测试(fibo, factorial, add-two, sum-range, floor, ceil, round, sqrt, rem, compare, not, let-chain, collatz-steps, gcd 等)
|
|
15
|
+
- `scripts/test-wasm.sh` — WASM 验证脚本(生成 WAT → wasmtime compile → 逐函数验证返回值)
|
|
16
|
+
|
|
17
|
+
## 修改文件
|
|
18
|
+
|
|
19
|
+
- `src/codegen/emit_wasm.rs` — 新增 `unary_op` 辅助函数, 添加 Floor/Ceil/Round/Sqrt/Identical 分支
|
|
20
|
+
- `package.json` — `check-all` 加入 `try-wasm` 步骤; 新增 `try-wasm` 脚本
|
|
21
|
+
- `docs/wasm-codegen.md` — 更新支持列表、测试说明、路线图
|
|
22
|
+
|
|
23
|
+
## 验证
|
|
24
|
+
|
|
25
|
+
- `yarn check-all` 通过(含 compile, try-rs, try-js, try-ir, try-wasm)
|
|
26
|
+
- `cargo test` 246/246 pass
|
|
27
|
+
- `cargo clippy -- -D warnings` clean
|
|
28
|
+
- wasmtime 逐函数验证: 17/17 assertions pass
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 202604161507 - WASM 数据结构编译 & rfc 目录重命名
|
|
2
|
+
|
|
3
|
+
## rfc 目录整理
|
|
4
|
+
|
|
5
|
+
- `drafts/` 重命名为 `rfc/`,所有文件加上 `MM-DD-` 创建日期前缀(从 git 历史提取)
|
|
6
|
+
- README.md 更新为完整索引,补全之前遗漏的 5 个文件条目
|
|
7
|
+
- Cargo.toml 中 `drafts/` 引用更新为 `rfc/`
|
|
8
|
+
|
|
9
|
+
## WASM 数据结构编译
|
|
10
|
+
|
|
11
|
+
### 新增能力
|
|
12
|
+
|
|
13
|
+
- **Memory Section**: 1 页 64KB 线性内存,Global section 维护 `heap_ptr` 作为 bump allocator
|
|
14
|
+
- **Tag 编译**: `collect_all_tags()` 在编译期为所有 tag 字面量分配整数 ID,运行时直接用 f64 表示
|
|
15
|
+
- **Record 编译**: `emit_record_new()` 在堆上分配 `[struct_tag_id, field0, field1, ...]`,`emit_record_nth()` 通过偏移直接读取
|
|
16
|
+
- **Tuple 编译**: `emit_tuple_new()` 在堆上分配 `[tag_id, payload0, payload1, ...]`,`emit_tuple_nth()` 直接读取
|
|
17
|
+
- **静态结构解析**: `try_parse_defrecord_form()` 从源码 AST 静态提取 `defrecord` 的字段定义,不依赖运行时宏展开
|
|
18
|
+
|
|
19
|
+
### 关键设计决策
|
|
20
|
+
|
|
21
|
+
- 全 f64 ABI:所有值(包括指针)用 f64 传递,需要 `i32.trunc_f64_u` / `f64.convert_i32_u` 转换
|
|
22
|
+
- record 字段按字母序排列(与 Calcit runtime 一致)
|
|
23
|
+
- bump allocator 不回收,适合短生命周期 WASM 模块
|
|
24
|
+
|
|
25
|
+
### 测试覆盖
|
|
26
|
+
|
|
27
|
+
- 22 项 WASM 检查全部通过:17 个数值运算 + 2 个 tag 比较 + 2 个 record 求和 + 1 个 tuple 求和
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 202604161520 - WASM 位运算 & match 语法编译
|
|
2
|
+
|
|
3
|
+
## 位运算 (bitwise operations)
|
|
4
|
+
|
|
5
|
+
- 新增 `bit-and`, `bit-or`, `bit-xor`, `bit-not`, `bit-shl`, `bit-shr` 6 个 WASM 编译支持
|
|
6
|
+
- 策略: f64 → i32 (trunc_f64_s) → 执行 i32 位运算 → i32 → f64 (convert_i32_s)
|
|
7
|
+
- 使用有符号转换 (I32TruncF64S / F64ConvertI32S) 保持负数语义
|
|
8
|
+
|
|
9
|
+
## match 语法编译
|
|
10
|
+
|
|
11
|
+
- 实现 `match` 表达式的 WASM codegen, 支持 enum tuple 模式匹配
|
|
12
|
+
- 策略: 加载 tuple 的 tag_id (offset 0), 对各分支做嵌套 if/else 比较
|
|
13
|
+
- 支持 tag 模式 `(:variant a b)` — 从 tuple 内存按偏移读取 payload 绑定到局部变量
|
|
14
|
+
- 支持 wildcard `_` 作为 fallback 分支
|
|
15
|
+
- 注意 block_depth 正确更新以保持 recur 分支跳转的一致性
|
|
16
|
+
|
|
17
|
+
## 测试覆盖
|
|
18
|
+
|
|
19
|
+
- 31 项 WASM 检查全部通过 (原 22 + 6 bitwise + 3 match)
|
|
20
|
+
- 246 cargo tests 通过 (179 + 67)
|
|
21
|
+
- clippy 零警告
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# WASM: Cross-namespace calls & host imports (pow, sin, cos)
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
为 WASM codegen 增加了跨命名空间函数调用和宿主函数导入两大功能。
|
|
6
|
+
|
|
7
|
+
## 知识点
|
|
8
|
+
|
|
9
|
+
### 跨命名空间调用
|
|
10
|
+
|
|
11
|
+
- `emit_wasm()` 从只处理 `init_ns` 改为遍历 `program_data` 全部命名空间
|
|
12
|
+
- `fn_defs` 从 3-tuple `(def, args, body)` 扩展为 4-tuple `(ns, def, args, body)`
|
|
13
|
+
- `fn_index` 同时保存 `"ns/def"` 全限定名和 `"def"` 裸名两种键
|
|
14
|
+
- `emit_call_expr` 中遇到 `Import` 节点时,先尝试全限定名再尝试裸名
|
|
15
|
+
- `collect_all_tags` 更名为 `collect_all_tags_from` 适配 4-tuple
|
|
16
|
+
|
|
17
|
+
### 宿主函数导入 (Host Imports)
|
|
18
|
+
|
|
19
|
+
- 新增 `HostImport` 结构体和 `HOST_IMPORTS` 常量数组(pow/sin/cos)
|
|
20
|
+
- WASM 模块新增 `ImportSection`,从 `"math"` 模块导入函数
|
|
21
|
+
- **关键约束**:导入函数占据函数索引 0..N,用户函数从 N 开始
|
|
22
|
+
- TypeSection: 先写 host import 的类型, 再写 user function 的类型
|
|
23
|
+
- FunctionSection: 只写 user function(import 自动拥有类型)
|
|
24
|
+
- ExportSection / fn_index: 用户函数索引均需加 `num_imports` 偏移
|
|
25
|
+
- `emit_host_call()` 辅助函数:按名称查找 HOST_IMPORTS 并发出 Call 指令
|
|
26
|
+
- `CalcitProc::Sin/Cos/Pow` 从返回错误改为调用 `emit_host_call`
|
|
27
|
+
|
|
28
|
+
### 测试
|
|
29
|
+
|
|
30
|
+
- `test-wasm.cirru` 新增 `test-wasm.helper` 命名空间(含 `add-and-double`)
|
|
31
|
+
- `test-wasm.mjs` 新增 `math` 导入对象 + `checkApprox()` 浮点近似断言
|
|
32
|
+
- 测试从 31 项增加到 39 项,全部通过
|
|
33
|
+
|
|
34
|
+
## 注意事项
|
|
35
|
+
|
|
36
|
+
- WASM 规范要求 Import section 必须在 Function section 之前
|
|
37
|
+
- 添加新 host import 时需更新 HOST_IMPORTS 数组,并确认 JS 测试 runner 提供对应实现
|
|
38
|
+
- 后续可考虑将 host import 模块名参数化(目前硬编码 `"math"`)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# WASM: 可变参数 (rest args) 支持
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
此前 `(defn f (a b & xs) ...)` 形式的函数在 WASM codegen 中会直接报错
|
|
6
|
+
"rest args not supported in WASM codegen" 而被跳过。这阻塞了 `calcit.core` 中
|
|
7
|
+
`<`、`-`、`/`、`dissoc`、`exclude`、`intersection`、`difference`、
|
|
8
|
+
`merge-non-nil`、`&str-spaced` 等大量可变参数函数的 WASM 编译。
|
|
9
|
+
|
|
10
|
+
## 实现
|
|
11
|
+
|
|
12
|
+
函数签名约定(匹配现有数据结构表示):
|
|
13
|
+
|
|
14
|
+
- rest 参数表现为一个 f64 参数,承载一个 list 指针。
|
|
15
|
+
- `(defn f (a b & xs) ...)` 的 WASM 签名是 `(a: f64, b: f64, xs: f64) -> f64`,
|
|
16
|
+
其中 `xs` 是调用方构造的 list 指针。
|
|
17
|
+
|
|
18
|
+
关键改动(全部在 `src/codegen/emit_wasm.rs`):
|
|
19
|
+
|
|
20
|
+
1. 新增 `compute_fn_arity(args) -> (wasm_arity, Option<rest_fixed>)`:
|
|
21
|
+
替代原来散落的 arity 计算,同时返回是否含 rest 以及固定参数数。
|
|
22
|
+
2. 新增 `fn_has_rest: HashMap<String, u32>`,记录每个有 rest 的函数的固定
|
|
23
|
+
参数数(`wasm_arity - 1`)。随 `fn_index` / `fn_arity` 一起在
|
|
24
|
+
`compile_fn` 之前建好。
|
|
25
|
+
3. `WasmGenCtx` 新增 `fn_has_rest` 字段,与其他 lookup 表一起 clone 进上下文。
|
|
26
|
+
4. `compile_fn`:
|
|
27
|
+
- 不再对 `CalcitArgLabel::RestMark` 报错。
|
|
28
|
+
- 跳过 `RestMark` 本身,但把 `&` 后的 `Idx` 仍然作为一个普通 f64 参数
|
|
29
|
+
压入 `param_names`(它承载 list 指针)。
|
|
30
|
+
5. `emit_call_expr`:提取新助手 `emit_call_args`,根据 callee 是否有 rest
|
|
31
|
+
分两条路径:
|
|
32
|
+
- 无 rest:照旧逐个 `emit_expr`,缺失的 optional 参数用 `f64_const(0.0)` 填补。
|
|
33
|
+
- 有 rest:前 `fixed` 个参数照旧求值,多余参数调用 `emit_list_new` 打包
|
|
34
|
+
成 list 指针作为最后一个参数。
|
|
35
|
+
|
|
36
|
+
## 踩坑记录
|
|
37
|
+
|
|
38
|
+
- **`bash scripts/test-wasm.sh` 用的是 release 产物,不是 `cargo run`**。改完
|
|
39
|
+
Rust 代码后一定要 `cargo build --release --bin cr`,否则看到的仍是旧行为。
|
|
40
|
+
这次调试时一度以为 `collect-rest` 还会被跳过,源头是 release binary 陈旧。
|
|
41
|
+
- `emit_list_new` 签名接受 `&[Calcit]`,直接传入 `&args_list[fixed..]`
|
|
42
|
+
就够用;不需要手动写 header/slot。
|
|
43
|
+
|
|
44
|
+
## 测试
|
|
45
|
+
|
|
46
|
+
新增 `calcit/test-wasm.cirru` 三个用例:
|
|
47
|
+
|
|
48
|
+
- `test-rest-count`:`collect-rest 1 2 3 4` 返回的 list 长度为 3。
|
|
49
|
+
- `test-rest-sum`:`sum-rest 1 2 3 4 5 = 15`,验证打包 list 后由被调函数
|
|
50
|
+
通过 `&list:first` / `&list:rest` / `recur` 逐项展开并求和。
|
|
51
|
+
- `test-rest-empty`:`sum-rest 10 20 = 30`,验证没有额外参数时 list 为空。
|
|
52
|
+
|
|
53
|
+
`scripts/test-wasm.mjs` 对应 3 条 `check(...)`。`yarn check-all` 全绿。
|
|
54
|
+
|
|
55
|
+
## 遗留
|
|
56
|
+
|
|
57
|
+
尚未支持的 WASM 功能(下一步继续攻克):
|
|
58
|
+
|
|
59
|
+
- `type-of`、各类 `xxx?` 谓词(需要运行时类型标记,难度较大)。
|
|
60
|
+
- `foldl` / `foldl-shortcut`(依赖闭包或函数指针)。
|
|
61
|
+
- 嵌套 `defn` / 闭包。
|
|
62
|
+
- 字符串字面量和字符串操作。
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# WASM codegen: implement `type-of` via heap type headers
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
在 WASM 子集里实现 `type-of` 是解锁 ~20 个核心函数(`list?`、`map?`、`set?`、
|
|
6
|
+
`number?` 等)的前置。所有值统一用 f64 表示,堆对象是“整数值 f64”的指针
|
|
7
|
+
(byte offset);没有 NaN-boxing,无法仅凭位模式区分类型。
|
|
8
|
+
|
|
9
|
+
## 方案
|
|
10
|
+
|
|
11
|
+
每一次堆分配前置 8 字节类型头:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
raw_base + 0 : i32 MAGIC (0xCA1C17A9)
|
|
15
|
+
raw_base + 4 : i32 tag_id (BUILTIN_TYPE_TAGS 里的下标对应的 tag_index 值)
|
|
16
|
+
raw_base + 8 : 原本的 payload
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`emit_bump_alloc` / `emit_bump_alloc_dynamic` 实际申请 `byte_size + 8` 字节,
|
|
20
|
+
返回的 **logical pointer** 仍然是 `raw_base + 8`,所以所有既有 offset 算术
|
|
21
|
+
(count at offset 0、fields at offset 8/16/…)无需改动。
|
|
22
|
+
|
|
23
|
+
### 11 个内建 type tag
|
|
24
|
+
|
|
25
|
+
`BUILTIN_TYPE_TAGS = ["nil","bool","number","tag","string","list","map","set",
|
|
26
|
+
"record","tuple","fn"]`。在 `collect_all_tags_from` 第一步 seed,确保
|
|
27
|
+
`:list` / `:map` 等关键字字面量和 `type-of` 返回值落到同一个 tag id。
|
|
28
|
+
|
|
29
|
+
### `type-of` 运行时判定
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
if v == trunc(v) // 整数
|
|
33
|
+
&& v >= (HEAP_START + 8) // 在堆范围内
|
|
34
|
+
&& v < current_heap_ptr {
|
|
35
|
+
raw_base = trunc(v) - 8
|
|
36
|
+
if load_i32(raw_base, 0) == MAGIC { // 魔数校验,排除纯数字误判
|
|
37
|
+
return f64(load_i32(raw_base, 4)) // 返回 tag id
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return f64(number_tag)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
之前只用“整数 + 在堆范围”做判定时,`42` 这类数字会被错误识别成指针 →
|
|
44
|
+
读到垃圾 tag;加上 `MAGIC` 校验后 `test-type-of-number` 通过。
|
|
45
|
+
|
|
46
|
+
## 踩坑
|
|
47
|
+
|
|
48
|
+
1. **签名顺序**:`emit_bump_alloc` 的参数顺序从
|
|
49
|
+
`(ctx, byte_size, type_tag, ptr_local)` 调整为
|
|
50
|
+
`(ctx, byte_size, ptr_local, type_tag)`,以匹配 21 处调用点的脚本化改写。
|
|
51
|
+
2. **Magic 必须**:只有“指针范围 + 整数值”不足以区分指针和裸数字,必须加
|
|
52
|
+
唯一性较高的 magic(这里用 `0xCA1C17A9`)。
|
|
53
|
+
3. **f64 ↔ i32**:`tag_id` 是小整数,用 i32 存取更便宜;新增
|
|
54
|
+
`mem_arg_i32` 辅助函数(align=2)。
|
|
55
|
+
4. **If/Else 嵌套返回 f64**:`type-of` 用两层 `BlockType::Result(ValType::F64)`
|
|
56
|
+
实现短路,避免在非指针情况下去读内存。
|
|
57
|
+
|
|
58
|
+
## 测试
|
|
59
|
+
|
|
60
|
+
`calcit/test-wasm.cirru` + `scripts/test-wasm.mjs` 新增 5 个用例:
|
|
61
|
+
`test-type-of-list/map/set/number/tuple`。全部返回 1(`&= (type-of x) :list`
|
|
62
|
+
之类比较)。已执行 `cargo fmt && cargo clippy --release -- -D warnings &&
|
|
63
|
+
cargo test --release && yarn check-all`,全部通过。
|
|
64
|
+
|
|
65
|
+
## 后续
|
|
66
|
+
|
|
67
|
+
解锁后可以把 `list?` / `map?` / `set?` / `number?` / `tuple?` / `record?`
|
|
68
|
+
等 proc 映射成 `(&= (type-of x) :xxx)` 或直接基于 tag 比较。下一轮挑战:
|
|
69
|
+
字符串支持 (24 个 skip)、nested defn/闭包 (19 个 skip)、`foldl` /
|
|
70
|
+
`foldl-shortcut` (13 个 skip)。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# WASM codegen: test derived type predicates on top of `type-of`
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
刚在上一条 commit 里把 `type-of` 跑通了,语料里 `calcit.core` 的几个核心
|
|
6
|
+
predicates (`list?` / `map?` / `number?` / `set?` / `tuple?` / ...) 都是
|
|
7
|
+
`(defn pred? (x) (&= (type-of x) :tag))` 这种薄包装。既然 `type-of`、`&=`、
|
|
8
|
+
keyword 字面量都已经支持,这些 predicate 本来就会被 WASM 编译器吐出来,
|
|
9
|
+
只是一直缺少验证。
|
|
10
|
+
|
|
11
|
+
## 改动
|
|
12
|
+
|
|
13
|
+
`calcit/test-wasm.cirru` + `scripts/test-wasm.mjs` 增加 4 个样例:
|
|
14
|
+
|
|
15
|
+
- `test-list?-true` : `(list? ([] 1 2))` → 1
|
|
16
|
+
- `test-list?-false` : `(list? 42)` → 0 (验证魔数检查能挡住整数伪装)
|
|
17
|
+
- `test-number?-true` : `(number? 42)` → 1
|
|
18
|
+
- `test-map?-true` : `(map? (&{} :a 1))` → 1
|
|
19
|
+
|
|
20
|
+
`yarn check-all` 全部通过,81 个 WASM 检查全绿。
|
|
21
|
+
|
|
22
|
+
## 当前 WASM skip 分类(跑 `cr wasm` 的 stderr)
|
|
23
|
+
|
|
24
|
+
总计 123 条 skipping,主要集中在:
|
|
25
|
+
|
|
26
|
+
- 24 String values not yet supported
|
|
27
|
+
- 19 nested defn not supported
|
|
28
|
+
- 13 foldl / foldl-shortcut / foldr-shortcut
|
|
29
|
+
- 7 `&call-spread`
|
|
30
|
+
- 4 `Tuple operation %:: not yet supported`
|
|
31
|
+
- 3 `&set:destruct`
|
|
32
|
+
- 少量其它(`sort`, `range`, `println`, `deref`, `turn-string` 等)
|
|
33
|
+
|
|
34
|
+
下一轮切入点优先级:字符串 → nested defn/闭包 → 高阶函数 (`call_indirect`)。
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Monomorphize `map` / `filter` at Compile Time
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
`try_specialize_polymorphic_call`(`src/runner/preprocess/mod.rs`)原本只能把
|
|
6
|
+
`count`、`empty?`、`first`、`rest`、`nth`、`get`、`assoc`、`contains?` 等
|
|
7
|
+
**built-in 多态 proc** 特化成 native proc(`NativeListCount` / `NativeMapGet`
|
|
8
|
+
等),彻底避免运行时按 `type_of` 做分支。
|
|
9
|
+
|
|
10
|
+
但是 Calcit 里最常见的集合高阶函数 `map` / `filter` / `reduce` 等都是在
|
|
11
|
+
`calcit-core.cirru` 里用 Calcit 本身定义的用户级 defn(例如 `&list:map`、
|
|
12
|
+
`&map:map`、`&set:filter`),原先不能在预处理阶段复用这套机制,运行时仍要靠
|
|
13
|
+
`list?`/`map?`/`set?` 三条 `cond` 分支来分发。
|
|
14
|
+
|
|
15
|
+
本次把能力扩展到 Calcit 级别的 core def,当 receiver 的静态类型可推断时,
|
|
16
|
+
直接把 `(map xs f)` 改写成 `(calcit.core/&list:map xs f)` 等形式。
|
|
17
|
+
|
|
18
|
+
## 关键改动
|
|
19
|
+
|
|
20
|
+
- `src/runner/preprocess/mod.rs`
|
|
21
|
+
- `try_specialize_polymorphic_call` 新增 `file_ns: &str` 参数,用于构造
|
|
22
|
+
`Calcit::Import` 头的 `ImportInfo::Core { at_ns }`。
|
|
23
|
+
- 在已有的 proc 特化表之前新增一张“core def 特化表”:
|
|
24
|
+
- `("map", List(_)) -> &list:map`
|
|
25
|
+
- `("map", Map(_, _)) -> &map:map`
|
|
26
|
+
- `("filter", List(_)) -> &list:filter`
|
|
27
|
+
- `("filter", Map(_, _)) -> &map:filter`
|
|
28
|
+
- `("filter", Set(_)) -> &set:filter`
|
|
29
|
+
- 命中时直接构造 `Calcit::Import(CalcitImport { ns: "calcit.core", def, info:
|
|
30
|
+
Core { at_ns: file_ns }, def_id: Some(program::ensure_def_id(...).0) })`
|
|
31
|
+
作为新的 head,保持已 preprocess 过的参数不变,避免重复预处理。
|
|
32
|
+
- 禁止对 `&list:map` / `&map:map` / `&set:map` / `&list:filter` /
|
|
33
|
+
`&map:filter` / `&set:filter` 自身再次 monomorphize,避免潜在 cycle。
|
|
34
|
+
- 调用点 `Ok(Calcit::from(CalcitList::from(ys)))` 前的 specialize 调用传入
|
|
35
|
+
`file_ns`。
|
|
36
|
+
|
|
37
|
+
## 验证
|
|
38
|
+
|
|
39
|
+
- `cargo fmt && cargo clippy --release -- -D warnings` ✓
|
|
40
|
+
- `cargo test --release` ✓ 所有现有测试通过
|
|
41
|
+
- `yarn check-all` ✓ 全量集成测试 + WASM 测试全部 OK
|
|
42
|
+
|
|
43
|
+
## 后续思路
|
|
44
|
+
|
|
45
|
+
- 可继续把 `reduce` / `last` / `reverse` / `concat` / `to-list` / `to-set`
|
|
46
|
+
等也加入特化表;其中 `concat` / `reverse` 已有 native proc(`NativeListConcat`
|
|
47
|
+
/ `NativeListReverse`),加到已有 proc 表即可。
|
|
48
|
+
- 可以考虑把谓词(`list?`、`map?`、`number?`、`string?` …)在类型已知时直接折叠成
|
|
49
|
+
`true` / `false`,进一步帮 WASM / JS codegen 消冗余分支。
|
|
50
|
+
- 未来 `&set:map` 如果单独实现,即可补齐 `("map", Set(_))` 分支。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Monomorphize `includes?` and `reverse`
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
继 `map` / `filter` 之后,继续扩展 `try_specialize_polymorphic_call`:
|
|
6
|
+
|
|
7
|
+
- `includes?` 原实现(calcit-core.cirru L3228)有 5 条 `if` 分支
|
|
8
|
+
(`nil?` / `list?` / `map?` / `set?` / `string?` fallback `.includes?`)。
|
|
9
|
+
- `reverse` 实际上在 runtime 就是 `&list:reverse` 的别名,但以前每次调用还是要
|
|
10
|
+
经过一层 Calcit user-def 间接。
|
|
11
|
+
|
|
12
|
+
## 改动
|
|
13
|
+
|
|
14
|
+
在 `src/runner/preprocess/mod.rs` 的 proc 特化表中追加:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
("includes?", T::List(_)) -> NativeListIncludes
|
|
18
|
+
("includes?", T::Map(_, _)) -> NativeMapIncludes
|
|
19
|
+
("includes?", T::Set(_)) -> NativeSetIncludes
|
|
20
|
+
("includes?", T::String) -> NativeStrIncludes
|
|
21
|
+
("reverse", T::List(_)) -> NativeListReverse
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`concat` 因为是变长参数 defn,暂不在这里处理;后续可以在 preprocess 阶段
|
|
25
|
+
把已知都是 list 的 `(concat a b)` 展开为 `(&list:concat a b)` 的折叠形式。
|
|
26
|
+
|
|
27
|
+
## 验证
|
|
28
|
+
|
|
29
|
+
- `cargo fmt && cargo clippy --release -- -D warnings` ✓
|
|
30
|
+
- `cargo test --release` ✓
|
|
31
|
+
- `yarn check-all` ✓
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Fold type predicates when static type is known
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
`list?` / `map?` / `set?` / `string?` / `number?` / `bool?` / `tag?` / `fn?` /
|
|
6
|
+
`tuple?` / `record?` 这些 type predicate defn 都是 `(&= (type-of x) :xxx)`
|
|
7
|
+
的形式。如果 preprocess 能拿到 `x` 的静态类型,整个调用就可以折叠成一个字面的
|
|
8
|
+
`true` 常量(或者保留运行时 check,但不把 `false` 折叠以保持安全)。
|
|
9
|
+
|
|
10
|
+
在 WASM / JS codegen 场景下这尤其有收益:谓词出现在 `if` / `cond` 里,
|
|
11
|
+
折叠成常量后整个分支会被后续 dead-code 消除。
|
|
12
|
+
|
|
13
|
+
## 改动
|
|
14
|
+
|
|
15
|
+
`src/runner/preprocess/mod.rs::try_specialize_polymorphic_call` 新增一段在
|
|
16
|
+
proc 特化表之前的折叠逻辑:
|
|
17
|
+
|
|
18
|
+
```rust
|
|
19
|
+
let predicate_true = matches!(
|
|
20
|
+
(fn_def, receiver_type.as_ref()),
|
|
21
|
+
("list?", T::List(_))
|
|
22
|
+
| ("map?", T::Map(_, _))
|
|
23
|
+
| ("set?", T::Set(_))
|
|
24
|
+
| ("string?", T::String)
|
|
25
|
+
| ("number?", T::Number)
|
|
26
|
+
| ("bool?", T::Bool)
|
|
27
|
+
| ("tag?", T::Tag)
|
|
28
|
+
| ("fn?", T::Fn(_) | T::DynFn)
|
|
29
|
+
| ("tuple?", T::Tuple(_) | T::DynTuple)
|
|
30
|
+
| ("record?", T::Record(_) | T::Struct(_, _))
|
|
31
|
+
);
|
|
32
|
+
if predicate_true { return Some(Calcit::Bool(true)); }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**设计选择**:只折叠“肯定为 true”的情形,不折叠“肯定为 false”的情形。
|
|
36
|
+
原因:`CalcitTypeAnnotation` 枚举有 30+ variant(包括 `Optional`、`Ref`、
|
|
37
|
+
`TypeVar`、`TypeRef`、`Trait` 等等),要列全“除此之外的全部”枚举会很脆弱、
|
|
38
|
+
以后新增 variant 容易漏。保留 runtime check 是安全的 fallback。
|
|
39
|
+
|
|
40
|
+
## 验证
|
|
41
|
+
|
|
42
|
+
- `cargo fmt && cargo clippy --release -- -D warnings` ✓
|
|
43
|
+
- `cargo test --release` ✓(179 + 67 测试全通过)
|
|
44
|
+
- `yarn check-all` ✓
|