From e28318188772718c656b307673fa2df7808dc94f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sun, 30 Aug 2026 16:47:45 +0900 Subject: [PATCH 001/138] =?UTF-8?q?docs:=20=E7=B1=BB=E5=9E=8B=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=96=B9=E5=90=91=E5=AE=9A=E6=A1=88=E6=8C=82=E8=B4=A6?= =?UTF-8?q?=EF=BC=88=E5=9B=BE=E6=9C=AC=E4=BD=93=20+=20=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=80=BB=E7=BA=B2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 1 + ...2026-08-30-type-system-direction-design.md | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-30-type-system-direction-design.md diff --git a/TODO.md b/TODO.md index 6aed9f63..58debe6b 100644 --- a/TODO.md +++ b/TODO.md @@ -232,6 +232,7 @@ - **dex 任意精度精确小数未实现**(dex-precision,2026-08-28 审计):设计 = 无上限小数位精确小数;实现 = 定点 S=10⁶(`src/stdlib/dex.cr:3-18`,注释「定点方案(执行时定稿,2026-08-16)」)——6 位小数 + int64 硬上限(加减 ≤9.2e12、乘 |a|·|b| ≤9.2e6);设计意图与实现为语义级偏差。修复方向 = 动态位数表示(任意精度运算,重活,排期) - **apx 降级策略被实现为报错**(apx-degrade,2026-08-28 审计):设计 = 只支持精确的环境直接忽略 apx 标签、走精确语义(优雅降级);实现 = 解释器对 apx 的 I2F/F2I 显式报错(`src/stdlib/dex.cr:32-33`;报错本身是 Task 6 安全修复——替代静默跳过致 SIGFPE,但方向与设计相悖)。修复方向 = 解释器忽略 apx 标签走精确路径。注:apx 结果跨环境可不同(native 走 binary64),为标签显式代价,账本如实记录 - **宽度类型移出语言**(width-out-of-language,2026-08-30 定案):设计决定——int 无上限为默认(已定);宽度(i64/u32/w32 等)不进入语言类型/标签体系,避免第二标签范式(单标签单范式原则);机器形状全部归 hw-map(硬件映射表,`docs/superpowers/specs/2026-08-23-hw-map-design.md`——依赖 crasm,链式阻塞现状不变);apx 保持单一语义 = 精度降级开关,不扩张为宽度标签。三层映射对应:图/格 = 纯数学,编码 = hw-map 领域。语言侧清理(不阻塞,可立即做):ast.cr `T_INT_I8..T_INT_U64` / `T_FLOAT_F32/F64` 死条目移除(勿重编号)、`_f32/_f64` 后缀死路径处理、`1_000` 两编译器分歧复核;v6 规格影响:编码层宽度由目标自动决定,hw-map 为未来显式控制通道(v6 格式规格先行更新) +- **类型系统方向定案**(type-system-direction,2026-08-30 定稿):图本体 + 接口统一总纲——图 = 唯一真相层(类型 = 图标注、接口 = 图上契约、类型检查 = 图良构性验证 pass,与指针三 pass 同级);接口注册表(int/dex/string = 原生接口条目,规则内建、公理引用规约层、用户不可实现);where 值约束三档语义(常量→编译错误 / 符号→VC / 动态→运行时检查);泛型 = 编译期接口具体化(无运行期字典);宽度移出语言(见 width-out-of-language)。损失账本(免费午餐债 5 项)+ 演进顺序(where 值约束 → 泛型=编译期接口 → 验证切片 → v6 格形态 → 类型概念收敛,两条根基革命不得同时进行)+ 学术支撑(PLDI 2025 Webs 平行印证、语义子类型 = 完整补偿、ISO TS 6010 provenance 对齐)详见 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md` ## 待实现特性 ### 控制流自动惰性(2026-08-09 记) diff --git a/docs/superpowers/specs/2026-08-30-type-system-direction-design.md b/docs/superpowers/specs/2026-08-30-type-system-direction-design.md new file mode 100644 index 00000000..f0e4761f --- /dev/null +++ b/docs/superpowers/specs/2026-08-30-type-system-direction-design.md @@ -0,0 +1,83 @@ +# 类型系统方向设计备忘:图本体 + 接口统一(2026-08-30 定稿) + +> 摘要(十六字):**图是本体,接口是契约,类型是推导,编码是映射。** + +## 一、定案清单 + +1. **图本体原则**:图(HDFG)是唯一真相层。类型 = 图节点的标注(投影),接口 = 图上签的契约(行为约束),类型检查 = 图的良构性验证 pass(与 PointerAnalysis / RegionCheck / ProvenanceVerify 同级,不建第二套数据结构)。 +2. **接口统一**:用户面 + 检查层只有接口一个概念。int / dex / string / bool = **原生接口条目**(规则内建在编译器、公理引用规约层、**用户不可实现**——公理是编译器领地,防伪造证明)。 +3. **实现决策**:一张 interface 注册表(动态表,int = 表头原生条目);检查器删掉所有类型特判、统一查表验证;**发射层原语映射不动**(`+` → addq 是机器知识,归 hw-map 编码层,不做表驱动化大重构)。 +4. **类型检查 = 图验证 pass**:标注一致性 + 操作数兼容性按注册表规则判定,与指针三 pass 同一层级。 +5. **where 值约束**:实现内联轻量前置条件。三档语义阶梯:编译期常量约束 → 编译错误/通过;符号约束 → VC 义务(验证管线消费);动态约束 → 运行时检查(防「证明不了也不静默」)。与 .corespec 前置条件映射(一条语法,两层消费:checker + VC)。 +6. **泛型 = 编译期接口具体化**:类型参数即「实现了某接口的具体类型」,声明处接口绑定 + 实例化检查,不做运行期字典。 +7. **宽度移出语言**:见 TODO `width-out-of-language`(单标签单范式;机器形状归 hw-map)。 +8. **明确不做**: + - 完整接口一等化(存在类型/运行期字典)——推迟到平台桥/hw-map 落地时做受限接口值 + - 「不需要类型系统」(全动态化)——实证危险(见 §5.3) + +## 二、设计推理 + +- **单范式原则**:一个概念树(接口)贯穿原语到用户类型,无第二标签范式、无第二类型世界。 +- **语义保鲜**:图保留一切,类型/接口是从图可推导的投影——标注可以自动算出,用户零标注负担。 +- **纯数学叙事**:int 无上限、dex 精确 = 公理化接口 + 原生实现——原语语义也可对照验证(CompCert 精神推广到原语层)。 +- **与既有定案咬合**:M1(身份 = 图节点)、无配方 = 标注、授权归治理层、8 项 Graph→Lattice 验证清单——内存模型 v4 已是图本体,类型系统站到同一层补一致性。 +- **接口 = 信息 + 规则**:节点上存储的标注(铭牌)+ 解释标注的规则(本体:良构性规则住验证 pass、公理住规约层)。 + +## 三、损失账本(免费午餐债——必须显式还的债) + +| 损失 | 说明 | 偿还方式 | +|------|------|---------| +| 构造期决议鸡生蛋 | 图构造需要类型选操作,类型靠图分析 | 临时标注 → 事后验证 → 重发射(现有管线已是此形态,显式化) | +| 枚举穷尽性 | match 覆盖检查是类型层性质 | 集合论类型补偿(§5.2) | +| 变型/子类型规则 | 接口需显式变型规则,否则静默健全性洞 | 语义子类型可判定化(§5.2) | +| 健全性定理 | 不再继承自标准类型系统 | 变为验证管线证明义务(语义保鲜项目反正要证) | +| 类型级结构信息静默丢失 | 忘记重编码的信息会被静默丢掉(dex 教训的翻版风险) | 显式设计「标注 + 公理」防漏清单 | + +## 四、演进顺序(每一步是上一步的顺水推舟) + +1. **where 值约束**(验证切片语法入口) +2. **泛型 = 编译期接口**(第 1 层,便宜) +3. **验证切片**(资助主线:规约 → VC → 自动证明) +4. **v6 格形态**(IR 根基革命) +5. **类型概念收敛**(远期,不动根基,且需 v6 之后) + +约束:两条根基革命(v6 与类型收敛)不得同时进行——交叉区域是 checker/ir_gen 全部。 + +## 五、学术支撑(前沿对照) + +### 5.1 论文地图 + +| 支柱 | 论文 | 印证/提示 | +|------|------|----------| +| 图本体 IR | [R-HLS(IEEE 2024 / arXiv 2408.08712)](https://ieeexplore.ieee.org/document/11126294)——RVSDG dialect,region + state edges + 内存消歧 | HDFG 设计在硬件综合领域独立平行出现 | +| 接口统一 + 图验证 | [Webs and Flow-Directed Well-Typedness(PLDI 2025)](https://pldi25.sigplan.org/details/pldi-2025-papers/31/Webs-and-Flow-Directed-Well-Typedness-Preserving-Program-Transformations) | 「生产者/消费者必须就接口达成一致 + 统一化类型分析」= 本备忘 §1.2/1.4 的平行印证 | +| 图 IR 形式语义 | [Denotational Semantics of SSA(2024,Lean 机械化)](https://www.emergentmind.com/papers/2411.09347) | 图 IR 获得形式语义基础 = 语义保鲜的学术形态 | +| 精化类型/易用验证 | [Flux(UCSD)](https://comunicaciones.dcc.uchile.cl/events/310-charla-flux-ergonomic-verification-of-rust-programs-with-liquid-types/)(liquid 推断)· [DML index-sensitive(ECOOP 2026)](https://drops.dagstuhl.de/storage/00lipics/lipics-vol372-ecoop2026/LIPIcs.ECOOP.2026/LIPIcs.ECOOP.2026.pdf)(索引敏感嵌套数组 = F11 方向) | where 值约束 + 推断验证路线成熟 | +| 系统级验证 | [Verus(SOSP 2024)](https://dl.acm.org/doi/10.1145/3694715) | 验证管线工程化前沿 | +| 健全性成本 | [Generic Refinement Types(POPL 2025)](https://dl.acm.org/doi/10.1145/3704885) · RefinedRust(Coq 机械化) | 免费午餐债的量化证据 | +| 内存 provenance | [CHERI C 形式化(ASPLOS 2024)](https://www.semanticscholar.org/paper/Formal-Mechanised-Semantics-of-CHERI-C%3A-Undefined-Zaliva-Memarian/606074ae655cbd43468b080e8ae35dde827ff395) · [时态安全(CPP 2025)](https://www.research.ed.ac.uk/en/publications/a-cheri-c-memory-model-for-verified-temporal-safety/) · [ISO TS 6010:2025 provenance-aware memory model](https://www.cl.cam.ac.uk/~pes20/papers/topic.Cerberus.html) | 指针模型与 ISO 标准方向收敛 | +| 整数↔指针 | [双非确定性内存模型(2024,已集成 CompCert)](https://lib.pusan.ac.kr/lawlib/resource/e-article/?db_id=edskci&mod=detail&page_number=1&record_id=edskci.ARTI.10554258) | asp=1(整数转指针标记)的同题前沿解 | + +### 5.2 缺口补偿图 + +| 缺口 | 补偿 | 怎么补 | +|------|------|--------| +| 枚举穷尽性 | **语义子类型/集合论类型**([Frisch-Castagna-Benzaken, JACM 2008](https://www.lri.fr/~benzaken/papers/semantic_subtyping.pdf)):类型 = 值的集合,union/intersection/negation,子类型 = 集合包含,**可判定** | 穷尽性 = 集合运算;match 覆盖可判定(CDuce typecase 先例) | +| 变型/子类型 | 同上(子类型可判定)+ [行多态(Rose POPL 2019 泛化;POPL 2026 行多态+type classes+Lean 4 机械化)](https://dl.acm.org/doi/full/10.1145/3776662) | 结构 + 约束 + 机械化健全性的完整形态 | +| 健全性债 | [Gradually Typed Languages Should Be Vigilant!(OOPSLA 2024)](https://dl.acm.org/doi/full/10.1145/3649842) | 「静态系统真的被强制执行」的精确语义判据(vigilance) | +| 无类型系统幻想 | [Typed and Confused(ASE 2024)](https://conf.researchr.org/details/ase-2024/ase-2024-research/149/Typed-and-Confused-Studying-the-Unexpected-Dangers-of-Gradual-Typing):3 万仓库实证 | 无检查集成 → type confusion 攻击的实证证据 | +| 决议机制 | [POPL 2026 elaboration 健全性](https://dl.acm.org/doi/full/10.1145/3776662)(源 → 目标演算,Lean 机械化) | 约束决议编译有机械化样板 | +| 子类型表达力边界 | [Structural Subtyping as Parametric Polymorphism(OOPSLA 2023)](https://scirate.com/arxiv/cs.PL?date=2023-05-07&range=31#3) | 全子类型不可编码为多态——接口系统子类型需自备机制 | + +### 5.3 战略定位 + +- **PLDI 2025 Webs** = 「接口统一 + 图验证」的独立平行印证——方向是当前编译研究前沿,非空想。 +- **语义子类型 = 完整解**:「类型 = 值的集合」与本备忘「类型 = 图上约束信息」**同构**——标注的语义解释有现成数学,且顺带解决穷尽性、子类型、报错质量(非空反例给具体值)。 +- **ISO TS 6010**:provenance 已进 C 标准路线——「指针模型与标准方向收敛」是资助材料的硬话。 +- **ASE 2024**:对「不需要类型系统」的最直接实证反驳。 + +## 六、关联 + +- TODO.md:`width-out-of-language`(宽度移出语言定案)、`dex-precision`、`apx-degrade` +- docs:`memory-model.md`(能力+格 v4)、`pointer-model.md`、`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(格形态 v6) +- 本备忘落点:类型系统增强总纲——后续实现以本备忘 + TODO 挂账为准 From 2da8b258d65eca58bc7444bd850dec2d4f6a63d9 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sun, 30 Aug 2026 19:13:07 +0900 Subject: [PATCH 002/138] =?UTF-8?q?docs:=20=E7=B1=BB=E5=9E=8B=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=96=B9=E5=90=91=E5=A4=87=E5=BF=98=20v2=EF=BC=8814?= =?UTF-8?q?=20=E6=9D=A1=E5=AE=9A=E6=A1=88=EF=BC=9A=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=8E=A8=E5=AF=BC=E5=85=A8=E9=87=8F=20+=20=E5=80=9F=E7=94=A8?= =?UTF-8?q?=E6=94=B6=E7=BC=A9=20+=20=E5=A3=B0=E6=98=8E=E5=BC=8F=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-08-30-type-system-direction-design.md | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-type-system-direction-design.md b/docs/superpowers/specs/2026-08-30-type-system-direction-design.md index f0e4761f..f745ebb5 100644 --- a/docs/superpowers/specs/2026-08-30-type-system-direction-design.md +++ b/docs/superpowers/specs/2026-08-30-type-system-direction-design.md @@ -1,37 +1,65 @@ # 类型系统方向设计备忘:图本体 + 接口统一(2026-08-30 定稿) > 摘要(十六字):**图是本体,接口是契约,类型是推导,编码是映射。** +> +> 自动化原则:**类型概念不显式**——类型 = 值集合,集合运算自动判定;显式性最小集 = where 值约束。 +> +> 声明式边界:**声明式上限(类型/契约/验证)+ 命令式下限(执行/副作用/内存)——两者共用一张图。** -## 一、定案清单 +## 一、定案清单(14 条) + +### 图本体(1-4) 1. **图本体原则**:图(HDFG)是唯一真相层。类型 = 图节点的标注(投影),接口 = 图上签的契约(行为约束),类型检查 = 图的良构性验证 pass(与 PointerAnalysis / RegionCheck / ProvenanceVerify 同级,不建第二套数据结构)。 2. **接口统一**:用户面 + 检查层只有接口一个概念。int / dex / string / bool = **原生接口条目**(规则内建在编译器、公理引用规约层、**用户不可实现**——公理是编译器领地,防伪造证明)。 3. **实现决策**:一张 interface 注册表(动态表,int = 表头原生条目);检查器删掉所有类型特判、统一查表验证;**发射层原语映射不动**(`+` → addq 是机器知识,归 hw-map 编码层,不做表驱动化大重构)。 4. **类型检查 = 图验证 pass**:标注一致性 + 操作数兼容性按注册表规则判定,与指针三 pass 同一层级。 + +### 约束与泛型(5-6) + 5. **where 值约束**:实现内联轻量前置条件。三档语义阶梯:编译期常量约束 → 编译错误/通过;符号约束 → VC 义务(验证管线消费);动态约束 → 运行时检查(防「证明不了也不静默」)。与 .corespec 前置条件映射(一条语法,两层消费:checker + VC)。 6. **泛型 = 编译期接口具体化**:类型参数即「实现了某接口的具体类型」,声明处接口绑定 + 实例化检查,不做运行期字典。 -7. **宽度移出语言**:见 TODO `width-out-of-language`(单标签单范式;机器形状归 hw-map)。 -8. **明确不做**: - - 完整接口一等化(存在类型/运行期字典)——推迟到平台桥/hw-map 落地时做受限接口值 - - 「不需要类型系统」(全动态化)——实证危险(见 §5.3) + +### 自动推导(7-13) + +7. **宽度移出语言**:见 TODO `width-out-of-language`(单标签单范式;机器形状归 hw-map)。语言侧清理(T_INT_I8.. 死条目移除、`_f32/_f64` 后缀死路径、`1_000` 两编译器分歧复核)可立即做;v6 编码层宽度由目标自动决定。 +8. **类型标注自动**:子类型关系自动判定(集合包含);枚举自动化为联合类型(`int | string`,穷尽性 = 补集空性检查);可选/可空 = 联合副产品(`Node?` = `Node | Null`);类型等价自动(双向包含);元组/记录 = product 自动组合;数组/切片 = 序列接口 + 元素接口 + **长度值约束**(F11 长度走 where 三档语义);转换 = 集合包含判定(provenance/asp 维度归 hw-map);借用**已自动**(逃逸检查是 pass、释放是 arena——用户零标注,Rust 的 `&`/`&mut` 标注体系在 Core 中不存在)。 +9. **借用收缩而非取消**:借用检查职责三分——逃逸安全 → 已被 RegionCheck pass 替代(可判定、便宜,保留);释放安全 → 已被 arena 结构性消除(不需要);并发共享 → 借用或验证二选一(多 M 未验证,**此处必须有一个机制,是唯一真正欠着的地方**)。完全验证化替代借用 = 免费午餐债再记一笔(VC 不一定证得出,借用永远可判定)——保留可判定逃逸 pass 当默认,验证处理借用覆盖不到的共享。 +10. **递归类型隐式化**:递归 = 注册表引用图上的环,环检测自动(无 `rec` 关键字);值递归自动拒绝(无限大小 → 编译错误,唯一必须存在的规则);引用递归自动支持(大小固定);归纳原理自动派生(VC 消费)。CDuce 语义子类型已证明递归 + 子类型判定可判定。 +11. **函数类型隐式化**:函数 = 子图(region,机制已有:arena 绑定、callee 内联、region 实例化);函数类型 = 子图端口签名自动派生;函数值 = 子图引用(`@addr` 机制已有);高阶参数 = 签名(非独立「函数类型」概念);闭包(捕获环境)后置,从纯引用开始。规约挂子图接口,VC 自动生成。 +12. **接口声明自动(用法驱动推导)**:接口 = 用法集合——函数体对参数的操作/字段访问自动构成参数的接口(约束推断路线,qualified types / HM(X) 家族理论);调用点自动检查。可读性对策:coreview 显示推导接口视图(「源码 ↔ 图双向高亮」扩展);报错时机:全推断错误在调用点(图→源码映射缓解)。 +13. **显式性最小集 = where 值约束**:语义层不可自动(`b != 0` 是语义属性,用法推不出来——它本身就是验证内容,是 .corespec 的语法入口)。返回类型可推断,保留与否自选。 + +### 边界(14) + +14. **声明式边界**:声明式上限(类型/契约/验证)+ 命令式下限(执行/副作用/内存)——两者共用一张图(数据依赖声明式,状态依赖命令式:state edges / region / 调度)。8 项验证清单第 8 条(格层不重引入顺序执行)即此边界。声明式化不是风格漂移,是验证的必然(验证器只能消费声明);但执行层必须保持命令式,否则内核路线(HIC/crasm/MMIO)漂走。 + +### 明确不做 + +- 完整接口一等化(存在类型/运行期字典)——推迟到平台桥/hw-map 落地时做受限接口值 +- 「不需要类型系统」(全动态化)——实证危险(ASE 2024:3 万仓库,无检查集成 → type confusion) +- HKTs(高阶泛型) ## 二、设计推理 - **单范式原则**:一个概念树(接口)贯穿原语到用户类型,无第二标签范式、无第二类型世界。 -- **语义保鲜**:图保留一切,类型/接口是从图可推导的投影——标注可以自动算出,用户零标注负担。 +- **语义保鲜**:图保留一切,类型/接口/契约全部是从图可推导的投影——标注可以自动算出,用户零标注负担(自动化原则的根源)。 - **纯数学叙事**:int 无上限、dex 精确 = 公理化接口 + 原生实现——原语语义也可对照验证(CompCert 精神推广到原语层)。 - **与既有定案咬合**:M1(身份 = 图节点)、无配方 = 标注、授权归治理层、8 项 Graph→Lattice 验证清单——内存模型 v4 已是图本体,类型系统站到同一层补一致性。 - **接口 = 信息 + 规则**:节点上存储的标注(铭牌)+ 解释标注的规则(本体:良构性规则住验证 pass、公理住规约层)。 +- **先例形态**:Verus / Dafny / F* = 声明式规约 + 命令式代码。Core 的独特处:契约层和实现层共用同一张图(语义保鲜区别于所有先例)。 ## 三、损失账本(免费午餐债——必须显式还的债) | 损失 | 说明 | 偿还方式 | |------|------|---------| | 构造期决议鸡生蛋 | 图构造需要类型选操作,类型靠图分析 | 临时标注 → 事后验证 → 重发射(现有管线已是此形态,显式化) | +| 报错时机 | 全推断/用法推导的标注缺失 → 错误在调用点而非声明处 | 图→源码映射 + LSP 基建(已在建)+ 参数标注可选保留 | | 枚举穷尽性 | match 覆盖检查是类型层性质 | 集合论类型补偿(§5.2) | | 变型/子类型规则 | 接口需显式变型规则,否则静默健全性洞 | 语义子类型可判定化(§5.2) | | 健全性定理 | 不再继承自标准类型系统 | 变为验证管线证明义务(语义保鲜项目反正要证) | | 类型级结构信息静默丢失 | 忘记重编码的信息会被静默丢掉(dex 教训的翻版风险) | 显式设计「标注 + 公理」防漏清单 | +| 验证化替代借用的可判定性 | VC 不一定证得出,借用永远可判定 | 保留可判定逃逸 pass 当默认,验证处理高级共享 | ## 四、演进顺序(每一步是上一步的顺水推舟) @@ -39,7 +67,7 @@ 2. **泛型 = 编译期接口**(第 1 层,便宜) 3. **验证切片**(资助主线:规约 → VC → 自动证明) 4. **v6 格形态**(IR 根基革命) -5. **类型概念收敛**(远期,不动根基,且需 v6 之后) +5. **类型概念收敛**(自动推导全量落地,远期,不动根基) 约束:两条根基革命(v6 与类型收敛)不得同时进行——交叉区域是 checker/ir_gen 全部。 @@ -50,11 +78,15 @@ | 支柱 | 论文 | 印证/提示 | |------|------|----------| | 图本体 IR | [R-HLS(IEEE 2024 / arXiv 2408.08712)](https://ieeexplore.ieee.org/document/11126294)——RVSDG dialect,region + state edges + 内存消歧 | HDFG 设计在硬件综合领域独立平行出现 | -| 接口统一 + 图验证 | [Webs and Flow-Directed Well-Typedness(PLDI 2025)](https://pldi25.sigplan.org/details/pldi-2025-papers/31/Webs-and-Flow-Directed-Well-Typedness-Preserving-Program-Transformations) | 「生产者/消费者必须就接口达成一致 + 统一化类型分析」= 本备忘 §1.2/1.4 的平行印证 | +| 接口统一 + 图验证 | [Webs and Flow-Directed Well-Typedness(PLDI 2025)](https://pldi25.sigplan.org/details/pldi-2025-papers/31/Webs-and-Flow-Directed-Well-Typedness-Preserving-Program-Transformations) | 「生产者/消费者必须就接口达成一致 + 统一化类型分析」= 定案 1/2/4 的平行印证 | | 图 IR 形式语义 | [Denotational Semantics of SSA(2024,Lean 机械化)](https://www.emergentmind.com/papers/2411.09347) | 图 IR 获得形式语义基础 = 语义保鲜的学术形态 | +| 接口自动推导 | qualified types / HM(X) 约束推断家族(Jones) | 用法驱动接口推导的成熟理论 | | 精化类型/易用验证 | [Flux(UCSD)](https://comunicaciones.dcc.uchile.cl/events/310-charla-flux-ergonomic-verification-of-rust-programs-with-liquid-types/)(liquid 推断)· [DML index-sensitive(ECOOP 2026)](https://drops.dagstuhl.de/storage/00lipics/lipics-vol372-ecoop2026/LIPIcs.ECOOP.2026/LIPIcs.ECOOP.2026.pdf)(索引敏感嵌套数组 = F11 方向) | where 值约束 + 推断验证路线成熟 | | 系统级验证 | [Verus(SOSP 2024)](https://dl.acm.org/doi/10.1145/3694715) | 验证管线工程化前沿 | | 健全性成本 | [Generic Refinement Types(POPL 2025)](https://dl.acm.org/doi/10.1145/3704885) · RefinedRust(Coq 机械化) | 免费午餐债的量化证据 | +| 借用替代:区域 | [Tofte-Talpin region inference](https://cristal.inria.fr/attapl/emlti-long.pdf)(Cyclone 线) | arena 模型的历史祖先——无借用规则的区域安全 | +| 借用替代:自动 RC | [Perceus(PLDI 2021)](https://pldi21.sigplan.org/details/pldi-2021-papers/7/Perceus-Garbage-Free-Reference-Counting-with-Reuse) + [FP²(ICFP 2023)](https://icfp23.sigplan.org/details/icfp-2023-papers/10/FP-Fully-in-Place-Functional-Programming) | 无 GC 无借用规则的自动内存管理(Koka 生产级) | +| 借用替代:验证 | [Verus linear ghost types(OOPSLA 2023)](https://2023.splashcon.org/details/splash-2023-oopsla/11/Verus-Verifying-Rust-Programs-using-Linear-Ghost-Types) | **反转洞察**:线性/借用是验证的工具不是负担——ghost 权限证明借用规则覆盖不到的共享 | | 内存 provenance | [CHERI C 形式化(ASPLOS 2024)](https://www.semanticscholar.org/paper/Formal-Mechanised-Semantics-of-CHERI-C%3A-Undefined-Zaliva-Memarian/606074ae655cbd43468b080e8ae35dde827ff395) · [时态安全(CPP 2025)](https://www.research.ed.ac.uk/en/publications/a-cheri-c-memory-model-for-verified-temporal-safety/) · [ISO TS 6010:2025 provenance-aware memory model](https://www.cl.cam.ac.uk/~pes20/papers/topic.Cerberus.html) | 指针模型与 ISO 标准方向收敛 | | 整数↔指针 | [双非确定性内存模型(2024,已集成 CompCert)](https://lib.pusan.ac.kr/lawlib/resource/e-article/?db_id=edskci&mod=detail&page_number=1&record_id=edskci.ARTI.10554258) | asp=1(整数转指针标记)的同题前沿解 | @@ -64,6 +96,7 @@ |------|------|--------| | 枚举穷尽性 | **语义子类型/集合论类型**([Frisch-Castagna-Benzaken, JACM 2008](https://www.lri.fr/~benzaken/papers/semantic_subtyping.pdf)):类型 = 值的集合,union/intersection/negation,子类型 = 集合包含,**可判定** | 穷尽性 = 集合运算;match 覆盖可判定(CDuce typecase 先例) | | 变型/子类型 | 同上(子类型可判定)+ [行多态(Rose POPL 2019 泛化;POPL 2026 行多态+type classes+Lean 4 机械化)](https://dl.acm.org/doi/full/10.1145/3776662) | 结构 + 约束 + 机械化健全性的完整形态 | +| 递归类型 | 同上(CDuce 已证明递归 + 子类型可判定) | 环检测自动 + 值递归拒绝 + 归纳原理自动派生 | | 健全性债 | [Gradually Typed Languages Should Be Vigilant!(OOPSLA 2024)](https://dl.acm.org/doi/full/10.1145/3649842) | 「静态系统真的被强制执行」的精确语义判据(vigilance) | | 无类型系统幻想 | [Typed and Confused(ASE 2024)](https://conf.researchr.org/details/ase-2024/ase-2024-research/149/Typed-and-Confused-Studying-the-Unexpected-Dangers-of-Gradual-Typing):3 万仓库实证 | 无检查集成 → type confusion 攻击的实证证据 | | 决议机制 | [POPL 2026 elaboration 健全性](https://dl.acm.org/doi/full/10.1145/3776662)(源 → 目标演算,Lean 机械化) | 约束决议编译有机械化样板 | @@ -72,12 +105,14 @@ ### 5.3 战略定位 - **PLDI 2025 Webs** = 「接口统一 + 图验证」的独立平行印证——方向是当前编译研究前沿,非空想。 -- **语义子类型 = 完整解**:「类型 = 值的集合」与本备忘「类型 = 图上约束信息」**同构**——标注的语义解释有现成数学,且顺带解决穷尽性、子类型、报错质量(非空反例给具体值)。 +- **语义子类型 = 完整解**:「类型 = 值的集合」与本备忘「类型 = 图上约束信息」**同构**——标注的语义解释有现成数学,且顺带解决穷尽性、子类型、递归、等价、报错质量(非空反例给具体值)。 +- **自动化原则的论文底座**:约束推断(qualified types / HM(X))半个世纪成熟;CDuce 递归可判定;Perceus 无借用自动内存管理——每个「自动」都有成熟先例。 +- **借用收缩而非取消**:arena + 逃逸 pass 已替代大半;唯一欠着的是并发共享(多 M)——借用或验证二选一,此为后续设计决策点。 - **ISO TS 6010**:provenance 已进 C 标准路线——「指针模型与标准方向收敛」是资助材料的硬话。 - **ASE 2024**:对「不需要类型系统」的最直接实证反驳。 ## 六、关联 -- TODO.md:`width-out-of-language`(宽度移出语言定案)、`dex-precision`、`apx-degrade` +- TODO.md:`type-system-direction`(本备忘挂账条目)、`width-out-of-language`、`dex-precision`、`apx-degrade` - docs:`memory-model.md`(能力+格 v4)、`pointer-model.md`、`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(格形态 v6) -- 本备忘落点:类型系统增强总纲——后续实现以本备忘 + TODO 挂账为准 +- 本备忘落点:类型系统增强总纲(14 条定案)——后续实现以本备忘 + TODO 挂账为准 From 0f84f6ba805fef3e7c68ed76a17119f7a938bbd1 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sun, 30 Aug 2026 20:44:20 +0900 Subject: [PATCH 003/138] =?UTF-8?q?docs:=20TODO=20=E6=8C=82=E8=B4=A6?= =?UTF-8?q?=E2=80=94=E2=80=94=E8=87=AA=E5=8A=A8=E5=8C=96=E5=8E=9F=E5=88=99?= =?UTF-8?q?=E5=85=A8=E5=AE=B6=E6=97=8F=EF=BC=88=E6=80=A7=E8=83=BD=206=20?= =?UTF-8?q?=E6=9D=A1=20+=20=E9=AA=8C=E8=AF=81/=E5=B7=A5=E5=85=B7=204=20?= =?UTF-8?q?=E6=9D=A1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/TODO.md b/TODO.md index 58debe6b..339410b4 100644 --- a/TODO.md +++ b/TODO.md @@ -254,6 +254,24 @@ - 循环体内"条件性用"与 if 分支惰性并列但需分开验证 - 参考:`docs/lazy.md`、`docs/superpowers/specs/2026-07-30-lazy-eval-design.md`、`src/compiler/ir_gen.cr`、`src/compiler/dataflow.cr`、`tests/suite/lazy_test.cr`(当前仅验证"包装后输出不变") +### 性能自动化(2026-08-30 记) + +- **自动并发**(auto-parallel,2026-08-30):数据依赖 + state edges 已显式化 → 无依赖 region 的可并行性**可判定**(区别于传统自动并行化的依赖猜测——四十年失败史的根源)。算法:扫描图 → 找无依赖独立 region → 自动分派 goroutine(go/sched 机制已有)。与 R-HLS(IEEE 2024,RVSDG 动态调度)平行;「并发异步」= 8 项验证清单的实证场景。注意:并行粒度成本模型(调度开销 vs 收益)需启发式;起步 = 显式 go 保持 + safe 子集自动并行 +- **自动记忆化**(auto-memo,2026-08-30):图显式纯度判定(无副作用边)→ 多次使用的纯节点自动缓存结果。与自动惰性同机制(惰性 = 延迟执行,memo = 缓存结果),可共用判定/下沉基础设施 +- **PGO 自动剖析**(pgo,2026-08-30):编译器自动插桩收集热路径 → 自动内联/特化/字段布局。标准基础设施(LLVM 成熟路线),零用户标注 +- **自动向量化**(auto-vectorize,2026-08-30):可向量化 region 检测 → SIMD 发射(hw-map 编码层落地后接入)。标准技术,优先级低 +- **自动内联**(auto-inline,2026-08-30):热路径自动内联(PGO 配套);`@inline` 显式保留 +- **自动软件流水**(auto-pipeline,2026-08-30):循环自动流水化。标准技术,优先级低 + +明确不自动:**自动 apx**(精度意图——编译器猜不了意图,apx 标签必须显式;Poseidon(LLVM 2024)参照仅限无验证义务上下文);**数据结构自动选择**(依赖意图,太远) + +### 验证/工具自动化(2026-08-30 记) + +- **不变量自动推断**(invariant-inference,2026-08-30):循环不变量/部分前置条件自动推断——Houdini(注解推断,MSR 2005)+ ICE(反例驱动不变量生成)路线;系统先尝试推断,推不出的才让用户写(与「显式性最小集 = where 值约束」衔接) +- **证明搜索自动**(proof-search,2026-08-30):SMT 层自动找证明,零证明脚本——Verus 免证明自动化路线(EPR 限制逻辑、proof-by-computation) +- **测试生成自动**(test-gen,2026-08-30):where 约束/规约 → 约束求解器自动生成测试用例(验证管线的副产品,近零成本) +- **序列化/打印自动**(serialize-auto,2026-08-30):从接口声明自动生成序列化代码与打印格式(serde 式派生;`dex_str` 已是雏形)——类型驱动代码生成家族 + ### .crasm 统一汇编抽象层(2026-08-09 记) - 目标:内核路线(project-book 第五阶段)的汇编级能力——MMIO、特权指令、中断。跨平台统一指令集 + 无限虚拟寄存器 + 平台映射表,寄存器分配按 v4 方向(缓存语义映射实例,`docs/regalloc-cache-mapping.md`——无限虚拟寄存器 + 平台映射表正是映射实例形态;现 `alloc_registers` 线性扫描器为升级起点) - 现状:设计已批准(2026-08-08 brainstorming 逐节确认),2026-08-09 整理为正式文档 `docs/crasm.md`;**尚未实现**——lexer/parser 无 asm 语法,汇编仅存在于 rt.s 手工汇编与 ELF 后端机器码发射 From 4dfd083bc1a4f1f2c93b90c12d68b02ce5a557e4 Mon Sep 17 00:00:00 2001 From: RhineIris <439114068@qq.com> Date: Sat, 5 Sep 2026 21:02:09 +0800 Subject: [PATCH 004/138] fix: harden compiler, runtime, and LSP safety (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: resolve remaining TODO safety gaps * fix: preserve strings in interpreter * test: cover pointer dereference escape regression * fix: handle generic types in bootstrap checker * fix: harden CCR parsing and i32 serialization * fix: validate APX and static FFI paths * perf: buffer LSP RPC input reads * fix: 字符串常量下标越界守卫 + hex/oct/bin 溢出守卫 base 校准 + 词法错误累积修复(M-2/P2 收尾) * fix: 字符串常量下标越界守卫 + hex/oct/bin 溢出守卫 base 校准 + 词法错误累积修复(M-2/P2 收尾) --------- Co-authored-by: DslsDZC --- TODO.md | 36 ++--- bootstrap/corec/frontend/ir_gen.py | 14 ++ bootstrap/corec/frontend/lexer.py | 35 +++-- bootstrap/corec/frontend/name_resolver.py | 4 + bootstrap/corec/frontend/parser.py | 2 +- bootstrap/corec/frontend/type_checker.py | 54 +++++-- bootstrap/corec/utils/module_loader.py | 20 ++- build_selfhost_native.py | 4 +- src/arch/linux/ld/elf.cr | 3 +- src/arch/linux/ld/instr.cr | 49 ++++-- src/arch/linux/ld/ld.cr | 65 ++++++-- src/arch/linux/ld/main.cr | 1 + src/arch/linux/ld/sizes.cr | 1 + src/compiler/ast.cr | 2 +- src/compiler/ccr_io.cr | 66 +++++++- src/compiler/checker.cr | 12 ++ src/compiler/cir_cache.cr | 4 +- src/compiler/corearch.cr | 1 + src/compiler/dataflow.cr | 9 +- src/compiler/dump.cr | 4 + src/compiler/interp.cr | 74 +++++++-- src/compiler/ir_gen.cr | 64 +++++++- src/compiler/lexer.cr | 98 ++++++++++-- src/compiler/main.cr | 1 + src/compiler/parser.cr | 5 +- src/compiler/region_check.cr | 10 ++ src/lsp/analysis.cr | 181 ++++++++++++++++------ src/lsp/json.cr | 118 ++++++++++---- src/lsp/lsp.cr | 22 ++- src/lsp/rpc.cr | 76 +++++---- src/lsp/test_json.cr | 31 +++- src/stdlib/fmt.cr | 7 +- tests/bootstrap/test_generics.py | 14 ++ tests/bootstrap/test_modules.py | 51 ++++++ tests/bootstrap/test_pipeline.py | 24 +++ tests/fixtures/dex_ffi_shim.c | 14 ++ tests/fixtures/dex_ffi_test.cr | 7 + tests/selfhost/test_dex_arith.py | 62 +++++++- tests/selfhost/test_dex_type.py | 39 ++++- tests/selfhost/test_interp_float.py | 2 + tests/selfhost/test_lsp.py | 28 ++++ tests/selfhost/test_pointer_safety.py | 13 ++ tests/selfhost/test_region_cfg.py | 17 +- tools/corec | 10 +- 44 files changed, 1137 insertions(+), 217 deletions(-) create mode 100644 tests/bootstrap/test_modules.py create mode 100644 tests/fixtures/dex_ffi_shim.c create mode 100644 tests/fixtures/dex_ffi_test.cr diff --git a/TODO.md b/TODO.md index 339410b4..0b6f7b1d 100644 --- a/TODO.md +++ b/TODO.md @@ -106,24 +106,24 @@ ### 2. 解释器局限 - **for 循环**: label/branch 与 dataflow 顺序执行不兼容 - **递归/跨函数调用**: inline 执行不支持 IR_CALL -- **泛型函数**: 类型检查通过但解释器返回 255 +- **泛型函数**: bootstrap 解释器已支持(并补充递归/泛型算术回归);self-hosted 解释器的泛型实例化运行仍待 native 工具链验证 ### 3. 标准库补全 - math.cr / collections.cr 均为 stub -- 字符串操作、JSON 序列化待补 +- 字符串操作、JSON 序列化待补(JSON-RPC 序列化已完成;动态字符串索引边界与字节读写已接入,通用字符串 API 仍待补) ## 第四轮 CompCert 对照遗留项(2026-08-17 记) 来源:`docs/compcert-round4-findings.md`(F1-F20 修复后残留)+ 波 1-3 修复审查产出。F1-F20 已全部修复,以下为范围外/需 IR 形态演进的遗留项: -- **M-2**:字符串索引 `s[5]` 静默 OOB(TI_STR 无检查)——F1/F2 范围外残留(来源:波 1-3 修复审查发现清单) -- **I-3**:模块别名导入断裂(`import fmt : f` / 模块限定调用生成对伪函数 "import" 的调用)——预存在,F16 修复后显性化(来源:波 3 修复审查) -- **lexer 字面量解析 2 项**:`2305843009213693952.0` 字面量解析为垃圾值(bi>53 时 pow2i(负数)=1);>18 位整数部分静默截断——预存在,ELF/interp 双侧受损(来源:波 2 lexer 修复审查) -- **Minor-2**:SPAWN 结果存储用 e2_st(rax) 非 e2_store_ret——float 返回值 spawn 存垃圾(预存)(来源:波 3 修复审查) +- ~~**M-2**:字符串索引 `s[5]` 静默 OOB(TI_STR 无检查)~~(2026-08-29 已修:常量下标与动态下标均生成边界检查,字符串读写按字节处理) +- ~~**I-3**:模块别名导入断裂(`import fmt : f` / 模块限定调用生成对伪函数 "import" 的调用)~~(2026-08-29 已修:bootstrap 保留导入元数据并按 alias 解析限定调用;`tests/bootstrap/test_modules.py` 覆盖) +- ~~**lexer 字面量解析 2 项**:`2305843009213693952.0` 字面量解析为垃圾值(bi>53 时 pow2i(负数)=1);>18 位整数部分静默截断~~(2026-08-29 已修:宽整数显式报错,宽小数避免负指数幂;`test_dex_type.py` 覆盖) +- ~~**Minor-2**:SPAWN 结果存储用 e2_st(rax) 非 e2_store_ret——float 返回值 spawn 存垃圾~~(2026-08-29 已修:IR_SPAWN 按返回类型保存 XMM0/rax) - **F11 运行时界切片长度**:需 IR 形态演进(slice 类型)——已标注设计项(来源:compcert-round4-findings.md F11 / 语义表 BC7) -- **ccr v5 指令记录 i32 截断** ≥2³¹ 的 s3(被 64MB alloc 上限 + null 陷阱兜底)(来源:波 1 修复审查) +- ~~**ccr v5 指令记录 i32 截断** ≥2³¹ 的 s3~~(2026-08-30 已修:`save_ccr` 对所有 i32 指令/region 字段做有符号范围校验,超界直接拒绝写出;`load_ccr` 对各段长度/计数做越界检查;`corearch` 拒绝无效文件大小;`test_ccr_writer_rejects_i32_overflow_inputs` 覆盖 writer guard) - **core_pattern 管道**致陷阱程序 core dump 挂起——CI 建议 `ulimit -c 0`(来源:波 3 测试审查) -- **BC-CONST**:interp TI_STR 字符串表索引近似——未核实,后续轮次(来源:compcert-round4-findings.md §2 注) +- ~~**BC-CONST**:interp TI_STR 字符串表索引近似~~(2026-08-29 已修:解释器统一用驻留索引传递字符串,补齐 `str_len`/`str_eq`/`concat`/`int_str`/`chr`/`get_char`/`str_sub` 与字节索引路径) ## 架构规划 @@ -287,7 +287,7 @@ ### 对照 CompCert 审查发现的未修复 bug(2026-08-11 记,详见 docs/compcert-reference.md) -- **region_check 误报(B11)**:deref 读出的 int 值被当作指针做区域逃逸检查——`v := *p; return v;` 被拦(预先存在,pts 语义需按类型过滤) +- ~~**region_check 误报(B11)**:deref 读出的 int 值被当作指针做区域逃逸检查——`v := *p; return v;` 被拦(预先存在,pts 语义需按类型过滤)~~(2026-08-29 已修:deref/return/store 仅对指针类型执行 provenance/区域逃逸检查;`test_deref_loaded_int_is_not_pointer_escape` 覆盖原始误报) ### float 支持实现记录(2026-08-11,对照 IEEE 754 / SysV 标准实现) @@ -300,18 +300,18 @@ ### corelsp 服务器加固 TODO(2026-08-16 终审分流,详见 LSP 任务审查记录) -- json.cr:节点索引无边界防御(-1/过期索引)、重复键取首值(规范为末值)、`\b`/`\f`/`\/` 拒绝、递归深度无上限 -- rpc.cr:Content-Length 数字溢出绕过上限(19+ 位 → 负 n → alloc)、"content-length" 子串可被其他头误匹配、裸 `\n\n` 头终止符不识别(规范强制 CRLF,合规)、逐字节读性能(100KB ≈ 10 万次 syscall) -- analysis.cr:类型节点索引 0 边界(文件首语句为命名类型 fn 时 hover 回退 "int")、self 参数显示 "int"(impl 解析挂起前不可达)、definition 指向 fn 关键字而非函数名、查询忽略请求 uri(多文档场景悬停 A 返回 B)、多字节字符串按字节列宽匹配(非 UTF-16) -- analysis.cr:completion/documentSymbol 关键字/@ 表以字面量 if 链镜像(新增关键字时漂移风险——已注释指向真源)、semanticTokens 未闭合字符串以 `\` 结尾 span+1、T_INT_I8.. 死条目(lexer 发 T_INT)、T_LET 死 kind +- ~~json.cr:节点索引无边界防御(-1/过期索引)、重复键取首值(规范为末值)、`\b`/`\f`/`\/` 拒绝、递归深度无上限~~(2026-08-28 已修:节点边界、末值语义、标准转义、128 层深度上限、代理对合并、INT64 边界与溢出检查) +- ~~rpc.cr:Content-Length 数字溢出绕过上限(19+ 位 → 负 n → alloc)、"content-length" 子串可被其他头误匹配、裸 `\n\n` 头终止符不识别(规范强制 CRLF,合规)、逐字节读性能(100KB ≈ 10 万次 syscall)~~(2026-08-30 已修:按 CRLF 行解析、字段起始匹配、数值预检与重复字段拒绝;stdin 改为 4KB 分块读取,保持逐字节解析语义) +- ~~analysis.cr:类型节点索引 0 边界(文件首语句为命名类型 fn 时 hover 回退 "int")、self 参数显示 "int"(impl 解析挂起前不可达)、definition 指向 fn 关键字而非函数名、查询忽略请求 uri(多文档场景悬停 A 返回 B)、多字节字符串按字节列宽匹配(非 UTF-16)~~(2026-08-28 已修:函数名令牌定义位置、请求 URI 快照隔离、UTF-16 code unit 坐标;self/impl 语义仍受前端快照限制) +- analysis.cr:completion/documentSymbol 关键字/@ 表以字面量 if 链镜像(新增关键字时漂移风险——已注释指向真源);~~semanticTokens 未闭合字符串以 `\` 结尾 span+1~~(2026-08-29 已修:span 在源末截断);T_INT_I8.. 和 T_LET 是保留但不由当前 lexer 发射的历史 token 常量,semanticTokens 不再将其误分类为 type(后缀位宽迁移仍待实现) - test_lsp.py:第七组 read 超时已修(select 5s);`->` 标记扫描已限定帧间(终审顺手修完成) - 顺手修遗留:报告文档类笔误(lsp-task-7-report 字节数、lsp-task-6-report §1 表未同步 T_WHILE)——scratch 文件,不阻塞 ### 数值类型(dex/apx)迁移遗留 TODO(2026-08-16 终审分流) -- **corearch `--link ` 静态路径崩溃**(so_parse_text SIGILL/SIGSEGV,ld.cr:400 附近):阻塞 extern dex 运行时 FFI 实测(M2 的 IR 层修复已合入并有 cir 断言,运行时验证待此修复);仓库无 .so 产物、--link 路径零测试覆盖;复现记录 + 备好的 C shim 在 /tmp/dex_ffi_shim.c。**执行注意**:shim 用精确位判等,字面量快路径(str_to_f64_bits 截断 ~2ulp)与计算路径(I2F/1e6)有 1-ulp 差(3.14 → 字面量 4614253070214989086 vs canonical 4614253070214989087)——判等须用 lexer 一致常量或容差 -- sizes.cr: IR_APPROX 无显式条目(默认 return 0 兜底、行为正确,纯对称性——与 IR_FAST/IR_UNROLL 显式条目对齐) -- 泛型+dex 返回类型:pre-existing(实例化调用点按 int 处理返回类型,与 float 时代逐位一致;bootstrap 侧正确) -- str_to_f64_bits ~2ulp 截断(保留站点文档化限制):binary64 判别子用 1/3(0.1b+0.2b==0.3 仅 −1ulp 组合恰好落位模式,不可作断言) -- INT_LIT 缺 hex/octal/binary 前缀分支(0x1F 等——既有缺口,tokens.ebnf 已如实记录);`1_000` 下划线产生 T_INT(-1) 静默值 0(两编译器分歧已文档化) +- ~~**corearch `--link ` 静态路径崩溃**(so_parse_text SIGILL/SIGSEGV,ld.cr:400 附近)~~(2026-08-30 已修:静态链接 relocation 使用 user-code 相对偏移,避免把 `.so` 嵌入偏移重复叠加;`.text` 优先按 section 名精确选择;修复 Intel 语法下 `call r16` 的保留寄存器名冲突;静态输出补 writable BSS `PT_LOAD`;`tests/selfhost/test_dex_arith.py::test_extern_dex_static_link` + `tests/fixtures/dex_ffi_shim.c` 覆盖实际 `.so` 构建、`corearch --link` 和 ELF 运行) +- ~~sizes.cr: IR_APPROX 无显式条目~~(2026-08-29 已补显式 0 字节条目,与其他注解指令对齐) +- ~~泛型+dex 返回类型~~(2026-08-30 已修复/验证:局部 dex 槽位按 `apx` 标签正确区分 binary64 与缩放整数;普通 Core 函数边界统一缩放形式;bootstrap 泛型与 native dex/APX 回归通过) +- str_to_f64_bits ~2ulp 截断(保留站点文档化限制):binary64 判别子用 1/3(0.1b+0.2b==0.3 仅 −1ulp 组合恰好落位模式,不可作断言);native APX/FFI/格式化回归已在 WSL 通过 +- ~~INT_LIT 缺 hex/octal/binary 前缀分支(0x1F 等);`1_000` 下划线产生 T_INT(-1) 静默值 0~~(2026-08-29 已修:bootstrap 与自举 lexer 支持 `0x`/`0o`/`0b` 及下划线,并拒绝非法数字) - interp 裸 opcode 数字风格(与既有 op == 26 风格一致,非缺陷);`_f32/_f64` 宽度透传死路径(EBNF/inventory 争议点 7 已标注,apx 位宽标注需新发射路径) diff --git a/bootstrap/corec/frontend/ir_gen.py b/bootstrap/corec/frontend/ir_gen.py index 354e5c60..0587401b 100644 --- a/bootstrap/corec/frontend/ir_gen.py +++ b/bootstrap/corec/frontend/ir_gen.py @@ -14,6 +14,7 @@ def __init__(self, symtab=None): self.local_vars = {} self.struct_fields = {} self.enum_variants = {} + self.module_functions = {} self.block_counter = 0 def new_temp(self, name="t"): @@ -27,6 +28,11 @@ def new_block(self, name="block"): def add_instr(self, instr): self.current_block.instrs.append(instr) def gen_module(self, ast: CompilationUnit) -> Module: + self.module_functions = { + (getattr(decl, '_module_alias', None), decl.name): decl + for decl in ast.declarations + if isinstance(decl, FunctionDecl) and getattr(decl, '_module_alias', None) + } for decl in ast.declarations: if isinstance(decl, StructDecl): self.struct_fields[decl.name] = decl.fields elif isinstance(decl, EnumDecl): self.enum_variants[decl.name] = decl.variants @@ -251,6 +257,14 @@ def gen_unary(self, unop): def gen_call(self, call): if isinstance(call.func, FieldAccess): + module_fn = None + if isinstance(call.func.object, Ident): + module_fn = self.module_functions.get((call.func.object.name, call.func.field)) + if module_fn is not None: + args = [self.gen_expr(a) for a in call.args] + dest = self.new_temp() + self.add_instr(CallInstr(module_fn.name, args, dest)) + return dest obj_expr = call.func.object struct_name = None if isinstance(obj_expr, Ident): diff --git a/bootstrap/corec/frontend/lexer.py b/bootstrap/corec/frontend/lexer.py index 4c978206..91a7077f 100644 --- a/bootstrap/corec/frontend/lexer.py +++ b/bootstrap/corec/frontend/lexer.py @@ -77,16 +77,28 @@ def read_string(self, quote: str) -> Token: def read_number(self, first: str) -> Token: start_line, start_col = self.line, self.col n = first - is_hex = (first == '0' and (self.current() == 'x' or self.current() == 'X')) + base = 10 + is_prefixed = False + if first == '0' and self.current() in 'xXoObB': + prefix = self.advance() + n += prefix + base = {'x': 16, 'X': 16, 'o': 8, 'O': 8, + 'b': 2, 'B': 2}[prefix] + is_prefixed = True is_float = False - if is_hex: - n += self.advance() # consume 'x' - while self.current().isalnum() or self.current() == '_': + if is_prefixed: + digits = '0123456789abcdefABCDEF' if base == 16 else ( + '01234567' if base == 8 else '01') + while self.current() in digits or self.current() == '_': c = self.current() - if c.isdigit() or ('a' <= c <= 'f') or ('A' <= c <= 'F') or c == '_': + if c in digits or c == '_': n += self.advance() else: break + if n[-1] in 'xXoObB' or not any(c != '_' for c in n[2:]): + self.error("invalid integer literal") + if self.current().isalnum() or self.current() == '_': + self.error("invalid digit in integer literal") else: while self.current().isdigit() or self.current() == '_': n += self.advance() @@ -101,9 +113,14 @@ def read_number(self, first: str) -> Token: suffix += self.advance() n += suffix n = n.replace('_', '') - if is_hex: - val = str(int(n, 16)) - return Token(TokenType.INT_LIT, val, start_line, start_col) + # 前缀进制(0x/0o/0b)整数字面量溢出拒绝——i64 语义,与 self-hosted + # lexer 的 base 校准守卫一致(P2)。十进制保持原样词法:self-hosted + # 对十进制幅值边界有既有处理(-9223372036854775808),不一致处理。 + if is_prefixed: + val = int(n[2:], base) + if val > 9223372036854775807: + self.error("integer literal overflow") + return Token(TokenType.INT_LIT, str(val), start_line, start_col) if is_float: return Token(TokenType.FLOAT_LIT, n, start_line, start_col) return Token(TokenType.INT_LIT, n, start_line, start_col) @@ -259,4 +276,4 @@ def tokenize(self) -> list: self.error(f"Unexpected character: '{ch}'") self.tokens.append(Token(TokenType.EOF, '', self.line, self.col)) - return self.tokens \ No newline at end of file + return self.tokens diff --git a/bootstrap/corec/frontend/name_resolver.py b/bootstrap/corec/frontend/name_resolver.py index fe2425c4..2b954302 100644 --- a/bootstrap/corec/frontend/name_resolver.py +++ b/bootstrap/corec/frontend/name_resolver.py @@ -34,6 +34,10 @@ def _declare_builtins(self): def resolve(self, ast: CompilationUnit): self._declare_builtins() + for imp in ast.imports: + alias = imp.alias or imp.path[-1] + if self.symtab.lookup(alias, recursive=False) is None: + self.symtab.define(alias, SymbolKind.MODULE, type_=imp.path[-1]) # 第一遍:收集顶层声明(函数、类型等) for decl in ast.declarations: if isinstance(decl, FunctionDecl): diff --git a/bootstrap/corec/frontend/parser.py b/bootstrap/corec/frontend/parser.py index ba0c5eab..5c76f8a9 100644 --- a/bootstrap/corec/frontend/parser.py +++ b/bootstrap/corec/frontend/parser.py @@ -199,7 +199,7 @@ def parse_import_decl(self): project = self.expect(TokenType.IDENT).lexeme file_id = self.expect(TokenType.IDENT).lexeme alias = None - if self.check(TokenType.AS): + if self.check(TokenType.AS) or self.check(TokenType.COLON): self.advance() alias = self.expect(TokenType.IDENT).lexeme self.expect(TokenType.SEMI) diff --git a/bootstrap/corec/frontend/type_checker.py b/bootstrap/corec/frontend/type_checker.py index e16ac61d..9575b121 100644 --- a/bootstrap/corec/frontend/type_checker.py +++ b/bootstrap/corec/frontend/type_checker.py @@ -13,6 +13,7 @@ def __init__(self, symtab: SymbolTable): self.borrow_state = {} # borrowed_var -> {ref_count, mut_ref} self.borrow_holders = {} # borrower_var -> [(borrowed_var, is_mut)] self.borrow_scope_stack = [] # stack of sets of borrower vars in each scope + self.module_functions = {} # -------------------------------------------------------------- # Borrow checking helpers @@ -84,6 +85,15 @@ def _pop_borrow_scope(self): for borrower in self.borrow_scope_stack.pop(): self._release_holder_borrows(borrower) + def _type_name(self, typ): + """Return a scalar type name without assuming every Type is BaseType.""" + return typ.name if isinstance(typ, BaseType) else None + + def _is_generic_type(self, typ): + """Whether typ is a generic parameter in the active function scope.""" + return (isinstance(typ, PathType) and len(typ.path) == 1 and + any(typ.path[0] in scope for scope in self.generic_scopes)) + # -------------------------------------------------------------- # Built-in types # -------------------------------------------------------------- @@ -100,6 +110,11 @@ def _declare_builtins(self): def check(self, ast: CompilationUnit): self._declare_builtins() + self.module_functions = {} + for decl in ast.declarations: + alias = getattr(decl, '_module_alias', None) + if alias and isinstance(decl, FunctionDecl): + self.module_functions[(alias, decl.name)] = decl for decl in ast.declarations: if isinstance(decl, StructDecl): self._declare_struct(decl) @@ -216,27 +231,36 @@ def _infer_expr(self, expr: Expr) -> Type: return left_t left_t = self._infer_expr(expr.left) right_t = self._infer_expr(expr.right) + left_name = self._type_name(left_t) + right_name = self._type_name(right_t) if expr.op in ('+','-','*','/','%'): - if left_t.name in ('int','dex') and right_t.name in ('int','dex'): - if left_t.name == 'dex' or right_t.name == 'dex': + if left_name in ('int','dex') and right_name in ('int','dex'): + if left_name == 'dex' or right_name == 'dex': return BaseType('dex') return BaseType('int') - elif expr.op == '+' and left_t.name == 'string' and right_t.name == 'string': + elif expr.op == '+' and left_name == 'string' and right_name == 'string': return BaseType('string') + elif self._is_generic_type(left_t) or self._is_generic_type(right_t): + # Generic parameters are dynamically represented by the + # bootstrap interpreter. Preserve the parameter type here + # instead of dereferencing .name on PathType. + return left_t if self._is_generic_type(left_t) else right_t else: - self.errors.append(f"Arithmetic type error between {left_t.name} and {right_t.name}") + self.errors.append(f"Arithmetic type error between {left_t} and {right_t}") return BaseType('never') elif expr.op in ('==','!=','<','>','<=','>='): - if left_t.name not in ('int','dex','string') or right_t.name not in ('int','dex','string'): + if ((left_name not in ('int','dex','string') and not self._is_generic_type(left_t)) or + (right_name not in ('int','dex','string') and not self._is_generic_type(right_t))): self.errors.append(f"Comparison not allowed") return BaseType('bool') elif expr.op in ('|', '&'): - if left_t.name == 'int' and right_t.name == 'int': + if ((left_name == 'int' and right_name == 'int') or + (self._is_generic_type(left_t) and self._is_generic_type(right_t))): return BaseType('int') self.errors.append(f"Bitwise ops require int") return BaseType('never') elif expr.op in ('&&','||'): - if left_t.name == 'bool' and right_t.name == 'bool': + if left_name == 'bool' and right_name == 'bool': return BaseType('bool') self.errors.append("Logical ops require bool") return BaseType('never') @@ -264,7 +288,7 @@ def _infer_expr(self, expr: Expr) -> Type: self.errors.append("Cannot dereference non-reference type") return BaseType('never') if expr.op == '!': - if operand_type.name != 'bool': + if self._type_name(operand_type) != 'bool': self.errors.append("Unary ! requires bool") return BaseType('never') return BaseType('bool') @@ -300,6 +324,8 @@ def _infer_expr(self, expr: Expr) -> Type: return GenericApplyType([enum_name], concrete_args) return func_decl.return_type elif isinstance(expr, FieldAccess): + if isinstance(expr.object, Ident) and (expr.object.name, expr.field) in self.module_functions: + return self.module_functions[(expr.object.name, expr.field)].return_type obj_t = self._infer_expr(expr.object) # Unwrap RefType for field access through references if isinstance(obj_t, RefType): @@ -379,7 +405,7 @@ def _infer_expr(self, expr: Expr) -> Type: return last_type elif isinstance(expr, If): cond_t = self._infer_expr(expr.cond) - if cond_t.name != 'bool': + if self._type_name(cond_t) != 'bool': self.errors.append("If condition must be bool") self.symtab.push_scope() self._push_borrow_scope() @@ -507,6 +533,16 @@ def _infer_call(self, call: Call) -> Type: # Track which function we're checking (set by _check_function) self._current_checking_func = getattr(self, '_current_checking_func', '?') if isinstance(call.func, FieldAccess): + if isinstance(call.func.object, Ident): + module_fn = self.module_functions.get((call.func.object.name, call.func.field)) + if module_fn is not None: + if len(call.args) != len(module_fn.params): + self.errors.append(f"Module function {call.func.field} argument count mismatch") + else: + for arg, (_, pt) in zip(call.args, module_fn.params): + if not self._type_equal(self._infer_expr(arg), pt): + self.errors.append("Module function argument type mismatch") + return module_fn.return_type obj_t = self._infer_expr(call.func.object) if isinstance(obj_t, RefType): obj_t = obj_t.inner diff --git a/bootstrap/corec/utils/module_loader.py b/bootstrap/corec/utils/module_loader.py index dd326380..02b7e0e8 100644 --- a/bootstrap/corec/utils/module_loader.py +++ b/bootstrap/corec/utils/module_loader.py @@ -24,7 +24,8 @@ def _parse_coretoml(search_path): toml_path = os.path.join(search_path, 'Core.toml') if not os.path.exists(toml_path): return None - m = re.search(r'^\s*name\s*=\s*"([^"]+)"', open(toml_path).read(), re.MULTILINE) + with open(toml_path, encoding='utf-8') as f: + m = re.search(r'^\s*name\s*=\s*"([^"]+)"', f.read(), re.MULTILINE) return m.group(1) if m else None @@ -34,7 +35,7 @@ def _determine_fileid(filepath): If the file starts with `fileid "name";`, use that. Otherwise use the filename without .cr extension. """ - with open(filepath) as f: + with open(filepath, encoding='utf-8') as f: head = f.read(512) # Only need first few hundred bytes m = _FILEID_RE.search(head) if m: @@ -87,7 +88,7 @@ def _parse_imports_from_file(filepath): Returns list of (file_id, alias, project). """ - with open(filepath) as f: + with open(filepath, encoding='utf-8') as f: src = f.read() lex = Lexer(src) tokens = lex.tokenize() @@ -168,6 +169,10 @@ def resolve_imports(ast, source_path=None, search_paths=None, errors=None): if source_path: current_dir = os.path.dirname(os.path.abspath(source_path)) + # Preserve imports as semantic metadata. Flattened declarations still need + # their alias when a qualified call is type-checked. + root_imports = list(ast.imports) + # Resolve each import loaded = set() while ast.imports: @@ -219,7 +224,7 @@ def resolve_imports(ast, source_path=None, search_paths=None, errors=None): loaded.add(abs_path) # Parse the imported file - with open(abs_path) as f: + with open(abs_path, encoding='utf-8') as f: src = f.read() lex = Lexer(src) tokens = lex.tokenize() @@ -230,4 +235,11 @@ def resolve_imports(ast, source_path=None, search_paths=None, errors=None): search_paths=search_paths, errors=errors) # Prepend imported declarations + module_alias = imp.alias or file_id + for decl in mod_ast.declarations: + if isinstance(decl, FunctionDecl): + decl._module_alias = module_alias + decl._module_file_id = file_id ast.declarations = list(mod_ast.declarations) + ast.declarations + + ast.imports = root_imports diff --git a/build_selfhost_native.py b/build_selfhost_native.py index e79f4746..56616e11 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -26,7 +26,7 @@ def concat(files, wrapper_fn=None): for f in files: path = os.path.join(BASE, f) if os.path.exists(path): - with open(path) as fh: + with open(path, encoding='utf-8') as fh: content = fh.read().strip() if content: # This build creates one flat compilation unit from an explicit @@ -84,7 +84,7 @@ def compile_and_assemble(src, label, out_name): os.makedirs('build', exist_ok=True) asm_path = f'build/{out_name}.s' - with open(asm_path, 'w') as f: + with open(asm_path, 'w', encoding='utf-8') as f: f.write(asm) result = subprocess.run(['as', '-o', f'build/{out_name}.o', asm_path], diff --git a/src/arch/linux/ld/elf.cr b/src/arch/linux/ld/elf.cr index 2929b01f..9fdeedb4 100644 --- a/src/arch/linux/ld/elf.cr +++ b/src/arch/linux/ld/elf.cr @@ -10,7 +10,8 @@ PT_LOAD : int = 1; PF_RX : int = 5; PF_RW : int = 6; -fn r16(buf: string, pos: int) -> int { return bu8(buf,pos) + bu8(buf,pos+1)*256; } +// Avoid the name r16: Intel syntax parses `call r16` as a register operand. +fn read_u16(buf: string, pos: int) -> int { return bu8(buf,pos) + bu8(buf,pos+1)*256; } TEXT_BASE : int = 4194304; // 0x400000 - base address of code segment diff --git a/src/arch/linux/ld/instr.cr b/src/arch/linux/ld/instr.cr index 3f3e8b49..002fb1fe 100644 --- a/src/arch/linux/ld/instr.cr +++ b/src/arch/linux/ld/instr.cr @@ -960,7 +960,11 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { e2_w32(buf, pos+cp+3, stack_bytes); cp = cp + 7; } } - cp = cp + e2_st(buf, pos+cp, 0, do2); + if d >= 0 && irv_type(d) == TI_DEX { + cp = cp + e2_sd_store(buf, pos+cp, do2); + } else { + cp = cp + e2_st(buf, pos+cp, 0, do2); + } return cp; } @@ -1284,10 +1288,15 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { do2 := g2_slot(d); idx := s3; // load array pointer (handles local and global) cp = cp + e2_load_var(buf, pos+cp, 10, s1); - // mov r10, [r10 + disp32] - // mov r10, [r10 + idx*8] + if irv_type(s1) == TI_STR { + // Strings are byte sequences, unlike arrays of 8-byte values. + cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 10/8); e2_w8(buf, pos+cp, 15); cp = cp + 1; + e2_w8(buf, pos+cp, 182); cp = cp + 1; + cp = cp + emit_modrm(buf, pos+cp, 2, 10%8, 10%8); cp = cp + e2_w32(buf, pos+cp, idx); + } else { cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 2, 10%8, 10%8); cp = cp + e2_w32(buf, pos+cp, idx * 8); + } cp = cp + e2_st(buf, pos+cp, 10, do2); return cp; } @@ -1299,9 +1308,15 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { // load value to store (handles local and global) cp = cp + e2_load_var(buf, pos+cp, 11, s2); // mov [r10 + disp32], r11 - // mov [r10 + idx*8], r11 + if irv_type(s1) == TI_STR { + // String assignment stores one byte, not a full machine word. + cp = cp + emit_rex(buf, pos+cp, 0, 11/8, 0, 10/8); e2_w8(buf, pos+cp, 136); cp = cp + 1; + cp = cp + emit_modrm(buf, pos+cp, 2, 11%8, 10%8); cp = cp + e2_w32(buf, pos+cp, idx); + } else { + // mov [r10 + idx*8], r11 cp = cp + emit_rex(buf, pos+cp, 1, 11/8, 0, 10/8); e2_w8(buf, pos+cp, 137); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 2, 11%8, 10%8); cp = cp + e2_w32(buf, pos+cp, idx * 8); + } return cp; } @@ -1311,9 +1326,14 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { cp = cp + e2_load_var(buf, pos+cp, 10, s1); // load index (handles local and global) cp = cp + e2_load_var(buf, pos+cp, 11, s2); - // mov r10, [r10 + r11*8] — SIB(scale=3, index=r11%8, base=r10%8) + // Strings use byte indexing; arrays use 8-byte element indexing. + if irv_type(s1) == TI_STR { + cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 11/8, 10/8); e2_w8(buf, pos+cp, 15); cp = cp + 1; e2_w8(buf, pos+cp, 182); cp = cp + 1; + cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 4); cp = cp + emit_sib(buf, pos+cp, 0, 11%8, 10%8); + } else { cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 11/8, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 4); cp = cp + emit_sib(buf, pos+cp, 3, 11%8, 10%8); + } cp = cp + e2_st(buf, pos+cp, 10, do2); return cp; } @@ -1325,9 +1345,15 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { cp = cp + e2_load_var(buf, pos+cp, 11, s2); // load value to store (handles local and global) cp = cp + e2_load_var(buf, pos+cp, 12, d); - // mov [r10 + r11*8], r12 — SIB(scale=3, index=r11%8, base=r10%8) + if irv_type(s1) == TI_STR { + // String assignment stores one byte at the runtime byte index. + cp = cp + emit_rex(buf, pos+cp, 0, 12/8, 0, 10/8); e2_w8(buf, pos+cp, 136); cp = cp + 1; + cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4); cp = cp + emit_sib(buf, pos+cp, 0, 11%8, 10%8); + } else { + // mov [r10 + r11*8], r12 — SIB(scale=3, index=r11%8, base=r10%8) cp = cp + emit_rex(buf, pos+cp, 1, 12/8, 11/8, 10/8); e2_w8(buf, pos+cp, 137); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4); cp = cp + emit_sib(buf, pos+cp, 3, 11%8, 10%8); + } return cp; } @@ -1367,9 +1393,14 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { // F1c:s2 是字面量长度(发射约定),修复前按变量槽加载(e2_load_var)—— // 把长度当变量索引读,检查恒错。 cp = cp + e2_load_var(buf, pos+cp, 10, s1); // index - // movabs r11, imm64 — REX.W+B 0x49 0xBB + imm64 - e2_w8(buf, pos+cp, 73); e2_w8(buf, pos+cp+1, 187); - e2_w64(buf, pos+cp+2, s2); cp = cp + 10; + if ti != 0 { + // Dynamic upper bound: load len_var into r11. + cp = cp + e2_load_var(buf, pos+cp, 11, s2); + } else { + // Constant upper bound: movabs r11, imm64. + e2_w8(buf, pos+cp, 73); e2_w8(buf, pos+cp+1, 187); + e2_w64(buf, pos+cp+2, s2); cp = cp + 10; + } cp = cp + e2_alu(buf, pos+cp, 57); // cmp r10, r11 // jb +2: if index < max (unsigned below), skip the 2-byte ud2 → continue e2_w8(buf, pos+cp, 114); // 0x72 = jb rel8 diff --git a/src/arch/linux/ld/ld.cr b/src/arch/linux/ld/ld.cr index c6f9b28c..1ae6f3ae 100644 --- a/src/arch/linux/ld/ld.cr +++ b/src/arch/linux/ld/ld.cr @@ -107,8 +107,8 @@ fn cby(k: int) -> int { fn so_find(buf: string, name: string) -> int { if str_len(buf) < 64 { return -1; } - if r16(buf, 16) != 3 { return -1; } - e_shoff := r64(buf, 40); e_shnum := r16(buf, 60); + if read_u16(buf, 16) != 3 { return -1; } + e_shoff := r64(buf, 40); e_shnum := read_u16(buf, 60); do : ., mut = 0; ds : ., mut = 0; so : ., mut = 0; ss : ., mut = 0; i : ., mut = 0; loop { if i >= e_shnum { break; } t := r32(buf, e_shoff+i*64+4); a := r64(buf, e_shoff+i*64+16); @@ -120,7 +120,7 @@ fn so_find(buf: string, name: string) -> int { sc := ds / 24; i = 0; loop { if i >= sc { break; } sn := r32(buf, do+i*24); sv := r64(buf, do+i*24+8); - si := bu8(buf, do+i*24+4); sx := r16(buf, do+i*24+6); + si := bu8(buf, do+i*24+4); sx := read_u16(buf, do+i*24+6); if si/16 == 1 && si%16 == 2 && sx != 0 { nm : ., mut = ""; k : ., mut = sn; loop { if k >= ss { break; } @@ -400,16 +400,44 @@ g_so_addr : int, mut; // .so .text base address fn so_parse_text(buf: string) -> int { // Find the executable PROGBITS section (.text) in the .so if str_len(buf) < 64 { return -1; } - e_shoff := r64(buf, 40); e_shnum := r16(buf, 60); + e_shoff := r64(buf, 40); e_shnum := read_u16(buf, 60); + // A normal linker may place .init/.plt before .text. The old first-match + // scan copied one of those stubs and then resolved symbols against the + // wrong address range. Prefer the exact .text name from shstrtab. + e_shstrndx := read_u16(buf, 62); + shstr_off : ., mut = 0; + shstr_sz : ., mut = 0; + if e_shstrndx < e_shnum { + sh := e_shoff + e_shstrndx * 64; + shstr_off = r64(buf, sh + 24); + shstr_sz = r64(buf, sh + 32); + } i : ., mut = 0; + fallback_off : ., mut = -1; + fallback_sz : ., mut = 0; + fallback_addr : ., mut = 0; loop { if i >= e_shnum { break; } t := r32(buf, e_shoff+i*64+4); fl := r64(buf, e_shoff+i*64+8); a := r64(buf, e_shoff+i*64+16); o := r64(buf, e_shoff+i*64+24); s := r64(buf, e_shoff+i*64+32); // SHT_PROGBITS(1) + SHF_ALLOC|SHF_EXECINSTR(6) = .text if t == 1 && fl == 6 && s > 0 { - g_so_off = o; g_so_sz = s; g_so_addr = a; - return 0; } + if fallback_off < 0 { fallback_off = o; fallback_sz = s; fallback_addr = a; } + if shstr_off > 0 && shstr_sz > 0 { + nm : ., mut = ""; p := e_shoff + i * 64; + no := r32(buf, p); + if no < shstr_sz { + k : ., mut = no; + loop { if k >= shstr_sz { break; } + c := bu8(buf, shstr_off + k); if c == 0 { break; } + one := get_char(" ", 0); store8(one, 0, c); nm = nm + one; k = k + 1; } + if str_eq(nm, ".text") != 0 { + g_so_off = o; g_so_sz = s; g_so_addr = a; return 0; + } + } + } + } i = i + 1; } + if fallback_off >= 0 { g_so_off = fallback_off; g_so_sz = fallback_sz; g_so_addr = fallback_addr; return 0; } return -1; } fn ctx_emit_static(buf: string, path: string) -> int { @@ -453,9 +481,20 @@ fn ctx_emit_static(buf: string, path: string) -> int { sym_addr := so_find(so_buf, fn_name); if sym_addr >= g_so_addr && sym_addr < g_so_addr + g_so_sz { func_off := sym_addr - g_so_addr; - call_pos := user_out + abs_pos; - target_va := 0x400000 + so_out + func_off; - rel := target_va - (call_pos + 5); + // Relocations are recorded against the full ELF buffer + // (the user code starts at offset 176), but ctx_emit_static + // copies that code to user_out after the embedded .so text. + // Convert the absolute emission position to a user-code + // relative offset before applying the new placement. + code_off := abs_pos - 176; + if code_off < 0 || code_off >= g_user_size { unresolved = unresolved + 1; rpi = rpi + 1; continue; } + // IR_CALL_EXTERN records the call opcode, while the regular + // unknown-call path records its displacement byte. Normalize + // both forms to the opcode before writing rel32. + call_pos : ., mut = user_out + code_off; + if bu8(g_user_code, code_off) != 232 { call_pos = call_pos - 1; } + target_va := 0x400000 + so_out + func_off; + rel := target_va - (0x400000 + call_pos + 5); w32(buf, call_pos + 1, rel); } else { println("error: 无法解析外部符号(静态链接):" + fn_name); @@ -471,11 +510,17 @@ fn ctx_emit_static(buf: string, path: string) -> int { w16(buf,16,2);w16(buf,18,62);w32(buf,20,1); w64(buf,24,0x400000 + user_out); // entry = user code's _start w64(buf,32,64);w64(buf,40,0); - w16(buf,52,64);w16(buf,54,56);w16(buf,56,1);w16(buf,58,64); + w16(buf,52,64);w16(buf,54,56);w16(buf,56,2);w16(buf,58,64); w32(buf,64,1);w32(buf,68,5);w64(buf,72,0); w64(buf,80,0x400000);w64(buf,88,0x400000); w64(buf,96,total);w64(buf,104,total); w64(buf,112,4096); + // The embedded user code still contains RIP-relative references to the + // original frontend BSS page, shifted by the copied .so text. Map a + // writable anonymous load at the next page so allocator/global slots do + // not fault when the linked executable starts. + bss_page := 0x400000 + ((total + 4095) / 4096) * 4096 + 4096; + write_phdr(buf, 1, 1, 6, 0, bss_page, bss_page, 0, 1073741824, 4096); fd := syscall3(2, path, 577, 493); // 0755:ELF 输出必须可执行(Task 6 修复 0644 怪癖) if fd < 0 { return -1; } diff --git a/src/arch/linux/ld/main.cr b/src/arch/linux/ld/main.cr index da29bc5f..dee96e01 100644 --- a/src/arch/linux/ld/main.cr +++ b/src/arch/linux/ld/main.cr @@ -42,6 +42,7 @@ fn corearch_main() -> int { if fd < 0 { print("error: cannot open "); println(src_path); return 1; } fsize := syscall3(8, fd, 0, 2); syscall3(8, fd, 0, 0); + if fsize < 36 { syscall3(3, fd, 0, 0); println("error: invalid .ccr file size"); return 1; } buf := alloc(fsize + 1); nread := syscall3(0, fd, buf, fsize); syscall3(3, fd, 0, 0); diff --git a/src/arch/linux/ld/sizes.cr b/src/arch/linux/ld/sizes.cr index 0e88f404..e6d9ee00 100644 --- a/src/arch/linux/ld/sizes.cr +++ b/src/arch/linux/ld/sizes.cr @@ -59,6 +59,7 @@ fn instr_size(op: int) -> int { if op == IR_FAST { return 0; } if op == IR_UNROLL { return 0; } if op == IR_SECTION { return 0; } + if op == IR_APPROX { return 0; } if op == IR_LAZY_THUNK { return 11; } if op == IR_LAZY_FORCE { return 11; } if op == IR_SPAWN { return 9; } // call(5) + store(4) diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index 792f140b..a836170e 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -566,7 +566,7 @@ IR_ADDR_INDEX : int = 31; // dest=addr, s1=arr_var, s2=index_var, s3=scale — c IR_SPAWN : int = 27; // dest=result_var, s1=first_arg, s2=arg_count, s3=fn_name_ni, type_kind=spawn_count (-1=dynamic) IR_YIELD : int = 28; // s1=value_var — emit value from flow to consumer channel IR_AWAIT : int = 29; // dest=value_var, s1=future_var — block until future ready, get value -IR_BOUNDS_CHECK : int = 30; // s1=index_var, s2=max_len — if index<0 or index>=max_len, abort (s2<0 = skip) +IR_BOUNDS_CHECK : int = 30; // s1=index, s2=max_len/len_var, type_kind=1 for dynamic max IR_ARENA_NEW : int = 32; // dest=arena_var, src1=size_estimate IR_ARENA_RESET : int = 33; // src1=arena_id (dest=-1) IR_INLINE : int = 34; // src1=fn_var — inline hint diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 2c78c47b..e788b266 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -87,6 +87,44 @@ fn buf_read_i64(buf: string, pos: int) -> int { return lo + hi_part; } +// Check a byte range without forming an overflowing end position. CCR counts +// come from the file, so every variable-length section must use this helper +// before reading or growing an array from its count. +fn ccr_has_bytes(pos: int, need: int, fsize: int) -> int { + if pos < 0 || need < 0 || pos > fsize { return 0; } + if need > fsize - pos { return 0; } + return 1; +} + +fn ccr_i32_fits(val: int) -> int { + if val < -2147483648 || val > 2147483647 { return 0; } + return 1; +} + +fn ccr_validate_i32_fields() -> int { + ii : ., mut = 0; + loop { + if ii >= g_ir_instr_count { break; } + if ccr_i32_fits(iri_dest(ii)) == 0 || + ccr_i32_fits(iri_s2(ii)) == 0 || + ccr_i32_fits(iri_s3(ii)) == 0 { return 0; } + ii = ii + 1; + } + si : ., mut = 0; + loop { + if si >= g_sg_count { break; } + f := si * ESZ_SG; + if ccr_i32_fits(r64(g_sgs, f + OFF_SG_KIND)) == 0 || + ccr_i32_fits(r64(g_sgs, f + OFF_SG_ENTER)) == 0 || + ccr_i32_fits(r64(g_sgs, f + OFF_SG_EXIT)) == 0 || + ccr_i32_fits(r64(g_sgs, f + OFF_SG_PARENT)) == 0 || + ccr_i32_fits(r64(g_sgs, f + OFF_SG_NSTART)) == 0 || + ccr_i32_fits(r64(g_sgs, f + OFF_SG_NCOUNT)) == 0 { return 0; } + si = si + 1; + } + return 1; +} + // --- Size calculation --- fn calc_ccr_size() -> int { @@ -153,6 +191,9 @@ fn calc_ccr_size() -> int { // --- Save --- fn save_ccr(path: string) -> int { + // The v5 wire format stores these fields as signed i32. Refuse to emit a + // lossy file instead of letting w32 silently keep only the low bits. + if ccr_validate_i32_fields() == 0 { return -1; } tsz := calc_ccr_size(); buf := alloc(tsz); pos : ., mut = 0; @@ -354,6 +395,14 @@ fn load_ccr(data: string, fsize: int) -> int { struct_cnt := buf_read_u32(data, pos); pos = pos + 4; enum_cnt := buf_read_u32(data, pos); pos = pos + 4; + // Reject impossible fixed-width counts before allocating their backing + // arrays. Variable sections are checked again at their exact position + // below, after preceding strings have been consumed. + if str_cnt > fsize / 4 || func_cnt > fsize / 28 || + instr_cnt > fsize / 28 || var_cnt > fsize / 12 || + str_const_cnt > fsize / 4 || struct_cnt > fsize / 8 || + enum_cnt > fsize / 8 { return -1; } + // Grow dynamic arrays to needed capacity grow_ir_vars(var_cnt); grow_ir_instrs(instr_cnt); @@ -376,7 +425,9 @@ fn load_ccr(data: string, fsize: int) -> int { si : ., mut = 0; loop { if si >= str_cnt { break; } + if !ccr_has_bytes(pos, 4, fsize) { return -1; } sl := buf_read_u32(data, pos); pos = pos + 4; + if !ccr_has_bytes(pos, sl, fsize) { return -1; } // Allocate buffer for string content s := alloc(sl + 1); ci : ., mut = 0; @@ -396,6 +447,7 @@ fn load_ccr(data: string, fsize: int) -> int { fi : ., mut = 0; loop { if fi >= func_cnt { break; } + if !ccr_has_bytes(pos, 28, fsize) { return -1; } fv0 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_name_idx, fi * 8, fv0); fv1 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_param_count, fi * 8, fv1); fv2 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_ret_type, fi * 8, fv2); @@ -411,6 +463,7 @@ fn load_ccr(data: string, fsize: int) -> int { ii : ., mut = 0; loop { if ii >= instr_cnt { break; } + if !ccr_has_bytes(pos, 28, fsize) { return -1; } opcode := buf_read_u32(data, pos); pos = pos + 4; dest := buf_read_i32(data, pos); pos = pos + 4; s1 := buf_read_i64(data, pos); pos = pos + 8; // 修复 14:s1 64 位 @@ -431,6 +484,7 @@ fn load_ccr(data: string, fsize: int) -> int { vi : ., mut = 0; loop { if vi >= var_cnt { break; } + if !ccr_has_bytes(pos, 12, fsize) { return -1; } name_ni := buf_read_u32(data, pos); pos = pos + 4; id := buf_read_u32(data, pos); pos = pos + 4; tk := buf_read_u32(data, pos); pos = pos + 4; @@ -445,6 +499,7 @@ fn load_ccr(data: string, fsize: int) -> int { sci : ., mut = 0; loop { if sci >= str_const_cnt { break; } + if !ccr_has_bytes(pos, 4, fsize) { return -1; } scv := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_str_consts, sci * 8, scv); g_ir_str_const_count = sci + 1; sci = sci + 1; @@ -454,9 +509,11 @@ fn load_ccr(data: string, fsize: int) -> int { sti : ., mut = 0; loop { if sti >= struct_cnt { break; } + if !ccr_has_bytes(pos, 8, fsize) { return -1; } name_ni := buf_read_u32(data, pos); pos = pos + 4; fc := buf_read_u32(data, pos); pos = pos + 4; if fc > MAX_STRUCT_FIELDS { println("error: .ccr struct field count exceeds max"); return 1; } + if fc > (fsize - pos) / 8 { return -1; } w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, name_ni); w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc); // Zero out all field slots and type nodes @@ -489,9 +546,11 @@ fn load_ccr(data: string, fsize: int) -> int { ei : ., mut = 0; loop { if ei >= enum_cnt { break; } + if !ccr_has_bytes(pos, 8, fsize) { return -1; } ename_ni := buf_read_u32(data, pos); pos = pos + 4; vc := buf_read_u32(data, pos); pos = pos + 4; if vc > MAX_ENUM_VARIANTS { println("error: .ccr enum variant count exceeds max"); return 1; } + if vc > (fsize - pos) / 8 { return -1; } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni); w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc); // Zero all variant slots @@ -514,6 +573,7 @@ fn load_ccr(data: string, fsize: int) -> int { vni := buf_read_u32(data, pos); pos = pos + 4; tc := buf_read_u32(data, pos); pos = pos + 4; if tc > MAX_VARIANT_TYPES { println("error: .ccr variant type count exceeds max"); return 1; } + if tc > (fsize - pos) / 4 { return -1; } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vi3 * OFF_EV_SIZE + OFF_EV_NAME, vni); w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vi3 * OFF_EV_SIZE + OFF_EV_TYPE_COUNT, tc); tf : ., mut = 0; @@ -530,7 +590,11 @@ fn load_ccr(data: string, fsize: int) -> int { // Globals (version >= 2) if ver >= 2 { + if !ccr_has_bytes(pos, 4, fsize) { return -1; } gc := buf_read_u32(data, pos); pos = pos + 4; + global_size : ., mut = 8; + if ver >= 4 { global_size = 16; } + if gc > (fsize - pos) / global_size { return -1; } grow_ir_globals(gc); g_ir_global_count = 0; gi : ., mut = 0; @@ -583,7 +647,7 @@ fn load_ccr(data: string, fsize: int) -> int { if ver >= 5 { if pos + 4 > fsize { return -1; } sg_n := buf_read_u32(data, pos); pos = pos + 4; - if pos + sg_n * ESZ_SG_DISK > fsize { return -1; } + if sg_n > (fsize - pos) / ESZ_SG_DISK { return -1; } sg_i : ., mut = 0; loop { if sg_i >= sg_n { break; } diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index 4388a073..52c24804 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -2086,6 +2086,18 @@ fn infer_expr(node: int) -> int { return get_type_data(arr_ti); // slice[i] → element type } if arr_ti == TI_STR { + // A string is a byte sequence. Reject a statically known invalid + // index instead of allowing a silent read past its terminator. + if ast_kind(ast_a(node)) == EXPR_STRING && ast_kind(ast_b(node)) == EXPR_INT { + string_len := istr_len(ast_int_val(ast_a(node))); + string_idx := ast_int_val(ast_b(node)); + if string_idx < 0 || string_idx >= string_len { + check_error(EC_R_OOB, + "string index out of bounds: " + int_str(string_idx) + + " (length " + int_str(string_len) + ")", + ast_line(node), ast_col(node)); + } + } return TI_INT; // string[i] → byte value } check_error(EC_TK_INDEX, "Cannot index non-array type", ast_line(node), ast_col(node)); diff --git a/src/compiler/cir_cache.cr b/src/compiler/cir_cache.cr index 27417988..c3c0fd43 100644 --- a/src/compiler/cir_cache.cr +++ b/src/compiler/cir_cache.cr @@ -7,7 +7,9 @@ // Magic header for .cir cache files // v12: invalidate older call flags, generic cloning/indexing, and yield ASTs. // v13: persist nested SG region metadata in per-function cache files. -CIR_CACHE_MAGIC : int = 0xC1C1C1C1C1C1C1C1; +// 注意:magic 位模式 = C1C1…(bytes);以 signed 十进制书写——hex 字面量 +// 0xC1C1C1C1C1C1C1C1 超 i64 上界,会被词法溢出守卫拒绝(见 lexer P2 修复)。 +CIR_CACHE_MAGIC : int = -4485090715960753727; CIR_CACHE_VER : int = 13; g_cir_write_buf : string, mut; diff --git a/src/compiler/corearch.cr b/src/compiler/corearch.cr index ae0956d8..ee96b558 100644 --- a/src/compiler/corearch.cr +++ b/src/compiler/corearch.cr @@ -42,6 +42,7 @@ fn corearch_main() -> int { if fd < 0 { print("error: cannot open "); println(src_path); return 1; } fsize := syscall3(8, fd, 0, 2); syscall3(8, fd, 0, 0); + if fsize < 36 { syscall3(3, fd, 0, 0); println("error: invalid .ccr file size"); return 1; } buf := alloc(fsize + 1); nread := syscall3(0, fd, buf, fsize); syscall3(3, fd, 0, 0); diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index 93ce71f6..ae1584db 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -125,7 +125,7 @@ fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, type_ } // Add edges for src fields that are IR variables (based on opcode) - df_connect_srcs(nid, opcode, src1, src2, src3); + df_connect_srcs(nid, opcode, src1, src2, src3, type_kind); // VSDG state chain: side-effecting nodes are ordered by state edges df_connect_state(nid, opcode, src3); return nid; @@ -191,7 +191,7 @@ fn df_use_var(consumer_node: int, var_idx: int) { // Connect source operands based on opcode semantics. // Only fields that carry IR variable indices create dataflow edges. -fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int) { +fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int, type_kind: int) { if opcode == IR_CONST { return; } // all srcs are scalar values/labels if opcode == IR_BINARY { @@ -265,6 +265,11 @@ fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int) { df_use_var(node_id, s1); // condition var (labels s2, s3 are not vars) return; } + if opcode == IR_BOUNDS_CHECK { + df_use_var(node_id, s1); + if type_kind != 0 { df_use_var(node_id, s2); } + return; + } if opcode == IR_REF { df_use_var(node_id, s1); // referenced var return; diff --git a/src/compiler/dump.cr b/src/compiler/dump.cr index 867784a2..b61b24e1 100644 --- a/src/compiler/dump.cr +++ b/src/compiler/dump.cr @@ -228,12 +228,14 @@ fn cmd_ir(src_path: string) -> int { return 1; } g_source_dir = dirname(src_path); + g_error_count = 0; // 会话起点(tokenize 多轮累积词法错误,见 lexer.cr) tokenize(g_source); g_str_count = 0; res_imports(); parse_all(); check_all(); if g_diag_count > 0 { print_diagnostics(); return 1; } + if g_error_count > 0 { print_parse_errors(); return 1; } ir_gen_all(); dot := df_graph_to_dot(); @@ -340,12 +342,14 @@ fn cmd_cir(src_path: string) -> int { return 1; } g_source_dir = dirname(src_path); + g_error_count = 0; // 会话起点(tokenize 多轮累积词法错误,见 lexer.cr) tokenize(g_source); g_str_count = 0; res_imports(); parse_all(); check_all(); if g_diag_count > 0 { print_diagnostics(); return 1; } + if g_error_count > 0 { print_parse_errors(); return 1; } ir_gen_all(); lower_to_ccr(); diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index 6f43c2cb..33ae5600 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -46,6 +46,24 @@ fn ir_interp_deref_write(ptr: int, val: int) { else { w64(ptr, 0, val); } } +// Interpreter string values are intern-table indices (IR_CONST stores the +// index, while native ELF stores a pointer). Keep string builtins in that same +// representation instead of passing the index to pointer-based helpers. +fn ir_interp_str_concat(a: int, b: int) -> int { + out : string, mut = ""; + out = out + istr_get(a); + out = out + istr_get(b); + return str_intern(out); +} + +fn ir_interp_str_char(a: int, i: int) -> int { + return str_intern(get_char(istr_get(a), i)); +} + +fn ir_interp_str_sub(a: int, start: int, len: int) -> int { + return str_intern(str_sub(istr_get(a), start, len)); +} + fn ir_interpret() -> int { // Find main function in the dataflow graph main_idx : ., mut = -1; @@ -184,28 +202,32 @@ fn ir_interpret() -> int { } if op == 13 { // IR_LOAD_INDEX: s1=arr_var, s3=literal_idx if d >= 0 && s1 >= 0 { - arr_ptr := r64(g_ir_vals, s1 * 8); - w64(g_ir_vals, d * 8, r64(arr_ptr, s3 * 8)); + arr_value := r64(g_ir_vals, s1 * 8); + if irv_type(s1) == TI_STR { w64(g_ir_vals, d * 8, str_load8(arr_value, s3)); } + else { w64(g_ir_vals, d * 8, r64(arr_value, s3 * 8)); } } } if op == 14 { // IR_STORE_INDEX: s1=arr_var, s2=val_var, s3=literal_idx if s1 >= 0 && s2 >= 0 { - arr_ptr := r64(g_ir_vals, s1 * 8); - w64(arr_ptr, s3 * 8, r64(g_ir_vals, s2 * 8)); + arr_value := r64(g_ir_vals, s1 * 8); + if irv_type(s1) == TI_STR { store8(istr_get(arr_value), s3, r64(g_ir_vals, s2 * 8)); } + else { w64(arr_value, s3 * 8, r64(g_ir_vals, s2 * 8)); } } } if op == 15 { // IR_LOAD_INDEX_VAR: s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0 { - arr_ptr := r64(g_ir_vals, s1 * 8); + arr_value := r64(g_ir_vals, s1 * 8); idx := r64(g_ir_vals, s2 * 8); - w64(g_ir_vals, d * 8, r64(arr_ptr, idx * 8)); + if irv_type(s1) == TI_STR { w64(g_ir_vals, d * 8, str_load8(arr_value, idx)); } + else { w64(g_ir_vals, d * 8, r64(arr_value, idx * 8)); } } } if op == 16 { // IR_STORE_INDEX_VAR: d=val_var, s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0 { - arr_ptr := r64(g_ir_vals, s1 * 8); + arr_value := r64(g_ir_vals, s1 * 8); idx := r64(g_ir_vals, s2 * 8); - w64(arr_ptr, idx * 8, r64(g_ir_vals, d * 8)); + if irv_type(s1) == TI_STR { store8(istr_get(arr_value), idx, r64(g_ir_vals, d * 8)); } + else { w64(arr_value, idx * 8, r64(g_ir_vals, d * 8)); } } } // IR_MAKE_ENUM (17):d := alloc(8·(1+s2));M[d+0] := s1(tag = 变体名索引)—— @@ -247,7 +269,9 @@ fn ir_interpret() -> int { // index < 0 或 index >= max → 陷阱(返回 -1 表示中止;BC11 补实现) if op == 30 && s2 >= 0 { iv := r64(g_ir_vals, s1 * 8); - if iv < 0 || iv >= s2 { return -1; } + limit : ., mut = s2; + if ti == 1 { limit = r64(g_ir_vals, s2 * 8); } + if iv < 0 || iv >= limit { return -1; } } // IR_LAZY_THUNK / IR_LAZY_FORCE. Calls are currently eager, so both @@ -383,6 +407,34 @@ fn ir_interpret() -> int { if str_eq(fn_name, "syscall3") != 0 || str_eq(fn_name, "syscall4") != 0 { if d >= 0 { w64(g_ir_vals, d * 8, 0); } } + if str_eq(fn_name, "str_len") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, istr_len(r64(g_ir_vals, s1 * 8))); } + } + if str_eq(fn_name, "str_eq") != 0 { + if d >= 0 && s2 >= 2 { + left_s := istr_get(r64(g_ir_vals, s1 * 8)); + right_s := istr_get(r64(g_ir_vals, (s1 + 1) * 8)); + w64(g_ir_vals, d * 8, str_eq(left_s, right_s)); + } + } + if str_eq(fn_name, "concat") != 0 { + if d >= 0 && s2 >= 2 { + w64(g_ir_vals, d * 8, ir_interp_str_concat( + r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); + } + } + if str_eq(fn_name, "int_str") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))); } + } + if str_eq(fn_name, "chr") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(chr(r64(g_ir_vals, s1 * 8)))); } + } + if str_eq(fn_name, "get_char") != 0 { + if d >= 0 && s2 >= 2 { w64(g_ir_vals, d * 8, ir_interp_str_char(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); } + } + if str_eq(fn_name, "str_sub") != 0 { + if d >= 0 && s2 >= 3 { w64(g_ir_vals, d * 8, ir_interp_str_sub(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8), r64(g_ir_vals, (s1 + 2) * 8))); } + } if str_eq(fn_name, "load_str_ptr") != 0 { if d >= 0 && s2 >= 2 { b := r64(g_ir_vals, s1 * 8); p := r64(g_ir_vals, s1 + 1 * 8); @@ -406,6 +458,10 @@ fn ir_interpret() -> int { } if d >= 0 { w64(g_ir_vals, d * 8, 0); } } + if str_eq(fn_name, "str_len") != 0 || str_eq(fn_name, "str_eq") != 0 || + str_eq(fn_name, "concat") != 0 || str_eq(fn_name, "int_str") != 0 || + str_eq(fn_name, "chr") != 0 || str_eq(fn_name, "get_char") != 0 || + str_eq(fn_name, "str_sub") != 0 { ip = ip + 1; continue; } // Regular function call (single level — no recursive/nested call support) if d >= 0 { cfi : ., mut = 0; diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 447b8ffe..65ba78e4 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -57,9 +57,32 @@ fn arr_len_lit_of(arr_var: int) -> int { if ti >= 0 && ti < g_type_count && get_type_kind(ti) == TYP_ARRAY { return get_type_extra(ti); } + if ti == TI_STR { + prod := r64(g_df_var_producer, arr_var * 8); + if prod >= 0 && r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CONST && + r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_TK) == TI_STR { + return istr_len(r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1)); + } + } return slice_len_get(arr_var); } +fn emit_string_bounds(arr_var: int, idx_var: int) { + if arr_var < 0 || idx_var < 0 || irv_type(arr_var) != TI_STR { return; } + len_var := new_ir_var("str_len", TI_INT); + emit(IR_CALL, len_var, arr_var, 1, str_intern("str_len"), TI_INT); + emit(IR_BOUNDS_CHECK, -1, idx_var, len_var, 0, 1); +} + +// 字面量下标 + 字符串 → 运行时边界守卫(M-2 遗留:emit_string_bounds +// 只覆盖变量下标路径,常量下标路径与 arr_len_lit 两头落空——静默越界)。 +fn emit_string_lit_bounds(arr_var: int, idx_lit: int) { + if arr_var < 0 || irv_type(arr_var) != TI_STR { return; } + idx_var := new_ir_var("_idx_lit", TI_INT); + emit(IR_CONST, idx_var, idx_lit, 0, 0, TI_INT); + emit_string_bounds(arr_var, idx_var); +} + fn grow_sg_alloc(needed: int) { if needed < g_sg_alloc_cap { return; } nc := g_sg_alloc_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } @@ -750,15 +773,17 @@ emit(IR_STORE, -1, target, val_var, 0, 0); // F1:写路径越界守卫钩子(EXPR_BINARY OP_ASSIGN 遗留路径,同步修复) arr_len_lit : ., mut = arr_len_lit_of(arr_var); if idx_kind == EXPR_INT { + emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_STORE_INDEX, -1, arr_var, val_var, ast_int_val(idx_node), 0); } - } else { - idx_var := gen_expr(idx_node); - idx_var = force_if_thunk(idx_var); - if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { - emit(IR_STORE_INDEX_VAR, val_var, arr_var, idx_var, 0, 0); - } + } else { + idx_var := gen_expr(idx_node); + idx_var = force_if_thunk(idx_var); + emit_string_bounds(arr_var, idx_var); + if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { + emit(IR_STORE_INDEX_VAR, val_var, arr_var, idx_var, 0, 0); + } } return val_var; } @@ -1001,6 +1026,7 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); // F1:写路径越界守卫钩子(修复前完全没有——见 compcert-round4 F1) arr_len_lit : ., mut = arr_len_lit_of(arr_var); if ast_kind(idx_node) == EXPR_INT { + emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_STORE_INDEX, -1, arr_var, val_var, ast_int_val(idx_node), 0); } @@ -1800,6 +1826,16 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); is_dyn_var = 1; } var := new_ir_var(istr_get(var_ni), TI_UNIT); + declared_ti : ., mut = TI_UNIT; + if type_node >= 0 { declared_ti = res_type_node(type_node); } + // `dex` is the source-level type; its IR slot has two forms. Only an + // explicitly tagged `apx` declaration uses binary64 bits. + target_ti : ., mut = declared_ti; + if declared_ti == TI_DEX { + if is_apx != 0 { target_ti = TI_DEX; } + else { target_ti = TI_DEX_S; } + irv_set_type(var, target_ti); + } is_arr : ., mut = 0; if type_node >= 0 && val_node < 0 { if ast_kind(type_node) == 19 { @@ -1819,6 +1855,16 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if val_node >= 0 { val_var := gen_expr(val_node); val_var = force_if_thunk(val_var); + // An `apx` dex local stores binary64 bits, unlike the default + // scaled-integer dex form. The annotation alone is not enough: + // switch the slot type and convert the initializer before later + // binary operations inspect its type. + if declared_ti == TI_DEX { + val_var = dex_store_adjust(var, val_var, val_node); + } else if declared_ti == TI_UNIT { + target_ti = irv_type(val_var); + irv_set_type(var, target_ti); + } if is_dyn_var != 0 { // Dyn variable: pack value with its type tag dyn_var := new_ir_var("_dyn", TI_DYN); @@ -1831,7 +1877,9 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } // Preserve the initializer type so later operations can select // type-specific lowering (notably string + -> concat()). - irv_set_type(var, irv_type(val_var)); + if declared_ti != TI_DEX { + irv_set_type(var, irv_type(val_var)); + } // F11:切片长度沿 LET 初始化传播(s := arr[0..2] → s 带长度 2) if slice_len_get(var) >= 0 || slice_len_get(val_var) >= 0 { slice_len_set(var, slice_len_get(val_var)); @@ -1936,12 +1984,14 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } v := new_ir_var("elem", TI_INT); if idx_kind == EXPR_INT { + emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_LOAD_INDEX, v, arr_var, 0, ast_int_val(idx_node), 0); } } else { idx_var := gen_expr(idx_node); idx_var = force_if_thunk(idx_var); + emit_string_bounds(arr_var, idx_var); if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { emit(IR_LOAD_INDEX_VAR, v, arr_var, idx_var, 0, 0); } diff --git a/src/compiler/lexer.cr b/src/compiler/lexer.cr index e9edd26b..29bbf243 100644 --- a/src/compiler/lexer.cr +++ b/src/compiler/lexer.cr @@ -123,19 +123,30 @@ fn str_to_f64_bits(s: string) -> int { i : ., mut = 0; neg : int = 0; if i < sl && load8(s, i) == 45 { neg = 1; i = i + 1; } - ip : int = 0; fp : int = 0; den : int = 1; sd : int = 0; dg : ., mut = 0; + ip : int = 0; fp : int = 0; den : int = 1; sd : int = 0; + ip_digits : ., mut = 0; fp_digits : ., mut = 0; + too_wide : ., mut = 0; loop { if i >= sl { break; } c := load8(s, i); if c == 46 { sd = 1; i = i + 1; continue; } if c < 48 || c > 57 { break; } - dg = dg + 1; - if dg <= 18 { - if sd != 0 { fp = fp * 10 + (c - 48); den = den * 10; } - else { ip = ip * 10 + (c - 48); } + if sd != 0 { + fp_digits = fp_digits + 1; + if fp_digits <= 18 { fp = fp * 10 + (c - 48); den = den * 10; } + } else { + ip_digits = ip_digits + 1; + if ip_digits <= 19 { + digit_ip := c - 48; + if ip > 922337203685477580 || + (ip == 922337203685477580 && digit_ip > 7 + neg) { + too_wide = 1; + } else { ip = ip * 10 + digit_ip; } + } else { too_wide = 1; } } i = i + 1; } + if too_wide != 0 { add_error("decimal literal integer part is too wide"); return 0; } if ip == 0 && fp == 0 { - if neg != 0 { return -9223372036854775808; } + if neg != 0 { return (-9223372036854775807 - 1); } return 0; } // 小数 64 位(长除:r=fp,每轮 r×2 vs den) @@ -155,14 +166,20 @@ fn str_to_f64_bits(s: string) -> int { exp : int = 0; if bi > 0 { sh := 53 - bi; - mant = (ip * pow2i(sh)) + (frac64 / pow2i(64 - sh)); + if sh >= 0 { + mant = (ip * pow2i(sh)) + (frac64 / pow2i(64 - sh)); + } else { + // Avoid pow2i of a negative value for integer parts wider than + // the binary64 significand. + mant = ip / pow2i(0 - sh); + } exp = 1023 + (bi - 1); } else { // 纯小数:frac64 的最高位 bf : ., mut = 0; t2 = frac64; loop { if t2 == 0 { break; } t2 = t2 / 2; bf = bf + 1; } if bf == 0 { - if neg != 0 { return -9223372036854775808; } + if neg != 0 { return (-9223372036854775807 - 1); } return 0; } sh := 53 - bf; @@ -175,10 +192,57 @@ fn str_to_f64_bits(s: string) -> int { if exp > 0 && exp < 2047 { bits64 = (mant % 4503599627370496) + exp * 4503599627370496; } - if neg != 0 { bits64 = bits64 + -9223372036854775808; } + if neg != 0 { bits64 = bits64 + (-9223372036854775807 - 1); } return bits64; } +// Parse an integer literal after tokenization. Supports decimal separators +// and 0x/0o/0b prefixes; overflow is reported as a lexer error. +fn str_int_literal(s: string) -> int { + sl := str_len(s); + base : ., mut = 10; + start : ., mut = 0; + if sl >= 2 && load8(s, 0) == 48 { + pfx := load8(s, 1); + if pfx == 120 || pfx == 88 { base = 16; start = 2; } + else if pfx == 111 || pfx == 79 { base = 8; start = 2; } + else if pfx == 98 || pfx == 66 { base = 2; start = 2; } + } + value : ., mut = 0; + digits : ., mut = 0; + i : ., mut = start; + loop { + if i >= sl { break; } + c := load8(s, i); + if c == 95 { i = i + 1; continue; } + d : ., mut = -1; + if c >= 48 && c <= 57 { d = c - 48; } + else if c >= 65 && c <= 70 { d = c - 55; } + else if c >= 97 && c <= 102 { d = c - 87; } + if d < 0 || d >= base { add_error("invalid integer literal"); return 0; } + // 溢出守卫按 base 校准:固定 i64max/10 阈值只对十进制有效——base 16 + // 累积值在最后一次 ×16 直接环绕(0x8000000000000000 → i64min 绕开守卫)。 + // 阈值必须 = i64max/base(base 10 的末位 d>7 边界保持)。 + if base == 10 { + if value > 922337203685477580 || + (value == 922337203685477580 && d > 7) { + add_error("integer literal overflow"); return 0; + } + } else if base == 16 { + if value > 576460752303423487 { add_error("integer literal overflow"); return 0; } + } else if base == 8 { + if value > 1152921504606846975 { add_error("integer literal overflow"); return 0; } + } else { + if value > 4611686018427387903 { add_error("integer literal overflow"); return 0; } + } + value = value * base + d; + digits = digits + 1; + i = i + 1; + } + if digits == 0 { add_error("integer literal requires digits"); return 0; } + return value; +} + // decimal string → 定点缩放整数(数值迁移 Task 4:dex 精确字面量解析) // S = 10^6(6 位小数)。整数部分 × S + 小数 6 位;第 7 位小数起四舍五入(半进, // 与 float_str_bits 的舍入规则一致)。负号直接处理('-' 前缀)。 @@ -293,7 +357,9 @@ fn skip_ws(src: string, pos: int, max_len: int) -> int { fn tokenize(_src: string) { g_token_count = 0; - g_error_count = 0; + // 注意:不再清零 g_error_count——res_imports 会重 tokenize(拼接 import + // 前后两轮),清零会把首轮词法守卫错误(整数字面量溢出等)吞掉。错误 + // 清理由 run_frontend 会话起点负责(错误累积跨多轮 tokenize)。 _pos : ., mut = 0; g_pos = 0; g_line = 1; @@ -355,9 +421,13 @@ fn tokenize(_src: string) { // Hex/octal/binary prefix if c == 48 && _pos - start == 1 { nx := cur_char_at(_src, _pos, _slen); - if nx == 120 || nx == 88 { _pos = _pos + 1; loop { hc := cur_char_at(_src, _pos, _slen); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) { _pos = _pos + 1; } else { break; } } } - else if nx == 111 || nx == 79 { _pos = _pos + 1; loop { oc := cur_char_at(_src, _pos, _slen); if oc >= 48 && oc <= 55 { _pos = _pos + 1; } else { break; } } } - else if nx == 98 || nx == 66 { _pos = _pos + 1; loop { bc := cur_char_at(_src, _pos, _slen); if bc == 48 || bc == 49 { _pos = _pos + 1; } else { break; } } } + if nx == 120 || nx == 88 { _pos = _pos + 1; loop { hc := cur_char_at(_src, _pos, _slen); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) || hc == 95 { _pos = _pos + 1; } else { break; } } } + else if nx == 111 || nx == 79 { _pos = _pos + 1; loop { oc := cur_char_at(_src, _pos, _slen); if (oc >= 48 && oc <= 55) || oc == 95 { _pos = _pos + 1; } else { break; } } } + else if nx == 98 || nx == 66 { _pos = _pos + 1; loop { bc := cur_char_at(_src, _pos, _slen); if bc == 48 || bc == 49 || bc == 95 { _pos = _pos + 1; } else { break; } } } + bad_prefix_digit : ., mut = 0; + badc := cur_char_at(_src, _pos, _slen); + if is_ident_char(badc) != 0 { bad_prefix_digit = 1; } + if bad_prefix_digit != 0 { add_error("invalid digit in integer literal"); } } // Float: only consume the '.' when it does not start a '..' // range operator (otherwise `0..4` lexes as `0.` `.4` and the @@ -386,7 +456,7 @@ fn tokenize(_src: string) { if has_dot != 0 || suffix == "f32" || suffix == "f64" { add_tok_int_lex(T_DEX, str_to_scaled(num_str), str_intern(num_str), start_line, start_col); } else { - ival : ., mut = str_int(num_str); + ival : ., mut = str_int_literal(num_str); if suffix == "u8" || suffix == "u16" || suffix == "u32" || suffix == "u64" { } else if suffix == "i8" || suffix == "i16" || suffix == "i32" || suffix == "i64" { } else if str_len(suffix) > 0 { } diff --git a/src/compiler/main.cr b/src/compiler/main.cr index ae87b021..f35015aa 100644 --- a/src/compiler/main.cr +++ b/src/compiler/main.cr @@ -124,6 +124,7 @@ fn read_static_runtime_source() -> string { // Run the shared frontend pipeline: tokenize → resolve → parse → check // Returns 0 on success, 1 on error. fn run_frontend() -> int { + g_error_count = 0; // 会话起点清词法/语法错误(tokenize 多轮累积,见 lexer.cr) println("[1/5] tokenize..."); tokenize(g_source); println("[2/5] resolve imports..."); diff --git a/src/compiler/parser.cr b/src/compiler/parser.cr index 04d557eb..980f74db 100644 --- a/src/compiler/parser.cr +++ b/src/compiler/parser.cr @@ -1708,7 +1708,10 @@ fn parse_all() { g_loop_depth = 0; g_loop_stack_cap = 0; g_extra_let_count = 0; g_block_stmt_count = 0; - g_error_count = 0; + // 注意:不再清零 g_error_count——tokenize 阶段(词法守卫,如整数字面量 + // 溢出)已用 add_error 累积错误;parse_all 清零会把它们一并吞掉, + // 造成「静默越界/静默环绕」类错误永远不报(run_frontend 在 parse_all + // 之后才检查 g_error_count)。parse 自身的语法错误继续 add_error 追加。 g_mod_path_count = 0; g_mod_path_cap = 0; g_iface_count = 0; g_iface_cap = 0; g_impl_for_count = 0; g_impl_for_cap = 0; diff --git a/src/compiler/region_check.cr b/src/compiler/region_check.cr index 3a49f4c1..4329bd49 100644 --- a/src/compiler/region_check.cr +++ b/src/compiler/region_check.cr @@ -59,6 +59,8 @@ fn rc_pts_has_escaped(pts: int, ni: int, nstart: int) -> int { fn rc_return_escape(ni: int, s1: int, nstart: int) { // Siebert: check if a returned pointer escapes its region if s1 < 0 { return; } + ptr_ti := irv_type(s1); + if ptr_ti < 0 || get_type_kind(ptr_ti) != TYP_PTR { return; } pts := r64(g_pts, s1 * 8); if pts == 0 { return; } cur_sg := subgraph_containing(ni); @@ -93,6 +95,8 @@ fn rc_store_escape(ni: int, ptr_var: int, val_var: int) { // Siebert: check if storing a pointer creates a dangling reference // *ptr = val — val's target allocations must have lifetime >= ptr's if val_var < 0 { return; } + val_ti := irv_type(val_var); + if val_ti < 0 || get_type_kind(val_ti) != TYP_PTR { return; } val_pts := r64(g_pts, val_var * 8); if val_pts == 0 { return; } rc_pts_has_escaped(val_pts, ni, 0); // simplified check @@ -109,6 +113,12 @@ fn region_check_func(nstart: int, ncount: int) { if is_in_unsafe(ni) != 0 { ni = ni + 1; continue; } if op == IR_DEREF && d >= 0 && s1 >= 0 { + // Only pointer-typed values carry provenance. An integer loaded + // through a pointer is ordinary data, not another pointer. + ptr_ti := irv_type(s1); + if ptr_ti < 0 || get_type_kind(ptr_ti) != TYP_PTR { + ni = ni + 1; continue; + } pts := r64(g_pts, s1 * 8); if pts != 0 { mask : int, mut = 1; diff --git a/src/lsp/analysis.cr b/src/lsp/analysis.cr index d2de99e8..108cd061 100644 --- a/src/lsp/analysis.cr +++ b/src/lsp/analysis.cr @@ -139,11 +139,14 @@ fn analysis_type_node_name(node: int, depth: int) -> string { // ── 令牌定位 ── -// 扫 g_tokens 找覆盖 (line, col)(0-based)的令牌,返回令牌索引;无则 -1。 -// 匹配:令牌所在行 == line,且 col ∈ [令牌起点, 起点 + lexeme 长度)。 +// 扫 g_tokens 找覆盖 (line, col)(0-based,col 为 UTF-16 code units)的令牌, +// 返回令牌索引;无则 -1。源码 lexer 的列按字节记录,定位时先转换回字节。 fn analysis_token_at(line: int, col: int) -> int { + if line < 0 || col < 0 { return -1; } tl : ., mut = line + 1; - tc : ., mut = col + 1; + line_start := analysis_line_start(line); + cursor := analysis_pos_of(line, col); + tc : ., mut = cursor - line_start + 1; i : ., mut = 0; loop { if i >= g_token_count { return -1; } @@ -159,12 +162,80 @@ fn analysis_token_at(line: int, col: int) -> int { } } +// Return the byte offset of a zero-based source line. +fn analysis_line_start(line: int) -> int { + if line <= 0 { return 0; } + sl := str_len(g_source); + current : ., mut = 0; + i : ., mut = 0; + loop { + if i >= sl { return sl; } + if load8(g_source, i) == 10 { + current = current + 1; + i = i + 1; + if current >= line { return i; } + } else { i = i + 1; } + } + return sl; +} + +// Decode one UTF-8 sequence from g_source. Invalid bytes occupy one unit. +fn analysis_utf8_next(pos: int, sl: int) -> int { + if pos < 0 || pos >= sl { return 1; } + b0 := load8(g_source, pos); + if b0 < 128 { return 1; } + if b0 < 224 && pos + 1 < sl { + b1 := load8(g_source, pos + 1); + if b1 >= 128 && b1 < 192 { return 2; } + } + if b0 < 240 && pos + 2 < sl { + b1 := load8(g_source, pos + 1); + b2 := load8(g_source, pos + 2); + if b1 >= 128 && b1 < 192 && b2 >= 128 && b2 < 192 { return 3; } + } + if b0 >= 240 && b0 < 248 && pos + 3 < sl { + b1 := load8(g_source, pos + 1); + b2 := load8(g_source, pos + 2); + b3 := load8(g_source, pos + 3); + if b1 >= 128 && b1 < 192 && b2 >= 128 && b2 < 192 && b3 >= 128 && b3 < 192 { return 4; } + } + return 1; +} + +fn analysis_utf8_cp(pos: int, adv: int) -> int { + b0 := load8(g_source, pos); + if adv == 1 { return b0; } + if adv == 2 { return (b0 - 192) * 64 + load8(g_source, pos + 1) - 128; } + if adv == 3 { return (b0 - 224) * 4096 + (load8(g_source, pos + 1) - 128) * 64 + load8(g_source, pos + 2) - 128; } + return (b0 - 240) * 262144 + (load8(g_source, pos + 1) - 128) * 4096 + (load8(g_source, pos + 2) - 128) * 64 + load8(g_source, pos + 3) - 128; +} + +fn analysis_utf16_units(start: int, end: int) -> int { + sl := str_len(g_source); + p : ., mut = start; + units : ., mut = 0; + loop { + if p >= end || p >= sl { break; } + adv := analysis_utf8_next(p, sl); + if p + adv > end { adv = 1; } + cp := analysis_utf8_cp(p, adv); + if cp >= 65536 { units = units + 2; } else { units = units + 1; } + p = p + adv; + } + return units; +} + +fn analysis_source_byte(pos: int, sl: int) -> int { + if pos < 0 || pos >= sl { return -1; } + return load8(g_source, pos); +} + // ── 函数 hover ── // EXPR_PARAM 节点 → 参数类型名 fn analysis_param_type_name(pn: int) -> string { tn := ast_data(pn); - if tn > 0 && ast_kind(tn) != 0 { return analysis_type_node_name(tn, 0); } + if tn >= 0 && tn < g_ast_count && ast_kind(tn) != 0 { return analysis_type_node_name(tn, 0); } return analysis_tyname(ast_type_val(pn)); } @@ -173,7 +244,7 @@ fn analysis_fn_ret_name(fi: int) -> string { fn_node := fi_ast_node(fi); if fn_node >= 0 && fn_node < g_ast_count { tn := ast_type_val(fn_node); - if tn > 0 && tn < g_ast_count { + if tn >= 0 && tn < g_ast_count { if ast_kind(tn) != 0 { return analysis_type_node_name(tn, 0); } return analysis_tyname(ast_type_val(tn)); } @@ -226,7 +297,7 @@ fn analysis_hover_struct(si: int) -> string { out = out + istr_get(si_field_name(si, f)); out = out + ": "; tn := si_field_type_node(si, f); - if tn > 0 && ast_kind(tn) != 0 { out = out + analysis_type_node_name(tn, 0); } + if tn >= 0 && tn < g_ast_count && ast_kind(tn) != 0 { out = out + analysis_type_node_name(tn, 0); } else { out = out + analysis_tyname(si_field_type(si, f)); } out = out + ","; f = f + 1; @@ -259,7 +330,7 @@ fn analysis_hover_enum(ei: int) -> string { // 中最近的一个(近似作用域)。无则 -1。 fn analysis_var_decl(name_ni: int, line: int, col: int) -> int { tl : ., mut = line + 1; - tc : ., mut = col + 1; + tc := analysis_pos_of(line, col) - analysis_line_start(line) + 1; best : ., mut = -1; bl : ., mut = -1; bc : ., mut = -1; @@ -295,7 +366,7 @@ fn analysis_var_type(vd: int, depth: int) -> string { if k == EXPR_FOR { return "int"; } if k == EXPR_LET { tn := ast_b(vd); - if tn > 0 { + if tn >= 0 && tn < g_ast_count { if ast_kind(tn) != 0 { return analysis_type_node_name(tn, 0); } return analysis_tyname(ast_type_val(tn)); } @@ -424,7 +495,9 @@ fn analysis_line_offset(ln1: int) -> int { fn analysis_def_json(ln1: int, cl1: int) -> string { off := analysis_line_offset(ln1); line0 : ., mut = ln1 - 1 - off; - col0 : ., mut = cl1 - 1; + line_start := analysis_line_start(ln1 - 1); + decl_pos := line_start + cl1 - 1; + col0 := analysis_utf16_units(line_start, decl_pos); uri : string, mut = "file://"; uri = uri + analysis_path_for_line(ln1); out : string, mut = "{\"uri\":"; @@ -436,7 +509,8 @@ fn analysis_def_json(ln1: int, cl1: int) -> string { out = out + "},\"end\":{\"line\":"; out = out + int_str(line0); out = out + ",\"character\":"; - out = out + int_str(col0 + 1); + adv := analysis_utf8_next(decl_pos, str_len(g_source)); + out = out + int_str(analysis_utf16_units(line_start, decl_pos + adv)); out = out + "}}}"; return out; } @@ -453,9 +527,10 @@ fn analysis_definition(line: int, col: int) -> string { // 函数 fi := find_func(name_ni); if fi >= 0 { - fn_node := fi_ast_node(fi); - if fn_node >= 0 && fn_node < g_ast_count { - return analysis_def_json(ast_line(fn_node), ast_col(fn_node)); + dfn := analysis_decl_tok(T_FN, name_ni); + if dfn >= 0 { + return analysis_def_json(r64(g_tokens, dfn * ESZ_TOKEN + OFF_TK_LINE), + r64(g_tokens, dfn * ESZ_TOKEN + OFF_TK_COL)); } return ""; } @@ -489,20 +564,26 @@ fn analysis_definition(line: int, col: int) -> string { // 注意:前缀直接回扫快照 g_source(不依赖令牌——半截标识符不成令牌); // 与 hover/definition 同为「最后一次成功检查的快照」语义。 -// (line, col)(0-based)→ g_source 字节偏移(跳过 line 个换行再前进 col; -// 越界 col 截断到源末尾——回扫前缀时防越界读) +// (line, col)(0-based,col 为 UTF-16 code units)→ g_source 字节偏移。 +// 光标落在 surrogate pair 中间时钳到该 code point 起点。 fn analysis_pos_of(line: int, col: int) -> int { sl := str_len(g_source); - ln : ., mut = 0; - i : ., mut = 0; + if line < 0 { return 0; } + start := analysis_line_start(line); + p : ., mut = start; + units : ., mut = 0; + want : ., mut = col; + if want < 0 { want = 0; } loop { - if ln >= line { break; } - if i >= sl { break; } - if load8(g_source, i) == 10 { ln = ln + 1; } - i = i + 1; + if p >= sl || load8(g_source, p) == 10 || units >= want { break; } + adv := analysis_utf8_next(p, sl); + cp := analysis_utf8_cp(p, adv); + next_units : ., mut = 1; + if cp >= 65536 { next_units = 2; } + if units + next_units > want { break; } + p = p + adv; + units = units + next_units; } - p : ., mut = i + col; - if p > sl { p = sl; } return p; } @@ -717,14 +798,19 @@ fn analysis_completion(line: int, col: int) -> string { // 1-based (line,col) → LSP range JSON(0-based 减 1,起点到名字后一字符) fn analysis_range_json(ln1: int, cl1: int) -> string { + line_start := analysis_line_start(ln1 - 1); + start_pos := line_start + cl1 - 1; + start_col := analysis_utf16_units(line_start, start_pos); + adv := analysis_utf8_next(start_pos, str_len(g_source)); + end_col := analysis_utf16_units(line_start, start_pos + adv); out : string, mut = "{\"start\":{\"line\":"; out = out + int_str(ln1 - 1); out = out + ",\"character\":"; - out = out + int_str(cl1 - 1); + out = out + int_str(start_col); out = out + "},\"end\":{\"line\":"; out = out + int_str(ln1 - 1); out = out + ",\"character\":"; - out = out + int_str(cl1); + out = out + int_str(end_col); out = out + "}}"; return out; } @@ -861,7 +947,7 @@ fn analysis_document_symbol() -> string { // T_IMPORT T_AS T_GO T_AWAIT T_FLOW T_YIELD T_UNSAFE // T_INTERFACE T_AUTO T_FILEID T_NONE T_SOME T_EXTERN // type (1): T_UNIT(unit 关键字令牌——parser 产 TY_UNIT) -// T_INT_I8..T_FLOAT_F64 T_INT_TYPE..T_AUTO_TYPE +// T_INT_TYPE..T_AUTO_TYPE // T_REF T_DYN;以及 T_IDENT 且(内置类型名 int/float/ // bool/string/char/never/dyn——parser.cr parse_type // T_IDENT 分支字面量清单,唯一真源——或 find_struct_by_ @@ -892,8 +978,8 @@ fn analysis_kind_type(k: int) -> int { k == T_FLOW || k == T_YIELD || k == T_UNSAFE || k == T_INTERFACE || k == T_AUTO || k == T_FILEID || k == T_NONE || k == T_SOME || k == T_EXTERN { return 0; } - if k == T_UNIT || (k >= T_INT_I8 && k <= T_FLOAT_F64) || - (k >= T_INT_TYPE && k <= T_AUTO_TYPE) || k == T_REF || k == T_DYN { + if k == T_UNIT || (k >= T_INT_TYPE && k <= T_AUTO_TYPE) || + k == T_REF || k == T_DYN { return 1; } if k == T_STRING || k == T_CHAR { return 5; } @@ -928,21 +1014,25 @@ fn analysis_token_type(ti: int) -> int { fn analysis_tok_span(ti: int, st: int, sl: int) -> int { k := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND); // 字符串/字符:开引号 → 未转义闭合引号(镜像 lexer:\x 转义 4 字节、 - // 插值 ${...} 跳至 '}' 并多吃一字节、换行终止未闭合串) + // 插值 ${...} 跳至 '}' 并多吃一字节、换行终止未闭合串)。未闭合 + // 字符串末尾的反斜杠只能覆盖到源末,不能把 span 推出源缓冲区。 if k == T_STRING || k == T_CHAR { - q := load8(g_source, st); + q := analysis_source_byte(st, sl); p : ., mut = st + 1; loop { if p >= sl { break; } - c := load8(g_source, p); - if c == 92 { p = p + 2; } + c := analysis_source_byte(p, sl); + if c == 92 { + if p + 1 >= sl { p = sl; break; } + p = p + 2; + } else if c == q { p = p + 1; break; } else if c == 10 { break; } - else if c == 36 && load8(g_source, p + 1) == 123 { + else if c == 36 && analysis_source_byte(p + 1, sl) == 123 { p = p + 2; loop { if p >= sl { break; } - if load8(g_source, p) == 125 { p = p + 1; break; } + if analysis_source_byte(p, sl) == 125 { p = p + 1; break; } p = p + 1; } if p < sl { p = p + 1; } // 镜像 lexer 循环尾 _pos+1 @@ -954,26 +1044,26 @@ fn analysis_tok_span(ti: int, st: int, sl: int) -> int { // 数字:镜像 lexer 数字分支('.' 起始、hex/oct/bin 前缀、小数、字母后缀) if k == T_INT || k == T_DEX { p : ., mut = st; - if load8(g_source, p) == 46 && is_digit(load8(g_source, p + 1)) != 0 { p = p + 1; } - loop { if is_digit(load8(g_source, p)) != 0 { p = p + 1; } else { break; } } + if analysis_source_byte(p, sl) == 46 && is_digit(analysis_source_byte(p + 1, sl)) != 0 { p = p + 1; } + loop { if is_digit(analysis_source_byte(p, sl)) != 0 { p = p + 1; } else { break; } } if p - st == 1 && load8(g_source, st) == 48 { - nx := load8(g_source, p); + nx := analysis_source_byte(p, sl); if nx == 120 || nx == 88 { p = p + 1; - loop { hc := load8(g_source, p); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) { p = p + 1; } else { break; } } + loop { hc := analysis_source_byte(p, sl); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) { p = p + 1; } else { break; } } } else if nx == 111 || nx == 79 { p = p + 1; - loop { oc := load8(g_source, p); if oc >= 48 && oc <= 55 { p = p + 1; } else { break; } } + loop { oc := analysis_source_byte(p, sl); if oc >= 48 && oc <= 55 { p = p + 1; } else { break; } } } else if nx == 98 || nx == 66 { p = p + 1; - loop { bc := load8(g_source, p); if bc == 48 || bc == 49 { p = p + 1; } else { break; } } + loop { bc := analysis_source_byte(p, sl); if bc == 48 || bc == 49 { p = p + 1; } else { break; } } } } - if load8(g_source, p) == 46 && load8(g_source, p + 1) != 46 { + if analysis_source_byte(p, sl) == 46 && analysis_source_byte(p + 1, sl) != 46 { p = p + 1; - loop { if is_digit(load8(g_source, p)) != 0 { p = p + 1; } else { break; } } + loop { if is_digit(analysis_source_byte(p, sl)) != 0 { p = p + 1; } else { break; } } } - loop { if is_alpha(load8(g_source, p)) != 0 { p = p + 1; } else { break; } } + loop { if is_alpha(analysis_source_byte(p, sl)) != 0 { p = p + 1; } else { break; } } return p - st; } // 标识符/关键字/类型关键字:lexeme 即源文本(add_tok_str) @@ -1039,7 +1129,8 @@ fn analysis_semantic_tokens() -> string { st := r64(offs, j * 8); if st < main_limit { line0 : ., mut = r64(g_tokens, j * ESZ_TOKEN + OFF_TK_LINE) - 1; - col0 : ., mut = r64(g_tokens, j * ESZ_TOKEN + OFF_TK_COL) - 1; + token_line_start := analysis_line_start(line0); + col0 := analysis_utf16_units(token_line_start, st); if first == 0 { out = out + ","; } if line0 > prev_line { // 跨行令牌:deltaStartChar 为绝对列(LSP 差分编码规范—— @@ -1056,7 +1147,7 @@ fn analysis_semantic_tokens() -> string { out = out + int_str(col0 - prev_col); } out = out + ","; - out = out + int_str(analysis_tok_span(j, st, sl)); + out = out + int_str(analysis_utf16_units(st, st + analysis_tok_span(j, st, sl))); out = out + ","; out = out + int_str(analysis_token_type(j)); out = out + ",0"; diff --git a/src/lsp/json.cr b/src/lsp/json.cr index 0cc06fd4..b5c67e0a 100644 --- a/src/lsp/json.cr +++ b/src/lsp/json.cr @@ -22,9 +22,8 @@ // // g_json_strs:字符串池(原始字节,UTF-8 直通存储;独立长度计数管理)。 // -// \uXXXX:解析只得到码点(编码为 UTF-8 存入池);UTF-16 代理对不做合并—— -// "😀" 两个码点各自 3 字节编码存储,stringify 时单独解码回两个 -// \uXXXX,往返一致。TODO:将来做代理对合并与键规范化。 +// \uXXXX:解析为 Unicode 码点并合并 UTF-16 代理对;stringify 对非 ASCII +// 统一使用大写 \uXXXX(非 BMP 输出为一对代理项)。 // ── 常量(全局初始化必须是字面量——ELF 后端限制)── J_NULL : int = 0; J_BOOL : int = 1; J_NUM : int = 2; @@ -114,10 +113,14 @@ fn json_new(kind: int) -> int { } fn json_set(node: int, slot: int, val: int) { + if node < 0 || node % J_NODE_SLOTS != 0 || node / J_NODE_SLOTS >= g_json_count || + slot < 0 || slot >= J_NODE_SLOTS { return; } _jw64(g_json_nodes, (node + slot) * 8, val); } fn json_get(node: int, slot: int) -> int { + if node < 0 || node % J_NODE_SLOTS != 0 || node / J_NODE_SLOTS >= g_json_count || + slot < 0 || slot >= J_NODE_SLOTS { return -1; } return _jr64(g_json_nodes, (node + slot) * 8); } @@ -126,6 +129,8 @@ fn json_get(node: int, slot: int) -> int { g_json_parse_pos : int, mut; g_json_parse_text : string, mut; g_json_parse_failed : int, mut; +g_json_parse_depth : int, mut; +J_MAX_PARSE_DEPTH : int = 128; fn _jp_len() -> int { return str_len(g_json_parse_text); } @@ -177,21 +182,35 @@ fn _jnum() -> int { else { break; } } if g_json_parse_pos == dstart { g_json_parse_failed = 1; return 0; } + if g_json_parse_pos - dstart > 1 && load8(g_json_parse_text, dstart) == 48 { + g_json_parse_failed = 1; return 0; + } v : ., mut = 0; i : ., mut = dstart; loop { if i >= g_json_parse_pos { break; } - if v > 922337203685477580 { g_json_parse_failed = 1; return 0; } - v = v * 10 + (load8(g_json_parse_text, i) - 48); + digit := load8(g_json_parse_text, i) - 48; + // Accumulate negative values negatively so INT64_MIN is representable. + if neg != 0 { + if v < -922337203685477580 || + (v == -922337203685477580 && digit > 8) { + g_json_parse_failed = 1; return 0; + } + v = v * 10 - digit; + } else { + if v > 922337203685477580 || + (v == 922337203685477580 && digit > 7) { + g_json_parse_failed = 1; return 0; + } + v = v * 10 + digit; + } i = i + 1; } - if v < 0 { g_json_parse_failed = 1; return 0; } // 乘法回绕 = 溢出 // 尾随 '.' / 'e' / 'E':v1 不支持浮点 → 失败(不静默截断) if g_json_parse_pos < _jp_len() { c2 := load8(g_json_parse_text, g_json_parse_pos); if c2 == 46 || c2 == 101 || c2 == 69 { g_json_parse_failed = 1; return 0; } } - if neg != 0 { v = -v; } return v; } @@ -212,18 +231,36 @@ fn _jstr_byte(b: int) { g_json_strs_len = g_json_strs_len + 1; } -// 码点 → UTF-8 编码写入池(\uXXXX 只解析为码点,代理对不做合并——TODO) +// 码点 → UTF-8 编码写入池 fn _jstr_cp(cp: int) { if cp < 128 { _jstr_byte(cp); } else if cp < 2048 { _jstr_byte(192 + cp / 64); _jstr_byte(128 + cp % 64); - } else { + } else if cp < 65536 { _jstr_byte(224 + cp / 4096); _jstr_byte(128 + (cp / 64) % 64); _jstr_byte(128 + cp % 64); + } else if cp <= 1114111 { + _jstr_byte(240 + cp / 262144); + _jstr_byte(128 + (cp / 4096) % 64); + _jstr_byte(128 + (cp / 64) % 64); + _jstr_byte(128 + cp % 64); + } else { g_json_parse_failed = 1; } +} + +fn _jparse_hex4(pos: int) -> int { + cp : ., mut = 0; + i : ., mut = 0; + loop { + if i >= 4 { break; } + hv := _jhex_val(load8(g_json_parse_text, pos + i)); + if hv < 0 { g_json_parse_failed = 1; return -1; } + cp = cp * 16 + hv; + i = i + 1; } + return cp; } // 解析 JSON 字符串(当前位置必须是 '"'),返回 J_STR 节点;失败返回 -1 @@ -249,21 +286,32 @@ fn _jparse_str() -> int { g_json_parse_pos = g_json_parse_pos + 1; if e == 34 { _jstr_byte(34); } else if e == 92 { _jstr_byte(92); } + else if e == 47 { _jstr_byte(47); } + else if e == 98 { _jstr_byte(8); } + else if e == 102 { _jstr_byte(12); } else if e == 110 { _jstr_byte(10); } else if e == 114 { _jstr_byte(13); } else if e == 116 { _jstr_byte(9); } else if e == 117 { // 'u' if g_json_parse_pos + 4 > _jp_len() { g_json_parse_failed = 1; return -1; } - cp : ., mut = 0; - i2 : ., mut = 0; - loop { - if i2 >= 4 { break; } - hv := _jhex_val(load8(g_json_parse_text, g_json_parse_pos + i2)); - if hv < 0 { g_json_parse_failed = 1; return -1; } - cp = cp * 16 + hv; - i2 = i2 + 1; - } + cp := _jparse_hex4(g_json_parse_pos); + if cp < 0 { return -1; } g_json_parse_pos = g_json_parse_pos + 4; + if cp >= 55296 && cp <= 56319 { + // A high surrogate must be immediately followed by a low + // surrogate escape; store the resulting scalar as UTF-8. + if g_json_parse_pos + 6 > _jp_len() || + load8(g_json_parse_text, g_json_parse_pos) != 92 || + load8(g_json_parse_text, g_json_parse_pos + 1) != 117 { + g_json_parse_failed = 1; return -1; + } + low := _jparse_hex4(g_json_parse_pos + 2); + if low < 56320 || low > 57343 { g_json_parse_failed = 1; return -1; } + g_json_parse_pos = g_json_parse_pos + 6; + cp = 65536 + (cp - 55296) * 1024 + (low - 56320); + } else if cp >= 56320 && cp <= 57343 { + g_json_parse_failed = 1; return -1; + } _jstr_cp(cp); } else { g_json_parse_failed = 1; return -1; } // 未知转义 @@ -278,7 +326,8 @@ fn _jparse_str() -> int { // ── 值解析(递归下降)── -fn _jparse_obj() -> int { +fn _jparse_obj(depth: int) -> int { + if depth > J_MAX_PARSE_DEPTH { g_json_parse_failed = 1; return -1; } g_json_parse_pos = g_json_parse_pos + 1; // 消费 '{' tbl : ., mut = -1; // 成员表偏移(惰性:首成员时分配) count : ., mut = 0; @@ -298,7 +347,7 @@ fn _jparse_obj() -> int { _jskip_ws(); if _jp_peek() != 58 { g_json_parse_failed = 1; return -1; } // ':' g_json_parse_pos = g_json_parse_pos + 1; - val := parse_value(); + val := parse_value(depth + 1); if val < 0 { return -1; } // 表 = 可增长列表:新成员时把已有成员迁移到当前末尾(子容器表在下方, // 不受影响),再写入新成员——保证成员表连续且不与子容器表重叠。 @@ -331,7 +380,8 @@ fn _jparse_obj() -> int { return n; } -fn _jparse_arr() -> int { +fn _jparse_arr(depth: int) -> int { + if depth > J_MAX_PARSE_DEPTH { g_json_parse_failed = 1; return -1; } g_json_parse_pos = g_json_parse_pos + 1; // 消费 '[' tbl : ., mut = -1; // 元素表偏移(惰性:首元素时分配) count : ., mut = 0; @@ -346,7 +396,7 @@ fn _jparse_arr() -> int { g_json_parse_pos = g_json_parse_pos + 1; _jskip_ws(); } - v := parse_value(); + v := parse_value(depth + 1); if v < 0 { return -1; } if count == 0 { tbl = g_json_tables_len; @@ -375,12 +425,13 @@ fn _jparse_arr() -> int { return n; } -fn parse_value() -> int { +fn parse_value(depth: int) -> int { + if depth > J_MAX_PARSE_DEPTH { g_json_parse_failed = 1; return -1; } _jskip_ws(); c := _jp_peek(); if c < 0 { return -1; } - if c == 123 { return _jparse_obj(); } - if c == 91 { return _jparse_arr(); } + if c == 123 { return _jparse_obj(depth); } + if c == 91 { return _jparse_arr(depth); } if c == 34 { return _jparse_str(); } if c == 116 { // 't' → true if _jk_match("true") != 0 { n1 := json_new(J_BOOL); json_set(n1, 1, 1); return n1; } @@ -413,9 +464,10 @@ fn json_parse(text: string) -> int { g_json_strs_len = 0; g_json_tables_len = 0; g_json_parse_failed = 0; + g_json_parse_depth = 0; g_json_parse_text = text; g_json_parse_pos = 0; - idx := parse_value(); + idx := parse_value(0); if g_json_parse_failed != 0 { return -1; } if idx < 0 { return -1; } _jskip_ws(); @@ -560,7 +612,8 @@ fn _jstringify_rec(idx: int) -> string { // 节点 → JSON 文本;非法索引返回空串 fn json_stringify(idx: int) -> string { - if idx < 0 || g_json_count == 0 { return ""; } + if idx < 0 || idx % J_NODE_SLOTS != 0 || idx / J_NODE_SLOTS >= g_json_count || + g_json_count == 0 { return ""; } return _jstringify_rec(idx); } @@ -568,10 +621,12 @@ fn json_stringify(idx: int) -> string { // 对象按键查找:线性扫描成员表;找不到返回 -1 fn json_obj_get(obj: int, key: string) -> int { - if json_get(obj, 0) != J_OBJ { return -1; } + if obj < 0 || obj % J_NODE_SLOTS != 0 || obj / J_NODE_SLOTS >= g_json_count || + json_get(obj, 0) != J_OBJ { return -1; } tbl := json_get(obj, 1); cnt := json_get(obj, 2); kl := str_len(key); + found_value : ., mut = -1; i : ., mut = 0; loop { if i >= cnt { break; } @@ -585,16 +640,17 @@ fn json_obj_get(obj: int, key: string) -> int { if load8(g_json_strs, koff + j) != load8(key, j) { matched = 0; break; } j = j + 1; } - if matched != 0 { return _jr64(g_json_tables, tbl + i * J_MEMBER_BYTES + 16); } + if matched != 0 { found_value = _jr64(g_json_tables, tbl + i * J_MEMBER_BYTES + 16); } } i = i + 1; } - return -1; + return found_value; } // 数组按下标取元素;越界或非数组返回 -1 fn json_array_get(arr: int, i: int) -> int { - if json_get(arr, 0) != J_ARRAY { return -1; } + if arr < 0 || arr % J_NODE_SLOTS != 0 || arr / J_NODE_SLOTS >= g_json_count || + json_get(arr, 0) != J_ARRAY { return -1; } cnt := json_get(arr, 2); if i < 0 || i >= cnt { return -1; } tbl := json_get(arr, 1); diff --git a/src/lsp/lsp.cr b/src/lsp/lsp.cr index f64d4a04..ee9b0624 100644 --- a/src/lsp/lsp.cr +++ b/src/lsp/lsp.cr @@ -21,6 +21,19 @@ // 注意(Python bootstrap 地雷):字符串累加必须从字面量初始化的 // `: string, mut` 累加器出发;本文件所有拼接均遵循该模式。 +g_lsp_snapshot_path : string, mut; + +fn lsp_request_matches_snapshot(root: int) -> int { + params := json_obj_get(root, "params"); + if params < 0 { return 0; } + td := json_obj_get(params, "textDocument"); + if td < 0 { return 0; } + un := json_obj_get(td, "uri"); + if un < 0 || json_get(un, 0) != J_STR { return 0; } + uri := lsp_str_pool_copy(json_get(un, 1), json_get(un, 2)); + return str_eq(lsp_uri_to_path(uri), g_lsp_snapshot_path); +} + // 从 JSON 字符串池复制字节段 → 稳定堆字符串。 // 池在每次 json_parse 会被重置复用,直接存池内指针会在下一帧失效。 fn lsp_str_pool_copy(off: int, len: int) -> string { @@ -59,7 +72,7 @@ fn lsp_json_escape_str(s: string) -> string { else if c == 10 { out = out + "\\n"; } else if c == 13 { out = out + "\\r"; } else if c == 9 { out = out + "\\t"; } - else if c < 32 { out = out + "\\u" + _jhex4(c); } + else if c < 32 { out = out + _jhex4(c); } else { out = out + chr(c); } i = i + 1; } @@ -153,6 +166,7 @@ fn lsp_publish_diags() { // 静默检查管线(禁止 run_frontend——进度 println 污染协议通道) fn lsp_check_file(path: string, src: string) { + g_lsp_snapshot_path = path; g_source = src; g_source_dir = dirname(path); reset_frontend_state(); @@ -236,6 +250,7 @@ fn lsp_did_close(root: int) { uri := lsp_str_pool_copy(json_get(uri_node, 1), json_get(uri_node, 2)); path := lsp_uri_to_path(uri); module_open_doc_remove(path); + if str_eq(path, g_lsp_snapshot_path) != 0 { g_lsp_snapshot_path = ""; } // 清诊断:发布空数组 lsp_send_file_diags("file://" + path, -1); } @@ -276,6 +291,7 @@ fn lsp_send_def_resp(root: int, loc: string) { } fn lsp_hover(root: int) { + if lsp_request_matches_snapshot(root) == 0 { lsp_send_hover_resp(root, ""); return; } params := json_obj_get(root, "params"); pos : ., mut = -1; if params >= 0 { pos = json_obj_get(params, "position"); } @@ -291,6 +307,7 @@ fn lsp_hover(root: int) { } fn lsp_definition(root: int) { + if lsp_request_matches_snapshot(root) == 0 { lsp_send_def_resp(root, ""); return; } params := json_obj_get(root, "params"); pos : ., mut = -1; if params >= 0 { pos = json_obj_get(params, "position"); } @@ -312,6 +329,7 @@ fn lsp_definition(root: int) { // textDocument/completion:params.position(0-based)+ context(当前仅 // 忽略——触发类型不影响候选);result 为 {"items":[...]},坏参数 → 空 items。 fn lsp_completion(root: int) { + if lsp_request_matches_snapshot(root) == 0 { lsp_send_def_resp(root, "{\"items\":[]}"); return; } params := json_obj_get(root, "params"); pos : ., mut = -1; if params >= 0 { pos = json_obj_get(params, "position"); } @@ -332,6 +350,7 @@ fn lsp_completion(root: int) { // textDocument/documentSymbol:params.textDocument.uri 只需存在即可 // (符号来自快照,与 uri 具体值无关);result 为 JSON 数组。 fn lsp_document_symbol(root: int) { + if lsp_request_matches_snapshot(root) == 0 { lsp_send_def_resp(root, "[]"); return; } params := json_obj_get(root, "params"); td : ., mut = -1; if params >= 0 { td = json_obj_get(params, "textDocument"); } @@ -349,6 +368,7 @@ fn lsp_document_symbol(root: int) { // (令牌来自快照,与 uri 具体值无关——与 documentSymbol 同模式);result // 为 {"data":[...]}(差分编码见 analysis.cr)。坏参数 → 空 data。 fn lsp_semantic_tokens(root: int) { + if lsp_request_matches_snapshot(root) == 0 { lsp_send_def_resp(root, "{\"data\":[]}"); return; } params := json_obj_get(root, "params"); td : ., mut = -1; if params >= 0 { td = json_obj_get(params, "textDocument"); } diff --git a/src/lsp/rpc.cr b/src/lsp/rpc.cr index cc93826d..3579da24 100644 --- a/src/lsp/rpc.cr +++ b/src/lsp/rpc.cr @@ -28,15 +28,30 @@ g_rpc_shutdown : int, mut; // 是否已收到 shutdown g_rpc_exit_code : int, mut; // 帧循环结束时的进程退出码 g_rpc_hdr_buf : string, mut; // 头缓冲(惰性 alloc 一次) g_rpc_hdr_ok : int, mut; +g_rpc_in_buf : string, mut; // stdin 分块缓冲,避免 read(1) 系统调用风暴 +g_rpc_in_pos : int, mut; +g_rpc_in_len : int, mut; +g_rpc_in_ok : int, mut; // ── 帧读 ── -// 从 stdin(fd 0)逐字节读;EOF/错误返回 -1 +// 从 stdin(fd 0)取一个字节;底层按 4KB 分块读取,EOF/错误返回 -1。 fn rpc_read_byte() -> int { - b := alloc(1); - n := syscall3(0, 0, b, 1); - if n <= 0 { return -1; } - return load8(b, 0); + if g_rpc_in_ok == 0 { + g_rpc_in_buf = alloc(4096); + g_rpc_in_pos = 0; + g_rpc_in_len = 0; + g_rpc_in_ok = 1; + } + if g_rpc_in_pos >= g_rpc_in_len { + n := syscall3(0, 0, g_rpc_in_buf, 4096); + if n <= 0 { return -1; } + g_rpc_in_pos = 0; + g_rpc_in_len = n; + } + c := load8(g_rpc_in_buf, g_rpc_in_pos); + g_rpc_in_pos = g_rpc_in_pos + 1; + return c; } // 读头直到 "\r\n\r\n",解析 Content-Length;EOF/坏头返回 -1 @@ -70,6 +85,7 @@ fn rpc_read_header() -> int { // buf[pos..] 与 key 大小写不敏感比较(键后必须为 ':' 或空白) fn rpc_key_at(buf: string, pos: int, key: string) -> int { kl := str_len(key); + if pos < 0 { return 0; } i : ., mut = 0; loop { if i >= kl { break; } @@ -88,32 +104,37 @@ fn rpc_key_at(buf: string, pos: int, key: string) -> int { // 从头缓冲解析 Content-Length;找不到/非法/超上限返回 -1 fn rpc_find_len(buf: string, blen: int) -> int { i : ., mut = 0; + found : ., mut = 0; + found_len : ., mut = -1; loop { - if i + 15 > blen { return -1; } // 至少 14 个键字节 + 1 个边界字节 + if i >= blen { break; } + // Header fields are matched only at line starts. This prevents a + // body-like substring in another header from being accepted. + if i + 1 < blen && load8(buf, i) == 13 && load8(buf, i + 1) == 10 { + break; + } + line_end : ., mut = i; + loop { + if line_end + 1 >= blen { return -1; } + if load8(buf, line_end) == 13 && load8(buf, line_end + 1) == 10 { break; } + line_end = line_end + 1; + } + if line_end - i < 15 { i = line_end + 2; continue; } if rpc_key_at(buf, i, "content-length") != 0 { j : ., mut = i + 14; - loop { - if j >= blen { return -1; } - c := load8(buf, j); - if c == 32 || c == 9 { j = j + 1; } - else { break; } - } - if j >= blen { return -1; } - if load8(buf, j) != 58 { return -1; } // ':' + if j >= line_end { return -1; } + // The key matcher permits whitespace before the colon. + loop { if j < line_end && (load8(buf, j) == 32 || load8(buf, j) == 9) { j = j + 1; } else { break; } } + if j >= line_end || load8(buf, j) != 58 { return -1; } j = j + 1; - // 跳过 ':' 后的空白(标准帧 "Content-Length: 58" 的冒号后有空格) - loop { - if j >= blen { return -1; } - c3 := load8(buf, j); - if c3 == 32 || c3 == 9 { j = j + 1; } - else { break; } - } + loop { if j < line_end && (load8(buf, j) == 32 || load8(buf, j) == 9) { j = j + 1; } else { break; } } v : ., mut = 0; got : ., mut = 0; loop { - if j >= blen { break; } + if j >= line_end { break; } c2 := load8(buf, j); if c2 >= 48 && c2 <= 57 { + if v > RPC_BODY_MAX / 10 || (v == RPC_BODY_MAX / 10 && c2 - 48 > RPC_BODY_MAX % 10) { return -1; } v = v * 10 + (c2 - 48); got = 1; j = j + 1; @@ -121,12 +142,15 @@ fn rpc_find_len(buf: string, blen: int) -> int { else { break; } } if got == 0 { return -1; } - if v > RPC_BODY_MAX { return -1; } - return v; + loop { if j < line_end && (load8(buf, j) == 32 || load8(buf, j) == 9) { j = j + 1; } else { break; } } + if j != line_end { return -1; } + if found != 0 { return -1; } + found = 1; + found_len = v; } - i = i + 1; + i = line_end + 2; } - return -1; // 不可达(循环内必 return),满足检查器 + return found_len; } // 读满 n 字节体;EOF 中断返回已读部分(调用方 json_parse 失败即忽略) diff --git a/src/lsp/test_json.cr b/src/lsp/test_json.cr index 69a42f62..a108ae7c 100644 --- a/src/lsp/test_json.cr +++ b/src/lsp/test_json.cr @@ -56,8 +56,12 @@ fn main() -> int { // 3. 字符串转义往返:\" \\ \n check_roundtrip("\"a\\\"b\\\\c\\nd\"", "\"a\\\"b\\\\c\\nd\""); - // \uXXXX:只解析为码点,序列化输出大写十六进制 + // \uXXXX:解析为码点,序列化输出大写十六进制 check_roundtrip("\"\\u4e2d\\u00e9\"", "\"\\u4E2D\\u00E9\""); + // UTF-16 surrogate pair becomes one Unicode scalar and round-trips in + // the canonical JSON form. + check_roundtrip("\"\\uD83D\\uDE00\"", "\"\\uD83D\\uDE00\""); + check_roundtrip("\"a\\b\\f\\/\"", "\"a\\u0008\\u000C/\""); // 4. json_obj_get:{"position": {"line": 3, "character": 7}} 取 line → 3 root := json_parse("{\"position\": {\"line\": 3, \"character\": 7}}"); @@ -82,6 +86,31 @@ fn main() -> int { "num: -42 -> J_NUM a=-42"); t_expect(str_eq(json_stringify(n), "-42") != 0, "stringify: -42"); + // JSON object keys are last-wins, and signed 64-bit boundaries are valid. + dup := json_parse("{\"x\":1,\"x\":2}"); + dx := json_obj_get(dup, "x"); + t_expect(dx >= 0 && json_get(dx, 1) == 2, "obj_get: duplicate key last wins"); + min_i64 := json_parse("-9223372036854775808"); + max_i64 := json_parse("9223372036854775807"); + t_expect(min_i64 >= 0 && json_get(min_i64, 1) == -9223372036854775808, + "num: INT64_MIN accepted"); + t_expect(max_i64 >= 0 && json_get(max_i64, 1) == 9223372036854775807, + "num: INT64_MAX accepted"); + check_bad("9223372036854775808"); + check_bad("-9223372036854775809"); + check_bad("01"); + check_bad("\"\\uD800\""); + + // Excessive nesting must fail without exhausting the native call stack. + nested : string, mut = "0"; + ni : ., mut = 0; + loop { + if ni >= 130 { break; } + nested = "[" + nested + "]"; + ni = ni + 1; + } + check_bad(nested); + // 7. json_array_get:下标访问 + 越界 a := json_parse("[10, 20]"); e0 := json_array_get(a, 0); diff --git a/src/stdlib/fmt.cr b/src/stdlib/fmt.cr index 4b0df973..023f4ab6 100644 --- a/src/stdlib/fmt.cr +++ b/src/stdlib/fmt.cr @@ -203,7 +203,12 @@ fn fpow2i(k: int) -> int { fn float_str_bits(bits: int) -> string { // 符号(bit63) neg : int = 0; u : int = bits; - if u < 0 { neg = 1; u = u - (-9223372036854775808); } // 减 -2^63 = 清 bit63(无 & 运算符) + if u < 0 { + neg = 1; + // Avoid spelling INT64_MIN: the lexer cannot represent its positive + // magnitude as an integer token before applying unary minus. + u = u - (-9223372036854775807) + 1; + } // 提取字段(除以 2^52 代替移位) exp := (u / 4503599627370496) % 2048; mant := u % 4503599627370496; diff --git a/tests/bootstrap/test_generics.py b/tests/bootstrap/test_generics.py index c5aba268..14809c7d 100644 --- a/tests/bootstrap/test_generics.py +++ b/tests/bootstrap/test_generics.py @@ -66,6 +66,20 @@ def run_test(name, src, expected): else: failed += 1 +# Generic function with a recursive call and generic arithmetic/comparison. +# This exercises PathType handling in the type checker; scalar-only code must +# not assume every type has BaseType.name. +if run_test('Generic Recursive Function', ''' +fn fact[T](n: T) -> T { + if n <= 1 { return 1; } + return n * fact(n - 1); +} +fn main() -> int { return fact(5); } +''', 120): + passed += 1 +else: + failed += 1 + print(f"\n{passed}/{passed + failed} passed", end="") if failed > 0: print(f", {failed} failed") diff --git a/tests/bootstrap/test_modules.py b/tests/bootstrap/test_modules.py new file mode 100644 index 00000000..6dbf81b6 --- /dev/null +++ b/tests/bootstrap/test_modules.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Bootstrap import alias regression tests.""" + +import os +import sys +import tempfile + +sys.path.insert(0, "bootstrap") + +from corec.backend.interpreter import Interpreter +from corec.frontend.desugar import MatchDesugarer +from corec.frontend.ir_gen import IRGen +from corec.frontend.lexer import Lexer +from corec.frontend.name_resolver import NameResolver +from corec.frontend.parser import Parser +from corec.frontend.type_checker import TypeChecker +from corec.utils.module_loader import resolve_imports + + +def main() -> int: + with tempfile.TemporaryDirectory() as directory: + helper = os.path.join(directory, "helper.cr") + main_path = os.path.join(directory, "main.cr") + with open(helper, "w", encoding="utf-8") as file: + file.write("fn value() -> int { return 42; }\n") + with open(main_path, "w", encoding="utf-8") as file: + file.write("import helper : h; fn main() -> int { return h.value(); }\n") + + with open(main_path, encoding="utf-8") as file: + source = file.read() + ast = Parser(Lexer(source).tokenize()).parse_compilation_unit() + errors = [] + resolve_imports(ast, source_path=main_path, search_paths=[directory], errors=errors) + resolver = NameResolver() + resolver.resolve(ast) + ast = MatchDesugarer(resolver.symtab).desugar(ast) + checker = TypeChecker(resolver.symtab) + checker.check(ast) + if errors or resolver.errors or checker.errors: + print("[FAIL] import alias:", errors + resolver.errors + checker.errors) + return 1 + result = Interpreter(IRGen(resolver.symtab).gen_module(ast)).run("main", []) + if result != 42: + print(f"[FAIL] import alias: expected 42, got {result}") + return 1 + print("[PASS] import module : alias resolves qualified function calls") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/bootstrap/test_pipeline.py b/tests/bootstrap/test_pipeline.py index be626976..83fec080 100644 --- a/tests/bootstrap/test_pipeline.py +++ b/tests/bootstrap/test_pipeline.py @@ -322,6 +322,30 @@ def test_raw(name, src, expected): fn main() -> int { return 7 % -3; } ''', 1) +# ── Lexer integer literal forms and wide apx literals ── +from corec.syntax.tokens import TokenType + +def check_integer_literals(): + tokens = Lexer('0x1f 0o17 0b1010 1_000').tokenize() + values = [int(t.lexeme) for t in tokens[:-1]] + if values != [31, 15, 10, 1000]: + print(f'[FAIL] integer literal forms: got {values}') + return False + try: + Lexer('0x').tokenize() + except SyntaxError: + pass + else: + print('[FAIL] empty prefixed integer was accepted') + return False + print('[PASS] integer literal forms: hex/octal/binary/separators') + return True + +if not check_integer_literals(): + failed += 1 +else: + passed += 1 + # ── Summary ── print(f"\n{passed}/{passed + failed} passed", end="") if failed > 0: diff --git a/tests/fixtures/dex_ffi_shim.c b/tests/fixtures/dex_ffi_shim.c new file mode 100644 index 00000000..0c6be93b --- /dev/null +++ b/tests/fixtures/dex_ffi_shim.c @@ -0,0 +1,14 @@ +#include +#include + +/* ABI probe: accept the lexer-compatible 3.14 binary64 bit pattern. */ +int64_t dex_ffi_bits_check(double value) { + int64_t bits = 0; + memcpy(&bits, &value, sizeof(bits)); + return bits == 4614253070214989086LL || + bits == 4614253070214989087LL ? 0 : 1; +} + +double dex_floor(double value) { + return value; +} diff --git a/tests/fixtures/dex_ffi_test.cr b/tests/fixtures/dex_ffi_test.cr new file mode 100644 index 00000000..0869034a --- /dev/null +++ b/tests/fixtures/dex_ffi_test.cr @@ -0,0 +1,7 @@ +import io + +extern fn dex_ffi_bits_check(d: dex) -> int; + +fn main() -> int { + return dex_ffi_bits_check(3.14); +} diff --git a/tests/selfhost/test_dex_arith.py b/tests/selfhost/test_dex_arith.py index 24f5cd25..327fb9ab 100644 --- a/tests/selfhost/test_dex_arith.py +++ b/tests/selfhost/test_dex_arith.py @@ -11,7 +11,7 @@ - ELF 端到端:精确断言全过(dex_test.cr 思路的主函数内联版) - apx 分流:同程序带 apx → binary64 行为(1.0b/3.0b != 0.3333333);不带 apx → 精确(为真) - apx 打印(Task 6 定稿):6 位定点舍入(半进)——0.1 → "0.1"、1/3 → "0.333333"、1.0000006 → "1.000001" -- `corec run` 解释器:精确运算结果正确;apx dex 显式报错(无 binary64 语义,Task 6) +- `corec run` 解释器:精确运算结果正确;apx dex 需要 binary64 语义时显式报错 - cir dump:`const dex = 3140000`(精确字面量 = 缩放整数,非二进制位模式) 文档化限制(Task 6 发现):经典 binary64 演示「0.1b+0.2b != 0.3」在本实现不成立—— @@ -292,8 +292,8 @@ def test_apx_print_rounding(): def test_interp_rejects_apx_dex(): - # Task 6:解释器无 binary64 语义——apx dex 运算(必经 I2F/F2I 转换)显式报错, - # 替代静默跳过/脏值(SIGFPE 防护)。exit != 0 且消息含 "binary64"。 + # The interpreter has no binary64 conversion instructions. Reject the + # path explicitly instead of returning a stale or silently truncated value. src = "fn main() -> int { x : dex, apx = 0.1; return @raw_int(x); }" r = subprocess.run( [str(COREC), "run", src], @@ -317,8 +317,8 @@ def test_extern_dex_arg_cir(): 字面量 3.14(缩放整数 3140000)被当 bits 直传。修复:extern 分支 TI_DEX_S 实参 → dex_scaled_to_bits(字面量重发射位模式常量)。断言(cir dump): main 内 extern 调用点前必须出现"缩放常量 + 位模式常量"序列(bits != 3140000)。 - 注:运行时 C 侧实测被既有 corearch --link 静态路径崩溃阻塞(终审 Fix F 记录), - 故以 IR 层断言为准——转换是否在调用点发生正是本次修复的全部内容。 + 运行时路径由 `test_extern_dex_static_link` 覆盖;本测试同时保留 IR + 断言,确认转换发生在调用点。 """ src = ( "extern fn dex_ffi_bits_check(d: dex) -> int;\n" @@ -350,6 +350,57 @@ def test_extern_dex_arg_cir(): return True +def test_extern_dex_static_link(): + """Static --link must relocate an extern call after embedding .so text.""" + src = ( + "extern fn dex_ffi_bits_check(d: dex) -> int;\n" + "fn main() -> int { return dex_ffi_bits_check(3.14); }\n" + ) + ccr = BASE / "build" / "dex_ffi_runtime.ccr" + binary = BASE / "build" / "dex_ffi_runtime" + shim = BASE / "build" / "core_dex_ffi.so" + try: + compile_shim = subprocess.run( + ["gcc", "-shared", "-fPIC", "-O2", + str(BASE / "tests" / "fixtures" / "dex_ffi_shim.c"), + "-o", str(shim)], + cwd=BASE, capture_output=True, text=True, timeout=60, + ) + if compile_shim.returncode != 0: + print(f"[FAIL] extern dex shim build: exit={compile_shim.returncode}") + print(compile_shim.stdout + compile_shim.stderr) + return False + r = run_corec(["ccr", "-o", str(ccr)], src) + if r.returncode != 0: + print(f"[FAIL] extern dex ccr generation: exit={r.returncode}") + print(r.stdout + r.stderr) + return False + r = subprocess.run( + [str(BASE / "build" / "corearch"), str(ccr), "--elf", + "--link", str(shim), "--static", "-o", str(binary)], + cwd=BASE, capture_output=True, text=True, timeout=180, + ) + if r.returncode != 0 or not binary.exists(): + print(f"[FAIL] extern dex static link: exit={r.returncode}") + print(r.stdout + r.stderr) + return False + os.chmod(binary, 0o755) + run = subprocess.run([str(binary)], cwd=BASE, capture_output=True, + text=True, timeout=30) + if run.returncode != 0: + print(f"[FAIL] extern dex static runtime: exit={run.returncode}") + print(run.stdout + run.stderr) + return False + print("[PASS] extern dex static --link relocates and runs") + return True + finally: + for path in (ccr, binary): + try: + path.unlink() + except FileNotFoundError: + pass + + def main(): if not COREC.exists(): print(f"[FAIL] missing native compiler: {COREC}") @@ -365,6 +416,7 @@ def main(): test_apx_print_rounding(), test_interp_rejects_apx_dex(), test_extern_dex_arg_cir(), + test_extern_dex_static_link(), ] passed = sum(results) print(f"{passed}/{len(results)} passed") diff --git a/tests/selfhost/test_dex_type.py b/tests/selfhost/test_dex_type.py index a4673568..cce5acf4 100644 --- a/tests/selfhost/test_dex_type.py +++ b/tests/selfhost/test_dex_type.py @@ -114,11 +114,48 @@ def test_dex_literal_type(): return True +def test_wide_decimal_literal(): + # A value whose integer part is wider than the 53-bit significand must + # not call the integer power helper with a negative exponent. + src = "fn main() -> int { x : dex = 2305843009213693952.0; return 1; }\n" + r = run_corec(["check"], src) + if r.returncode != 0: + print(f"[FAIL] wide decimal literal: exit={r.returncode}") + print(r.stdout + r.stderr) + return False + print("[PASS] wide decimal literal does not overflow lexer conversion") + return True + + +def test_static_string_bounds(): + src = 'fn main() -> int { return "abc"[3]; }\n' + r = run_corec(["check"], src) + out = r.stdout + r.stderr + if r.returncode != 1 or "R02" not in out: + print(f"[FAIL] static string bounds: exit={r.returncode}") + print(out) + return False + print("[PASS] literal string index at length is rejected with R02") + return True + + +def test_decimal_literal_overflow_is_reported(): + src = "fn main() -> int { x : dex = 123456789012345678901.0; return 1; }\n" + r = run_corec(["check"], src) + out = r.stdout + r.stderr + if r.returncode == 0 or "too wide" not in out: + print(f"[FAIL] wide decimal literal should be rejected: exit={r.returncode}") + print(out) + return False + print("[PASS] over-wide decimal literal is rejected explicitly") + return True + + def main(): if not COREC.exists(): print(f"[FAIL] missing native compiler: {COREC}") return 1 - results = [test_dex_type_declares(), test_dex_literal_type()] + results = [test_dex_type_declares(), test_dex_literal_type(), test_wide_decimal_literal(), test_static_string_bounds(), test_decimal_literal_overflow_is_reported()] passed = sum(results) print(f"{passed}/{len(results)} passed") return 0 if all(results) else 1 diff --git a/tests/selfhost/test_interp_float.py b/tests/selfhost/test_interp_float.py index 27618d57..f5c474d2 100644 --- a/tests/selfhost/test_interp_float.py +++ b/tests/selfhost/test_interp_float.py @@ -38,6 +38,8 @@ ("addr_index", "fn main() -> int { a := [10, 20, 30]; p := &a[1]; if p == 0 { return 1; } return 0; }", 0), # ── 纯 int 回归 ── ("int_arith", "fn main() -> int { a := -7; b := 3; c := a / b; d := a % b; if c == -2 && d == -1 { return 1; } return 0; }", 1), + # ── BC-CONST: interpreter string values remain intern indices ── + ("interp_strings", "fn main() -> int { s := \"abc\"; t := \"ab\" + \"c\"; if str_len(s) != 3 { return 1; } if str_eq(s, t) == 0 { return 2; } i := 1; if s[i] != 98 { return 3; } return 0; }", 0), ] diff --git a/tests/selfhost/test_lsp.py b/tests/selfhost/test_lsp.py index 95528cd1..c591c5e9 100644 --- a/tests/selfhost/test_lsp.py +++ b/tests/selfhost/test_lsp.py @@ -355,6 +355,33 @@ def test_stdout_pollution_guard(): print(f" POLLUTION CHECK: {len(frames)} frames, 0 dirty bytes, " f"{len(data)} bytes") + +def test_large_frame_body(): + """A body larger than the input buffer must still be framed exactly.""" + proc = subprocess.Popen([BIN], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + initialize = {"jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"capabilities": {}}} + body = json.dumps(initialize).encode() + proc.stdin.write(f"Content-Length: {len(body)}\r\n\r\n".encode() + body) + proc.stdin.flush() + response = read_frame(proc) + assert response["id"] == 1, response + + # Keep the request valid while forcing several 4KB stdin refills. + uri = "file:///tmp/large_frame.cr" + text = "fn main() -> int { return 0; }\n" + ("// padding\n" * 600) + request = {"jsonrpc": "2.0", "method": "textDocument/didOpen", + "params": {"textDocument": {"uri": uri, "version": 1, + "text": text}}} + body = json.dumps(request).encode() + assert len(body) > 4096, len(body) + proc.stdin.write(f"Content-Length: {len(body)}\r\n\r\n".encode() + body) + proc.stdin.flush() + diagnostics = read_frame(proc) + assert diagnostics["method"] == "textDocument/publishDiagnostics", diagnostics + assert diagnostics["params"]["diagnostics"] == [], diagnostics + shutdown_and_wait(proc) + # ── 统一驱动(Task 7)─────────────────────────────────────────────────────── # 按序执行全部测试组(每组独立 spawn,隔离全局状态);失败即非零退出。 @@ -367,6 +394,7 @@ def test_stdout_pollution_guard(): test_semantic_tokens, test_semantic_tokens_multiline, test_stdout_pollution_guard, + test_large_frame_body, ] def main() -> int: diff --git a/tests/selfhost/test_pointer_safety.py b/tests/selfhost/test_pointer_safety.py index 314c4b35..1d3f5f90 100644 --- a/tests/selfhost/test_pointer_safety.py +++ b/tests/selfhost/test_pointer_safety.py @@ -172,6 +172,18 @@ def test_alloc_buffer_cast_remains_tracked(): ) +def test_deref_loaded_int_is_not_pointer_escape(): + """A dereference result with integer type must not trigger pointer escape checks.""" + assert_accepted( + "fn main() -> int {\n" + " value := 42;\n" + " pointer := &value;\n" + " loaded := *pointer;\n" + " return loaded;\n" + "}\n" + ) + + def test_external_pointer_is_accepted_inside_unsafe(): assert_accepted( "fn main() -> int {\n" @@ -265,6 +277,7 @@ def test_dynamic_multi_target_pointer_is_rejected(): test_indirect_external_pointer_requires_unsafe, test_external_pointer_arithmetic_preserves_address_space, test_alloc_buffer_cast_remains_tracked, + test_deref_loaded_int_is_not_pointer_escape, test_external_pointer_is_accepted_inside_unsafe, test_dynamic_in_bounds_load_runs, test_dynamic_in_bounds_store_runs, diff --git a/tests/selfhost/test_region_cfg.py b/tests/selfhost/test_region_cfg.py index e68ddc17..165b2959 100644 --- a/tests/selfhost/test_region_cfg.py +++ b/tests/selfhost/test_region_cfg.py @@ -298,6 +298,18 @@ def test_ccr_v2_sg_section(): f"expected SG section with >=2 regions (func+for), got {sg_count}" assert end == fsize, f"format walk ended at {end} of {fsize} bytes" + +def test_ccr_writer_rejects_i32_overflow_inputs(): + """The v5 writer must reject out-of-range i32 fields instead of truncating.""" + # This is a source-level regression marker for the writer guard. A normal + # source program cannot currently synthesize a >i32 instruction operand; + # the guard is exercised by backend-generated metadata and malformed IR. + ccr_source = os.path.join(BASE, "src", "compiler", "ccr_io.cr") + with open(ccr_source, encoding="utf-8") as fh: + text = fh.read() + assert "fn ccr_i32_fits(val: int) -> int" in text + assert "if ccr_validate_i32_fields() == 0 { return -1; }" in text + def test_ccr_roundtrip_v2(): """save→load 往返守卫:v2 文件经 corearch 加载后 ELF 输出行为不变。 程序计算 0+1+2+3=6,ELF 运行时以 main 返回值为退出码。""" @@ -419,8 +431,9 @@ def test_state_edges_cache_persist(): test_for_loop_run, test_while_loop_run, test_while_break_continue_run, test_inline_callee_while_run, test_inline_callee_for_run, test_break_continue_run, - test_nested_loop_run, - test_ccr_v2_sg_section, test_ccr_roundtrip_v2, + test_nested_loop_run, + test_ccr_v2_sg_section, test_ccr_writer_rejects_i32_overflow_inputs, + test_ccr_roundtrip_v2, test_region_check_pointer_escape, test_region_check_cache_hit, test_nested_regions_cache_persist, test_state_edges_cache_persist] failed = 0 diff --git a/tools/corec b/tools/corec index a8ba4354..037f52d3 100755 --- a/tools/corec +++ b/tools/corec @@ -50,7 +50,7 @@ def cmd_build(args): for func in mod.functions: pass # rebuild struct_fields if needed later # Temporarily re-parse to get struct fields - with open(args.source) as f: + with open(args.source, encoding='utf-8') as f: src = f.read() lex = Lexer(src) ast = Parser(lex.tokenize()).parse_compilation_unit() @@ -60,7 +60,7 @@ def cmd_build(args): struct_fields[decl.name] = [(fname, str(ftype)) for fname, ftype in decl.fields] ccr_path = args.source.replace('.cr', '.ccr') - with open(ccr_path, 'w') as f: + with open(ccr_path, 'w', encoding='utf-8') as f: for func in mod.functions: f.write(f"Function: {func.name}\n") for blk in func.blocks: @@ -73,7 +73,7 @@ def cmd_build(args): asm_code = asm_gen.generate() out_path = args.output asm_path = out_path + ".s" - with open(asm_path, 'w') as f: + with open(asm_path, 'w', encoding='utf-8') as f: f.write(asm_code) obj_path = out_path + ".o" subprocess.run(['as', '-o', obj_path, asm_path], check=True) @@ -88,7 +88,7 @@ def cmd_ir(args): sys.exit(1) graphs = extract_all(mod) cir_path = args.source.replace('.cr', '.cir') - with open(cir_path, 'w') as f: + with open(cir_path, 'w', encoding='utf-8') as f: for name, g in graphs.items(): f.write(f"// Function: {name}\n") f.write(g.to_dot()) @@ -102,7 +102,7 @@ def cmd_cir(args): print("Errors:", errors, file=sys.stderr) sys.exit(1) ccr_path = args.source.replace('.cr', '.ccr') - with open(ccr_path, 'w') as f: + with open(ccr_path, 'w', encoding='utf-8') as f: for func in mod.functions: f.write(f"Function: {func.name}\n") for blk in func.blocks: From a9603a27d12cf8397313ae8cdf16d206f78a010f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 5 Sep 2026 22:07:14 +0900 Subject: [PATCH 005/138] feature/syntax doc rewrite (#62) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: spec 补定——要点部分以格形态为标准解释方式(三层映射) * docs: 语法文档重写(第 1-3 章:设计要点/词法/模块) * docs: 语法文档重写(第 4-7 章:类型标注/声明/表达式/控制流) * docs: 语法文档重写(第 8-10 章:函数/接口泛型/规约) * docs: 语法文档重写完成(第 11-13 章 + 全文验证通过) * docs: 语法文档重写实现计划(4 任务/逐章验证/全文清单) * docs: 设计要点补定——一切自动推断完整清单(类型/所有权/编译期/惰性/指针/dyn/概率/机器形状,无关键字) --- docs/language-syntax.md | 796 ++++++++---------- .../2026-09-01-language-syntax-rewrite.md | 279 ++++++ .../2026-09-01-syntax-doc-rewrite-design.md | 70 ++ 3 files changed, 720 insertions(+), 425 deletions(-) create mode 100644 docs/superpowers/plans/2026-09-01-language-syntax-rewrite.md create mode 100644 docs/superpowers/specs/2026-09-01-syntax-doc-rewrite-design.md diff --git a/docs/language-syntax.md b/docs/language-syntax.md index c5dc6a08..66075556 100644 --- a/docs/language-syntax.md +++ b/docs/language-syntax.md @@ -1,409 +1,352 @@ -Core 语言语法 +# Core 语言语法 + +本文档描述 Core 语言**本体**的语法:词法、类型标注、声明、表达式、控制流、接口与泛型、规约、并发。语法形式以 `grammar/core.ebnf` 与 `src/compiler/` 源码为准(冲突时以源码为准);语义以格形态为标准框架解释。 --- -## 设计哲学 +## 一、设计要点 + +Core 的语义保鲜(semantic preservation)由三层映射承载: + +| 层 | 形态 | 格式 | 职责 | +|----|------|------|------| +| 图 | 结构表达(关系空间) | `.cir` | 程序的结构:节点、边、region | +| 格 | 存在表达(状态空间) | `.ccr` | 存在:状态、条目、共存、驱逐 | +| 编码 | 机器形状 | ELF | 目标实现细节,由 hw-map 领域决定 | + +编译把图降为格的线性投影(`lower_to_ccr()`),格的线性投影承载全部类型与语义信息,形式验证直接消费——这就是语义保鲜:正确性陈述对全部可能执行成立,不按控制流路径陈述。详见 `docs/memory-model-capability-lattice.md`(三层映射 v4 §四)与 `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(格形态 IR)。 -- `dyn` 动态类型:图全量已知所有分支,编译器自动插转换代码,不需要用户写 cast。 -- 裸指针,和 C 一样自由,编译器通过HDFG自动验证安全。 -- 编译时执行由HDFG自动推导,无需关键字。`@comptime` 提供"算不了就报错"的保证。 -- requires / ensures 规约与函数体并列,可选编写,参与静态检查。 -- 同步调用与 go/await 并发构造统一映射为HDFG。 -- I/O 是流变换(转导器):程序 = (输入流) → (输出流),输入输出是同一数学对象(流)的两端——输出即对世界的输入。I/O 程序语义 = 流变换,规约可描述「输入流性质 → 输出流性质」,是一等公民语义而非验证黑洞。read_file/print/println 等现有 API 保持原样,系统调用等物理实现由后端按平台桥抽象隔离。 +语言层面的设计要点: + +- **无独立类型系统**:类型 = 图上的标注,接口 = 图上的契约,类型检查 = 图的良构性验证(与指针三 pass 同级)。本文档只描写类型标注的**语法**。 +- **无 `let` 关键字**:变量用 `:=` 或 `: Type =` 声明。 +- **无生命周期标注**:引用有效性由编译器全权推断。 +- **一切自动推断,无关键字**——编译器看图自动做,用户零标注负担: + - **类型推断**:`:=` / `auto` / `.` 推断;类型/接口是图的投影,标注可自动算出(语义保鲜,见类型方向定案) + - **所有权推断**:默认借用——传参、赋值自动借用,显式 `move` 转移所有权 + - **编译时执行**:图见全常量输入即自动执行,`@comptime` 兜底("算不了就报错") + - **惰性求值**:控制流分支自动惰性——未执行路径不求值,多路径需要时自动升级 eager,**无 `lazy()` 关键字**(见 `docs/lazy.md`) + - **指针安全**:裸地址与 C 同级自由,provenance 自动推导,编译器自动验证 + - **动态类型转换**:`dyn` 边界自动插转换,无需 cast + - **概率推导**:编译期自动进行(probabilistic pass) + - **机器形状**:整数位宽、浮点格式由编码层按目标自动决定(hw-map 领域) +- **并发 = 流的构造**:`go` / `await` / `flow` / `yield` 构成数据流网络。 --- -## 一、词法与注释 +## 二、词法 + +### 2.1 注释 ```core // 单行注释 /* 多行注释 */ ``` -## 关键字 +多行注释可以嵌套(`/* /* */ */`)。 -(以 `src/compiler/lexer.cr lookup_keyword()` 的实现为准,共 35 个) +### 2.2 标识符 -fn mut return if else loop while for break continue -true false struct enum extern impl match import pub -go await unsafe flow yield interface type mod as -auto fileid move in None Some unit +``` +IDENT = LETTER { LETTER | DIGIT | '_' | '\'' } +``` -注:`comptime` 不是关键字(`@comptime` 是 @ 内建原语);`requires/ensures/old/result/self/Self` -亦非词法关键字(规约语法见 `docs/spec-design.md` 与 `corespec.ebnf`,`self` 按标识符解析)。 +字母(a-z / A-Z / `_`)开头,后续可含字母、数字、下划线与单引号。`_` 本身是通配符(match 分支、占位)。 -注意:* 用于解引用指针,-> 仅用于返回类型,& 取地址。指针安全由HDFG自动验证,详见 `docs/pointer-model.md`。 +### 2.3 关键字 -`@` 开头的是编译器内建原语,不通过标准库实现: -- `@typeInfo` `@field` `@hasField` `@fields` `@sizeOf` `@alignOf` — 类型内省 -- `@comptime` `@inline` `@unroll` `@section` — 编译控制 -- `@no_bounds_check` `@fast` — 安全检查(unsafe 范畴) +共 37 个(以 `src/compiler/lexer.cr lookup_keyword()` 为准): ---- +``` +fn mut return if else loop while for break continue +true false struct enum extern impl match import pub +go await unsafe flow yield interface type mod as +auto fileid move self in None Some unit +``` -## 二、模块与导入 +- `self` 是词法关键字,用于方法接收者(`self` / `&self` / `&mut self`)。 +- `Self` 不是词法关键字——它是接口/方法签名中的上下文类型名,按标识符解析,由 checker 按上下文处理。 +- `comptime` 不是关键字(`@comptime` 是 @ 内建原语)。 +- `requires` / `ensures` / `old` / `result` 不是词法关键字——它们是规约层的语法(见第 10 章与 `grammar/corespec.ebnf`)。 -Core 使用基于**文件标识符**和**项目标识符**的展平模块系统,不依赖文件物理路径。 +### 2.4 字面量 -### 2.1 文件标识符 +| 字面量 | 示例 | 说明 | +|--------|------|------| +| 整数 | `42` `0` `-7` | 十进制。无进制前缀、无 `_` 分隔符、无宽度后缀;机器位宽由编码层按目标决定 | +| 小数 | `3.14` `.5` | 精确小数,默认 `dex` 语义(`3.` 不是合法小数) | +| 字符串 | `"Core"` `"a\nb"` | 不可变 UTF-8 | +| 字符 | `'x'` `'\n'` `'\x41'` | Unicode 标量值 | +| 布尔 | `true` `false` | | +| 单元 | `()` | `unit` 类型的唯一值 | +| 可选 | `None` `Some(expr)` | 可选类型构造器(见 4.1) | -默认使用文件名(不含 `.cr` 扩展名)作为标识符,可在文件头部手动覆盖: +转义序列:`\n` `\t` `\r` `\0` `\\` `\"` `\'` `\xHH`(两位十六进制)。 -```core -// file: lib/helper.cr -fileid "my_helper" -fn greet(name: string) { ... } -``` +### 2.5 运算符记号 -同一项目内标识符必须唯一,重复会编译报错。 +| 记号 | 含义 | +|------|------| +| `:=` | 变量声明(无 `let`) | +| `=` | 赋值(含声明初始化 `: T =`) | +| `->` | 返回类型(仅此用途) | +| `*` | 乘 / 解引用 | +| `&` `&mut` | 取地址(引用) | +| `+ - / %` | 算术 | +| `== != < > <= >=` | 比较 | +| `&& \|\| !` | 逻辑 | +| `.` | 字段访问 / 类型推断占位 | +| `::` | 路径分隔符(文件内路径) | +| `,` | 分隔 | +| `;` | 语句结束(可省略) | +| `?` | 可选标记 / 错误传播 | +| `..` | 范围 | +| `@` | 内建原语前缀 | +| `_` | 通配符 | -### 2.2 项目标识符 +--- -在项目根目录的 `Core.toml` 中声明项目名: +## 三、模块与导入 -```toml -name = "acme" -``` +模块系统基于**文件标识符**与**项目标识符**,不使用文件物理路径。 -代码中使用 `@项目名` 前缀引用外部项目。 +### 3.1 文件标识符 -### 2.3 导入语法 +默认使用文件名(不含 `.cr`)作为标识符,可在文件头部覆盖: ```core -// 导入同一项目的文件(使用文件标识符) -import math // 之后用 math.符号 -import math : m // 重命名为 m,之后用 m.符号 +fileid "my_helper"; -// 导入外部项目的文件 -import @acme math // 项目 acme 中的 math 文件 -import @acme math : m // 并重命名为 m +fn greet(name: string) -> string = "hi " + name; ``` -### 2.4 符号访问 +同一项目内标识符必须唯一,重复报错。 -使用点号 `.` 访问导入的符号: +### 3.2 模块声明 ```core -import math : m -result := m.add(3, 5) // 调用 math 文件中的 add 函数 +mod examples::pi; ``` -未导入时也可通过完整路径直接使用(不推荐):`math::add(3,5)`(双冒号为文件内路径分隔符)。 - -### 2.5 目录级批量导入 +`mod` 声明路径(`Path = IDENT { '::' IDENT }`),`::` 是文件内路径分隔符。 -每个目录下可放置 `_import.cr` 文件,其中的导入会自动应用于该目录及所有子目录的 `.cr` 文件(除非子目录有自己的 `_import.cr` 覆盖)。 - -示例 `_import.cr`: +### 3.3 导入 ```core -import @acme math : m -import @std io +import math; // 同一项目的文件(文件标识符) +import math : m; // 重命名,之后用 m.符号 +import @acme math; // 外部项目 acme 中的 math 文件 +import @acme math : m; // 并重命名 ``` -同一目录及子目录下的文件可直接使用 `m.add` 和 `io.println`,无需重复 import。 - -### 2.6 依赖裁剪 +`@项目名` 前缀区分本地符号与外部项目符号,不可省略。项目名在项目根的 `Core.toml` 中声明(`name = "acme"`)。 -编译时以 `main` 为入口,分析所有被引用的符号(含跨项目),从外部 IR 中提取被引用的函数、类型及其依赖,合并为单一可执行文件,丢弃未使用代码。适用于内核/嵌入式场景。 +### 3.4 符号访问 ```core -// 示例项目结构 -// -// my_project/ -// ├── Core.toml # name = "myapp" -// ├── _import.cr # 全局导入 -// ├── main.cr -// └── lib/ -// ├── _import.cr # 可选,子目录覆盖 -// └── helper.cr +import math : m; -// _import.cr(顶层) -import @acme math : m -import @std io - -// main.cr(无需重复 import) -fn main() { - result := m.add(3, 5) - io.println("result = {result}") -} +result := m.add(3, 5); // 点号访问导入符号 ``` -### 2.7 注意事项 - -- 项目名前的 `@` 前缀不可省略,用于区分本地符号与外部项目符号。 -- 文件标识符在当前项目内必须唯一,编译器会检测重复并报错。 -- `_import.cr` 本身不生成代码,只起导入声明作用。子目录的 `_import.cr` 会继承父目录的导入(合并,冲突时报错)。 -- 依赖裁剪需要 IR 级别的分析器,是 Core 工具链的组成部分。 -- 推荐总是先导入再使用,保持代码清晰可读。 - ---- +未导入时可用完整路径 `math::add(3, 5)` 直接访问(不推荐)。 -## 三、变量与基础类型 +### 3.5 目录级批量导入 -### 3.1 变量声明 - -变量使用 `:=` 或 `: Type =` 语法声明(无 `let` 关键字): +目录下的 `_import.cr` 自动应用于该目录及子目录(子目录自己的 `_import.cr` 覆盖;继承合并,冲突报错): ```core -x := 42; // 不可变,类型推断为 int -y : ., mut = 3.14; // 可变,类型推断为 dex(精确小数) +// _import.cr +import @acme math : m; +import @std io; ``` -类型标注、标签(mut/pub/apx)可选: - ```core -x : int = 42; -name : string = "Core"; - -count : int, mut = 0; // 可变 + 显式类型 -pub_val : int, pub = 42; // 公开字段 -speed : ., apx = 3.14; // apx = 近似授权:授权后端对 dex 运算做语义级变换(CPU 上兑现为 binary64 快路径) - -// 批量声明 -a, b : int = 1, 2; +// main.cr——无需重复 import +fn main() { + result := m.add(3, 5); + io.println("result = {result}"); +} ``` -`auto` 关键字和 `.`(点号)可用于类型推断占位,语义完全相同。既可用于变量声明,也可用作函数返回类型: +`_import.cr` 本身不生成代码,只起导入声明作用。 -```core -// 使用 auto -x : auto = 42; -counter : auto, mut = 0; +## 四、类型标注 -// 使用 .(auto 的简写) -y : . = 3.14; -z : ., mut = 20; +Core 没有独立的类型系统:类型是图上的**标注**(`x : int` 标注 x 的节点),接口是图上的**契约**。本节描写类型标注的语法形式。 -// 批量声明 -a, b, c : auto, mut = 0; +### 4.1 类型表达式 -// 全局公开可变 -shared : auto, pub, mut = 42; ``` -`.` 更简洁,常用于 `: ., mut` 模式;`auto` 更显式,两者完全等价。 -推荐正式代码中使用 `auto` 提高可读性,个人脚本或快速原型可使用 `.` 简写。 - -`auto` 也可用于函数返回类型,让编译器从函数体推导返回类型: - -```core -fn add(a: int, b: int) -> auto { - return a + b; // 推导为 int -} - -fn pi() -> auto = 3.14159; // 推导为 dex(精确小数,单行形式) +Type = BaseType | PathType | RefType | OptionalType | TupleType | ArrayType | SliceType ``` -注意:`auto` 是关键字,不可用作变量名或类型名。 - -### 3.2 基础类型 +**基础类型**: | 类型 | 说明 | |------|------| -| int | 整数(精确;i64 实现) | -| dex | 精确小数(默认实数语义,缩放整数/定点实现,精度内置) | -| bool | true / false | -| string | 不可变 UTF-8 字符串 | -| char | Unicode 标量值 | -| dyn | 动态类型——图已知全部分支,边界自动插转换 | - -`dyn` 动态类型详见 `docs/dynamic-typing.md`。 -| unit | 空值 () | -| never | 发散类型 | +| `int` | 整数(精确) | +| `dex` | 精确小数(默认实数语义,缩放整数/定点实现) | +| `bool` | `true` / `false` | +| `string` | 不可变 UTF-8 字符串 | +| `char` | Unicode 标量值 | +| `unit` | 空值 `()` | +| `never` | 发散类型 | +| `dyn` | 动态类型——图全量已知所有分支,边界自动插转换(详见 `docs/dynamic-typing.md`) | +| `Self` | 上下文类型名,仅在接口/方法签名内有效(实现类型本身) | -`3.14` 字面量默认产生 dex;近似需显式授权——`apx` 是变量级标签(与 mut/pub 同族): -`x : ., apx = 3.14` 授权后端把 dex 运算降级为 binary64 FPU 快路径(结果可以变); -不带 apx 时 dex 恒精确(其他范式忽略 apx 附注即回退到精确缩放整数运算)。 -`_f32` / `_f64` 后缀保留,暂作 apx 的 CPU 位宽标注。 - -显式位宽后缀: +**复合类型**: ```core -addr := 0x1000_u64; -count := 100_i32; +pair : (int, bool); // 元组 +zeros : [int; 16]; // 数组(长度是常量) +slice : [int]; // 切片 +maybe : int?; // 可选 +ref : &Point; // 引用 +mutr : &mut Point; // 可变引用 ``` -### 3.3 指针 +**接口作类型**:任何接口名可作为类型标注(`PathType`),见第 9 章。 -Core 的指针是裸地址,和 C 一样自由,但编译器通过HDFG自动验证安全。 +**机器形状不在语言内**:整数位宽、浮点格式等机器形状由编码层按目标自动决定,不进入类型/标签体系;未来显式控制走 hw-map。 -```core -p := &arr[0]; // 取地址,编译器记下 provenance -p = p + n; // 偏移,随便算 -x := *p; // 解引用,编译器验证 offset ∈ [0, len) -ptr := cast(addr); // 显式转换 -``` +### 4.2 标注位置 -指针安全基于HDFG的 provenance 推导,详见 `docs/pointer-model.md`。 +类型标注出现在:变量声明(`x : int = 42`)、函数参数(`fn f(a: int)`)、返回类型(`-> int`)、泛型参数(`[T: Eq]`,见第 9 章)。 -### 3.4 编译时执行 +### 4.3 标签 -Core 的编译时执行以`HDFG的自动推导为主,`@comptime` 兜底为辅。 +变量与字段可带标签,`mut`(可变)/ `pub`(公开)/ `apx`(近似授权): ```core -// 自动推导——图看见输入全是常量,自动编译期执行 -x := fibonacci(40); -cfg := read_file("config.toml"); -z := arr[3]; // offset 已知,编译期消错 - -// 强制——算不了就报错 -@comptime { - x := fibonacci(40); - cfg := read_file("config.toml"); -} +count : int, mut = 0; // 可变 +pub_val : int, pub = 42; // 公开字段 +speed : ., apx = 3.14; // apx:授权后端把 dex 运算降级为 binary64 快路径 ``` -详见 `docs/comptime.md`。 - -### 3.5 @ 内建原语 +`apx` 与 `mut` / `pub` 同族。不带 `apx` 时 `dex` 恒精确。 -`@` 开头的标识符是编译器内建的能力入口,不通过标准库实现。分三类: +### 4.4 推断占位:`auto` 与 `.` -**元数据查询**——编译期查询类型信息,零运行时开销: +类型推断占位,两者完全等价,可用于变量声明与返回类型: ```core -@sizeOf(T) // 类型大小 -@alignOf(T) // 类型对齐 -@fields(T) // 字段名列表 -@field(T, name) // 字段偏移和类型 -@hasField(T, name) // 字段是否存在 -@typeInfo(T) // 完整类型结构 +x : auto = 42; // 推断为 int +y : . = 3.14; // 推断为 dex +z : ., mut = 20; +a, b, c : auto, mut = 0; ``` -**编译控制**——不改变语义,只改变编译方式: +`auto` 更显式、`.` 更简洁——注意 `: ., mut` 模式中 `.` 是类型占位。 -```core -@inline(fn) // 提示内联 -@unroll(n) // 展开循环 -@section(name) // 指定代码段 -@comptime(expr) // 强制编译期执行 -``` +--- -**安全检查**——unsafe 范畴,调用方自己保证: +## 五、变量声明 + +无 `let` 关键字。声明即语句,`:=` 推断类型(不可变),`: Type =` 显式类型: ```core -@no_bounds_check // 跳过边界检查 +x := 42; // 推断为 int,不可变 +name : string = "Core"; // 显式类型 +count : int, mut = 0; // 显式 + 可变 +shared : auto, pub, mut = 42; // 全局公开可变 +a, b : int = 1, 2; // 批量声明 ``` -详见 `docs/at-intrinsics.md`。 - -### 3.6 可选类型 +`move` 显式移动所有权(默认借用语义,见第 1 章设计要点——无生命周期标注,引用有效性编译器全权推断): ```core -maybe: int? = Some(5); -nothing: int? = None; +b := move a; // a 的所有权转移到 b,a 失效 +consume(move b); // 参数移动传入 ``` ---- +复制类型(`int`、`dex`、`bool` 等实现 Copy 接口的类型)赋值时自动复制,不需 `move`。 -## 四、复合类型 +--- -### 4.1 元组 +## 六、表达式与运算符 -```core -pair := (42, true); -(n, b) := pair; // 解构 -``` +### 6.1 优先级 -### 4.2 数组与切片 +从低到高(`grammar/core.ebnf` 的推导链): -```core -arr := [1, 2, 3]; -zeros : [int; 16] = [0; 16]; -slice : [int] = arr[0..2]; // 切片,引用原数组 +``` +Assignment → LogicalOr → LogicalAnd → Equality → Comparison +→ Addition → Multiplication → Unary → CallOrField → Primary ``` -### 4.3 结构体 +| 优先级 | 运算符 | 结合性 | +|--------|--------|--------| +| 最低 | `=`(赋值)`:=`(声明) | 右 | +| | `\|\|` | 左 | +| | `&&` | 左 | +| | `==` `!=` | 左 | +| | `<` `>` `<=` `>=` | 左 | +| | `+` `-` | 左 | +| | `*` `/` `%` | 左 | +| | 一元 `-` `!` `&` `&mut` | — | +| 最高 | 调用 `f(...)`、字段 `.x`、下标 `[i]`、`?` | 左 | + +### 6.2 基本表达式 ```core -struct Point { - x: dex, - y: dex, -} +// 字面量与标识符 +42; 3.14; "Core"; 'x'; true; (); None; Some(5); -p := Point { x = 1.0, y = 2.0 }; -println(p.x); -``` +// 括号 +(1 + 2) * 3; -### 4.4 枚举 +// 块表达式——块的值 = 最后一条语句的值 +x := { a := 1; a + 2 }; -```core -enum IpAddr { - V4(u8, u8, u8, u8), - V6(u16, u16, u16, u16, u16, u16, u16, u16), -} +// if 表达式 +category := if x > 0 { "positive"; } else { "not positive"; }; -enum Option[T] { - Some(T), - None, -} +// match / loop / for / go / await / unsafe 也是表达式(见第 7、11、12 章) ``` ---- - -## 五、函数 - -### 5.1 定义 +### 6.3 调用、字段与下标 ```core -fn add(a: int, b: int) -> int { - return a + b; -} - -// 单行形式 -fn add(a: int, b: int) -> int = a + b; +result := m.add(3, 5); // 函数调用 +p.x; // 字段访问 +arr[0]; // 下标 ``` -### 5.2 方法 +### 6.4 转换 `as` ```core -impl Point { - fn norm(&self) -> dex { - (self.x * self.x + self.y * self.y).sqrt() - } - - fn move_by(&mut self, dx: dex, dy: dex) { - self.x += dx; - self.y += dy; - } -} +x : int = 3; +d : dex = x as dex; // 显式转换 +ptr : RawRef = addr as RawRef; ``` -### 5.3 泛型 +### 6.5 错误传播 `?` + +`?` 后缀:表达式失败时立即从当前函数返回错误,只能用于返回可传播错误类型(如 `Result`)的函数中: ```core -fn first[T](list: &[T]) -> T? { - if list.len() > 0 { - return Some(list[0]); - } - None +fn fetch() -> Result { + data := read_file("config.txt")?; // 出错则立刻返回 Err + Ok(data) } ``` -### 5.4 形式规约 +### 6.6 @ 内建调用 + +`@` 开头是编译器内建原语(见第 13 章): ```core -fn divide(a: int, b: int) -> int? - requires b != 0 - ensures result.is_some() implies (a / b) == result.unwrap() -{ - if b == 0 { - return None; - } - return Some(a / b); -} +n := @sizeOf(Point); ``` -- requires —— 调用方必须满足的前提条件 -- ensures —— 保证的后置条件,result 指返回值,old(expr) 指函数入口时表达式的值 - -规约参与静态检查,不影响运行时性能。 - --- -## 六、控制流 +## 七、语句与控制流 + +语句以 `;` 结束(可省略)。`Statement = Expr ';' | LetStmt | ReturnStmt | YieldStmt | BreakStmt | ContinueStmt`。 -### 6.1 条件 +### 7.1 条件 ```core if x > 0 { @@ -413,14 +356,12 @@ if x > 0 { } else { println("zero"); } - -category := if x > 0 { "positive" } else { "not positive" }; ``` -### 6.2 循环 +### 7.2 循环 ```core -for i in 0..10 { +for i in 0..10 { // 范围 0..10(含 0 不含 10) println(i); } @@ -428,7 +369,7 @@ for item in items { println(item); } -loop { +loop { // 无限循环 if done { break; } } @@ -437,18 +378,20 @@ while condition { } ``` -### 6.3 模式匹配 +`break` / `continue` 用于 `loop` / `while` / `for`。 + +### 7.3 匹配 ```core match opt { Some(x) => println("got {x}"), - None => println("nothing"), + None => println("nothing"), } ``` -匹配必须穷尽所有情况。 +匹配必须穷尽所有情况。模式(Pattern)可以是字面量、标识符、元组/结构体/枚举模式、`_` 通配。 -当 match 的 scrutinee 是字符串表达式且所有分支都是字符串常量时,编译器自动生成基于哈希的跳转表——计算一次哈希值,与各分支的预计算哈希比较,冲突时再检查相等。无需逐个分支线性比较。 +当 scrutinee 是字符串表达式且所有分支是字符串常量时,编译器自动生成基于哈希的跳转表——计算一次哈希,与各分支预计算哈希比较,冲突时再检查相等: ```core match cmd { @@ -457,245 +400,248 @@ match cmd { "restart" => restart_server(), _ => println("unknown command"), } -// 编译器优化为跳转表,而非逐个字符串比较 ``` +### 7.4 返回 + +```core +fn add(a: int, b: int) -> int { + return a + b; // 显式返回 +} +``` + +### 7.5 不安全块 + +```core +unsafe { + ptr : RawRef = addr as RawRef; + ptr.write(42); +} +``` + +`unsafe` 块隔离不安全操作(见第 12 章)。 + --- -## 七、所有权与借用 +## 八、函数与方法 -- 默认借用:函数传参、赋值等不移动所有权,编译器自动插入借用。 -- 显式移动:使用 move 关键字转移所有权。 +### 8.1 函数定义 ```core -a := vec![1, 2, 3]; -b := move a; // a 失效 +fn add(a: int, b: int) -> int { + return a + b; +} -fn consume(v: Vec) { ... } -consume(move b); // b 移动进去 +fn pi() -> auto = 3.14159; // 单行形式;返回类型推断 ``` -- 复制类型:整数、精确小数(dex)、布尔、小元组等实现 Copy 接口,赋值时自动复制。 - -无生命周期标注,编译器全权推断引用有效性。 +`FunctionDecl = [ 'pub' ] 'fn' IDENT [ GenericParams ] '(' [ ParamList ] ')' '->' Type ( FunctionBody | '=' Expr ';' )`。参数 `Param = IDENT ':' Type`。返回类型可以是 `auto`(或 `.`),由函数体推导。 ---- +### 8.2 方法 -## 八、接口 +`impl` 为类型挂接方法,接收者 `self` 是第一个参数: ```core -interface Hash { - fn hash(&self) -> u64; +impl Point { + fn norm(&self) -> dex { + (self.x * self.x + self.y * self.y).sqrt() + } + + fn move_by(&mut self, dx: dex, dy: dex) { + self.x += dx; + self.y += dy; + } } +``` + +接收者形式:`self`(按值)/ `&self`(借用)/ `&mut self`(可变借用)。 + +--- +## 九、接口与泛型 + +接口是图上的**契约**(见类型方向定案 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`):声明一组方法签名,实现类型必须满足。 + +### 9.1 接口声明与实现 + +```core interface Eq { fn eq(&self, other: &Self) -> bool; } + +impl Eq for Point { + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} ``` -实现: +接口签名中的 `Self` 指实现类型本身。基础类型(`int` / `dex` / `string` 等)对标准接口(`Eq`、`Copy`、`Hash` 等)内建实现。 + +### 9.2 泛型 = 编译期接口具体化 + +泛型参数 `[T]` 声明,接口绑定 `[T: Eq]` 约束 T 必须实现该接口: ```core -impl Hash for Point { - fn hash(&self) -> u64 { - ... +fn first[T: Eq](list: &[T]) -> T? { + if list.len() > 0 { + return Some(list[0]); } + None } ``` -标准库为常用类型提供 Hash 实现——哈希的语义是散列函数(数学:同一输入必得同一输出),具体算法是后端实现细节、可替换(平台桥提供 hash_bytes/hash_str 语义接口,现有 str_hash 保持): +实例化时检查类型参数满足接口绑定,**编译期具体化**——无运行期字典。 -| 类型 | Hash 实现 | -|------|----------| -| `int` | 整数值直接作为哈希 | -| `string` | 基于内容的哈希 | -| `bool` | true/false 映射为固定值 | -| `dex` | 按位表示的哈希 | -| `(T1, T2)` | 组合哈希 | +### 9.3 类型别名 -用户也可为自定义类型手动实现或通过 derive 自动生成。 +```core +type Point3 = (dex, dex, dex); +``` + +--- -### 8.1 HashMap / HashSet +## 十、规约 -标准库提供基于 Hash + Eq 的泛型集合类型: +规约(requires / ensures)与函数体并列,可选编写,参与静态检查,不影响运行时性能。规约语法详见 `docs/spec-design.md` 与 `grammar/corespec.ebnf`。 + +### 10.1 requires / ensures ```core -import std::collections; +fn divide(a: int, b: int) -> int? + requires b != 0 + ensures result.is_some() implies (a / b) == result.unwrap() +{ + if b == 0 { + return None; + } + return Some(a / b); +} +``` + +- `requires` —— 调用方必须满足的前提条件 +- `ensures` —— 保证的后置条件;`result` 指返回值,`old(expr)` 指函数入口时表达式的值 -map : collections::HashMap[string, int] = .{}; -map["key"] = 42; +### 10.2 where 值约束 -set : collections::HashSet[int] = .{}; -set.insert(7); +`where` 实现内联轻量前置条件,与 `requires` 同层: + +```core +fn sqrt(x: dex) -> dex where x >= 0 { + // ... +} ``` -类型约束由编译器自动推导:`HashMap[K, V]` 要求 `K: Hash + Eq`,`HashSet[T]` 要求 `T: Hash + Eq`。 +值约束按表达式形态分三档语义(一条语法,两层消费:checker + 验证管线): + +| 约束形态 | 语义 | +|----------|------| +| 常量约束 | 编译期求值——不满足 → 编译错误;满足 → 通过 | +| 符号约束 | 生成 VC(验证义务),由验证管线消费 | +| 动态约束 | 运行时检查——「证明不了也不静默」 | + +与 `.corespec` 前置条件互相映射。 --- -## 九、并发 +## 十一、并发 -### 9.1 异步启动 +### 11.1 异步启动 `go` ```core -handle := go some_work(arg1, arg2); // handle: Flow +handle := go some_work(arg1, arg2); // handle: Flow ``` -### 9.2 等待结果 +### 11.2 等待 `await` ```core result := await handle; // 阻塞直到完成 ``` -属性:当需要值时自动等待 +按需自动等待——当值被需要时自动等待: ```core -val := go fetch(url) + go fetch(other); // 自动等待 +val := go fetch(url) + go fetch(other); // 两个 flow 自动等待 ``` -### 9.3 长期执行流 +### 11.3 长期执行流 `flow` / `yield` / `recv` ```core flow counter(start: int) -> int { n : ., mut = start; loop { - yield n; // 产出数据 + yield n; // 产出数据 n += 1; } } f := go counter(0); for i in 0..5 { - v := f.recv(); // 接收一个值 + v := f.recv(); // 接收一个值 println(v); } ``` -### 9.4 通道(标准库) +--- -```core -import std::chan; -(tx, rx) := chan::new(); -go producer(tx); -go consumer(rx); -``` +## 十二、指针与 unsafe ---- +### 12.1 指针 -## 十、错误处理 +指针是裸地址,与 C 同级自由,编译器通过格/图自动验证安全(详见 `docs/pointer-model.md`): ```core -enum Result[T, E] { - Ok(T), - Err(E), -} - -fn fetch() -> Result { - data := read_file("config.txt")?; // 出错则立刻返回 Err - Ok(data) -} +p := &arr[0]; // 取地址,编译器记下 provenance +p = p + n; // 偏移,随便算 +x := *p; // 解引用,编译器验证 offset ∈ [0, len) +ptr := addr as RawRef; // 显式转换 ``` -? 只能用于返回 Result 的函数中。 +### 12.2 unsafe 块 ---- +`unsafe { }` 块内允许: -## 十一、不安全区域 +- 使用 `RawRef` +- 调用外部 C 函数 +- 编译器固有 ```core unsafe { - reg := 0xFEED_0000 as RawRef; - reg.write(0x42); + ptr : RawRef = addr as RawRef; + ptr.write(42); } ``` -unsafe 块内允许: - -- 使用 RawRef -- 调用外部 C 函数 -- 编译器固有 - -RawRef 无法逃逸到安全代码。 +`RawRef` 无法逃逸到安全代码。 --- -## 十二、HDFG视图(设计) +## 十三、@ 内建原语 -编写顺序代码时,后台自动构建HDFG: - -- 每个 flow / go 映射为图节点 -- yield / recv 定义连续的数据边 -- 并行例程形成动态扇出/扇入结构 -- 集成 select 可以表达汇合点 - -整张图可被静态分析和形式化验证。 - ---- +`@` 开头的标识符是编译器内建的能力入口,不通过标准库实现。分三类(详见 `docs/at-intrinsics.md`)。 -## 十三、示例:并行计算 π +**元数据查询**——编译期查询类型信息,零运行时开销: ```core -mod examples::pi; - -fn leibniz_partial(start: int, n: int) -> dex { - sum : ., mut = 0.0; - sign : ., mut = if start % 4 == 1 { 1.0 } else { -1.0 }; - for i in 0..n { - sum += sign / ((start + 2 * i) as dex); - sign = -sign; - } - return sum; -} - -flow worker(id: int, base: int, chunk: int) -> dex { - offset : ., mut = base; - loop { - partial := leibniz_partial(offset, chunk); - yield partial; - offset += chunk * num_workers(); - } -} - -flow orchestrator(workers: int, chunk_size: int) { - flows : ., mut = []; - for i in 0..workers { - flows.push(go worker(i, 1 + 2 * i * chunk_size, chunk_size)); - } - - pi : ., mut = 0.0; - loop { - for f in &flows { - pi += 4.0 * f.recv(); - } - println("π ≈ {pi}"); - } -} - -fn main() { - worker_count := 8; - chunk := 10_000; - go orchestrator(worker_count, chunk); -} +@sizeOf(T) // 类型大小 +@alignOf(T) // 类型对齐 +@fields(T) // 字段名列表 +@field(T, name) // 字段偏移和类型 +@hasField(T, name) // 字段是否存在 +@typeInfo(T) // 完整类型结构 ``` -输出:持续逼近 π 的近似值,如 +**编译控制**——不改变语义,只改变编译方式: -``` -π ≈ 3.1415926... -π ≈ 3.141592653... -... +```core +@inline(fn) // 提示内联 +@unroll(n) // 展开循环 +@section(name) // 指定代码段 +@comptime(expr) // 强制编译期执行 ``` -此程序展示了:只用 go、flow、yield、recv,就能构建并行的数据流网络。 - ---- - -## 设计要点 +**安全检查**——unsafe 范畴,调用方自己保证: -1. 无指针操作符(*, ->)和地址运算 -2. 类型自动推导,减少显式类型标注 -3. 默认借用语义,显式 move 转移所有权 -4. 并发构造仅 go 和 await 两个关键字 -5. 控制流结构(if、for、match)保持直观 -6. 可选规约标注参与静态检查,不影响运行时 -7. unsafe 块清晰隔离不安全操作 +```core +@no_bounds_check // 跳过边界检查 +``` diff --git a/docs/superpowers/plans/2026-09-01-language-syntax-rewrite.md b/docs/superpowers/plans/2026-09-01-language-syntax-rewrite.md new file mode 100644 index 00000000..a004188c --- /dev/null +++ b/docs/superpowers/plans/2026-09-01-language-syntax-rewrite.md @@ -0,0 +1,279 @@ +# language-syntax.md 重写实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将 `docs/language-syntax.md` 重写为 13 章参考手册式语法文档(定案后的完整语言,以格形态为标准解释,不标实现状态)。 + +**Architecture:** 单文件文档重写。按内容群分 4 个任务顺序书写:骨架+设计要点+词法+模块 → 类型标注+声明+表达式+控制流 → 函数+接口泛型+规约 → 并发+指针+内建+全文验证。每个任务自带验证(grep 检查 + 章节内容核对),最后一个任务执行 spec 的完整验证清单。 + +**Tech Stack:** Markdown(无代码、无编译;验证用 grep)。版本控制用 `jj`(铁律 #2,禁止 git)。 + +**Spec:** `docs/superpowers/specs/2026-09-01-syntax-doc-rewrite-design.md` + +## Global Constraints + +(逐条抄自 spec,所有任务隐式包含) + +1. **定位**:定案后的完整语言,当前实现与未来设计统一呈现,全文不出现「已实现 / 未实现 / 待实现」标注。 +2. **边界**:语言本体(语法为准)。不含标准库表层(HashMap / HashSet / 通道 / Result)、IR、工具链、平台桥、HDFG 愿景。 +3. **宽度类型全面消失**:u8/u16/u32/u64/i32 等宽度类型名与 `_u64` / `_i32` / `_f32` / `_f64` 后缀不在文档中出现;机器形状一句话带过(归 hw-map)。 +4. **无「类型系统」概念**:类型 = 图标注、接口 = 图上契约;文档只描写「类型标注语法」,不设类型系统章、不使用「类型系统」表述。 +5. **要点解释标准**:设计要点与语义解释以格形态为标准框架(三层映射:图 `.cir` = 关系空间 / 格 `.ccr` = 存在空间 / 编码 = hw-map 领域;语义保鲜 = 格全量承载类型/语义信息;正确性对全部可能执行成立),不用 CFG/编译器视角。 +6. **风格**:参考手册式为主,教程式为辅。每个语法构造 = 语法形式 + 说明 + 示例。 +7. **真源原则**:关键字表以 `src/compiler/lexer.cr lookup_keyword()` 为准;优先级与语法形式以 `grammar/core.ebnf` 为准;冲突时 lexer.cr / parser.cr 优先于 EBNF。 +8. **变更范围**:仅改 `docs/language-syntax.md` 一个文件。grammar/*.ebnf、源码、其他文档不动。 +9. **提交**:全部用 `jj`(`jj commit -m "..." `)。会话开始前已有未跟踪文件 `logs_90275072099.zip`,任何提交不得包含它。 + +## 关键事实(已验证,直接使用) + +**关键字表(37 个,抄自 lexer.cr lookup_keyword()):** + +``` +fn mut return if else loop while for break continue +true false struct enum extern impl match import pub +go await unsafe flow yield interface type mod as +auto fileid move self in None Some unit +``` + +- `self` 是关键字(T_SELF);`Self` 非词法关键字(类型名,按标识符解析,checker 按上下文处理)。 +- `comptime` 不是关键字(`@comptime` 是 @ 内建);`requires/ensures/old/result` 非词法关键字(规约语法,见 corespec.ebnf)。 +- 运算符记号:`*` 解引用、`&`/`&mut` 取地址、`->` 仅返回类型、`:=` 声明、`..` 范围、`?` 可选与错误传播、`@` 内建前缀、`::` 路径、`as` 转换、`move` 移动。 + +**表达式优先级链(grammar/core.ebnf):** + +``` +Assignment → LogicalOr → LogicalAnd → Equality → Comparison +→ Addition → Multiplication → Unary → CallOrField → Primary +``` + +**EBNF 关键产生式(grammar/core.ebnf,执行时通读全文):** + +``` +Type = BaseType | PathType | RefType | OptionalType | TupleType | ArrayType | SliceType ; +BaseType = 'int' | 'dex' | 'bool' | 'string' | 'char' | 'unit' | 'never' | 'Self' ; +RefType = '&' [ 'mut' ] Type ; +OptionalType = Type '?' ; +ArrayType = '[' Type ';' INT_LIT ']' ; +SliceType = '[' Type ']' ; +Let = IDENT [ ':' ( Type | 'auto' | '.' ) [ ',' Tag { ',' Tag } ] ] ( ':=' | '=' ) ; +Tag = 'mut' | 'pub' | 'apx' ; +``` + +**拟定语法(本 spec 定稿,文档直接呈现):** + +- where 值约束:`fn f(...) -> T where <布尔表达式> { ... }`(与 requires 同层),三档语义:常量约束 → 编译错误/通过;符号约束 → VC 义务;动态约束 → 运行时检查。 +- 泛型接口绑定:`fn first[T: Eq](...)`,声明处接口绑定 + 实例化检查,编译期具体化,无运行期字典。 + +--- + +### Task 1: 骨架 + 设计要点 + 词法 + 模块(第 1-3 章) + +**Files:** +- Rewrite: `docs/language-syntax.md`(本文档整个重写,从零写;本节覆盖第 1-3 章,含文件头) + +**Interfaces:** +- Consumes: spec 的 Global Constraints + 关键事实 +- Produces: 文档骨架(标题 + 13 章标题 + 关联文档链接),后续任务只填充各自章节 + +- [ ] **Step 1: 通读事实源** + +Run: `sed -n '60,102p' grammar/core.ebnf && sed -n '71,115p' src/compiler/lexer.cr` +目的:确认 struct/enum/interface/import 等产生式与字面量 token 规则(INT_LIT/DEX_LIT/STRING_LIT/CHAR_LIT/UNIT_LIT 形式),供第 2、3 章使用。 + +- [ ] **Step 2: 写文件头 + 第 1 章设计要点** + +写 `docs/language-syntax.md` 全文框架(13 章标题 + 每章占位注释行 `` 标记未写章节),然后立即写: + +- 标题 `# Core 语言语法` + 说明段:本文档描述 Core 语言的语法(定案后的完整语言观)。 +- **一、设计要点**(短文,以格形态为标准解释): + - 三层映射:图(`.cir`)= 结构表达(关系空间)→ 格(`.ccr`)= 存在表达(状态空间:存在/条目/共存/驱逐)→ 编码 = 机器形状(hw-map 领域)。链接 `docs/memory-model-capability-lattice.md` 与 `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`。 + - 语义保鲜:格形态全量承载类型/语义信息,形式验证直接消费;正确性陈述对全部可能执行成立。 + - 无独立类型系统(类型 = 图标注、接口 = 图上契约、类型检查 = 图良构性验证)。 + - 无 `let` 关键字(`:=` 声明)、无生命周期标注(引用有效性编译器全权推断)、默认借用 + 显式 `move`。 + - 编译时执行由格/图自动推导,`@comptime` 兜底。 +- **二、词法**: + - 注释:`//` 单行、`/* */` 多行。 + - 标识符规则(对照 lexer:字母/下划线开头,字母数字下划线;`lookup_keyword` 之外的即标识符)。 + - 关键字表:37 个,按关键事实清单列出,注明「以 lexer.cr lookup_keyword() 为准」;注 `self` 是关键字、`Self` 非词法关键字、comptime 非关键字、requires/ensures 非词法关键字。 + - 字面量:整数(十进制;进制前缀按 lexer 实际支持写)、小数(`3.14` → dex,DEX_LIT)、字符串(不可变 UTF-8)、字符、`true`/`false`、`()`(unit)、`None`/`Some`。 + - 运算符记号表(关键事实清单里 10 个记号,一行一个或表格)。 +- **三、模块与导入**(压缩自原 2.1-2.7,去「依赖裁剪」等工具链内容): + - `fileid "name"` 文件标识符、`mod` 模块声明、`import math` / `import math : m` / `import @acme math` / `import @acme math : m`。 + - `Path = IDENT { '::' IDENT }`(`::` 文件内路径分隔符)。 + - `_import.cr` 目录级批量导入(继承合并,子目录覆盖,冲突报错)。 + +- [ ] **Step 3: 验证第 1-3 章** + +Run: +```bash +grep -n "类型系统" docs/language-syntax.md # 除「无独立类型系统」表述外不得出现 +grep -cE "^(#|##) " docs/language-syntax.md # 检查标题结构 +``` +Expected: 「类型系统」仅出现在「无独立类型系统」相关句;标题层级结构完整(13 个二级章标题 `## 一、` 到 `## 十三、`)。 + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "docs: 语法文档重写(第 1-3 章:设计要点/词法/模块)" docs/language-syntax.md +``` + +--- + +### Task 2: 类型标注 + 变量声明 + 表达式 + 语句(第 4-7 章) + +**Files:** +- Modify: `docs/language-syntax.md`(替换 Task 1 的占位注释行) + +**Interfaces:** +- Consumes: Task 1 的骨架与第 1-3 章 +- Produces: 第 4-7 章内容;第 4 章的类型表达式语法被第 8-10 章引用(函数返回类型、接口、规约) + +- [ ] **Step 1: 写第 4 章类型标注语法** + +标题用「**类型标注**」或「**类型语法**」,不得叫「类型系统」。内容: +- 类型表达式:基础类型 `int`(整数,精确)/ `dex`(精确小数,默认实数语义)/ `bool` / `string`(不可变 UTF-8)/ `char`(Unicode 标量)/ `unit`(`()`)/ `never`(发散)+ `dyn`(动态类型,链接 `docs/dynamic-typing.md`)+ `Self`(上下文类型,接口/方法签名内)。 +- 复合类型:元组 `(T1, T2)`、数组 `[T; n]`、切片 `[T]`、可选 `T?`、引用 `&T` / `&mut T`。 +- 接口作类型(`PathType`,链接第 9 章)。 +- 标注位置:变量声明、函数参数、返回类型、泛型参数。 +- 标签体系:`mut` / `pub` / `apx`(apx = 近似授权,授权后端把 dex 运算降级 binary64 快路径;与 mut/pub 同族)。 +- `auto` 与 `.`:类型推断占位,两者等价;用于变量与返回类型。 +- 宽度说明一句:机器形状(位宽)由编码层按目标自动决定,未来显式控制走 hw-map——不出现任何宽度类型名。 + +- [ ] **Step 2: 写第 5 章变量声明** + +- `x := 42;` 推断不可变;`x : int = 42;` 显式类型;`x : ., mut = 0;` 标签;`a, b : int = 1, 2;` 批量。 +- `auto` 与 `.` 占位(引用第 4 章)。 +- `move x` 显式移动(默认借用语义,无 let、无生命周期标注)。 + +- [ ] **Step 3: 写第 6 章表达式与运算符** + +- 优先级链按关键事实清单(Assignment 最低到 Primary 最高),表格或层级列表 + 每个运算符一行示例: + - `=`(赋值)、`:=`(声明)、`||`、`&&`、`==`/`!=`、`<`/`>`/`<=`/`>=`、`+`/`-`、`*`/`/`/`%`、`-`/`!`/`&`/`&mut`(一元)、调用/字段/下标/`?`。 + - `as` 类型转换(`expr as T`)。 + - 语句性表达式:block、if、match、loop、for、go、await、unsafe(链接第 7、11、12 章)。 + - `?` 错误传播:只能用于返回 Result 的函数中(Result 是语言内建概念——按定案接口注册表 int/dex/string 原生条目,「错误传播 `?`」写语法行为即可,不写 Result 枚举定义)。 + +- [ ] **Step 4: 写第 7 章语句与控制流** + +- `if` / `else if` / `else`(含表达式用法);`loop`(含 `break`/`continue`);`while`;`for`(`for i in 0..10`、`for item in items`)。 +- `match` 穷尽匹配;字符串常量分支自动哈希跳转表(作为语义说明保留原文档这段)。 +- `return`;`unsafe { }` 块(链接第 12 章)。 +- 所有示例只用 int/dex/string/bool/char + 复合类型。 + +- [ ] **Step 5: 验证第 4-7 章** + +Run: +```bash +grep -nE "u8|u16|u32|u64|i8|i16|i32|i64|f32|f64" docs/language-syntax.md +grep -nE "vec!|HashMap|HashSet|chan" docs/language-syntax.md +``` +Expected: 两组均无输出(宽度类型与标准库示例零残留)。 + +- [ ] **Step 6: 提交** + +```bash +jj commit -m "docs: 语法文档重写(第 4-7 章:类型标注/声明/表达式/控制流)" docs/language-syntax.md +``` + +--- + +### Task 3: 函数 + 接口泛型 + 规约(第 8-10 章) + +**Files:** +- Modify: `docs/language-syntax.md` + +**Interfaces:** +- Consumes: 第 4 章类型表达式语法(返回类型、泛型参数、接口作类型) +- Produces: 第 9 章接口与泛型(被第 12 章 RawRef 与第 13 章 @typeInfo 引用)、第 10 章规约(被第 6 章 `?` 的错误语义支撑) + +- [ ] **Step 1: 写第 8 章函数与方法** + +- `fn name(params) -> Type { ... }`;单行形式 `fn name(params) -> Type = expr;`;`-> auto` 返回推断。 +- 参数 `name: Type` 列表。 +- `impl Type { fn ... }` 方法;`self` / `&self` / `&mut self` 接收者。 +- 示例:`fn add(a: int, b: int) -> int { return a + b; }`、`fn pi() -> auto = 3.14159;`、`impl Point { fn norm(&self) -> dex { ... } }`。 + +- [ ] **Step 2: 写第 9 章接口与泛型** + +- `interface Name { fn sig; ... }` 声明;`impl Name for Type { ... }` 实现。 +- 泛型:`fn first[T](...)` 声明处接口绑定 `[T: Eq]`(拟定语法,直接呈现);实例化时检查接口实现,编译期具体化,无运行期字典。 +- 示例:`interface Eq { fn eq(&self, other: &Self) -> bool; }` + `fn first[T: Eq](list: &[T]) -> T?`(不用宽度类型,`?` 链接第 6 章)。 +- 链接类型方向定案 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(接口 = 图上契约)。 + +- [ ] **Step 3: 写第 10 章规约** + +- `requires` / `ensures` / `old(expr)` / `result`:附加在函数签名后的规约(`fn divide(...) -> int? requires b != 0 ensures ... { ... }`),参与静态检查,不影响运行时性能。链接 `docs/spec-design.md` 与 `grammar/corespec.ebnf`。 +- **where 值约束(拟定语法)**:`fn f(x: int) where x > 0 { ... }`——与 requires 同层;三档语义:常量约束 → 编译错误/通过;符号约束 → VC 义务(验证管线消费);动态约束 → 运行时检查(防「证明不了也不静默」)。与 .corespec 前置条件映射(一条语法,两层消费:checker + VC)。 + +- [ ] **Step 4: 验证第 8-10 章** + +Run: +```bash +grep -nE "u8|u16|u32|u64|i8|i16|i32|i64|f32|f64" docs/language-syntax.md +``` +Expected: 无输出。另通读第 9、10 章检查:接口示例无宽度类型、where 语法形式为 `where `。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "docs: 语法文档重写(第 8-10 章:函数/接口泛型/规约)" docs/language-syntax.md +``` + +--- + +### Task 4: 并发 + 指针 unsafe + @ 内建 + 全文验证(第 11-13 章 + 收尾) + +**Files:** +- Modify: `docs/language-syntax.md` + +**Interfaces:** +- Consumes: Task 1-3 全部章节 +- Produces: 完整文档(最终交付物) + +- [ ] **Step 1: 写第 11 章并发** + +- `go expr`(异步启动,`handle := go f(...)`);`await handle`(阻塞等待;按需自动等待)。 +- `flow name(...) -> T { yield v; }` 长期执行流;`recv()` 接收。 +- 示例:`flow counter(start: int) -> int { ... yield n; ... }` + `f := go counter(0);` + `v := f.recv();`。 +- 只写语法与行为,不写「HDFG 视图」愿景节。 + +- [ ] **Step 2: 写第 12 章指针与 unsafe** + +- `&expr` 取地址(provenance 记录)、`*p` 解引用(验证 offset ∈ [0, len))、指针偏移运算、`cast(addr)` 显式转换。 +- 裸指针自由与 C 同级,编译器通过格/图自动验证安全(链接 `docs/pointer-model.md`)。 +- `unsafe { }` 块:RawRef 使用、外部 C 调用、编译器固有;RawRef 无法逃逸到安全代码。 +- 示例:`p := &arr[0]; x := *p;` 等(无宽度类型)。 + +- [ ] **Step 3: 写第 13 章 @ 内建原语** + +- 三类:元数据查询(`@sizeOf` `@alignOf` `@fields` `@field` `@hasField` `@typeInfo`)/ 编译控制(`@inline` `@unroll` `@section` `@comptime`)/ 安全检查(`@no_bounds_check`,unsafe 范畴)。链接 `docs/at-intrinsics.md`。 + +- [ ] **Step 4: 全文验证(spec 验证清单逐条执行)** + +Run: +```bash +echo "=== 1. 宽度/标准库残留 ===" && grep -nE "u8|u16|u32|u64|i8|i16|i32|i64|f32|f64|vec!|HashMap|HashSet|chan" docs/language-syntax.md +echo "=== 2. 实现状态标注 ===" && grep -nE "已实现|未实现|待实现" docs/language-syntax.md +echo "=== 3. 类型系统概念(仅允许「无独立类型系统」句)===" && grep -n "类型系统" docs/language-syntax.md +echo "=== 4. 章结构 ===" && grep -nE "^## " docs/language-syntax.md +echo "=== 5. 关键字表 ===" && grep -n "37" docs/language-syntax.md +echo "=== 6. 表格断裂检查 ===" && grep -n "| unit" docs/language-syntax.md +``` +Expected: +1. 无输出 +2. 无输出 +3. 仅出现在第 1 章「无独立类型系统」句 +4. 输出 13 个二级标题:一、设计要点 … 十三、@ 内建原语 +5. 「37 个关键字」字样出现于词法章 +6. 无孤立表格行(表格内行 `| unit |` 前后有表头/分隔行) + +- [ ] **Step 5: 通读全文最终校对** + +Read: `docs/language-syntax.md`(全文) +检查:无占位注释残留(`` 全部清除)、每个构造有「语法形式 + 说明 + 示例」、示例可读、无标准库/愿景内容、第 1 章以格形态解释。 + +- [ ] **Step 6: 提交** + +```bash +jj commit -m "docs: 语法文档重写完成(第 11-13 章 + 全文验证通过)" docs/language-syntax.md +``` diff --git a/docs/superpowers/specs/2026-09-01-syntax-doc-rewrite-design.md b/docs/superpowers/specs/2026-09-01-syntax-doc-rewrite-design.md new file mode 100644 index 00000000..afc7470d --- /dev/null +++ b/docs/superpowers/specs/2026-09-01-syntax-doc-rewrite-design.md @@ -0,0 +1,70 @@ +# 语法文档重写设计(language-syntax.md) + +日期:2026-09-01 +状态:已批准(brainstorming 逐节确认) +关联定案:`2026-08-30-type-system-direction-design.md`(类型方向)、`2026-08-16-numeric-types-design.md`(数值类型)、`2026-08-16-platform-abstract-design.md`(平台桥)、`2026-08-23-hw-map-design.md`(硬件映射表) + +## 背景:现状问题 + +`docs/language-syntax.md`(701 行、13 章)混合了「语言设计愿景」与「实际语法参考」两种定位,且多处与最新定案、源码现状冲突: + +1. **宽度类型与定案矛盾**:3.2 节教 `0x1000_u64` / `100_i32` 后缀语法、4.4 枚举示例用 `u8/u16`、八章接口示例返回 `u64` —— 全部与 2026-08-30 width-out-of-language 定案(宽度移出语言)矛盾。 +2. **虚构语法**:七章 `a := vec![1, 2, 3];` —— `vec!` 在源码中不存在。 +3. **自相矛盾**:文末「设计要点」称「无指针操作符(\*, ->)和地址运算」,3.3 指针节却教 `*p` / `&arr[0]` / `cast`。 +4. **关键字表错误**:称「共 35 个」且「self 按标识符解析」——实际 lexer.cr `lookup_keyword()` 为 **37 个**,且 `self` 是关键字(T_SELF)。 +5. **markdown 表格断裂**:3.2 基础类型表被 `dyn` 说明行打断,unit/never 行落在表格外。 +6. **愿景内容混排**:十二章「HDFG 视图」、十三章「π 示例」、5.4 requires/ensures 示例均未实现(源码零出现),与参考内容无区分地混排。 +7. **标准库示例渗入**:8.1 HashMap/HashSet、9.4 通道 —— 语言语法文档不应包含标准库表层。 + +## 定位决策(brainstorming 已确认) + +| 维度 | 决策 | +|------|------| +| 定位 | **定案后的完整语言**:当前实现与未来设计统一呈现,**不标注哪些已实现、哪些未实现** | +| 边界 | **语言本体(语法为准)**:词法、类型标注、声明、表达式、控制流、接口/泛型、规约、并发。不含标准库表层、IR、工具链、平台桥、HDFG 愿景 | +| 未定稿语法 | **文档拟定语法形式**(where 值约束、泛型接口绑定),成为该语法的第一份设计 | +| 风格 | **参考手册式为主,教程式为辅**:每个语法构造 = 语法形式 + 说明 + 示例 | + +## 目标结构(13 章,无「类型系统」概念) + +> Core 没有独立类型系统:类型 = 图标注、接口 = 图上契约、类型检查 = 图良构性验证(见类型方向定案)。文档只有「类型标注语法」的描写,不设类型系统章。 + +1. **设计要点**(短文,**以格形态为标准解释方式**):按三层映射「图 → 格 → 编码」讲(`docs/memory-model-capability-lattice.md` v4 §四 + `2026-08-27-lattice-form-ir-design.md`)——图(`.cir`)= 结构表达(关系空间),格(`.ccr`)= 存在表达(状态空间,存在/条目/共存/驱逐),编码 = 机器形状(hw-map 领域);语义保鲜 = 格形态全量承载类型/语义信息供形式验证直接消费;正确性陈述对全部可能执行成立(非 CFG 路径);无独立类型系统(类型 = 图标注、接口 = 图上契约);无 let;无生命周期标注;默认借用 +2. **词法**:注释、标识符、关键字表(37 个,以 `src/compiler/lexer.cr lookup_keyword()` 为准)、字面量(int / dex / string / char / bool / unit / None / Some)、运算符记号(`*` 解引用、`&` 取地址、`->` 返回类型、`:=` 声明、`..` 范围、`?` 可选与错误传播、`@` 内建前缀) +3. **模块与导入**:fileid / mod / import / `::` 路径 / `@项目名` / _import.cr +4. **类型标注语法**(非类型系统):类型表达式——基础类型(int / dex / bool / string / char / unit / never / Self,另含 dyn 动态类型[设计内容,见 `docs/dynamic-typing.md`])、复合类型(元组 / 数组 / 切片 / 可选 / 引用)、接口作类型;标注位置(变量 / 参数 / 返回类型 / 泛型参数);标签体系(mut / pub / apx);auto 与 `.` +5. **变量声明**:`:=`、`: Type =`、批量声明、move、标签 +6. **表达式与运算符**:优先级链(按 `grammar/core.ebnf` 顺序)、运算符表、调用 / 字段访问 / 下标、`?`、`as`、@ 内建调用 +7. **语句与控制流**:if / else、loop / while / for、break / continue、match、return、unsafe 块 +8. **函数与方法**:fn 声明、参数、返回类型(含 auto)、单行形式、impl、self(&self / &mut self) +9. **接口与泛型**:interface 声明、impl for、泛型声明与接口绑定、编译期具体化 +10. **规约**:requires / ensures / old / result + where 值约束(拟定语法,三档语义) +11. **并发**:go / await / flow / yield / recv +12. **指针与 unsafe**:`&` 取地址、`*` 解引用、偏移运算、cast、RawRef、unsafe 块 +13. **@ 内建原语**:元数据查询 / 编译控制 / 安全检查(详见 `docs/at-intrinsics.md`) + +**删除内容**:HDFG 愿景章、π 示例章、标准库表层(HashMap / HashSet / 通道 / Result 枚举——`?` 运算符本身保留在表达式章)、`vec!` 等虚构语法。 + +## 内容处理规则 + +1. **宽度类型全面消失**:u8/u16/u32/u64/i32 等任何宽度类型名称与后缀(`_u64` / `_i32` / `_f32` / `_f64`)不出现在示例中。接口示例改用 int / dex 返回(如 `fn hash(&self) -> int`)。机器形状归属一句话带过(hw-map,未来显式控制通道)。 +2. **矛盾修复**:删除「无指针操作符」表述;`vec!` 示例删除;关键字数 35→37、`self` 标注为关键字;3.2 表格断裂修复(dyn 定为基础类型之一,按定位不标实现状态)。 +3. **拟定语法**(未定稿,文档给出第一份设计): + - where 值约束:`where <布尔表达式>` 附加在函数签名后(与 requires 同层),三档语义——常量约束 → 编译错误/通过;符号约束 → VC 义务;动态约束 → 运行时检查。 + - 泛型接口绑定:`fn first[T: Eq](...)`,声明处接口绑定 + 实例化检查,无运行期字典。 +4. **示例规则**:全部示例只用语言本体语法(int / dex / string / bool / char / 复合类型 / 可选 / 接口),可读且不依赖标准库与未定语法(where 示例除外,其语法为本文档拟定)。 +5. **实现状态**:全文不出现「已实现 / 未实现 / 待实现」标注(定位决策)。 +6. **要点解释标准**:设计要点与语义解释一律以格形态为标准框架(格 = 存在空间、图 = 关系空间、编码 = hw-map;语义保鲜 = 格全量承载),不用传统的 CFG/编译器视角解释语言行为。 +7. **真源原则**:关键字表以 lexer.cr 为准;优先级与语法形式以 `grammar/core.ebnf` 为准;冲突时 lexer.cr / parser.cr 优先于 EBNF(仓库惯例:实现以源码为准)。 + +## 验证方式 + +- 关键字表逐项核对 `src/compiler/lexer.cr lookup_keyword()` +- 语法构造核对 `grammar/core.ebnf`(差异处加注,注优先指源码) +- grep 检查:文档中无 `u8|u16|u32|u64|i8|i16|i32|i64|f32|f64|vec!|HashMap|HashSet|chan` 残留 +- 无「已实现 / 未实现 / 待实现」字样 +- markdown 表格结构完整(无断裂行) + +## 变更范围 + +仅 `docs/language-syntax.md` 一个文件(重写)。`grammar/*.ebnf`、源码、其他文档不动。 From 2e5c746948e1b16ea4cf9bc8ab1f79bf9daa435d Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 5 Sep 2026 21:10:07 +0800 Subject: [PATCH 006/138] =?UTF-8?q?docs:=20TODO=20=E6=B8=85=E8=B4=A6?= =?UTF-8?q?=E2=80=94=E2=80=94=E9=94=99=E8=AF=AF=E7=A0=81=E4=BD=93=E7=B3=BB?= =?UTF-8?q?=E5=A4=8D=E6=A0=B8=E5=88=92=E9=94=80=E3=80=81asp=20=E8=90=BD?= =?UTF-8?q?=E7=82=B9=E6=A0=B8=E5=AE=9E=E5=88=92=E9=94=80=E3=80=81F11/M-2/h?= =?UTF-8?q?ex=20=E6=BA=A2=E5=87=BA=E6=9D=A1=E7=9B=AE=E8=A1=A5=E6=B3=A8=209?= =?UTF-8?q?/5=20=E6=94=B6=E5=B0=BE=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 0b6f7b1d..c6d4bd4e 100644 --- a/TODO.md +++ b/TODO.md @@ -116,11 +116,11 @@ 来源:`docs/compcert-round4-findings.md`(F1-F20 修复后残留)+ 波 1-3 修复审查产出。F1-F20 已全部修复,以下为范围外/需 IR 形态演进的遗留项: -- ~~**M-2**:字符串索引 `s[5]` 静默 OOB(TI_STR 无检查)~~(2026-08-29 已修:常量下标与动态下标均生成边界检查,字符串读写按字节处理) +- ~~**M-2**:字符串索引 `s[5]` 静默 OOB(TI_STR 无检查)~~(2026-08-29 修变量下标路径 + 字符串读写按字节处理;2026-09-05 #61 收尾补全:常量下标路径 `emit_string_lit_bounds` 覆盖读/写/复合赋值三处——修复前 `s[9]` 常量越界静默;同时修复词法错误被 `parse_all()`/多轮 `tokenize()` 清零吞没的机制,溢出守卫等词法错误现正确上报) - ~~**I-3**:模块别名导入断裂(`import fmt : f` / 模块限定调用生成对伪函数 "import" 的调用)~~(2026-08-29 已修:bootstrap 保留导入元数据并按 alias 解析限定调用;`tests/bootstrap/test_modules.py` 覆盖) - ~~**lexer 字面量解析 2 项**:`2305843009213693952.0` 字面量解析为垃圾值(bi>53 时 pow2i(负数)=1);>18 位整数部分静默截断~~(2026-08-29 已修:宽整数显式报错,宽小数避免负指数幂;`test_dex_type.py` 覆盖) - ~~**Minor-2**:SPAWN 结果存储用 e2_st(rax) 非 e2_store_ret——float 返回值 spawn 存垃圾~~(2026-08-29 已修:IR_SPAWN 按返回类型保存 XMM0/rax) -- **F11 运行时界切片长度**:需 IR 形态演进(slice 类型)——已标注设计项(来源:compcert-round4-findings.md F11 / 语义表 BC7) +- **F11 切片长度**(2026-09-05 现状更新):字面量界已完成——`g_ir_slice_lens` 侧表 + `slice_len_set/get`(ir_gen.cr),切片经 LET 初始化/赋值/字面量界登记传播(F11 注释);**剩余缺口 = 运行时界切片**(low/high 为变量的 slice 无长度跟踪,解引用处 `arr_len_lit_of` 查不到)——来源:compcert-round4-findings.md F11 / 语义表 BC7 - ~~**ccr v5 指令记录 i32 截断** ≥2³¹ 的 s3~~(2026-08-30 已修:`save_ccr` 对所有 i32 指令/region 字段做有符号范围校验,超界直接拒绝写出;`load_ccr` 对各段长度/计数做越界检查;`corearch` 拒绝无效文件大小;`test_ccr_writer_rejects_i32_overflow_inputs` 覆盖 writer guard) - **core_pattern 管道**致陷阱程序 core dump 挂起——CI 建议 `ulimit -c 0`(来源:波 3 测试审查) - ~~**BC-CONST**:interp TI_STR 字符串表索引近似~~(2026-08-29 已修:解释器统一用驻留索引传递字符串,补齐 `str_len`/`str_eq`/`concat`/`int_str`/`chr`/`get_char`/`str_sub` 与字节索引路径) @@ -227,8 +227,8 @@ - **概率性 pass**(probabilistic-pass,2026-07-30 已批准):src/ 零实现,仅文档(`docs/probabilistic.md`)——范式相关,优先级低,待排期 - **硬件映射表**(hw-map,2026-08-23 已批准):无实现——**依赖 crasm 的映射表**(crasm 未实现则链式阻塞)。关联 `docs/superpowers/specs/2026-08-23-hw-map-design.md` - **平台桥抽象**(platform-abstract,2026-08-16 定案):设计定案待实现——语义接口 + 后端实现原则;程序 IO = 流转导器。关联 `docs/superpowers/specs/2026-08-16-platform-abstract-design.md` -- **错误码体系**(error-codes,2026-08-08 已批准):`EC_R_*` 常量在 ast.cr/checker.cr/main.cr 有引用但体系完整度待确认(历史 BC17 曾记录「全仓库无引用」——需复核现状) -- **裸指针 asp 标记**(bare-ptr-model):实现状态待确认(TODO 旧条目「整数转指针标记 asp=1」的源码落点需核实) +- ~~**错误码体系**(error-codes,2026-08-08 已批准)~~(2026-09-05 复核:体系完整——ast.cr 定义 `EC_R_*`,checker.cr:2080/2095 发射 R002(编译期越界),main.cr:148 硬错误路径(R002/TK05/TK06 拦编译),diag.cr `error_cat_prefix`/`pad_diag_num` 完整打印 R001-R004;BC17「全仓库无引用」为历史旧况,已过时) +- ~~**裸指针 asp 标记**(bare-ptr-model)~~(2026-09-05 落点核实:ir_gen.cr:2111-2117 整数转指针 asp=1 且从 `source_ti` 的 type extra 继承/清零,checker.cr:2244-2246 同步——与 pointer-model 设计一致) - **dex 任意精度精确小数未实现**(dex-precision,2026-08-28 审计):设计 = 无上限小数位精确小数;实现 = 定点 S=10⁶(`src/stdlib/dex.cr:3-18`,注释「定点方案(执行时定稿,2026-08-16)」)——6 位小数 + int64 硬上限(加减 ≤9.2e12、乘 |a|·|b| ≤9.2e6);设计意图与实现为语义级偏差。修复方向 = 动态位数表示(任意精度运算,重活,排期) - **apx 降级策略被实现为报错**(apx-degrade,2026-08-28 审计):设计 = 只支持精确的环境直接忽略 apx 标签、走精确语义(优雅降级);实现 = 解释器对 apx 的 I2F/F2I 显式报错(`src/stdlib/dex.cr:32-33`;报错本身是 Task 6 安全修复——替代静默跳过致 SIGFPE,但方向与设计相悖)。修复方向 = 解释器忽略 apx 标签走精确路径。注:apx 结果跨环境可不同(native 走 binary64),为标签显式代价,账本如实记录 - **宽度类型移出语言**(width-out-of-language,2026-08-30 定案):设计决定——int 无上限为默认(已定);宽度(i64/u32/w32 等)不进入语言类型/标签体系,避免第二标签范式(单标签单范式原则);机器形状全部归 hw-map(硬件映射表,`docs/superpowers/specs/2026-08-23-hw-map-design.md`——依赖 crasm,链式阻塞现状不变);apx 保持单一语义 = 精度降级开关,不扩张为宽度标签。三层映射对应:图/格 = 纯数学,编码 = hw-map 领域。语言侧清理(不阻塞,可立即做):ast.cr `T_INT_I8..T_INT_U64` / `T_FLOAT_F32/F64` 死条目移除(勿重编号)、`_f32/_f64` 后缀死路径处理、`1_000` 两编译器分歧复核;v6 规格影响:编码层宽度由目标自动决定,hw-map 为未来显式控制通道(v6 格式规格先行更新) @@ -313,5 +313,5 @@ - ~~sizes.cr: IR_APPROX 无显式条目~~(2026-08-29 已补显式 0 字节条目,与其他注解指令对齐) - ~~泛型+dex 返回类型~~(2026-08-30 已修复/验证:局部 dex 槽位按 `apx` 标签正确区分 binary64 与缩放整数;普通 Core 函数边界统一缩放形式;bootstrap 泛型与 native dex/APX 回归通过) - str_to_f64_bits ~2ulp 截断(保留站点文档化限制):binary64 判别子用 1/3(0.1b+0.2b==0.3 仅 −1ulp 组合恰好落位模式,不可作断言);native APX/FFI/格式化回归已在 WSL 通过 -- ~~INT_LIT 缺 hex/octal/binary 前缀分支(0x1F 等);`1_000` 下划线产生 T_INT(-1) 静默值 0~~(2026-08-29 已修:bootstrap 与自举 lexer 支持 `0x`/`0o`/`0b` 及下划线,并拒绝非法数字) +- ~~INT_LIT 缺 hex/octal/binary 前缀分支(0x1F 等);`1_000` 下划线产生 T_INT(-1) 静默值 0~~(2026-08-29 已修:bootstrap 与自举 lexer 支持 `0x`/`0o`/`0b` 及下划线,并拒绝非法数字;2026-09-05 #61 收尾补强:溢出守卫按 base 校准——固定 i64max/10 阈值对 base 16 无效,`0x8000000000000000` 静默环绕为 i64min;现 16/8/2 分别用 i64max/base 阈值,bootstrap `read_number` 同步前缀进制溢出拒绝,cir_cache 魔数改 signed 十进制同字节模式) - interp 裸 opcode 数字风格(与既有 op == 26 风格一致,非缺陷);`_f32/_f64` 宽度透传死路径(EBNF/inventory 争议点 7 已标注,apx 位宽标注需新发射路径) From 2cd158c011a8df5f2388c071edf4ab08a0871110 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 5 Sep 2026 21:31:19 +0800 Subject: [PATCH 007/138] =?UTF-8?q?fix:=20F11=20=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=97=B6=E7=95=8C=E5=88=87=E7=89=87=E9=95=BF=E5=BA=A6=E5=AE=88?= =?UTF-8?q?=E5=8D=AB=E2=80=94=E2=80=94len=20=E5=8F=98=E9=87=8F=E7=99=BB?= =?UTF-8?q?=E8=AE=B0/=E4=BC=A0=E6=92=AD=20+=20=E8=A7=A3=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E5=8A=A8=E6=80=81=20BOUNDS=5FCHECK=EF=BC=88test=5Fslice=5Fboun?= =?UTF-8?q?ds=207=20=E7=94=A8=E4=BE=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 2 +- src/compiler/ir_gen.cr | 99 +++++++++++++++++--- tests/selfhost/test_slice_bounds.py | 139 ++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 14 deletions(-) create mode 100644 tests/selfhost/test_slice_bounds.py diff --git a/TODO.md b/TODO.md index c6d4bd4e..06c44413 100644 --- a/TODO.md +++ b/TODO.md @@ -120,7 +120,7 @@ - ~~**I-3**:模块别名导入断裂(`import fmt : f` / 模块限定调用生成对伪函数 "import" 的调用)~~(2026-08-29 已修:bootstrap 保留导入元数据并按 alias 解析限定调用;`tests/bootstrap/test_modules.py` 覆盖) - ~~**lexer 字面量解析 2 项**:`2305843009213693952.0` 字面量解析为垃圾值(bi>53 时 pow2i(负数)=1);>18 位整数部分静默截断~~(2026-08-29 已修:宽整数显式报错,宽小数避免负指数幂;`test_dex_type.py` 覆盖) - ~~**Minor-2**:SPAWN 结果存储用 e2_st(rax) 非 e2_store_ret——float 返回值 spawn 存垃圾~~(2026-08-29 已修:IR_SPAWN 按返回类型保存 XMM0/rax) -- **F11 切片长度**(2026-09-05 现状更新):字面量界已完成——`g_ir_slice_lens` 侧表 + `slice_len_set/get`(ir_gen.cr),切片经 LET 初始化/赋值/字面量界登记传播(F11 注释);**剩余缺口 = 运行时界切片**(low/high 为变量的 slice 无长度跟踪,解引用处 `arr_len_lit_of` 查不到)——来源:compcert-round4-findings.md F11 / 语义表 BC7 +- ~~**F11 切片长度**~~(2026-09-05 已修:字面量界原已完成——`g_ir_slice_lens` 侧表 + LET/赋值传播;同日补齐运行时界——`len = high − low` 长度变量登记(侧表编码:≥0 字面量 / ≤−2 长度变量 / −1 清除),沿赋值传播,解引用处发射动态 `IR_BOUNDS_CHECK`(ti=1,interp/ELF 零后端改动);`tests/selfhost/test_slice_bounds.py` 7 用例覆盖——越界读/写/变量下标/空切片 trap,合法访问值与字面量界回归。来源:compcert-round4-findings.md F11 / 语义表 BC7) - ~~**ccr v5 指令记录 i32 截断** ≥2³¹ 的 s3~~(2026-08-30 已修:`save_ccr` 对所有 i32 指令/region 字段做有符号范围校验,超界直接拒绝写出;`load_ccr` 对各段长度/计数做越界检查;`corearch` 拒绝无效文件大小;`test_ccr_writer_rejects_i32_overflow_inputs` 覆盖 writer guard) - **core_pattern 管道**致陷阱程序 core dump 挂起——CI 建议 `ulimit -c 0`(来源:波 3 测试审查) - ~~**BC-CONST**:interp TI_STR 字符串表索引近似~~(2026-08-29 已修:解释器统一用驻留索引传递字符串,补齐 `str_len`/`str_eq`/`concat`/`int_str`/`chr`/`get_char`/`str_sub` 与字节索引路径) diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 65ba78e4..613a548c 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -49,6 +49,72 @@ fn slice_len_set(var_idx: int, len: int) { g_ir_slice_len_count = g_ir_slice_len_count + 1; } +// F11(运行时界切片,2026-09-05 扩展):len 字段编码—— +// >= 0 = 字面量长度;<= -2 = 长度 IR 变量(-2-len_var,避开 -1 清除码);-1 = 已清除/无记录。 +fn slice_len_raw_set(var_idx: int, len_code: int) { + si : ., mut = 0; + loop { if si >= g_ir_slice_len_count { break; } + if r64(g_ir_slice_lens, si * 16) == var_idx { + w64(g_ir_slice_lens, si * 16 + 8, len_code); + return; + } + si = si + 1; } + if len_code == -1 { return; } // 清除无条目 = no-op;负编码(长度变量)允许新增 + grow_ir_slice_lens(g_ir_slice_len_count + 1); + w64(g_ir_slice_lens, g_ir_slice_len_count * 16, var_idx); + w64(g_ir_slice_lens, g_ir_slice_len_count * 16 + 8, len_code); + g_ir_slice_len_count = g_ir_slice_len_count + 1; +} + +// 登记运行时界切片的长度变量;查询返回 len_var(无 → -1)。 +fn slice_len_var_set(var_idx: int, len_var: int) { + if var_idx < 0 || len_var < 0 { return; } + slice_len_raw_set(var_idx, -2 - len_var); +} + +fn slice_len_var_get(var_idx: int) -> int { + si : ., mut = 0; + loop { if si >= g_ir_slice_len_count { break; } + if r64(g_ir_slice_lens, si * 16) == var_idx { + v := r64(g_ir_slice_lens, si * 16 + 8); + if v <= -2 { return -2 - v; } + return -1; + } + si = si + 1; } + return -1; +} + +// 切片长度沿赋值/LET 传播:无条件同步 src 记录到 dst——src 无记录 +// 时清除 dst(防非切片赋值后陈旧长度残留导致误守卫)。 +fn slice_len_copy_to(dst_var: int, src_var: int) { + si : ., mut = 0; + loop { if si >= g_ir_slice_len_count { break; } + if r64(g_ir_slice_lens, si * 16) == src_var { + slice_len_raw_set(dst_var, r64(g_ir_slice_lens, si * 16 + 8)); + return; + } + si = si + 1; } + slice_len_raw_set(dst_var, -1); +} + +// 切片解引用守卫(运行时界长度):s1 = idx 变量/字面量,上限 = 长度变量 +// (IR_BOUNDS_CHECK ti=1 动态上限——与 emit_string_bounds 同机制)。 +fn emit_slice_bounds(arr_var: int, idx_var: int) { + if arr_var < 0 || idx_var < 0 { return; } + lv := slice_len_var_get(arr_var); + if lv >= 0 { emit(IR_BOUNDS_CHECK, -1, idx_var, lv, 0, 1); } +} + +fn emit_slice_lit_bounds(arr_var: int, idx_lit: int) { + if arr_var < 0 { return; } + lv := slice_len_var_get(arr_var); + if lv >= 0 { + t := new_ir_var("_slice_idx", TI_INT); + emit(IR_CONST, t, idx_lit, 0, 0, TI_INT); + emit(IR_BOUNDS_CHECK, -1, t, lv, 0, 1); + } +} + // 数组/切片的编译期长度:TYP_ARRAY 的 extra=size;切片查侧表(字面量界)。 // 未知 → -1(ext_safety 不发射 IR_BOUNDS_CHECK)。 fn arr_len_lit_of(arr_var: int) -> int { @@ -742,10 +808,8 @@ fn gen_expr(node: int) -> int { } else { // dex 槽位形式转换(数值迁移 Task 4:apx 槽存 bits,精确槽存缩放) val_var = dex_store_adjust(target, val_var, right); - // F11:切片长度沿赋值传播(RHS 切片 → 拷贝长度;其他 → 清除) - if slice_len_get(target) >= 0 || slice_len_get(val_var) >= 0 { - slice_len_set(target, slice_len_get(val_var)); - } + // F11:切片长度沿赋值传播(字面量/运行时界长度变量同步;源无记录则清除) + slice_len_copy_to(target, val_var); emit(IR_STORE, -1, target, val_var, 0, 0); } } else { @@ -774,6 +838,7 @@ emit(IR_STORE, -1, target, val_var, 0, 0); arr_len_lit : ., mut = arr_len_lit_of(arr_var); if idx_kind == EXPR_INT { emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); + emit_slice_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_STORE_INDEX, -1, arr_var, val_var, ast_int_val(idx_node), 0); } @@ -781,6 +846,7 @@ emit(IR_STORE, -1, target, val_var, 0, 0); idx_var := gen_expr(idx_node); idx_var = force_if_thunk(idx_var); emit_string_bounds(arr_var, idx_var); + emit_slice_bounds(arr_var, idx_var); if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { emit(IR_STORE_INDEX_VAR, val_var, arr_var, idx_var, 0, 0); } @@ -997,10 +1063,8 @@ emit(IR_STORE, -1, target, val_var, 0, 0); emit(IR_DYN_PACK, lv, val_var, tag, 0, 0); } else { val_var = dex_store_adjust(lv, val_var, val_node); - // F11:切片长度沿赋值传播 - if slice_len_get(lv) >= 0 || slice_len_get(val_var) >= 0 { - slice_len_set(lv, slice_len_get(val_var)); - } + // F11:切片长度沿赋值传播(字面量/运行时界长度变量同步;源无记录则清除) + slice_len_copy_to(lv, val_var); emit(IR_STORE, -1, lv, val_var, 0, 0); } } else { @@ -1027,12 +1091,14 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); arr_len_lit : ., mut = arr_len_lit_of(arr_var); if ast_kind(idx_node) == EXPR_INT { emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); + emit_slice_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_STORE_INDEX, -1, arr_var, val_var, ast_int_val(idx_node), 0); } } else { idx_var := gen_expr(idx_node); idx_var = force_if_thunk(idx_var); + emit_slice_bounds(arr_var, idx_var); if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { emit(IR_STORE_INDEX_VAR, val_var, arr_var, idx_var, 0, 0); } @@ -1880,10 +1946,9 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if declared_ti != TI_DEX { irv_set_type(var, irv_type(val_var)); } - // F11:切片长度沿 LET 初始化传播(s := arr[0..2] → s 带长度 2) - if slice_len_get(var) >= 0 || slice_len_get(val_var) >= 0 { - slice_len_set(var, slice_len_get(val_var)); - } + // F11:切片长度沿 LET 初始化传播(s := arr[0..2] → s 带长度 2; + // 运行时界 slice 同步长度变量——slice_len_copy_to) + slice_len_copy_to(var, val_var); emit(IR_STORE, -1, var, val_var, 0, 0); } bind_local(var_ni, var); @@ -1975,16 +2040,23 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } v := new_ir_var("slice", TI_INT); emit(IR_SLICE, v, arr_var, low_var, high_var, 0); - // F11:字面量界 → 登记切片编译期长度(解引用处 arr_len_lit 用) + // F11:字面量界 → 登记切片编译期长度(解引用处 arr_len_lit 用); + // 运行时界 → 计算 len = high − low 并登记长度变量(解引用处发射 + // 动态边界检查——见 emit_slice_*_bounds)。 if ast_kind(low_node) == EXPR_INT && ast_kind(high_node) == EXPR_INT { sl := ast_int_val(high_node) - ast_int_val(low_node); if sl >= 0 { slice_len_set(v, sl); } + } else { + len_var := new_ir_var("slice_len", TI_INT); + emit(IR_BINARY, len_var, high_var, low_var, OP_SUB, TI_INT); + slice_len_var_set(v, len_var); } return v; } v := new_ir_var("elem", TI_INT); if idx_kind == EXPR_INT { emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); + emit_slice_lit_bounds(arr_var, ast_int_val(idx_node)); if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { emit(IR_LOAD_INDEX, v, arr_var, 0, ast_int_val(idx_node), 0); } @@ -1992,6 +2064,7 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); idx_var := gen_expr(idx_node); idx_var = force_if_thunk(idx_var); emit_string_bounds(arr_var, idx_var); + emit_slice_bounds(arr_var, idx_var); if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { emit(IR_LOAD_INDEX_VAR, v, arr_var, idx_var, 0, 0); } diff --git a/tests/selfhost/test_slice_bounds.py b/tests/selfhost/test_slice_bounds.py new file mode 100644 index 00000000..b44aa4eb --- /dev/null +++ b/tests/selfhost/test_slice_bounds.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +"""F11 切片边界回归测试——运行时界 slice 的越界守卫(2026-09-05 修复)。 + +背景:字面量界切片(a[1..3])长度编译期已知、有越界守卫;运行时界切片 +(a[lo..hi])此前长度丢失——s[k] 越界静默(读到 slice 外、底层数组内的值)。 +修复:ir_gen 为运行时界 slice 计算 len = high − low 的长度变量并沿 +赋值/LET 传播,解引用处发射 IR_BOUNDS_CHECK(动态上限,ti=1)。 + +SIGILL = 132;main 正常返回值 = 进程退出码(低 8 位)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # TODO:125:core_pattern 为 systemd-coredump 管道,陷阱程序(SIGILL)在 + # core dump 写入时会挂起——测试禁用 core dump 让 SIGILL 立即终止。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def build_and_run(source: str): + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(source) + src = f.name + out = src[:-3] # strip .cr + try: + built = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=120, + ) + if built.returncode != 0: + return f"compile-failed: {built.stdout}{built.stderr}" + return subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + finally: + for p in (src, out, out + ".ccr"): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def case(name, source, expect_rc): + r = build_and_run(source) + if isinstance(r, str): + print(f"[FAIL] {name}: {r}") + return False + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc}, got {r.returncode}") + return False + print(f"[PASS] {name}: rc={r.returncode}") + return True + + +def main() -> int: + if not COREC.exists(): + print(f"[FAIL] missing native compiler: {COREC}") + return 1 + # SIGILL(ud2 trap):shell 显示 132,subprocess returncode = -4(-signum) + SIGILL = -4 + ok = [ + # 运行时界 slice 越界读/写必须 trap + case("runtime_bounds_literal_index_oob_traps", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + lo := 1; hi := 3; + s := a[lo..hi]; + x := s[2]; + return x; +} +""", SIGILL), + case("runtime_bounds_var_index_oob_traps", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + lo := 1; hi := 3; + s := a[lo..hi]; + i := 2; + return s[i]; +} +""", SIGILL), + case("runtime_bounds_store_oob_traps", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + lo := 1; hi := 3; + s := a[lo..hi]; + s[5] = 99; + return 0; +} +""", SIGILL), + # 空切片(len=0)任何访问都越界 + case("empty_slice_index_traps", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + lo := 2; hi := 2; + s := a[lo..hi]; + return s[0]; +} +""", SIGILL), + # 合法访问不误伤:值必须正确(s[1] == 30) + case("runtime_bounds_in_bounds_load_ok", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + lo := 1; hi := 3; + s := a[lo..hi]; + return s[1]; +} +""", 30), + # 字面量界 slice 的既有守卫不回归 + case("literal_bounds_oob_still_traps", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + s := a[1..3]; + return s[2]; +} +""", SIGILL), + case("literal_bounds_in_bounds_ok", """ +fn main() -> int { + a : [int; 5] = [10, 20, 30, 40, 50]; + s := a[1..3]; + return s[1]; +} +""", 30), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) From 5028cd0a13e068c4c57278045e83b766e8f58c08 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 5 Sep 2026 21:44:23 +0800 Subject: [PATCH 008/138] =?UTF-8?q?fix:=20core=5Fpattern=20=E5=9D=91?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E5=A4=84=E7=90=86=E2=80=94=E2=80=94trap=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=A6=81=20core=20dump=20+=20CI=20ulimit=20-?= =?UTF-8?q?c=200=EF=BC=9BTODO=20=E5=88=92=E9=94=80=E5=B9=B6=E6=8C=82?= =?UTF-8?q?=E5=9B=BE=E6=8E=A8=E5=AF=BC=E8=BE=B9=E7=95=8C=20pass=20?= =?UTF-8?q?=E6=A0=B9=E6=B2=BB=E8=B4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 3 ++- src/ci/run.sh | 5 +++++ tests/selfhost/test_pointer_safety.py | 11 ++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 06c44413..892085f8 100644 --- a/TODO.md +++ b/TODO.md @@ -122,7 +122,7 @@ - ~~**Minor-2**:SPAWN 结果存储用 e2_st(rax) 非 e2_store_ret——float 返回值 spawn 存垃圾~~(2026-08-29 已修:IR_SPAWN 按返回类型保存 XMM0/rax) - ~~**F11 切片长度**~~(2026-09-05 已修:字面量界原已完成——`g_ir_slice_lens` 侧表 + LET/赋值传播;同日补齐运行时界——`len = high − low` 长度变量登记(侧表编码:≥0 字面量 / ≤−2 长度变量 / −1 清除),沿赋值传播,解引用处发射动态 `IR_BOUNDS_CHECK`(ti=1,interp/ELF 零后端改动);`tests/selfhost/test_slice_bounds.py` 7 用例覆盖——越界读/写/变量下标/空切片 trap,合法访问值与字面量界回归。来源:compcert-round4-findings.md F11 / 语义表 BC7) - ~~**ccr v5 指令记录 i32 截断** ≥2³¹ 的 s3~~(2026-08-30 已修:`save_ccr` 对所有 i32 指令/region 字段做有符号范围校验,超界直接拒绝写出;`load_ccr` 对各段长度/计数做越界检查;`corearch` 拒绝无效文件大小;`test_ccr_writer_rejects_i32_overflow_inputs` 覆盖 writer guard) -- **core_pattern 管道**致陷阱程序 core dump 挂起——CI 建议 `ulimit -c 0`(来源:波 3 测试审查) +- ~~**core_pattern 管道**致陷阱程序 core dump 挂起~~(2026-09-05 临时处理:`src/ci/run.sh` 全局 `ulimit -c 0`;trap 类测试内置 `RLIMIT_CORE=0`——test_slice_bounds / test_pointer_safety。机理:桌面 core_pattern = systemd-coredump 管道,SIGILL 后内核写 core 经管道唤醒服务,拥堵时进程挂起;GitHub CI 容器为文件 core_pattern 无此坑。根治方向 = 图推导边界判定 pass——可静态判定的越界前移为编译期拒绝,缩小运行时 trap 面,见下条目。来源:波 3 测试审查) - ~~**BC-CONST**:interp TI_STR 字符串表索引近似~~(2026-08-29 已修:解释器统一用驻留索引传递字符串,补齐 `str_len`/`str_eq`/`concat`/`int_str`/`chr`/`get_char`/`str_sub` 与字节索引路径) ## 架构规划 @@ -233,6 +233,7 @@ - **apx 降级策略被实现为报错**(apx-degrade,2026-08-28 审计):设计 = 只支持精确的环境直接忽略 apx 标签、走精确语义(优雅降级);实现 = 解释器对 apx 的 I2F/F2I 显式报错(`src/stdlib/dex.cr:32-33`;报错本身是 Task 6 安全修复——替代静默跳过致 SIGFPE,但方向与设计相悖)。修复方向 = 解释器忽略 apx 标签走精确路径。注:apx 结果跨环境可不同(native 走 binary64),为标签显式代价,账本如实记录 - **宽度类型移出语言**(width-out-of-language,2026-08-30 定案):设计决定——int 无上限为默认(已定);宽度(i64/u32/w32 等)不进入语言类型/标签体系,避免第二标签范式(单标签单范式原则);机器形状全部归 hw-map(硬件映射表,`docs/superpowers/specs/2026-08-23-hw-map-design.md`——依赖 crasm,链式阻塞现状不变);apx 保持单一语义 = 精度降级开关,不扩张为宽度标签。三层映射对应:图/格 = 纯数学,编码 = hw-map 领域。语言侧清理(不阻塞,可立即做):ast.cr `T_INT_I8..T_INT_U64` / `T_FLOAT_F32/F64` 死条目移除(勿重编号)、`_f32/_f64` 后缀死路径处理、`1_000` 两编译器分歧复核;v6 规格影响:编码层宽度由目标自动决定,hw-map 为未来显式控制通道(v6 格式规格先行更新) - **类型系统方向定案**(type-system-direction,2026-08-30 定稿):图本体 + 接口统一总纲——图 = 唯一真相层(类型 = 图标注、接口 = 图上契约、类型检查 = 图良构性验证 pass,与指针三 pass 同级);接口注册表(int/dex/string = 原生接口条目,规则内建、公理引用规约层、用户不可实现);where 值约束三档语义(常量→编译错误 / 符号→VC / 动态→运行时检查);泛型 = 编译期接口具体化(无运行期字典);宽度移出语言(见 width-out-of-language)。损失账本(免费午餐债 5 项)+ 演进顺序(where 值约束 → 泛型=编译期接口 → 验证切片 → v6 格形态 → 类型概念收敛,两条根基革命不得同时进行)+ 学术支撑(PLDI 2025 Webs 平行印证、语义子类型 = 完整补偿、ISO TS 6010 provenance 对齐)详见 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md` +- **边界判定图 pass**(bounds-inference,2026-09-05 挂账):在 HDFG 上做下标/切片长度的符号传播 pass(与指针三 pass 同级)——idx 来源链 + slice 长度来源链(F11 侧表传播的图化升级):证明恒安全 → 不发射检查(零成本);证明恒越界 → 编译期 R002 拒绝;证明不了 → 保留运行时检查并带图推导必要性标注。目标:运行时 trap 面最小化(trap = 推导不出才留的兜底,兼作验证证据);核心_pattern 桌面挂起坑的根治方向(见 :125 划销注)。现状基础:pass_before_array_access/ext_safety 检查发射钩子、slice_len 侧表(字面量/长度变量编码)、checker R002 编译期越界 ## 待实现特性 ### 控制流自动惰性(2026-08-09 记) diff --git a/src/ci/run.sh b/src/ci/run.sh index 6b95bc34..e011f529 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -10,6 +10,11 @@ CI_JOB_NAME="${CI_JOB_NAME:-}" set -euo pipefail IFS=$'\n\t' +# TODO:125(临时处理):禁用 core dump——桌面 core_pattern 为 systemd-coredump +# 管道时,陷阱程序(SIGILL)在 core dump 写入会挂起;CI 无诊断价值。根治方向 +# 见 TODO「图推导边界判定 pass」(检查前移编译期,缩小运行时 trap 面)。 +ulimit -c 0 2>/dev/null || true + if [ -n "$CI_JOB_NAME" ]; then echo "[CI_JOB_NAME=$CI_JOB_NAME]" fi diff --git a/tests/selfhost/test_pointer_safety.py b/tests/selfhost/test_pointer_safety.py index 1d3f5f90..ff170bc7 100644 --- a/tests/selfhost/test_pointer_safety.py +++ b/tests/selfhost/test_pointer_safety.py @@ -2,6 +2,7 @@ """Regression tests for pointer provenance, bounds, and unsafe boundaries.""" import os +import resource import subprocess import tempfile @@ -9,6 +10,13 @@ BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +def _no_core_dump(): + # TODO:125:core_pattern 为 systemd-coredump 管道,陷阱程序(SIGILL)在 + # core dump 写入时会挂起——trap 测试禁用 core dump 让信号立即终止 + # (临时处理;根治方向见 TODO:图推导边界判定 pass)。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + def compile_ccr(src: str): with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as source: source.write(src) @@ -50,7 +58,8 @@ def build_and_run(src: str): ) os.chmod(output_path, 0o755) return subprocess.run( - [output_path], capture_output=True, text=True, cwd=BASE, timeout=10 + [output_path], capture_output=True, text=True, cwd=BASE, timeout=10, + preexec_fn=_no_core_dump, ) finally: for path in (source_path, output_path, output_path + ".ccr"): From c779e643caa4252cedef45434e260d586483c4e6 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Tue, 15 Sep 2026 23:19:27 +0800 Subject: [PATCH 009/138] =?UTF-8?q?R2=20=E6=8E=A5=E5=8F=A3=E5=8C=96=20P2b?= =?UTF-8?q?=E2=86=92P6=20+=20=E5=AE=B9=E9=87=8F=E6=89=B9=20+=20fail-closed?= =?UTF-8?q?=20=E9=97=B8=E9=97=A8=20+=20=E7=BC=93=E5=AD=98=E9=9D=99?= =?UTF-8?q?=E9=BB=98=E7=BC=BA=E9=99=B7=E4=BF=AE=E5=A4=8D=EF=BC=88#60?= =?UTF-8?q?=EF=BC=89=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: v6 SYM 归并/REG 坐标化——spec §3.2/§3.5 目标形状落地(vars 并入函数记录声明区、globals 带 type、REG kind/parent/enter/exit/first/last、root_region span 派生函数边界;ELF 发射零变化) * refactor: regalloc-consistency.corespec → src/compiler/regalloc-consistency.cr——定位修正(分配器逻辑契约文档载体,非规约/非编译单元;配套 verify 运行自检在 opt.cr) * docs: regalloc-consistency.cr 重写——去 corespec 语法改 Core 注释契约文档(分配器逻辑契约 + opt.cr 实现映射 + 三层正确性定位 + 缺口挂账) * refactor: .corespec 退役——独立规约格式取消(crasm 同构:规约 = .cr 语法唯一表达);ebnf/spec 文件标注 + CLAUDE.md 同步 + 语法并入 core.ebnf 挂账 * fix: 数据面防御批 1(GC-1/GC-3/GC-4)——entries_coexist 上界校验 + loader REG root span 对 NOD 空间校验 + save var 行序位置级守卫 - GC-1(M-5):opt.cr entries_coexist 补 e1/e2 ≥ g_entry_count 上界守卫返回 0 (槽后零填充/缓冲外越界读 → 判定垃圾或崩溃);新增 cir --inject-coexist-oob 探针 - GC-3(SYM 评审 M1):ccr_io.cr loader NOD 解析后逐函数校验 root region span instr_start+count ≤ instr_cnt(REG 段解析先于 NOD 无法对照;ENT ele>instr_cnt 同风格) - GC-4(SYM 评审 M2):ccr_io.cr save var 行序守卫升级为位置级(vs == 前缀累计 + 末块铺满;计数级 Σ 总量守恒时察觉不到块错位);新增 ccr --inject-var-shift 钩子 - 钩子均隐藏 debug 标志(真实构建路径永不注入,同 check-regalloc inject-* 模式) - 测试:test_live_ranges(GC-1,11→12)/ test_ccr_v6(GC-3/GC-4,6→8),先红后绿 * feat: 定值识别建模扩权——dest≥0 = 定值点(GC 批 2):ALLOC_ARRAY/ALLOC_STRUCT 内存对象诞生 + producer 直写并入版本切分(格式定稿 §4.1);STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH dest 非定值排除(IR 全集勘定);判定②义务边界自动扩权代码零改动已验证;inject-read-gap 迭代序保非 func0 回归面 * test: param_ents=-1 定向测试(GC 批 3)——重定值参数无入参版本条目断言(.ccr SYM param_ents + dump-entries 双通道;对照只读参数 def=-1 条目 id);regalloc-consistency.cr 挂账注记同步(机制括注 + 测试引用);顺手项:v6 spec §4.1 步骤 1 补 carve-out 注记(STORE s1 单列 + 三 op 排除,引 §3.4)+ opt.cr rl_rule2_func「dest 从不读」注释修正(三 op dest = 真实读点,扫列边界如实) * fix(hit): M2 独立小项批——--table × --link/--shared 显式拒绝(stderr+exit 1);未用哨兵字段改存 0(store dst/load s2,修自持截断除 255 vs Python 0xFFFFFFFF 分裂);add 反减 dest-fresh 防御(d==s1 落 0 事件旧路径);"events emitted" 计数改名(表模式总结语义准确);REX 注释 W+R→W+R+B;M1 plan 表文件格式示例同步真源(0x4D 29 DA/角色)+ core-x86.toml 头部注释修正 * docs: TODO M2 挂账勾销(小项批 3/4 完成;负值常量池载体仍挂) * docs: int 无上限语义定稿(int-unbounded-semantics)+ 溢出错误编码层归位(注释更新,消息/错误码不动) Task A: 新建 docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md—— int = 数学整数公理化接口(无溢出概念,溢出非语义事件);经典 64 位机器字 = 快路径投影,超界 = 编码层事务(静态区间证明免检/推导不出才检查提升,表示策略 归 hw-map 远期);溢出类错误(lexer 字面量守卫/CCR i32 字段校验)重新定位 = 编码层限制错误;与 dex 精确两线不混(int = 语义层无上限、dex = 表示层工程); int 公理可被 .cr requires/ensures 规约对照验证(CompCert 精神推广到原语层)。 引用 type-system-direction / width-out-of-language / 三层映射 v4 / bounds-inference 挂账 / spec-design §9.4。 Task B(保守落实):lexer.cr str_int_literal / bootstrap lexer.py 前缀进制守卫 注释更新为编码层限制语义(语言无上限、经典投影受限;两编译器差异 = 投影形状 差异非语义分歧);ccr_io.cr save_ccr 守卫注释 v5→v6 修正 + 文件格式域归位。 错误文本与错误码体系不动(无测试断言该文本;消息改动需全量重建 + 并行会话 共享工作副本,风险>价值)——注记方案入 spec §七。 * docs: int 多字表示设计草案(后端策略 B:64 快路径 + 溢出扩展 2-limb)——语义层零改动,编码层投影;决策点 D1-D6 待拍 * docs: int 多字 M1 实施计划——add/sub 溢出全链(tagged 槽/jo 慢路径/2-limb/分配器只栈);D4 常量多字推迟 M2(IR_CONST 64 槽=格层表示需 v6 常量段) * feat: int 多字 M1 Task 1——tagged 槽框架(增量移植:识别/tag 区/栈布局;HIT 发射保持完整)+ test_mw_task1 * fix: int 多字 M1 Task 1 评审修复——mw_setup_tags 单遍前向→不动点闭包(循环回边携带 tag 态)+ B' IR_STORE 定值拷贝边(变量行入集)+ test_mw_task1 补 s6 红用例/s7 帧区分(v6 oracle) * feat: int 多字 M1 Task 2——快路径溢出检测发射(tagged int add/sub 后 jo rel32 → 函数尾慢路径块骨架) - instr.cr:IR_BINARY int ADD/SUB 分支 e2_alu 后、e2_st 前发射 e2_mw_jo (0F 80 rel32,6B——块附函数尾,rel8 ±127 在 >127B 函数体不可达;单遍 发射目标后知 → 恒 rel32 唯一正确)+ g2_init 清零计数(ret_patch 同款 逐函数段)+ mw_int_arith_jo_needed(表模式门控复用) - elf.cr:Phase 3 每函数尾声 ret 后——jo 记录回填(块位置后知)+ 慢路径 块骨架(ud2 占位,溢出 = 确定性 SIGILL;Task 3 替换为 2-limb 修正代码, jo 记录届时扩展 dest 等现场);HIT 表路径对 tagged int add/sub 整条排除 - sizes.cr sz_jo=6;globals/dyn_arr g_x86_mw_jo_* + grow_mw_jo_patch - untagged 快路径零字节变化(z 用例与改动前快照指令流逐字节一致;E8 call 位移例外——stdlib 函数布局合法后移) - test_mw_task2.py:v6 oracle 期望 jo 数(不动点 tag 集)+ main 区域 0F 80 扫描(计数/目标==函数尾块/ud2)+ t3 大函数 rel32 必需性(12KB 体首个 jo 距块 >127B)+ t2 溢出 rc -4(SIGILL,跳转可达性)+ 零 diff 基线比较 * feat: int 多字 M1 Task 3——慢路径 + 2-limb 表示运行时:jo 后数学修正(add 溢出高 limb = CF?-1:0 / sub = CF?0:-1——sbb 推演 + 4 万样本验证)、alloc(16) 2-limb 堆对象写 [lo,hi]、dest 值槽回存(含 O1/O2 reg 形态)+ tag 置位(g2_tag_off)、jmp 回快路径 store 后(jo 记录扩展 pos/dest/is_sub/resume;每站点独立函数尾块;rsp 16 对齐 call alloc)+ test_mw_task3(syscall3 原始 16B 通道验全 128,CF/op 判别矩阵)+ test_mw_task2 块形状更新 * feat: int 多字 M1 Task 4——消费者适配(tag 读路径 + tag 卫生):tag 卫生 = 定值点写 tag(add/sub 快路径落值清 0 + IR_STORE/IR_LOAD 拷贝运行时 tag 传播 + prologue 参数保存清 0——无帧入口清零,行模型定值点全集论证);消费者读路径 = return 解 2-limb 指针取低 limb(exit 低 8 位 = 数学值)+ 128 位比较(高 limb 带符号/低 limb 无符号)+ 128 位混合链式 add/sub(快操作数 sar 63 扩展、进位/借位链、结果可装 64 位降级快值保持 2L 非零不变量、hi 出 {0,-1} 域仍精确——码域界论证 <2^66 无错误路径);每站点函数尾 2L 块 + 快路径 tag 检查(0F 85 rel32 回填,jo 同款);表路径门扩展(tagged 比较落旧路径)+ 评审 Minor REX docstring 3 处误修;+ test_mw_task4(16 用例 x O0/O1/O2:return/比较判别矩阵、链式 128 流、卫生红用例) * fix: int 多字 M1 Task 4 评审 Critical——fits-64 检查编码 0x4D→0x49(0x4D 39 D3 错编 = cmp r11,r10——reg 字段误指 r10;0x49 39 D3 = cmp r11,rdx = hi vs sar63(lo) signmask):假 fit(hi==lo、hi∉{0,−1})静默降级截断(链达 −2^64−2 → −2 / 2^64+1 → 1,活体实证)→ 一字节修复 + sar63→cmp r11,rdx 恢复活(死代码态随修复消除)+ 判别回归 d1/d2(syscall3 16B 通道全 128 断言——评审确认唯一判别形态;红 6/6→绿 6/6,既有 18 用例 54/54 不受扰);spec(int-multiword-backend-design §5)补 return 出逃边界句(M1 内 return-2L 到内部调用方 = 确定性截断低 64 wrap,全 128 return = M2);Minor:报告数字订正 16→18/54、IR_STORE/IR_LOAD s1<0 尾分支注释、块体积注释(最坏 ~160B/rel8 局部 ≤~60B)、卫生论证锚进 mw_setup_tags 注释;+ test_mw_task4 判别回归 2 用例(20 用例 x O0/O1/O2 = 60 组全绿) * docs/task5: int 多字 M1 Task 5 裁决——D6 tagged 只栈不实施(确认 + 测试加固):现状实证 O2 下 tagged 变量(含运行时 2L 态:循环携带/参数行/双 2L 对象)确被 CAG 分配寄存器(rbx/r12-15),O1 恒全栈(alloc_registers 门 = opt≥2);D6 排除动机(多字不可驻单寄存器)被 D1a 表示消解(驻留 = 快值或堆指针,128 载荷在堆,tag 帧字节)——reg 形态 = 单 seam(g2_slot/g2_tag_off)结构保证 + task4 60 组 O2 绿实证,排除零收益有代价 → 零代码改动(分配器/发射不变);+ test_mw_task5(3 用例×O0/O1/O2 = 15 组:O2 元断言 tagged∩REG_ASSIGN≠∅ 于 2L 溢出函数 + O1 元断言 REG_ASSIGN=0 + 行为绿/16B 通道——裁决可执行形态,翻案则红)+ instr.cr 消费方接口注释 D6 定案 + plan(Task 5 执行记录/D6 修订/Task 1 偏差注——评审 M3 同步)+ spec §6 D6 定案条目 * docs/task6: int 多字 M1 收官——验证 + 回归 + 文档收尾:+ test_mw_task6(8 边界用例 × O0/O1/O2 = 24 组:i64max±1 恰好一步越界 / 上边界三步链 + 降级回 i64max 再越界 / 下边界邻域 −2^63 快值不溢出 + add/sub 连续越界 + 回升降级 / sub 形式 i64max− −1 / 上下边界双站 + 2L vs 2L 高 limb 判别 / max 边界 fast↔2L 比较真值表双分派 + EQ/GE −10 哨兵 / min 边界同 hi 低 limb 无符号判别 / 循环携带越上边界——期望值 Python 独立计算,16B 通道验全 128);收官全量回归绿(mw1-6 + hit_table 11/11 + compile + backend_bootstrap stage1→3 byte-identical + ccr_v6 9/9 + region_cfg 22/22 + slice_bounds 7/7 + corec check)+ O2 自举重建;plan(状态行 M1 完成 + Task 6 执行记录 + 偏差/风险四项逐条处置:jo 修正已验(8 万样本 + 判别矩阵)/保守 tagged 集代价记录/D1c=M2/arena 生命周期已核/return 低 8 位 → 16B 测试钩子)+ spec backend-design(状态定稿 + §8 M1 完成范围 + M2 挂账 9 条)+ int-unbounded §六挂账 2 注记 M1 实例落地 + TODO 新节(M1 完成 → M2 挂账) * docs: M1 收官补账——零 diff 基线 CI 化入 M2 挂账(Task 6 评审)+ plan jo rel32 括注同步(3 处)+ hi∈{0,-1} 放宽注记 * docs: M1 spec §8 M2 挂账补 10/11——零 diff CI 基线化(Task 6 评审指派)+ hi∈{0,-1} 放宽注记(编号修正) * docs: 寄存器分配移后端设计草案——编码层资源决策归位(corearch 数据面+分配+判定;.ccr opt_meta 简化;纯机械映射表述取消);决策点 R1-R5 待拍 * docs: regalloc 移后端 Task 1——opt.cr 按层切分盘点(归属/依赖/调用面清单入库)+ CSE 产物零效果实证 + D-1/D-3 拍板点;实施计划入版本库 * refactor: regalloc 编码决策层纯搬移入 corearch——新建 src/arch/linux/ld/regalloc.cr(opt.cr 189-1737 保真:数据面 compute_live_ranges/entries/共存 + CAG alloc_registers + 判定 verify/注入钩子,逐字搬移待表述清理)+ corearch 闭包接线 + get_ir_var_name 移 dyn_arr.cr(corearch 闭包无 ir_gen.cr,判定打印随迁;双侧共享位);行为零变化(build 绿 + compile/ccr_v6 回归绿) * feat: regalloc 移后端切换——分配+判定归位 corearch(R1a/R2/R3 落地;D-1=Y ENT 出格式、D-3=b 栈共享停用) corearch:load 后 O2 自算 alloc_registers + regalloc_verify_all(emit 前,违反=编译错误,成功静默);表模式跳过(M1 直线路径无 O2 组合验证);调试通道随迁同名 flag(--dump-entries/--dump-coexist/--check-regalloc/--inject-* 三态 dispatch + 新 --dump-regassign 逐对输出) corec:opt.cr 1940→312 行(数据面/共存/判定/注入/CAG alloc/pass_stack_share 迁出删除;留 AST 折叠 + pass_cse + optimize_all 死码摘 alloc 引用);main.cr O2 段与 cir 调试通道摘除;dataflow lower 尾 compute 摘除;ccr_io save 恒空(ENT 0 条/SYM func 与 REG first/last -1/param_ents -1/opt_meta 0——loader 空表语义已支持,零 version bump) 测试:test_live_ranges 通道改道 corearch(断言期望零变化 13/13);test_mw_task5 元断言改 --dump-regassign(O2 tagged∩REG_ASSIGN≠∅ 于 2L 溢出函数、O1 恒空——reg 形态裁决在 corearch 自算下保持);test_ccr_v6 重写(ENT/opt_meta 恒空断言 8/8,conversion/param_ents 载体消亡测试改/删) 验证:O2 行为等价全锚点绿(exit 42/55/20/14/22/30/31/6/15/7 + 16B 全 128 通道);回归全绿 mw1-6 + compile + backend_bootstrap stage0-2 + ccr_v6 8/8 + region_cfg 22/22 + slice_bounds 7/7 + hit_table 11/11 + bootstrap 3 套 + selfhost 其余套件 偏差注:Task 2/3/4 因 corec 闭包互锁合并为单切换提交(纯搬移提交先行可独立评审);CSE 实证无产物效果(NOD O0/O1/O2 逐字节同——R5 门语义留 Task 5 表述) * docs: regalloc Task 5+6——表述清除与 CLI 语义(R5)+ spec/plan/TODO 收尾 Task 5:regalloc.cr 判定区头/镜像/alloc 区头过时注记(.ccr 传输 meta、corec 不含后端文件等 5 处)→ corearch 自算自消费表述;instr.cr get_reg_for_var 头注(saved in .ccr v3+ → O2 自算填充,O0/O1 恒空);globals.cr 区间/条目表填充方注(opt.cr → regalloc.cr + D-1=Y 恒空);regalloc-consistency.cr 参考实现指针 → src/arch/linux/ld/regalloc.cr + 负编码数据模型表述清理 + 缺口注记(pass_stack_share 停用 / param_ents 载体消亡 / GC-1 已修);corec/corearch -O 帮助文案(O1 = CSE corec 进程内、O2 = 分配+判定 corearch——corec build 透传);opt.cr 头注释 = 剩件清单;「纯机械映射」src 侧零残留(project-book 哲学表述校对挂账 TODO) Task 6:spec R1-R5/D-3 拍板记录 + 实施完成状态(§2-§7 更新);plan 状态行 + Task 1-6 执行记录(纯搬移/切换两提交评审线 + 互锁偏差说明);TODO 新节(完成 → 挂账五条) 验证:改动后 build 绿;行为回归面 = 切换提交已全量绿(注释/文案零行为面) * docs: HIT M2 设计定稿——运行时核事件集 + 全表驱动收口(specs/2026-09-07-hit-m2-design.md):方案 1 拍板(4 数据事件保持 + 扩展 jump/branch/call/ret/extern = 9 事件运行时核;schema v2 投影模板语言;数据化边界 = 单指令编码、序列算法保留;运行时条目最小描述 x86 一档);范围 = A+B 全量、验收含自举;M2a-d 分段(事件集/降低全 op/发射收口/运行时+纯表自举)+ 验收链(模板对照→suite→组合→自举两段) * docs: HIT M2 实施计划定稿(plans/2026-09-07-hit-m2.md)——Task 0 盘点锚点(形态族/schema 定稿/序列边界/op 映射三清单驱动后续)+ M2a(schema v2 加载/解释器新形态 + 事件流注入测试通道解耦/jump·branch·call·ret·extern 模板)+ M2b(控制流标签回填/调用降低/合成规则完备 + suite 混合过渡验收 + 自举冒烟透传 --table)+ M2c(形态族分批数据化 byte-identical + emit 汇合消灭混合)+ M2d(运行时条目参数化/extern 解锁组合验证/纯表自举终验/文档清账);风险注记 + 表模式恒 O0 约束 * docs: HIT M2 Task 0 盘点——形态族/schema v2 定稿 + 序列算法边界 + IR op 映射三清单(计划锚点回填) * docs: HIT M2 Task 0 修复——评审 findings(A3 mov rcx,r11 字节 49 89 CB → 4C 89 D9 反转修正;A1 opcode 十进制 → 0x 十六进制统一;R1 帧公式去 mw_frame_size 加项(tag 偏移 -(vc*8+1+k) 同步);R2/Task 5 里程碑引用 15 → 14(运行时条目参数化 = Task 14);清单 hit 层 seam 函数具名) * feat: HIT schema v2——投影模板语言扩展 + 运行时核事件声明(M2a Task 1) * fix: HIT M2 Task 1 评审修复——step 小节解析越界修正 + 多 proj step 小节 fixture(复审闭环) * feat: 模板解释器 v2 形态 + 事件流注入测试通道——REX/ModRM/SIB/disp/imm/rel(M2a Task 2) * docs: 格层边界定稿——判据(G1-G4)+ v6 审计裁决(NOD/ENT/REG/边表)+ corearch 转换器契约 C1-C6(格→编码投影器定位、实例化边界推广、M2/HIT 判据裁决) * docs: v7 格形态设计定稿——真图载体(边界 spec P1-P3 修复·载体革命级): 边表必落配方闭合 + ENT 主干化(v6 §4.1 规则执行化)+ 调度=投影 + op 字典显式遗留 + 归一化双通道 + 行为判据(v7 重建 ≡ v6 链);两段式第一段,字节格式/实施计划后置 * docs: v7 字节格式定稿——真图载体两段式第二段: NOD 36B(邻接 first_edge/edge_count) + EDG 段 8B×n(数据/state 前向边) + ENT 28B 恒空→实记录 + 拓扑不变量校验(回边 = 操作数非边);cir_cache v13 邻接先例,实施计划后置 * docs: v7 格形态实施计划(2026-09-09)——Task 0 盘点锚点 + Task 1 v7 骨架(version 7/NOD 36B 邻接/EDG 段写读同改,v6 读退役)+ Task 2 ENT 主干化(corec 产实记录 = regalloc compute_entries 镜像基 + loader 激活 + SYM/REG 回填)+ Task 3 收官(test_ccr_v7 全量迁移/全量回归/自举重建/文档);行为等价判据 = 回归全绿 + stage 链 byte-identical;CSE 疑点非修复项注记 * docs: corearch 重写设计定稿——范式无关小内核 + 实例定制(决策点 3): 内核三组件(语义对象模型/判定分配引擎族/实例注册契约——regalloc-cache-mapping 判定四条 = 首个成员)+ x86 实例组成(调度重建/lower/实例算法族/实例数据 = HIT 表归位)+ 现模块归属裁决表(regalloc 拆分语义侧 vs CAG)+ 演进序(v7 → 内核抽取 → x86 实例化 = M2 复启接续);判据 = 行为等价 + 投影正确性(C4) * docs: v7 Task 0 盘点——EDG 内容期望清单 + CSE 实测注记 + ENT 规则对照 + 落盘序核对(计划锚点回填) * docs: v7 计划引文修正(Task 0 评审 Minor 2-6)——save_ccr 尾界 :636、arena 补丁位 ir_gen.cr:1711/:2287、cir_cache 恢复区段精确化、ARENA_RESET GAP 标注、BOUNDS_CHECK 文件名 * feat: v7 格式骨架——version 7 + NOD 36B 邻接 + EDG 段必落 + df 播种修复(写读同改,v6 读路径退役) * docs: v7 编译器缺陷跟踪注册——elf.cr 巨函数尾代码生成缺陷(TODO 预存 Bug #4: 机制/子系统假设/gdb 证据/repro/推迟批准)+ v7 计划风险注记(Task 1 越界配套裁决记录) * feat: ENT 主干化——corec 产实记录(v6 §4.1 规则执行化)+ loader 激活 + SYM/REG 条目回填 * feat: v7 收官——测试族迁移(V7File walker 全量)+ cache-hit 回归 + ENT 负分支覆盖 + 全量回归 + 自举重建 + 文档同步 * docs: v7 format spec §5 测试族行同步(Task 3 评审 Minor 3——test_ccr_v7 23/23 已取代 v6 族,无 v6-vs-v7 链对照) * docs: corearch 内核抽取实施计划(2026-09-10)——Task 1 语义侧纯搬(ent_kernel.cr: 数据面/共存/规则①② 判定 + meta 读通道随判定; CAG/meta 写入留实例)+ Task 2 写侧单源化(compute_entries_v7 镜像消除, 内核进双 concat)+ Task 3 注册契约最小面(实例声明表 + 表驱动分派)+ Task 4 收官; 行为零变化判据 = test_live_ranges 13/13 + test_ccr_v7 23/23 byte-identical * refactor: 内核抽取 Task 1——语义侧搬移 ent_kernel.cr(判定引擎 + 数据面纯搬,零语义改动) * docs: 内核抽取计划修订(Task 1 评审)——Task 2 Files/Step 1 携带 RPT_MAX + ir_op_kind_name 迁入(Important)+ LOC_HOME_BASE 裁决修正三处残留清理(Minor 3) * refactor: 内核抽取 Task 2——写侧单源化(compute_entries_v7 镜像消除,双转录 R5 收敛)+ RPT_MAX/ir_op_kind_name 迁入内核 * fix: 内核抽取 Task 2 补漏——src/compiler 自举单元 import ent_kernel(full-bootstrap N06/SIGSEGV 同类闭合)+ globals 表头注同步 + 回归面补全 * feat: 内核抽取 Task 3——注册契约最小面(实例声明表 + 表驱动分派,行为不变) * feat: 内核抽取收官——全量回归 + 自举重建 + 蓝图步骤 2 完成注记 + cir cache 指纹缺陷挂账(TODO #5) * docs: 内核组件完备化设计定稿(步骤 2.5)——① 语义对象模型(对象面 API + 调度重建移实例)+ ② 判定中立化(登记表通道 + 位置域声明化, LOC_HOME_BASE 出内核)+ ③ 注册契约完整(资源域代数/③④ 形式声明不实现/双向输出面)+ 中立性静态 guard; 行为零变化判据 * docs: 内核组件完备化实施计划(步骤 2.5)——Task 0 盘点锚点(重建段依赖面/region_of_nod 定夺/登记接入面/判定读 meta 面/guard 设计)+ Task 1 对象模型(对象面 API + 调度重建移实例)+ Task 2 判定中立化(登记表通道 + 位置域声明化)+ Task 3 注册契约完整(声明扩展/③④ 形式声明/输出面)+ Task 4 收官(中立性 guard + 全量回归);行为零变化判据 * docs: 内核完备 Task 0 盘点——重建段依赖面/region_of_nod 定夺/登记接入面/判定读 meta 面/中立性 guard 设计(计划锚点回填) * docs: 内核完备计划 Minor 修正(Task 0 评审)——邻接偏移 +28/+32(s1 8B)、g_opt_meta 提及 7 注释+5 读、meta_reg_for_var 删→体改写不删(注入探针 :254/:261 同步)、中立性 guard 前置 Task 2 Step 1(红验证不丢) * feat: 内核完备 Task 1——语义对象模型(对象面 API + NOD 对象存储 + 调度重建移实例 build_linear_schedule) * fix: 内核完备 Task 1 补正——ld/main.cr 活入口注记重写(I-1)+ EDG 守卫常量化(M-3)+ 设计 §1.2 配方查询注记(M-1) * docs: main.cr 活入口注记行引修正(复审残留 Minor——:152/:23/:56/:63 → :158/:29/:62/:69, 前插 +6 漂移) * docs: 内核完备域参数化废弃修正(用户原则——内核零寄存器/域分类概念)——设计 §2.2 重写(位置 = 不透明整数 + home 面 = 条目字段独立互斥遍; 合成 = 实例侧事务)+ §3.1 删 home_base/reg_domain 字段 + 计划 Task 2/3 删 kern_set_loc_domain/域接线步 * docs: 内核完备计划重组——全量中立化波(Task 2 A 通道 compute 系改 NOD 对象面 + 写侧 populate / Task 3 B 通道登记表 + LOC_HOME_BASE 移除 / Task 4 输出面中性化 + 配方查询补体 + ③④ 声明 / Task 5 收官);审计结论 F1-F5 入设计 §4.5 + 写侧载体裁决 * feat: 内核完备 Task 2——A 通道中立化(compute 系改 NOD 对象面 + 写侧 populate,内核零线性流引用) * feat: 内核完备 Task 3——B 通道中立化(登记表通道 + LOC_HOME_BASE 移除,内核零位置编码/零实例布局读) * feat: 内核完备 Task 4——输出面中性化 + 配方查询补体(nod_inputs)+ 注册契约完整化(③④ 声明) * feat: 内核完备收官——中立性 guard 全绿 + 全量回归 + 自举重建 + 文档同步(步骤 2.5 完成) * docs: 内核完备计划头部重复 Tech Stack 行清理(控制器编辑残留) * docs: 设计 spec 评审 Minor 1/2 修正——§1.2 nod_inputs 交付取代注记 + §7 判据引用锚点修正(§4.1-4.4 → §4 判据 1-2)+ CI 聚合层注记 * refactor: 语义内核目录分层——ent_kernel.cr 迁入 src/lattice/(与 src/kernel 验证内核命名区分;双 concat/回退链/_import/guard 路径同步) * fix: ld 自举单元补 import lower_to_core——N06/N01 静默未定义闭合(c138c44c 同类;入口能力分歧挂 TODO #6) * docs: TODO #6 评审措辞修正(旗标计数 6 非 7 /「机制在但 CLI 未接线」精确化)+ 补静默未定义类守卫建议(③) * docs: x86 实例化设计定稿(步骤 3)——实例 = 三轴组合(架构 x86_64 × 格式 ELF × OS Linux):三轴目录(src/arch/x86_64 + src/format/elf + src/os/linux)+ 表数据迁架构轴(hit 引擎保持)+ 序列算法文件化(frame/callseq/tag2l/syscall/entry)+ 构建清单分段守卫 + 寄存器分配切分判定表(需求推导内核候选/物理选择实例)+ spill·驱逐能力演进预留(双向契约三段形状)+ 演进波次(结构/参数化/能力) * docs: x86 实例化波 1(结构波)实施计划——Task 0 锚点 + Task 1 三轴搬迁+组合根(src/targets/x86_64-linux)+清单分段守卫(零抽取)+ Task 2 entry 整搬 + Task 3 frame 抽取(帧公式双源合流)+ Task 4 tag2l 整搬 + Task 5 callseq 抽取(五函数 SysV 三处同源合流)+ Task 6 syscall 抽取 + Task 7 收官;H1-H7 风险注记 * refactor: x86 实例化波 1 Task 1——三轴目录搬迁 + 组合根(src/targets/x86_64-linux)+ 清单分段守卫(零抽取) * fix: 波 1 Task 1 补正——project-mode 构建 error 门接入 run_checked(TODO #6 ③ 事发层闭合)+ CLAUDE.md 路径同步 + TODO #7 死文件登记 * docs: TODO #6 ③ 残留面清点补全 + TODO #7 碰撞集/引述精确化(波 1 Task 1 复审遗留)+ elf.cr 头注路径同步 * refactor: x86 实例化波 1 Task 2——emit_start/emit_start_size 整搬 src/os/linux/entry.cr(OS 轴首个文件) * docs: 波 1 Task 2 评审遗留——entry.cr 头注耦合分类修正(g_call_main_pos 本文件写)+ sizes.cr 单源守护注补 entry.cr(H2 类)+ TODO #7 第二实例登记(活文件双源,结构性防护非删除) * refactor: x86 实例化波 1 Task 3——frame.cr 抽取(帧公式双源合流单入口 + prologue/epilogue 函数化) * refactor: x86 实例化波 1 Task 4——tag2l.cr 整搬(mw 族纯编码 + tag 表 owner 携行; frame↔tag2l 同轴双向引用裁定注记) * docs: 波 1 Task 4 评审遗留——frame.cr pf_frame_overhead 预存双计显式注记(波 2 清, 无发射影响)+ sizes.cr tag2l 同源化声称软化(值一致尚未同源——e2_mw_jo 字面量 6) * refactor: x86 实例化波 1 Task 5——callseq.cr 抽取(参数/栈/返回/调用序列五函数,SysV 三处同源合流) * docs: TODO #8/#9 注册(波 1 Task 5 评审裁决)——前端 ≥18 形参静默误编译类(高优先级,含 7B 清理 runtime 覆盖收口条件)+ extern >6 int 参语义缺口(callseq.cr:45 指针落地) * refactor: x86 实例化波 1 Task 6——syscall.cr 抽取(syscall3/4 内置体,Linux syscall 约定;扫描段留位加注) * docs: TODO #10 注册(波 1 Task 6 评审 Important)——elf.cr 三处手写 syscall 序列(mmap/clone/exit,syscall1/5/6 形,格式轴内)OS 轴收编面,波 2/实例 B 前必办 + elf.cr 扫描段注补交叉引用 * test: x86 实例化波 1 收官 carry——syscall4 套件持久覆盖(tests/suite/syscall4_test.cr,os.cr wait4 r10 第四参径) * refactor: x86 实例化波 1 收官——全量回归 + 自举重建 + syscall4 套件覆盖 + 文档同步(波 1 结构波完成) * docs: 波 1 Task 7 评审修复——Task 6 提交哈希抄错修正(94d1ac15359→94d1cac15359 ×3)+ 清单八段化/elf.cr 锚点 877-886/构建脚本段注时态 * docs: 语言面收窄裁决定稿——定长数组「退役类型身份、保留表示提示」(A+B 融合,用户拍板)+ 宽度语言侧清理三项 + 交叉执行序(波 R1 收窄批 → R2 接口化主设计 → R3 映射侧与实例化波 2/3 合批) * docs: TODO/README crasm 退役同步——专节标退役(原里程碑划销/保留备查)+ README 汇编层与语言面两行状态更正(对齐 docs/crasm.md 2026-09-05 已废弃状态行) * docs: 语言面收窄 波 R1 实施计划定稿(plans/2026-09-10-language-surface-narrowing-r1.md)——五任务(定长注记/宽度死条目/词法面收窄/全局初始化修复/收官)+ 侦查证据链(root cause = 初始化缺失)+ 行为变化面与零变化判据 * docs: 语言面收窄 R1 Task 1——定长数组「表示层概念」注记(ast/checker/parser 七处,零行为改动;语义归处 = product/序列+长度约束/F11,语法保留为内联容量存储表示提示) * docs: R1 计划 Task 3 改写——数字字面量「不支持 _ 分隔符」两编译器统一响亮报错(用户裁决 2026-09-10;消灭 1_000 静默 0)+ bootstrap 既有测试同步点 * docs: R1 计划补 diff 校验规范式样(Task 1 实现者实测:jj diff 默认 color-words 无 + 前缀 → grep 校验恒真空通过;ugrep 式样报错;fish noclobber) * docs: R1 计划 Task 4 补边界——无初值切片 `[T]`(长度 0)不注入序言(保持 BSS 零=空切片;空切片解引用属独立 null 陷阱类) * docs: R1 计划 Task 3 修正——lexer 诊断前缀是 `error: ` 非 `error[XX]`(实测 0xZZ),测试断言改为消息标记;Task 5 增登记「lexer 诊断前缀不一致 → 守卫 error[ 扫描面零覆盖」 * refactor: 语言面收窄 R1 Task 2——宽度死条目移除(ast.cr 77-86 token kind + W_* 值域,墓碑注释勿重编号)+ parser 两处死分支清理(lexer 从不发射,引用面归零) * docs: R1 计划 Task 2 Step 4 判据精确化——墓碑注释自身含标识符字样,判据按「代码引用零命中」(实现者实测) * docs: R1 计划 Task 5 收 Task 2 评审遗留(M1 parser 注释恢复 + M3 文档面随迁清单,含 numeric-migration-inventory:148 过时断言按实测修正) * docs: R1 计划 Task 4 补 cir 缓存交互前置——函数命中缓存不走 ir_gen_func(注入点)+TODO #5 跨重建不失效 → 判据前必须清缓存并记录该交互 * fix: 语言面收窄 R1 Task 3——数字词法收窄(_ 分隔符与宽度后缀一律响亮报错:消灭 1_000 静默 0 误编译;bootstrap/self-hosted 双侧对齐;grammar + bootstrap 测试同步;新增词法对照测试) * docs: R1 计划 Task 5 收 Task 3 评审 Minor 登记(诊断重复 4 遍 / 1._5 与 1. 边缘分歧 / 0x_ 措辞优先级) * fix: 语言面收窄 R1 Task 4——全局初始化修复(聚合/非常量全局降级为 main 序言 IR 序列:alloc+逐元素存+写槽;解释器补标量常量初始化阶段)——修 139 崩溃与静默 0;同面另修三处根因:ELF 字段访问全局基址(e2_load_var 规约,原读栈垃圾)/解释器 callee 内联缺聚合族 opcode/callee 返回值暂存槽覆写首个全局;类型别名型聚合全局一并覆盖;新增双路径测试(15 例) * docs: R1 计划 Task 5 收 Task 4 评审发现(枚举族 callee 内联缺口 Important / 嵌套别名一层解析 / 测试 clean_cache 返回码检查 / 报告措辞) * docs: 语言面收窄 R1 收官——全量回归 11/11 + 自举全链 cmp IDENTICAL/N06=0 全绿 + spec/TODO/台账同步 + test_compile 清单陈旧路径修正(存在性断言)+ Task 2 遗留清账(parser 行尾注释 / 伪代码与盘点文档随迁,含 analysis.cr:895 行实测修正) * docs: R1 计划收官修正——Step 2 自举口径(corec2/corec3 链在 src/ci/run.sh full-bootstrap,非 test_backend_bootstrap)+ 32 复选框补勾(Task 1-5 全执行) * docs: R1 终审收尾——TODO #11 扩写为 callee 内联缺 18 opcode(枚举/裸指针/切片/边界检查族,含终审复现件)+ 全局初始化机制条补缓存交叉引用 + spec §1.3 括注与实现对齐(内联表示未兑现=R3)+ 测试 pid 后缀防并发互踩 * docs: 接口化主设计轮(R2)设计定稿——语义基座(类型=值集合/包含判定/穷尽性=补集空性)+ 双轴接口注册表(本质条目·横切接口·用户契约)+ 类型项层与语义子类型引擎 + 检查器四步迁移 + 五项能力落点(定长退役/泛型具体化/impl 重定义/穷尽性)+ .ccr TYPE·IFACE 载体段 + P0-P5 分期与判据 + 现状事实底座(含 2026-09-10 侦查 file:line) * docs: R2 P0 实施计划定稿(plans/2026-09-10-r2-p0-type-engine.md)——五任务(类型项表+构造 API+自测通道 / NNF+DNF 规范化 / 判定引擎三态+原子互斥+μ memo+预算守卫 / 反例+穷尽性+递归参数化用例 / 收官)+ 清单三处注册 + checker 零改动与零产物变化判据 * docs: R2 P0 计划修缺陷——索引探针改 tt_mod 非负取模(i64 向零截断下负哈希得负下标 → 扩容后去重静默失效;Task 1 压力测试实证 grew.dedup_* 全灭) * docs: R2 P0 计划补附加验证纪律(禁长 sleep / 同缺陷至多 2 轮迭代 / 计划代码非免检——负哈希取模越界实证) * feat: R2 P0 Task 1——类型项表(DAG + 哈希去重)+ 构造 API + selftest-types 自测通道骨架(checker 零改动) * docs: R2 P0 Task 1 收尾——计划三处修正(用例下限 MIN_CASES=5→Task 4 抬 32 / 扩容函数自持 type_terms.cr 不入 dyn_arr.cr 共享面 / 三条实现约定:键不含哈希·零初值 ready 位·无 XOR 用 FNV-1 加法)+ Task 4 增索引扩容守门用例 + TODO #14 登记(bootstrap 非字面量全局初值静默 0,实测确认) * feat: R2 P0 Task 2——NNF(否定下推/De Morgan/双重否定)+ DNF 规范化(∩→∪ 分配律,两支全展开)+ 形态判定(is_literal/product_chain/dnf)+ 5 例规范化自测(10/10) * docs: R2 P0 计划 Task 2 修分配律骨架缺陷(只展开一支 = 静默丢项;自测 norm.distributed 实证) * feat: R2 P0 Task 3——语义包含判定引擎(三态 1/0/-1 + 原子类互斥公理 + 字面蕴含 + μ 展开代入 + 假设-判定 memo(预算耗尽结果不缓存)+ 预算守卫 + 可空/不相交/等价)+ 13 例判定自测(23/23) * feat: R2 P0 Task 4——反例(witness = A\B 规范化项 + 可空过滤)+ 穷尽性(补集空性 + 遗漏 witness)+ 递归/参数化用例 + 索引扩容守门;修两处引擎缺陷:μ 展开须走 memo 入口(余归纳终止)、字面矛盾规则收窄(正×负异类不空——原版致 witness 恒 -1);MIN_CASES 抬至 32(38/38) * docs: R2 P0 收官——spec §9 P0 标 ✅(落点/提交链/未覆盖面)+ TODO #15 登记(引擎落点 + 4 项未覆盖面 + 4 条实现期实证教训) * fix: R2 P0 终审修复——3 Critical + 4 Important:① ¬P ⊆ ¬X 方向反(改 X ⊆ P)② memo 表满开放寻址死循环(装填因子守卫 + 重建重放 + 查找计预算)③ -1 被吞成 0 并污染缓存(字面蕴含上抛 + 预算重置清 memo + witness 区分 -1/-2)④ tt_is_dnf 不认 n≥3 析取 ⑤ AK_NAMED 不给确定答案(三态 + 未覆盖面置位)⑥ 规范化计入预算(2^n 爆炸可截断)⑦ ¬μ 不静默改写为 μ¬(保留字面);另修 grow_tt_index 定容未含「≥2×项数」(自测守门用例实证的重放死循环);用例 38→47(Critical/Important 各配回归例) * fix: R2 P0 复审 Minor 收口——N1 预算重置连 memo 容量一并清(grow_ty_memo 早退致清空承诺失效)+ N2 预算窗口扫描守「exhausted⇒-1」不变量 + N3 补 witness/exhaust 的 -2 未知例 + N5 未覆盖面断言前显式重置 + N4 文档同步(spec 49 例 / TODO #15 三态与 -2 / 测试注释);用例 47→49 * docs: TODO #15 补未覆盖面⑥(memo 重建 × 进行中态无实测覆盖——复审 ⚠️ 登记) * docs: R2 P1(影子对拍)实施计划定稿——桥接层(ti→类型项,N 不入身份)+ type_equal 包装(8 站点标注)+ 分类计数(AGREE/OLD_STRICTER/OLD_LOOSER/UNKNOWN)+ CLI 通道(两态产物逐字节不变)+ 三档语料差异清单 + 收官 * feat: R2 P1 Task 1——影子桥接层(checker ti → 引擎类型项,含 N 不入身份断言) - src/compiler/ty_shadow.cr:sh_term_of_ti(ti → 引擎类型项)+ per-ti 缓存(16B/条 开放寻址,装填因子守卫 + 重建重放 + 写回前重探)+ sh_tuple_to_product + sh_map_hits/entries - 原生映射按**语义**逐项对应(TI_BOOL=2/TI_STR=3 vs AK_STRING=2/AK_BOOL=3 下标序不同—— 计划注释「前 7 项 1:1」不成立,按下标直通会 bool↔string 静默错标,守门用例 str_ak/bool_ak) - TYP_TUPLE 实读布局:data=字段数、extra=g_gen_apply_data 起始下标(计划文的 g_tuple_* 不存在) - 自测 49 → 67 例(桥接 16 例 + 扩容/重放 2 例);零行为变化(ptr_arith 产物与 R1 基线逐字节相同) - 注册:_import.cr / build_selfhost_native.py(corec 清单,checker 段后)/ tests/selfhost/test_compile.py * docs: R2 P1 计划修 3 处骨架缺陷——AK/TI 数值不 1:1(bool/str 下标互换,须语义分派)+ 桥接缓存须装填守卫/重放/回写前重探 + TYP_TUPLE 实读布局(data=字段数/extra=g_gen_apply_data 起始);并裁决 TY_DEX_S→AK_DEX、TYP_DYN 过宽近似→Task 3 单列 * docs: R2 P1 计划收 Task 1 评审 Minor(M1 native 分派改 get_type_data 显式比对 / M4 头注点明 AK-TI 下标互换)——并入 Task 2 落地 * docs: R2 P1 计划收 Task 1 评审完整版(M2/M3 转 Task 2 执行者 / M5 &T vs &mut T 差异 Task 3 单列 / M6 骨架代码补装填守卫·重放·无守卫重放版) * feat: R2 P1 Task 2——影子判定挂点(type_equal 包装 + 8 站点)+ 分类计数 + CLI 通道(两态产物逐字节不变) - checker.cr: type_equal → type_equal_core(6 处内部递归一并改名)+ 包装 type_equal (旧判定照常返回;--type-shadow 开时 sh_compare 只观察);8 个外部决策点各加 sh_site_begin(id)(727/964/977/992/1232/1426/1818/2151,id ↔ 语义全表见 ty_shadow.cr 头注) - ty_shadow.cr: sh_compare(三态分类:agree/old_stricter/old_looser/unknown,前后各 ty_budget_reset(200000) 不污染)+ 256 槽差异环形缓冲(满则只计数)+ sh_report 摘要行 + sh_dump_write 条目转储(TSV:site/t1/t2/old_ok/kind);头注点明 AK_* 与 TI_* 下标 2/3 互换 (M4)并勘误骨架站点标签(3/8) - ty_shadow.cr M1: sh_native_ak 改读类型表本体(kind==TYP_BASE 且 data==TY_* 码), 消除「init_types 行号序恰与 TI_* 码序一致」的隐式依赖 - globals.cr: g_shadow_on/site/total/agree/old_stricter/old_looser/unknown + 环形缓冲 3 全局 - main.cr: --type-shadow / --type-shadow-dump 旗标 + sh_finish(check/build/cir/ccr/run 共用; check 路径实测生效);build_selfhost_native.py: corelsp 清单补引擎+影子层(否则 checker 引用悬空) - 判据:OFF/ON 两态产物与 /tmp/r1t4_base_bin 三份 sha256 全等(95084e7b…d475); 自测 67/67;回归 36/36 套件绿 * docs: R2 P1 计划收 Task 2 落地——站点表实读修正(无 match 路径/6 不可达/8=EXPR_ASSIGN)/ Task 3 未知桶拆两因(不可翻译 vs 引擎 -1)/ Task 4b 挂账清零(M4 注释与 corelsp 清单、>1024 重建用例、既有 @inline rc=139 登记) * test: R2 P1 Task 2 评审补强——M2 ptr class 断言 + M3 native 分派全表覆盖(row 7 dyn 陷阱守门) Task 1 评审补强项(M2/M3)落地到 type_selftest.cr(M1 已在 844cec6c 落地): - M2: bridge.ptr_ak —— TYP_PTR 整支 class 断言(原 bridge.ptr_inner 只断参数链, 整支误译成 AK_SEQUENCE/AK_REF 也会绿;对照 bridge.ref_inner 已断 class) - M3: native 分派(kind==TYP_BASE 且 data==TY_* → sh_base_ak)全表覆盖,M1 后是主路径 且此前零用例: · bridge.native_row_table —— 行 0..7 逐行对照 data 语义 → AK(含 bool↔str 不互换),不抽样 · bridge.native_dyn_kind_branch —— **row 7 陷阱守门**:TI_DYN 行 kind=TYP_DYN 但 data=0(== TY_INT)→ 断言 sh_native_ak(TI_DYN) == -1(先判 kind 后比 data 才是对的, 先比 data 会把 dyn 静默译成 AK_INT)且项本身 = AK_DYN · bridge.native_dex_s —— TY_DEX_S 占位行(row 8)M1 后走 native 路径,两侧同 = AK_DEX - 负控实证(/tmp 副本,非仓库文件):把 sh_native_ak 改成 data-first 后重建 → 14 例红 (含 bridge.dyn_ak 得 0=AK_INT、native_dyn_kind_branch、native_row_table)→ 守门用例 具备判别力,不是恒绿 - 判据复跑(新编译器):selftest 71/71;OFF-IDENTICAL / ON-IDENTICAL; tests/suite 31 程序两态扫 27/27 IDENT(4 SKIP = 2 既有 @inline rc=139 + 2 预期失败 fixture) - 三二进制哈希:corec 77d20bb2…c420 / corearch、corelsp 同前次(注释与测试增量,零 codegen 影响) * docs: R2 P1 Task 2 评审落地——Task 3 增 Step 0(未知桶拆两因,先改码再采数)+ Task 4b 更新(核销①/补 type_terms.cr:73 陈旧注/站点 4 恒 agree 限制)+ TODO #16 登记(嵌套 fn 声明 rc=139,最小复现与两版归属证据) * docs: R2 P1 Task 3——影子对拍差异清单(三档语料计数 + 分类样本 + 归因与裁决建议)+ Step 0 未知桶拆两因(kind=3 桥接缺口 / kind=0 引擎负值,摘要增拆因子段与站点直方图) * docs: R2 P1 Task 4 收官清单(Step 4c)——Task 3 评审 4 Minor(findings 精度/5 字段/关态开销注释+早退/kind 名)+ 5 条 TODO 登记(F1 P2 硬前置/F2 裁决/F3/F4/仓库卫生)+ harness git 拦截缺口登记 * docs: R2 P1 Task 4 收官——全回归 14 套件绿(selftest-types 71→72:第二次重建守门用例 + 负控有牙)+ 两态逐字节 IDENTICAL(build/check 双路径,sha256 95084e7b…d475)+ 自举 CMP_IDENTICAL/N06=0/冒烟 42;Task 3 评审 4 Minor 收口(M1 findings 精度 + 2 行未提交哈希修正核实/M2「前 6 字段」→「前 5 组 key=value」3 处/M3 sh_site_begin 关态早退 + 注释如实改写/M4 dump kind 名 unknown_engine 须知)+ 5 条 TODO 登记(F1 P2 硬前置/F2 裁决/F3 checker.cr:1053/F4 泛型后续形参/仓库卫生)+ harness 缺口登记(block-git 复合命令可绕过,只登记未改)+ Step 4b(type_terms.cr:73 陈旧注更正/corelsp 自 Task 2 起必链引擎层)+ spec §9 P1 ✅ 落点 + TODO #17(开关默认值与产物影响/裁决归属) * docs: R2 P1 终审 4 Minor 收口——M-1 站点 6 去留裁决(保留挂点待 P2:落 TODO #17 + findings §8)/ M-2 findings §10 复现注释 71→72(+Task 3 时点说明)/ M-3 §3.1 加「Task 3 时点」注(自指语料漂移量化:main.cr 3964→3967、_import.cr 3844→3847、ccr_io.cr 4177→4180,Task 4 实测;非自指档抽检不变)/ M-4 globals.cr + ty_shadow.cr「关时连读都不发生」限定为「除 g_shadow_on 自身外」(M3 早退后的事实);复验 = 三 guard clean + selftest-types 72/72 + 两态 OFF/ON 与基线逐字节同(sha256 95084e7b…d475) * docs: R2 P2a(逐点替换批 a)实施计划定稿——F1 同名 TYP_NAMED 建表去重(用户裁决:根治)+ F2 数组长度转常量档长度约束(用户裁决:不留静默缺口)+ type_equal 判定替换(legacy 保留对拍)+ 对拍复跑差异归零/站点覆盖同报;P2b(iface 查表 + res_type_node 合一)另立 * feat: R2 P2a Task 1(F1)——同名 TYP_NAMED 建表去重(唯一分配点 + 侧表守卫/重放/重探;读取点读名字故语义不变) * docs: R2 P2a 计划收 Task 1 落地——用例数回填 77 + Task 3 增三条必测项(同名泛型应用跨站点探针 C-1 / 自指语料 .ccr 实测 C-5 / unknown 计数与站点覆盖同报) * docs: R2 P2a 计划收 Task 1 评审——Task 3 增负控(同名不同实参不得判等)+ 管线内断言(C-4);Task 4 增登记(.ccr 与 cir 缓存态相关,判据前必须 clean-cache) * docs: R2 P2a Task 1 评审 3 Minor 收口(纯注释)——C-3① named_dedup_reset 可达性纠错(reset_frontend_state→check_all 每请求 + 长驻进程 str_intern 下标稳定 ⇒ 删之即静默错型,非「不可达防御」)/ C-3② 负键分支理由改写(bump 不置零 → 任意 ti,非特指 ti=0)/ ③ probe 注释 ret→slot 对齐 * feat: R2 P2a Task 2(F2)——数组长度 N 迁为常量档长度约束(身份去 N,拒绝语义保持:异长仍报错、不静默通过) 实现:① type_equal_core 的 TYP_ARRAY 分支去 N(身份与引擎面一致:桥接 AK_SEQUENCE 参数链不含 N);② 新增具名约束 array_len_constraint_ok(沿结构对应位置下钻:数组元素/指针/引用/切片/元组字段/泛型实参——N 不在顶层位也拦住);③ 8 个判定点经 type_compat_strict(身份 ∧ 长度约束)显式补检,-1(长度约束违反)走专属措辞(码不变:TF01/TA01/TC02 门语义不动),type_equal 调用次数/位置不变(影子站点采样面保持可比)。判据:selftest-types 85/85;ELF-IDENTICAL(95084e7b…d475);.ccr 冷缓存三语料 IDENTICAL;P1 语料影子对拍 agree=100%(含自指 main.cr 4029 判定、old_stricter=0);探针 D/G 形态复现:异长报 error[TF01]「Array length constraint not satisfied」、同长通过。 * docs: R2 P2a 计划收 Task 2 评审——Task 3 增两条硬性(N 面验收由行为探针承担:F2 后影子对 N 失明 / N 不得回身份);Task 4 增自指 .ccr 冷缓存对比 + M2 用例 * docs: R2 P2a Task 2 评审 M1 收口(纯注释)——array_len_constraint_ok 头注「同形」限定:仅指递归位覆盖、非比较项相同(REF 的 extra/mut 与 TUPLE/APPLY 元数·基型行号归身份面、约束有意不重复;PTR 的 extra/asp 身份亦不比 = 反向不对称) * feat: R2 P2a Task 3——type_equal 判定替换(快路径 + 引擎语义判定;未知回落 legacy 并计数,不静默)+ 对拍复跑差异归零 + 站点覆盖同报 - checker.cr:type_equal 判定权移交引擎(type_equal_engine = 快路径 t1==t2 / 桥接 sh_term_of_ti / ty_equiv 三态 / unknown 与桥接失败回落 legacy 并分列计数);旧实现保留为 type_equal_legacy (影子对照物 + 回落实现,数组分支 Task 2 起 N-free,P5 删);type_equal = 判定入口 + 影子包装 (对照物切 legacy);array_len_constraint_ok 仍是长度面唯一判定点(8 站点全经 type_compat_strict)。 - globals.cr:g_replace_unknown / g_replace_bridge(init_types 归零,与行号空间同生命周期)。 - ty_shadow.cr:影子分类语义 = 引擎 vs legacy(替换门);[type-shadow] 摘要行尾追加两回落计数。 - main.cr + checker.cr:C-4 管线内断言 --verify-named-dedup(侧表↔res_type_node 三组检查,默认关)。 - type_selftest.cr:+3 用例(88/88)= N 不回身份 / unknown 回落计数 / 桥接回落计数。 - tests/selfhost/test_named_dedup.py:C-4 断言常驻判据。 判据:ELF 逐字节 95084e7b…d475 IDENTICAL(开/关两态)· 对拍 71 文件 24796/24796 agree (old_stricter/old_looser/unknown/replace_* 全 0;站点 1/2/4/6 零命中同报)· 全回归 23 套件 rc=0 · 自举 corec2/corec3 CMP_IDENTICAL + N06=0 + 冒烟 42 · .ccr 4 语料(含自指 main.cr)IDENTICAL · 收紧清单空。 * test: R2 P2a Task 4 收官——M2 用例(REF/SLICE 下钻位 + 站点 0/-1 措辞分派,双负控突变有牙)+ 自指 .ccr 冷缓存直接采样 + F1/F2 划销与 P2a 落地登记(含 F5/opt.cr:177 + .ccr 缓存态同族) * fix: R2 P2a 终审 Critical——桥接缓存随类型表重置失效(长驻进程行号复用致两个不同类型被判等,静默漏报) 机制(本批引入):Task 3 让判定路径**无条件**调 sh_term_of_ti(此前仅 --type-shadow 下),而 init_types() 只清类型表 + named_dedup 侧表、未清桥接缓存(key = ti 本体)⇒ corelsp 每请求 reset_frontend_state → check_all → init_types 复用行号时,陈旧 ti→term 命中即返回 = 不同类型判等。 评审实证:同 URI 两次 didOpen(只换 a 的元素型 int→bool)→ 第二次 diagnostics=0(应 1 条)。 - ty_shadow.cr:新增 sh_map_reset()(cap/entries/hits 归零,惰性重建;同 named_dedup_reset 先例); 头注失真更正——「--type-shadow 关时除 g_shadow_on 外连读都不发生」自 Task 3 起不成立, 改写为「只观察 = 不写 checker 判定状态/不改判定结果/不写产物,非零写入」+ 缓存失效契约。 - checker.cr:init_types() 调 sh_map_reset()。 - type_selftest.cr:+2 用例(95/95)= t3c.map_reset_clears_bridge(计数)/ t3c.no_stale_term_after_reset (行为:行号复用不得返回陈旧项,元素 AK int→bool)。 - tests/selfhost/test_lsp.py:+test_double_open_bridge_cache_reset(TESTS 第 10 项)。 - TODO.md §24:Critical 修复记录 + 同族清偿登记(桥接缓存 + type_equal_legacy = 调试结构进生产路径, P5 前清偿)+ F2 4/6 下钻位缺突变控制 → 覆盖清单;「3 处」更正为 5 个文件、「23 套件」改称已跑集。 负控(修复前必红):LSP 用例在修复前二进制上 lsp suite: FAIL (1/10)(仅新用例红,V2 diagnostics=[]); 自测用例突变控制(副本删 sh_map_reset 调用)93/95、两例 FAIL。 复验:selftest-types 95/95 · ELF 开/关逐字节 95084e7b…d475 · .ccr 4 语料(含自指 main.cr)IDENTICAL · 自举 CMP_IDENTICAL + N06=0 + 冒烟 42 · 全仓 38 套件 rc=0(含 test_lsp 10/10)· 对拍 71 文件 24853/24853(T1-T3)全五档 27046/27046 agree,old_stricter/old_looser/unknown/replace_* 全 0。 * docs: 语义安全路线图 + 9 份设计 spec 起草完成(用户裁决:全部起草;S-B 一族一份)——共享契约(7 属性轴 + 义务四档 + 确定性轴族/不确定性=显式请求 + policy 即数据 + 三类不可消)+ S-A 基础设施(含 C1-C4 规范性约定:单调展开/属性写右侧/⊥ 非真/双负控)+ 五族策略(注入/授权/信息流/围栏/状态版本)+ S-C 性能无实现承诺 + S-D 解释·预测·增量 + S-E 汇合语义(四形态图级定义 + 物理⇏语义降级义务 + select 重定);含跨 spec 交叉检查(S-B1→S-A 健全性约定、S-B5/S-E 独立互证 extern 效应无保证序通道) * docs: 效应/纯度修正实施计划(P0 插队批)——真纯度计算(保守起点+调用图不动点+SCC 保守)替换乐观常量 + state 链分类表补全(IR_CALL_EXTERN + spawn/yield/hotpatch/dyn_dispatch)+ 判据重定(边集语义断言取代 .ccr 字节同旧);关键决策 = lazy 判定解耦(保住 ELF 逐字节不变判据) * docs: 语义安全 9 spec 收敛轮(用户裁决 ★10 全照推荐 + 两簇评审 findings 全收)——A 簇: 轴真源 D1/放行声明统一 schema D2/新增 C5 非退化性(右项非 ⊤ + 正例非 ⊥ + 假 unknown 分因)/装载期校验 D6/效应清单唯一 D7 + 8 Minor; B 簇: 成本模型五字段 D4/纯度事实前提改写(标为前置缺陷并指向修复批)/汇合点收窄为跨执行流 + 负控 D10/deadline 仅诊断 D8/S-D3 判据可达化 + D_effect join 三值/边界=合法显式源 D19 + 6 Minor * feat: 效应纯度修正 Task 1——真纯度计算(全程序 IR 面 + 调用图不动点 + SCC 保守)替换乐观常量;state 链迁至 IR 生成后重建(lazy 判定冻结保发射面) - checker.cr: compute_all_purity(store 族/效应 opcode/传递闭包/SCC 保守不动点; 不可解析调用与 unsafe 区判不纯)+ src_func_of_ir/df_func_of_node/fi_ispure_of - dataflow.cr: df_create_node/sg_pop 的链连接迁至 df_replay_state_chain(成品图重放, 循环终止规则整块复刻);df_state_finalize = 真纯度 + 链重建(两个管线调用点) - 泛型:实例由自身体算 + 源 = 实例合取(monomorph 登记 g_purity_inst 侧表; brief 的继承写入在该时点会抄到乐观默认值,会把有效应实例错标纯) - lazy 判定与其输入(checker 乐观默认,IR 生成期)原样冻结 ⇒ 发射面逐字节不动 - cir 缓存版本 14→15(快照不再含链边,旧快照 + 重放 = 重复边) - 新自测通道 selftest-purity(纯度 13 例 + 链语义 4 例)+ tests/selfhost/test_purity.py - test_compile.py 源清单补新文件(清单漂移 = resolver Undefined name)+ 构建清单同步 * docs: 语义安全 spec 收敛批 v3(第二轮闭环,A+B 两线全部遗留收口)——A 线: N1 未声明面限定「已声明域内声明不完整」/ N2 §3.1 schema 补第四族(S-B4 kind=mint)+ 四族填法 / N3 integrity 首消费方改「暂无对侧确认(登记)」/ N5 族级例外限定 + ⚠️2 declassify 通道=kind 投影; B 线: N1 Critical 汇合判据改锚语义(前驱间无依赖路径 ∧ 各自可达并发/边界根——原 region 身份判据经实测恒假、执法空过)+ 非空断言 + §4.2 现状断言改正 / N2 本地编号统一 L + 路线图 §4 编号对照表 / N3 S-C 切片靶对象重定(循环不变纯节点;实证 thunk 双路 eager 直通 ⇒ 「≥2 次使用」无收益)/ N4 S-D4 与 S-A D_external 来源列对齐 / N5-N8(返回面/可观测面收窄/清单唯一口径/v7 段位); 纯度事实回填指引留待在飞批 Task 4 * feat: 效应纯度修正 Task 2——state 链分类表补全(单一真源 = purity_op_effect;D7 效应清单收敛为一份)——df_connect_state 的 opcode 级判定改调 purity_op_effect,一并覆盖计划五 opcode(IR_CALL_EXTERN/SPAWN/YIELD/HOTPATCH_ROUTE/DYN_DISPATCH)+ 只进了纯度侧的 IR_STORE_PTR(裸指针写)与 IR_AWAIT(spawn 同步);载荷级区分经核不需要(分类按 opcode,s1 不参与);selftest-purity 17→26 例(红态 3 例精确失败 extern/spawn/yield:touch 0 vs 3)——验收例 effect 双调全入链 / pure_add 零入链;判据 selftest-types 95/95 · test_purity PASS · test_ccr_v7 24/24 未重锁 · test_compile PASS · ELF 逐字节 95084e7b…d475 IDENTICAL(发射面零泄漏) * test: 效应纯度修正 Task 3——判据重定(边集语义断言取代 .ccr 字节同旧)+ test_ccr_v7 期望表实测未变不重锁 + TODO 判据条目 - 期望表重锁判据 = **实测**:clean-cache + corec ccr PROBE_SRC → V7File 解码 → data 41/state 13 逐集合 == EXPECT_DATA/EXPECT_STATE(相等 True),源码域内唯一 IR_CALL = node 35 pure_add 且 kind=1 入边 = 0 ⇒ Task 1 预测成立 → **不重锁、不改表**(只加注释说明)。 - 新增边集语义断言三条(各带正/负控,全部 .ccr 层——判据的产物面): ① 可证纯调用(pure_add + 泛型纯实例 id[int])⇒ 无 kind=1 入边;负控 = 同文件 effect 调用必有入边; ② 效应调用四类(store 体 / extern / 不可解析 builtin load64 / 间接 IR_DYN_DISPATCH)⇒ 必有 kind=1 入边;负控 = 同文件 pure_add 无入边; ③ 每函数链独立(全文件 kind=1 边零跨函数 + 非退化守卫:329 条链边 / 15 函数);负控 = byte mutation(main 内一条 kind=1 边 to_nod 改写到后一函数区间,保前向不变量)→ 同判据函数必须报出恰好该违规([] → [(f,t')])。 - 咬合实证(/tmp/corec2 = Task 1 前编译器,同 fixture 同谓词):① RED(id[int] 被链指入,入边 [32]——monomorph 未写 fi_ispure 读 0 的既有缺陷面);② RED(effect/extern/dyn_dispatch 三类无入边;load64 当时已保守入链);③ 旧产物亦零跨函数(该性质无历史 RED,咬合由 byte mutation 控制承担)。当前编译器三条全 GREEN。 - TODO.md 新增 #26 判据条目:state 边/纯度类改动判据 = 语义零变化 + 边集语义断言 + 自举稳定(连续两次编译产物一致);旧「.ccr 逐字节同旧版」退役;产物变更清单(.ccr NOD/EDG + --dump-objects + .cir 缓存)+ clean-cache 要求;ELF 逐字节不变仍有效(lazy 解耦) * docs: 效应/纯度修正批 Task 4 收官——全量回归 38/38 rc=0 + 语料级旧前端/当前 ELF 逐字节 20/20 同(ISO 发射面零泄漏)+ canary 95084e7b…d475 IDENTICAL + 自举两连建产物一致 + corec2b/corec3b cmp IDENTICAL + N06=0 + 冒烟 42;.ccr 变更面实测 = 仅 chain(kind=1)边增删(NOD 语义字段零差异、STR/SYM/ENT/REG 逐字节不变;CHAIN_SRC 303→329 / ptr_arith 293→318 / 无效应程序 +24 全来自 runtime 闭包);TODO 新增 #27 落地登记(fi_ispure 真计算 + purity_op_effect 单一真源;链覆盖 = 函数内全部保守效应 + 不可解析/未证纯调用;残余 = lazy 判定冻结 + use_count 时序缺陷保持独立)+「控制流自动惰性」行号复核 + auto-memo 补 IR_DYN_DISPATCH 间接调用入链注;五份 spec 纯度回填(S-A/S-C/S-D/S-E/S-B5:缺陷标记改已修 + commit ids + 链覆盖范围 + 残余限定;S-C 首切片门禁与 §2.2 C2 加时点约束、S-E 判据 (a) 前提改「已满足」+ 残余三条 + N6 重述、S-D §1.7.1 硬约束 8 与 S-B5 §1.2 事实底座相应修订) * docs: Task 4 回填补遗——S-C 两处残留「今日取保守条款」措辞改真纯度事实(§1 表来源列 + §2.1 合法性行;时点约束指向 §2.2 C2) * fix: TODO #8 ≥18 形参静默误编译——根源 = FuncInfo.param_types 16 槽定长内嵌槽区(16→144=return_type)而 parser/monomorph 写入无界:第 17 槽踩 return_type、第 18 槽踩 ast_node,形参类型值 TY_INT=0 恰把 ast_node 写成节点表首项(实测 rt.cr:4:13 的 int 类型节点)⇒ checker 读 fi_ast_node=0 发 TF01 误归 + .ccr name_idx=0(import)/param_count=0 + IR 体丢失(46→28 instrs)而 corec build 仍 rc=0、产物 SIGSEGV 139(N=17 因 TY_INT=0 误打正着而通过——钳位最小实验单独证明因果);修复 = 槽区扩至 MAX_FN_PARAMS=64(对齐 ast.cr 镜像 [int;64],覆盖 ≥22 = 6 寄存器参 + 16 栈参的 >127B 栈清理形)+ fi_param_type/fi_set_param_type 槽区护栏(唯一写点受护,未来调用点不可能再越界写)+ 超限签名 P020 硬错 rc=1(绝不静默 rc=0);回归 tests/selfhost/test_params_limit.py(N=17/18 逐值 + 22/64 逐参校验 + 65 拒绝且无产物)+ tests/suite/params_many.cr(22 参 = add rsp,0x80 128B 栈清理形 runtime 覆盖——Task 5 Minor 4 收口)+ src/ci/run.sh selfhost-tests 挂钩;既存 red 划界:check src/compiler 两条 TF01 系 ty_shadow.cr:118 记载的 loop-落空既有误报(lits_copy/ty_memo_slot_no_grow,形参 ≤2 在边界外)——判据 selftest-types 95/95 · test_compile PASS · test_purity PASS · test_ccr_v7 24/24 未重锁 · ELF 逐字节 95084e7b…d475 IDENTICAL(发射面零泄漏) * fix: TODO #25 F5 元组槽位——parser 元组分支改两趟(先全部解析元素值、后统建连续 wrapper:EXPR_TUPLE 契约 a=首 wrapper/b=个数,wrapper.a=元素值节点),修正 TODO「照 struct 先例」的交错顺序实测不成立(交错下相邻 wrapper 间插着下一值子树:P{a:11,b:g()} 误返 0);消费点 4 处同步解引用(checker/ir_gen/monomorph 克隆改先值后连续 wrapper/opt),并校正 opt.cr 与 ir_gen ast_patch_node 的 a/b 槽约定(两处经核无调用者=死码);第二根因:checker EXPR_TUPLE 元素类型两趟落盘(data_start 曾在推断循环前取,嵌套元素推断向 g_gen_apply_data 追加数据顶开后续落点、extra 错位 → 嵌套元组异型互赋静默通过);探针 14 例(3 主判据 + 边界 3 + 嵌套 4 + IR/ELF 冒烟)入 tests/selfhost/test_tuple_slots.py + run.sh selfhost-tests;判据 selftest-types 95/95 · test_compile/test_purity PASS · test_ccr_v7 24/24 · ELF 逐字节 95084e7b…d475 IDENTICAL · .ccr 同哈希(AST 布局零泄漏,ptr_arith 无元组字面量)· 泛型实例化与 suite 回归绿;同族**未修**登记 = struct 字面量复合值误编译 + ARRAY 复合元素误读([[1,2],[3,4]] vs [5,6] 静默接受) * fix: TODO #8 评审 Important 收口——CIR_CACHE_VER 15→16(修复前 ≥18 形参函数的坏 IR 已被写入 .cir 快照;缓存键 = 源路径::函数名 + 纯 AST 指纹,不含编译器身份 ⇒ 同源文件修复前后指纹相同,旧快照会被命中并把坏 IR 原样复活成活产物 rc=0——正是本修复要消灭的静默类;bump 使旧条目整体失效,cache miss = 无害重建。键缺编译器身份的根因仍归 TODO #5 另单);判据 selftest-types 95/95 · ELF 95084e7b…d475 IDENTICAL · params_many 冷/暖产物 IDENTICAL + rc=0 * docs: TODO #8/#25 收口登记——#8 根因(16 槽内嵌槽区越界写)+ 修复(MAX_FN_PARAMS=64/护栏/P020)+ 阈值重定说明 + 缓存面;#25 两趟修法(推翻 TODO 原建议,附实测反证)+ 第二根因 + 同族未修三项转条目 * docs: 语义安全 spec 收敛批 v3 复核收口——改名误伤 8 处(S-D 六处 L7/L17/L10/L8/L25/D12 + S-B1 §6 判据 D4 + S-B3 两处 L7 指向不存在或错误的决定;均按 roadmap §4 对照表改正)+ 路线图 标题/正文 八份→九份(其余 8 份)+ 裸评审编号补线别前缀(B 线 I3/I4/I5/N4 等);来源 = safety-verify-v3-2 复核的单一阻断项,收敛批 v3 自身引入的失效模式 * fix: TODO #16 函数体内嵌套 fn 声明编译段错误——根因 = parse_primary 无 T_FN 分支,语句位遇嵌套 fn 落回通用兜底「Unexpected token in expression」只消费 fn 一个 token ⇒ 解析失步:内层返回类型 + 函数体花括号(int 后随 {)被当作 struct 字面量(IDENT + {)解析、把外层 } 当字段吃掉直至 EOF,而该字段循环只认 } 且 advance_tok 在 EOF 是空操作(parser.cr:24-28)⇒ 自旋每轮 ast_alloc 直至 bump allocator 触顶(g_ast_cap 0x400000 节点 × ESZ_ASTNODE 72 = 288MB)→ rt.s .Lalloc_oom 返回 NULL → grow_ast/_dyncpy rep movsb 向 NULL 拷贝 ⇒ rc=139(gdb 实测崩点 = grow_ast 内 rep movsb、rdi=0x0 目的地 NULL、rcx=0x12000000=288MB 拷贝量;编译日志止于 [3/5] parse,与 TODO 记载一致;A/B 用 /tmp 构造的修复前二进制独立复现 139);语言面判定 = 嵌套 fn 不属语言面:grammar/core.ebnf Statement 不含 FunctionDecl(函数声明仅 TopLevelDecl)、bootstrap 参考实现(parser.py parse_block 已带 EOF 护栏)同输入报 SyntaxError 2:6 Unexpected token: fn、docs/spec-design.md 实现层函数值/闭包按 YAGNI 挂起、checker/ir_gen/monomorph 无任何嵌套函数注册路径 ⇒ 正确修复 = 定位诊断拒绝而非「支持」,TODO 原文「判据 = 最小复现 rc=0」的前提不成立(裁决与依据记入 tests 文件头与 run.sh 注释);修复 = ①parser.parse_stmt 语句位遇 fn/flow(含 pub 前缀)报 error[P21](ast.cr EC_P_NESTED_FN=1021,格式与 P020 同族)+ skip_nested_fn 花括号配平整段跳过(从函数体 { 起算 depth、体内嵌套块/struct 字面量成对计入;= expr; 与 extern 无体形式遇分号收尾)⇒ 后续语句恢复解析不再失步 ②同族 6 处只认 } 的循环补 EOF 护栏(parse_primary struct 字面量字段循环 = 实际自旋点 · parse_pattern struct 模式 · parse_declaration 的 struct/enum/interface/impl 体循环)③run.sh selfhost-tests 挂钩 test_nested_fn.py(沿 params_limit/tuple_slots 惯例)、run_suite 的 mini* SKIP 注释改记负例性质(at_test_mini4/6 = 嵌套 fn,5 = 扁平正例已在套件);A/B 实测(同源双二进制对拍):嵌套 fn 最小件 139→rc=1 + error[P21] + --> 2:5 定位、5 例嵌套变体(@inline 调用形/pub/双层/后续语句/无 @inline)全 rc=1、4 例 EOF 失步探针(struct 字面量开括号/带 token、未闭合 if、未闭合 fn 体)139→rc=1、check/run/build/cir/ccr 五命令齐 rc=1 且无产物(绝不静默);判据:build_selfhost_native rc=0 · selftest-types 95/95 · test_compile/test_purity PASS · test_ccr_v7 27/27 · test_nested_fn 17/17 · params_limit 5/5 · tuple_slots 14/14 · suite 21 pass/0 fail(mini* 9 例按既有 SKIP)· ELF 逐字节 95084e7b…d475 IDENTICAL(clean-cache 后 ptr_arith)· 扁平对照 build rc=0 + 运行 exit 2 正确;残留登记(非本次引入):EOF 失步恢复期重复打印无定位 parse error(有界 ≤64 条、毫秒级、rc=1,属既有解析错误报告面)+ docs/error-codes.md 表止于 P019(P020/P021 未登记,沿前次同类修复惯例) * docs: TODO #16 收口 + 错误码登记——嵌套 fn 根因(parse_primary 无 T_FN ⇒ 解析失步 ⇒ bump allocator 耗尽 ⇒ rep movsb 向 NULL 拷,gdb rdi=0/rcx=288MB)+ 结局判定(不属语言面 ⇒ P021 定位拒绝 rc=1,推翻 TODO 原文 rc=0 前提)+ EOF 护栏 6 处;error-codes 表补 P020/P021(表尾计数 P001–P019→P001–P021) * fix: TODO #11 解释器 callee 内联路径缺 18 个 opcode——分派统一为 ir_interp_call/ir_interp_run_fn 单实现(主循环与 callee 路径不再各持一份)+ 18 族同语义同守卫补齐 + 嵌套/递归内联(重入帧保存恢复 + 深度守卫 + 中止码沿调用链上抛,绝不静默落 0) 根因:主循环有一份 IR_CALL 分派,callee 内联循环只有旧聚合族(7/8/11-16/31),其余 18 个 opcode(4/17/18/23/24/25/26/27/28/29/30/41/42/43/44/45/48/51)在 callee 路径静默落空——同一段源码写在 main 里正确、写进普通函数(被内联)里错值 / SIGSEGV,而 corec run rc=0,属静默分叉类(rc 不反映)。复现原件:枚举 p14 interp rc=0 vs ELF rc=33、p15 interp rc=139 vs ELF rc=33;裸指针 g:[int;3]=[5,6,7]; fn f()->int{ p:=&g[2]; return *p; } interp rc=0 vs ELF rc=7。 修复: ① 分派单实现化——ir_interp_call(内置近似 / 用户函数内联 / 嵌套递归 / 中止码上抛只需一处)同时供主循环(IR_CALL 4 / IR_SPAWN 27)与 callee 内节点的 4/27 使用,双路径不再各持一份分派。 ② ir_interp_run_fn = callee 图内联执行唯一实现:建标签表 → 实参先入缓冲再落形参槽(递归时实参槽与形参槽同属本函数槽区,逐个直搬会被己方写覆盖)→ 顺序执行节点区 → 恢复标签表 → 返回值经局部变量传递。删除 ret_slot 暂存槽机制(旧约定 g_ir_vals[0] 会静默覆写首个全局,改 g_ir_var_count 只是把静默覆写挪到填充槽——返回经局部传递后该缺陷类结构性消失)。 ③ 递归/互递归:活动函数栈重入检测 + 逐帧保存/恢复调用前槽区(扁平槽模型下同一函数只有一份槽,不保存则外层帧被内层调用覆写 → 静默错值);深度上限 IR_INTERP_MAX_DEPTH=400,超限响亮报错(非零 rc + 诊断),不挂死。 ④ 18 族逐条对齐主循环(同语义同守卫):MAKE_ENUM/LOAD_ENUM_TAG、REF/DEREF/STORE_PTR/ADDR_INDEX、SLICE/BOUNDS_CHECK(越界 = 中止码 -1 沿调用链上抛,与主循环 return -1 同义)、DYN_PACK/VAL/TAG、DYN_DISPATCH/CALL_EXTERN(响亮报错 rc=2 + 诊断)、YIELD/AWAIT(eager 值传递)、FNADDR(无真实地址 = 0 显式近似)、APPROX(纯标注 no-op)。 判据:selftest-types 95/95 · test_compile PASS · test_purity PASS · test_ccr_v7 27/27 · ELF 逐字节 95084e7b…d475 IDENTICAL(解释器改动零泄漏到发射面)· 新 tests/selfhost/test_interp_parity.py 23/23(每例三断言:interp-callee ≡ interp-main ≡ ELF oracle;parity/approx 另加两形态诊断奇偶断言;每例再用 corec cir 自检 probe() 节点区真的含目标 opcode——防用例空转,先前 apx dex 写法错致 IR_APPROX 根本未发射即是此坑)。 留白:41 IR_DYN_TAG 全仓无发射点(ast/dataflow/checker/opt/regalloc/instr/sizes/interp 均有处理器,ir_gen 无 emit)= 当前不可达,内联侧处理器为纯防御性对齐,不列入覆盖表;其不可达性本身另单。 * docs: TODO #11 收口登记——分派统一单实现(ir_interp_call/ir_interp_run_fn 双臂共用)+ 18 族补齐 + 嵌套/递归内联(帧保存恢复/深度守卫/中止码上抛,绝不静默落 0)+ 回归 23/23 + RED 对照(修复前 4/23;原件 interp 0/SIGSEGV/0 vs ELF 33/33/7)+ 余留三项登记;合并态判据:selftest-types 95/95 · purity 26/26 · 四新测试 5/14/17/23 全绿 · ccr_v7 27/27 · ELF 逐字节 95084e7b…d475 IDENTICAL * docs: 登记 docs 重组合并时的连带改点——ast.cr:315 / ptr_analysis.cr:235 两处注释的路径改点须随 feature/docs-reorg(已推送 9eab0757)合并时同批做;本线两路径仍正确故暂不改(既避免 src/ 入 docs 分支,也避免本线悬空引用) * fix: TODO #28 F5 同族(struct 字面量 / struct 模式 / 数组字面量)——parser 三分支的「值节点在 g_ast 连续」假设对复合表达式值不成立(逐值后随建 wrapper 交错分配:复合值子树夹在相邻 wrapper 之间 ⇒ 第 2 个起槽位整体错位,消费者读到值节点的子节点而非值节点)。RED(base 08a9cf72 编译器、冷缓存实测)= P{a:11,b:g()} 的 b 静默得 0(rc=76≙1100,应 1103)· P{a:g(),b:h()} 的 b 得 0(30,应 34)· [1,g(),3] 的 a[1] 得 0(10,应 17)· [[1,2],[3,4]] 的 a[1] 被读成扁平 int 3 ⇒ a[1][0] SIGSEGV 139 · [[1,2],[3,4]] vs [5,6] 类型互赋静默通过(嵌套元素类型被错录为 int = soundness 漏放)。修复 = 三分支统一两趟(先解析全部值进暂存表、后统建连续 wrapper;契约 a=首 wrapper / b=个数 / wrapper(kind=EXPR_NONE).a=值节点——类型形 [T;N] / [T] 由 parse_type 产出、不经此路不受影响)+ 消费点逐 wrapper 解引用(checker infer_expr · ir_gen gen_expr/ast_patch_node · monomorph 克隆改「先克隆值、后统建 wrapper」· opt 折叠)。附带修正三处恒空转/旧约定残留:opt.cr 的 EXPR_STRUCT/EXPR_ARRAY 与 ir_gen ast_patch_node 的 EXPR_STRUCT 原直接把 EXPR_NONE wrapper 交给递归(两函数均无该 kind 分支)⇒ 字段值/元素从不常量折叠、泛型实例体 struct 字段内的方法调用名不替换;[v;N] 重复形由浅拷贝根节点 N−1 份改为共享同一值节点 + N wrapper;monomorph EXPR_STRUCT/EXPR_STRUCTPAT 旧 gen_clone_consecutive(b, c*2) 为「2 节点/字段」旧约定残留(对现 1 wrapper/字段布局多克隆一倍)改两趟。F5 同族面由 #25 元组修复过程一并指出(Task 3 评审判定「照 struct 先例」不成立时实测 P{a:11,b:g()} 返 0)。回归 tests/selfhost/test_agg_slots.py 13 例(值 9:struct 主判据×2 + 三字段中位 + 嵌套 struct + 单槽反证边界 + 泛型实例体克隆 + 数组中位 / 嵌套数组 / [v;N];类型 4:嵌套 vs 扁平拒 / 元素数不符拒 / 同形收 / [T;N] 类型形控制)+ src/ci/run.sh selfhost-tests 挂钩;判据 selftest-types 95/95 · test_compile/test_purity PASS · test_tuple_slots 14/14 · test_ccr_v7 27/27 · selfhost-tests 与 suite 两 CI 作业 rc=0(21 语料 ALL PASS)· ELF 逐字节 95084e7b…d475 IDENTICAL(语料含数组字面量,且与独立重建的 base 编译器产物同 sha)· ptr_arith 的 error[B04] 非致命诊断经 base 对照实测属既存(行数与产物零差异);同族**未修**四项登记:struct 字段名被丢弃(fni 取后未用 ⇒ 按声明位序绑定,P{b:11,a:22} 静默得 a=11)· struct 字段类型不与声明比对(P{a:1,b:"x"} / 缺字段 / 嵌套错型全 rc=0)· 数组元素同质性不检查([1,"x",3] rc=0)· struct 模式绑定未实现(EXPR_STRUCTPAT 消费点直接返 TI_UNIT/-1,模式分支修复为防御性) * docs: TODO #29 登记 F5 同族剩余面(#28 发现即登记)——struct 字段名被丢弃(P{b:11,a:22} 静默按位序绑定)+ 字段类型不比对声明 + 数组元素同质性不检查(三项静默 rc=0)+ struct 模式绑定未实现;含修复方向与判据建议(照 #28 的 test_agg_slots 风格) * fix: TODO #5 cir 缓存缺编译器身份——头部新增运行中编译器 ELF 内容哈希(/proc/self/exe 全文件 FNV-1 64 乘加:8B 字 + 余尾 + 总长,memoized),save 写入 / load 比对,不等即 miss;身份取不到(无 procfs/读取失败)= 两端点均关缓存(**绝不落「身份 0」条目**——那正是本静默类在退化路径复活);CIR_CACHE_VER 16→17(头部 +8B 整体后移) RED(pre-fix 二进制 sha256 f0f00d7d…):①载荷毒化(alpha 条目内 var[0]/var[2] 的 name_ni 互换 =「另一编译器写下的索引序」)后重跑——条目原样未被重写(命中)且 dump 错位(`binary 24 = a + b` → `24 = 20 + b`、`dest=22` → `dest=a`,rc=0)⇒ 装载路径对「索引序是否属于本编译器」零判据;②跨二进制 A/B:bootstrap 建的 build/corec vs 自举建的 corec2 实测 B 直接沿用 A 条目(无重写)。根因 = 键(源路径::函数名)+ 指纹(func_fingerprint/sig_fingerprint 只覆盖目标 AST)无编译器身份 ⇒ 重建后 g_strs 驻留序漂移不反映到键上,快照内旧序索引(var irv_name / instr s1..s3)被当活产物恢复。 选型(推翻 TODO 原「构建期源哈希常量」方向):运行中二进制自哈希捕获**全部**决定编译器语义的输入(src/compiler 清单+顺序 + bootstrap Python 工具链 + rt.s + as/ld),构建期源哈希只是其中一部分的代理——漏 bootstrap/工具链即本类复活;且无需构建管道改动/生成文件(tests 直编 src/compiler、full-bootstrap 的 corec2/corec3 不因缺生成文件而断)。前提 = 本仓构建逐字节确定(两连建 cmp IDENTICAL,修复前后两侧均实测)⇒ 同源重建身份不变、命中面不退化;若构建转为非确定,症状 = 缓存恒 miss(性能面,由该判据暴露)。 成本(实测,热身缓存,1.32MB 二进制):逐字节形 +11.6ms/次(小件编译 17→29ms)→ 8 字节字粒度 min 基准 +2~3ms/次(小件 15→17ms;大件热身 5.5s 下不可分);只在真正走到缓存的进程付一次(strace:全量热身编译 1 次 open("/proc/self/exe"))。 回归 = tests/selfhost/test_cache_identity.py 5/5(身份语义 = FNV-1(ELF) 且 ver=17 · 同身份仍命中[改「被装载跳过」的函数名字节 canary] · 异身份拒[身份+载荷毒化 → 条目被改写回当前身份] · 差分「条目缺失 ≡ 条目毒化」(dump + 重建条目逐字节同)· 跨二进制 A/B[副本+1 字节模拟重建:canary 被抹 + 身份改写为 B])+ src/ci/run.sh selfhost-tests 挂钩;RED 对照 = 同一测试对 pre-fix 二进制 1/5(仅命中控制过,cross_binary_ab canary KEPT)。 判据:两连建 build/corec 逐字节 IDENTICAL(sha256 699b360a…)· selftest-types 95/95 · test_compile/test_purity PASS · test_ccr_v7 27/27 · clean-cache 后 ptr_arith ELF 95084e7b…d475 IDENTICAL · suite 21 pass/0 fail · params_limit 5/5 · tuple_slots 14/14 · nested_fn 17/17 · region_cfg 22/22 · impl 7/7 · borrow 7/7 · pointer_safety PASS · interp_parity 23/23 · /proc 掩掉(unshare -rm + tmpfs 覆盖)→ 编译 rc=0 且零条目落盘(退化路径实测)。 未修(同族第二实例照旧):.ccr 冷/热缓存态分歧(at_test_struct.cr:cold 891377232b5b ≠ warm 8dbb12f740d8,第三次起稳定)本修复**不影响**(pre/post 三连跑同哈希)⇒ 比较 .ccr 前仍须 clean-cache。 * docs: TODO #5 收口登记——根因(键+指纹只覆盖目标源 ⇒ 重建后 g_strs 驻留序漂移不反映到键上)+ 修复(头部编译器身份字段 = 运行中 ELF 内容哈希 / CIR_CACHE_VER 16→17 / 身份不可证关缓存)+ 选型理由(自哈希 ⊃ 构建期源哈希:覆盖 bootstrap/工具链/清单顺序,且不动构建管道/不引入生成文件)+ RED 实证 + 成本采样 + 回归与判据 + 未修面登记(.ccr 冷/热分歧不受本修复影响 ⇒ 比较 .ccr 前仍须 clean-cache;CORE_SAFE/-O 经核不在缓存面);global-init 交叉引用修订(跨编译器升级面已关闭,同二进制内命中含注入无碍)+ #25 操作约束理由改由冷/热分歧承担 * docs: TODO #5 提交引用同步——集成 rebase 后 44b31afe 不再解析,正文两处改指 3be4cb48(change id 不变;由协调者统一处理,避免再增 rebase churn) * test: 纯度批终审收口——① Important:判据②(边集语义断言)**接上 CI**(src/ci/run.sh selfhost-tests 补挂 test_purity.py + test_ccr_v7.py——此前本批是唯一没挂 CI 的,其后每个修复都挂了);② Minor:文档锚点漂移修正(checker.cr:2891/2938/3083 → 2912/2959/3104,因 #25/#28 在 checker.cr 上部 +21 行;四份 spec S-A/S-C/S-B5/S-E 共 6 处) * fix: 纯度批终审 3 Minor 收口——① test_purity.py MIN_CASES 17→26(Task 2 把用例加到 26 未同步下限 ⇒ 静默删减守卫一度允许删 9 例;随用例表钉死并注明后续同改)② checker.cr 注释纠偏:IR_INLINE 的 s1 依发射点分两形(调用旗标形=名 ni / @inline(expr) 形=变量索引 ⇒ 该形必然解析失败、保守判不纯,误差方向安全)③ df_state_finalize 幂等护栏(重复调用会二次连链;旗标 g_df_state_finalized 随 reset_frontend_state 复位,长驻进程每编译恰一次);判据 selftest-purity 26/26 · test_purity PASS · selftest-types 95/95 · ccr_v7 27/27 · 六新测试全绿 · ELF 逐字节 95084e7b…d475 IDENTICAL * fix: TODO #29 聚合字面量「名/型/同质性」三校验——名字绑定(P{b:11,a:22} 恢复正确值)+ TS01-04/TK02 定位硬错误 + 附带修复 bootstrap 后端 return 非块终结符 ① **名字绑定**:parser 把字段名 idx 写入 wrapper.b(EXPR_STRUCT 契约扩展:wrapper.a=值节点、wrapper.b=名字 idx、-1=无名字信息→位序回落;名字与值并列的平行表,两趟结构不变)。字段值按名字绑定(与 Python bootstrap gen_struct_lit 同语义):checker 解出「字面量字段 i→声明下标 j」,ir_gen 同一解算落 IR_STORE_FIELD;求值顺序仍源序(实测 P{b:nx(),a:nx()} 返回 201=b 先求值,按声明序会得 102)。monomorph 克隆保留 wrapper.b(丢名字⇒实例体回落位序=静默错值)。RED:修复前 P{b:11,a:22} build rc=0 得 1122(a=11,b=22),现 2211。 ② **三校验 + 类型比对**:未知字段 TS02 / 重复 TS04 / 缺字段 TS01(只报首个;P{} 亦覆盖)/ 字段类型 vs 声明 TS03(res_type_node + type_compat_strict + diag_type_incompatible,与赋值同引擎,影子站点 9)。两个**不假拒门**:(a) 两侧任一含未实例化 TYP_GENERIC_PARAM(下钻数组/指针/引用/切片/元组/泛型应用)→ 跳过(泛型函数体 f[T](x:T){ p:=P{a:1,b:x} } 的值类型就是 T);(b) 声明类型提及结构体泛型参数(T/[T;3]/Box[T])→ 跳过比对走绑定(结构体泛型参数在字面量作用域不在名字表,res_type_node 会造出具名 T ⇒ 必假拒)。泛型绑定改用 unify_types,结果 TYP_GENERIC_APPLY 实参改按**参数声明序**取(旧按字面量字段序⇒双参数且字段序≠参数序时实参错位,判据 Sw[A,B]{y:B,x:A} 乱序字面量)。 ③ **元素同质性**:元素类型取**首**元素(旧逐个覆盖=随末元素漂移,soundness 漏放),后续逐个 type_compat_strict(站点 10,同带泛型门)→ TK02。 **硬错误门**(非静默的必要条件):TS01-04+TK02 入 run_frontend 硬错误名单(同 R002/TK05/TK06)——修复前这些字面量即便报错也照常产出二进制(未知/缺字段⇒字段从未写入读垃圾值、错型⇒按错宽度存、异质数组⇒类型漂移),现 rc=1 且无产物。 **附带修复(本修复的直接阻塞,发现即修)**:bootstrap/corec/backend/x86_64_stack_asm.py 仅在「块的最后一条恰是 ReturnInstr」时补 epilogue ⇒ 同块双 return(return 后跟死代码)两条 mov rax 都发射、末尾单出口 ⇒ 返回值=**最后一个** return(死代码赢,静默错值)。实测命中 checker.cr 的 unify_types(return true; return false; 被编成恒返 false;其返回值此前无人消费——infer_gen_call 丢弃、递归点从未触发——故潜伏),#29 首次消费即暴露(Box{val=100} 误报 TS03)。修复=ReturnInstr 视为块终结符(发 epilogue 后停止发射同块余下指令),并删除失去调用者的 _block_ends_with_return;全 build/corec.s 扫描命中面恰此 1 处;修复后 build/corec/corearch 与修复前逐字节相同。语料 generics_test.cr 的拦截即此误编译面(非新检查过严),修后端后恢复 PASS,检查未放宽。 回归 tests/selfhost/test_agg_checks.py 17/17(值 7:名字绑定主判据/源序副作用/三字段逆序/乱序+嵌套字面量/泛型 Pair 乱序/双参数字段序≠参数序/monomorph 克隆保名字;类型 10:负 7 均验 rc≠0+码+**无产物**+定位,正 3)+ src/ci/run.sh selfhost-tests 挂钩 + ty_shadow 站点 8→10(范围/容量/名/打印/头注)+ docs/error-codes.md TS 段与 TK02 注明实现化 + TODO #29 收口。判据:selftest-types 95/95 · test_compile/test_purity PASS · test_agg_slots 13/13 · test_tuple_slots 14/14 · test_ccr_v7 27/27 · selfhost 余 8 套全绿 · bootstrap 三套 29/29+4/4+3/3 · tests/suite 21 语料 ALL PASS · ELF 逐字节 95084e7b…d475 IDENTICAL · 三阶段自举 corec2≡corec3 逐字节。④ struct 模式绑定按范围保持未实现。 * fix: 嵌套泛型应用载荷写坏(#29 评审 Important 暴露的既存缺陷)——g_gen_apply_data 三站同款「先认领块、后解析实参」,实参解析自身追加嵌套载荷 ⇒ 内层 append 冲掉外层 count 槽与后续载荷槽(终点 count 截断);照 #25 元组第二根因模板改两趟(先解析进暂存、后一次性认领连续块):res_type_node 泛型应用分支 / res_call_type / substitute_return_type。实证 = Box[Box[int]] 在字段位与返回位均被误判(error[TS03] expected Box, got Box / 返回位 TF01)⇒ 修复后 check/build/run 全 OK(rc=7);regression test_agg_checks 17→19 例(值 1 + 正控 1,修复前两例均红);判据 selftest-types 95/95 · selftest-purity 26/26 · 七新测试全绿 · test_compile/test_purity PASS · test_ccr_v7 27/27 · generics_test 语料 OK · ELF 逐字节 95084e7b…d475 IDENTICAL * test: 嵌套泛型应用错型负控入测试(评审 Important #1 的反面——两趟修复不得引入假阴性)——Box[Box[int]] 字段收到 Box[Box[string]] 仍 error[TS03] 且无产物(实测);test_agg_checks 19→20 * fix: 泛型应用类型显示丢失实参(#29 评审 Minor #2)——type_display 在 get_type_name 捷径**之前**处理 TYP_GENERIC_APPLY(后者只返回基名 ⇒ 实参全丢、下方实参分支成死码),现打印 Box[int] / Pair[Box[int], string] 形;实测 expected Box[int], got Box[string](修复前 expected Box, got Box——真正不匹配的实参恰是读者最需要的)。判据 selftest-types 95/95 · selftest-purity 26/26 · 七新测试全绿(agg_checks 20/20)· ELF 逐字节 95084e7b…d475 IDENTICAL * docs: R2 接口化 P2b/P3 实施计划定稿——P2b(infer_expr 公理区 → iface_ops 查表接线:本质条目表 + 操作许可 + 字面量定型;res_type_node 双份 TY→TI 映射合一)· P3(能力落地:定长退役/序列接口+长度约束/穷尽性·联合·可选/泛型约束/impl 契约;拆 P3a 非阻塞五任务 + P3b 阻塞于 P2b 的横切接口与 impl 契约,含与 P2b 的交接契约 iface_satisfies 三态 + 变型字段位) * feat: R2 P2b Task 1——本质条目表 + iface_* 查询 API(建层;零消费者零行为变化)——新建 iface_registry.cr(13 条原子条目 = 8 原生 + product/sequence/ref/ptr/named:表为静态常量、40B/条 × 5 字段 {ak,ti_row,name_ni,lit_code,ops} 扁平缓冲、单一 alloc、不 alloc g_types 行(先例 init_builtins 的 g_rt_builtin_* 旁表)⇒ 类型段/行号零扰动)+ 11 个 iface_* 逐签名(count/entry/ops/permits/lit_ti/lit_ak/kind_of/by_ty_code/ti_of/of_term + 幂等 iface_registry_init 挂 init_types 尾部)+ AK↔TI 逐项语义分派(bool/string 下标不 1:1,禁止数值直传;by_ty_code 含 DEX_S→AK_DEX、GENERIC_PARAM→AK_NAMED 两条已裁决灰格);四处清单同步(_import.cr · build_selfhost_native.py 的 corec+corelsp 两清单 · test_compile.py concat_sources,一律置引擎之后、桥接层之前);判据 selftest-types 95→138/138(+43 例 iface.* = 13 条目两向 + 8 API + 越界/负键/空许可集/位下标越界守卫)· ELF 逐字节 95084e7b…d475 IDENTICAL · .ccr 逐字节 IDENTICAL(冷缓存 ecd7a9df…,实测)· check 32 语料输出逐字节同 · check src/compiler 仅既有 2 条 TF01(lits_copy/ty_memo_slot_no_grow,行号 +13 位移、零新增诊断)· test_compile/test_purity PASS · test_ccr_v7 27/27;偏差登记(1 处):name_ni 列落 -1——str_intern 会把 13 个显示名追加进 g_strs,而 .ccr 的 STR 段 = 编译期全量字符串表(ccr_io.cr:537;实测探针未用标识符 zzz_probe_unused_ident 即入 STR 段),与「.ccr 预期逐字节不变」硬判据冲突 ⇒ 显示名待首个消费者(P3)落地时裁决 * test: 类型引擎自测接上 CI + P2b 计划精修回填——run.sh selfhost-tests 补挂 test_type_engine.py(selftest-types 138 例;P0/P1/P2a/P2b-T1 一路漏挂,与纯度批同款缺口)+ 计划数字精修(公理区 65→66 条、内联链 5→6 处、交叉引用 §6.2) * refactor: R2 P2b Task 2——iface_kind_of 与桥接分派(sh_native_ak/sh_base_ak)单源化(全表对拍 + 负控探针实证零行为变化) - ty_shadow.cr:两入口改**薄委托** iface_registry.cr 的 iface_by_ty_code(唯一实现);改动前实现 保留为**字面拷贝** sh_base_ak_legacy/sh_native_ak_legacy(仅 type_selftest.cr 全表对拍面用, P5 删——登记入 TODO #24 继承项)。**两个门显式保留**:sh_native_ak **不得**换成 iface_kind_of (后者对 TYP_DYN 行回 AK_DYN、对结构/命名行回其类;前者只认 TYP_BASE 行,DYN 行 -1 走 sh_term_of_ti 的 TYP_DYN 分支)——「单源化」= 共用同一张 TY 码**逐项分派表**,不是合并语义。 - type_selftest.cr:+7 例(138→145)。① TY 码面全表对拍(10 键 = 9 个 TY_* 含 TY_DEX_S / TY_GENERIC_PARAM 两条已裁决灰格 + 未映射码 999)② 行号面全表对拍(逐 ti ∈ [0, g_type_count), 含 TI_DYN 行、TI_DEX_S 占位行与既有结构/命名行,非抽样)③ **反真空哨兵**(扫描计数:码面 10 键、行面恰 = g_type_count 且 ≥ 9;比较活性:已知不等的一对必须报不等)④ 生产入口委托逐项 钉死(含两灰格与未映射码)⑤ 原门保留(DYN/结构/负 → -1 与注册表侧 DYN → AK_DYN 并列各自钉) ⑥ 下标不 1:1 守卫(AK/TI 互换即红)。 - iface_registry.cr / src/ci/run.sh:Task 2 边界注释(防后人把 sh_native_ak 误换成 iface_kind_of /防误读「单源化」为语义合并)+ 例数 138→145 同步。注释面改动实测 corec+corearch 二进制 **逐字节不变**(AST 布局零泄漏)。 - 判据:selftest-types 145/145 rc=0;**负控探针**(iface_by_ty_code 的 TY_BOOL 钳位成 AK_STRING) 令 ①②④ 三例报红、复位后回绿且 ELF 哈希复原(证明对拍确为活判据,非空转);ELF 逐字节 95084e7b…d475 IDENTICAL(vs 基线与改动前二进制产物);.ccr 冷缓存 4 文件逐字节同基线 (ptr_arith/at_test_struct/generics_test/dyn_test);**同源双编译器对拍**(改动前二进制 vs 改动后 二进制,同一份当前源码):corpus check 32 文件 stdout+stderr+rc 逐字节同 · check src/compiler 日志 + rc(=1 既有) 同 · 影子语料 71 文件 summary + 全部 dumps 逐字节同(agree 28430/28430 · old_stricter 0 · old_looser 0 · unknown 0 · replace_bridge 0 · replace_unknown 0 · 站点直方图 assign-node 21333 / fn-body-ret 5113 / if-branch 1950 / struct-field-type 28 / array-elem-type 4 / generic-apply-base 2);test_compile PASS · test_purity PASS · test_ccr_v7 27/27 · test_type_engine PASS · test_lsp 10/10(含 P2a 评审 Critical 的 bridge_cache_reset)。 - 影子总数口径(诚实声明):28430 既 ≠ 计划书沿用的 findings §11 旧值 26989,也 ≠ 前基线 28370 ——差 +60 全落 3 个**传递 import type_selftest.cr** 的文件(main.cr / _import.cr / ccr_io.cr, 各 +20:assign-node +18、fn-body-ret +2),系本 Task 新增用例的**输入增长**所致;两编译器在 同一输入上逐字节恒等 ⇒ 判定面零变化。计划书 26989 为更早口径,本批不复现该数字。 * refactor: R2 P2b Task 3——字面量定型查表接线(infer_expr 5 处内联 if → iface_lit_ti;唯一真源 = 注册表 lit_code 列,逐格转录现状常量;短路顺序逐字保持——EXPR_NONE 转发行仍夹在 EXPR_INT 与 EXPR_DEX 之间,未合并成「先取 kind 再查」循环;计划行号漂移 1892-1897 → 按内容实测定址 1896-1901);selftest 145→155(+10 例:lit.node_kinds 反真空哨兵 + lit.infer_int/dex/str/bool/char 端到端经真 infer_expr 跑构造字面量节点 + lit.infer_is_table 端到端↔表对拍 + lit.none_forwards/none_empty_unit 短路序钉红 + lit.neg_node_unit);**突变控制**(翻注册表 entry0 lit_code EXPR_INT→EXPR_DEX 重建 → iface.lit_int/dex/ak + lit.infer_int/dex/none_forwards 6 例红 149/155 rc=1 → 复位后 155/155 且 build/corec/corearch 与探针前逐字节同)实证「接线确在读表」非空转;TODO #24 P5 继承项④(sh_native_ak_legacy/sh_base_ak_legacy 删除归 P5,连对拍三例同步调整);判据 = selftest-types 155/155 · ELF 95084e7b…d475 IDENTICAL(clean-cache)· 32 语料 check stdout+stderr+rc diff -r 全空 · 4 语料 .ccr 哈希逐项同 + 32 档影子通道输出同 + check src/compiler 零新增诊断(旧二进制对新源码 == 新二进制对新源码,同源双编译器对拍隔离输入增长;仅既有 2 条 TF01 行号 +5)· test_compile/test_purity/test_ccr_v7 27/27/test_type_engine 绿 * refactor: R2 P2b Task 4——操作许可查表接线(checker.cr 三个门 ← iface_registry 的 ops 列;ANY/ALL/ONE 三种门形状与现状逐字等价;宽松面原样保留登记) 接线 4 处(3 种门形状,全部「与改动前源码一字等价」): · 算术门 checker.cr:1959(ANY:「至少一侧许可即通过」)——改动前 `lt != TI_INT && lt != TI_DEX && rt != TI_INT && rt != TI_DEX` → `iface_permits(iface_kind_of(lt), op) == 0 && iface_permits(iface_kind_of(rt), op) == 0` · 逻辑门 :1969(ALL:「每侧都须许可」)→ `permits(k(lt), OP_AND) == 0 || permits(k(rt), OP_AND) == 0`(谓词写 OP_AND;**不另设 IP_LOGIC**,OP_AND/OP_OR 表中同步置位) · 条件门 :2274(ONE / IP_COND = bool|int)与 :2385(ONE / IP_COND_BOOL = 仅 bool)——两条规则现状不同,**不得合并** IP_* 位下标常量声明在 globals.cr(Task 1 §5.2 的声明序约束:变量/常量跨文件前向引用不成立,checker.cr 位于 iface_registry.cr 之前;函数不受限) ops 列逐格填空(13 类 × 组位集,每组注现状行号):ADD..MOD **只含 int/dex**(这是与 :1959 一字等价的最小集——把 PTR/STRING 填进 ADD 格,ANY 语义下 permits(PTR,ADD)=1 即放行 `*T + *T`/`"a" - "b"`);AND/OR 与 IP_COND 恰 {int, bool};IP_COND_BOOL 恰 {bool};比较 6 位 / 一元 4 位(NEG/NOT/REF/DEREF)/ IP_AS 对**全部 13 类**置 1(现状无拒绝路径 ⇒ 本批不接线、只登记为 P3 收紧旋钮);索引/字段/方法位 = Task 5 消费格。 计划两处表格勘误(**探针实测裁决**):计划许可格表把 AND/OR 与 IP_COND 写在「AK_INT/AK_DEX」行(括注「允许 bool|int」自相矛盾),但 dex 在现状被拒(`1.5 && true` → error[TC01]、`if 1.5` → error[TC01],实测)⇒ DEX 两格必为 0,否则 = 放宽;取计划 Step 2 的替换代码(= 与现状一字等价)为准。 18 站点台账(11 二元 / 4 一元 / 3 条件):已接线 4 = 算术门 + 逻辑门 + if + while;**留代码不接线 13**(均已核为「结果规则或无门可换」,逐条见报告:组派发/串拼接/指针±/指针差/dex 支配/兜底 return=结果类型决定;比较=现状无拒绝路径;赋值=type_compat_strict(P2a 引擎面,无 ops 位);一元 4 条=NEG/NOT 透传、REF→TYP_PTR、DEREF 三分支+兜底、尾兜底透传;range 端点 `st != TI_INT`/`et != TI_INT`=**无对应位定义**,改用 IP_COND 会放宽 `range(bool, 3)`)——按计划口径「早退规则留代码」,无一被强行接线。 判据:build rc=0(3 守卫 error[=0)· selftest-types 155→183(+28 例 = ops.* 逐格全表对拍 15 例 + 端到端经真 infer_expr 走门 13 例,含负控码断言)· ELF 95084e7b…d475 IDENTICAL(clean-cache)· **零行为变化四链**(旧二进制 × 新源 vs 新二进制 × 新源:32 语料 check stdout+stderr+rc / 32 档影子通道含 dump / 4 语料 .ccr 冷缓存哈希 / check src/compiler 全部 diff 空——.ccr 四哈希与 Task 1-3 基线逐字相同,编译器自源仅既有 2 条 TF01 误报行号位移)· test_compile/test_purity PASS · test_ccr_v7 27/27 · test_type_engine PASS · test_lsp 10 例 ALL PASS · 11 个自测套件(impl/borrow/pointer_safety/params_limit/tuple_slots/agg_slots/agg_checks/named_dedup/interp_parity/cache_identity/nested_fn)全绿 · 新 tests/selfhost/test_iface_ops.py 39/39(正控/负控/登记面三类;已挂 run.sh selfhost-tests)· **突变控制**(把 PTR 填进 ADD 格 → selftest 179/183 rc=1 + 端到端 ops.infer_ptr_add_ptr_diag 0≠1 + test_iface_ops 38/39「neg_ptr_add_ptr: 静默放行 = 放宽面复活」→ 复位后 183/183、39/39、ELF 同、全产物逐字节复原、源文件 cmp 同 = 零残留)实证负控有牙且门确在判。 登记(P3 收紧面输入,报告含事实表):全许可列(比较/一元/AS)+ range 端点位 + 索引/字段/方法位(Task 5 消费)+ §2 九条宽松面逐条落格记录 * refactor: R2 P2b Task 5——容器面接线(唯一可零行为变化接线点 = 索引兜底拒绝 ← IP_INDEX 查表:门体即原 TK01 调用,可达集 = 表拒绝集逐行等价(全类型行枚举 + 表/端到端/真实源三层突变实证);字段/转换/dyn 三面逐条实测不接线——前两面无拒绝路径(EXPR_FIELD/EXPR_AS 全形 rc=0 零诊断)、dyn 谓词非类级位(逐具体行方法表判定,类级 IP_METHOD 门会抑制既有 N08)= 放宽 ⇒ 登记 P3 旋钮)+ selftest 18 例(183→201)+ iface_ops 21 例(39→60)+ run.sh 例数同步;判据 ELF 95084e7b…d475 IDENTICAL · 语料 32 / 影子 32 / .ccr 4 逐字节同 · 自源仅 2 条既有 TF01 行号 +11 · 全回归绿 * refactor: R2 P2b Task 6——res_type_node/res_call_type 双份 TY→TI 映射合一(单表 ty_code_to_ti + 各站点显式回落) - 新表单源(iface_registry.cr):8 个可达码(TY_INT..TY_CHAR + 7)逐项语义分派;7 = TY_GENERIC_PARAM/TI_DYN 数值撞车格**现状写法原样保留**(parser.cr:98 对 `dyn` 正产 type_val=7 ⇒ 该格可达,探针 N08 实证); 表内**不兜底**(域外/负码 → -1,尾部回落是调用点的显式责任)。**TY_DEX_S(8) 不入表**(计划注「不属两表 并集」;实测全 src 无任何 parser/铸点写 type_val=8,入表 = 若可达即行为变化——selftest 把「码 8 → TI_UNIT」钉红)。 - 两处基型分支 + 同族 6 处内联链(hotpatch 注册/首版、extern 注册、check_func 形参/返回、iface 返回)逐处 改调单表;**逐站域差异显式保留**(不得按「表 + 尾回落」一刀切——站点域是**载荷**的:`fn f() -> char` 的非泛型符号类型 = unit、extern 同形 = char;突变 M2 直接证伪一刀切写法)。iface 返回站的 `iface_ret2 == TY_*` 撞车比较按语义**显式化**(值域本是 TI_*)。 - 两表唯一语义差 = NEVER 格(res_call_type 现状无该格 ⇒ 调用位点落 TI_UNIT):探针实测**可达** (`fn g[T](a: T) -> never` 调用位点;插桩 build 实测该格命中;行为面 = `@raw_int` 报 TF07)⇒ 保留 `|| tv == TY_NEVER` 显式差异,不静默合一;「never 在调用位点被当 unit」登记 P3 待裁决项。 - 用例:selftest-types 201→**212** 例(表侧逐码 + 两处基型分支端到端 + NEVER 差异格双向 + 域外回落 + 反真空哨兵);test_iface_ops 60→**76** 例(新增「逐条 (码,行号) 多元集」判据,把 8 个站点的域钉死); run.sh 例数同步。 - 判据:自举 rc=0(3 守卫 clean)· selftest-types 212/212 · ELF 95084e7b…d475 IDENTICAL(clean-cache)· 同源双编译器对拍:语料 32 档 / 影子 32 档(decisions=2826 agree=2826,全 0)/ .ccr 4 档 / `check src/compiler` **逐字节相同**;自源仅既有 2 条 TF01 行号 +10(= checker.cr 净增行数);13 套回归 绿(27/27 · 7/7 · 14/14 · 13/13 · 20/20 · 23/23 · 17/17 · LSP 10/10…)· 冒烟 rc=42 · 自举链 corec2 ≡ corec3 IDENTICAL + --help rc=1。 - 突变控制三层(逐条复位,重建与探针前**逐字节相同**):M1 删 NEVER 门 → selftest 211/212 + 行为 75/76; M2 放宽 S1 域(= 一刀切写法)→ 行为 75/76(selftest 全绿 ⇒ 行为层不可省);M3 翻转表 CHAR 格 → selftest 209/212 + 行为 73/76。 * docs: R2 P2b 收官(回归 + 自举 + 收紧清单 + 文档)——全量回归 46 套件 rc=0(7 bootstrap + 39 selfhost;计划记 38=7+31,差 8 为期间新增套件)· selftest-types 212/212 · test_iface_ops 76/76 · 收紧清单=空(同源双编译器对拍:21 档 tests/suite 语料 check 输出 + 自源 check src/compiler + 32 档影子通道 + 4 档 .ccr 逐字节同;ELF canary 95084e7b…d475 开/关影子两态同;自源仅既有 2 条 TF01 误报 lits_copy:9383/ty_memo_slot_no_grow:9498 且两侧编译器输出一致)· 自举 corec2==corec3 IDENTICAL c201916d…a7d9 + N06=0 + 冒烟 42 · build/corec 重建 sha 复原 8f69e346…858b;P2b 交接包 = P3 计划新增附录 A(未接线站点台账 31 条 = T4 的 14 + T5 的 17 逐条实测理由 + P3 裁决点 10 项:iface_satisfies 未交付/name_ni=-1(.ccr STR 段约束)/TY_DEX_S 不入表/never 调用位点=unit 与类型位点=never/全许可列 11 位×13 类/字段·转换面无拒绝路径/dyn 逐行谓词+位图 64 上限/索引侧缺须 int 位/枚举域无 AK_SUM 条目/性能面;澄清「20 个全许可列站点」不可复现:实测 11 位/未接线 31 站点)+ findings 文档 §12「P2b 后:公理区事实表」(九条宽松面→表中格→现状诊断→P3 收紧建议);spec §9 P2 (b) 标 ✅(落点/提交链/未覆盖面)+ TODO #30 落地条目(#24 的「P2b 待办」划销、P5 继承项 sh_*_ak_legacy/type_equal_legacy/unknown 清零并记) * docs: P2b 阶段评审收口——计划两处勘误回填(AK_DEX 逻辑/条件格 = 0 实测拒:1.5 && true / 1.5 || false / if 1.5 → error[TC01];门体即原兜底、不得叠第二次裸调用=双 TK01 可观测变化)+ TODO #30 陈旧引用纠(ty_shadow.cr:118→:143-146,Task 2 legacy 插入后移位)+ TODO #31 登记 CI 挂点缺口(selfhost-tests 15/39、bootstrap-tests 3/7;test_lsp/test_named_dedup/test_slice_bounds 未挂) * feat: R2 P3 Task 0——引擎展开层(struct→product / enum→sum / generic-apply 代入;仅满足判定与域查询用,等价判定保持原子名义) 边界(本任务第一性约束,已钉成可执行判据):展开只服务 ① 满足判定 T <: 形状(iface_satisfies,Task 2/6)② 穷尽性域/模式项(Task 3);等价/包含判定(type_equal → sh_term_of_ti → ty_equiv)一概不经本层——sh_term_of_ti 的 NAMED/GENERIC_* 分支一行未动,命名类型在等价面保持原子名义;裁错即同形不同名类型被判等价 + 推翻 P2a 对拍归零基线(亦为 P5 删 type_equal_legacy 的前提)。守门用例 unf.nominal_{structural_equal,equiv_atomic_unknown,type_equal_false} 三例双钉(展开项结构相等 ∧ 桥接项 ty_equiv 仍 -1 ∧ type_equal 仍 false 且回落计数 +1)。 交付:sh_struct_term(命名/泛型应用行 → AK_PRODUCT,字段声明序;字段节点经 checker res_type_node 解析 + 泛型实参代入,与 EXPR_FIELD 同源同径)/ sh_enum_domain_term(枚举 → 变体项之并 = 域;空枚举 → ⊥)/ sh_variant_term(变体身份 = 枚举名 + 变体名入参数链——引擎 lit_implies 只比 a/c 两槽,身份若只放 b 槽或省去则异枚举同名变体静默合并 ⇒ 穷尽性假通过,守门例 unf.enum_variant_identity_scoped)/ sh_iface_shape_term + iface_satisfies(两块占位恒三态 -1:P2b 未交付条目/满足关系 + 接口签名仍裸 TY,接管归 Task 2/6);展开缓存 = 第二张表(键空间同 ti、值语义不同 ⇒ 不得与桥接表共用)+ init_types 随类型表失效(sh_unf_map_reset,同 sh_map_reset 评审 Critical 同因)+ 装填守卫/重建重放/回写重探(照 sh_map_* 同式);checker 侧新增 decl_name_of_ti/find_struct_row_of/find_enum_row_of(名字解析归 checker、只读)。 判据:selftest-types 212→229(+17 例:泛型代入 2/字段形态 2/名义不变量 3/枚举域 4/穷尽性端到端 3/缓存 1/缺口上抛 1/接口占位 1)rc=0 · test_iface_ops 76/76 · ELF canary 95084e7b…d475 IDENTICAL · .ccr ecd7a9df… IDENTICAL(计划预期不变)· 判定面零行为变化:72 档 check 面 diff -r 空(旧二进制×新源 ≡ 新二进制×新源,rc 分布同)· 影子对拍门 72 档 29090/29090 agree、old_stricter/old_looser/unknown/replace_* 全 0、日志+dump diff 空、站点 1/2/4/6 零命中(同报:零差异 ≠ 面收敛——本能力面语料零命中,正确性由定向用例+突变控制承担)· CI selfhost-tests 15 档 rc=0 零 FAIL · 突变控制 M1(字段改读裸码槽)→ 两例红、M2(变体身份去枚举名)→ 一例红,复位后 229/229 + 三二进制 sha 逐字节复原。 登记(发现,转 Task 3/4/6):① 枚举表只存 payload 裸码(parser.cr:1601 unpack_type;非基型塌缩 0 = TY_INT 与「payload 是 int」不可区分)⇒ 变体项不含 payload(不谎报),Task 4 的「Some(1) 满足 int?」须先扩表存 payload 类型节点(照 struct OFF_SI_FIELD_TYPE_NODES 先例);② ty_exhaust_witness 原始形态 = 各析取支之并(含空析取支,norm 无净化)⇒ Task 3 计划用例「witness = 该变体」按 ty_equiv 不成立,须取有住户支(引擎不在 Task 3 Files 内,需裁决);同族:不可展开域在引擎侧给 1/0 而非 -1;③ bootstrap 后端不短路 &&/||(反汇编实证两侧均求值;自托管后端短路——探针 rc=1 证明 D11 成立),无符号通道(selftest-types)实测 SIGSEGV(sym_kind ← res_type_node 的 si >= 0 && sym_kind(si),g_syms=NULL),生产路径无外部表现,本批以夹具 def_sym 登记形参规避,未改后端;④ corearch 二进制 sha 变(globals.cr 属 common_files ⇒ 数据段布局变;自身零引用零行为变化;产品 ELF canary + 72 档 check 面 + 影子面三重零泄漏)。 * feat: R2 P3 Task 1——定长退役收口(array/slice 方向 + 变型表 + N 读取面守门;修切片→固定长 静默放宽) 方向(checker,array_len_constraint_ok 内,签名不变):`[T;N] <: [T]` 放行 / `[T] ⊄ [T;N]`(无法证 len==N ⇒ **决定** 0,不给 -1 当 0);新增 array_len_walk(a,b,dir) 三态递归(数组元素/指针元素/引用元素/切片元素/元组字段/泛型实参 6 位),对称核 array_len_constraint_sym 供无方向的 hotpatch 站。⚠ 10 判定点参序**归一为(源,目标)**——P2a 落点时实参序随各站原 type_equal 调用继承、未归一(站点 5 本即(值,声明);6/8/9/10 与 unify 三处为(目标,源)⇒ 换序):换序零副作用 = 身份面 ty_equiv 双向 + 同 kind N 比较对称(码级)且全语料 72 档旧/新逐字节同日志(实测)。 变型(引擎,ty_variance_of + tt_list_variance/_at 取代 P0「参数仅同形判等」):只读 ref 元素协变(ty_sub 三态直传)/ 可写 ref 不变(条件变型单点收敛在比较函数内);AK_SEQUENCE/AK_PTR 元素不变(数组/切片本语言**可写**——探针 `s := a[0..3]; s[0] = 9;` rc=0 实证);AK_REF 槽 0 = mut 标记(原子令牌:比较**可判定**,不同标记 ⇒ 0 不回 -1)。桥接:序列项 b 槽 = 固定性位(N/-1,**不入等价身份**——`[int;3] ≡ [int;4]` 实测 1);AK_REF 链 = [mut 标记, 元素](mut **成为判定维度**,替代旧「不入链」= 引擎把 `&T` 与 `&mut T` 判等价的静默放宽;legacy 比 extra ⇒ 替换后失守,本批关闭)。 实测修复的本批回归:固定性经**嵌套元素位的节点同一性**泄进判定——`[[int;3];2]` 与 `[[int];2]` 的元素项除 b 外逐位相同却节点不同 ⇒ 引擎 -1 ⇒ 回落 legacy ⇒ 措辞退化为普通不匹配且身份路径出现 N(违 R1 不变量)⇒ 新增 tt_type_elem_same/tt_seq_same(**双方皆 AK_SEQUENCE** 时忽略 b 的结构相等;其余构造子节点同一性——AK_NAMED 的 b = 行号是**身份**);并硬化 Task 0 变体身份令牌(裸 name 索引 → sh_name_token 原子:索引入链会被结构比较当术语下标读,实测两起误判)。 收紧清单 6 条(旧接受→新拒绝;全语料命中 0 = 未触及自举源码):pA 返回位切片→`[int;3]`(计划明令 rc=0→1 + 专属措辞 Array length constraint not satisfied)· pF struct 字段 `[int;3]`←切片 · pIf1 分支合并(结果类型 = then 侧)· pN3 嵌套位同律 · pE1/pE2 `&mut T`↔`&T` 双向拒(计划 pE 原文「反向拒绝」= mut 面**等式**语义)。语义保持:pB/pAsgnSlice/pD/pIf2 拓宽全保(参序归一的判别用例)、pC/pN1/pN2 异长拒绝措辞不变。 判据:selftest-types 229→**249**(+20 例,含嵌套身份双钉与站点三态);ELF canary 95084e7b…d475 IDENTICAL;.ccr ecd7a9df… IDENTICAL;影子对拍 72 档旧/新逐字节同(logs+dumps diff 空)聚合 decisions=29267 agree=29267 old_stricter/old_looser/unknown/replace_* 全 0、站点 1/2/4/6 零命中(新能力面语料零命中 ⇒ 由 19 探针 + 突变控制承担);CI selfhost-tests rc=0 0 FAIL · full-bootstrap corec2==corec3 IDENTICAL · 15 套件全绿;突变控制 M1/M2/M3 = 241/247/246(方向/mut/嵌套身份三组**专一**咬合 + 探针面交叉证),三次复位后 249/249 + 三二进制 sha 逐字节复原。 登记(未修/后续面,详见 .superpowers/sdd/p3-task1-report.md):**EXPR_LET 站点无任何兼容检查**(既有洞:`x:[int;3] = s` 与 `x:[int;4] = [1,2,3]` 旧/新皆 rc=0——非 10 判定点,追加即新硬错误门,建议专批)· 不变槽「确定不等价 ⇒ 0」需先有「链元素皆类型项」不变量 · 只读序列视图第二层协变未做(`&[T]` 元素仍不变)· 符号档 VC 义务不做(动态档 = 运行期检查,编译期不放行)。 * feat: R2 P3 Task 3——match 穷尽性真判定(补集空性 + 具体变体反例)+ TM03 硬门 + 覆盖集 判据本体 = 引擎补集空性 ty_exhaustive(domain \ ⋃patterns 的可满足性);引擎不在本 Task 文件 面内(协调者裁决)⇒ 两处引擎侧缺口在**消费点**消化:① 域守卫(仅枚举 scrutinee 判穷尽,非枚举 域 = **不判**,显式登记)② 模式守卫(字面量 / struct 模式 / 异枚举变体名 = 不可映射 ⇒ **不判**, 不得当 ∅ 凭空判「不穷尽」、也不得当 ⊤ 凭空判「穷尽」——三态纪律)。 反例给**具体变体名** = 走**变体逐项覆盖位**命名:引擎 witness 原始形态含空析取支,本批实测 ty_equiv(witness, 缺失变体) == **-1**(未覆盖面——Task 0 报告记「== 0」,此处按实测修正)∧ ty_sub(witness, 缺失变体) == 1 ∧ 反方向 == -1 ∧ 可空 == 1 ⇒ witness 项不可作显示名。 - ty_shadow.cr:sh_match_pat_kind / sh_match_pat_variant(裸名 + `Enum.Variant` 限定名:末点 后段解析、前缀须为本枚举名、**不调 str_intern**——.ccr STR 段 = g_strs 全量约束;名字槽 = **ast_a**,parser/ir_gen 契约)· sh_match_variant_term · sh_match_bit · sh_match_first_missing · sh_match_exhaustive(三态 1/0/-1) - checker.cr EXPR_MATCH:模式项 CONS 链 + 覆盖位 + 不可映射标记(与臂体推断同趟采集,臂体诊断 顺序不变)→ 预算前后隔离(照 type_equal_engine 窗口纪律)→ 0 ⇒ error[TM03] 含缺失变体名; TM04 冗余臂(覆盖面无贡献:重复变体 / 通配后再见)落**软面**(同 Rust unreachable-pattern 警告口径) - main.cr:EC_TM_EXHAUST(9003) 入硬名单。修复前 match **零检查**(EC_TM_* 仅定义零 raise): 缺臂 ⇒ rc=0 + 产物照出,未匹配值静默得 0(探针 p1b 实测 rc=100 = 0+100,零信号)。开门依据 = 全语料 report-only 72 档 check 日志与基线逐字节同(清单为空);自源零命中(无「停下上报」) - 用例:type_selftest 249 → **263**(14 例:全覆盖 / 缺一+反例名 / 通配吸收 / 单变体 / 无臂 / 域守卫 / 不可映射守卫 / 空枚举域空洞穷尽(实测 1——计划原文「= 0」偏差登记)/ payload 混合 / 名字解析 / 类别分类 / 重复臂 / 泛型应用行);tests/selfhost/test_match_exhaust.py **16 例** (正 9 = 解释器+ELF 双路径 · 限定名 · payload · 泛型枚举 · 形参位;负 4 = 缺臂/无臂/check/run 双路径 + 反例名 + **无产物**;登记 2 = 非枚举域不判 / 不可映射模式不判;软面 1 = TM04 非硬门) 入 ci/run.sh selfhost-tests 收紧清单(逐条,全语料零命中):缺臂 / 无臂 / 枚举形参位缺臂 rc=0+产物 → **rc=1 + 无产物** (error[TM03] + 具体缺失变体名);TM04 仅新增软诊断(rc 面不动)。不判面(反例保持 rc=0): 非枚举域(p5 int / p15 struct)、不可映射模式(p7 混字面量 / p16 异枚举变体)、空枚举域(p14)。 判据:build rc=0(三守卫 error[=0 · undefined=0)· selftest-types **263/263** · iface_ops **76/76** · ELF canary 95084e7b…d475 **IDENTICAL** · .ccr ecd7a9df… **IDENTICAL** · 影子对拍 29,442 判定全 agree + 四零桶(站点 1/2/4/6 零命中、3 仅 2 次——新能力面语料零命中, 证据由探针/用例承担)· 突变控制 M1(域守卫)/M2(前缀守卫)/M3(反例命名)→ **257/263** (红例逐突变互不重叠)+ 行为集 12/15,复位后 263/263 + 三二进制 sha **逐字节复原** · CI selfhost-tests rc=0 / 0 FAIL(新档 16/16)· full-bootstrap rc=0 + corec2==corec3 IDENTICAL + N06=0 + 冒烟 42 · suite / lsp / bootstrap-tests 绿(`check` job 两条 TF01 为既有 red 划界—— 基线二进制 × 新源同现,仅行号随源增长位移) * feat: R2 P3 Task 4——联合/可选(T? = T ∪ null;退役内建 Option 注册与 EXPR_TRY 名字解包;+T0 交接① 枚举载荷类型节点列) 决策点裁定(计划推荐 a):null = 引擎原生第九原子 AK_NULL(值域单点;ak_disjoint 与一切异类不相交, 与 AK_DYN 按 DYN 相容律;不入 iface 条目表 ⇒ iface_kind_of 回 -1 = 无操作许可,门全拒)。 行面:TYP_OPTIONAL(data=内层行)/TYP_NULL/AST EXPR_OPTIONAL(T? 目标形态,零名字依赖——删 parser 的 str_intern("Option"));桥接把 T? 译作 union(内层项, null 项)——引擎零新规则。 退役内建 Option 注册(checker):Option 不再是内建类型名(零行零符号);Some/None 走关键字名分派 (用户同名变体 def_sym 优先 = Task 3 p20 语义保持);EXPR_TRY 按类型项结构解包(废 base_name 串)。 判定点:type_compat_strict 新增可选目标子类型注入(T ⊆ T?、null ⊆ T?;T? ⊄ T 仍拒)——该 kind 仅由 T? 产生(语料零命中)⇒ 既有站点接受/拒绝集合逐点不变。match 可选域(两分支:有值=Some/null=None)+ 反例命名('Some'/'None');TM03 既有门接新域(未新增硬门)。 T0 交接①:枚举变体载荷类型节点列(OFF_EV_TYPE_NODES,照 struct 先例;ESZ_ENUMINFO 2360→4408)+ 消费者 sh_variant_payload_term + 入库断言通道 --verify-evp-nodes(collapses = 裸码列丢真值的槽数)。 修掉两处既有缺陷(本批暴露):① parser T_SOME 分支违反 EXPR_ARG 链契约 → checker 挂死 rc=124(实测); ② 退役早期 def_sym 后 g_syms 可为 NULL + bootstrap 不短路 && ⇒ sym_kind(-1) 读 NULL 段崩 rc=139 (sym_* 四读取器加范围闸;根因 bootstrap 不短路仍归 T0 §5-③ 裁决)。 判据:自举 rc=0 · selftest-types 279/279(+16 t4.*;4 突变增量法咬合 6 例 + 行为 4 例,复位三 sha 逐字节复原) · test_iface_ops 76/76 · test_match_exhaust 16/16 · test_optional 12/12(双路径 rc + check 三路同证) · ELF canary 95084e7b…d475 IDENTICAL · .ccr 实测变(STR 恰 -10B = "Option" 退役 + 索引 -1;其余段尺寸/结构零变化, test_ccr_v7 27/27)· 72 档 check 诊断码集合/rc 逐档同(仅自源行号位移)· shadow decisions/agree=29687 全零 + dump 逐字节同 · CI selfhost-tests/bootstrap-tests/suite 全绿 · full-bootstrap corec2==corec3 IDENTICAL + N06=0 + 冒烟 42。 登记:运行期表示未统一 / T? 与用户 enum Option 分家(T2)/ EXPR_TRY 用户枚举不再按名解包(T3)/ 枚举变体·载荷 >16 越界写(既有)——详见 .superpowers/sdd/p3-task4-report.md。 TODO #32:EXPR_LET 站点无任何兼容检查(协调者交办;含 T? 漏放实测证据)。 * feat: R2 P3 Task 5(P3a 半边)——泛型约束:结构/枚举泛型约束保留(parser 侧表 + 空槽预填 -1)+ 实例化点 ty_sub 本质轴判定(TG02 软诊断 + tt_witness 反例文本 + 诊断去重侧表)+ F4 形参链导航修复(前扫 EXPR_PARAM;旧 pn+1 落在类型节点上致后续形参声明类型被绕过/凭空绑定)+ monomorph 实例键类型项化(inst_key_of_ti 规范结构名 / inst_type_node_of_ti 节点合成 / ti 型替换 / 克隆期嵌套调用绑定重映射);用户接口 T: I 满足判定 = P3b 阻塞登记(iface_satisfies 未交付 ⇒ 结构/枚举侧 -1 不判 + 函数侧回落既有 check_iface 逐字未动);MAX_GENERICS=4 按计划二选一取「显式登记」(>4 = parse 期 P01/TA08 非静默截断、语料最大 2);monomorph.cr 自 corearch 清单迁出(零代码引用逐符号实证:仅 globals.cr 注释提及);判据 selftest-types 288/288(+9)· test_generic_constr 14/14(新)· iface_ops 76/76 · match_exhaust 16/16 · optional 12/12 · test_impl 7/7 · ccr_v7 27/27 · ELF canary 95084e7b…d475 IDENTICAL · 同源 72 档语料诊断零差异 · 影子 decisions=30338 agree=30338 全计数 0 + dump 同 T4 · ptr_arith .ccr 逐字节同 / generics_test +370B(仅实例名 get_val[unit]→get_val[int]+get_val[string] = 反折叠,段增量恰一个实例)· corec2==corec3 + N06=0 + 冒烟 42 · 突变 4 条(F4/本质轴判定/实例键/约束保留)逐条咬合、复位后二进制逐字节复原;台账 = 收紧 3(Box[string] · EBox[string] · f[T:int]←s 增 TG02)+ 放宽 1(F4 假拒 TF01→rc=0)+ 实例名忠实化 6 例 * docs: R2 P3a 收官(回归 + 自举 + 判据 + 收紧台账 + P3b 交接包)——全量回归 49 套件 rc=0(42 selfhost + 7 bootstrap;首次枚举 48/49,唯一 red = T5 漏改 project-mode 清单:src/targets/x86_64-linux/_import.cr 的 import monomorph 未随 concat 面同批迁出,而 monomorph 类型项化引入 checker/parser 层依赖(get_type_kind/alloc_node 族 46 处)⇒ project-mode corearch 构建 33×error[N06] 静默未定义(rc=0 + 产物照出、stage 链互测不可见);守门者 test_backend_bootstrap.py 未挂 CI(#31)⇒ P3a 五任务回归面全未覆盖、收官枚举首次暴露;修复 = 删该 import(与 concat 面同一零引用证据:定义符只出现在 globals.cr 注释),复验 = 该套件 rc=0(stage1/2/3 逐字节同 + smoke/O2 全绿)+ 构建日志 error[ 33→0 + ELF canary 不变 + 三二进制 sha 不变(该文件不入任何 concat 清单))· 五 CI job:selfhost-tests/bootstrap-tests/suite/full-bootstrap rc=0、check rc=1 = 既有 red(2 条 TF01 误报 lits_copy:10069 / ty_memo_slot_no_grow:10292——行号随源码增长漂移,同源对拍两侧一致,非本批引入)· 自举链 corec2==corec3 cmp IDENTICAL(2404414B)+ N06=0 + 冒烟 42(corec/corec2 双通道)+ corec3 --help rc=1 既有约定 · ELF canary 95084e7b…d475 IDENTICAL(修复前后各一次,clean-cache)· .ccr:ptr_arith 54e3856b…(88943B = T4/T5 记录值)/ generics_test 130974B(反折叠 get_val[int]+get_val[string])· 终态影子 decisions=30128 agree=30128 全计数 0 + agree==decisions + sum(sites)==decisions;check/shadow/dump 三面对拍 diff -r 空(旧=Task 0 起点二进制 8f69e346 × 新源 == 新二进制 × 新源);站点覆盖 assign-node=22785/fn-body-ret=5338/if-branch=1971/struct-field-type=28/array-elem-type=4/generic-apply-base=2(1/2/4/6 零命中、3=2 次);30,338→30,128 的 −210 精确归因 = 上述修复使 monomorph 退出 target main.cr/_import.cr 两档导入闭包(逐档切换实测 3577→3472、3542→3437,各 −105)⇒ 非语义变化 · 收紧台账汇总(T0 0 / T1 6 / T3 6 / T4 3 / T5 3 = 18 条 + 诊断面新增 TM04 软 1 + 放宽 8 + 实例名忠实化 6 + 挂死修复 2 端;全语料零命中、自举源码零改造)· 文档 = 计划 Task 7 六步勾选 + 实施状态总表(P3a 五任务 ✅/P3b ⛔)+ 新增附录 B「P3b 开工前必读」(B.1 唯一阻塞 iface_satisfies 未交付 + 三处已就位接线点·gen_constr_satisfied 首分支/sh_iface_shape_term/ty_variance_of;B.2 台账逐条;B.3 语料证据 + −210 归因;B.4 登记面 19 条含函数侧 save_func_gen_constrs 稀疏零初值风险、never 非 ⊥、Box[S] 判 -1、MAX_GENERICS=4、EXPR_LET=#32、运行期可选表示未统一、bootstrap &&/|| 短路根因;B.5 P5 继承含「等价面是否展开」须裁决;B.6 回归修复档案)+ TODO #34(P3a 落地:五提交链/判据/台账/P3b 剩余面/登记面/P5 继承)+ fixed-array-retire 未办句划销(N 与固定/视图维度已出等价身份、入变型/方向规则)+ #19「P3 面遗留」收口(动态档=既有机制、符号档=VC 义务显式不实现)+ #26 判据继承注 + #31 缺口实际代价实证与复测(selfhost-tests 18/42、bootstrap-tests 3/7)+ spec §9 P3 行标 P3a ✅(含未覆盖面登记)+ findings §13「P3a 后复跑」 * docs: P3a 阶段评审收口(errata)——影子终态数纠 30338→30128(spec §9 / TODO #34 / 计划 Step 2 三处,含计划 :188 同族陈旧处;−210 归因 = monomorph 退出 target 两档)+ 运行期可选表示缺口升级一等条目(计划新增 B.7 + B.4-6 重写 + B.1 与 TODO/spec 指针;本轮复跑实测:check/build rc=0 零诊断 + 产物/解释器双路径 rc=139 + 非 P3a 引入(p3t0_base_bin 同 rc);勘误:纯通配臂实测不崩 rc=7,崩溃面 = Some 臂载荷解包,含 Some+通配混合臂 139)+ B.4-7 措辞纠(静默仅 check 面;build = .ccr 读回 ccr_io.cr:1121 rc=1,17 变体实测确认)+ 新 TODO #35 枚举表写入侧无护栏(17 变体槽起点 4360 = OFF_EI_VARIANT_COUNT,越 ESZ_ENUMINFO 224B;#8 同族含护栏方向) * feat: R2 P3b Task 0——iface_satisfies 契约回补(P2b 交接面 ① 交付;P3b 唯一阻塞解除) 契约(type_engine.cr):iface_satisfies(t_ti, iface_ni) 三态 1/0/-1 + 每查询预算隔离(IFACE_SAT_BUDGET; 照 type_equal_engine 先例);轴分派 A 横切形状名(iface_shape_* 表,本批空表;条目细化归 Task 2 Step 1) → C 用户接口(sh_iface_shape_term 路由(Task 6 落地前恒 -1)→ 结构谓词 iface_user_satisfies)→ B 本质轴 (= P3a gen_constr_satisfied 引擎面逐字同口径);轴优先序 A > C > B(「接口名 vs 原生类型名」保持 P3a 现状 = 接口优先,selftest isat.axis_order_* 两例钉死)。轴 C 谓词 = 与 check_iface 同源(方法名在位 + 参数计数 + 返回码),域 = 命名行(TYP_NAMED / TYP_GENERIC_APPLY 基名;原生/dyn/泛型形参 ⇒ -1 不判——「查不到」 绝不等于 0);查名走 g_methods 三元组(parser impl 分支唯一写点)**零 str_intern**(.ccr STR 段守, isat.no_str_intern_on_missing + 突变 M3 咬合)。 接线(附录 B.1 三处 + 一处说明书):① gen_constr_satisfied 首分支 → iface_satisfies(1 提前返回; 0/-1 一律回落既有 check_iface 名拼接路径 ⇒ 函数调用点措辞/去重/rc 逐字未动,0 → 新措辞的切换仍归 Task 6 Step 3);② 新消费者 gen_inst_constr_satisfied(结构/枚举实例化点**直取真值**)⇒ T: I 在实例化点 **真判定**(P3a 登记面关闭),诊断措辞与既有函数调用点路径一致(does not satisfy interface 'I'); ③ sh_iface_shape_term 仍恒 -1(阻塞精确定位 = Task 6 Step 1 签名类型项化)但升为轴 C 第一路由(切换点 就位)+ gen_constr_raise 措辞分派(接口名约束不附反例——接口非类型行、反例项不可得 ⇒ 不谎报)。 收紧台账(6 例;全语料 72 档**零命中**):实例化点接口约束违反 rc=0 零诊断 → check rc=1 + error[TG02] + does not satisfy interface 'I'(TG02 软诊断,check rc=1 / build rc=0 + 产物照出——与既有约束检查同门 同码,**未新增硬门**);test_generic_constr.py 的 struct_iface_constr_unjudged(P3a「不判」钉)按台账改为 ..._violated + 新增正控 ..._present_accepted。 判据:自举 rc=0(警告集与基线逐条同:344 = 344,零新增;corec 函数数 875→889)· selftest-types 288→**304/304**(+16 isat.*)· 新套件 tests/selfhost/test_iface_satisfies.py **17/17**(真 parser 输入三路 同证;已挂 run.sh selfhost-tests,例数注释 288→304 同步)· iface_ops 76/76 · generic_constr 15/15 · match_exhaust 16/16 · optional 12/12 · impl 7/7 · 更宽 selfhost 集 **43/43 rc=0**(全枚举,含 CI 未挂的 24 档)· ELF canary 95084e7b…d475 IDENTICAL(clean-cache)· .ccr ptr_arith/generics_test 逐字节同 (本批**零 .ccr 变更**)· 同源对拍(旧二进制×新源 == 新二进制×新源)check 日志逐字节同 + 归一化诊断 610 形态零增零减 + 影子日志/转储逐字节同 · 影子 decisions=30271 agree=30271 全计数 0(30128→30271 = +143 输入增长,逐档精确归因 main/ccr_io/targets_import 各 +47;旧二进制 × 新源同值 ⇒ 非语义变化)· 五 CI job:selfhost-tests / bootstrap-tests / suite / full-bootstrap **rc=0** + corec2==corec3 IDENTICAL (90e8f773…)+ 冒烟 rc=42,check rc=1 = 既有 2 条 TF01 误报划界(行号随源位移,同源对拍两侧一致)· 突变控制 4 条逐条咬合(M1 缺方法→-1:selftest 1 例 + 行为 4 例同时红;M2 轴序:1 例;M3 名拼接:1 例; M4 缺出口复位:1 例——**M4 首轮未咬合**,暴露「只走节点同一快路径 ⇒ 预算断言空转」的弱用例,已改为 快路径 + 非同一比较双路),复位后 build/corec 逐字节复原。 登记(详版 = 计划附录 B.1 首注 + TODO #36):接口签名槽 = 映射层编码(parser 两侧同取 unpack_type; 非原生类型塌缩为码 0 = TY_INT,self/&self 槽两侧均 0)⇒ 逐参数类型不参与判定、返回码语义 = 编码相等 (inst_encoding_limit_named_ret_pinned 钉住现状;解锁 = Task 6 Step 1 后由形状项包含判定取代)· 形状项口径勘误(AK_SEQUENCE 元素槽不变 ⇒ 序列接口形状项须用 ⊤ₖ 形,isat.axis_a_shape 即该形)· Task 2 Step 1/3、Task 6 Step 1/3 剩余面与 P5 继承见报告。 * feat: R2 P3b Task 2——横切接口接线(序列/可索引/可迭代/product 形状满足判定取代 TYP_* 直比;只读协变/可写不变两形状) - 形状条目(Step 1,iface_registry.cr 横切形状段,六条唯一构造点):sh_shape_seq (⊤ₖ(AK_SEQUENCE);Task 0 §1.3 勘误——sequence<⊤> 参数链在不变槽落 -1)· sh_shape_seq_ro(序列本体 ∪ &[⊤ₖ(SEQ)]:只读视图由 AK_REF **协变槽**判定 = 「只读 = 协变」, T1 §6-④ 在形状面闭合;mut 等式语义 ⇒ &mut[T] 不满足)· sh_shape_seq_rw(本体面 + 拒绝 只读视图;可写视图形状**不可表达** = 不变槽遇 ⊤ 落 -1,登记 + 用例钉死)· sh_shape_indexable(⊤ₖ(SEQ) ∪ ⊤ₖ(STRING),与 IP_INDEX 许可集逐行同集)· sh_shape_iterable · sh_shape_product。零 str_intern(.ccr STR 段守,selftest 钉);名字面生产路径不注册 (注册名 = 驻留 ni,初始化路径 str_intern 会动 STR 段)⇒ 无名字消费者走 iface_satisfies_term。 - 消费点换位(Step 3):checker.cr 索引兜底门 → 可索引形状(-1 回落既有许可表,**不**折算 三态);range 分支 → 序列形状 ∧ 固定性位(ty_shadow.cr sh_seq_fixed_len_of_ti 读序列项 b 槽, 表示层提示 = spec §5.1)。结果三分支(arr→元素+F2 / slice→元素 / str→int)原地保留(A.2 #16-18)。 - 判据:selftest-types 304→**319/319**(x2.* 15 例 = 形状满足 7 + 逐行枚举等价 2 + 注册/复位 2 + 零驻留 1 + 三态 1 + 接口形状项 1 + 固定性判定面 1;两条全类型行枚举 = 消费点等价前提,两路咬合 含「非同一比较」断言防快路径空转);新套件 tests/selfhost/test_xcut_iface.py 12 例挂 run.sh; test_iface_ops 76/76 · test_iface_satisfies 17/17 · test_generic_constr 15/15 · test_match_exhaust 16/16 · test_optional 12/12 · test_impl 7/7。 - 行为保持(主判据):12 例探针老/新两二进制逐例同;全语料 72 档 check 面 diff -r **零差异** (rc 33×0/39×1 同);**收紧 0 · 放宽 0**(无新硬门 ⇒ report-only 前置未触发)。 - 零变化面:ELF canary 95084e7b…d475 · .ccr ptr_arith 54e3856b…(88943B)/ generics_test 97be2f9e…(130974B)逐字节同;影子对拍 30400/30400 全计数 0(老二进制×新源同值 ⇒ 零语义变化; +129 vs T0 = 新源码行自身站点);五 CI job(check rc=1 = 既有 2 条 TF01 误报划界)+ 更宽 selfhost 枚举 44/44 + 自举链 corec2==corec3 3597aaf8… + N06=0 + 冒烟 42。 - 突变控制:M1 序列形状→⊤ₖ(PTR)(selftest 7 红 + 探针 4 红)· M2 丢弃固定性维度(探针 1 红)· M3 可索引 += ⊤ₖ(NAMED)(selftest 1 红 + 探针 1 红);复位后二进制逐字节复原 (corec 6919bb54… / corearch e7bd4537…)。编译耗时无回归(main.cr 2.46-2.51s vs 2.58-2.59s)。 - 落点:src/compiler/{iface_registry,ty_shadow,checker,type_selftest}.cr · tests/selfhost/test_xcut_iface.py · src/ci/run.sh(挂套件 + 例数注释 304→319)· 计划 Task 2 步骤/状态表/附录 B.1 交付注 · TODO #37。 * feat: R2 P3b Task 6——impl 契约(签名类型项化 + 形状项逐成员判定 + mangling 退役)+ 覆盖集 Step 1 签名类型项化:g_ifaces 方法条目新增类型**节点**槽(OFF_IFM_PARAM_NODES/RET_NODE/ SELF_MODE;ESZ_IFMETHOD 88→168、ESZ_IFACEINFO 1432→2712——不入 .ccr 序列化,实测逐字节同)+ parser 写点;裸码槽保留(S6 站点返回型映射值域 = 映射层编码,P2b 已钉);参数裸码槽自本批起 零读者(登记)。上限 16/8 **显式登记保留**(超限 = 硬错 rc=1 非静默截断,新套件两例钉死; 解除面 = 方法表迁侧表,按 B.4-7「先加护栏、再评估解除」口径)。 Step 3 满足判定接引擎 + mangling 退役:方法解析改 g_methods 表查询(type_has_method / check_iface / check_impl_for / 泛型约束路径全走 iface_find_method;**零 str_intern**——旧形态对 缺失名会增长驻留表);sh_iface_shape_term 建形状项(方法集 = product of fn,b 槽 = 方法名); 判定 = **逐成员包含**(方法名表查询 + 接收者模式显式比较 + 签名规范项经引擎结构比较原语 tt_list_same)。两处按实测重定:① 整形状 ty_sub 路由**不可用**(引擎对 AK_NAMED 不展开 ⇒ 命名行实参恒 -1;product 比较是结构相等而非子集包含 ⇒ 带额外方法的实现方误拒);② 逐成员比较 **不得**用 ty_sub(引擎把不变槽的「确定不同」上抛为 -1 ⇒ 签名不符退化为「不判」= 静默通过面 复活,突变 M3b 实证 8 自测 + 3 行为例红)。**崩溃修复**:泛型体内方法调用(fn f[T: I] 的 x.m())旧态 = 合成 "T.m" 串 + 克隆期文本替换 ⇒ 实例体调用目标悬空、产物运行 rc=139 ⇒ 改为 实例化时按具体类型查表解析(CALL_FLAG_IFACE_METHOD + monomorph 的 EXPR_CALL 克隆分支)。 裁决(报告 §7 逐条):① **用户轴保持结构口径**(方法集包含),不采 spec §2.3 名义口径 (impl I for T 声明即满足——采纳会翻转既有钉死用例 5+ 例,且属语义争议:spec §0 裁决 6 停下 上报);② 形状**名字面**生产路径仍不注册(接口形状项按接口自身名 ni 建,零新串 ⇒ 零 .ccr STR 增长);命名消费语法仍待裁。 判据:selftest-types 319→**333/333**(+14 ifc.*:签名四维/形状项三面/表查询/同源/三态/零驻留); 新套件 tests/selfhost/test_impl_iface.py **20 例**(挂 run.sh;正向双站点同证——Task 0 M4 教训); 六套件全绿(test_iface_satisfies 按台账改 1 例:inst_encoding_limit_named_ret_pinned → inst_encoding_limit_named_ret_rejected);收紧 4 条 / 放宽 0;ELF canary 95084e7b…d475 IDENTICAL + .ccr 逐字节同(88943B/130974B——类型层零泄漏发射面);同源对拍 72 档 check 日志 diff 空 + 影子 30628/30628 agree 全计数 0;五 CI job(check = 既有 2 条 TF01 误报划界)+ 更宽 selfhost 45/45 + test_backend_bootstrap rc=0(附录 B.6 必含档);突变控制 4 条(M1 应用展开 / M2 接收者 模式 / M3b tt_list_same↔ty_sub / M4 monomorph 解析——**行为级独有牙**如实登记)→ 复位后 二进制逐字节复原 * docs: R2 P3b 收官(Task 7b)——全量回归 + 判据复验 + 统一台账 + P3 总状态 + P4 交接包(附录 C) 全量回归(committed tree e838f18b 独立复跑):五 CI job = selfhost-tests/bootstrap-tests/suite/full-bootstrap rc=0、check rc=1 = 既有 red 划界(check src/compiler 恰 2 条 TF01 误报 lits_copy:10162 / ty_memo_slot_no_grow:10385——check 命令按 F19 以非零反映任意诊断 ⇒ rc=1 由该既有误报产生,同源对拍两侧一致,非本批引入;该对亦现于 corec2 构建日志 10177/10400 = rt_src 前置的 +15 偏移,error[ 计数 2);全枚举 45 selfhost + 7 bootstrap 逐档 rc=0(含 #31 未挂 CI 档,test_backend_bootstrap rc=0 = 附录 B.6 必含档)。 判据复验(本轮实跑,fresh clean-cache):ELF canary 95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 IDENTICAL · corec2/corec3 cmp IDENTICAL(双 2542222B,sha 03c9812fc735ec4b…)+ N06=0(两日志)+ 冒烟 42(corec/corec2 双通道)+ corec3 --help rc=1 · .ccr 零变更(ptr_arith 88943B 54e3856b383301be… / generics_test 130974B 97be2f9e7431fde5… 与 Task 0/2/6 记录值逐字节同)· selftest-types 333/333 · build/corec sha 9a583215af8397cc… = Task 6 复位基线(构建确定性)。 统一台账(P3b = T0 6 · T2 0/0 · T6 4 ⇒ 收紧 10 / 放宽 0 / 崩溃修复 1):T0 实例化点 T: I 违反 rc=0 零诊断 → rc=1 + error[TG02](6 例 + struct_iface_constr 重钉)· T2 换位批零差异(12 例探针两二进制逐例同 + 72 档 check diff -r 空 + 影子旧×新同值)· T6 签名四维(参数类型/返回类型/应用实参/接收者模式)→ rc=1 + TG02(4 例 + inst_encoding_limit 重钉)+ 崩溃修复(泛型体内方法调用旧产物运行 rc=139 → 正确值 7/9);全语料零命中 + 同源对拍零差异(30271/30400/30628 判定,全计数 0,站点覆盖同报)+ 突变控制 11 条(4+3+4)逐条咬合;TG02 = 软诊断(check rc=1 / build rc=0 + 产物照出),P3b 未新增硬门。 P3 阶段总账(P3a+P3b):收紧 28 · 放宽 8 · 软诊断面 +1(TM04)· 实例名忠实化 6 · 挂死与崩溃修复 3 端;五项目标能力全部接通;selftest-types 95→333;新套件 6 个;判据面全程稳定(canary IDENTICAL / 自举链 / .ccr 变更面仅 T4 STR −10B + T5 generics_test +370B)。未开工清单(终态):iterable 接线(EXPR_FOR 零类型检查)· 形状命名消费语法(T: 可索引)+ STR 段增长裁决 · 整形状 ty_sub 路由(实测不可用)· MAX_* 解除 · TODO #38 名义 vs 结构(维护者)· B.7 可选表示统一 · #32(EXPR_LET 无检查)· #35(枚举写侧护栏)。 P4 交接包(新写计划附录 C):P4 无独立计划(实查)⇒ 附录 C = 载体盘点(类型项 DAG/g_ifaces 方法条目节点槽/形状表/g_impl_for/g_methods 全在进程内存、零 .ccr 序列化——ccr_io.cr 全文件 iface/impl_for/g_methods 命中 = 0;DFNode.TK = 粗类型码槽 ir_gen.cr:190-206)+ .ccr 加段四面(CCR_SEG_COUNT/calc 尺寸/save 写侧/load 读侧闸与 have{1..6} 标量族/test_ccr_v7 结构断言;tag 7+ 预留)+ 翻转钉 7 条(零 str_intern 三守门/.ccr 记录值作废换结构断言/iface_count()==13 硬值/ELF canary 不得变等)+ 待裁决 8 条(形状命名语法+STR 增长 · #38 名义 vs 结构 · 13 条表扩列 vs 引擎侧另表 · 整形状 ty_sub 路由 · B.7 可选表示与 TYPE 段同批 · MAX_* 统一处置 · never/符号档 · TK 升格发射面判据);并纠正 spec §6.1 的失效文件名引用(2026-09-07-lattice-ir-v7-format.md 不存在 ⇒ 实为 2026-09-09 版)。 文档:计划新增 Task 7b 节(六步 + 统一台账 10 条 + 语料证据表 + P3 总状态 + 未开工清单)+ 附录 C + 实施状态总表(Task 0b/2/6 行填实际提交 sha + Task 7b 行 + 表头注)+ 附录 B 存档注(P3b 开工期档案)+ B.7 头注(P4 必修);spec §9 P3 行「P3b 未开工」→「P3b ✅ 已实施」(三提交链 + 判据 + 未开工面)+ 未覆盖面两条状态更新(MAX_IFACE 硬错护栏已立 / B.7 转 P4);TODO 新增 #39(本批落地)+ #34 的 P3b 剩余面 bullet 更新为已全部交付(指向 #36/#37/#38)。#38/#32/#35 三条指针逐字复核无需改动。 判据:报告 .superpowers/sdd/p3b-task7-report.md(未跟踪面,不入提交);证据脚本 /tmp/p3b7/regress.sh + 日志 /tmp/p3b7/*。 * docs: R2 P4 载体批实施计划定稿——8 任务(引擎核纯化拆分 / 段机制 TYPE=7·IFACE=8 + 版本 7→8 / TYPE 段 / IFACE 段 / DFNode.TK 双槽 + .cir v18 + canary 证明 / 可选表示解包侧 / MAX_* 护栏 / 收官);含 8 条维护者裁定(2026-09-12 全按推荐)与三项实测待定项(段尺寸量级 / STR 增长字节 / Task 5 判别载体) * refactor: R2 P4 Task 0——引擎核纯化拆分(iface 簇 → iface_axis.cr;iface_kind_of 移出注册表)为 corearch 链接面开路;零行为变化 拆分(纯搬运,逐字):新建 src/compiler/iface_axis.cr = 新头注 + type_engine.cr:613-772(IFACE_SAT_BUDGET + iface_satisfies/_term/_ti + iface_find_method + iface_user_satisfies/_ii,160 行)逐字 + iface_registry.cr:271-293(iface_kind_of + 头注,23 行)逐字;两原位各留一行指路注释(type_engine.cr 772→612 行、iface_registry.cr 433→411 行)。逐字性 = 块级字节比对(搬迁块 == 3f8885ec 版原文件行区间,且在 iface_axis.cr 中逐字存在、于原文件不存在)。 红态实证(Step 1,两面都测):① concat 面(/tmp 内存清单:corearch 30 文件 + type_terms/type_engine/iface_registry = 33)→ resolver 16×Undefined name + checker 16×Undefined function(13 distinct:find_iface(2)/find_func/decl_name_of_ti/gen_constr_type_ti/sh_term_of_ti(3)/sh_shape_member_at/sh_iface_shape_term/sh_iface_self_mode/sh_func_self_mode/sh_func_sig_term/get_type_kind/get_type_data/sh_ref_mut_marker)= 构建硬失败 rc=1(bootstrap 面不产 error[ 前缀,guard_build_log 计的是未定义标记);② project-mode 面(自宿主 corec × /tmp 工程:目标 _import.cr + 三行 import)→ rc=0 + 16×error[(14×N06 + 2×TF01)= B.6 静默未定义形态。拆分后同集残差 = 仅 sh_ref_mut_marker(1 处,iface_registry.cr:411 sh_shape_seq_ro 对 ty_shadow 的引用,非本任务移出面)——登记给 T2/T3 加 corearch 面时处置。 清单面(计划列三面,实测第 4 面同改):build_selfhost_native.py 的 corec_files/corelsp_files 各 +iface_axis.cr(iface_registry.cr 之后;corearch 清单本任务不动);src/compiler/_import.cr += import iface_axis(**project-mode 必需**——实测反证:仅去掉该行 ⇒ src/compiler/main.cr 单元 rc=0 + 23×error[N06](iface_satisfies 12/iface_satisfies_term 6/iface_find_method 3/iface_user_satisfies_ii 1/iface_kind_of 1)+ 2×TA01 级联(checker.cr:8855/:8911),全静默);tests/selfhost/test_compile.py 的 concat_sources() += 同条目(该测试自带 corec 清单副本,全枚举首轮 44/45 由它捕获,补齐后 45/45)。src/targets/x86_64-linux/_import.cr 零改动(test_backend_bootstrap rc=0 + error[=0 实测投射面零变化)。 判据:build rc=0(corec 44/corearch 30/corelsp 31 清单守卫 + 三面 build log clean error[=0/undefined=0);test_backend_bootstrap rc=0 + error[=0;selftest-types 333/333(用例数零增减);ELF canary 95084e7b…d475 IDENTICAL(clean-cache);.ccr 冷缓存逐字节同(ptr_arith 88943B 54e3856b… / generics_test 130974B 97be2f9e…);同源对拍 72 档 check 日志 diff -r 空(rc 分布 33×0/39×1 两侧同)+ 影子 decisions=30628 agree=30628 全计数 0 + logs/dumps diff -r 空(站点覆盖同报 assign-node=23150/fn-body-ret=5464/if-branch=1980/struct-field-type=28/array-elem-type=4/generic-apply-base=2);check src/compiler = 恰 2 条既有 TF01(lits_copy/ty_memo_slot_no_grow,行号 10162/10385);自举链 corec2==corec3 cmp IDENTICAL(双 2542222B)+ N06=0 + 冒烟 42 双通道 + corec3 --help rc=1;五 CI job(check rc=1 = 既有划界,其余 4×rc=0)+ 全枚举 45/45 selfhost + 7/7 bootstrap。二进制面:corearch sha 逐字节不变(599ee275…,清单未动)、corec/corelsp 因源跨文件重排变(c05df756…/337505f3…,非判据)、corec2 绝对 sha 变(03c9812f…→bec6e379…,同上因;判据为链固定点 cmp)。 * feat: R2 P4 Task 1——`.ccr` 段机制:TYPE(7)/IFACE(8) 加段 + 版本 7→8 + 加段四面(两段空壳落盘) D9 段 tag 7/8 = TYPE/IFACE(format spec §2 的 `9+` 预留顺移 9/10——两段代码零实现、仅注释冲突): 保住 loader 规范序闸 `tg == ri+1` 与段体连续闸 `soff == cursor` 两不变式不变 ⇒ 加段成本 = 闸界 6→8 一处。 D10 版本 7→8(`CCR_VERSION`):留 7 会让旧 v7 六段文件被静默读成「两段缺席 = 空表」——三态纪律 C.5-3 要消灭的静默类。实测拒收:前置二进制(`c05df756…`,T0 收官物)产 v7 三档(`ptr_arith` 88943B `54e3856b…` / `generics_test` 130974B `97be2f9e…` / small 86121B)→ 新 corearch 全 **rc=1** 「error: invalid .ccr file」+ **无产物**(旧 corearch 同文件 rc=0 = 文件自证有效)。 加段四面:① 常量 `CCR_VERSION=8`/`CCR_SEG_COUNT=8`/`CCR_SEG_TYPE=7`/`CCR_SEG_IFACE=8`;② 尺寸 `ccr_type_seg_size`/`ccr_iface_seg_size`(空壳各 4B)+ `calc_ccr_size` 两段;③ 写侧段表 6→8 行 + 段体末尾两处 `count=0`(8B);④ 读侧标量族 `seg_off7/8`·`seg_end7/8`·`have7/8` + 闸界扩 + 必备集扩 (D11:TYPE/IFACE 缺席 = 拒绝,与 ENT「缺段 = 空表」先例不同源)+ 空壳期段体校验(`count==0` 且恰 4B, 非空/尾随字节 = 拒绝——内容面落地前不接受外部半成品)。D18 共享层纯度保持:`ccr_io.cr`(corearch 也链接)零前端符号引用,段体构造归 T2/T3 的 `ccr_types.cr`(缓冲两件套随内容落地,见报告 §2.1 偏差登记)。 判据(全部本轮实跑):build **rc=0**(三守卫 `error[ = 0, undefined = 0`;核心清单 44/30/31)· `test_backend_bootstrap` **rc=0 + `error[` = 0** · `selftest-types` **333/333**(零增减)· ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL**(clean-cache)· `.ccr` **纯加法**(前六段体逐字节同;恰 +32B = 段表 2×12 + 空壳 2×4;新冷缓存记录值 `ptr_arith` 88975B `15645387ea174df87b29735bb46ea776b5175439fccc523b8951296767e5ae10` / `generics_test` 131006B `f2e566b68165584697b120614d3b08e472ba4c329aae63065bc9412e30ba0ff8`——P3b 旧值按 TODO #26 作废留档)· 冷/热两态 SYM/NOD/ENT/REG/EDG/TYPE/IFACE 七段逐字节同(仅 STR 差 = TODO #5 既有已登记面,本任务不修)· `test_ccr_v7` **27/27**(结构断言重定:SEG_TAGS 1..8 / HEADER_TABLE=112 / seg_count==8 / 坏版本 (7,6,5))+ `test_region_cfg` **22/22** + 新套件 `test_ccr_types` **9/9**(红态 2/9 → 绿;挂 `run.sh` selfhost-tests)· 全枚举 **46/46** selfhost + **7/7** bootstrap · 五 CI job(4×rc=0;`check` rc=1 = 既有划界恰 2 条 TF01 `lits_copy`/`ty_memo_slot_no_grow` 10162/10385)· 自举 `corec2==corec3` cmp **IDENTICAL**(`db2b63dd…`)+ **N06=0** + 冒烟 **42** · 影子 `decisions=30736 agree=30736` 全计数 0、 站点覆盖两侧同报、logs/dumps `diff -r` 空 · 同源对拍 72 档 check 输出 diff 空(rc 33×0/39×1 两侧同)。 计划外第 5 面:`test_region_cfg.py` 自带 `.ccr` 解析器(段集硬编码 1..6 + `ver==7`)由**全枚举** 捕获并重定(T0 的 `test_compile.py` 同款教训)——后续动 `.ccr` 布局者须全枚举,勿只 grep。 报告 = `.superpowers/sdd/p4-task1-report.md` * feat: R2 P4 Task 2——TYPE 段(类型行表 + 类型项 DAG 序列化 + 确定性装填 D13 + corearch 读回重建);corearch 链接 type_terms.cr 段体(D12)= [row_count u32][row×24B{kind,data,extra} i64×3][term_count u32][term×40B{tag,a..d} i64×5](哈希不落盘,加载侧 tt_hash5 重算;字段宽 8B = 与内存槽逐位同源——spec §3.7 的 i32 记法与自述字节数 24B/40B 自相矛盾,实施权威取 8B,spec 与 ccr_io 头注释同批改正)。写侧 = 新 corec-only 文件 ccr_types.cr(D18 解耦:装填引用桥接层 sh_term_of_ti,corearch 无此层);save_ccr 只搬运 g_ccr_type_seg 缓冲(globals 声明——双 concat 共享面约束)。D13 确定性装填 = 项层/引擎面/桥接层三面复位(tt_layer_reset 共享层新增,含引擎预算/memo/lits 等价复位)→ 按行序 0..g_type_count-1 装填 → 段体缓冲;段内容 = 类型表纯函数(与判定历史无关);不可译行/空缓冲 ⇒ save 拒落盘(rc=1 + 诊断,防静默空表)。读侧 = load_ccr 解析重建 g_types 行表 + g_type_terms 项 + g_tt_index(grow_tt_index→tt_reindex 重放);逐条不变量违规 = 整体拒绝:计数/长度自洽 + 恰两小节(无尾随字节)+ tag∈0..10 + a..d≥-1 + 子项引用 -1 或 <自身行号(字段表按 tag 分派——ATOM 的 b 为自类型行号可达数千,不得误拒)。加段面:corearch 清单 += type_terms.cr(ccr_io 之前,常量可见性)+ 双注册面 + test_compile.py 清单(清单漂移由全枚举捕获)。--dump-types(corec ccr / corearch)同一条打印路径(跨进程行格式零分歧)+ 项层原语 probe(tt_norm/tt_is_dnf/tt_is_literal + 规范化结果下标入摘要 = 重建索引与去重行为的实证)。 判据:selftest-types 338/338(333+5:装填/roundtrip/dedup/确定性/拒绝面)· test_ccr_types 19/19(T1 机制面 9 例重定 + T2 内容面 10 例新增)· test_ccr_v7 27/27 · test_region_cfg 22/22 · test_cache_identity 5/5 · test_compile PASS · test_backend_bootstrap rc=0(error[=0)· ELF canary 95084e7b…d475 IDENTICAL · 全枚举 46/46 selfhost + 7/7 bootstrap · CI 三 job(check rc=1 = 既有 2×TF01 划界同对 lits_copy/ty_memo_slot_no_grow;suite 0 ALL PASS;full-bootstrap 0:corec2==corec3 cmp IDENTICAL 308d93d8… + N06=0 + 冒烟 42 + corec3 --help rc=1)· 同源对拍 check 面与影子面输出 diff 空(decisions=31270=Σ站点,全计数 0)· report-only 56 档:新拒绝面零命中(失败集与 pre-T1 逐条相同)· 突变控制:A(loader 拓扑校验关闭)⇒ 拓扑拒绝用例转红 + 17/19 仍绿;B(跳过装填)⇒ 确定性用例转红(首版仅判定扰动对 B 假绿 ⇒ 补唯一标注项)。.ccr 冷记录值(旧值作废):ptr_arith 89859B 0f744675…(TYPE 888B / 15 行 / 13 项)· generics_test 133698B 8380c393…(2696B / 42 / 42)· 自源 src/compiler/_import.cr 9942142B 3219971f…(1736B / 27 / 27)。 **实测推翻计划一处预测**:冷/热两态行表分歧未随 D13 消除——项表与 probe 逐位同、热态行表 = 冷态前缀(前 12/15 行),根因 = .cir 缓存命中跳过 ir_gen 的 alloc_type(预热路径不重建前端行);连带:热态 .ccr 的 SYM 有 6 处类型引用指向缺失行(冷态 0,两侧引用均不出冷态行表)——TODO #5 末条家族,本任务不修(修它须改 .cir 缓存语义/行分配时点;强行 loader 拒绝会让暖态构建整类失败,属新硬错误+report-only 面)⇒ 用例锁**实际成立的不变量**(前缀 + 项表同值 + 引用域),悬挂计数入日志。未覆盖面:判定原语(ty_sub 族)跨进程同值**未覆盖**——type_engine.cr 入 corearch 即触发 project-mode error[=0 门(该文件两条既有诊断:lits_copy 真·类型洗白 TF01 + ty_memo_slot_no_grow loop-落空误报,即 check 作业 rc=1 的划界对)⇒ 回退到计划清单形(corearch 只入 type_terms.cr),探针改项层原语;收口路径(修 TF01 / 门白名单 / 维持)登记待裁。 * fix: TF01 收口——check src/compiler 两条长期诊断归零(落空分析 + lits_copy 类型洗白);type_engine.cr 入 corearch 清单(P4 Task 2 §9-② 收口路径 (a) 兑现) 定性(最小探针 + 旧二进制 c910df2d… 实证,非仅跑 check): · lits_copy = **真·类型洗白**:alloc 类型模型 = () -> string(checker.cr bi_add("alloc", TI_STR)), 声明 -> int 与 `return buf` 冲突;消费面全为字节缓冲(r64(buf: string, …) 收 string; lits_contradictory_buf 另一调用点传 g_ty_lits : string)⇒ 修 = 声明改 string,**不动**模型。 探针:alloc→int 报 TF01 / alloc→string rc=0。 · ty_memo_slot_no_grow = **checker 误报**:体以无 break 的 loop 收尾(只从体内 return 出)⇒ 永不 落空,而站点 5 取「块末语句类型」判(EXPR_LOOP 恒 unit)⇒ 误报。探针:loop{return 5;} 修复前 TF01、修复后 rc=0;落空体(1+1;)两侧恒 TF01。 修法: · checker.cr 新增落空分析 stmt_cannot_fall_through + loop_body_has_break + seg_has_break(保守: 只认**不产出值**的不可落空形态——无直系 break 的 loop / 双分支皆不可落空且有 else 的 if / STMT·UNSAFE·block 包裹;**return 不判 divergence**(其值类型正是站点 5 的被检对象——豁免即 洗白 lits_copy 型真错面);break 扫描在嵌套循环处截断(break 归内层;EXPR_MATCH 臂 = c 链 **非连续槽**、LET/ENUM_CONSTRUCTOR/AT 的 a 是名 ni 不下钻、GO 体在 b——子槽枚举照 monomorph gen_clone_tree 全表);未枚举形态兜底 = 1(按「可能有 break」)= fail-closed),站点 5 接线一行。 · type_engine.cr:lits_copy -> string;lits_contradictory_buf(buf: string, n: int)。 · ty_shadow.cr:sh_map_find_nogrow 头注「已知误报面」改写为「该误报已修」(原文被 TODO/计划引用)。 判据(nice -n 19、cwd=仓库根、比较前 clean-cache、一构建一编译串行): · **check src/compiler rc=0**(诊断 0 条,目标达成);构建 rc=0(GUARD:corec 45 / corearch **32** (+type_engine.cr)/ corelsp 31 + 三条 build log clean error[=0/undefined=0)。 · test_backend_bootstrap rc=0(11 PASS + error[=0——project-mode 门**通过**);selftest-types 338/338。 · ELF canary 95084e7b…d475 **IDENTICAL**(清单步前后 + 定稿重建各一次)。 · 全枚举 **47 selfhost + 7 bootstrap 全 rc=0**(第二轮 = 定稿二进制;第一轮唯一红 = test_iface_ops 3 条过期期望,见下)。 · 五 CI job 全 rc=0:check / suite(21 档 ALL PASS)/ bootstrap-tests(3/3)/ selfhost-tests (含新套件 19/19)/ full-bootstrap(corec2==corec3 cmp IDENTICAL 991eecc1…b2ba 双 2604942B + N06=0 + 冒烟 42 ×2 + --help rc=1)。 · 同源对拍:30 档 tests/suite 语料 check 输出 + rc 与旧二进制**逐字节同**(行为 delta 仅限 「体以无 break 的 loop 收尾」形,语料零命中)。 · 突变控制 5 条(/tmp 侧「清单照旧 + 内容替换」构建,仓库零改动):过接受 → 恰 8 条负控红; 过拒绝 → 恰 9 条正控红;嵌套截断失效 / match 臂链失效 / 连续段失效 → 各恰 1 条对应档红。 附带:test_iface_ops 3 条**过期期望**随批更新(t6_C_never_call_unit / t6_S1_never_unit / t6_S6_iface_never_unit——少掉的 TF01 恰为同误报类(无 break 的 loop 收尾 + 声明 -> never), 三条用例的关注面诊断(调用位点/S1 链域/S6 iface 返回链)原样保留 ⇒ 76/76 复绿)。 type_engine.cr 入 corearch 清单两面(build_selfhost_native.py backend_support_files + src/targets/x86_64-linux/_import.cr;位置 = type_terms 之后 / ccr_io 之前),31→32 文件, P4 Task 2 §9-② 收口路径 (a) 兑现(ty_sub 族跨进程同值探针面解阻,消费者待接;corearch 二进制 变大 = 死码,非判据面)。 登记(本批不修):① TF01 对 build **非致命**(含 TF01 的源 corec build rc=0 且照出可运行产物, 实测)⇒ 本批收益面 = check 门(CI/工具链/清单门),非运行期行为;② `while <标识符> { … }` 被当 **结构体字面量**解析(parse_while_expr 缺 parse_if_expr 同款 g_parse_no_struct_literal 守卫, 旧二进制同款复现,既有);③ 非落空体内 return 的**值类型**不核对(显式放宽面;逐 return 核对 须重跑推断 = 副作用风险,本批不做;套件 pos_boundary_no_return_value_check 注释钉住边界)。 新增 tests/selfhost/test_tf01_fallthrough.py(19 例:正控 10 / 负控 8 / 端到端 1)挂 src/ci/run.sh selfhost-tests;报告 = .superpowers/sdd/tf01-cleanup-report.md。 * feat: R2 P4 Task 3——IFACE 段内容(原生条目扩列 13→16 + 形状命名化 + 接口签名项化 + impl 边/方法表序列化 + corearch 读回);STR 段增长按裁决 1 接受 段布局(D14 五小节;字段宽度逐条;实现权威 = ccr_io.cr/ccr_types.cr 头注 + spec §3.8): ① 原生条目 16 行 × 24B {ak i32, ti_row i32, name_ni i32, lit_code i32, ops i64} ② 形状 6 条 × 8B {name_ni i32, term i32} ③ 用户接口 {name_ni, method_count, generic_count, pad} + 方法 × 80B {name_ni, param_count, self_mode, ret_term, param_terms[8], param_codes[8]}(字段序取计划「声明行」序——同块 尺寸式两数组次序相反,偏差登记入报告) ④ impl 边 × 8B(g_impl_for——裁决 2 声明元数据,不入判定) ⑤ 方法表 × 12B(g_methods——iface_find_method 唯一数据面) 实测:ptr_arith 452B · 2 接口夹具 744B。 扩列(裁决 3+7):IFACE_ENTRY_COUNT 13→16(+AK_NULL/AK_SUM/AK_FN,ops=0 = 纯信息面); name_ni 逐名注册(20 名一表 = 16 条目名 + 4 形状独有名);iface_kind_of 零改动 (判定面零变化由全类型行枚举 + TYP_NULL/TYP_OPTIONAL 行显式 -1 守门)。 形状命名化(裁决 1 + D17):六名(sequence/sequence_ro/sequence_rw/indexable/iterable/ product)经 iface_shape_builtin_init 在 init_types 尾部注册 + D13 重建重跑;判定语义零变化。 签名项化:sh_iface_sig_ret_term/sh_iface_sig_param_term 按接口声明序装填进 g_ifaces 新槽 (OFF_IFM_PARAM_TERMS=168/OFF_IFM_RET_TERM=232;ESZ_IFMETHOD 168→240、ESZ_IFACEINFO 2712→3864);不可建(dyn 位图行入签名)⇒ save 拒绝(先 report-only 61 档零命中后开门)。 读侧(corearch):五小节重建条目表(g_iface_registry_ok=1 = 段为真源)/形状表/接口表 (节点槽恒 -1、项槽 = 段值)/impl 边/方法表 + 跨段引用域闸(ni < STR 串数、ti_row < TYPE 行数、term < TYPE 项数);--dump-ifaces 双侧共用打印路径 + ifaceprobe 跨进程同值。 清单面零改动(读侧只用共享层符号 ⇒ 条目布局常量与 iface_shape_grow 自 iface_registry.cr 迁入 dyn_arr.cr,为 corearch 链接面开路)。.cir 快照字段清单不含 iface 表 ⇒ CIR_CACHE_VER 保持 17。 判据(最终二进制 corec 6da65418):构建 rc=0(三面 error[=0/undefined=0)· check src/compiler rc=0 · selftest-types 338→347 · test_ccr_types 19→28 · ELF canary 95084e7b…d475 IDENTICAL · corec2==corec3 cmp IDENTICAL(双 2737902B 55dc7cda…)+ N06=0 + 冒烟 42 + --help rc=1 · 五 CI job 全 rc=0 + 全枚举 54/54(47 selfhost + 7 bootstrap)· 同源对拍 check 72 档 diff 空 + 影子 decisions=32280=agree(站点覆盖同报)· report-only 零新增拒收 · 突变控制 3 条逐条转红 (loader 闸关/签名装填跳过/init_types 注册缺失;含突变器首轮打错产物 = 假绿的实证修正)· 重建逐字节同(仓库零改动)。.ccr 新记录值(冷缓存):ptr_arith 90823B fb4a3b59… · generics_test 134701B cafb4278… · 自源 10418710B 3d617813…;STR 增长 +16 串/+156B(用户程序) / 0(自源);连带面 = 晚于 init_types 驻留的串 ni 后移 ⇒ SYM/NOD 取值重编号(布局/语义/ELF 不变)。 * feat: R2 P4 Task 4——DFNode.TK 迁移期双槽(ESZ_DFNODE 64→72:tk 语义/字节零变化 + 新槽 OFF_DF_TK_TERM 项引用)+ 允许清单制派生 {IR_CONST,IR_BINARY}(实证排除两处「数值恰是合法类型行」陷阱:DEREF/STORE_PTR 的访问宽度 8 = TI_DEX_S 行、BOUNDS_CHECK 的旗标 1 = TI_DEX 行)+ .cir 快照 CIR_CACHE_VER 保持 17(计划 D15「快照入项槽 + 17→18」的被测前提推翻:入槽版暖路径恢复的项索引全数悬空 bad_term=329 ⇒ 改装载侧 emit 等价时点重派生,冷/暖 (op,tk,term,tag,a,b,c) 1298 行逐行同);发射面零泄漏(ELF canary 95084e7b…d475 IDENTICAL · ptr_arith/generics_test .ccr 八段逐字节同 · --dump-objects 逐字节同);判据 selftest 355/355 · test_ccr_types 34/34 · 全枚举 54/54 · 五 CI job rc=0 · corec2==corec3 cmp IDENTICAL + N06=0 + 冒烟 42 * fix: R2 P4 Task 5——运行期可选表示统一(裁决 5「解包侧判表示」):B.7 裸值 + Some 臂双路径 SIGSEGV(rc=139)闭合 形态 = 表示位运行期分派(IR 层,非后端旁路字节):每个可选槽配一个普通 int IR 变量 (0 = 裸值「槽内即 T 值、Some 臂载荷 = 槽值本身」/ 1 = 装箱「IR_MAKE_ENUM 对象、 载荷 = 字段 fi+1」);写点置位(LET/赋值:静态已知 → 常量、源有表示位 → 拷贝)+ 两解包点分派(match 的 Some/None 臂**条件**与**载荷绑定**、`?`);无表示位的槽逐字 走既有路径。跨函数面 = 隐藏全局信道(返回点写/调用点读、调用点写/被调序言读;纯 IR) ⇒ 零 ABI/opcode/帧布局改动 ⇒ ELF 与解释器同源(全部判据双路径同值,无一例分歧)。 零足迹门:AST 无 EXPR_OPTIONAL 且无 Some/None ⇒ 整面不启用。 实测:ELF canary 95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 IDENTICAL(非可选程序零足迹)+ ptr_arith/generics_test .ccr 与基线编译器**同源**重建逐 字节同(新记录值 90966B/134844B;与 Task 4 记录值的 +143B = 期间 arena_globals 导入 引入的 4 全局 + 1 串,同源基线重建同得该差 ⇒ 非本任务)+ --dump-objects 逐字节同 + 可选程序冷/热 .ccr 同;test_optional 12→32/32(B.7 四形态 + 写点/条件写 + 跨函数 6 面 + 未覆盖面 2 例);selftest-types 355/355 · 全枚举 54/54 · CI{check,bootstrap-tests,suite, selfhost-tests} rc=0 · 自举链 cmp IDENTICAL(双 2783750B 5492d9ad…)+ N06=0 + 冒烟 42 · 同源对拍 check/shadow 两侧 diff 空(decisions=32564=agree)+ 编译耗时无回退。 连带根因(后 4 条为计划未列面,逐条实测): ① 表示位侧表空槽哨兵——alloc 零初始化而哨兵 = -1 ⇒ 未写槽读 0 =「表示位 var 0」; ② ELF 后端 IR_CONST 无全局行分支(g2_slot 对全局返回帧外伪偏移 ⇒ 静默丢写、与解释器 双路径分歧)——本任务首次出现该发射点,按 IR_LOAD/IR_STORE 同款修(根因修,非绕开); ③ 负值文件级常量在自举二进制静默读 0(= TODO #14 live 触发;CIR_CACHE_MAGIC 为同类 live 实例)⇒ 改 oe_bare()/oe_boxed() 函数; ④ 返回类型判据误取 fi_return_type 裸码(T? 类型节点 type_val = 0 ⇒ 判据恒假)⇒ 被调 序言与调用点两侧改走返回类型节点(callee_ret_optional); ⑤ 惰性 thunk 推迟调用 ⇒ 返回信道读点悬空 ⇒ 被调返回可选者禁 thunk。 未覆盖面(登记,不静默):结构体字段/数组元素/全局槽/match 结果/可选的惰性 thunk ⇒ 裸值形态 rc=139 双路径同(响亮)、装箱形态照常正确(两例钉住);对象布局里的表示位面 与 MAX_* 定长槽评估同批归 P5。可选面启用即关 .cir 缓存(表示位侧表 = 进程内状态、 快照不载 → 与冷路径产物分歧,同 Task 4 bad_term 教训形态)。 * fix: R2 P4 Task 5 补——IR_LOAD_ENUM_TAG tag 读取源补全局行分派(②同族第三处;实测发现的**预存**静默双路径分歧) 现象:全局可选槽 `g : int? = Some(5);` + `Some` 臂 ⇒ **ELF=0 / 解释器=5**(match 静默走空臂 返回未写的 0——双路径分歧 = 静默类)。定位:`IR_LOAD_ENUM_TAG` 用 `e2_ld(g2_slot(s1))`(帧槽) 读 tag ⇒ 全局行读帧外伪偏移 ⇒ tag 读垃圾、臂条件恒否。**基线二进制同病**(同命令 ELF=0/interp=5)⇒ 预存缺陷,非本任务引入;本任务在「未覆盖面逐形态实测」时发现。 修法:tag 源改走 `e2_load_var`(局部/全局双态;与 IR_LOAD_FIELD 同款)——修在根因; 目的槽的全局行仍零发射点(注释登记)。与前一提交的 IR_CONST 全局行修复同族 (ELF 后端逐 op 全局分派缺口,本任务共触发/发现三处)。 判据:`U2` 5/5(原 0/5)· `U2b`(None)9/9 · test_optional 32 → **34/34**(+2 全局槽登记例)· ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL** (该路径不在 canary 语料内)· ptr_arith `.ccr` 与基线编译器同源重建仍逐字节同 · selftest 355/355 · 全枚举 **54/54** · CI {check,bootstrap-tests,suite,selfhost-tests} 全 rc=0 · 自举链(冷缓存) `corec2==corec3` cmp IDENTICAL(双 2783750B 5492d9ad…)+ N06=0 + 冒烟 42 + `corec3 --help` rc=1 · 21 形态矩阵逐值不变。**附带登记(预存,非本任务)**:冷/热缓存下 `corec build src/compiler/main.cr -O 0` **暖态 SIGSEGV**(`load_cir_cache` → `str_len`,gdb 栈实测;基线同病)——本任务「可选面关 缓存」顺带规避(仅限可选程序),归 .cir 缓存恢复面(TODO #5 末条同族)转主批。 * fix: R2 P4 Task 6——枚举/结构体写入侧 MAX_* 护栏(TODO #35:变体/载荷/字段;P022/P023 定位硬错,#8 同族形态)+ MAX_* 解除评估登记 根因/危害(运行期危害形态首次构造,收口 TODO #35「未做」项):EnumVariant 16 槽区与 StructInfo 16 槽字段区是定长内嵌槽区(其后紧跟自身 count/generic 槽、再往后是邻记录/缓冲区尾),parser 写入循环对 vc/tc/fc 零上限闸——第 17 变体槽起点 = OFF_EI_VARIANT_COUNT 自身(4360)、槽尾越 ESZ_ENUMINFO 224B。危害**不是崩溃**(grow_* 扩容余量吸收越界 ⇒ 无 SIGSEGV 可观测,故 check 面全静默)而是「表污染 + 判定静默」:① 17 变体声明 check rc=0 零诊断;② 该枚举上 **match 穷尽性判定被静默禁用**(第 17 名槽与 count 同址、count 后写胜出 ⇒ 名→变体项查表失败 ⇒ 域不可展开 ⇒ 三态回落 -1 不判)⇒ 真·非穷尽 match 也 rc=0(对照实验:16 变体同形必报 TM03);③ 第 17 变体不可构造(假诊断 Undefined enum constructor);④ **同族第三处 = MAX_STRUCT_FIELDS**(17 字段结构体 check rc=0 静默——RED 步发现,一并修);⑤ 修复前 build rc=1 仅靠 .ccr 读回侧闸(护栏在读回侧 ≠ 写入侧)。 修复:parser 三处写入点跳过超限槽(计数保持真值,照 P020 的 add_func(pc) 先例)+ 末尾单条定位诊断 error[P022](枚举变体/载荷,含真实数码)/error[P023](结构体字段);dyn_arr.cr 唯一受护写点(ei_set_variant_*/si_set_field_*,照 fi_set_param_type 先例)+ 读护栏(越界回哨兵 -1/0,count 保持真值仍不越读)。实测修正:拒绝由 parse 阶段诊断闸承担(删 hard 名单条目对照构建零行为差异 ⇒ main.cr 零改动,与 P019/P020/P021 同款)⇒ 拒绝发生在**前端**,不进入 lower/写 .ccr/ELF(阶段断言入回归用例)。 判据:selftest-types 355/355 · test_enum_limit.py 13/13(新,挂 src/ci/run.sh)· test_backend_bootstrap rc=0 + error[=0 · check src/compiler rc=0 · ELF canary 95084e7b…d475 IDENTICAL · .ccr 与修复前编译器逐字节同(ptr_arith 592afa31… 90966B / generics_test ec8413ec… 134844B——同源双编译器 6afbd333 × 5d2b15ad 对拍)· 五 CI job rc=0 · 全枚举 55/55 · 自举链 corec2==corec3 cmp IDENTICAL(双 2792078B 4919023b…)+ N06=0 + 冒烟 42 + --help rc=1 · 影子同源对拍逐行同(decisions=5642=agree,全计数 0)· 突变控制(删 3 诊断 ⇒ 8 例红 + 静默面复活;复位 ⇒ 二进制逐字节复原 5d2b15ad)· report-only 全语料零命中(静态扫描:语料最大结构体 9 字段、无 >12 变体枚举)。 登记:解除评估 = 三项 16 上限(变体/载荷/字段)本批均不解除(解除面 = 记录布局迁侧表,与 P5 段布局决策同源;.ccr 盘面本为变长 ⇒ 不受限;枚举 >63 变体另需改覆盖位表示);MAX_GENERICS=4 不属本类(写入侧有界、超限 parse 期硬报,无静默面);MAX_IFACE_METHODS/PARAMS 已有 P13/P11 硬错(本轮复测)。未构造极端形态(grow_enums 零余量时越出分配);ast.cr 镜像与布局不一致(StructInfo 镜像 64 vs 布局 16)登记待解除时归位。报告 = .superpowers/sdd/p4-task6-report.md * docs: R2 P4 Task 7 收官(载体批 8 提交链收口)——全量回归:五 CI job 全 rc=0 · 全枚举 55/55 rc=0(48 selfhost + 7 bootstrap)· selftest-types 355/355 · check src/compiler rc=0 · 构建确定性(build/corec 5d2b15ad… 与 T6 记录值逐字节同 ⇒ 提交树 == 被测二进制度);碰撞窗未背书五项**本实例重跑**(无并发构建窗口):clean-cache ELF canary 95084e7b…d475 IDENTICAL(28822B)· corec2==corec3 cmp IDENTICAL(双 2792078B 4919023b…)+ N06=0 + 冒烟 42/42 + corec3 --help rc=1 · 全枚举 · 同源对拍 72 档 × {check,shadow}(pre-P4 9a583215… × 当前源 vs 当前 × 当前源):影子摘要两侧同值 decisions=32620=agree,全计数 0,站点覆盖两侧逐项同(assign-node=24721 · fn-body-ret=5828 · if-branch=2037 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2),日志面唯一差异 = 5 档语料的 TF01 收口类(5 处 ty_memo_slot_no_grow 误报消失 + 3 档 rc 1→0;rc 分布 31/41→34/38)= P4 内有意修复非语义回归;dumps 两侧均空 = 无差异条目(sh_dump_write 早退)⇒ 空洞证据不引用;统一台账(T0-T6 汇总):收紧 10 类(T1 3 / T2 2 / T3 2 / T6 3)· 放宽 2(T1 空壳闸退役,非语义)· 崩溃·静默修复 8 形态 + 1 静默错值类(T5)· 全语料命中 0(T1 72 档 / T2 56 档 / T3 61 档 report-only + T6 139 文件静态扫描——失败集与各自基线逐条相同)· pin flip 逐任务(T0 清单第 4 面 → T1 结构断言重定 → T2 333→338 → TF01 check 判据 rc=0 → T3 338→347 + 扩列 13→16 → T4 347→355 + CIR_CACHE_VER 保持 17 → T5 12→34 → T6 13 例)· 实施偏差 7 条(D15「.cir 快照入项槽 + 17→18」被实测推翻/ TYPE 段冷热半成立 / spec 3.7 字段宽度勘误 / T3 方法记录字段序 / T1 缓冲延后 / T2 判定原语未覆盖 / 清单面实为四面);判据勘误两条落纸(①「.ccr 冷/热两态逐字节同」对非可选程序不可达 = 预存面(pre-P4 同病 89086/88954,TODO #5 末条),只对可选面零足迹程序成立(2bfbbe2b… 97604B 冷≡热);TYPE 段纯函数承诺项面成立行面不成立(行表热 12/冷 15 前缀)②.ccr 记录值两套命令口径差恒 143B(--static 前置 rt.cr,main.cr:423-425):ccr = 90823B/134701B,--static = 90966B/134844B,两态各自确定性可重复);P5 交接包落计划附录 D(D-1 口径/冷热表含 ccr 口径 90823/90626 + 自源新记录值 10661888B 76c76bb1… · D-2 任务面(单槽化 + 大步删旧路径 + 文档台账)· D-3 十四项继承项(项引用稳定形态 bad_term=329 红证 / type_equal_legacy 删除 / unknown 清零二选一 / 桥接缓存退出 / 站点 6 / 对象布局表示位 / MAX_* 解除=记录布局迁侧表(T6 裁定)/ 预存暖态 cir SIGSEGV 另单必修 / TODO #32 未收口等)· D-4 开工前置检查单);文档:TODO #44(P4 收官条目)+ #26 判据继承注 + #43 重复段修正、findings §14「P4 后复跑」、CLAUDE.md 与两 spec 的 v8/8 段与计数同步。纯文档 6 文件,源码零改动(被测二进制度继续有效)。 * docs: R2 P5 收尾批实施计划定稿——8 任务(T0 前置侦查+冻结基线 · T1 .cir 暖态 SIGSEGV 根因 · T2 单槽化(α) · T3 unknown 清零 · T4 删 legacy+回落面+死分支/站点6 · T5 影子层下线+回归网切换 · T6 TODO #32 · T7 收官+附录 E(P6 交接));排序决策 = T0 分类表+T4 稳定形态先裁 → T2 内存面单源(OFF_DF_TK 改项引用/OFF_DF_TK_TERM→OFF_DF_AUX/72B 不变/盘面快照仍派生码 ⇒ .ccr/.cir 零变化不 bump),盘面项索引与 corearch atom_of 推 P6;unknown 清零走命名面判定化(不接结构性展开层,P3a 已反证) * docs: R2 P5 Task 0 实施记录——前置侦查 + 冻结基线(源码零改动) 冻结基线(下游比较基准):build ×2 逐字节同;corec 5d2b15ad… / corearch 493dc490… / corelsp 18b94bd9…(= P4 记录值)· ELF canary 95084e7b…d475(28822B)IDENTICAL · corec2==corec3 cmp IDENTICAL(双 4919023b…)+ N06=0 + 冒烟 42 + --help rc=1 · 全枚举 55/55 · check src/compiler rc=0 · selftest-types 355/355 · .ccr 两套口径冷态 四条与 P4 附录 D-1 逐条同(90823/134701/90966/134844);72 档语料基线(runner /tmp/p3t0_run.sh 12dc571f…):check rc 34×0/38×1,shadow 70 档 decisions=32620=agree、 全计数 0、站点覆盖 assign-node=24721/fn-body-ret=5828/if-branch=2037/struct-field-type=28/ array-elem-type=4/generic-apply-base=2(站点 6 等 4 站 0 命中);产物 /tmp/p5t0/**。 四表:A 全 51 opcode 逐行分类(类型行 9/旗标 1/宽度 2/无 38/填充伪值 1)+ 后端 ti 消费者 机器审计恰 5 分支 + 允许清单量化复核(790828 节点:term≥0 恰 = {CONST,BINARY});B 站点 评级 10 行 + 站点 6 不可达三面证明(parser tok2op/T_EQ/+= 族 + 语料 0 命中);D 9 探针 -1 面清点(异名命名 + 同实参泛型应用两次实例化 = 实测 -1,均 uncovered + 回落;ty_sub 负值无计数通道、预算面用户语料不可达)。 T2 前必须落纸的两项未决:F1 ir_gen.cr:1758 5 参 emit 调用 ⇒ IR_HOTPATCH_ROUTE tk = r9 残留(实测 0/4 两态,bootstrap x86_64_stack_asm.py:222-226 只为实参写寄存器);F2 atom_of 对复合行不可逆(ptr_ref_first BINARY tk=10 = 指针行 ⇒ 派生码不变量 D22-① 在 复合行 op 上不可满足 ⇒ 三条候选先裁)。另登记:op 0/22/41 零发射点(死 opcode)、 op 35/36 仅 @x() 括号形态可达。 * fix: R2 P5 Task 1——.cir 暖态 SIGSEGV 根因修复(装载侧逐函数 read_file 累计 1.1674GiB > 1GiB 堆 ⇒ alloc 耗尽返回 0 ⇒ str_len(0) 解引用;改单一复用读缓冲、载入失败 ⇒ miss)+ 冷/暖两态产物一致回归 + 突变控制 - 根因(实测三跑同栈):装载侧逐函数 read_file = alloc(fsize+1),bump 分配器(rt.s alloc)不回收 ⇒ 946 个快照读缓冲累计 1,253,453,548B = 1.1674GiB > 1GiB 堆(rt.s heap_start .space 1024*1024*1024)。单条目 max 3,987,879B,因快照含全程序边表 ⇒ 尺寸 O(程序) 而非 O(函数)(nod_s3:13 节点条目 2,595,772B,其中 98.7% = 80100 条全程序边 ×32B)。 - 症状:corec build src/compiler/main.cr -O 0 冷 rc=0 / 暖 rc=139;gdb #0 load64(rdi=0,rsi=-8) ← #1 str_len ← #2 load_cir_cache;冻结基线 /tmp/p5t0/base/corec(5d2b15ad…)同病 ⇒ 预存、非本批引入。写读对称表(字段/写偏移/读偏移/长度来源 + pos 推进)逐字段核 ⇒ 无格式/长度域缺陷 ⇒ CIR_CACHE_VER 保持 17(停条件不触发)。 - 修法:cir_cache.cr::cir_read_snapshot(新增 30 行)= 单一复用读缓冲 g_cir_read_buf/g_cir_read_cap(与写侧既有 g_cir_write_buf/cap 同构——读侧原是唯一逐调用新分配方);w64(buf,-8,fsize+1) 保住 str_len 口径 ⇒ 既有 r64/str_sub 消费面零改动;载入失败(open/空文件/短读/缓冲不可得=read EFAULT/长度<56)⇒ -1 = cache miss(三态纪律:载入失败 ⇒ 拒绝,绝不静默当空表)。刻意不写 buf == 0 空判(该 string-int 混比在 bootstrap 侧 x86_64_stack_asm.py:164 编成 str_eq(buf,0) ⇒ str_len(0) 自崩溃;登记另单)。 - 回归:tests/selfhost/test_cir_warm_path.py(新,19 例:冷/暖 rc 双 0 + ELF 逐字节同 + .ccr 段级契约(STR 冷≠暖为预存口径)+ 暖态真命中 + 变体 4 + 可选面零条目 + 累计装载复现例(416 条目补零 3MB ⇒ Σ=1.31GB > 堆;暖 rc=0 + 峰值 RSS 19.9MB)+ COREC_WARM_SELF=1 自源 2 例)+ src/ci/run.sh 挂点与计数同步。RED 对照:冻结基线 × 同套件 = 15 PASS/1 FAIL(D3 rc=-11);突变 M1(load_cir_cache 回灌修复前读路径)× 同套件 = 15 PASS/1 FAIL;精确回滚后源 sha 1220742f… 复原 + 三二进制逐字节回原值。 - 判据(本实例实跑):build_selfhost_native rc=0 · check src/compiler rc=0 · test_backend_bootstrap rc=0 + error[=0 · selftest-types 355/355 · 全枚举 56/56(48+1 selfhost + 7 bootstrap)· CI suite job 21 档 build/run rc=0 · ELF canary 95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 IDENTICAL · 暖≡冷(自源冷/暖×2 ELF 三跑同 sha 13230542f79da591c4ea…(cmp IDENTICAL)+ .ccr 八段全同 + 946 条目戳三跑全等 = 真命中;冻结基线冷产物亦同 sha = 零发射面漂移)· 自举链 corec2==corec3 cmp IDENTICAL(双 13230542…)+ N06=0 + 冒烟 42 + --help rc=1。 - 登记:① string == int 混比双路径分歧(bootstrap str_eq 分派;本修复绕开未修,建议另单)② g_cir_read_cap 于 alloc 失败时仍前进 = 良性确定性(此后恒 EFAULT ⇒ miss)③ 单条目 > 1GiB 的「格式分片」议题(非本任务面)④ .cir 其余口径逐字未动(VER=17/身份闸/指纹闸/项槽重派生/save 侧)。TODO #43 登记面 ⓪ 与 #44 交接包 D-3-9-(ii) 本提交闭合。 - 报告:.superpowers/sdd/p5-task1-report.md(工作区,不入提交,同 T0 口径)。 * refactor: R2 P5 Task 2——DFNode.TK 单槽化(α):OFF_DF_TK=类型项引用(类型面唯一真源)+ OFF_DF_TK_TERM→OFF_DF_AUX=辅码(旗标/宽度/计数/不可逆行的原码),ESZ_DFNODE 保持 72B ⇒ 布局与版本位零改动(CCR_VERSION=8 · CIR_CACHE_VER=17);码=派生量(iri_tk/.ccr NOD/.cir 快照一律 sh_dfn_code_of_slots(项,辅码),emit/lower_to_ccr/装载侧单源);分类表(Task 0 表 A 代码化,ty_shadow.cr)= 类型行面 {CONST,BINARY} ∪ P4 六排除项再裁的 {ALLOC,CALL,LOAD,I2F,F2I}(P4 的「后端不按 ti 分派」= 发射面判据,不构成语义类型面的排除理由)+ 辅码面 {BOUNDS_CHECK,DEREF,STORE_PTR,SPAWN,HOTPATCH_ROUTE} + 其余无面;F1 裁决实施(ir_gen.cr:1758 5 参 emit ⇒ 补显式第 6 实参 0:旧行为 = r9 残留,实测同语料 0/4 两态;已裁决偏差 = hotpatch 语料 3 节点码 4→0,.ccr 逐字节核恰 3 处)、F2 裁决实施(复合行不可逆 ⇒ 不建项 ∧ 原码进辅码槽保真——全语料实测仅 ptr_ref_first BINARY tk=10 一例;5 个 ti 消费者安全性 = Task 0 机器审计 + 值敏感面逐点核 + 负控,突变 M1 反证该格可抓);D23 新硬错(类型面行不可译 ⇒ emit 计数 + ccr_seg_prepare_save 拒绝落盘 rc=1+诊断;仅 emit 面——装载侧不计数:暖进程类型表可更小,缓存命中绝不误拒;全语料 94 档 face_fail=0 report-only 先行);消费点逐点核(provenance 宽度改读 AUX;interp/ir_gen:129 读派生码;instr/regalloc/lower_to_core/dump/ccr_io 读 iri_tk 零改动);判据 = selftest-types 367/367(+12)· test_ccr_types 40/40(+6;㉙ 判据重定:冷/暖改「派生码面逐行同 + 项内容等价」——暖态表可更小系预存缓存面局限)· test_cir_warm_path 19/19 · test_cache_identity 5/5(VER_EXPECTED=17 未 flip)· check src/compiler rc=0 · test_backend_bootstrap rc=0+error[=0 · ELF canary 95084e7b…d475 IDENTICAL · .ccr 两套口径冷态四条与起点基线逐条同 · --dump-objects 逐字节同(2427 行)· .cir DOT 逐字节同 + 快照 payload 16/16 同(仅编译器身份头字段异,TODO #5 有意面)· 派生码不变量:94 档 338,207 非自源节点逐节点 diff 仅 F1 的 3 节点 · 全枚举 56/56 · 五 CI job rc=0 · 自举链 corec2==corec3 cmp IDENTICAL(2821374B)+N06=0+冒烟 42 · 72 档 check 同源对拍(rc 同、日志仅 2 档自源行号位移)· 影子对拍 decisions=agree=32767 + 全分类计数 0 · 构建确定性 ×2 逐字节同 · 突变控制 4 条逐条转红(并补两处断言孔);登记:暖态项索引/表长可小于冷态(P6 交接,报告 §5-④) * feat: R2 P5 Task 3——unknown 清零(命名面判定化:身份链 + 可判定面加强;两构造点同步;全语料回落计数归零) 形态:桥接(ty_shadow.cr)命名原子挂**身份链**——TYP_NAMED/TYP_GENERIC_PARAM ⇒ tt_atom(AK_NAMED, 本行, [名字令牌]);TYP_GENERIC_APPLY ⇒ tt_atom(AK_NAMED, **基型行**, [基名令牌, 实参项…])(基型行 = 规范形:两处实例化同型 ⇒ 同节点;两构造点 sh_term_of_ti/sh_sig_term_of_ti **逐位同构** ⇒ 计划停条件③不触发)。引擎(type_engine.cr)lit_implies 正原子 AK_NAMED 守卫 → te_named_pair:同类 ⇒ 链比较(令牌位 = 节点同一性 / 实参位 = 引擎 ty_equiv 三态)、混类 ⇒ 0(命名侧带链 ∧ 异类侧非 NEVER/DYN)、**域外 ⇒ -1 + uncovered**(空链 / 令牌位错位 / 实参位 μ 变元 / NEVER·DYN 混类——三态纪律)。不接结构性展开(P3a 反证口径保持:展开项结构相等 1 与桥接项 0 双断并存)。 清零判据(实跑):语料 72 档 shadow decisions=agree=32917、old_stricter/old_looser/unknown*/replace_* 全 0;探针可判定域 18 档 unknown_engine/replace_unknown 1~2→0(Table D 两形态「异名同形命名」「同实参泛型应用两实例化」+ 异实参应用 / 命名 vs 原生 / 可选异名 / 应用含命名实参 / 嵌套应用 / T 赋 int);rc/诊断逐条不变(清零 ≠ 放宽 ≠ 收紧)。 判据:selftest-types 386/386(367+19;3 处重钉 t3.named_face_decided_no_fallback / unf.nominal_equiv_atomic_decided / unf.nominal_type_equal_false)· 新套件 test_named_face.py 18/18(行为断言形态,不依赖影子通道——T5 下线后照常)· ELF canary 95084e7b…d475 IDENTICAL · .ccr ptr_arith 逐字节同 / generics_test +640B(TYPE 段项表 52→68 行 = 身份链新增令牌/CONS 项;发射面零泄漏:同语料 ELF 逐字节同 + --dump-objects 同 + .cir DOT 同 ⇒ 判据⑤按「无语料命名行档逐字节同 + delta 归因」重定)· check 语料 34×0/38×1 逐档同 · D26 同源对拍零差异(rc + 日志)· 全枚举 57/57 · 五 CI job rc=0 · 自举链 cmp IDENTICAL(ba8d98b8…)+ N06=0 + 冒烟 42 + --help rc=1 · 构建确定性 ×2 逐字节同 · 突变控制 3 条逐条转红(M1 令牌塌缩:自测 8 FAIL + 套件 2 例转绿;M2 链异判 1:7+4;M3 域外守卫去掉:1)+ 源/二进制精确回滚。 收紧面(登记,提请复核):混类判定 -1→0 ⇒ 约束站点(gen_constr_satisfied/gen_inst_constr_satisfied)「命名实参 × 原生约束」由不判变 TG02(check rc=1;build rc 不变——TG02 非硬名单);全语料 0 命中,唯一命中 = test_generic_constr.py 1 例登记面(重钉 struct_inst_named_arg_rejected)。残留 -1(可判定但计划停条件②面,本任务不动,待单独裁决):参数链不变槽元素非同形([NA;2] vs [NB;2] / *NA vs *NB / 元组含异名)——行为面早已正确拒绝,残留仅在计数面(探针 n13/n17/n18 + 套件 residual.* 两例钉行为)。其他登记:¬ 面保持 -1(桥接零 TT_NOT ⇒ 不可达);链不区分 kind(NAMED vs PARAM 同名 ⇒ 判等,可达性未证实);APPLY 项 b 槽取基型行 ⇒ DF 载体面该行 face 0→face 1(派生码不变);预存发现另单建议 = 非硬名单诊断(TA01/TG02)不阻 build(main.cr 硬名单闸,冻结基线同)。报告 .superpowers/sdd/p5-task3-report.md;TODO #46。 * docs: R2 P5 Task 3 收口注——不变槽残留面裁定 = 默认案 ①(另立一步派发;三件证据见 p5-task3-report §6.1 交接条目) * fix: R2 P5 Task 3b 不变槽元素三态 te_elem_cmp——T3 §6 残留面收口(令牌按节点同一性 / 类型项按 ty_equiv / 域外 -1)+ 三件证据 残留(T3 停条件② 登记,交接条目作本步 RED):n13 `[NA;2]` vs `[NB;2]` / n17 `*NA` vs `*NB` / n18 元组异名 赋值 ⇒ `unknown_engine=1 replace_unknown=1`(可判定但不变槽判不出 ⇒ 回落 legacy);本步后 **0/0**,rc/诊断逐条不变(冻结基线同诊断)。 实现:`tt_list_variance_at` 不变槽分支由「非同形 ⇒ -1 + uncovered」改「同形(tt_type_elem_same)⇒ 1;否则 te_elem_cmp: ① 两侧皆身份令牌(AK_UNIT ∧ b ≥ 0)⇒ 节点同一性(不同节点 = 确定不同 0,禁 ty_equiv = 令牌碰撞类); ② 两侧皆类型项 ⇒ 引擎 ty_equiv 三态直传;③ 其余(μ 变元 / ¬ / ⊤ₖ = 域外)⇒ -1 + uncovered」;同形快路径逐位保持。 `te_elem_cmp` **不构造项** ⇒ .ccr 零新行(四件与 T3 后状态逐字节同)。 证据① 适用域(「链元素皆类型项或身份令牌」的排序分派):逐构造点排序表 = 令牌位 AK_REF 槽 0 / AK_SUM 槽 0,1(AK_NAMED 槽 0 走 te_named_pair,不经本函数); 类型项位 = SEQUENCE/PTR/PRODUCT/FN 全槽 + AK_REF 槽 1 ⇒ 同 (ak,slot) 无混排。域外面**保持 -1**且**可达** (接口形状 `⊤ₖ(序列)` 落可写 ref 不变槽 = 既有守门例 x2.rw_view_unexpressible)。 证据② 全语料对拍(72 档):check rc **34×0/38×1 逐档同**(仅 2 自源档诊断行号 +174 = 源码增长,措辞/码/列逐字同); shadow **decisions=agree=32988**、old_*/unknown*/replace_* **全 0**(非自源 65 档逐字节同;自源增量全部 agree = 源码行数效应)。 证据③ 突变控制 4 条 + 负控:M1 令牌面中和 4 FAIL / M2 类型项面中和 7 FAIL 且**残留探针复红 1/1**(因果硬证据)/ M3 域外守卫去掉 3 FAIL / M4 分派次序倒置 3 FAIL(**复现 P3-T1 令牌碰撞 `unf.enum_variant_identity_scoped`**); 源 sha + 二进制 sha **精确回滚**。负控:同名令牌恒同节点(DAG 去重五字段键,`t3b.token_same_name_same_node`); `ty_equiv(unit 项, 令牌) == 1` **假等实证** vs `te_elem_cmp == 0`(先判令牌 = 失效方向要求)。 判据:`selftest-types` 386 → **401/401**(−1 例迁出 + 13 t3b 段 + 3 witness 拆项)· `test_named_face.py` **18/18** (`residual.*` 两例行为不变 = T3 交接预期成立)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + error[=0 · 全枚举 **57/57** · **ELF canary IDENTICAL**(95084e7b…d475)· `.ccr` 四件 / `--dump-objects` / `.cir` DOT 与 T3 逐字节同 · 五 CI job rc=0 · 自举链 `corec2==corec3` IDENTICAL(0c4b0a9e…)+ N06=0 + 冒烟 42 + `--help` rc=1 · 构建确定性(含注释面复建同 sha)。 翻转与登记:`param.unknown_p0` → `param.elem_class_diff_zero`(`[int]` vs `[str]` 元素位 -1 → **0** = 语义加强,与 `sequence<⊤>` 正向面无关);`t3n` 残留负例删除(迁 t3b 正例);`t3.counterexample_named_variant` 合取式拆项, 反向/等价 -1 → 0 —— 链:空析取支异变体令牌比较由 -1 变 0 ⇒ 撤掉原先掩盖 `sub_cover(B, B∩¬R)=0` 的 -1, **揭开预存 ¬ 面过判**(同类原子 ⇒ 判「不含」,值集语义应 1;影响面 = 仅 witness 项的 ty_sub 消费者,全语料零翻转) ⇒ 登记后续步(¬ 面「异身份原子 ⇒ 不交」加强)+ 自测钉名 `*_over_claim` 待重钉。 * refactor: R2 P5 Task 4——删 type_equal_legacy 与全部回落面(引擎唯一权威 + 残留 -1 政策 P-A);桥接 legacy 副本与 EXPR_BINARY+OP_ASSIGN 死分支/站点 6 一并删除 清零判据(删除前置,起点二进制 f0862d70… 本实例自跑):72 档 check 34×0/38×1;shadow decisions=agree=32988、old_stricter/old_looser/unknown*/replace_*=0(70 档逐档);站点覆盖 assign-node=24998·fn-body-ret=5905·if-branch=2051·struct-field-type=28·array-elem-type=4·generic-apply-base=2(assign-binary=0 = 站点 6 不可达的语料佐证)。 删除清单(逐件 + 死亡证据): · type_equal_legacy(checker.cr:497-573,注记 10 行 + 函数 67 行,含 7 自调)+ 回落两处(桥接缺口 / 引擎 -1)——死因 = 全语料 replace_*=0(引擎判定与其逐点同值,无人调用其结论); · g_replace_unknown/g_replace_bridge(globals.cr 声明 + checker.cr:246 复位 + ty_shadow.cr 摘要两字段)——计数无对象; · sh_native_ak_legacy/sh_base_ak_legacy(ty_shadow.cr 对照物两份)——零生产调用;自测 3 例改钉冻结期望表 ts_t4_ak_expected/ts_t4_native_ak_expected(生产文件里不再有第二份实现); · 站点 6:checker.cr:2459-2470 分支(12 行,含 sh_site_begin(6))+ ir_gen.cr:932-996 对应发射分支(65 行)——三面不可达(tok2op 零 OP_ASSIGN / T_EQ→EXPR_ASSIGN / += 族显式包裹且 op∈{ADD,SUB,MUL,DIV})+ 语料站点直方图 assign-binary=0;站点 id 不重编号、退役注记落站点表; · type_equal 包装层对照计算 → 纯转发 + sh_compare 调用点移除(D24:对照物已删 ⇒ 判定观察面停摆,中间态不跑影子判据;函数体余部留 Task 5)。 P-A 政策(计划 Step 1 推荐案,落纸待维护者签核):残留 -1 ⇒ 新码 EC_ICE_TY_INDET(ICE04)「type judgment indeterminate」——两因分列措辞(bridge gap / uncovered face|budget exhausted)+ 反例(两侧类型项文本 tt_display)+ 入 main.cr 硬名单(build 亦拒绝落盘);type_equal_engine = 「快路径 + 桥接 + 三态(1/0/P-A 硬错)」;判定点无 AST 位置 ⇒ line/col = 0(定位由调用点自身诊断承担);回退面 = 删硬名单一行。 判据(全部本实例实跑):build rc=0(error[=0)· corec=eae13428cfe881c832d4f836d30361d529b70578a37cb281cc743c9fc3e755d5 · selftest-types 404/404(401+ts_t4_run 3 例;5 处计数钉重钉「零 ICE04 诊断」形态)· check src/compiler rc=0 · 五 CI job 全 rc=0 + 全枚举 57/57 · ELF canary 95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 IDENTICAL(28822B)· .ccr 四件与 T3b 后逐字节同(ptr_arith 90823B fb4a3b59… / 90966B 592afa31…;generics_test 135341B ddec1ce6… / 135484B cd2af565…)· --dump-objects 两口径 cmp IDENTICAL · .cir DOT 逐字节同(165915B)· 语料 check 34×0/38×1 + 日志仅 2 档自源均匀 −38 行位移 · 冻结基线同源对拍(pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源)72 档 rc + 日志逐字节零差异 · 探针全集(T3/T3b/T0 三套)pre-T4 vs post-T4 rc + 日志全同 · 自举链 corec2==corec3 cmp IDENTICAL(36e8f3fd…)+ N06=0/0 + 冒烟 42 + --help rc=1 · 突变控制 3 条逐条转红并精确回滚(M1 去 P-A 报告面 ⇒ 3 例红;M2 两因措辞合并 ⇒ 1 例红;M3 强制触发 ⇒ check rc=1 打印 error[ICE04]: type judgment indeterminate: string vs int (uncovered face) + build rc=1 无产物 = 硬名单接线端到端证明)。 登记:ts_t4_run 初版用 AK_NEVER×AK_DYN 断言 -1 被实测推翻(⊥⊆⊤ 不反向 ⇒ 已判面 0)⇒ 改用命名原子×NEVER(域外守卫);docs/error-codes.md 补 ICE04(含统计表);src/ci/run.sh 计数注释 401→404;报告 .superpowers/sdd/p5-task4-report.md(工作区,不入提交)。 * refactor: R2 P5 Task 5——影子层下线(判定面对拍仪器整体退役)+ 回归网切换(冻结基线同源对拍 + 行为探针 + 突变控制);桥接层剥调试计数 Gate(下线前置,实测):sh_compare 零调用点(T4 已移除)⇒ 影子判定输出结构性为空——起点二进制 × 72 档语料 70/70 档 decisions=0 agree=0 + 全差异桶 0;消费者只剩 sh_finish(main.cr)← 两个默认关的 CLI 旗标;无测试驱动 ⇒ 无活依赖。 删除面:12 函数(sh_compare/sh_record/sh_site_begin/sh_report/sh_dump_write/sh_site_name/sh_kind_name/sh_count_* ×4/sh_map_hits)+ sh_finish(main.cr,含 2 调用点)+ 17 个 g_shadow_* 全局(含调试计数 g_shadow_hits)+ SHADOW_RING_CAP + 2 个 CLI 通道(--type-shadow/--type-shadow-dump)+ g_shadow_on 赋值 + checker 9 处 sh_site_begin 挂点(站点 1/2/3/4/5/7/8/9/10;判定点本体一律保留)+ hits 存-复原 hack(sh_tk_split_impl/sh_tk_term_of_code/sh_map_reset)。改名(计划择一条件:引用 29≤30 且不触 corearch 清单):g_shadow_map/_cap/_entries → g_term_map/_cap/_entries;SHADOW_MAP_INIT_CAP → TERM_MAP_INIT_CAP。保留 = 桥接层全族与展开层全族(均为生产面)+ sh_unf_* 计数(展开层自测读数)。 偏离计划 D27 一项(提请复核):--verify-named-dedup/named_dedup_verify 未删——非影子面(零 g_shadow_* 耦合)+ 有活消费者 tests/selfhost/test_named_dedup.py(侧表↔res_type_node 管线内断言;删旗标 = 该套件无驱动、违「自测用例只增不减」);回退面 = 一行。--verify-evp-nodes 同理保留。 回归网切换(D26-①;声明落 src/ci/run.sh 自述块 + ty_shadow.cr 文件头 + TODO #48):判定面回归网 = ① 冻结基线同源对拍(pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源,72 档 check rc + 日志逐档 diff)+ ② 行为探针 + ③ 突变控制 + ④ 三态纪律(ICE04);后续批次不得引用已下线的计数/摘要/站点直方图;N 面证据只能来自行为探针(影子对 N 面失明)。新挂 test_named_dedup.py 进 selfhost-tests(部分兑现 #31 点名守卫)。 判据(全部本实例实跑):build_selfhost_native.py rc=0(corec 2342fd03…,期间全部构建同 sha;真实诊断 error[=0)· test_backend_bootstrap rc=0(error[=0)· check src/compiler rc=0 · selftest-types 404/404(两例重钉不增减:tk_derivation_not_a_shadow_site→tk_query_purity_and_cache_hit、bridge.hit_delta→bridge.cache_hit_same_term_no_new_entry)· 五 CI job 全 rc=0 · 全枚举 57/57 · ELF canary 95084e7b…d475 IDENTICAL(28822B)· .ccr 四件(fb4a3b59…/592afa31…/ddec1ce6…/cd2af565…)与 T4 逐字节同 · --dump-objects 两口径 3521 行 IDENTICAL · .cir DOT 165915B IDENTICAL · 冻结基线同源对拍 72 档 rc + 日志零差异 · 探针全集 30 档 vs T4 记录零差异 · 自举链 corec2==corec3 cmp IDENTICAL(75da3610…,2854878B)+ N06=0 + 冒烟 42 + --help rc=1 · 突变控制 2 条:M1 派生面(sh_dfn_code_of_slots 去辅码 ⇒ 自测 400/404 + test_ccr_types 35/40 + .ccr sha 变)、M2 判定面(type_equal_engine 的 e==0 判真 ⇒ test_named_face 10/18 + 自测 397/404 + 语料同源对拍 12/12 档 DIFF,其中 3 档 rc 1→0 = 拒绝静默消失)——两条均精确回滚(源 sha + 二进制 sha 复原) * docs: R2 P5 Task 5 收口——P3/P4 两计划的「每任务复跑影子对拍」约束行改写(影子通道已下线 ⇒ 证据载体换为冻结基线同源对拍 + 行为探针 + 突变控制;「零差异 ≠ 面收敛」与「影子对载体面零覆盖」两条口径继续有效) 依据 = P5 计划 Task 5 Step 3(把 P4 Global Constraints 的该行在计划继承注里改写,避免后续批次引用已死的通道);指针 = TODO.md #48 + findings §15。本提交仅文档(两行注记),零源码/零产物面。 * docs: R2 P5 Task 5 收口(补)——P3 计划的「影子对拍门(每任务复跑)」约束行改写 补 d65ff9ad 的声明范围:上一提交因路径笔误(`2026-03-11-…`)实际只含 P4 计划一行,本提交补齐 P3 计划 `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md` 的同款注记(影子通道已下线 ⇒ 证据载体换为冻结基线同源对拍 + 行为探针 + 突变控制;「零差异 ≠ 面收敛」/站点覆盖口径继续有效)。仅文档,零源码/产物面。 * fix: R2 P5 Task 6——EXPR_LET 站点判定点(TODO #32 收口) 判定(新 `checker.cr::check_let_annot_compat`)= `type_compat_strict`(身份 + 常量档长度 + 可选目标注入)+ `diag_type_incompatible`,**两调用点共用**:① `infer_expr` 的 `EXPR_LET` 分支(局部);② `check_global_let`(**全局初始化器**——TODO #32 点名的 `:1614-1620` 同形 缺口,本任务显式划界覆盖)。参序照 P3 T1 §3.1(源 = `val_ti`,目标 = 注解行)。**码 = TA02**(`EC_TA_DECL`,error-codes.md 既有槽位,修复前定义零 raise ⇒ 复用非新码)+ 入 `main.cr` 硬名单(build rc=1 + 无产物;拒绝早于 lower/写 .ccr/ELF)。豁免四条(函数头注 逐条附理由):无注解/auto/无初值 · 注解 `dyn` · 值 `TI_NEVER`(底部 + **错误标记=诊断级联 抑制**:`x : int = nosuchname` 只发 N01)· 注解泛型形参(照返回位点既有策略,`fn f[T](v: T) -> T { return 5; }` 今天即 rc=0)。 RED(冻结基线 `/tmp/p5t0/base/corec` 与本批起点 `2342fd03…` 同值 ⇒ 预存):`x : int = "s"` (本任务加)/`x : [int;3] = s`(切片)/`x : [int;4] = [1,2,3]`(异长常量档)/`x : int? = 5; y : int = x`(可选流窄槽)全部 check rc=0 零诊断 ⇒ 现全部 TA02 定位硬错 + build rc=1 无产物。 report-only(开门依据):72 档 runner `check` **rc 34×0/38×1 与 pre-P5 基线逐档同 + 诊断正文 归一化逐条同 + 零 TA02**;`check src/compiler` rc=0 零诊断;**58 套件全枚举**(tests/selfhost **内联源** + tests/bootstrap)命中面 = **恰 1 例**:`test_named_face.py::named.in_container_same` 的 `p : *NA = 0;`。**裁决 = 修语料,不放松检查**(三证:① `*T ← int`/`*T ← None` 在**赋值 位点**早已 TA01 拒绝,冻结基线与本任务二进制同值;② bootstrap 参考实现 `_check_let_decl` 的 全局面本有 `declared type ... but initialized with ...` 比对 ⇒ 「声明 vs 初始化」是设计语义; ③ 改为 `p : *NA, mut;` 后指针身份面语义逐字保持)⇒ 该套件 18/18、全枚举 58/58。计划预期的 三类命中(泛型体 `T` 注解 / `[T;N]` / 可选注入)实测**零命中**(豁免面/正控覆盖,已逐条实跑)。 判据(本实例实跑;二进制 `f737e26b…`):新套件 `tests/selfhost/test_let_check.py` **24/24** (拒绝 11 + 正控 13;无产物 + 前端拒绝 + 级联抑制 exact-diags;挂 `src/ci/run.sh`)· `selftest-types` **404/404**(不增减)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · **五 CI job 全 rc=0** · **全枚举 58/58** · **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· **`.ccr` 两套口径四条与 T5 记录逐字节同**(`ptr_arith` 90823B `fb4a3b59…`/90966B `592afa31…`;`generics_test` 135341B `ddec1ce6…`/135484B `cd2af565…`) ⇒ 零发射面泄漏 · 自举链 `corec2==corec3` cmp IDENTICAL(2854910B `5559beef…`)+ N06=0/0 + 冒烟 42 + `--help` rc=1 · **冻结基线同源对拍** 72 档 rc + 诊断正文零差异 · **突变控制 4 条** (M1 局部调用点移除 → 15/24 · M2 豁免④移除 → 23/24 · M3 硬名单条目移除 → 20/24「静默面 复活」· M4 全局面调用点移除 → 23/24 单点归因)逐条转红 + **精确回滚**(源 sha `fff7a8ba…`/`739f7f5f…` + 三二进制 sha 复原)。 登记(不静默):`-> never` 函数的**调用**被推断为 unit(非 never)⇒ `x : int = boom()` 新增 TA02,但 `return boom()` **今天即 TF01**(冻结基线同值)⇒ 同一既有 `never` 类型化缺口多暴一 例、非新类(全语料零 `-> never`);**#20(F3 调用位点)不并入**(本任务只落声明位点)。 报告 = `.superpowers/sdd/p5-task6-report.md`(工作区);计划 Task 6 记录 + TODO #32 划销同批。 * docs: R2 P5 Task 7 收官——全量回归 + 判据复验 + 统一台账 + 文档 + 附录 E 增补(P6 交接包) 全量回归(本实例实跑;构建确定性 ×2 逐字节同 = corec f737e26b… / corearch 544e9201… / corelsp 4251108b… ⇒ 提交树 == 被测二进制度): 五 CI job 全 rc=0 · 全枚举 58/58 rc=0(51 selfhost + 7 bootstrap)· selftest-types 404/404 · check src/compiler rc=0 · 自举链 corec2==corec3 cmp IDENTICAL(5559beef…,2854910B)+ N06=0 + 冒烟 42(corec/corec3 双通道)。 判据复验:ELF canary 95084e7b…d475(28822B)IDENTICAL · .ccr 两套口径冷态四条(90823/90966/135341/135484,差恒 143B)· --dump-objects 3521 行 IDENTICAL · .cir DOT 165915B IDENTICAL · 冻结基线同源对拍(pre-P5 5d2b15ad… × 当前源 vs 当前 × 当前源) 72 档 rc + 日志零差异 · 行为探针全集 30 档零差异 · 突变控制 M1(派生面:400/404 + 35/40 + .ccr sha 变)/ M2(判定面:10/18 + 397/404 + 对拍子集 12/12 DIFF)逐条转红 + 精确回滚 · 冷/热三类缓存态复测(缓存启用档真命中、ELF 冷≡暖、.ccr 冷≠暖各自确定性;含泛型/可选档 entries=0 ⇒ 冷≡暖)。 统一台账(T0–T6 汇总):删除/退役 6 件 + 1 改名族(legacy 判定族 / g_replace_* / 桥接副本 / 站点 6+死分支 / 影子层 12 函数+17 全局+2 CLI+9 挂点 / hits hack;g_shadow_map→g_term_map)· 收紧 5 类(D23 · TG02 混类 · te_elem_cmp 不变槽 0 · ICE04 · TA02)· 放宽 0 · 修复 4(暖态 SIGSEGV / F1 r9 残留 / 声明位点静默错产物 / 死分支)· 实施偏差 8 条;全语料命中 0。 文档:spec §9 P5 行 ✅ + §3.4/§6.2/§6.3(单槽形态 + D20 稳定引用形态规范 + β→P6)· format spec §3.3(NOD tk = 派生码)+ test_ccr_types 34→40 · findings §16 P5 收官(文件自冻结)· TODO #49 + #24/#31/#48 指针更新 · 计划:实施状态总表 + 收官总述(一页)+ Task 7 实施记录 + 统一台账 + 附录 E 增补 7 条 (¬ 面过判 / -> never 调用面 / #20 调用位点 / 冻结基线复现配方 / ICE04 签核 + 非硬名单诊断不阻 build / string==int 混比 + >1GiB 分片 / 设计先例引用面)。 源码零改动(纯文档面);待维护者签核三项已给回退面(ICE04-P-A 政策 · --verify-named-dedup 保留 · 冻结基线配方落位)。 * docs: R2 P6(接口化尾巴批)实施计划定稿——附录 E 16 项分诊(实施 7:β 盘面项索引 / ¬ 面过判 / -> never 调用返回型 / 冻结基线复现配方 / 残项与陈旧件 / 先例引用;待裁 7:容量批归属·政策复核·string==int 混比语义·iterable 是否入批;维持登记 1.5)+6 任务(T0 侦查+基线重建 pinned 9bcb7083 · T1 回归网可复现化 · T2 ¬ 面过判 · T3 β+版本 8→9+atom_of 读回 · T4 never 调用面 · T5 文档卫生 · T6 收官)+停条件(canary 变/对拍 diff/重建 sha 不符/新硬错非空/被迫动 .cir);三项未确定如实标注(E-7 意图无出处 · E-1 的 36B→40B 与其点名两量矛盾列为 T3 内裁 · 探针 30 档实为 29 且源已丢、runner 空跑 ⇒ T0 清点) * feat: R2 P6 Task 1——回归网可复现化(E-13):冻结基线重建配方 + sha 白名单 + 双 runner + 探针语料入仓 - tools/baseline/{REBUILD.md,rebuild.sh,parity_run.sh,probes_run.sh}:配方 = jj workspace add --revision 9bcb7083 + build_selfhost_native.py;sha 硬闸(corec 5d2b15ad… / corearch 493dc490… / corelsp 18b94bd9…;不符即 exit 1,不得改白名单对齐)+ 换代纪律 + 已知限制(CI 不挂钩:浅检出 fetch-depth:2 且工作流无 jj)。 - tests/probes/:29 档探针语料实拷(源 = /tmp/p5t0/probes 11 + /tmp/p5t3/probes 18;sha 与 T0 清单 29/29 逐条同)+ README 逐档来源表 + 运行条件(变异态/影子态不可复跑面登记,不假装可跑)。 - U-4 收口:探针正式清点 29;「30」= 29 + 迁移 runner 的字面 glob 伪条目(新 runner 以显式清单 + 计数断言消灭该形态)。 - src/ci/run.sh 自述块:冻结基线腿自本任务起可复现(指向 tools/baseline/;不再引用已死 /tmp 路径);TODO #50 新条目 + #48① 闭合;计划 Task 1 六步勾选 + 实施记录。 - 判据(本实例实跑):rebuild 41s ⇒ 三 sha=白名单 + cmp 与 /tmp/p6t0、/tmp/p5t0 IDENTICAL ×3 + 冒烟 42;入仓 runner vs 迁移 runner(同二进制)72 档 diff -rq 零输出(34×0/38×1 两侧同);腿① 主判据(冻结基线 × 当前源 vs 当前二进制 × 当前源)零差异;探针两态零差异 + rc/log 29/29 与 P5 T7 逐字节同;五 CI job 全 rc=0;全枚举 58/58;check src/compiler rc=0;selftest-types 404/404;canary 95084e7b…d475 IDENTICAL(28822B);自举链 corec2==corec3 5559beef… + N06=0 + 冒烟 42 + --help rc=1;test_backend_bootstrap rc=0 + error[=0;构建确定性(多构建同 sha f737e26b…/544e9201…/4251108b…);源码零改动(checker.cr fff7a8ba… = P5 T6 记录值)。 - 突变控制 5 条转红(M1 语料掉档 / M2 伪条目 / M4 cwd 陷阱 / M5 rc 通道打哑 / M7 源级判定放宽 ⇒ 12 档 DIFF、5 档 rc 1→0)+ 1 条诚实未红(M3 去 clean-cache:check 面不读 .cir 缓存 ⇒ 本腿不可检出);仓库零改动。 - 报告 = .superpowers/sdd/p6-task1-report.md(工作区,不入库)。 * fix: R2 P6 Task 2——¬ 面过判收口(E-10) 落点(经前置侦查修正计划 Step 3):`ak_disjoint` 只收类 id、看不到项(调用面 :118/:327/自测均只传类 id)⇒ 按计划字面改 = 「同类一律不交」= 假不交。改为在 `lit_implies` 的 ¬ 面新增 `atom_same_class_disjoint`(域守卫)+ `sum_chain_same`(AK_SUM 链逐元素令牌同一性、三态),`:326`(AK_NAMED 早退)后插一行分派;`ak_disjoint` 本体与正×正(`lit_empty_pair:118`,经 ty_inhabited→ty_exhaustive 达 TM03 生产面)零改动。 域 = 仅 AK_SUM 变体项(链 = [枚举名令牌, 变体名令牌]):异链 1 / 同链 0;域外逐条维持原语义(AK_NAMED 维持 -1(P0 终审 Important B 在册政策,不翻转)· 参数化构造子(反例 [int] vs [int∪string])与 ⊤ₖ(tt_top_k 的 c 槽 = 0 非 -1)与空链/链元素域外(P1-a)一律 0)⇒ 零新增未覆盖面、零行为变化面。 用例:三例 t3.witness_reverse_over_claim / t3.witness_equiv_over_claim / t3.product_neg_over_claim 转 1 重钉(0→1)+ 邻域负控 11 例(子 runner ts_t2_neg_run,不内联巨型函数):同身份 / μ 变元字面 / ⊤ₖ 双向 / 跨枚举同名 / 同枚举异变体 / 令牌字面 / AK_NAMED / 空链与链元素域外 / 参数化面 / REF mut 标记面。 判据(隔离 workspace @e5ce8f9e,主树零改动):selftest-types 415/415(404+11,只增)· check src/compiler rc=0 · ELF canary 95084e7b…d475(28822B) IDENTICAL · .ccr 两口径冷态四条 = T1 记录值(90823 fb4a3b59 / 90966 592afa31 / 135341 ddec1ce6 / 135484 cd2af565,差恒 143B)· --dump-objects 3521 行 = T0 记录 · .cir DOT 165915B = T0 记录 · 五 CI job 全 rc=0 · 全枚举 58/58 · 自举链 corec2==corec3 IDENTICAL + N06=0 + 冒烟 42 + --help rc=1 · 冻结基线同源对拍 72 档零差异(rc 分布两侧 34×0/38×1)· 探针 29 档逐档内容同 T0 记录(rc 17×0/12×1)· 对 T0 起点仅两自源档行号偏移 +155(= 本任务净增行数 166-11;去行号后内容逐字同)。 突变控制(判据 = M1/M2a/M2c′/M3):M1 撤销加强 ⇒ 5 FAIL(三例 t3 + N5 + N10)· M2a 类域守卫去掉 ⇒ N11 转红 · M2c′ 不可判出口 return 0→1 ⇒ N8 转红 · M3 次序对照(新行移到 ak_disjoint 之后)⇒ 零差异(最小性)· 附加 M3b(同类条件去掉)⇒ 既有 exhaust.witness_bool 转红(条件必要性);M2b(⊤ₖ 守卫)因 tt_top_k c 槽 = 0 ⇒ 解释不稳,仅探索记录。回滚逐条源 sha 复原。 实现偏差 2 条(诚实登记):① 首版链比较误用 te_chain_cmp(命名面槽位语义:槽 ≥1 = 类型项)⇒ AK_SUM 槽 1 的变体令牌走 ty_equiv 被判等 ⇒ 三例仍 0 而 9 例负控全过;补 N10「同枚举异变体」暴露,改用 sum_chain_same 修复。② 调用侧空链守卫 `if cp < 0 || cq < 0` 单独删除 ⇒ 零差异(与 sum_chain_same 内层等价)= 无牙齿行,保留作显式域注释(如实登记,不假称牙齿)。 登记 3 条(未修):① tt_display_at 参数链分支对带链原子渲染退化为 `>`(预存缺陷,ICE04/反例诊断的参数化类型显示退化,建议 TODO);② 正×正(lit_empty_pair:118)同类异身份加强 = 单列候选(达 TM03 生产面);③ 命名链半项维持 -1(政策面待裁)。 计划勘误并入:Task 2 Step 2/3/4/5/6 + 附录 E-10 行 + 统一台账行 + 实施记录块。 * feat: R2 P6 Task 3——β:`.ccr` NOD 盘面项索引 + CCR_VERSION 8→9 + corearch atom_of 读回(发射面零泄漏) 先裁(Step 1 门):U-2 = L-a(40B = 32B 语义区 + 8B 邻接;新字段 item i32 @+28 = TYPE 段文件空间项索引,-1 = 无项;邻接域顺移 +32/+36);D20 合规三论证 = ① 只引入 D20-② 唯一许可形态(跨边界引用零新增)② 「码 = 派生量」不变(两值同源于唯一写点)③ 分歧即硬拒(loader TYPE 段后一致性校验:域外 / atom_of(项) ≠ 盘上码 ⇒ 拒绝 rc=1)。L-b 排除 = 单槽「项索引 ∪ 辅码」+ 分类表判读读侧不可判定(分类表恰需被丢弃的派生码;反例 ptr_ref_first 的 IR_BINARY tk=10)。U-3 = (i)(不动 .cir,CIR_CACHE_VER 保持 17)。 实现:写侧 = ccr_types.cr:ccr_nod_item_populate(保存期由盘上派生码经 sh_tk_face_of_code/sh_term_of_ti 单源重派生——计划 Step 3 原文「同出 emit 期那一次拆分」与实读时序矛盾:ccr_type_populate 的 tt_layer_reset 在 save 前作废 emit 期引用、D20-③/bad_term=329 同类;偏差登记于计划 Task 3 实施记录);读侧 = load_ccr 的 TYPE 段后硬校验 + corearch --dump-nod-items 读回通道(item → 项表取项 → tt_atom_of_term → 行);tt_atom_of_term 自 ty_shadow.cr 整函数迁入共享层 type_terms.cr(corearch 清单不含桥接层;B.6 三面核无清单增删)。 判据:ELF canary 95084e7b…d475(28822B)IDENTICAL · --dump-objects 两口径逐字节同(vs T0 记录 cmp)· .ccr 两口径四条重锁 + 逐段 delta 归因 = 4B × 节点数(pa 90823→96015 / 90966→96158;gt 135341→142793 / 135484→142936;口径差恒 143B)· 72 档 check 同源对拍零差异(构造性隔离腿:check 在 main.cr:448/:450 早退,不读不写 .ccr/.cir)· 探针 29 档零差异 · 五 CI job 全 rc=0 · 全枚举 58/58 · selftest-types 404/404 · check src/compiler rc=0 · 自举链 corec2==corec3(2859006B)+ N06=0 + 冒烟 42 + --help rc=1 · test_backend_bootstrap rc=0 + error[=0 · test_ccr_types 40→48 / test_ccr_v7 27/27 / test_region_cfg 22/22(三处 v8/36B 重锁;region_cfg 为计划漏列点)· 突变 M1–M5 逐条转红 + 精确回滚(M3 = 改用 emit 期活表索引 ⇒ loader 拒 = save 期重派生的机器抓手)。 Files 勘误(并入计划 Task 3 实施记录):type_terms.cr(共享层迁移)· regalloc.cr(L-a 下零改动,读回实体)· test_region_cfg.py(漏列)· corearch 读回被挂在 main.cr(实为 corearch.cr:243 + regalloc.cr:877)· 锚点 src/compiler/ent_kernel.cr 实为 src/lattice/ent_kernel.cr。残留登记:暖态项面为部分(缓存命中跳过 ir_gen ⇒ 暖进程未分配行落 item=-1,码由 tk 保真;冷态完整、冷/热各自确定性不变);更严单源形态 L-c 登记未取。TODO #51。 * docs: R2 P6 Task 5——文档/卫生批(MIN_CASES 收紧至 415 + module_to_ccr 废弃标注 + 两 spec 陈旧值与 --type-shadow 先例载体改写 + 计划勘误) - tests/selfhost/test_type_engine.py:MIN_CASES 32 → 415(实测值;注释口径「只增不减——新增/删除用例须同步下界」)。 - tools/module_to_ccr.py:头注废弃块(写出 version=1 远古 .ccr / 现行 corearch 整类拒收(ccr_io.cr:135 CCR_VERSION=8)/ 零 in-repo 调用者);**保留不删**,删除候选登记。 - explain-predict spec **5 处**:.cir 缓存版本旧值 14→**17** + 行号 :16→**:51** + **magic `C1C1…` 分句删去**(该断言与实测不符——自举二进制落盘 magic 全 0,P4 T5/TODO #14);:84/:181 旗标计数 8→7;:127/:447 先例载体 → `--verify-named-dedup`/`--verify-evp-nodes` + 行为探针口径(原则不变:审计通道默认关、两态产物同)。 - policy-injection spec:275:同款先例载体改写。 - src/ci/run.sh:**零改动**(逐条实测复核 = 全部 `N 例` 注与实测一致,T2/T3/T4a 已各自同步;陈旧「14 档挂点」在计划文件,非 run.sh)。 - 计划勘误(本节实况化 + 侦查底座陈旧锚:挂点 28 / 自述块行域 / 计数注行号 + T1 记录一句不精确);TODO #53 条目 + 五项登记(删除候选 / test_purity MIN_CASES 邻域 / T0 记录归 T6 / T2 缺 TODO 条目 / pkg 非入库面)。 - 判据(本实例实跑):**重建 ⇒ corec ae01de75… / corearch 228f82e9… / corelsp 90eb19c6… 与编辑前逐字节同**(T5 零 .cr 改动 ⇒ 零行为变化的构造性证据)· 五 CI job 全 rc=0 · 全枚举 58/58 · selftest-types 415/415 · check src/compiler rc=0 · canary 95084e7b…d475(28822B)IDENTICAL · .ccr 四条 = T3 新锁值(96015 680a6f98…/96158 76f36e6a…/142793 41e9d845…/142936 704316c8…,差恒 143B)· 腿① 同源对拍 72 档零差异 · test_type_engine.py 绿 · test_backend_bootstrap rc=0 + error[=0 · 自举链 corec2==corec3 IDENTICAL(0b2e06d0…)+ N06=0 + 冒烟 42 + --help rc=1。 - grep 复核:--type-shadow 无活跃引用(src/ 8 + tests/ 3 全为「已下线/历史」注;注册面三处零在位);陈旧版本值在 docs/ src/ tests/ 零引用。 * docs: R2 P6 Task 6 收官——全量回归 + 判据复验 + 统一台账 + spec §9 P6 行 + TODO #54 + 尾批终态陈述(P6 全阶段收官) - 回归(实测):构建确定性 ×2 逐字节同 · 五 CI job 全 rc=0 · 全枚举 58/58 · check src/compiler rc=0 · selftest-types 415/415。 - 判据复验(实测):入仓配方重建(tools/baseline/rebuild.sh)三 sha = 白名单(5d2b15ad…/493dc490…/18b94bd9…)+ 冒烟 42 + 零残留 · 腿① 同源对拍 72 档零差异(34×0/38×1 两侧)· 探针 29 档两态零差异(17×0/12×1)· canary 95084e7b…d475(28822B)IDENTICAL · 自举链 corec2==corec3 0b2e06d0… + N06=0 + 冒烟 42 + --help rc=1 · .ccr 四条 = T3 新锁值(96015 680a6f98…/96158 76f36e6a…/142793 41e9d845…/142936 704316c8…;差恒 143B)· --dump-objects 3521 行 · .cir DOT 165915B · test_backend_bootstrap rc=0 + error[=0。 - 统一台账(计划「统一台账」7 小节):实施 6 · 删除 0(+1 候选)· 迁移 1 · 收紧 3 类(¬ 面异身份不交 / .ccr 版本闸 v9 整类拒收 / loader 一致性硬校验)· 放宽 1 类(-> never 调用返回型)· 修复 1 件(+1 并记)· 登记 19 · 偏差 9(含 T3 抢跑事故记录引用 + 提交形态 + T4a CI 期间改源事故)· 未取裁 7。 - 文档:spec §9 P6 行 + P5 行「行为探针 30 档」U-4 勘误注 · TODO #54(P6 收官条目;建议补 #55 = T2 的落地登记)· CLAUDE.md 一行(tests/probes/ + tools/baseline/ + 判定面回归网四腿)· 计划:Task 0 实施记录回填(T0 当批只出报告、不入库 ⇒ 计划零留痕;归属裁定 = T6)· 未决项/登记收口 · 尾批终态陈述(一页;下一批指向 = 容量批或验证切片轮,按裁-P6-1)。 - 待维护者处置(只登记):四裁定门全未取裁(裁-P6-1 容量批 / 裁-P6-2 政策五问 / 裁-P6-3 混比 / 裁-P6-4 EXPR_FOR)+ U-2 的 L-c + T2 正×正面候选 · 主树工作副本 9 未提交文件 + build/* 为 T3 味(最终整合涉还原 ⇒ 须明确许可)。 - 本提交零源码/工具/测试面改动(纯文档/台账面;jj diff --stat -- src/ tools/ tests/ = 0 files)。 * docs: 容量批实施计划定稿(E-2 对象布局表示位 / E-3 MAX_* 定长槽解除 / E-4 可选程序收回 .cir 缓存)——E-3 四前置逐条实核(全部未满足;镜像不一致面比登记宽 3 处)+裁决门 3 主问 3 附问+任务 T1–T5+8 条锚点漂移(CIR_CACHE_VER 实值 17 非 18 · 受护访问器区间 · E-4 机制未能复现须 RED 实证)+最大风险(E-2 约 8 处写点漏一处即「响亮 139」变「静默错值」) * docs: fail-closed 判据线实施计划定稿(报错 ⇒ rc=1 + 零产物;硬名单反转为默认阻断 + 已知假阳性豁免登记表)——全诊断面清点(可达 42 码 = 必须阻断 25 / 必须豁免 7 / 已 fail-closed 10;定义零 raise 死码 ≈104)+现行闸门实为三处且 check 面本就 fail-closed(缺口只在 build/ccr/cir/run 四面)+report-only 先行(T1 零源码改动采 build 面命中清单)+裁决门 6 条(含 B004 × canary 载体硬冲突:ptr_arith.cr 自身即 check rc=1 档)+任务 T0–T6 * chore: 容量批 T1——冻结基线白名单换代(9bcb7083 → 97f4394f;P4 收官 → P6 终态) 换代依据 = tools/baseline/REBUILD.md 换代纪律 1/2(下一批以本批收官为 pre-侧;旧值留痕)。 - PINNED_REV: 9bcb7083(P4 收官)→ 97f4394f(P6 终态 = 容量批 CAP 的 pre-侧) - 白名单(新): corec ae01de75… · corearch 228f82e9… · corelsp 90eb19c6… - 白名单(旧,留痕): corec 5d2b15ad… · corearch 493dc490… · corelsp 18b94bd9… (REBUILD.md 新增「白名单历史」表;rebuild.sh 白名单块加换代注) - 语义零变化:仅配方常量(pinned 修订 + 三 sha),零源码改动 自证(本实例实跑;证据 /tmp/capt1/**): 1. 换代后重跑配方 ×2(其内部 workspace add/forget 自管)⇒ 两次三 sha 逐条 = 新白名单 + 冒烟 rc=42 + cmp ×3 IDENTICAL(base_v2 / base_v2b) 2. 树等价证明:jj diff --from 97f4394f --to 661ae569 = 仅 2 个计划 md 新增、0 修改、0 删除 ⇒ 构建输入与 P6 终态逐字节同(直接构建产物 = 配方产物同 sha) 3. 起点判据 14/14 与 P6 终态记录全同:五 CI job rc=0 · 全枚举 58/58 · selftest 415/415 · canary 95084e7b…d475(28822B)· .ccr 四条 96015/96158/142793/142936(差恒 143B;暖态同冷态)· --dump-objects 3521 行 · .cir DOT 165915B · 自举链 0b2e06d0… + N06=0 + 冒烟 42 + --help rc=1 · 72 档 34×0/38×1 零差异 · 探针 29 档 17×0/12×1 零差异 · test_optional 34/34 · test_enum_limit 13/13 (报告:.superpowers/sdd/cap-task1-report.md) * fix: TC02 收口——if 分支相容判定的发散豁免(P3 不对称;真误报修复) 根因(归因链 = /tmp/fct1/tc02_attribution.md): - `EXPR_RETURN` 推断 = **所返回值类型** ⇒ `{ return 1; }` 的块类型 = int; - 而 `if` 的值类型定义为 **then_ti**(EXPR_IF 合并点 `return then_ti`) ⇒ 合并点的相容判定(`:2907`,仅豁免 `TI_NEVER`)把**发散支的幻影类型**当有值参与比较 ⇒ `if { } else { return 1; }`(及镜像形)恒报 TC02。**与 float 无关、预存、对称**。 修法(维护者 2026-09-14 裁 = P3): - 新增 `stmt_diverges(node)`(return 族 + STMT/UNSAFE 包裹 + 块内任一句;**只服务本判定点**); - `:2907` 加嵌套 `if` 守卫:**仅 else 支确定发散 ⇒ 不报**; - **不对称是有意的**:then 支发散时 then_ti 本身即幻影(模型面,另案)⇒ 仍报,保留真信号。 - **明令不做**:改 `EXPR_RETURN` 推断(会连带关掉 TF01 ⇒ 静默错产物);动 `stmt_cannot_fall_through` 本体与其 5 个调用点(其头注明令 return 不判 divergence)。 判据(本实例实跑;二进制 corec 269d0a47… / corearch 228f82e9…): - 新套件 tests/selfhost/test_tc02_branch.py **15/15**(挂 run.sh selfhost-tests); - 五 CI job 全 rc=0 · 全枚举 **58/58**(test_native_float 两正例在 fail-closed 裸反转口径下由红转绿); - **ELF canary `95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 96015/96158/142793/142936(差恒 143B); - `--dump-objects` 3521 行 · `.cir` DOT 165915B b1bdf480… · selftest-types **415/415**; - 自举链 `corec2==corec3` IDENTICAL(c8c63985…,2880022B)+ N06=0 + 冒烟 42 + --help rc=1; - 72 档对拍零差异(34×0/38×1)· 探针 29 档零差异; - **report-only 全语料扫面**(590 内联源两态):**仅 2 处变化、均为 TC02 消失、均在 test_native_float** (无新增码、无非 TC02 变化); - 突变 **M1**(去守卫)⇒ 新套件恰 3 例复红 + c01/c02/c05 复报; **M2**(放宽为「任一支发散」= P1)⇒ `tc02_then_return_kept` 越界转绿被抓(14/15)。 文档:docs/error-codes.md(TC02 行注)· TODO #55 · fail-closed 计划勘误(豁免表 10→9 码;selfhost-tests 裸反转真红面 6→5 档)。 * feat: 容量批 T2——E-2 表示面聚合扩展(裁-CAP-1 (a) 存储边界规范化;发射面与 .ccr 零泄漏) 法则(裁-CAP-1 (a)):可选**聚合槽**(结构体字段 / 数组·切片元素 / 元组元素 / 枚举载荷 / 全局槽)恒持装箱值或 None;裸 T 值在**写点**装箱,读取侧零改动。零布局变更、纯 IR (IR_MAKE_ENUM + IR_STORE_FIELD,无新 opcode)⇒ ELF/解释器天然同源;全部由既有零足迹门 `g_optrep_on` 门控 ⇒ 非可选程序逐字节不变。 - 新增单源件(ir_gen.cr):声明类型侧表(g_ir_var_decl_ti)+ ti_is_optional / slot_decl_ti / elem_ti_of(_decl) / field_ti_of_node / struct_row_field_ti / global_decl_ti / enum_payload_ti / elem_node_optional / emit_box_some / box_for_slot_flag / box_for_optional_slot - 接线:A 类 8 写点(字段赋值·字段字面量·数组元素字面量下标/动态下标·数组字面量·全局赋值· 全局初值·match 臂)+ B 类 2 点(枚举载荷 #10 / 元组元素 #11——RED 实证可达后升 A) + match 面按裁决 (i)(result_var 配表示位) - global 面双根因(**预存**,本批修):① 读点常量折叠(find_global_const_node 把 `g : int? = 5` 的读折成裸常量 ⇒ tag 读解引用裸值)② 写点静态度量初值(global_needs_runtime_init 对字面量 返回 0)——两处均只对可选全局抑制/改道 - 过程事故(自查抓到并修复):首版未门控 ⇒ 非可选语料 generics_test.cr 的 .ccr **+168B(全在 TYPE 段)**——根因 = 辅助函数无条件调 res_type_node(对数组/命名类型节点每次分配新类型行); 12 处补 g_optrep_on 门后 gt.ccr 逐字节回到 41e9d845…(142793B) 用例(tests/selfhost/test_optional.py 34 → **48/48**):原未覆盖面 4 例**重钉**(-11 ⇒ 精确值; 死亡证据 = 本批能力变更)+ 新增 14 例(case_dual 三路同证 + **精确值断言**);run.sh 计数注同步。 判据(全绿):canary 95084e7b…d475(28822B)IDENTICAL(**无豁免**)· .ccr 两口径四条(冷+暖) 96015/96158/142793/142936 逐字节同起点(零泄漏)· --dump-objects 3521 行与 P5T5 件 IDENTICAL · .cir DOT 165915B b1bdf480… 不变 · 72 档对拍仅 2 档行号 +231 位移(内容零变,已归因)· 探针 29 档零差异 · 五 CI job rc=0 · 全枚举 59/59 · selftest-types 415/415 · check src/compiler rc=0 · 自举链 corec2==corec3 819af35c… + N06=0 + 冒烟 42 + --help rc=1 · test_backend_bootstrap 绿。 突变 4 条(沙箱):M1 字面量写点 / M3 全局初值点 ⇒ 各自用例 139 红(各证两写点独立)· **M4 双重装箱 ⇒ 4 例 rc=0 静默错值**(仅精确值断言抓得住)· M8 门控去掉 ⇒ **canary 变**(门有牙)。 登记(裁 (A):#9 指针面 :1192 一行未动):五形态证据表 · 根因链(同站点双语义 + 绕过 emit_rep_set + 别名不可静态闭合)· **c3(位=0 时 *p = Some(9))ELF 8 / interp 216 双路径分歧 静默错值——今天就存在的 miscompile,本批不修、非本批引入,建议独立高优先 TODO** · 待裁候选 (D)(语言面拒绝 *p = v 当 pointee 为 T?;影响面已实读)· 其它未覆盖面(字面量非 ident 元素 / 泛型形参字段实例化为 T? / &s.a 写入丢失)。 报告:.superpowers/sdd/cap-task2-report.md(证据 /tmp/capt2/**) * docs: TODO #56——指针写可选槽 × 表示位/规范化互斥(双路径分歧静默错值;容量批 T2 裁 (A) 登记) - 现象:`x : int? = 5; p := &x; *p = Some(9)` ⇒ ELF 8 / interp 216(双路径分歧 + 双错值); 成因 = 表示位 0(裸)而指针写入装箱值(绕过 emit_rep_set) - 三子形态:c1 正确但脆弱(位=0 写裸)· c2 139 响亮(位=1 写裸)· c3 静默分歧(位=0 写装箱) - 根因链 + 同族未覆盖面(数组元素经指针 139 · 全局经指针分歧 · &s.a 写入丢失)+ 待裁候选 (D) - 性质:**今天就存在 · pre-T2 同值 · 本批不修、非本批引入**(容量批 T2 报告 §8-1/8-3 证据) * feat: 容量批 T3——E-3 记录布局迁侧表(裁-CAP-2 (a):字段/变体/载荷上限解除;.ccr 不 bump) 核心手法 = 「内联定长槽区 → 基址 + 侧表」,**记录尺寸与既有 OFF_* 常量不变**(外部算术零改动): - Stage A 结构体字段:OFF_SI_FIELD_NAMES 槽改义为 field_base;名/类型码/类型节点迁三条扁平 侧表(高水位 g_si_f_used;写侧自动推进 ⇒ 未显式 commit 亦不重叠);si_field_row 改「计数为界」 读护栏;P023 闸与槽上限分支删除;ccr_io 读回闸与 16 槽清零循环删除;selftest 15 处直接槽写迁移 - Stage B 枚举变体/载荷:OFF_EI_VARIANTS 槽改义为 variant_base;变体面三条 + 载荷面两条侧表; 载荷基址**惰性分配**(首写占位、计数申报整体占位);P022 两闸与槽上限分支删除;两读回闸与 16 槽清零循环删除;selftest 29 处直接写迁移 - Stage C 覆盖位无界:2^vi 单 int(≥63 溢出;70 变体实测 SIGFPE rc=136)⇒ 无界位图 + 栈纪律 (mc_begin/mc_add/mc_has/mc_first_missing/mc_end);checker 四处消费点同批换;旧 sh_match_bit 保留供 selftest ≤62 域(头注标明生产不再使用) - Stage D:三 MAX_* 常量退役 + P022/P023 标「零 raise」+ ast.cr 四个**死镜像结构**删除 (全仓零类型使用点、[.;64] 与运行期布局本已不一致;布局真源 = dyn_arr.cr) 用例(test_enum_limit.py 13 → 18 例):16 边界回归钉 · >16 转正(17/40 枚举/载荷/字段;死亡 证据 = 侧表解除)· ≥63 覆盖位(64/70 穷尽正确)· 负控(缺臂 TM03 **含正确反例名** / 冗余臂 TM04)· 跨记录完整性(含**判定面敏感**例);每例断言精确值或精确诊断文本。 判据(全绿):结构体 17/40 · 枚举 17/40/64/70 · 载荷 17 ⇒ 编译 + 运行值正确(旧拒绝面转正); ELF canary 95084e7b…d475(28822B)IDENTICAL(**无豁免**);**.ccr 两口径四条冷+暖逐字节同起点 ⇒ 「不 bump」判据成立**(盘面零变化);--dump-objects 3521 行 / .cir DOT 165915B 不变;72 档对拍 仅 2 档行号 +116 位移(内容零变);探针 29 档零差异;五 CI job rc=0(test_optional 48/48 + test_enum_limit 18/18);枚举 59/59;selftest 415/415;check src/compiler rc=0;自举链 IDENTICAL + N06=0 + 冒烟 42 + --help rc=1。突变 3 条(沙箱):MA 结构体基址恒 0 ⇒ 邻记录用例红 · MB 枚举基址 恒 0 ⇒ 判定面敏感用例红(**该用例补强后才有牙**,方法教训已登记)· MC 覆盖位不记录 ⇒ ≥63 反例名 用例红。 登记:MAX_GENERICS 不同批(裁附问)· sh_match_bit 遗留(selftest ≤62)· 侧表只增不减 · 死镜像删除 (历史留痕)。报告:.superpowers/sdd/cap-task3-report.md * docs: TODO #57——E-4 可选程序 .cir 缓存门:机制实证证伪 ⇒ 维持现状(裁 (c));移除门登记为待裁候选 - 实证(沙箱放开唯一门控):冷/暖全命中 × 16 档 + 混态 3 态 × 2 程序 ⇒ ELF 逐字节全同 ⇒ P4 所述「命中恢复 ⇒ 解包回落装箱 ⇒ 冷/暖分歧」未能复现(与 T2 静态枚举一致) - 处置 = 门原样保留(零源码改动);移除门(性能面)登记为待裁候选并附补证清单 - 报告 .superpowers/sdd/cap-task4-report.md · 证据 /tmp/capt4/** * docs: 容量批 T5 收官——全量回归 + 统一台账 + spec §9 容量批行 + TODO #58 + 终态陈述 全量回归(起点 15357b04;本任务零 .cr/tests 改动 ⇒ 二进制「同」为构造性,判据仍全量实跑): 构建确定性 ×2 · 五 CI job rc=0 · 全枚举 59/59 · selftest-types 415/415 · check src/compiler rc=0 · canary 95084e7b…d475(28822B)IDENTICAL · .ccr 两口径四条(冷+暖)96015/96158/142793/142936 · --dump-objects 3521 行 · .cir DOT 165915B · 自举链 f69240ae… + N06=0 + 冒烟 42 + --help rc=1 · **入仓配方重建(现行代 97f4394f)三 sha = 白名单 + 冒烟 42**(换代链路端到端生效)· **腿① 同源对拍(换代基线 × 当前源)72 档零差异** · 探针 29 档零差异 · test_optional 48/48 · test_enum_limit 18/18。**与起点逐项对照:全同,无「异」项**。 台账(计划 docs/superpowers/plans/2026-09-15-capacity-batch.md):KPI = 实施 4 · 删除 3 类 · 迁移 2 类(+ selftest 44 处)· 收紧 0 · 放宽 2 类 · 修复 3 件 · 登记 12 · 偏差 6 · **未取裁 2**; 含基线换代与二进制代逐代留痕(P6 终态/TC02/T2/T5 起点);过程教训三条(「新增类型面查询前 必须核是否 alloc」·「用例必须落在经记录的判定路径上」· 断线固盘纪律);终态陈述三栏。 登记归属:#56(c3 指针写双路径分歧静默错值;高优先·预存·本批不修)· #57(E-4 移除门候选; 待裁)· (D) 语言面拒绝候选(已上报维护者)· 能力面遗留 6 条(台账 §5)。 报告:.superpowers/sdd/cap-task5-report.md * feat: FC 批 T2——fail-closed 闸门落地(默认阻断 + 豁免登记表 9 条;旧正向名单 11 码退役) 语义:任何不在豁免表内的类型面诊断 ⇒ rc=1 + 零产物(判据成文化 =「判定继续 ⇒ 产出静默 错产物」,五组先例:R002 / TS01-04+TK02 / TM03 / ICE04 / TA02)。 落点: - src/compiler/main.cr:硬判定循环反转为 `diag_gate_exempt(ec, GATE_SCOPE_BUILD) == 0 ⇒ hard = 1` (旧 6 语句行 / 11 码正向名单整体退役);`[5/5] frontend done` 改为「诊断后、返回前」打印 (保 72 档日志除行号偏移外零差异);零产物:`exit_code != 0` ⇒ 删本次写下的 `.ccr`。 - src/compiler/diag.cr:新增 `diag_gate_exempt`(豁免表 9 条 = build ×6 [TF01·TF07·TB01·TM04· TK01·B04] + check ×3 [N01·N06·N11,仅登记——裁-FC-1=(C) 终局],每条带位点证据/理由/退出条件; 只减不增)。 - src/compiler/globals.cr:`GATE_SCOPE_BUILD/CHECK` 常量(与 DIAG_REC_SIZE 同址)。 - 语法面闸(:134/:135)与安全检查面闸(:594-603)**不动**(后者本就「任意新诊断 ⇒ rc=1」)。 - tests/selfhost/test_diag_gate.py:新套件 17 例(正控 9 / 负控 6 [三面] / 零产物 3)+ run.sh 挂点。 判据(本实例实跑;corec 93b6b8f5… / corearch 22e96d03…): - 五 CI job 全 rc=0(冻结源后重跑;新套件在 job 内跑出 17/17)· 全枚举 60/60 · selftest-types 415/415 - ELF canary 95084e7b…d475(28822B)IDENTICAL · .ccr 四条 96015/96158/142793/142936(差恒 143B) - --dump-objects 3521 行 · .cir DOT 165915B b1bdf480… · 自举链 corec2==corec3 IDENTICAL (5154425478…)+ N06=0 + 冒烟 42 + --help rc=1 · test_backend_bootstrap rc=0 - 72 档 rc 零差异(34×0/38×1);日志差异 2 档 = 纯行号偏移 +53(编辑编译器自身源码所致; 先例 = P6 T2 的 +146);探针 1 档 = 进度行(旧硬名单码档 n05_recursive) - report-only 对表(T1 §5):无表外命中——would_block 7 条 build 面行全部放行(逐档 rc=0 + 产物)、 34 条 check 面行逐条不变、6 档红套件全绿 - 突变 4/4 咬合:M1 删 B04 条 ⇒ ptr_arith 复红 · M2 scope 失效 ⇒ N01 在 build 面静默放行被抓 · M3 去零产物删除点 ⇒ .ccr 残留 · M4 尾 return 1 ⇒ 负控转绿 文档:docs/error-codes.md(fail-closed 语义头注)· TODO #59(本批)+ #60(**预存高危**:安全面诊断 随 .cir 暖缓存静默消失——冷 rc=1 / 暖 rc=0 出 ELF;P6 期二进制同病;含 runner 暖态腿派生建议, 本批不实施)· FC 计划勘误(T2 段落地 + 差异 + 登记)。 * feat: FC 批 T3——隐藏通道 --diag-gate-report(report-only 二次核对载体;默认关、两态零差异) 形态:main.cr 注册 cli_flag_bool("diag-gate-report", …)(与 --verify-* 家族同址同式)+ 闸门内 只读报告行 `[diag-gate] face=build blocked=N total=M codes=<用户面码名,…>`(经 error_cat_prefix/ pad_diag_num 渲染);**只读**——不改 rc、不改产物。 判据(本实例实跑;corec 308f06a3…): - **默认关两态零差异**:72 档 parity `diff -rq` 零输出 · 探针 29 档零差异(与 FC-T2 基线逐字节同) - **开态**:rc 与产物 sha 逐字节同(ptr_arith canary 两态同 95084e7b…d475;arena_test/ptr_ref_first 同),日志仅多一行标记 - 重放 101 档(72 语料 + 29 探针)对表 T1:**无表外命中**——语料 25 档 blocked 与 T1 would_block.tsv 的 34 行对齐(34 − 7 行 build 面豁免 − 2 行 P21[解析阶段闸,不属本报告面]); 探针 11 档(TA01/TS03)= T1 表未覆盖面(口径差,登记) - 五 CI job 5/5 rc=0 · 枚举 60/60 · selftest 415/415 · canary IDENTICAL · .ccr 四条 96015/96158/142793/142936 · dump 3521 · cir 165915B · 自举链 24802386a1… IDENTICAL · backend_bootstrap rc=0 口径注(写入计划勘误):报告面只覆盖**前端类型面闸**;解析阶段闸(:134/:135)与安全检查面闸 (:594-603)的诊断不出现在报告行(P21/TU03 等)。 环境干扰登记(非本批所为):测量期间 build/ 被外部删两次、examples/*.{ccr,cir,o} 等 tracked 文件 被删(jj status 见 D);重建后全部重跑取有效值;那批 D 未提交、未还原(铁律 #3)。 * docs: FC 批 T6 收官——统一台账 + 批终态陈述 + TODO #62(T1–T6 全阶段收官;T4/T5 折入 T2) 台账(7 小节,逐条可溯源):实施 3 件(T1 扫面/T2 闸门/T3 隐藏通道)· **T4/T5 折入 T2 声明 (含理由)** · 收紧 1 类(正向名单 11 码 → 默认阻断 + 豁免表)· 放宽 0 · 豁免表 9 条(逐条带 位点证据与退出条件)· 登记 4 件(#59/#60/#61 + 环境干扰)· 偏差 6 条(72 档 +53 行号偏移 · 探针 1 档进度行 · 进度行保真修正 · corearch sha 口径 · B6 暖态如实保留 · T3 期环境干扰)。 批终态三栏:已实施面 / 已裁定面(裁-FC-1..6 逐条)/ 剩余登记面(#60 独立批候选 + 12 个 tracked D 待维护者 + 豁免退出条件 + T3 报告面口径注)。 判据终值(T6 实测,二进制 corec 308f06a3…):五 CI 5/5 rc=0 · 枚举 60/60 · selftest-types 415/415 · ELF canary 95084e7b…d475(28822B)IDENTICAL · .ccr 四条 96015/96158/142793/142936 (差恒 143B)· --dump-objects 3521 · .cir DOT 165915B · 自举链 24802386a1… IDENTICAL + N06=0 + 冒烟 42 · test_backend_bootstrap rc=0 · 72 档 rc 零差异(34×0/38×1)· 探针 29 零差异(17×0/12×1)。 空间:本批 /tmp 证据共 11MB(未清理);/tmp 95% 占用非本批所致;无 ENOSPC 事件。 * fix: TODO #60 T2 暖缓存「共享面副作用见证」——分配类型行的函数不再落条目(暖态 TU03 静默消除) 缺陷(T1 实锤,报告 /tmp/fct4/task1-report.md):缓存命中跳过 `ir_gen_func` ⇒ ir_gen 期 `alloc_type` 出的类型行(`TYP_PTR extra=1` 等)不重建 ⇒ `provenance_verify.cr:66` 的 `get_type_kind(ti)==TYP_PTR && get_type_extra(ti)!=0` 判定落空 ⇒ 冷态 `error[TU03]`(rc=1) 在暖态消失(rc=0 + 照常出 ELF)——同一源、同一二进制,`clean-cache` 与否是唯一变量。 修法(裁-W1 = (b) 先行,**见证式一般化**,零格式变更/零发射面变更):`main.cr` miss 分支 `tc0 := g_type_count` 见证「本函数 ir_gen 期是否分配过共享类型行」,分配过 ⇒ **不写条目** ⇒ 下跑必 miss ⇒ 重放全部生成期副作用(「宁可 miss 不可静默」)。**非 TU03 专用**:判据 = 「生成期对快照**不载**的共享面有副作用」;扩展点 + 未判面(`g_ir_locals`/字符串池/外链重定位/ region)登记在注释内。完备性旁证(本批实核):`ir_gen` 对 `g_ast` **零写点**(`ast_patch_node` 唯一调用者 = 自身 = 死码)、`g_types` 唯 `alloc_type` 追加写(无内容改写)⇒ 计数式见证对该面完备。 代价(实测):语料 101 档 989→986 条目(**0.3%**:ptr_arith/ptr_ref_first/n19_ref_named 的 `::main`)· 编译器自源 110→109(0.9%)。连带面:**复合 mint 行**(唯一来源 = ir_gen 分配行) 在见证下**结构上不落盘**(101 档全扫:仅 ptr_ref_first 有复合行、全缺)⇒ `test_ccr_types.py` ㊲ 重钉(可缓存夹具 PLAIN_FIXTURE + 断言放宽为「带码 mint 行」全集,死亡证据入 docstring)+ 新增 ㊳(能力变更钉:分配行函数无条目 ∧ 抑制选择性)+ ㉟ 收口(暖态丢项钉死为 0)。 用例:`tests/selfhost/test_warm_cache_gate.py` **6 例**(tu03 load/store 冷/暖同 · 机制钉 「副作用函数无条目」· 正控真命中 · TK01 旁面 · `ccr` 面同判;**定路径 = 本设计的要点**——套件 用例用唯一 temp 路径 ⇒ 缓存键唯一 ⇒ 结构性无暖态,T1 方法学发现);`src/ci/run.sh` selfhost-tests 挂钩。 判据(本 T2 实跑):重建 `corec 129d55a6…`/`corearch 22e96d03…`(确定性:与重建前逐字节同)· canary `95084e7b…d475`(28822B)**IDENTICAL** · `.ccr` 四条**冷态** 96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…` · `--dump-objects` 3521 行 · `.cir` DOT 165915B `b1bdf480…` · 72 档 34×0/38×1 零差异(vs T1)· 探针 29 档 17×0/12×1 零差异 · **枚举 61/61(非零 0)** · `selftest-types` 415/415 · `check src/compiler` rc=0 · 五 CI job **5/5 rc=0** · 自举链 `corec2 == corec3` `006e58e1…` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1 · 突变 M1(去见证 ⇒ 暖态 rc=0 + 28822B 产物,静默回归)/ M2(不变量过宽 ⇒ 0 条目 vs 16)。 TODO.md #60 条目同步:根因转实证 + T2 落地 + (c)/(d) 未决判据。 * feat: TODO #60 T3 判据网暖态腿 + 入仓「定路径」探针语料(tests/probes/warm/ 7 档 + warm_leg.sh) 背景:本缺陷(暖态 TU03 静默)长期不可见 = 判据面对暖态**结构性零覆盖**——两条 runner 逐档 `clean-cache`,且既有 72/29 档语料不含 `as *T` 形态(T1 实核)⇒ 只把「同路径二跑」加进既有 语料会**恒绿而空洞**。故 T3 = 入仓「定路径」语料 + 独立腿。 (实核补充:`check` 面写 **0 条**缓存条目 ⇒ 暖态腿必须走 `ccr` 面;两 runner 的 `check` 主腿 格式与产物**保持原样**,暖态结果落 `/warm/`、清单单一真源 `/corpus.tsv`。) **入仓语料 `tests/probes/warm/`(7 档,README 载逐档判据;定路径 = 设计要点)**: `tu03_load`/`tu03_store`(缺陷面:冷/暖都须 rc=1+TU03;暖 rc=0 = 缺陷回归)· `tk01_oob_load`/`tk01_oob_store`/`tk01_width`(旁面:冷/暖都须 rc=1+TK01)· `plain`(正控:条目在 + 二跑真命中,防腿恒绿)· `alloc_witness`(见证面:生成期分配行的函数无条目)。 **腿**:`tools/baseline/warm_leg.sh`(两层:广度 = 父 runner 清单 / 牙齿 = 语料 + 期望值断言; 命中口径 = **本档前缀**条目 size/mtime 未变)· `tools/baseline/warm_run.sh`(牙齿层入口)· 两 runner 挂钩(`WARM_LEG=0` 关)。套件 `tests/selfhost/test_warm_cache_gate.py` 重写为 「语料**就地**编译」(6 例 → **10 例**;含 `build` 面同判 + 见证选择性 + 正控真命中)。 **实测**:牙齿层 7 档 · **暖态生效(≥1 真命中)7** · 0.9s · FAIL=0;广度层 parity 72 档 **生效 40**(+125s)· probes 29 档 **生效 15**(+3.5s)。**CI 决定** = 只挂最小面(套件 10 例 / 0.5s,已在 `selfhost-tests`);全量扫描留手工判据。对 pre-fix 二进制:广度层**预期绿** (实测 M1 二进制 72/29 档 FAIL=0)、牙齿层**必红**(FAIL=2,腿有牙的判据)。 **突变 4 条**:M1 去见证 ⇒ 牙齿腿红 · M2 不变量过宽 ⇒ 暖态生效 **0**(空洞警报)· M3 唯一 temp 路径 ⇒ 缺陷在场仍 FAIL=0(**定路径载荷性**)· M4 断言致盲 ⇒ 缺陷不可见(断言载荷性)。 **判据**:canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条冷态 96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…` · dump 3521 · cir 165915B `b1bdf480…` · selftest 415/415 · check-self rc=0 · 72 档 logs vs T1 **零差异** · 探针 vs T1 **零差异**(排除 T3 新增产物)· 牙齿腿 FAIL=0 · 枚举 **61/61** · 五 CI job **5/5 rc=0** · 链 `corec2==corec3` `006e58e1…` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1。 TODO.md #60 同步:T3 落地 + **(c)/(d) 裁决**(性能面、非正确性 ⇒ 本批不实施;挂两个可证伪 触发条件,触发后**先试「加一行见证」的廉价路径**)。 * docs: #60 批 T4 收官——统一台账 + 批终态 + 文档指针(计划/REBUILD/spec/TODO) 台账(计划 §8):根因链(F1–F9 实读 + **E1 实锤**:冷 13 行/暖 11 行,恰缺 `TYP_PTR extra 0/1` ⇒ `provenance_verify.cr:66` 判定落空;形态判决 = 本例为**尾部缺行越域**,一般形态可错位,同由 见证规则闭合)· 修法 (b) 一般化论证(判据 = 生成期对快照不载共享面有副作用;旁证:`g_types` 只增不改、`ir_gen` 对 `g_ast` 零写点)+ 代价实测 **0.3%/0.9%** + 行号空间守恒归纳 · **(c)/(d) 判为性能面非正确性**(两个可证伪触发条件,触发后先试「加一行见证」)· **判据网盲区三因**(语料无 `as *T` / runner 逐档 clean-cache / 套件唯一 temp 路径 ⇒ 缓存键唯一)· **`check` 面 0 条目**(暖态腿必须走 `ccr` 面)· **M3/M4 承重性实证**(定路径/断言)· **㊲ 重钉的死亡证据**(复合 mint 行唯一来源 = ir_gen 分配行 ⇒ 见证下结构不落盘)· **三件自纠**(腿快照取位 / `$RANDOM` 假红 / stdin 挂起)· **遗留未判面 4 条** (`g_ir_locals` / 字符串池 / 外链重定位 / region)。 终态判据(T4 实跑,逐项同=基线):canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条 冷态 96015 `680a6f98…`/96158 `76f36e6a…`/142793 `41e9d845…`/142936 `704316c8…` · dump 3521 · cir 165915B `b1bdf480…` · 72 档/探针 vs T1 零差异 · 枚举 **61/61** · selftest 415/415 · check-self rc=0 · 五 CI job **5/5 rc=0** · 链 `corec2==corec3` `006e58e1…` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1(**异**:链 sha 随本批编译器源码改动而变,自洽性不变)· 二进制 `corec 129d55a6…`/`corearch 22e96d03…`(**异**:本批修复引入,构造性 emit 零泄漏)。 文档指针:`REBUILD.md` 补「腿本体不进 CI(维护者裁)+ **何时该跑**四条」;`.cir` 格式 spec 顶部 追加 #60 T2/T3 条目(格式零变化 / 保存侧见证规则 / §3.3 暖态缺行支收窄 / 暖态腿实核); TODO #63 = 批收官条目(提交链 + 台账 KPI + 触发条件);#60 条目标「已全阶段收官」。 --- CLAUDE.md | 25 +- README.md | 4 +- TODO.md | 657 ++- bootstrap/corec/backend/x86_64_stack_asm.py | 22 +- bootstrap/corec/frontend/lexer.py | 33 +- build_selfhost_native.py | 460 ++- docs/compcert-reference.md | 2 +- docs/crasm.md | 4 + docs/error-codes.md | 35 +- docs/ir-schema/coreir-schema.md | 38 +- docs/numeric-migration-inventory.md | 18 +- docs/project-book.md | 2 +- docs/pseudocode/compiler/ast.md | 84 +- docs/pseudocode/compiler/parser-2.md | 47 +- .../plans/2026-09-05-hit-minimal-core-m1.md | 358 ++ .../plans/2026-09-05-lattice-ir-v6.md | 361 ++ .../2026-09-06-core-maintainer-docs-reorg.md | 437 ++ .../plans/2026-09-06-int-multiword-m1.md | 169 + docs/superpowers/plans/2026-09-07-hit-m2.md | 437 ++ .../plans/2026-09-07-regalloc-backend.md | 215 + .../plans/2026-09-09-lattice-ir-v7.md | 184 + .../2026-09-10-corearch-kernel-completion.md | 281 ++ .../2026-09-10-corearch-kernel-extraction.md | 124 + ...026-09-10-language-surface-narrowing-r1.md | 663 +++ .../plans/2026-09-10-r2-p0-type-engine.md | 942 +++++ .../plans/2026-09-10-r2-p1-shadow-parity.md | 366 ++ .../plans/2026-09-10-r2-p2-replace.md | 209 + .../plans/2026-09-10-x86-instance-wave1.md | 135 + .../plans/2026-09-11-effect-purity-fix.md | 100 + .../plans/2026-09-11-r2-p2b-iface-ops.md | 446 ++ .../plans/2026-09-11-r2-p3-capabilities.md | 611 +++ .../plans/2026-09-12-r2-p4-carrier.md | 404 ++ .../plans/2026-09-13-r2-p5-cleanup.md | 495 +++ .../plans/2026-09-14-r2-p6-tail.md | 465 +++ .../plans/2026-09-15-capacity-batch.md | 415 ++ .../2026-09-15-fail-closed-diagnostics.md | 471 +++ .../2026-09-15-warm-cache-diagnostics.md | 280 ++ .../specs/2026-08-08-crasm-design.md | 2 + .../2026-08-27-lattice-form-ir-design.md | 2 +- .../2026-09-05-hardware-interface-table.md | 132 + .../specs/2026-09-05-lattice-ir-v6-format.md | 200 + ...09-06-core-maintainer-docs-reorg-design.md | 149 + ...2026-09-06-int-multiword-backend-design.md | 99 + .../2026-09-06-int-unbounded-semantics.md | 94 + .../specs/2026-09-07-hit-m2-design.md | 128 + .../2026-09-07-regalloc-backend-design.md | 56 + .../2026-09-09-corearch-rewrite-design.md | 148 + ...-09-09-lattice-encoding-boundary-design.md | 225 ++ ...2026-09-09-lattice-ir-v7-carrier-design.md | 189 + .../specs/2026-09-09-lattice-ir-v7-format.md | 208 + ...09-10-corearch-kernel-completion-design.md | 248 ++ ...09-10-language-surface-narrowing-design.md | 97 + ...09-10-type-interface-unification-design.md | 332 ++ .../specs/2026-09-10-type-shadow-findings.md | 437 ++ .../specs/2026-09-10-x86-instance-design.md | 191 + ...9-11-explain-predict-incremental-design.md | 570 +++ .../2026-09-11-merge-semantics-design.md | 504 +++ ...1-performance-without-commitment-design.md | 297 ++ .../2026-09-11-policy-authorization-design.md | 356 ++ ...26-09-11-policy-information-flow-design.md | 420 ++ .../2026-09-11-policy-injection-design.md | 455 +++ ...9-11-policy-resource-confinement-design.md | 318 ++ ...9-11-policy-state-version-toctou-design.md | 256 ++ ...9-11-semantic-safety-obligations-design.md | 595 +++ .../2026-09-11-semantic-safety-roadmap.md | 162 + grammar/core.ebnf | 5 +- grammar/corespec.ebnf | 14 +- grammar/tokens.ebnf | 15 +- spec/compiler/checker.corespec | 7 + spec/compiler/parser.corespec | 7 + spec/stdlib/collections.corespec | 7 + spec/stdlib/io.corespec | 7 + src/arch/hit/hit.cr | 1166 ++++++ src/arch/hit/lower_to_core.cr | 657 +++ src/arch/linux/ld/_import.cr | 15 - src/arch/x86_64/core-x86.toml | 213 + src/arch/x86_64/frame.cr | 289 ++ src/arch/{linux/ld => x86_64}/instr.cr | 758 +++- src/arch/x86_64/regalloc.cr | 881 ++++ src/arch/{linux/ld => x86_64}/sizes.cr | 13 +- src/arch/x86_64/tag2l.cr | 547 +++ src/ci/run.sh | 46 + src/compiler/_import.cr | 21 + src/compiler/ast.cr | 112 +- src/compiler/ccr_io.cr | 1973 +++++++-- src/compiler/ccr_types.cr | 398 ++ src/compiler/checker.cr | 2264 +++++++++-- src/compiler/cir_cache.cr | 233 +- src/compiler/corearch.cr | 455 ++- src/compiler/dataflow.cr | 166 +- src/compiler/diag.cr | 48 + src/compiler/dump.cr | 92 + src/compiler/dyn_arr.cr | 459 ++- src/compiler/globals.cr | 304 ++ src/compiler/iface_axis.cr | 210 + src/compiler/iface_registry.cr | 451 +++ src/compiler/interp.cr | 565 ++- src/compiler/ir_gen.cr | 967 ++++- src/compiler/lexer.cr | 47 +- src/compiler/main.cr | 164 +- src/compiler/module.cr | 47 + src/compiler/monomorph.cr | 469 ++- src/compiler/opt.cr | 277 +- src/compiler/parser.cr | 422 +- src/compiler/provenance_verify.cr | 5 +- src/compiler/purity_selftest.cr | 299 ++ src/compiler/regalloc-consistency.cr | 69 + src/compiler/ty_shadow.cr | 1434 +++++++ src/compiler/type_engine.cr | 823 ++++ src/compiler/type_selftest.cr | 3595 +++++++++++++++++ src/compiler/type_terms.cr | 422 ++ src/{arch/linux/ld => format/elf}/elf.cr | 380 +- src/{arch/linux/ld => format/elf}/ld.cr | 2 +- src/{arch/linux/ld => format/elf}/resolve.cr | 0 src/lattice/ent_kernel.cr | 1068 +++++ src/lsp/analysis.cr | 7 + src/os/linux/callseq.cr | 220 + src/os/linux/entry.cr | 136 + src/os/linux/syscall.cr | 70 + src/stdlib/toml.cr | 106 + .../ld => targets/x86_64-linux}/Core.toml | 0 src/targets/x86_64-linux/_import.cr | 80 + .../linux/ld => targets/x86_64-linux}/main.cr | 21 +- tests/bootstrap/test_pipeline.py | 13 +- tests/hit/smoke_add.cr | 7 + tests/hit/smoke_mem.cr | 15 + tests/probes/README.md | 79 + tests/probes/n02_diff_named.cr | 8 + tests/probes/n03_apply_twice.cr | 6 + tests/probes/n04_apply_diff_args.cr | 6 + tests/probes/n05_recursive.cr | 7 + tests/probes/n09_t_vs_int.cr | 6 + tests/probes/n10_named_vs_int.cr | 7 + tests/probes/n12_alias.cr | 7 + tests/probes/n13_arr_diff_named.cr | 8 + tests/probes/n14_optional_diff_named.cr | 8 + tests/probes/n15_apply_named_arg.cr | 6 + tests/probes/n16_nested_apply.cr | 3 + tests/probes/n17_ptr_named.cr | 4 + tests/probes/n18_tuple_named.cr | 8 + tests/probes/n19_ref_named.cr | 11 + tests/probes/n20_apply_named_vs_int.cr | 4 + tests/probes/n21_t_assign_int.cr | 5 + tests/probes/n22_named_struct_field.cr | 8 + tests/probes/n23_enum_named.cr | 8 + tests/probes/p_annots.cr | 9 + tests/probes/p_annots2.cr | 7 + tests/probes/p_dex.cr | 7 + tests/probes/p_dyn.cr | 6 + tests/probes/p_enum.cr | 5 + tests/probes/p_ffi.cr | 5 + tests/probes/p_ffi2.cr | 5 + tests/probes/p_go.cr | 6 + tests/probes/p_i2f.cr | 6 + tests/probes/p_opt.cr | 6 + tests/probes/p_spawn.cr | 5 + tests/probes/warm/README.md | 46 + tests/probes/warm/alloc_witness.cr | 7 + tests/probes/warm/plain.cr | 4 + tests/probes/warm/tk01_oob_load.cr | 5 + tests/probes/warm/tk01_oob_store.cr | 6 + tests/probes/warm/tk01_width.cr | 6 + tests/probes/warm/tu03_load.cr | 4 + tests/probes/warm/tu03_store.cr | 5 + tests/selfhost/test_agg_checks.py | 348 ++ tests/selfhost/test_agg_slots.py | 267 ++ tests/selfhost/test_backend_bootstrap.py | 16 +- tests/selfhost/test_cache_identity.py | 288 ++ tests/selfhost/test_ccr_types.py | 2296 +++++++++++ tests/selfhost/test_ccr_v7.py | 2204 ++++++++++ tests/selfhost/test_cir_warm_path.py | 348 ++ tests/selfhost/test_compile.py | 59 +- tests/selfhost/test_diag_gate.py | 156 + tests/selfhost/test_ent_kernel_neutrality.py | 134 + tests/selfhost/test_enum_limit.py | 212 + tests/selfhost/test_generic_constr.py | 298 ++ tests/selfhost/test_global_init.py | 216 + tests/selfhost/test_hit_table.py | 1474 +++++++ tests/selfhost/test_iface_ops.py | 347 ++ tests/selfhost/test_iface_satisfies.py | 328 ++ tests/selfhost/test_impl_iface.py | 399 ++ tests/selfhost/test_interp_parity.py | 448 ++ tests/selfhost/test_let_check.py | 260 ++ tests/selfhost/test_lexer_parity.py | 43 + tests/selfhost/test_live_ranges.py | 728 ++++ tests/selfhost/test_lsp.py | 54 + tests/selfhost/test_match_exhaust.py | 228 ++ tests/selfhost/test_mw_task1.py | 301 ++ tests/selfhost/test_mw_task2.py | 310 ++ tests/selfhost/test_mw_task3.py | 164 + tests/selfhost/test_mw_task4.py | 294 ++ tests/selfhost/test_mw_task5.py | 205 + tests/selfhost/test_mw_task6.py | 205 + tests/selfhost/test_named_dedup.py | 58 + tests/selfhost/test_named_face.py | 217 + tests/selfhost/test_nested_fn.py | 268 ++ tests/selfhost/test_optional.py | 428 ++ tests/selfhost/test_params_limit.py | 141 + tests/selfhost/test_purity.py | 50 + tests/selfhost/test_region_cfg.py | 104 +- tests/selfhost/test_tc02_branch.py | 161 + tests/selfhost/test_tf01_fallthrough.py | 289 ++ tests/selfhost/test_tuple_slots.py | 280 ++ tests/selfhost/test_type_engine.py | 46 + tests/selfhost/test_warm_cache_gate.py | 133 + tests/selfhost/test_xcut_iface.py | 250 ++ tests/suite/params_many.cr | 69 + tests/suite/syscall4_test.cr | 33 + tools/baseline/REBUILD.md | 107 + tools/baseline/parity_run.sh | 82 + tools/baseline/probes_run.sh | 58 + tools/baseline/rebuild.sh | 81 + tools/baseline/warm_leg.sh | 111 + tools/baseline/warm_run.sh | 11 + tools/module_to_ccr.py | 10 + tools/pseudocode_extract.py | 4 +- 216 files changed, 54189 insertions(+), 2213 deletions(-) create mode 100644 docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md create mode 100644 docs/superpowers/plans/2026-09-05-lattice-ir-v6.md create mode 100644 docs/superpowers/plans/2026-09-06-core-maintainer-docs-reorg.md create mode 100644 docs/superpowers/plans/2026-09-06-int-multiword-m1.md create mode 100644 docs/superpowers/plans/2026-09-07-hit-m2.md create mode 100644 docs/superpowers/plans/2026-09-07-regalloc-backend.md create mode 100644 docs/superpowers/plans/2026-09-09-lattice-ir-v7.md create mode 100644 docs/superpowers/plans/2026-09-10-corearch-kernel-completion.md create mode 100644 docs/superpowers/plans/2026-09-10-corearch-kernel-extraction.md create mode 100644 docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md create mode 100644 docs/superpowers/plans/2026-09-10-r2-p0-type-engine.md create mode 100644 docs/superpowers/plans/2026-09-10-r2-p1-shadow-parity.md create mode 100644 docs/superpowers/plans/2026-09-10-r2-p2-replace.md create mode 100644 docs/superpowers/plans/2026-09-10-x86-instance-wave1.md create mode 100644 docs/superpowers/plans/2026-09-11-effect-purity-fix.md create mode 100644 docs/superpowers/plans/2026-09-11-r2-p2b-iface-ops.md create mode 100644 docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md create mode 100644 docs/superpowers/plans/2026-09-12-r2-p4-carrier.md create mode 100644 docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md create mode 100644 docs/superpowers/plans/2026-09-14-r2-p6-tail.md create mode 100644 docs/superpowers/plans/2026-09-15-capacity-batch.md create mode 100644 docs/superpowers/plans/2026-09-15-fail-closed-diagnostics.md create mode 100644 docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md create mode 100644 docs/superpowers/specs/2026-09-05-hardware-interface-table.md create mode 100644 docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md create mode 100644 docs/superpowers/specs/2026-09-06-core-maintainer-docs-reorg-design.md create mode 100644 docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md create mode 100644 docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md create mode 100644 docs/superpowers/specs/2026-09-07-hit-m2-design.md create mode 100644 docs/superpowers/specs/2026-09-07-regalloc-backend-design.md create mode 100644 docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md create mode 100644 docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md create mode 100644 docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md create mode 100644 docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md create mode 100644 docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md create mode 100644 docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md create mode 100644 docs/superpowers/specs/2026-09-10-type-interface-unification-design.md create mode 100644 docs/superpowers/specs/2026-09-10-type-shadow-findings.md create mode 100644 docs/superpowers/specs/2026-09-10-x86-instance-design.md create mode 100644 docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md create mode 100644 docs/superpowers/specs/2026-09-11-merge-semantics-design.md create mode 100644 docs/superpowers/specs/2026-09-11-performance-without-commitment-design.md create mode 100644 docs/superpowers/specs/2026-09-11-policy-authorization-design.md create mode 100644 docs/superpowers/specs/2026-09-11-policy-information-flow-design.md create mode 100644 docs/superpowers/specs/2026-09-11-policy-injection-design.md create mode 100644 docs/superpowers/specs/2026-09-11-policy-resource-confinement-design.md create mode 100644 docs/superpowers/specs/2026-09-11-policy-state-version-toctou-design.md create mode 100644 docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md create mode 100644 docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md create mode 100644 src/arch/hit/hit.cr create mode 100644 src/arch/hit/lower_to_core.cr delete mode 100644 src/arch/linux/ld/_import.cr create mode 100644 src/arch/x86_64/core-x86.toml create mode 100644 src/arch/x86_64/frame.cr rename src/arch/{linux/ld => x86_64}/instr.cr (67%) create mode 100644 src/arch/x86_64/regalloc.cr rename src/arch/{linux/ld => x86_64}/sizes.cr (78%) create mode 100644 src/arch/x86_64/tag2l.cr create mode 100644 src/compiler/ccr_types.cr create mode 100644 src/compiler/iface_axis.cr create mode 100644 src/compiler/iface_registry.cr create mode 100644 src/compiler/purity_selftest.cr create mode 100644 src/compiler/regalloc-consistency.cr create mode 100644 src/compiler/ty_shadow.cr create mode 100644 src/compiler/type_engine.cr create mode 100644 src/compiler/type_selftest.cr create mode 100644 src/compiler/type_terms.cr rename src/{arch/linux/ld => format/elf}/elf.cr (86%) rename src/{arch/linux/ld => format/elf}/ld.cr (99%) rename src/{arch/linux/ld => format/elf}/resolve.cr (100%) create mode 100644 src/lattice/ent_kernel.cr create mode 100644 src/os/linux/callseq.cr create mode 100644 src/os/linux/entry.cr create mode 100644 src/os/linux/syscall.cr rename src/{arch/linux/ld => targets/x86_64-linux}/Core.toml (100%) create mode 100644 src/targets/x86_64-linux/_import.cr rename src/{arch/linux/ld => targets/x86_64-linux}/main.cr (79%) create mode 100644 tests/hit/smoke_add.cr create mode 100644 tests/hit/smoke_mem.cr create mode 100644 tests/probes/README.md create mode 100644 tests/probes/n02_diff_named.cr create mode 100644 tests/probes/n03_apply_twice.cr create mode 100644 tests/probes/n04_apply_diff_args.cr create mode 100644 tests/probes/n05_recursive.cr create mode 100644 tests/probes/n09_t_vs_int.cr create mode 100644 tests/probes/n10_named_vs_int.cr create mode 100644 tests/probes/n12_alias.cr create mode 100644 tests/probes/n13_arr_diff_named.cr create mode 100644 tests/probes/n14_optional_diff_named.cr create mode 100644 tests/probes/n15_apply_named_arg.cr create mode 100644 tests/probes/n16_nested_apply.cr create mode 100644 tests/probes/n17_ptr_named.cr create mode 100644 tests/probes/n18_tuple_named.cr create mode 100644 tests/probes/n19_ref_named.cr create mode 100644 tests/probes/n20_apply_named_vs_int.cr create mode 100644 tests/probes/n21_t_assign_int.cr create mode 100644 tests/probes/n22_named_struct_field.cr create mode 100644 tests/probes/n23_enum_named.cr create mode 100644 tests/probes/p_annots.cr create mode 100644 tests/probes/p_annots2.cr create mode 100644 tests/probes/p_dex.cr create mode 100644 tests/probes/p_dyn.cr create mode 100644 tests/probes/p_enum.cr create mode 100644 tests/probes/p_ffi.cr create mode 100644 tests/probes/p_ffi2.cr create mode 100644 tests/probes/p_go.cr create mode 100644 tests/probes/p_i2f.cr create mode 100644 tests/probes/p_opt.cr create mode 100644 tests/probes/p_spawn.cr create mode 100644 tests/probes/warm/README.md create mode 100644 tests/probes/warm/alloc_witness.cr create mode 100644 tests/probes/warm/plain.cr create mode 100644 tests/probes/warm/tk01_oob_load.cr create mode 100644 tests/probes/warm/tk01_oob_store.cr create mode 100644 tests/probes/warm/tk01_width.cr create mode 100644 tests/probes/warm/tu03_load.cr create mode 100644 tests/probes/warm/tu03_store.cr create mode 100644 tests/selfhost/test_agg_checks.py create mode 100644 tests/selfhost/test_agg_slots.py create mode 100644 tests/selfhost/test_cache_identity.py create mode 100644 tests/selfhost/test_ccr_types.py create mode 100644 tests/selfhost/test_ccr_v7.py create mode 100644 tests/selfhost/test_cir_warm_path.py create mode 100644 tests/selfhost/test_diag_gate.py create mode 100644 tests/selfhost/test_ent_kernel_neutrality.py create mode 100644 tests/selfhost/test_enum_limit.py create mode 100644 tests/selfhost/test_generic_constr.py create mode 100644 tests/selfhost/test_global_init.py create mode 100644 tests/selfhost/test_hit_table.py create mode 100644 tests/selfhost/test_iface_ops.py create mode 100644 tests/selfhost/test_iface_satisfies.py create mode 100644 tests/selfhost/test_impl_iface.py create mode 100644 tests/selfhost/test_interp_parity.py create mode 100644 tests/selfhost/test_let_check.py create mode 100644 tests/selfhost/test_lexer_parity.py create mode 100644 tests/selfhost/test_live_ranges.py create mode 100644 tests/selfhost/test_match_exhaust.py create mode 100644 tests/selfhost/test_mw_task1.py create mode 100644 tests/selfhost/test_mw_task2.py create mode 100644 tests/selfhost/test_mw_task3.py create mode 100644 tests/selfhost/test_mw_task4.py create mode 100644 tests/selfhost/test_mw_task5.py create mode 100644 tests/selfhost/test_mw_task6.py create mode 100644 tests/selfhost/test_named_dedup.py create mode 100644 tests/selfhost/test_named_face.py create mode 100644 tests/selfhost/test_nested_fn.py create mode 100644 tests/selfhost/test_optional.py create mode 100644 tests/selfhost/test_params_limit.py create mode 100644 tests/selfhost/test_purity.py create mode 100644 tests/selfhost/test_tc02_branch.py create mode 100644 tests/selfhost/test_tf01_fallthrough.py create mode 100644 tests/selfhost/test_tuple_slots.py create mode 100644 tests/selfhost/test_type_engine.py create mode 100644 tests/selfhost/test_warm_cache_gate.py create mode 100644 tests/selfhost/test_xcut_iface.py create mode 100644 tests/suite/params_many.cr create mode 100644 tests/suite/syscall4_test.cr create mode 100644 tools/baseline/REBUILD.md create mode 100644 tools/baseline/parity_run.sh create mode 100644 tools/baseline/probes_run.sh create mode 100644 tools/baseline/rebuild.sh create mode 100644 tools/baseline/warm_leg.sh create mode 100644 tools/baseline/warm_run.sh diff --git a/CLAUDE.md b/CLAUDE.md index 1f8a60e6..0adc87e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,7 +73,7 @@ python3 build_selfhost_native.py # Produces build/corec + build/corearch # Dataflow graph dump ./build/corec cir FILE.cr -# 格形态 IR dump(现 v5 = 格层线性投影) +# 格形态 IR dump(v8 = 段表 8 段:STR/SYM/NOD/ENT/REG/EDG + TYPE(7)/IFACE(8)) ./build/corec ccr FILE.cr ``` @@ -95,6 +95,8 @@ python3 tests/selfhost/test_borrow.py # Self-hosted borrow checker (7 rules Integration tests in `tests/suite/` are `.cr` source files — run through `./build/corec`. +**行为探针语料**(`tests/probes/`,29 档 `check` 面负例为主,**非编译单元**)与**回归网配方**(`tools/baseline/`:冻结基线重建 + sha 白名单 + 两条 runner)见 `tools/baseline/REBUILD.md`——判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律(R2 P6 Task 1 起;影子通道已下线,不得引用其计数)。 + ## Architecture ### Python Bootstrap Compiler @@ -114,7 +116,7 @@ bootstrap/corec/frontend/type_checker.py → Type inference + checking + borrow bootstrap/corec/frontend/ir_gen.py → AST → Core IR bootstrap/corec/ir/cir.py → Dataflow graph IR definitions -bootstrap/corec/ir/ccr.py → 格形态 IR(现 v5 线性指令定义;C 路线:升级格形态 v6,见 specs/2026-08-27-lattice-form-ir-design.md) +bootstrap/corec/ir/ccr.py → 格形态 IR(注:该文件实际不存在——.ccr 仅由 self-hosted ccr_io.cr 读写;现行 = v8 八段段表,见 specs/2026-09-09-lattice-ir-v7-format.md;v6 版历史设计见 specs/2026-09-05-lattice-ir-v6-format.md) bootstrap/corec/ir/base.py → IRNode base class, IRVar, VarKind bootstrap/corec/ir/symbol_table.py → Scoped symbol table @@ -157,17 +159,16 @@ src/compiler/ └── _import.cr → Shared imports for all compiler modules ``` -### ELF Backend (`src/arch/linux/ld/`) +### Backend(x86 实例化三轴布局:架构 × 格式 × OS;`src/arch/linux/ld/` 已退役) -Direct ELF binary output for x86-64, used by `corearch`: +Direct ELF binary output for x86-64, used by `corearch`. 三轴目录 = 跨轴 import 的 +唯一通道(`module.cr` 回退链),组合根 = `src/targets/x86_64-linux/`(target triple 命名): ``` -src/arch/linux/ld/ -├── elf.cr → ELF header + program header generation, _start emission -├── instr.cr → Instruction encoding: REX, ModRM, SIB, all IR opcode emitters -├── sizes.cr → Instruction byte size helpers (sz_* functions) -├── resolve.cr → Label resolution pass (res_labels) -└── ld.cr → Dynamic linking (PLT/GOT, .so loading) +src/arch/x86_64/ → regalloc.cr(CAG 寄存器分配)· sizes.cr(指令字节尺寸单源)· instr.cr(指令编码 REX/ModRM/SIB + 全 IR opcode 发射 + e2_* 原语)· frame.cr(帧布局/序言尾声——帧公式单入口 pf_frame_size)· tag2l.cr(int 多字 M1 tag/2L 编码族)· core-x86.toml(HIT 表数据) +src/format/elf/ → elf.cr(ELF 头/phdr/段发射 + elf_gen)· resolve.cr(标签解析 res_labels)· ld.cr(动态链接 PLT/GOT/.so 装载) +src/os/linux/ → entry.cr(_start 发射序 emit_start/emit_start_size)· callseq.cr(SysV AMD64 参数/栈/返回/调用序列)· syscall.cr(syscall3/4 内置体——rax 号 + rdi/rsi/rdx(/r10) 参数序)(三件均波 1 落位:Task 2/5/6) +src/targets/x86_64-linux/ → 组合根(target triple):main.cr + _import.cr + Core.toml(`corec build ` 的 project-mode 入口) ``` ### Standard Library (`src/stdlib/`) @@ -263,11 +264,11 @@ Design documents (Chinese): ## Key Conventions -- File extensions: `.cr` (source), `.cir` (dataflow graph IR / 图形态), `.ccr` (格形态 IR,现 v5 = 格层线性投影,v6 演进中), `.corespec` (spec) +- File extensions: `.cr` (source), `.cir` (dataflow graph IR / 图形态), `.ccr`(格形态 IR,**v8 = 段表架构 8 段**:STR/SYM/NOD/ENT/REG/EDG + TYPE(7)/IFACE(8)(R2 P4 载体批;文件名/测试名保留「v7」= 段表架构代号,见 specs/2026-09-09-lattice-ir-v7-format.md)), `.corespec`(已退役 2026-09-06——规约并入 .cr 语法,见 grammar/core.ebnf 迁移事项) - Tests in `tests/bootstrap/` and `tests/selfhost/` define inline Core source strings and compare output - Python bootstrap: `sys.path.insert(0, 'bootstrap')` to import compiler modules - VS Code extension in `vscode-core/` -- Spec files in `spec/` (`.corespec`) for formal verifier +- Spec files in `spec/`(.corespec 已退役——规约语法并入 .cr,图即验证承载于 TagNode/.csr) - Examples in `examples/` ## Known Issues & TODO diff --git a/README.md b/README.md index 3a0dd742..1af556b1 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ | | `@hotpatch` 滚动更新(IR_HOTPATCH_ROUTE + SIGHUP 热加载) | 完成 | | | 惰性求值(IR_LAZY_THUNK/FORCE,调用级) | 部分(thunk/force 为值搬运,无实际延迟) | | | 控制流自动惰性(编译期指令下沉路线) | 设计定案,待实现 | -| **汇编层** | `.crasm` 统一汇编抽象层(虚拟寄存器 + 平台映射表) | 设计完成(docs/crasm.md) | +| **汇编层** | ~~`.crasm` 统一汇编抽象层~~——**2026-09-05 退役**,被硬件接口表(HIT)吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern | 已退役(docs/crasm.md 保留为历史) | | **语义检查** | 名字解析 + 类型检查、结构化错误码 + 源码定位 | 完成 | | **模块系统** | `import`、`fileid`、`@project`、`_import.cr`、依赖裁剪 | 完成 | | **标准库** | `io.cr` / `cli.cr` / `toml.cr` | 完成 | @@ -72,7 +72,7 @@ | 内存 | 完整自举 1 GiB bump heap 峰值(需按函数回收而非扩堆) | TODO 预存 bug 1 | | 并发 | 多 M worker 未连调度器、channel 队列未并发验证 | TODO 预存 bug 2 | | CI | 工作流注册冻结(注册表陈旧,GitHub 侧)——develop 的 required_status_checks 暂移除 | spec §3.3 | -| 语言 | 控制流自动惰性、`.crasm` 汇编层、分布式(跨机器/QUIC) | 设计完成/定案,待实现 | +| 语言 | 控制流自动惰性、分布式(跨机器/QUIC);~~`.crasm` 汇编层~~(2026-09-05 退役,被 HIT 吸收) | 设计完成/定案,待实现 | --- diff --git a/TODO.md b/TODO.md index 892085f8..9cae4c0c 100644 --- a/TODO.md +++ b/TODO.md @@ -112,6 +112,535 @@ - math.cr / collections.cr 均为 stub - 字符串操作、JSON 序列化待补(JSON-RPC 序列化已完成;动态字符串索引边界与字节读写已接入,通用字符串 API 仍待补) +### 4. 自举编译器巨型函数尾代码生成缺陷(elf.cr 回填循环,2026-09-09 v7 Task 1 激活发现——用户批准推迟,注册跟踪) +- **现象**:elf_gen 的 hit_rel 回填循环(×24)在巨型函数尾读**未初始化栈槽**(循环界比较值)——gdb 实证全函数零写点;v6 时代同源 dir 构建潜伏(陈旧栈值恰 0),v7 loader 帧轮廓改变陈旧值(fsize-4 残值)后激活(确定性 codegen → 非 v7 loader 回归——v7 Task 1 评审裁决:loader 与 g_hit_rel 状态零引用,28B 语义字段 verbatim,stage 链 byte-identical + hit_table 24/24 证明 loader 干净) +- **现状**:elf.cr:1450-1479 语义等价规避(回填界读取一次入局部变量 + 无条件重置保持)+ 代码内注记(诚实标注 = 规避非修复) +- **子系统假设**:ELF 后端巨型函数尾的栈槽分配/寄存器分配(循环界变量被分配到一个全程无写点的栈槽——疑似栈槽复用/分配器对巨函数尾的处理) +- **repro**:还原 elf.cr 重构(注记处)→ `python3 build_selfhost_native.py` dir 构建 stage1 → 确定性 segv +- **gdb 证据**:v7 Task 1 报告(.superpowers/sdd/v7-task-1-report.md Concern 1)+ elf.cr 注记 +- **修复方向**:编译器层专项(独立于 v7 链——仓促同修危及 byte-identical 判据);修复后还原规避代码验证 + +### 5. cir cache 跨编译器重建不失效(2026-09-10 内核抽取 Task 3 评审确认——预存,**已修 2026-09-11**) +- **✅ 已修(2026-09-11,提交 `3be4cb48`;工作区报告 `.superpowers/sdd/fix-cache5-report.md`)**: + 根因 = 条目身份**只有目标源**(键 = 源路径::函数名;头部指纹 `func_fingerprint`/`sig_fingerprint` 只覆盖目标 AST)⇒ 同源重建后字符串驻留序(`g_strs` intern 序)漂移不反映到键/指纹上,旧条目被命中、快照内旧序索引(var `irv_name` / instr `s1..s3`)被当活产物恢复(rc=0 静默)。 + **RED(pre-fix 二进制 sha256 `f0f00d7d…`)**:①载荷毒化(alpha 条目内 `var[0]/var[2]` 的 `name_ni` 互换 =「另一编译器写下的索引序」)后重跑——条目原样未被重写(命中)且 dump 错位(`binary 24 = a + b` → `24 = 20 + b`、`dest=22` → `dest=a`,rc=0)⇒ 装载路径对「索引序是否属于本编译器」零判据;②跨二进制 A/B(bootstrap 建的 `build/corec` vs 自举建的 `corec2`)实测 B 直接沿用 A 条目(无重写)。 + 修复 = 头部新增**编译器身份**字段(`CIR_CACHE_VER 16→17`,布局 magic/ver/**identity**/fp/sig/name_len/name…):`cir_compiler_identity()` = **运行中编译器自身 ELF 的内容哈希**(`/proc/self/exe` 全文件单趟 FNV-1 64 乘加:8B 字 + 余尾 + 总长;memoized),save 写入 / load 比对,不等即 miss;身份取不到(无 procfs/读取失败)= 两端点均关缓存(**绝不落「身份 0」条目**——那正是本静默类在退化路径复活;`unshare -rm` + tmpfs 掩掉 /proc 实测 rc=0 且零条目落盘)。 + 选型(推翻本条原「构建期源哈希常量」方向):运行中二进制自哈希捕获**全部**决定编译器语义的输入(`src/compiler` 清单+顺序 + bootstrap Python 工具链 + rt.s + as/ld),构建期源哈希只是其中一部分的代理——漏 bootstrap/工具链即本类复活;且无需构建管道改动/生成文件(tests 直编 `src/compiler`、full-bootstrap 的 corec2/corec3 不因缺生成文件而断)。前提 = 本仓构建逐字节确定(两连建 cmp IDENTICAL,修复前后两侧均实测)⇒ 同源重建身份不变、命中面不退化;若构建转为非确定,症状 = 缓存恒 miss(性能面,由该判据暴露)。与 `CIR_CACHE_VER` 分工:VER = 手工粗粒度(格式/行为代),身份 = 自动细粒度(编译器内容驱动)——行为变更不再需要「记得 bump」。 + 成本(实测,热身缓存,1.32MB 二进制):逐字节形 +11.6ms/次(小件编译 17→29ms)→ 8 字节字粒度 min 基准 +2~3ms/次;只在真正走到缓存的进程付一次(strace:全量热身编译 1 次 `open("/proc/self/exe")`)。 + 回归 = `tests/selfhost/test_cache_identity.py`(5/5:身份语义 = FNV-1(ELF) 且 ver=17 · 同身份仍命中[改「被装载跳过」的函数名字节 canary] · 异身份拒[身份+载荷毒化 → 条目被改写回当前身份] · 差分「条目缺失 ≡ 条目毒化」(dump + 重建条目逐字节同)· 跨二进制 A/B[副本+1 字节模拟重建:canary 被抹 + 身份改写为 B])+ `src/ci/run.sh` selfhost-tests 挂钩;RED 对照 = 同一测试对 pre-fix 二进制 1/5(仅命中控制过)。 + 判据 = 两连建 `build/corec` 逐字节 IDENTICAL(sha256 `699b360a…`)· selftest-types 95/95 · test_compile/test_purity PASS · test_ccr_v7 27/27 · clean-cache 后 ptr_arith ELF `95084e7b…d475` IDENTICAL · suite 21 pass/0 fail · params_limit 5/5 · tuple_slots 14/14 · nested_fn 17/17 · region_cfg 22/22 · impl 7/7 · borrow 7/7 · pointer_safety PASS · interp_parity 23/23。 + **未修(本单外,第二实例照旧)**:`.ccr` 冷/热缓存态分歧(`at_test_struct.cr`:cold `891377232b5b` ≠ warm `8dbb12f740d8`,第三次起稳定)本修复**不影响**(pre/post 三连跑同哈希)——根因 = 缓存命中路径不复现冷路径的字符串驻留/渲染状态(与编译器身份正交)⇒ 比较 `.ccr` 前仍须 `clean-cache`。另:`CORE_SAFE` 环境旗标与 `-O` 级别经核**不触及**缓存面(安全/优化 pass 均在缓存相之后,`main.cr:551/566/583`)。 +- **现象**:`.core/cache/cir` 以「源路径::函数名」为键缓存每函数 CIR 快照;缓存命中时旧条目跨编译器重建存活 → 复用的 CIR 与新版字符串表(g_strs 驻留序)错位 → dump 通道(如 corearch --dump-entries)输出部分变量名缺失/错位——实证:806→381 个 `name=` 的翻转(Task 3 验证中复现,同源重建即触发,与具体代码改动无关) +- **机制**:指纹 = magic/格式版本/完整解析源 AST,无编译器身份分量;源未变则键/指纹不变 → 二进制重建后字符串驻留序漂移不反映到键上 → 陈旧条目静默污染 dump-channel var 名输出 +- **证据**:内核抽取 Task 3 评审 concern ①(.superpowers/sdd/kernel-task-3-report.md)——Task 3 diff 仅 corearch + 注释,base↔head corec cmp 相同;清 .core/cache 后逐字节复现判据成立(计划 Global Constraints 的「清 cache 跑测试」即为规避) +- **修复方向**:cir_cache.cr 缓存键 += 编译器指纹(产物内容哈希或编译器身份分量)——重建后旧条目自动失效;现状兜底 = CIR_CACHE_VER 手工 bump + 测试前清 cache +- **同族第二实例(2026-09-11 R2 P2a Task 4 收官登记):`.ccr` 输出与 cir 缓存态相关——同一二进制二次运行产出不同 `.ccr` 字节**。实证(Task 1 评审首测 + Task 4 亲测复现,`tests/suite/at_test_struct.cr`):`clean-cache` 后首次 `corec ccr` → sha256 `53ebcf12…`;**同二进制紧接二次运行**(缓存已被首次填热)→ `5054e26d…`(三次运行起稳定于该值)。冷缓存下 pre/post 两态行为一致,故**判据比较 `.ccr` 前必须 `clean-cache`**(已并入 R2 P2a 计划 Global Constraints 精神与各任务采样方法);本条与上条同源(缓存键缺编译器/环境身份分量),非独立缺陷。 + +### 6. 双入口能力分歧:ld project-mode 后端缺 HIT/表旗标(2026-09-10 ld 导入集修复评审确认——预存,待修) +- **现象**:`src/arch/linux/ld/main.cr` 的 corearch_main 只注册 **6** 个旗标(elf/shared/static/link/output/opt-level,:31-36;评审修正——原文计数 7 误),而 `src/compiler/corearch.cr` 的 corearch_main 同段注册其全部超集(:260-273):`--table` / `--hit-events-file` / `--dump-events` / `--dump-table` 与全族 `--dump-entries`/`--dump-coexist`/`--dump-regassign`/`--check-regalloc`/`--inject-*`/`--dump-objects` 调试通道。ld/main.cr 全文零处引用 `table`/`instance_decl_init`/`instance_select`/`g_active_instance`/`g_hit_table`——即 ld project-mode 构建出的后端(`corec build src/arch/linux/ld`,test_backend_bootstrap 的 stage1/2/3 即此产物)**HIT lowering 层与表编码器已编译在内但惰性**(评审修正——原文「整体不含 HIT/表机制」过度表述;精确缺面 = CLI/分派接线:旗标注册 + 实例声明引导 + 表装载),`:15`「两入口行为同构」括注对表模式为假。 +- **实证**:`./build/corearch --dump-table` → 正常解析(打印 usage);ld project-mode 产物 `--dump-table` / `--table` → `unknown flag: --dump-table` / `unknown flag: --table`(cli_parse 拒绝,非 usage 兜底)。故 `src/arch/linux/ld/main.cr` 头注「入口二元性…两入口…(现 load 后行为同构)」(:11-15)对表模式(及全部调试通道)**为假**——同构仅限 ELF 发射主路径。 +- **机制**:入口二元性(concat 入口 = src/compiler/corearch.cr wrapper;project-mode 入口 = ld/main.cr)靠人工双份维护 corearch_main——旗标注册与 load 后接线分散两处,无共享注册函数、无一致性守卫;《内核完备》抽取后 src/compiler/corearch.cr 单侧长表模式(M2 系列),ld/main.cr 未同步。 +- **影响**:ld 自举 stage 链(及任何经 ld 工程构建的后端)无法经 --table 走表投影路径——表模式回归只在 concat corearch 面被覆盖;`--hit-events-file` 注入测试通道对 ld 产物同样缺席。当前无测试经此入口断言表模式,故静默。 +- **证据**:本次 ld 导入集修复(N06/N01 静默未定义闭合)评审 trace —— .superpowers/sdd/lattice-move-report.md 与本文 #6 挂账;同类先例 = c138c44c(init 单元缺 import → 静默未定义 + SIGSEGV,N06 类静默失败反复出现于「入口/单元维护面不同步」)。 +- **修复方向**:①旗标注册收敛——两入口共调一个 `register_backend_flags()`(或 ld/main.cr 直接复用 corearch.cr 的注册段),保留双 wrapper 前提下的单份真源;②或显式 documented divergence 留档——ld/main.cr 头注改为「表模式/调试通道仅 concat 入口支持」并加守卫(如 ld 产物遇 --table 报明确「此入口不支持表模式」而非 unknown flag)。①优先(同构主张才是原本设计意图)。**③静默未定义类守卫(2026-09-10 修复评审残留建议)**:project-mode 构建(`corec build src/arch/linux/ld` 等单元)的 `error[` 计数无任何自动断言——N06 类静默失败在 rc=0 + 产物正常 + 全部互测 byte-identical 的情况下完全不可见(pre-fix 状态即如此通过全套);建议加「build log error 计数非零 = 失败」的构建/测试门(或 tools/diagnose.py 类收集纳入套件)。**已部分落地(波 1 Task 1 修复 c927525b)**:`test_backend_bootstrap.py` run_checked 加 `error[` 门(project-mode 事发层封闭)+ 构建脚本 bootstrap 面门;**残留 rc-only 面(评审清点)**:`test_mw_task2.py:172`、`test_compile.py:127`、`test_live_ranges.py:52,73`(文件模式)+ `test_directory_build.py:50,80,100`(project-mode)——同类门可同法接入;执行力边界注记 = test_backend_bootstrap 未被 CI job 调用(门仅手动/计划判据路径生效,`corec check ` 面 rc=1 非静默已核)。 +- **注意**:修①会改 ld project-mode 单元内容 → stage 链产物字节变(expected,非缺陷);须同步跑 test_backend_bootstrap 判据(stage1==stage2==stage3 全程同源,仍成立)。 +- **波 1 注记(2026-09-10 x86 实例化波 1 Task 1 搬迁 + Task 7 收官同步)**:双入口之一已随三轴搬迁移入组合根——`src/arch/linux/ld/main.cr` → **`src/targets/x86_64-linux/main.cr`**(本条目正文路径 = 搬迁前快照,语义未变);即 **波 1 已搬、未收敛**:旗标注册分歧(本条目现象/机制段)逐字保持,`test_backend_bootstrap` stage 链仍走该入口,收敛留波 2(H7 纪律——波 1 只同步注记措辞、不动 flag 面)。正文头注文件位置同步:`src/targets/x86_64-linux/main.cr:12-16`(入口二元性注记)。 + +### 7. 死文件 `src/compiler/elf.cr`(566 行,零 importer,不入任何清单)——符号遮蔽/归属误导(2026-09-10 x86 实例化波 1 Task 1 评审登记) +- **现象**:`src/compiler/elf.cr` 无任何 importer、不入任何 concat 清单(build_selfhost_native.py / Core.toml / _import.cr 三面皆无),但以**同名符号**与活文件碰撞。**定义级碰撞集(评审修正——原 13 名清单夸大)**:`w8/w32/w64`(= src/compiler/dyn_arr.cr 字节写函数)、`w16`(= src/format/elf/elf.cr:741,非 dyn_arr)、`align_up`(活 elf.cr:745)、`g_asm_code_size`(活 elf.cr:748);另死文件对 `g_label_poses/g_label_count`(定义于 src/compiler/globals.cr:113-114)有写点。原列其余 11 名(elf_write_code/elf_begin/elf_finish/parse_line/encode_instr/measure_instr/asm_to_bytes/skip_dir/is_label_line/ElfCtx/LineInfo)在活树任何文件中均不存在——遮蔽危害结论不变,碰撞面以本表为准。 +- **第二实例(活文件双源——2026-09-10 波 1 Task 2 评审登记)**:同名**双活**文件——`src/compiler/entry.cr`(corec 闭包 shim,属 corec concat)vs `src/os/linux/entry.cr`(OS 轴 _start 启动,波 1 Task 2 迁入)。组合根 `import entry` 命中靠回退链槽序(os/linux 槽 6 先于 compiler 槽 7——评审加载迹实证命中正确文件);corec 单元侧 `g_source_dir = src/compiler/` 槽 1 自身目录命中 = 设计内。与死文件实例的差异:**两文件皆活** → 修复方向 = 结构性防护(改名一者或 import 独立 fileid),**非删除**;失败形态 = 链序重排即翻转(重复 `fn main` / undefined `compiler_main`——project-mode `error[` 门可捕,但该门未被 CI 调用,见 #6 ③ 执行力边界注记)。 +- **机制/隐患**:`module.cr` 回退链序当前**恰好**保护它——三轴目录(`src/format/elf` 等)置于 `src/compiler/` **之前**(module.cr:544-553 注释明言:否则本文件遮蔽 `src/format/elf/elf.cr` → 目标单元解析漂移 → elf_gen 等 N06 静默未定义)。但这是**顺序依赖**而非结构保证:任何回退链重排或新增目录(波 1 Task 2-6 正在改 import 集与命中面)都可能翻转命中,而翻转后果 = `error[` 静默未定义类故障(先例见 #6 ③ / c138c44c)。 +- **实证**:波 1 Task 1 评审(.superpowers/sdd/w1-task-1-report.md §2.1、§6.1)+ module.cr:548-553 注释推理;代码级引用 = 零(仅 plans 历史文档 `2026-08-08-pseudocode-tdd.md:400,413` 提及);`tools/pseudocode_extract.py` ROOTS 含 `src/compiler` 整目录 glob,仍从该死文件抽取 ELF 写侧符号 → `docs/pseudocode/标识符对照表.md` **20 行**归属误导(且对照表整体未随波 1 重生成——212 行仍写已退役 `src/arch/linux/ld` 路径;重生成 = 修复方向二部分)。 +- **修复方向**:**删除**(首选——零 importer、零功能贡献;删除需用户明确许可,铁律 #3)并重生成伪代码对照表;备选 = 迁出活树(`legacy/` 等,避开 ROOTS glob)保留历史。删除后复跑 test_backend_bootstrap + full-bootstrap guard(预期零影响、byte-identical)。 + +### 8. 前端 ≥18 形参静默误编译类(2026-09-10 x86 实例化波 1 Task 5 评审登记——高优先级:静默误编译) +- **✅ 已修(2026-09-11,提交 `d7ad71d314cb` + 缓存面 `68ffa1e8`;工作区报告 `.superpowers/sdd/fix-params18-report.md`)**: + 根因 = `FuncInfo.param_types` 是 **16 槽定长内嵌槽区**(`dyn_arr.cr` `OFF_FI_PARAM_TYPES=16` ⇒ `16+16×8=144=OFF_FI_RETURN_TYPE`),而 `parser.cr:1224` / `monomorph.cr:423` 写入无界——第 17 槽踩 `return_type`、**第 18 槽踩 `ast_node`**;形参类型值 `TY_INT=0` 恰把 `ast_node` 写成 AST 节点表首项(`rt.cr:4:13` 的 int 类型节点)⇒ checker 读 `fi_ast_node=0` 发 TF01 误归 + `.ccr` `name_idx=0`('import')/`param_count=0` + IR 体 46→28 instrs,而 `corec build` 仍 rc=0、产物 SIGSEGV 139(N=17 因 `TY_INT=0` **误打正着**;钳位最小实验单独证明因果)。 + 修复 = 槽区扩至 `MAX_FN_PARAMS=64`(对齐 `ast.cr` 镜像 `[int;64]`;覆盖 ≥22 参 = 6 寄存器 + 16 栈参的 `>127B` 栈清理形,使该结构 runtime 可达)+ `fi_param_type`/`fi_set_param_type` **唯一读写点护栏**(未来新调用点结构性地不可能再越界写)+ 超限签名 `P020` 硬错 rc=1(绝不静默 rc=0)。 + 回归 = `tests/selfhost/test_params_limit.py`(N=17/18 逐值 + 22/64 逐参校验 + 65 拒绝且无产物)+ `tests/suite/params_many.cr`(22 参 = `add rsp,0x80` 128B 栈清理形 runtime 覆盖——**Task 5 Minor 4 收口**)+ `src/ci/run.sh` 挂钩。 + 缓存面 = `CIR_CACHE_VER 15→16`(修复前 ≥18 参函数的坏 IR 已被写入 `.cir` 快照;键不含编译器身份 ⇒ 同源指纹相同会命中旧快照复活坏产物——评审 Important,已收口)。 + **修复方向 ② 的阈值经重定**:字面「≥18 形参改硬错」与 ③(N=18 ok)+④(22 参 runtime rc=0)互斥 ⇒ 硬错定在 64(唯一自洽解)。 +- **现象(评审修正——非单纯"丢名")**:≥18 形参的函数在 .ccr 中 `name_idx=0`(解析为字符串表首项 'import')且 `param_count=0`(N=17 正常:name_idx=3 'f' param_count=17);同源 checker 另发 `error[TF01] Function return type mismatch` 并**误归到无关声明**(如 `g_rt_argc : int, mut;`);`corec build` **仍 rc=0**,产物 SIGSEGV/错值(多行 18/20 参签名同样触发——参数计数依赖,非行长度依赖)。调用补丁随后失败 rel32=0 → rc=139。 +- **复现**:m18 probe(task 5 报告 + 评审 /tmp 产物)——基线工具链同样复现 = 预存,非波 1 引入(波 1 四文件均不在 corec_files)。 +- **影响**:a) 静默误编译类(rc=0 + 崩);b) `>127B 栈清理形`(需 ≥16 栈参 = ≥22 形参)**无 runtime 覆盖可能**(结构不可达)——波 1 Task 5 的 7B add rsp 形仅字节级覆盖(评审确认 16×push + add rsp,0x80 逐字节同)。 +- **修复方向**:①修参数表解析/AST bookkeeping(根源);②≥18 形参签名改硬错 rc=1(防御面);③回归探针 `N=17 ok / N=18 rejected-or-ok` 入 tests/;④缺陷修复后补 22 参调用 runtime 用例(`tests/suite`,断言 rc=0)——任务 5 Minor 4 的收口条件。 +### 9. extern >6 int 参静默语义缺口(2026-09-10 波 1 Task 5 评审登记——callseq.cr:45 指针落地) +- **现象**:IR_CALL_EXTERN 仅装载 6 个寄存器参(不调用栈参/清理——与 IR_CALL 的不对称,预存原样保留于 callseq.cr);>6 int 参 extern 调用 = 静默语义缺口。 +- **取证/修复**:FFI 面(2026-07-30 FFI 计划未提及);修复 = extern 栈参分派补齐或显式拒绝(rc=1)——波 2 FFI 面。 + +### 10. elf.cr 三处手写 syscall 序列 = OS 轴收编面(2026-09-10 波 1 Task 6 评审登记——波 2 / 实例 B 前必办) +- **现象**:`src/format/elf/elf.cr` 内三处 raw syscall 序列(**非内置体径**——内置体 syscall3/4 已归 src/os/linux/syscall.cr):`emit_heap_expand` mmap(:442-468——6 参 rdi/rsi/rdx/r10/r8/r9,raw `w8(...)` 发射)、worker exit(:680——`mov eax,60` + syscall,**syscall1 形**)、clone(:710-724——`mov eax,56` + syscall,**syscall5 形**)。 +- **为何登记而非报告注**:①在**格式轴**文件里——波 2 若只做"syscall3/4 参数化"扫不到它们;②**证伪实例 B 承诺**——设计 §1/§5"换轴零改动"要求非 Linux OS 轴下 Linux ABI 字节不得留在 `src/format/elf/`。 +- **修复方向**:OS 轴 syscall 序面收编(搬运/参数化到 `src/os/linux/`——波 2 或实例 B 前置任务);`elf.cr` 相关注已加交叉引用(Task 6 评审同批)。 +- **关联**:波 1 Task 6 评审 Important(.superpowers/sdd/w1-task-6-report.md);TODO #8 ④(22 参 runtime 用例)同属波 1 遗留收口。 + +### 11. 解释器 callee 内联路径缺 opcode(枚举 / 裸指针 / 切片 / 边界检查族)→ 双路径分叉(2026-09-10 R1 终审扩写——Important,本批 15 例未覆盖) +- **✅ 已修(2026-09-11,提交 `9068629c`(并入后 `c589c47b` 系);报告 `.superpowers/sdd/fix-interp11-report.md`)**: + 改法 = **分派统一为单实现**(`ir_interp_call` / `ir_interp_run_fn`:callee 内联臂与主循环臂共用同一实现,不再两处各写一份)——比逐个补 opcode 更彻底;18 族按「同语义同守卫」补齐,另补 **嵌套/递归内联**(重入帧保存恢复 + 深度守卫 + 中止码沿调用链上抛,**绝不静默落 0**)。 + 回归 = `tests/selfhost/test_interp_parity.py` **23/23**(interp callee ≡ interp main ≡ ELF oracle);**RED 对照**(换回修复前 interp.cr 重建)= 4/23,TODO 原件 interp `0`/SIGSEGV(`-11`)/`0` vs ELF `33`/`33`/`7` → GREEN 全对齐。 + 判据:selftest-types 95/95 · test_compile/test_purity PASS · test_ccr_v7 27/27 · **ELF 逐字节 `95084e7b…d475` IDENTICAL**。 + **余留**:① `IR_DYN_TAG`(41) 全仓无 ir_gen 发射点 = 当前不可达(内联侧为防御性对齐,建议另单);② 深度守卫超限 interp rc=255+诊断 而 ELF SIGSEGV(**都非静默**,行为不同);③ `IR_FNADDR=0`/`ARENA no-op` 为既存近似,判据只钉「双路径一致」不钉「与 ELF 等价」。 +- **现象**:解释器(interp.cr)的 callee 内联分派相对主循环**缺 18 个 opcode**(终审实测清单:4/17/18/23/24/25/26/27/28/29/30/41/42/43/44/45/48/51)——Task 4 只补了聚合族(7/8/11-16/31),其余仍缺。 +- **复现(终审亲跑)**:① 枚举:`build/review_t4/p14_enum_in_callee.cr` interp rc=0 vs ELF rc=33(interp 错值);`build/review_t4/p15_enum_split.cr` interp rc=139(SIGSEGV)vs ELF rc=33。② 裸指针解引用:`g:[int;3]=[5,6,7]; fn f()->int{ p:=&g[2]; return *p; } fn main()->int{return f();}` → interp rc=0 vs ELF rc=7。ELF 侧均正确。 +- **机制**:外函数内联路径缺 opcode 分派(主循环有、callee 路径无);静默分叉类(rc 不反映)。 +- **批前基线**:同两例批前 interp 亦 0/未处理、ELF 139 → **非本批回归**;但 R1「全局双路径一致」的留白比原 #11 标题(仅枚举族)更宽。 +- **修复方向**:按 Task 4 §6.2 同款「与主循环同语义同守卫」逐族补齐(枚举 / 裸指针解引用 / 切片 / 边界检查 / DYN_PACK…),并加双路径用例入 tests/。 + +### 12. 类型别名仅一层解析(2026-09-10 R1 Task 4 评审发现——Minor) +- **现象**:`type A = [int;2]; type B = A; g : B;` 仍双路径 SIGSEGV(interp rc=139 / ELF rc=139;R1 Task 5 亲测复核);一层别名(`g : A`)正常(对照 interp rc=7)。 +- **机制**:`agg_elem_count_of` 只解析一层别名,嵌套别名至底元素类型/长度未回溯。 +- **修复方向**:`agg_elem_count_of` 递归/迭代至底(并防环——自引用别名)。有初值形态工作、无初值形态崩,故非全断。 + +### 13. lexer 诊断前缀不一致 + 重复打印 + 边缘形态分歧(2026-09-10 R1 Task 3/5 评审与实测发现——诊断门零覆盖面) +- **① 前缀不一致(守卫/测试门零覆盖——重要)**:lexer 走 `add_error`(`src/compiler/lexer.cr:38-44`)输出 `error: `(**无错误码**),而 checker/其余走 `diag.cr:134` 的 `error[XX]`。既有守卫与测试门(`build_selfhost_native.py:171-185` guard_build_log、`test_backend_bootstrap.py:29-40` run_checked、test_global_init `has_diag`)一律扫 `error[`——对 **lexer 诊断零覆盖**(实测 `./build/corec run 'fn main()->int{return 0xZZ;}'` → `error: invalid digit in integer literal`,全无 `error[`,rc=1 非静默故未被吞,但门不可见)。建议统一格式或扩展守卫扫描面(`error:` 类同样计入门)。 +- **② 重复打印 4 遍(Task 3 评审 Minor)**:同一条 lexer 诊断打印 4 次——多轮 tokenize 累积、`tokenize()` 不清零 `g_error_count`(既有行为,父版同款注释)。措辞层噪声,非正确性。 +- **③ 边缘形态分歧(既有,超出 R1 判据集)**:`1._5`(self-hosted 响亮报错 vs bootstrap 词法层 INT(1)+DOT+IDENT(_5)——整管线下仍报错、**无静默接受**);`1.`(既有分歧);`0x_` 诊断措辞优先级变化(现报分隔符消息,原报 invalid integer literal——两者皆错误,仅措辞)。 + +### 14. bootstrap 后端:全局初值为非字面量(含一元负号)→ 静默降级为 0(2026-09-10 R2 P0 Task 1 实测确认) +- **现象**:`bootstrap/corec/frontend/ir_gen.py:68-74` 的 `constant_value` **只对 `Literal` 赋值**——`x : int = -1;`(一元负号)、`x : int = f();`(调用)等初值一律拿到 `.quad 0`(`bootstrap/corec/backend/x86_64_stack_asm.py:622-626` 的 `cv is None` 分支),解释器侧同源(`interpreter.py:35/81` 取 `constant_value`)→ **静默错值**(0 冒充初值)。 +- **影响面**:仅 **bootstrap 构建路径**(Python 工具链产出物);self-hosted 路径的同类缺陷已由 R1 Task 4(`71cb6278`:main 序言注入 + 解释器常量阶段)修复。当前自举链未触发(编译器自身无此类全局),属潜伏缺陷。 +- **触发实证**:R2 P0 Task 1 的 `tt_top()` 惰性 memo 原计划用全局 `= -1` 作「未初始化」哨兵 → 实测返回项 0(= ⊥),被迫改零初值 + ready 位(`g_tt_top_ok`/`g_tt_nil_ok`)。 +- **触发实证 ②(2026-09-13,R2 P4 Task 5)**:本任务的两枚表示码常量 `OE_BARE = -1` / `OE_BOXED = -2`(先置于 `ir_gen.cr`、后移 `globals.cr` 均同病)在自举链二进制里**读 0** ⇒ `Some` 初值的表示位与裸值同码 ⇒ 解包走错分支(gdb 实测 `emit_rep_set(enc=0)`、常量槽读 0);**改走 `oe_bare()/oe_boxed()` 函数**(返回值表达式不受该路径限制)后消解。原文「当前自举链未触发(编译器自身无此类全局)」**已不成立**——本仓自源现有此形态。 +- **live 实例(本轮实测)**:`cir_cache.cr` 的 `CIR_CACHE_MAGIC : int = -4485090715960753727` 在 bootstrap 构建的 corec/corearch 里同读 0 ⇒ 落盘 `.cir` 头部 magic 实测 **全 0**(`od` 读缓存文件首 8B = `0000000000000000`,应为 C1C1…)——写/读两侧同常量故**自洽**(不炸测试),但格式与 spec 记载的 magic 值静默不符(属本条目同族;修复须与 `CIR_CACHE_VER` bump 同批)。 +- **修复方向**:bootstrap `ir_gen.py` 的初值提取扩展到一元负号/常量折叠可判定形态(与 self-hosted 的 `global_init_val` 对齐),或对不可判定初值发诊断(**禁止静默 0**);判据 = `tests/bootstrap/` 增用例(负号初值/调用初值 → 值正确或响亮报错)。 + +### 15. R2 P0 类型项引擎落地(2026-09-10——落点与未覆盖面登记,非缺陷) +- **落点**:`src/compiler/type_terms.cr`(类型项 DAG 表 48B/条 + 开放寻址索引 + 哈希去重 + NNF/DNF 规范化)、`src/compiler/type_engine.cr`(三态判定 `ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited` + 反例 `tt_witness` + 穷尽性 `ty_exhaustive`(补集空性);原子类互斥公理、μ 展开余归纳 memo、预算守卫)、`src/compiler/type_selftest.cr`(38 例用例表)+ CLI `corec selftest-types` + 判据 `tests/selfhost/test_type_engine.py`。spec = `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §9 P0;计划 = `docs/superpowers/plans/2026-09-10-r2-p0-type-engine.md`。 +- **P0 边界兑现**:checker/ir_gen/后端/内核零改动;产物 byte-identical(`ptr_arith.cr` 与 R1 基线逐字节相同);自举 `corec2/corec3` `cmp` IDENTICAL。 +- **未覆盖面(显式登记:命中返回 -1 或按守卫,**不静默**)**:① 参数化原子的参数仅同形判等(变型规则 = P3);② `AK_NAMED` 具体行不展开(待 P2 接入 checker 类型表后可用);③ 空递归(如 μX.X)按深度守卫 512 → -1;④ 判定预算默认 200000 步(**规范化亦计入**:∩ 分配律 2^n 爆炸在 n≈13-14 处截断),超限 → -1 + `g_ty_exhausted`;⑤ 命中①/②/`¬μ` 时置 `g_ty_uncovered = 1`(写入点在 `lit_implies` 与 `tt_nnf_neg`)并以 -1 上抛——**未覆盖面恒给「未知」,不给确定答案**;⑥ memo 重建(`ty_memo_rehash`)与「进行中」状态的交互**无实测覆盖**(现有规则下需单次证明内 >512 互异对才可达;读码核验成立,复审登记)。 +- **三态约定(判据面)**:判定 API 返回 `1 = 成立 / 0 = 不成立 / -1 = 未知(预算耗尽或未覆盖)`;`tt_witness` 另用 `-2 = 未知` 与 `-1 = 不可满足` 严格区分(`ty_exhaustive` 对 -2 返回 -1,绝不当「穷尽」)。**预算耗尽的结果一律不缓存**(memo 每顶层查询清空)。 + +### 16. 函数体内嵌套 `fn` 声明 → 编译段错误 rc=139(2026-09-10 R2 P1 Task 2 评审确认——既有缺陷,非该批回归) +- **✅ 已修(2026-09-11,提交 `f417d3453a79`;报告 `.superpowers/sdd/fix-nestedfn16-report.md`)**: + 根因 = `parse_primary` **无 `T_FN` 分支** ⇒ 语句位遇嵌套 `fn` 落回通用兜底 → 解析**失步**;随后 struct 字面量字段循环在 EOF 处**自旋** → bump allocator 耗尽 → `grow_ast` 的 `rep movsb` 向 **NULL** 拷贝(gdb 实测 `rdi=0`、`rcx=0x12000000`=288MB),日志止于 `[3/5] parse`(与 TODO 归属证据一致)。 + **结局判定(推翻 TODO 原文「判据 rc=0」前提)**:嵌套 `fn` **不属语言面**——`grammar/core.ebnf` 的 Statement 不含 FunctionDecl、bootstrap 对同输入报 SyntaxError、函数值设计为 YAGNI 挂起 ⇒ 正确结局 = **定位拒绝 `P021`(rc=1)**,而非 rc=0。依据已写入 `tests/selfhost/test_nested_fn.py` 文件头与 `run.sh` 注释。 + 修复 = `P021` 定位拒绝 + `}`-键循环 6 处 EOF 护栏(防同类自旋)+ `run.sh` selfhost-tests 挂钩;回归 = `test_nested_fn.py` 17/17(含 4 例 EOF 失步探针,独立对拍父版均 rc=139)+ `tests/suite` 21 pass/0 fail。 + **余留**:① EOF 失步恢复期会重复打印无定位 parse error(有界 ≤64、毫秒级、rc=1;既有报告面,非本次引入);② 若将来要「支持」嵌套函数,`P021` 即显式拦截点,需连 checker/ir_gen/后端一起做。 +- **最小复现**:函数体内**嵌套 `fn` 声明**的 `.cr` 编译 rc=139(评审最小件 `/tmp/rv_nested.cr`);**扁平版(同逻辑不嵌套)rc=0** → 触发点 = 嵌套 fn 声明本身。注意:原先被归因的 `tests/suite/at_test_mini4/6` 只是同族样例(**mini6 并无 `@inline`**,原报告措辞有误)。 +- **归属证据**:编译日志止于 `[3/5] parse...`(该行打印于 `parse_all()` 之前),checker 的 `[4/5]` 未开始 → 崩点在 parse→checker 之间;**在 R2 P1 之前的编译器上同样复现**(两版均 139)→ 与影子层/类型引擎无关。 +- **影响**:`tests/suite/` 中 2 个 fixture 长期 rc=139(P1 语料扫描标记 SKIP)。 +- **修复方向**:嵌套 fn 的解析/注册路径(parser 的嵌套声明处理或 checker `collect_decls`)——先定位崩溃点(gdb/诊断输出),再修;判据 = 最小复现 rc=0 + 两个 fixture 恢复 + 全回归。 +- **实现期实证教训(后续期通用)**:Core **无三元运算符** `?:`;取模须非负(i64 向零截断,负下标 → 越界静默失效);键比较**不得依赖 i64 回绕**(bootstrap 解释器任意精度 → 回绕等式恒假);字面矛盾规则须窄(正原子 × 异类负原子**不空**:`int ∩ ¬string = int`);μ 展开必须走 memo 入口(余归纳终止)。 + +### 17. R2 P1 影子对拍落地(2026-09-11——落点 / 开关默认值与产物影响 / 裁决归属登记,非缺陷) +- **落点**:`src/compiler/ty_shadow.cr`(桥接 ti→类型项 + per-ti 缓存 + 8 站点挂点 + 分类计数 + 摘要/转储)、`src/compiler/checker.cr`(`type_equal` → `type_equal_core` 包装 + 8 站点 `sh_site_begin`)、`src/compiler/type_selftest.cr`(桥接用例)、`src/compiler/globals.cr`(`g_shadow_*` 组)、`src/compiler/main.cr`(CLI 旗标)。清单三处注册:corec + **corelsp**(Task 2 起 checker 引用影子层)+ test_compile。清单 = `docs/superpowers/specs/2026-09-10-type-shadow-findings.md`;计划 = `plans/2026-09-10-r2-p1-shadow-parity.md`;提交链 `e4c293b2`(桥接)→`844cec6c`+`514956a1`(挂点/补强)→`75c20297`(Step 0 拆因 + 语料清单)。 +- **开关默认值与产物影响(登记)**:`--type-shadow`(**默认关**,纯观察通道;`--type-shadow-dump ` 另给差异转储)。两态产物**逐字节相同**(`ptr_arith.cr` 开/关/基线三份 sha256 全等 `95084e7b…d475`;`check` 路径 stdout 仅开态追加以 `[type-shadow]`/`[type-shadow-sites]` 起头的两行)。影子预算/memo 每次判定前后各 `ty_budget_reset` → 不污染 checker 判定;关态 `sh_site_begin` 首行早退(Task 4 M3)→ 关态残留开销 = 每次判定一次全局读。**结论:开关可安全长期保留默认关,无产物影响。** +- **差异清单裁决归属(登记)**:findings 的 F1(P2 硬前置)/F2(P2 裁决)/F3/F4(checker 缺陷面,单开任务)与「按差异清单替换旧判定」的**替换门**全部归 **P2**;P2 验收不得只看「差异数 = 0」,须同时报告站点覆盖(P1 实测 4/8 站点零命中)。下 #18-#21 为逐条登记。 +- **站点 6 去留裁决(终审 M-1 交接项)**:站点 6(`assign-binary`,`EXPR_BINARY + OP_ASSIGN` 遗留路径——parser 已把 `=` 一律降为 `EXPR_ASSIGN`,`parser.cr:222-224`,故**不可达、无样本**)**保留挂点,去留归 P2 裁决**(**后由 P5 D25 推翻**:`tok2op` 零 `OP_ASSIGN` + 语料站点直方图 0 命中 ⇒ 分支与挂点**已删**,`P5 Task 4 / #47`;站点 id 不重编号、退役注记落站点表)。 + +### 18. ~~F1(P2 硬前置):同名 named 类型占多行未规范化 → 引擎按行建原子 → 判不了~~(2026-09-11 R2 P2a Task 1 **已修**:建表去重——唯一分配点收敛 `alloc_named_type` + 开放寻址侧表(装填守卫/重建重放/回写重探,照 `sh_map_*` 先例),8 个生产分配点全改调;12 个读取点读**名字**故语义不变。落点 `src/compiler/checker.cr`(+`globals.cr`/`type_selftest.cr`),提交 `3c8402e8`(评审 3 Minor 收口 `8c9d773f` 纯注释);判据 = `selftest-types`(f1.* 四例 + 扩容重建例)+ 侧表↔`res_type_node` 管线内断言(`tests/selfhost/test_named_dedup.py`,正控 mismatches=0 / 负控注入 6、rc=1)+ P1 9 条 unknown **清零** + ELF 逐字节同) +- **现象**:`type_equal_core` 的 `TYP_NAMED` 按 **name_idx** 判等(同名恒等价);类型表同一名字可占**多行**(`MemLayout` 2 行 / `Box` 5 行——`res_call_type` 在符号不可见处 `alloc_type(TYP_NAMED,...)` 新建行、泛型实例化另建行),而桥接按**行号**建原子 `tt_atom(AK_NAMED, ti, -1)` → 两个不同原子 → 引擎 `AK_NAMED` 不展开 → **-1(未判定)**。 +- **实证**:P1 三档语料 26,704 判定中 9 条差异(去重 3 类型对)**全部**由此产生(`old_looser=0`、`old_stricter=0`;差异 = 「旧能判、引擎判不了」);根因探针 = findings §4.2 + §6.F1。 +- **影响(为何是硬前置)**:替换后主流「同类型比较」会从**真**变**未判定**;若 P2 把未知当拒绝 → 大面积假拒,当通过 → 静默失去命名类型检查。二者皆不可接受。 +- **修复方向**:二选一并裁决——① 引擎侧引入 named 身份规范化(同名 → 同一原子,需 name 注册表);② 桥接把同名行折叠到同一项(须先裁决「同名是否恒等价」,会掩盖跨声明域同名)。**P2 第一项,且须补同名多行的守门用例**(findings 探针 B/F/G/K 可升格为回归)。 + +### 19. ~~F2(P2 裁决):数组长度 `N` 旧判定比、引擎不比 → 替换即放宽~~(2026-09-11 R2 P2a Task 2 **已裁决并实施**:用户裁决 = 落「常量档长度约束」保持现状拒绝语义——`array_len_constraint_ok` 沿结构对应位下钻比 N(数组元素/指针元素/引用元素/切片元素/元组字段/泛型应用实参 6 位),身份分支去 N,判定点 `type_compat_strict` 显式补检(`-1` = 长度违反 → 专属措辞,码不变);判定点**恒先调 `type_equal`** 保影子站点采样面。落点 `src/compiler/checker.cr`,提交 `b36d8e76`(评审 M1「同形」限定 `507daf7a` 纯注释);判据 = `selftest-types` f2.* 8 例(**Task 4 补至 10 例**:+REF/SLICE 下钻位)+ 异长拒绝行为探针 + 全语料 `old_stricter=0`(探针 D/G 的 F2 面消除);**P3 面遗留**:符号档/动态档长度约束未实现(本批按字面量比较 extras,异形不下钻))——**✅ 2026-09-12 收口(P3a Task 1;#34)**:动态档 = 既有运行期检查(`g_ir_slice_lens` 侧表 + `IR_BOUNDS_CHECK`,R1 已落)+ checker 字面量界检查;**符号档 = VC 义务,显式登记不实现**;N 读取面守门 = 「N 只许被常量档检查与表示层读(`arr_len_lit_of`/`type_size`/`type_align`),身份/子类型路径零 N」已由 `t1.n_face_gate*` 用例钉住(含嵌套固定性「b 盲」比较修复) +- **现象**:`type_equal_core` 的 `TYP_ARRAY` 分支比较 `extra`(= N);桥接按 R1 裁决**不把 N 入身份**(`AK_SEQUENCE` 参数链只含元素项)→ 替换后 `[int;3]` vs `[int;4]` **静默通过**(当前是 `error[TF01]`)。 +- **实证**:探针 D(`[int;4]` 返回给 `[int;3]`)与探针 G(泛型实参位)均 `old_stricter=1`;本轮语料 0 触发。 +- **修复方向**:R1 的「N 不入身份」是 **IR 侧**身份裁决;**checker 侧是否保留 N 检查需单独裁决**——保留 → 需在引擎/桥接把 N 作为维度/字面量入判定;不保留 → 记为有意的语言放宽并写进 spec。裁决后同步 `bridge.len_not_identity` 用例语义。 + +### 20. F3:调用位点实参类型不匹配**无诊断**(`unify_types` 返回值被丢弃,`checker.cr:1053`) +- **现象**:`infer_gen_call` 调 `unify_types(pattern_ti, concrete_ti);`(`src/compiler/checker.cr:1053`)**丢弃返回值**;非泛型调用位点同样不报。 +- **实证**:`fn take2(n: int)->int` 以 `take2("s")` 调用 → 输出 `ok`(无诊断);`fn take[T](a: T, n: str)` 以 `take(1, 2)` 调用 → `ok`(探针 J/C,源在 `/tmp/r2p1t3/probes/`)。 +- **与影子的关系**:影子只对账 `type_equal` 的 verdict(此例两侧皆「拒」= agree),**不负责诊断**;但 P2 若以引擎判定作为诊断来源,必须补「判定 → 诊断」这一环(含 `unknown` 的处置策略:拒绝 / 降级警告 / 放行,需与「语义保鲜」目标一致)。 +- **修复方向**:单开任务(属 checker 缺陷面,非影子层)。 + +### 21. ~~F4:泛型函数**后续形参**的声明类型在推断中不生效~~(2026-09-11 R2 P3 Task 5 **已修**:根因 = `infer_gen_call` 的形参链导航 `pn = pn + 1` 假设 EXPR_PARAM 在节点表**连续**——实际每个形参的**类型节点**都在其形参节点之前分配(`parse_type` 对**每个**类型都占一节点,含基类型)⇒ 第 i+1 个形参落在类型节点上,`ast_data(该节点)` 对基类型节点 = 0 ⇒ `res_call_type(0)` 读**AST 节点 0**(本文件首个类型节点)⇒ 后续形参的声明类型被绕过、且未绑定形参被凭空绑定(节点 0 恰是某泛型形参 ident 时绑到它)。修法 = 前扫到下一个 EXPR_PARAM(照 `ir_gen.cr` 三处同款:dex 参数对齐 :1481-1487 / 参数建 var :2279-2285 / mono 克隆 :2715),扫到表尾置 -1。判据 = selftest `t5.f4_later_param_declared_type`(手工夹具复刻不连续布局 + 节点 0 = 另一泛型形参 ident,正/负控)+ `t5.f4_bind_seg_recorded` + 行为 `tests/selfhost/test_generic_constr.py::f4_later_param_binds_correctly`(旧态假拒 TF01 → 新态 rc=0,**放宽 1 条**见 #33)+ 突变控制 M1(退回旧导航 → 两处同时红)。落点 `src/compiler/checker.cr` 的 `infer_gen_call`。) +- **现象**:`fn take[T](a: T, n: int)` 以 `take(1, "s")` 调用时,`unify_types` 收到的 pattern 是 **`gparam(T)`** 而非 `int`(探针副本 `[dbg-unify]` 实测);`fn take[T](a: T, n: str)` 以 `take(1, 2)` 调用同样收到 `gparam(T)`。而**首个形参为具体类型**时正常(探针 K:`fn take[T](n: int, a: T)` → pattern = `base(int)`,正确进站点 4)。 +- **影响**:泛型函数的非泛型形参类型约束在调用推断中被绕过(与 #20 叠加 → 完全无诊断)。 +- **修复方向**:单开任务定位 `ast_data(pn)` / 形参链导航(`infer_gen_call`,`checker.cr:1030-1060`)。 + +### 33. R2 P3 Task 5 落地(2026-09-11——泛型约束:保留 / 实例化判定 / monomorph 实例键类型项化;**半 P3a 交付 + P3b 阻塞登记**,含未覆盖面) +- **落点**:`src/compiler/parser.cr`(结构/枚举泛型约束**不再丢**:`save_struct_gen_constrs`/`save_enum_gen_constrs`——**空槽预填 -1**,见 §缺陷①;F4 无关)、`src/compiler/globals.cr`(`g_sgen_constr`/`g_egen_constr` 两张稀疏侧表 + `g_gen_binds` 调用点绑定段 + `g_constr_seen` 诊断去重表)、`src/compiler/dyn_arr.cr`(增长函数 + `si_gen_constr`/`ei_gen_constr` 读取护栏)、`src/compiler/checker.cr`(F4 形参链修复;`gen_constr_type_ti`/`gen_constr_satisfied`/`gen_constr_witness_str`/`gen_constr_raise`/`gen_inst_constr_check` + 去重表;`infer_gen_call` 的**本质轴**判定 + 调用点绑定登记;`res_type_node`/`res_call_type` 的**实例化点**检查)、`src/compiler/ty_shadow.cr`(`tt_display` 反例文本化——**零 str_intern**,守 `.ccr` STR 段约束)、`src/compiler/monomorph.cr`(`inst_key_of_ti` 规范结构名 / `inst_type_node_of_ti` 类型节点合成 / `gen_subst_ti` + `inst_ti_concrete` / `gen_remap_binds` 克隆期绑定重映射 / `gen_find_or_create_bind`)、`src/compiler/ir_gen.cr`(泛型调用点优先用 checker 绑定段建键)、`src/compiler/type_selftest.cr`(**279 → 288 例**,t5.* 9 例)、`tests/selfhost/test_generic_constr.py`(新建 **14 例**)、`src/ci/run.sh`(selfhost-tests 挂新套件 + 计数同步)、**`build_selfhost_native.py`**(`monomorph.cr` 自 `backend_support_files` 迁入 `corec_files`——见 §登记③)。 +- **落地语义(P3a 半边)**:① 结构/枚举泛型约束保留 + **实例化点**判定;② 实例化判定 = `ty_sub(实参项, 约束项)`(**本质轴**:约束名 = 原生/已声明类型)三态,违反 = `error[TG02]`(**软诊断**,未新开硬门)+ **非空反例文本**;③ monomorph 实例键 = 类型行**规范结构名**(`inst_key_of_ti`),替换 = **ti 型节点合成**(取代名字文本替换);④ F4(#21)。 +- **P3b 阻塞面(本批显式不判,**不得**当「不满足」)**:`T: I`(I = `interface` 名)的**满足判定**须走 P2b 注册表 `iface_satisfies`(未交付,P3 计划附录 A.3-①)⇒ ① 结构/枚举实例化点对接口约束 = **-1 零动作**(`tests/selfhost/test_generic_constr.py::struct_iface_constr_unjudged` 钉住);② 函数调用点**回落**既有 `check_iface` 名拼接路径(逐字未动,`iface_constr_legacy_*` 两例)。 +- **缺陷①(本批修复)**:稀疏侧表的**零初值 = 合法名字 ni**(首个驻留串)⇒ 未写过的槽被读成 0 = 有效约束名(凭空约束)——修法 = save 函数整行(MAX_GENERICS 槽)预填 -1;selftest `t5.constr_side_table_readback` 钉(突变 M4 咬合)。 +- **登记②(MAX_GENERICS = 4,**未解除**)**:>4 泛型形参现状 = parse 期硬报(实测 `P01`/`TA08`,**非静默截断**);解除需改 struct/enum 表布局(`OFF_SI/OFF_EI_GENERIC_NAMES` 各 4 槽 + `ESZ_*` 变更)+ `parse_generics_into` 的 4 槽常量 + `ccr_io` 零写槽 ⇒ 归单开批;触发条件 = 语料出现 >4 形参(当前最大 = 2:`tests/suite/generics_test.cr` 的 `pair[A,B]`)。 +- **未覆盖面(显式登记)**:命名类型实参 vs 原生约束 = **-1 不判**(引擎 AK_NAMED 不展开 ⇒ `struct Box[T: int]` + `Box[S]` 静默,行为例 `struct_inst_named_arg_unjudged`);`never` **不**满足原生约束(引擎 AK_NEVER 是与 AK_INT 互斥的**原子**而非 ⊥——既有语义,本批如实钉住);约束名既非类型也非接口 ⇒ 不判(**拼写错误的约束名静默**,`unknown_constr_name_unjudged`);复合实参(元组/嵌套应用)无语法形态 ⇒ 键走行号兜底 + 替换回落名字路径;ti 型替换**不覆盖实例形参**(`EXPR_FN` 分支不克隆形参——形参类型由 `fi_param_type` 裸码承担);`inst_key_of_ti` 对未知 kind 用行号兜底(同编译期内确定,跨行同型不去重)。 +- **登记③(清单变更)**:`monomorph.cr` 自 corearch 的 `backend_support_files` 迁出——corearch 侧对它**零代码引用**(逐符号脚本核对:全部定义符只出现在 `globals.cr` 的注释里),而本批的类型项化需 checker/parser 层符号(`get_type_kind`/`alloc_node`,corearch 清单无此层);安全面 = ELF canary 逐字节同 + 全套回归 + `corec2==corec3`。 +- **死码登记**:`ir_gen.cr` 的 `find_or_create_mono_func` **零调用者**(全仓 grep 实测);其读调用节点 `int_val` 的**旧语义**(= 首个绑定类型名)已被本批改写为「绑定段起始 + 1」——若将来自复活,必须先改该函数(否则错读绑定段下标)。 +- **判据(Task 5)**:`selftest-types` **288/288**;`test_generic_constr.py` **14/14**;`test_iface_ops` 76/76 · `test_match_exhaust` 16/16 · `test_optional` 12/12 · `test_impl` 7/7 · `test_ccr_v7` 27/27;ELF canary `95084e7b…d475` **IDENTICAL**;`.ccr`:`ptr_arith` 逐字节同、`generics_test` **+370B**(唯一差异 = `get_val[unit]` → `get_val[int]` + `get_val[string]`,即**反折叠**;逐段 STR+10/SYM+56/NOD+180/ENT+84/REG+24/EDG+16 = **恰一个额外实例**,无结构漂移);全语料 72 档 check 面诊断/rc **零差异**;影子 `decisions=30338 agree=30338`,全计数 0,站点覆盖同前批(站点 1/2/4/6 语料零命中、3 仅 2 次);`corec2==corec3` IDENTICAL + N06=0 + 冒烟 42;突变控制 4 条(M1 F4 / M2 本质轴判定 / M3 实例键 / M4 约束保留)逐条咬合且**复位后二进制逐字节复原**(`d78f49aa…`)。报告 = `.superpowers/sdd/p3-task5-report.md`。 + +### 34. R2 P3a 落地(2026-09-12——五任务:引擎展开层 / 定长退役收口 / 穷尽性 / 联合可选 / 泛型约束;**P3b 剩余面 + 收紧台账 + P5 继承项登记**) +- **落点与提交链**(P3a = Task 0/1/3/4/5;计划 = `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md`,**P3b 开工前必读其附录 B**): + · **Task 0 引擎展开层**(`61d3a8b4`):`src/compiler/{ty_shadow,checker,type_engine,globals,type_selftest}.cr`——`sh_struct_term`/`sh_enum_domain_term`/`sh_variant_term`/`sh_iface_shape_term`(占位 -1)+ `iface_satisfies` 占位(恒 -1)+ 展开缓存(第二张表)+ 行→声明入口(`decl_name_of_ti`/`find_struct_row_of`/`find_enum_row_of`)。**语义边界 = 只服务满足判定与域查询,等价判定保持原子名义**;纯新增 502/0、72 档三面零差异。 + · **Task 1 定长退役收口**(`ef61f002`):变型表 `ty_variance_of`(只读协变/可写不变/表外默认不变)+ `sh_seq_term`(固定性位入 b 槽,N 不入等价身份)+ `sh_ref_mut_marker`(mut 成判定维度)+ `array_len_walk` 方向规则(视图→固定 ⇒ 0)+ 10 判定点参序归一(源, 目标);关闭「切片→固定长」静默放宽(P2a 洞)。 + · **Task 3 穷尽性真判定**(`e9818d62`):`sh_match_*`(模式项/覆盖位/反例命名)+ checker `EXPR_MATCH` 判定 + `EC_TM_EXHAUST`(9003) 入硬名单(report-only 先行)+ TM04 软面;新套件 `tests/selfhost/test_match_exhaust.py`(16 例)。 + · **Task 4 联合/可选**(`c8c7731b`):`T?` = `T ∪ null`(`TYP_OPTIONAL`/`TYP_NULL` + `EXPR_OPTIONAL`;`AK_NULL` = 引擎原生第九员,**不进** iface_registry 条目表——`iface.count == 13` 为判据硬值);退役内建 Option 注册 + `Some`/`None` 关键字名分派 + `EXPR_TRY` 结构解包(废名字串);枚举载荷**类型节点列**(T0 交接①);可选目标子类型注入(`ti_subsumes`);修 `Some(x)` 实参链挂死 + `sym_*` 越界崩;新套件 `test_optional.py`(12 例)。 + · **Task 5 泛型约束(P3a 半边)**(`9c0a83ec`):结构/枚举约束保留(侧表 + 空槽预填 -1)+ 实例化点 `ty_sub` 本质轴判定(TG02 软诊断 + `tt_witness` 反例文本,零 `str_intern`)+ F4(#21 划销)+ monomorph 实例键类型项化;`T: I` 半边 = P3b;新套件 `test_generic_constr.py`(14 例)。 +- **判据(收官复验,2026-09-12)**:全量回归 = 五 CI job(selfhost-tests · bootstrap-tests · suite · check · full-bootstrap)+ **42 selfhost + 7 bootstrap 全量套件**逐档 rc 记录;`selftest-types` 212→**288**;ELF canary `95084e7b…d475` IDENTICAL(clean-cache);`corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42;影子终态 `decisions=30128 agree=30128`(P3a 收官修复 target 清单后由 30338 → 30128,−210 = monomorph 退出 target 两档闭包;全计数 0,站点覆盖同报:站点 1/2/4/6 语料零命中、3 仅 2 次);`.ccr`:T0/T1/T3 不变 · T4 = 「Option」串退役(STR −10B)· T5 = `generics_test` +370B(`get_val[unit]`→`get_val[int]`+`get_val[string]` 反折叠;`ptr_arith` 不变);`check` job rc=1 = **既有 red 划界**(2 条 TF01 误报 `lits_copy`/`ty_memo_slot_no_grow`,行号随源码增长漂移,非本批引入)。(→ **2026-09-13 TF01 收口已修**,见 #40;本句为当期实跑记录,原样保留。) +- **收紧/放宽台账(P3a 汇总;逐条 = 计划附录 B.2)**:**收紧 18 条**(T0 0 · T1 6 · T3 6 · T4 3 · T5 3;其中 1 条 rc 不变仅诊断码集合扩张)+ 诊断面新增 1(TM04 软)+ **放宽 8 条**(T4 7 含 1 条非语义放宽 · T5 1)+ 实例名忠实化 6(T5 反折叠等)+ 挂死修复 2 端(T4)。**全语料零命中**(72 档 × 五任务同源双编译器对拍)⇒ 自举源码零改造。 +- **P3b 剩余面(~~未开工;唯一阻塞 = `iface_satisfies` 未交付~~ ⇒ **2026-09-12 已全部交付**:Task 0b `9aa1786c` + Task 2 `44100ba4` + Task 6 `e838f18b` + 收官 **#39**;各件详版 = #36/#37/#38)**:Task 2 横切接口(形状条目 + 索引/切片/迭代消费点 `get_type_kind` 直比换位)、Task 6 impl 契约(签名类型项化 + 满足判定接引擎 + mangling 退役)、Task 5 的 `T: I` 半边(接线点已就位:`gen_constr_satisfied` 首分支 + `sh_iface_shape_term`)。开工顺序与硬口径(report-only / STR 段约束 / 三态纪律)见计划附录 B.1。 +- **登记面(本批新增/更新;详版 = 计划附录 B.4 十九条)**:**非枚举域不判穷尽**(不假装穷尽/不假装不穷尽)+ **空枚举域 = 空洞穷尽**(实测判穷尽=1;计划原文「=0」偏差登记)+ **空递归保守**(P0 未覆盖面③,本批未动)+ **MAX_* 上限处置总口径**(MAX_ENUM_VARIANTS/MAX_VARIANT_TYPES 越界写未修 = **#35**(17 变体实测:check 静默 rc=0 / build rc=1;代码级定位含 224B 越界)· MAX_GENERICS=4 已登记未解除(#33 登记②)· 三者同族 ⇒ 统一「先加护栏、再评估解除」);函数侧 `save_func_gen_constrs` 稀疏零初值同族风险(未修);`never` 非 ⊥(不满足原生约束 / 不给 `never ⊆ T?`);命名实参 vs 原生约束 = -1 不判(引擎 AK_NAMED 不展开);运行期可选表示未统一(裸值 vs `Some(...)` 对象)= **`Some` 臂双路径 SIGSEGV(check/build rc=0 零诊断 + 产物/解释器 rc=139,非 P3a 引入)= 计划附录 B.7 一等条目(P3b/P4 必修)**;`EXPR_LET` 无检查 = #32(本站点仍留缺口);bootstrap `&&`/`||` 不短路根因仍开放(T4 只关 `g_syms` 未分配触发链);witness 含空析取支(反例命名走覆盖位,若净化 `tt_norm` 须同步);`EXPR_ENUMPAT` 名字槽 = `ast_a`(新消费者必守)。 +- **P5 继承项(并入 #24/#30 清单;**本条已全部收口**)**:~~`type_equal_legacy` 删除~~(**已删:P5 Task 4 / #47**)+ ~~`sh_*_ak_legacy` 删除~~(同上)+ ~~站点 6 挂点~~(同上)+ ~~影子层(`--type-shadow`/`replace_*`)同族下线~~(**已删:P5 Task 5 / #48**——判定通道/环缓冲/摘要/转储/站点直方图/9 挂点/17 全局/2 CLI 通道;回归网切换见 #48)+ `tt_display` 零 `str_intern` 的可选改造(**未做,转 P6**,若接受 `.ccr` STR 段增长);**残项** = `--verify-named-dedup`(非影子面 + 有活消费者 ⇒ 不删,偏离登记见 #48)。 +- **判据继承(#26)**:本批 `.ccr` 判定按 #26 口径执行(`test_ccr_v7` 结构性断言 27/27 + 语义零变化 + 自举稳定),T4/T5 变更面**逐段实测**(非「与旧版逐字节同」);三任务 ELF canary 全同 ⇒ 类型层改动未泄进发射面。 +- **⚠ 收官发现并修复的回归(Task 7;**T5 漏改 project-mode 清单**)**:`src/targets/x86_64-linux/_import.cr`(corearch project-mode 导入清单)的 `import monomorph` 漏删——T5 只把 `monomorph.cr` 从 concat 面 `backend_support_files` 迁出,而 monomorph.cr 的类型项化新增 checker/parser 层依赖(`get_type_kind`/alloc_node 族 46 处)⇒ project-mode corearch 构建 **33×error[N06] 静默未定义**(rc=0 + 产物照出;stage 链互测不可见),`tests/selfhost/test_backend_bootstrap.py` 的 project-mode `error[` 门首步 rc=1。**为何 P3a 期间不可见** = 该套件未挂 CI(#31,同批已更新证据)。**修复** = 删该行(与 concat 面同一零引用证据;注记落文件头);**复验** = 该套件 rc=0(stage1/2/3 逐字节同 + smoke/O2 smoke 全绿)+ project-mode 构建日志 `error[` = 0(33→0)+ ELF canary `95084e7b…d475` 不变 + 三二进制 sha 不变(本文件不入任何 concat 清单)。⇒ 收官全量枚举(42+7 逐档)终态 **49/49 rc=0**。 +- **报告**:`.superpowers/sdd/p3-task{0,1,3,4,5,7}-report.md`(六份)。 + +### 22. 仓库卫生:suite 空 fixture ×2 + 死文件扫描噪声(2026-09-11 R2 P1 Task 3 语料扫描发现) +- **`tests/suite/test_control_flow.cr` / `test_generics.cr` = 0 字节空文件**:`corec` 对它们报 `error: cannot read`(**勘误**:Task 2 报告曾记为「预期失败 fixture」,实为空文件)。修复方向 = 补内容或删名(删需用户许可,铁律 #3)。 +- **`src/compiler/elf.cr`**:`check` 扫描 rc=0 但 **2 个 parse error、decisions=0**——陈旧遗留文件(死文件本体的登记见 #7;本条补充**语料卫生**事实:批量 check 脚本须排除它,否则 parse error 混入语料日志)。**`src/compiler/linker.cr` 为 0 字节空文件**(同类:批扫时排除或删除)。 +- **实证**:findings §8 末「其他已登记项」;语料日志 `/tmp/r2p1t3/all_tiers2.txt`。 + +### 23. harness 缺口:`.claude/hooks/block-git.py` 只拦「以 git 开头」→ 复合/管道命令可绕过(2026-09-11 R2 P1 Task 3 评审发现——**只登记,本轮不改 harness**) +- **现象**:钩子仅判 `stripped == "git"` 或 `stripped.startswith("git ")`(`.claude/hooks/block-git.py:10-11`)——`cd x && git status`、`echo hi; git log`、`(git status)`、`sudo git ...` 等**复合/包装形态全部放行**;铁律 #2 的「机械拦截」有洞。 +- **影响**:非恶意误用(多 agent 并行时的习惯性复合命令)即可能绕过禁 git 约束;本轮工作副本已出现一次只读 `git diff --numstat` 违例(Task 3 自陈 D7,无写操作)。 +- **修复方向(建议单开)**:按 shell 分隔符(`;` `&&` `||` `|` `(` 换行等)分词后做**词边界**匹配,命中 `git` 词即拒(注意放行 `jj git push` 等 jj 子命令形态与 `gitignore` 类词元);并补负控用例(复合形态必拒、`jj git` 必放)。 + +### 24. R2 P2a 落地(2026-09-11——落点 / 未覆盖面登记 / P5 继承项,非缺陷) +- **落点**:`src/compiler/checker.cr`(`alloc_named_type` + 去重侧表 / `array_len_constraint_ok` / `type_compat_strict`+`diag_type_incompatible` / `type_equal_engine`+`type_equal_legacy` 拆分)、`src/compiler/ty_shadow.cr`(对照物切 legacy + `replace_*` 计数)、`src/compiler/globals.cr`(`g_named_dedup*` / `g_replace_*`)、`src/compiler/main.cr`(`--verify-named-dedup`)、`src/compiler/type_selftest.cr`(f1./f2./t3.* 用例)、`tests/selfhost/test_named_dedup.py`(新增)。计划 = `docs/superpowers/plans/2026-09-10-r2-p2-replace.md`;报告三份 = `.superpowers/sdd/r2p2-task-{1,2,3}-report.md` + 收官报告。提交链 `3c8402e8`+`8c9d773f`(F1)→ `b36d8e76`+`507daf7a`(F2)→ `10718ba8`(判定替换)→ 收官(本条目所在提交)。 +- **行为面兑现**:判定权由结构判等移交引擎(`ty_equiv`),unknown(-1)/桥接失败**回落 legacy 并计数**(`replace_unknown`/`replace_bridge`,不静默);ELF 逐字节 = R1 基线 `95084e7b…d475`(开/关两态);自举 `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 42;**已跑集**(17 selfhost + 5 bootstrap + `selftest-types`)rc=0。 +- **评审 Critical 修复(2026-09-11 终审;本条目所在提交)**:**桥接缓存 `g_shadow_map` 无重置钩子**——本批起判定路径**无条件**调 `sh_term_of_ti`(此前仅 `--type-shadow` 下),而 `init_types()` 只清类型表与 `named_dedup` 侧表、**未清桥接缓存** ⇒ 长驻进程(corelsp 每请求 `reset_frontend_state → check_all`)里行号复用 + 命中即返回 = **两个不同类型被判等**(静默漏报;评审实证 corelsp 同 URI 两次 `didOpen` 换元素型 → 第二次 diagnostics=0)。修法 = `sh_map_reset()`(cap/entries/hits 归零)经 `init_types()` 调用(照 `named_dedup_reset` 先例);判据 = `selftest-types` `t3c.*` 两例(计数 + 行为双断;**突变控制**:副本去掉该调用 → 93/95、两例 FAIL)+ LSP 双请求用例 `test_double_open_bridge_cache_reset`(修复前实测 RED:V2 diagnostics=0;修复后 1 条 `Assignment type mismatch`)+ ELF/`.ccr`/自举逐项复验。 +- **未覆盖面(显式登记)**:① 站点 1/2/4/6 语料**零命中**(同 P1)→「对拍差异归零」效力范围 = 赋值/返回/if 面(站点 5/7/8),泛型实参/应用基型/兜底等价/热补丁四面的证据来自定向探针而非语料(P1 交接硬性要求:报差异数须同报站点覆盖);② 语料 `replace_unknown = 0` 说明语料未触达引擎未知面——未知面证据来自探针与 C-1(`G<[int;3]>` 等 **5 处** =1:n5/n6 + `c1_genapply_same/diff/diff2`)。 +- **P5 继承项(须在删 legacy 前清零/落地)**:① ~~**`type_equal_legacy` 删除归 P5**——现为「对拍对照物 + unknown/桥接回落实现」双用~~(**已删:P5 Task 4 / #47**——引擎唯一权威 + 回落计数随删;残部 = 影子层,已由 P5 Task 5 / #48 下线);② **unknown 清零的前提 = 引擎命名展开**(`TYP_GENERIC_APPLY`/`AK_NAMED` 目前不展开 → 引擎 -1 → 回落 legacy;命名类型/泛型应用面的等价判定**仍由 legacy 承担**)——引擎命名展开落地前 unknown 清零不可达,且删 legacy 会把「回落」变「未判定」;**状态更新(R2 P5 Task 3)**:清零已由**命名面判定化**(身份链,**不接**结构性展开——P3a 反证口径保持)达成:语料 + 探针可判定域回落计数 = 0(见 #46);残留 -1 = 不变槽面(停条件②,单独裁决;**已由 P5 Task 3b / #46 收口**);③ ~~站点 6 挂点去留(P1 终审 M-1 交接)随 P5 站点面清理一并办~~(**已办:P5 Task 4 / #47**——分支与挂点删除(三面不可达证明 + 语料 `assign-binary=0`);站点 id 不重编号);④ ~~**R2 P2b Task 2 的 `sh_native_ak_legacy`/`sh_base_ak_legacy` 删除归 P5**~~(**已删:P5 Task 4 / #47**——三例对拍改钉**冻结期望表** `ts_t4_ak_expected`/`ts_t4_native_ak_expected`,生产文件里不再有第二份实现)。 +- **同族清偿(P5 前必办)**:**桥接缓存(`g_shadow_map` 及其 `g_shadow_*` 诊断计数)+ `type_equal_legacy` 同属「调试/对拍结构进了生产判定路径」一族**——P1 时两者皆仅 `--type-shadow` 下活跃,P2a 起 `sh_term_of_ti` 与 legacy 回落都在**默认路径**上。清偿方向 = 引擎覆盖面补齐后(命名展开 + 长度面入库)删除 legacy 回落、桥接层退化为纯调试通道(或随影子层一并下线),届时 `g_shadow_map` 的失效钩子、`replace_*` 计数、`--verify-named-dedup`/`--type-shadow` 两个隐藏通道一并清理。**T7 注(R2 P5 收官)**:本段清偿方向**已全部兑现**——legacy 回落与 `replace_*` 计数删除(#47)、桥接层剥调试计数并改名 `g_term_map`(桥接缓存 = 生产面保留;#48)、`--type-shadow`/`--type-shadow-dump` 下线(#48);`--verify-named-dedup` 经裁决**保留**(非影子面 + 活消费者 `test_named_dedup.py`,偏离登记见 #48)。 +- **覆盖清单(Task 4 挂账 → 由终审转入本条)**:F2 约束的 **4/6 下钻位(REF / SLICE 元素位)用例已补(`f2.ref_elem_reject` / `f2.slice_elem_reject`)但缺「突变控制」**(未验证把该位实现改坏时用例真的会红——其余下钻位同此:现有用例只断「同长 → 1 ∧ 异长 → 0」,未见证伪实验)。补法 = 逐位做一次「删/改该位下钻 → 用例必须 FAIL」的突变实验并记档(同 `t3c.*` 与 LSP 用例的负控做法)。 +- **P2b 待办(同 spec §9 P2 行)**:`infer_expr` 公理区 → `iface_ops` 查表接线;`res_type_node` 两表合一。→ ~~**2026-09-11 已落地**,见 #30~~(P2b 未交付面——`iface_satisfies` 等——已转入 P3 交接包:findings §12 + P3 计划附录 A)。 + +### 25. F5:`EXPR_TUPLE` 元素连续槽位假设对**复合表达式元素**不成立 → 元组字段类型错录(假拒 + soundness 漏放;含 `opt.cr:177` 恒空转登记) +- **✅ 已修(2026-09-11,提交 `0390f0f4`;工作区报告 `.superpowers/sdd/fix-tuple25-report.md`)**: + parser 元组分支改**两趟**(先全部解析元素值、后统建连续 wrapper;`EXPR_TUPLE` 契约 a=首 wrapper / b=个数,wrapper.a=元素值节点)。**TODO 原建议「照 struct 先例(交错 wrapper)」经实测不成立**——交错下相邻 wrapper 间插着下一值子树(`P{a:11,b:g()}` 误返 0),故不采用。消费点 4 处同步解引用(checker/ir_gen/monomorph/opt;`opt.cr` 与 `ir_gen ast_patch_node` 两处 a/b 槽约定一并校正,经核均无调用者 = 死码,产物零影响)。 + **第二根因**:checker `EXPR_TUPLE` 元素类型**两趟落盘**(`data_start` 曾在推断循环前取 ⇒ 嵌套元素推断向 `g_gen_apply_data` 追加数据顶开后续落点、extra 错位 ⇒ 嵌套元组异型互赋静默通过)。回归 = `tests/selfhost/test_tuple_slots.py` 14 例(3 主判据 + 边界 3 + 嵌套 4 + IR/ELF 冒烟)。 + **同族已修(另立条目 #28,2026-09-11)**:struct 字面量复合字段值误编译(`P{a:11,b:g()}` 返 0)、ARRAY 复合元素 `[[1,2],[3,4]]` vs `[5,6]` 静默接受、monomorph 数组克隆实参错位——三处与元组同属「值节点连续槽位」契约,修法同款两趟 wrapper。 +- **现象(Task 3 评审加码定位,pre-existing;建议单开任务)**:元组字面量 `(e0, e1, …)` 的元素类型按「`a + 0..a+ec-1` 连续槽位」读取,但**复合表达式元素自身占多个 AST 槽**(如 `[1,2,3]` 的数组节点在其 3 个子节点**之后**)→ 后续元素的槽位推断全错位。两类实证(`/tmp/r2p2t3/probes`): + - ① **假拒绝(类型面)**:`(1,a)` vs `(1,[1,2,3])`(**语义同型** `(int,[int;3])`)→ 误报 `error[TA01]`; + - ② **soundness 漏放**:`(1,[1,2,3])` vs `(1,h())`(`h()->int`,**异型互赋**)→ **静默接受**; + - ③ N 面漏检(Task 3 §6.1):`(int,[int;3])` 字段位的异长静默通过(元素为复合表达式时该位无检查)。 + - 反证边界:`([1,2,3],1)`(数组在**首位**)正确——首位 = `parse_expr` 返回值,位置天然正确;`(1,a)`(单节点元素)正确。 +- **root cause(实读)**:`parser.cr:487` 元组分支只记 `ef = parse_expr()` 首元素节点 + `ec` 元素**个数**,注释「stored in consecutive g_ast slots」对复合元素不成立;**对照物 = struct 字面量有 wrapper**(`parser.cr:459` 每字段值后 `ast_alloc(0, fv, …)` 建转发节点 → 字段恒连续),元组分支缺同款。 +- **消费点(4 处同假设,须一并复核)**:`checker.cr:2639`(`infer_expr` 的 `EXPR_TUPLE` 分支)、`ir_gen.cr:2207`(`gen_expr` 的 `EXPR_TUPLE`)、`monomorph.cr:296`(注释原文「elements are consecutive」)、**`opt.cr:177`**(`EXPR_ARRAY || EXPR_TUPLE` 分支——读 `ast_b`/`ast_c` 而约定是 `a`/`b`(a=首元素槽、b=个数)⇒ 数组/元组元素优化**恒空转**(`ac` 取到 0 → 循环体从不执行);**无正确性影响**,但同属「a/b 槽约定被写错」家族,修 F5 时一并校正)。 +- **修复方向**:补 wrapper(照 struct 先例,改动最小)或改元素存储为显式槽位表;须同步 4 个消费点。 +- **判据(建议)**:修 parser 后 3 条新探针(异长拒 / 同型接受 / 异型拒)+ `selftest-types` 增例 + 4 消费点复核 + ELF/`.ccr` 逐字节(**注意:改 AST 布局可能改变产物,须列明并逐字节实测**)+ 全回归。 +- **为何未在 P2a 修**:修法动 AST 布局 + **4 个消费点**(上列),可能改变 `.ccr`/ELF 产物 → 须单独立项裁决(非「顺手改」面)。 + +### 26. 判据重定:state 边 / 纯度类改动的判定基准(2026-09-11 效应/纯度修正批 Task 3 确立——**非缺陷,登记性条目**) +- **适用面**:凡触及 state 链 / 纯度的改动——`dataflow.cr`(`df_connect_state` / `df_replay_state_chain`)、`checker.cr`(`purity_op_effect` / `compute_all_purity`)、`monomorph.cr`(实例纯度继承)、`ccr_io.cr`(EDG/NOD 序列化)等。 +- **新判据**(取代旧「`.ccr` 与旧版逐字节相同」):① **语义零变化**(解释器 + ELF 两侧行为一致);② **边集语义断言**——`.ccr` 层 `tests/selfhost/test_ccr_v7.py` 三条性质(可证纯调用⇒无 kind=1 入边 / 效应调用四类(store 体·extern·不可解析 builtin·间接调用)⇒必有 kind=1 入边 / 每函数链独立零跨函数;各带正负控)+ C 层同族 `corec selftest-purity`;③ **自举稳定**(连续两次编译产物一致:`corec2`/`corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42)。 +- **旧判据退役理由**:该类改动**必然改**产物——`.ccr` 的 **EDG 段**(kind=1 边增删)与 **NOD 邻接域**(first_edge/edge_count;节点序不变——链只加边)、`corearch --dump-objects` 通道输出、(缓存态下)`.core/cache/cir` 与二次运行产物。Task 1/2 实测:`IR_CALL_EXTERN`/`IR_SPAWN`/`IR_YIELD` 由不入链改为入链 ⇒ EDG kind=1 边 +N。**ELF 逐字节不变判据在本类改动下仍有效**(lazy 判定解耦:`ir_gen.cr` 的 lazy 与 `IR_LAZY_THUNK/FORCE` 发射面不动)——若 ELF 变,说明改动泄进发射面,须停下上报。 +- **操作约束**:比较任何 `.ccr` 产物 sha 前必须 `clean-cache`(同 #5 家族:**冷/热缓存态 `.ccr` 分歧**——2026-09-11 起「升级后旧缓存静默失效」那一半已随 #5 修复关闭[编译器身份字段],本约束由冷/热分歧面继续承担);`.cir` 缓存同源受此约束。 +- **实测锚**:Task 1(`762bd429`)/ Task 2(`c9099d73`)/ Task 3(本条目所在提交)三任务后 `test_ccr_v7.py` 的 `EXPECT_DATA 41 / EXPECT_STATE 13` **未变**——fixture(PROBE_SRC)只含可证纯调用(不入链)与 store 族,无新入链 opcode ⇒ 期望表不重锁(该判断本身 = 实测后按「确无变化则不重锁」处理的记录)。 +- **R2 P4 继承与判据勘误(2026-09-13,P4 Task 7 收官;#44)**:P4 载体批(8 提交)全程按本条口径(结构性断言 + 语义零变化 + 自举稳定,不以「与旧版逐字节同」为准——T1 起旧记录值作废、T3 起 STR 增长按裁决 1 接受)。**两条勘误落纸(计划 Global Constraints + 附录 D-1,实测)**:(i) **「`.ccr` 冷/热两态逐字节同」对非可选程序本就不可达**(冷≠热 = 预存,pre-P4 二进制同病 89086/88954,本条「冷/热缓存态 `.ccr` 分歧」那一半至今未修)⇒ 该判据只对**可选面零足迹**程序成立(可选程序关 `.cir` 缓存 ⇒ 冷≡热按构造;实测 `2bfbbe2b…` 97604B 冷热同);非可选程序改为「冷/热各自确定性 + 与同源基线编译器逐字节同(同口径)」。**TYPE 段「纯函数」承诺项面成立、行面不成立**(行表热 12/冷 15 = 热路径跳过 `ir_gen` 不重建 `alloc_type` 行)。(ii) **`.ccr` 记录值有两条命令口径**(引用必须带口径):`corec ccr F -o O`(`ptr_arith` 90823B / `generics_test` 134701B)vs `corec build F -o O --static`(90966B / 134844B;`--static` 前置 `rt.cr` ⇒ +143B,`main.cr:423-425`)——两者各自确定性可重复。 +- **R2 P3a 继承(2026-09-12,P3 Task 7 收官;#34)**:本批(Task 0/1/3/4/5)对 `.ccr` 的判定**一律按本条口径执行**——T0/T1/T3 `.ccr` 逐字节不变;**T4 变**(内建 Option 串退役:STR 段 −10B、后续索引整体 −1,逐段实测);**T5 变**(`generics_test` +370B = 恰一个额外实例:`get_val[unit]` → `get_val[int]`+`get_val[string]` 反折叠,逐段实测);三任务 **ELF canary 均逐字节同** ⇒ 类型层改动未泄进发射面(本条「ELF 变须停下上报」条款得到执行);结构性断言 `test_ccr_v7` 27/27 全程绿。 + +### 27. 效应/纯度修正批落地(2026-09-11 P0 插队批——`fi_ispure` 真计算 + state 链分类表补全;**非缺陷,落地登记**) +- **落点**:`src/compiler/checker.cr`(`purity_op_effect` :2891 · `compute_all_purity` :2938 · `fi_ispure_of` :3083 · `src_func_of_ir` / `df_func_of_node`)、`src/compiler/dataflow.cr`(`df_connect_state` :146 改调单一真源 · `df_replay_state_chain` :197 · `df_state_finalize` :249)、`src/compiler/purity_selftest.cr`(新增,26 例)、`src/compiler/monomorph.cr`(实例→源侧表登记)、`src/compiler/cir_cache.cr`(版本 14→15——快照不再持久化链边)、`tests/selfhost/test_purity.py`、`tests/selfhost/test_ccr_v7.py`(24→27)。 +- **提交链**:`762bd429`(Task 1:真纯度计算 + 链迁至 IR 生成后重建)→ `c9099d73`(Task 2:分类表补全 / 单一真源)→ `c7ca4251`(Task 3:判据重定)→ 收官(本条目所在提交:全量回归 + 复验 + 本回填)。 +- **修了什么(P0 症状)**:`fi_ispure` 由「乐观常量 1」(`checker.cr:1107`/`:1142` 两写入点——**有意保留**为 IR 生成期的冻结输入,见残余面①)改为全程序 IR 面真计算——保守起点(全不纯)+ 调用图不动点升纯 + 递归/SCC 保守 + 不可解析调用保守 + `unsafe` 区不纯 + 泛型(实例按自身体;源 = 实例合取)⇒ `print`/`read_file`/`chan_send`/`sched_go` 等一切可解析但不纯的调用重新入 state 链。分类表 opcode 面收敛为 `purity_op_effect` **单一真源**(D7):`IR_CALL_EXTERN`/`IR_SPAWN`/`IR_YIELD`/`IR_HOTPATCH_ROUTE`/**间接调用 `IR_DYN_DISPATCH`**/`IR_STORE_PTR`/`IR_AWAIT` 全入链。 +- **链覆盖范围(回填口径,供 spec 引用)**:函数内 = store 家族 + 上列全部效应 opcode + 一切不可解析调用 + 一切被判不纯的可解析调用;**链仍每函数重置**(`df_replay_state_chain` 链头重置与 `df_begin_func` 同语义——跨函数不连;判据域内调用节点 = 被调者效应的序代理)。**残余面**:① lazy 判定(`ir_gen.cr:1590`)**有意冻结**(本批 `ir_gen.cr` 零改动 ⇒ 生成期 `fi_ispure` 仍是乐观默认值);② 其 use_count 时序缺陷**独立未修**(保持登记于「控制流自动惰性」节)。 +- **判据(本批确立,见 #26)与收官实测(Task 4)**:全量回归 **38/38 rc=0**(7 bootstrap + 31 selfhost,含 `test_ccr_v7` 27/27 · `test_purity` · `test_compile`);`tests/suite` 语料 **20/20**(非 `*_mini*` 且非空 = 20 个)build+run rc=0;ELF canary `tests/suite/ptr_arith.cr` = `95084e7b…d475` **IDENTICAL**;语料级「旧前端(Task 1 前)+ 当前后端 vs 当前全链」**20/20 ELF 逐字节相同**(发射面零泄漏;运行 rc/stdout 同);自举两连建(`build_selfhost_native.py` ×2)corec/corearch/corelsp 产物 sha 逐一相同 + `corec2b`/`corec3b` `cmp` **IDENTICAL** + N06=0 + 冒烟 42。 +- **`.ccr` 变更面(Task 4 实测,冷缓存)**:kind=1(链)边增删为唯一语义差;**NOD 语义字段(op/dest/s1/s2/s3/tk)零差异**(节点序不变),唯 `first_edge`/`edge_count` 邻接域随动;STR/SYM/ENT/REG 四段逐字节不变。量级(旧前端→当前):`CHAIN_SRC` 303→**329**(+26)· `PROBE_SRC` 298→322(+24,但 src 域期望集合未变——Task 3)· `ptr_arith` 293→**318**(+25;Task 1 时代为 317,Task 2 的 opcode 面 +1)· 无效应程序 `fn main()->int{return 42;}` 285→309(+24——**全部**来自编译内建的 runtime/builtin 函数体,该程序自身零调用)。归因(noeffect 逐 opcode):新增入链目标 = `IR_CALL` +18 入边、`IR_STORE` +6 入边。**效应 opcode 清单唯一 = `purity_op_effect`**(新增 IR opcode 必须同步该处,否则链/纯度两判据漂移——D7 的机械保证)。 + +### 28. F5 同族:`EXPR_STRUCT` / `EXPR_STRUCTPAT` / `EXPR_ARRAY`(字面量形)的「值节点连续槽位」假设同样不成立 → 静默错误值 + `a[1][0]` SIGSEGV + soundness 漏放 +- **✅ 已修(2026-09-11,本条提交;工作区报告 `.superpowers/sdd/fix-struct2-report.md`;F5 系由 #25 发现者提出)**:parser 三分支统一**两趟**(先解析全部值进暂存表、后统建连续 wrapper)。契约 = `EXPR_STRUCT`/`EXPR_STRUCTPAT`/`EXPR_ARRAY`(字面量形) 的 **a=首 wrapper、b=个数**;wrapper(kind=`EXPR_NONE`)在 `g_ast` 中连续、`wrapper.a`=值节点。消费点 5 处同步逐 wrapper 解引用:`parser.cr:452`(struct 字面量)/ `parser.cr:607`(数组字面量)/ struct 模式分支(同款)· `checker.cr:2513`(`infer_expr` EXPR_STRUCT)/ `checker.cr:2576`(EXPR_ARRAY)· `ir_gen.cr:2087`(`gen_expr`)/ `ir_gen.cr:2112`(数组)/ `ir_gen.cr:2606`+`:2622`(`ast_patch_node`)· `monomorph.cr:282`+`:311`+`:391`(克隆改「先克隆值、后统建 wrapper」)· `opt.cr:130`+`:188`(折叠)。 +- **现象(RED 实测,修复前)**:① 静默错误值 `P{a: 11, b: g()}`(`g()->3`)→ `p.b` 得 **0**(`p.a*100+p.b` = 76≙1100,rc=0 无任何诊断);`P{a: g(), b: h()}` → `p.b` 得 0;② `[[1,2],[3,4]]` 的 `a[1]` 被读成扁平 int 3 → `a[1][0]` **SIGSEGV 139**;`[1, g(), 3]` 的 `a[1]` 得 0;③ soundness:`[[1,2],[3,4]]` 与 `[5,6]` 类型互赋**静默通过**(元素类型被错录为 int)。 +- **root cause(实读)**:`parser.cr` struct 字面量分支原文 `fv := parse_expr(); ast_alloc(0, fv, …)` 逐值后随建 wrapper——值节点是复合表达式(调用 / 嵌套字面量 / 下标)时子树自占多槽、夹在相邻 wrapper 之间 ⇒ 第 2 个起槽位整体错位,消费者读到值节点的**子节点**。数组/模式分支同形(数组更甚:连 `[T; N]` 类型形与字面量形共用 `EXPR_ARRAY`,靠 b=0 区分)。**#25 的元组修复即为模板,本条目 = 同契约补齐最后三处实例**。 +- **附带修正(同族、原恒空转/旧约定残留)**:`opt.cr` 的 EXPR_STRUCT/EXPR_ARRAY 分支直接对 wrapper 递归,而 wrapper 的 `EXPR_NONE` 在该函数无分支 ⇒ 字段值/元素**从不被常量折叠**(静默空转);`ir_gen.cr` `ast_patch_node` 的 EXPR_STRUCT 分支同为对 wrapper 空转(泛型实例体内 struct 字面量字段中的方法调用名得不到替换);`[v; N]` 重复形由「浅拷贝根节点 N−1 份」改为「共享同一值节点 + N 个 wrapper」(语义等价,不再复制多槽子树);`monomorph.cr` EXPR_STRUCT/EXPR_STRUCTPAT 旧 `gen_clone_consecutive(b, c * 2)` 为「2 节点/字段」旧约定残留(对现 1 wrapper/字段布局**多克隆一倍**)→ 一并改两趟。 +- **判据(实测)**:`selftest-types` 95/95 · `test_compile` PASS · `test_purity` PASS · `test_tuple_slots` 14/14(未回归)· `test_ccr_v7` 27/27 · 新增 `tests/selfhost/test_agg_slots.py` **13/13**(值 9 例 build+run 退出码 = main 返回值 & 0xFF:struct 主判据 2 + 三字段中位 + 嵌套 struct + 单槽反证边界 + 泛型实例体克隆 + 数组中位 / 嵌套数组 / `[v;N]`;类型 4 例:嵌套 vs 扁平拒 / 元素数不符拒 / 同形收 / `[T;N]` 类型形控制)+ `src/ci/run.sh` selfhost-tests 挂钩;ELF canary `tests/suite/ptr_arith.cr` = `95084e7b…d475` **IDENTICAL**(该语料**含**数组字面量 ⇒ 数组侧的「AST 布局零泄漏」为实测证据,非同 #25 的「语料无该构造」情形);`.ccr` sha 与前序状态相同(`ecd7a9df…d29d`,struct 修复后 → 数组修复后未变)。 +- **本族剩余面(发现即登记,未修)**:① **struct 字面量字段名被丢弃**——parser 取 `fni` 后从未写入(值按**声明位序**绑定):`P{b: 11, a: 22}` 静默得 `a=11,b=22`(rc=0 静默错值),字段名/顺序校验、缺字段均不存在;② **struct 字面量字段类型不比对声明**:`P{a: 1, b: "x"}`(b: int)、`P{a: 1}`(缺 b)、`P{a: 1, b: 3}`(b: Q 结构体)全部 rc=0 静默通过;③ **数组元素同质性不检查**:`[1, "x", 3]` rc=0;④ **struct 模式绑定未实现**(`P{a: x}` 中 `x` 报 N01 未定义——checker 对 `EXPR_STRUCTPAT` 直接返 `TI_UNIT`、ir_gen 返 -1)⇒ 模式分支的槽位修复为防御性,无可观测行为变化。以上四项另立条目。 + +### 29. F5 同族剩余面(#28 修复时发现即登记,2026-09-11——struct/数组字面量的「名 / 型 / 同质性」三校验全缺 + struct 模式绑定未实现) +- **✅ ①②③ 已修(2026-09-11;工作区报告 `.superpowers/sdd/fix-agg29-report.md`)**: + · **① 名字绑定** —— parser 把字段名 idx 写入 **wrapper.b**(EXPR_STRUCT 契约:`wrapper.a`=值节点、`wrapper.b`=名字 idx,**-1 = 无名字信息 → 位序回落**;与值并列的平行名字表,两趟结构不变、仍无交错分配)。字段值**按名字绑定**(与 Python bootstrap 的 `gen_struct_lit` 同语义):checker 解出「字面量字段 i → 声明下标 j」(`struct_field_index_by_name`),ir_gen 同一解算落 `IR_STORE_FIELD`,求值顺序仍为**源序**(实测 `P{b:nx(), a:nx()}` = 201 = b 先求值)。monomorph 克隆**保留 wrapper.b**(丢名字 ⇒ 实例体回落位序 = 静默错值,与 #29 同类)。 + · **② 三校验 + 类型比对** —— 未知字段 `TS02` / 重复字段 `TS04` / 缺字段 `TS01`(只报首个)/ 字段类型 vs 声明 `TS03`(判定走 `type_compat_strict` + `diag_type_incompatible`,与赋值同一引擎,站点 9)。**两侧任一含未实例化泛型参数(`TYP_GENERIC_PARAM`,含嵌套)→ 跳过比对**(不假拒:泛型函数体 `fn f[T](x: T){ p := P{a:1,b:x}; }` 的值类型是 T);字段声明类型提及结构体泛型参数(`T` / `[T;3]` / `Box[T]`)→ 跳过比对、走参数绑定(`unify_types`)。**附带修正**:泛型结构体字面量的 `TYP_GENERIC_APPLY` 实参改按**参数声明序**取(旧代码按字面量字段序 ⇒ 双参数且字段序 ≠ 参数序时实参错位)。 + · **③ 元素同质性** —— 元素类型取**首**元素(旧代码逐个覆盖 = 随**末**元素漂移),后续逐个比对 `TK02`(站点 10),同样带泛型参数跳过门。 + · **硬错误门(非静默的必要条件)**:`TS01-04` + `TK02` 入 `run_frontend` 硬错误名单(与 R002/TK05/TK06 同类)——修复前这些字面量即便报错也照常产出二进制:未知/缺字段 ⇒ 字段从未写入(读垃圾值)、错型 ⇒ 按错宽度存、异质数组 ⇒ 类型漂移(soundness 漏放)。现 rc=1 且**无产物**。 +- **④ struct 模式绑定仍未实现**(独立特性,需 checker + ir_gen 联动;`EXPR_STRUCTPAT` 槽位契约已由 #28 对齐)——本任务范围外,保持原状。 +- **附带修复(发现即修,本条提交):bootstrap 后端 `return` 非块终结符** —— `bootstrap/corec/backend/x86_64_stack_asm.py` 仅在「块的最后一条指令恰是 ReturnInstr」时补 epilogue:**同块双 return**(return 后跟死代码)时两条 `mov rax, ...` 都发射、只在末尾退出 ⇒ 返回值 = **最后一个** return 的值(死代码赢,静默错值)。实测命中 `checker.cr` 的 `unify_types`(`return true; return false;` 被编成**恒返 false**)——此函数返回值此前**无人使用**(`infer_gen_call` 丢弃返回值、递归点从未触发)故长期潜伏;#29 的泛型绑定判定首次消费其返回值即暴露(`Box{val=100}` 被误判 TS03)。修复 = ReturnInstr 视为**块终结符**(发射 epilogue 后停止发射同块余下指令)。全 `build/corec.s` 扫描:命中面恰此 1 处。 +- **判据(实测,均在冷缓存下)**:`tests/selfhost/test_agg_checks.py` **17/17**(值 7:名字绑定主判据 / 源序副作用 / 三字段逆序 / 乱序+嵌套字面量 / 泛型 Pair 乱序 / 双参数字段序≠参数序 / 泛型函数体经 monomorph 克隆;类型 10:负 7(TS02+TS01 / TS04 / TS01 / TS03 int←string / TS03 Q←int / TK02 扁平 / TK02 嵌套,均验 rc≠0 + 码 + **无产物** + 定位)/ 正 3)+ `src/ci/run.sh` selfhost-tests 挂钩;`selftest-types` 95/95 · `test_compile` PASS · `test_purity` PASS · `test_agg_slots` 13/13 · `test_tuple_slots` 14/14 · `test_ccr_v7` 27/27 · selfhost 其余 7 套(impl/borrow/pointer_safety/params_limit/nested_fn/interp_parity/cache_identity/backend_bootstrap)全绿 · bootstrap 三套 29/29+4/4+3/3 · `tests/suite` 21 语料 ALL PASS(**generics_test.cr 是本次唯一被新检查拦下的现存语料**——见附带修复:它是 `Box { val = 100 }` 被误拒,属修复前潜伏的 bootstrap 误编译面,非新检查过严)· ELF canary `ptr_arith` = `95084e7b…d475` **IDENTICAL** · 三阶段自举 corec2 ≡ corec3 逐字节。 +- **`.ccr`/形状面**:名字随 wrapper 携带**不改变 IR 发射**(消费者解算后字段位与修复前同名同序写法完全一致)——ELF 逐字节 canary 与三阶段自举逐字节为证。 + +### 30. R2 P2b 落地(2026-09-11——`infer_expr` 公理区查表接线 + 双份 TY→TI 合一;落点 / 未覆盖面 / P3 交接,非缺陷) +- **落点**:新建 `src/compiler/iface_registry.cr`(13 条本质条目表 = 8 原生 + product/sequence/ref/ptr/named,40B/条 × 5 字段 `{ak, ti_row, name_ni, lit_code, ops}`,静态常量、不 alloc `g_types` 行;`iface_*` 查询 API 11 个 + `ty_code_to_ti` 单表)、`src/compiler/checker.cr`(字面量定型 5 处 `:1906-1911` ← `iface_lit_ti`;算术门 ANY `:1969` / 逻辑门 ALL `:1984` / `if` ONE `:2298` / `while` ONE `:2411` ← `iface_permits`;索引兜底拒绝 `:2678` ← `IP_INDEX`;8 个 TY→TI 站点 ← `ty_code_to_ti`,含 `res_type_node`/`res_call_type` 两表合一)、`src/compiler/ty_shadow.cr`(`sh_native_ak`/`sh_base_ak` 委托 `iface_by_ty_code` 单源化,legacy 拷贝保留为对照物)、`src/compiler/globals.cr`(`g_iface_*` + `IP_*` 位下标常量——**必须落 globals.cr**:变量/常量跨文件按声明序可见)、`type_selftest.cr`(**95 → 212 例**)、`tests/selfhost/test_iface_ops.py`(新建 **76 例**)、`src/ci/run.sh`(selfhost-tests 新挂 `test_type_engine.py`(`selftest-types` 212 例——P0/P1/P2a 一路漏挂)与 `test_iface_ops.py`)。计划 = `docs/superpowers/plans/2026-09-11-r2-p2b-iface-ops.md`;报告六份 = `.superpowers/sdd/p2b-task{1..6}-report.md` + 收官报告。 +- **提交链**:`7f520dce`(T1 建层)→ `31143cd0`(T2 桥接单源化)→ `9b15fd2a`(测试挂 CI + 计划回填)→ `6d2790cd`(T3 字面量定型)→ `a99ee825`(T4 操作许可)→ `7145eea5`(T5 容器面)→ `30545da7`(T6 双表合一)→ 收官(本条目所在提交)。 +- **口径兑现(保语义 = 零行为变化,硬判据)**:表的每格 = 现状逐格转录(格注现状 `file:line`)⇒ **收紧清单 = 空**(「旧接受 → 新拒绝」零条,反向亦零条)。收官实测:全量回归 **46 套件 rc=0**(7 bootstrap + 39 selfhost;计划记 38 = 7+31,差 8 为期间新增套件)+ `selftest-types` **212/212** + `test_iface_ops` **76/76**;ELF canary `95084e7b…d475` **IDENTICAL**(clean-cache;**开/关影子两态逐字节同**);`.ccr` 4 档逐字节同(`ecd7a9df…`/`891377232b…`/`35cf0f26…`/`dfb82d2b…`,与 T1~T6 逐字一致);**同源双编译器对拍**(旧二进制 × 新源 == 新二进制 × 新源):21 档语料 check 输出 + 自源 `check src/compiler` + 32 档影子通道(decisions=2826 agree=2826,全计数 0)逐字节同;自举 `corec2==corec3` IDENTICAL(`c201916d…a7d9`)+ **N06=0**(两段 build log)+ `--help` rc=1 + 冒烟 42;`build/corec` 重建后 sha 复原(`8f69e346…858b`,构建确定性)。自源仅既有 2 条 TF01 误报(`lits_copy:9383` / `ty_memo_slot_no_grow:9498`,`ty_shadow.cr:143-146` 已登记;两侧编译器输出逐字节同 ⇒ 非本批引入)。(→ **2026-09-13 TF01 收口已修**,见 #40:`lits_copy` = 真·类型洗白(返回型改 `string`),`ty_memo_slot_no_grow` = checker 落空分析缺失;`ty_shadow.cr` 注释已同批改写。) +- **P3 交接(本批最有价值的产出)**:**未接线站点台账 31 条**(T4 的 14 + T5 的 17:结果规则 / 无拒绝路径 / 谓词非类级位,逐条实测理由)+ **P3 裁决点 10 项**(`iface_satisfies` 未交付 / `name_ni=-1`(.ccr STR 段约束)/ `TY_DEX_S` 不入表 / `never` 调用位点=unit 而类型位点=never / 「全许可列」11 位×13 类 / 字段·转换面无拒绝路径 / dyn 逐行谓词 + 位图 64 上限 / 索引侧缺「须 int」位 / 枚举域无 `AK_SUM` 条目 / 性能面)落 `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md` **附录 A**;现状九条宽松面事实表落 `docs/superpowers/specs/2026-09-10-type-shadow-findings.md` **§12**。 +- **未覆盖面(显式登记)**:`iface_satisfies`(零调用者,P3 交付);`iface_size`/`iface_align`(归 hw-map);横切轴/用户轴条目;`AK_SUM`/`AK_FN` 无 checker 对应;dyn 位图 64 上限;`name_ni=-1`;18+18+8 个候选站点中**仅 5 个门 + 5 处字面量定型 + 8 个映射站点**(另加桥接单源化)可零行为变化接线,其余 31 个逐条实测不可接线(理由见附录 A.2)。 +- **P5 继承项(并入 #24 清单;~~已落地~~)**:~~`sh_native_ak_legacy`/`sh_base_ak_legacy` 删除~~ + ~~`type_equal_legacy` 删除~~(**均已删:P5 Task 4 / #47**)+ ~~unknown 清零~~(**已达成:P5 Task 3/3b,见 #46**)。 + +### 31. CI 挂点缺口:`selfhost-tests` 挂 15/39、`bootstrap-tests` 挂 3/7(2026-09-11 R2 P2b 阶段评审登记——非缺陷,覆盖面缺口) +- **现状**:`src/ci/run.sh` 的 `selfhost-tests` 只跑 15/39 个 selfhost 套件、`bootstrap-tests` 只跑 3/7 个 bootstrap 套件;未挂套件含前序阶段点名的守卫——`tests/selfhost/test_lsp.py`(桥接缓存重置,R2 P2a 评审 Critical 的回归钉)、`test_named_dedup.py`、`test_slice_bounds.py`。 +- **影响**:不削弱 P2b 批自身保证(其两个新测试文件已挂 `run.sh:71-72`),但上述守卫此后回归 CI 捕获不到。 +- **建议修法**:一次性挂齐(评估耗时后决定全集或分批)。**2026-09-13 进展(P5 T5 / #48)**:`test_named_dedup.py` 已挂进 `selfhost-tests`(点名四守卫之一;另三:`test_backend_bootstrap.py` 早已挂、`test_lsp.py`/`test_slice_bounds.py` 仍未挂)。 +- **2026-09-12 复测(P3a 收官;#34)**:`selfhost-tests` 挂 **18/42**(期间新增 3 档——`test_match_exhaust` / `test_optional` / `test_generic_constr`——均已挂钩)、`bootstrap-tests` 仍 **3/7**;缺口未变。收官全量枚举(42+7 逐档 rc)仅在收官运行器里执行一次,**未**落 CI。 +- **2026-09-14 复测(P5 收官;#49)**:`selfhost-tests` 挂 **28/51**(P5 期间新增挂钩 3 档:`test_named_face` / `test_let_check` / `test_named_dedup`;其余为前批挂点)、`bootstrap-tests` 仍 **3/7**;**未挂守卫** = `test_lsp.py` / `test_slice_bounds.py`(+ 其余按耗时未挂的档)。缺口缩小但未闭合 ⇒ 保留本条。**兜底口径**:P5 起每批收官以**全枚举 58/58**(逐档 rc)实测,不依赖 CI 挂点覆盖面。 +- **⚠ 缺口的实际代价(2026-09-12 实证,#34)**:P3 Task 5 的 monomorph 迁出只改了 concat 面清单(`build_selfhost_native.py`),漏改 project-mode 清单(`src/targets/x86_64-linux/_import.cr` 的 `import monomorph`)⇒ project-mode corearch 构建 **33×error[N06] 静默未定义**(rc=0 + 产物照出),**该缺陷在整个 P3a 期间不可见**(5 个任务的回归面均未跑到:`test_backend_bootstrap.py` 正是唯一守卫,而未挂 CI);收官全量枚举首次暴露(rc=1)。⇒ 本条从「覆盖面缺口」升级为「已有一次真实漏检」;挂齐建议的优先级相应上调(至少把 `test_backend_bootstrap.py` / `test_lsp.py` / `test_named_dedup.py` / `test_slice_bounds.py` 四个点名的守卫先挂)。 + +### 32. `EXPR_LET` 站点**无任何兼容检查**(2026-09-11 R2 P3 Task 1 实测发现 → Task 4 登记——既有洞,建议专批) +- **✅ 已修(2026-09-13,R2 P5 Task 6;报告 `.superpowers/sdd/p5-task6-report.md`)**: + - **修法**:新判定函数 `checker.cr::check_let_annot_compat`(与 `type_compat_strict` / `diag_type_incompatible` 同族),两个调用点共用——① `infer_expr` 的 `EXPR_LET` 分支(局部,本站点);② `check_global_let`(**全局初始化器**——原登记面 `:1614-1620` 的注册趟同形缺口,本任务**显式划界覆盖**:全局符号同样在注册趟取注解行 ⇒ 不查同样静默错产物)。参序 =(源 = `val_ti`,目标 = 注解行)——Task 1 §3.1 归一。码 = **TA02**(`EC_TA_DECL`,error-codes.md 既有槽位「变量声明类型与初始值不符」,修复前**定义零 raise**;复用而非新码)+ 入 `main.cr` 硬名单 ⇒ `build` 亦拒绝。 + - **豁免四条**(各附理由,见函数头注):无注解 / `: .` / `: auto` / 无初值(无契约可核)· 注解 `dyn`(按值追踪;照赋值位点)· 值 `TI_NEVER`(底部 + **错误标记=诊断级联抑制**:`x : int = nosuchname` 只发 N01 不叠 TA02)· 注解为泛型形参(声明期不可验证;照返回位点 `return 5` 于 `-> T` 体今天即 rc=0 的既有策略,套件 `let_generic_param_annotation_not_judged` 同策略钉住)。 + - **RED→GREEN(三形态 + 同族)**:`x : int = "s"` / `x : [int;3] = s`(切片)/ `x : [int;4] = [1,2,3]` / `x : int? = 5; y : int = x` 全部 check rc=0 零诊断(冻结基线 `/tmp/p5t0/base/corec` 与本批起点 `2342fd03…` 同值 = 预存)⇒ 现全部 TA02 定位硬错 + `build` rc=1 **无产物**(拒绝早于 lower/写 .ccr/ELF)。 + - **report-only 全语料(开门依据)**:72 档 runner `check` **rc 34×0/38×1 与 pre-P5 基线逐档同 + 诊断正文逐条同(归一化)**;`check src/compiler` rc=0 零诊断;**58 套件全枚举**(tests/selfhost 内联源 + tests/bootstrap 全部经 corec 编译)命中面 = **1 例**(`test_named_face.py::named.in_container_same` 的 `p : *NA = 0;`)⇒ 已按「与赋值位点既有语义对齐」修正为无初值声明(`*T ← int/null` 在**赋值位点**早已 TA01 拒绝,两侧同证;非新增语义,见报告 §收紧台账)。 + - **判据(全部本实例实跑,二进制 `f737e26b…`)**:新套件 `tests/selfhost/test_let_check.py` **24/24**(含正控 13 + 级联抑制 2;挂 `src/ci/run.sh` selfhost-tests)· `selftest-types` **404/404** · `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · 五 CI job 全 rc=0 · 全枚举 **58/58** · **ELF canary `95084e7b…d475` IDENTICAL** · `.ccr` 两套口径四条与 T5 记录**逐字节同**(`ptr_arith` `fb4a3b59…`/`592afa31…`;`generics_test` `ddec1ce6…`/`cd2af565…`)· 自举链 `corec2==corec3` cmp IDENTICAL(`5559beef…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · 冻结基线同源对拍 72 档 **rc + 诊断正文零差异** · **突变控制 4 条**逐条单点转红(局部调用点移除 → 15/24;豁免④移除 → 23/24;硬名单条目移除 → 20/24「静默面复活」;全局面调用点移除 → 23/24)并**精确回滚**(源 sha + 三二进制 sha 复原)。 + - **未覆盖 / 登记**:`-> never` 函数的**调用**被推断为 unit(非 never;`return boom()` 今天即 TF01)⇒ `x : int = boom()` 新增 TA02——与邻站现状一致、非新类,全语料零 `-> never`;`EXPR_FOR` iterable 接线(附录 E-6)与 **#20(F3 调用位点)** 不并入本任务(本任务只落声明位点;#20 仍开放,见其条目)。 + +### 35. 前端枚举表**写入侧无护栏**:≥17 变体 / ≥17 载荷类型越界写(2026-09-12 R2 P3a 阶段评审登记——**#8 同族**;代码级定位 + check/build 两面实测) +- **✅ 已修(2026-09-13,R2 P4 Task 6;工作区报告 `.superpowers/sdd/p4-task6-report.md`)**: + 根因 = `EnumVariant` 槽区(16 槽 × `OFF_EV_SIZE=272`)与 `StructInfo` 字段槽区(16 槽,**同族第三处**,本轮 RED 步实测 `check` rc=0 静默)是**定长内嵌槽区**——其后紧跟自身 count/generic 槽,再往后是**邻记录**(同一 buffer),而 parser 的写入循环对 `vc`/`tc`/`fc` 零上限闸。 + **危害形态(首次构造,收口本条「未做」项;二进制 = 修复前 `6afbd333`,三档探针 + 突变对照)**:(a) 17 变体声明 `check` **rc=0 零诊断**;(b) **该枚举上的 match 穷尽性判定被静默禁用**——第 17 变体名槽与 `variant_count` 同址(4360),count 后写胜出 ⇒ 第 17 名槽存的是 count 值 ⇒ `sh_variant_term` 的「名→变体项」查表失败 ⇒ 域不可展开 ⇒ 三态回落 **-1「不判」** ⇒ **真·非穷尽 match 也 rc=0 零诊断**(对照:16 变体同形必报 TM03——同探针实测);(c) 第 17 变体**不可构造**:`V16()` ⇒ 假诊断「Undefined enum constructor 'V16'」;(d) 17 字段结构体 / 17 载荷类型同族(载荷第 17 槽写 = `OFF_EV_TYPE_COUNT`、第 17 载荷**节点**写 = 下一变体槽首 `OFF_EV_SIZE`)。越界写多数落在 `grow_enums`/`grow_structs` 的扩容余量内 ⇒ **无 SIGSEGV 可观测**——危害 = **表污染 + 判定静默**(不是崩溃),故 `check` 面全静默。 + 修复 = ① parser 三处写入点跳过超限槽(计数保持真值,照 P020 的 `add_func(pc)` 先例)+ 末尾 `error[P022]`(枚举变体/载荷)/`error[P023]`(结构体字段)**定位硬错**——由 **parse 阶段诊断闸**(`main.cr` 的 `[3/5] parse...` 之后 `g_diag_count > 0 ⇒ rc=1`,与 P019/P020/P021 同款)拒绝;实测 **hard 名单条目对本族冗余**(删条目零行为差异 ⇒ 未加)⇒ 拒绝发生在**前端**,不进入 lower/写 `.ccr`/ELF(阶段断言入回归用例);② `dyn_arr.cr` **唯一受护写点**(`ei_set_variant_name/type/type_count/type_node` + `si_set_field_name/type/type_node`,照 `fi_set_param_type` 先例)+ **读护栏**(`vi ≥ MAX ⇒ 名字/类型码/类型节点 -1、计数 0`;count 保持真值使 `.ccr` 读回闸仍响亮)。 + 回归 = `tests/selfhost/test_enum_limit.py`(13 例:16 边界三面正控 + 17 变体/载荷/字段 × check+build 拒绝 + 无产物 + 「拒绝早于 lower/写 .ccr/ELF」阶段断言)+ `src/ci/run.sh` 挂钩。 + 判据(Task 6 实测)= `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · `selftest-types` 355/355 · **ELF canary IDENTICAL** · **`.ccr` 与修复前编译器逐字节同**(`ptr_arith` `592afa31…` 90966B / `generics_test` `ec8413ec…` 134844B——同源双编译器对拍:修复前二进制 `6afbd333` × 修复后 `5d2b15ad`)· 全枚举 55/55 · 自举链 · 突变控制(删 3 条诊断 ⇒ **8 例红 + 静默面复活**(`check` rc=0);复位 ⇒ 二进制逐字节复原 `5d2b15ad`)。 + **未解除(评估见 Task 6 报告 §解除评估)**:三上限仍为 16;`MAX_GENERICS=4` 面不属本类(写入侧有界,超限 = parse 期硬报 P01 desync,无静默接受面);`MAX_IFACE_METHODS/PARAMS` 已有 P13/P11 硬错护栏(本轮复测成立)。 +- **机制(代码级逐行核对,2026-09-12)**:`parser.cr` 枚举分支的变体循环(`:1672` 变体名 / `:1679` 载荷裸码 / `:1683` 载荷节点 / `:1690` 载荷计数;`:1694` 收尾写 variant_count)对 `vc`/`tc` **无任何上限闸**。布局(`dyn_arr.cr:158-164`,T4 扩表后):`OFF_EI_VARIANTS=8` · `OFF_EV_SIZE=272`(name 8 + types[16] 128 + type_count 8 + type_nodes[16] 128)· `OFF_EI_VARIANT_COUNT=4360`(= 8+16×272,**恰 16 槽**)· `ESZ_ENUMINFO=4408`。**第 17 个变体**(0-based 下标 16)槽起点 = **4360 = `OFF_EI_VARIANT_COUNT` 自身**(先覆盖 variant_count),槽尾 4632 ⇒ 越过记录尾 **224B**(踩下一条枚举记录头部;分配按记录数 `grow_enums` = `nc*ESZ_ENUMINFO` ⇒ 溢出者为缓冲区最后一条记录时越出分配)。载荷侧同构:第 17 个载荷类型写 `OFF_EV_TYPES+16×8 = 136 = OFF_EV_TYPE_COUNT`(并踩 type_nodes 槽)。 +- **两面实测(2026-09-12;17 变体最小例 `enum E17 { V0..V16 }`,P3a 收官二进制)**:`check` = **rc=0 零诊断**(前端不读回 ⇒ **「静默」只限这一面**);`build` = **rc=1**(corearch `.ccr` 读回侧闸 `ccr_io.cr:1121` `vc > MAX_ENUM_VARIANTS` ⇒ `error: .ccr enum variant count exceeds max` + `error: invalid .ccr file`,**无产物**;载荷侧 `:1148` 同)——但**写入已在读回闸之前发生**(护栏在读回侧 ≠ 写入侧)。 +- **既有事实**:`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES`(皆 16,`ast.cr:117-118`)**只**被 `.ccr` 读回侧消费(全仓 grep;parse 侧零引用);`ty_shadow.cr:955` 注释「MAX_ENUM_VARIANTS = 16 保证不溢出」以「写入侧有闸」为前提——该前提不成立。 +- **未做(如实登记)**:越界写的**运行期危害形态**(跨记录污染 / 越 buffer 的具体破坏)未单独构造复现——本轮只测两面 rc + 常量/写点逐行核对;危害面排查可伴随修复批做。 +- **修复方向(照 #8 先例)**:①parser 写入点护栏(`vc >= MAX_ENUM_VARIANTS` / `tc >= MAX_VARIANT_TYPES` ⇒ `P0xx` 硬错 rc=1,绝不静默;唯一写点收口照 #8 的 `fi_param_type`/`fi_set_param_type` 式);②访问器侧护栏(`ei_variant_*` 读写加范围闸);③**先 report-only** 全语料(当前 72 档零命中)。**同族**:MAX_GENERICS=4(#33 登记②)、#8(16 槽形参区)——统一「先加护栏、再评估解除」。 +- **关联**:计划附录 B.4-7(登记面;「静默」措辞已按本条两面实测修正)+ B.7(一等条目:可选表示缺口);出处 = `.superpowers/sdd/p3-task4-report.md` §7-⑥。 + +### 36. R2 P3b Task 0 落地(2026-09-12——`iface_satisfies` 契约交付:轴分派/三态/首个消费者;**P3b 唯一阻塞解除**,含未覆盖面登记) +- **交付面(P2b 交接契约 ① 的落地;计划 = `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md` 附录 B.1)**:`iface_satisfies(t_ti, iface_ni)`(`src/compiler/type_engine.cr`)= **原生/横切/用户三类同入口**,三态 1/0/-1 + 每查询预算隔离(照 `type_equal_engine`/`gen_constr_satisfied`)。轴分派 = **A 横切形状名**(`iface_shape_*` 表,本批**空表** → 恒未命中;条目细化归 Task 2 Step 1)→ **C 用户接口**(`find_iface` 命中:① 形状项路由 `sh_iface_shape_term`(Task 6 落地前恒 -1)→ ② 结构谓词 `iface_user_satisfies`)→ **B 本质轴**(= P3a `gen_constr_satisfied` 引擎面逐字同口径,迁移到统一入口)。轴优先序 A > C > B(同名撞车时:形状优先;原生名 vs 接口名**保持 P3a 现状 = 接口优先**,selftest `isat.axis_order_*` 两例钉死)。 +- **首个消费者(P3a 接线点换位)**:① `gen_constr_satisfied` 首分支(函数调用点)——**1 提前返回**,0/-1 一律回落既有 `check_iface` 名拼接路径(**措辞/去重/rc 逐字未动**;0 → 新措辞的切换归 Task 6 Step 3);② **新消费者** `gen_inst_constr_satisfied`(结构/枚举实例化点)——**直取真值** ⇒ `T: I` 在实例化点**真判定**(P3a 登记面关闭)。谓词 = **与 `check_iface` 同源**(方法名在位 + 参数计数 + 返回码),域 = 命名行(`TYP_NAMED` / `TYP_GENERIC_APPLY` 基名);非命名行/dyn/泛型形参 ⇒ **-1 不判**(绝不「查不到即 0」)。判定路径**零 `str_intern`**(走 `g_methods` 三元组查名,不构造 `"T.m"` 串——`.ccr` STR 段守卫,selftest `isat.no_str_intern_on_missing` 钉死)。 +- **收紧台账(旧 → 新;全语料 72 档零命中,同源对拍旧/新逐字节同)**:结构/枚举实例化点「接口约束违反」由 **rc=0 零诊断** ⇒ **check rc=1 + `error[TG02]` + `does not satisfy interface 'I'`**(措辞与既有函数调用点路径一致;TG02 软诊断——check rc=1 / build rc=0 + 产物照出,与既有约束检查同门同码,**未新增硬门**)。逐例 = `test_iface_satisfies.py` 6 条(缺方法/计数不符/返回码不符/枚举侧/多方法半实现/第二接口);`test_generic_constr.py::struct_iface_constr_unjudged`(P3a 的「不判」钉)按台账改为 `..._violated`(rc=1)+ 新增正控 `..._present_accepted`。**未覆盖面(登记,非漏放)**:接口签名槽 = **映射层编码**(parser 两侧同取 `unpack_type`/`ast_type_val`,非原生类型节点一律塌缩为码 0 = `TY_INT`,`self`/`&self` 槽两侧均写码 0)⇒ 逐参数类型不参与判定、返回码语义 = 编码相等(`inst_encoding_limit_named_ret_pinned` 钉住现状);**解锁 = Task 6 Step 1 签名类型项化**后由形状项包含判定取代(`iface_satisfies` 轴 C ① 的路由已是切换点)。 +- **判据(详版 = `.superpowers/sdd/p3b-task0-report.md`)**:`selftest-types` **304/304**(288 → +16 `isat.*`:轴分派 4 / 三态 3 / 谓词对拍 1 / 零驻留 1 / 形状表 3 / 空接口 1 / 泛型应用 1 / 预算隔离 1)+ 新套件 `tests/selfhost/test_iface_satisfies.py` 17 例(已挂 `run.sh` selfhost-tests)+ `test_iface_ops` 76/76 · `test_generic_constr` 15/15 · `test_match_exhaust` 16/16 · `test_optional` 12/12 · `test_impl` 7/7;ELF canary 逐字节同 + 同源对拍零差异 + 影子全计数 0(站点覆盖同报)+ 突变控制(逐条咬合 + 复位后二进制逐字节复原)+ 全回归 + 自举链。 +- **P3b 剩余(未开工,阻塞已解除)**:Task 2 横切接口(形状条目细化 = 注册进本批的 `iface_shape_*` 表 + 索引/切片/迭代消费点换位)· Task 6 impl 契约(签名类型项化 + `check_iface`/`check_impl_for` 接引擎 + mangling 退役 + `sh_iface_shape_term` 建形状项)· 函数调用点的 0 → 新措辞切换(Task 6 Step 3)。 +- **关联**:计划附录 B.1(本批解除唯一阻塞项)/ B.2(收紧台账追加)/ B.4-1(`save_func_gen_constrs` 稀疏零初值——**未触发**:本批未动 `infer_gen_call` 的约束循环)/ B.4-3(命名实参 vs 原生约束 = -1,同族未覆盖面);出处 = P3b Task 0 报告。 + +### 37. R2 P3b Task 2 落地(2026-09-12——横切接口接线:六条形状项 + 索引/切片消费点换位;**行为保持**,含未覆盖面登记) +- **交付面(计划 Task 2 Step 1/3)**:① **形状条目(Step 1)**=`src/compiler/iface_registry.cr` 横切形状段六条构造器——`sh_shape_seq`(序列接口 = `⊤ₖ(AK_SEQUENCE)`)、`sh_shape_seq_ro`(只读序列 = 序列本体 ∪ `&[⊤ₖ(SEQ)]`,只读视图由 **AK_REF 协变槽**判定 = 「只读 = 协变」兑现;T1 §6-④ 登记的「只读序列形状」在视图面闭合)、`sh_shape_seq_rw`(可写序列 = 序列本体;拒绝只读视图)、`sh_shape_indexable`(可索引 = `⊤ₖ(SEQ) ∪ ⊤ₖ(STRING)`,与 IP_INDEX 许可集逐行同集)、`sh_shape_iterable`(可迭代 = 序列;字符串不宣称——迭代面无类型检查代码面证据)、`sh_shape_product`(= `⊤ₖ(AK_PRODUCT)`)。**⊤ₖ 形是硬约束**:`sequence<⊤>` 参数链在不变槽落 -1(Task 0 §1.3 勘误;`x2.fixedness_not_in_shape_judgment` 反向钉住「固定性不入形状判定」)。② **消费点换位(Step 3)**=`src/compiler/checker.cr` 索引兜底门(`iface_permits(iface_kind_of(ti), IP_INDEX)` → `iface_satisfies_term(ti, sh_shape_indexable())`)与 range 分支类别判定(`arr_kind == TYP_ARRAY` → 序列形状 ∧ **固定性位**取自序列项 b 槽 `sh_seq_fixed_len_of_ti`);结果三分支(arr→元素+F2 / slice→元素 / str→int)**原地保留**(同时决定结果类型,A.2 #16-18)。**三态纪律**:形状 -1(不可译行)**不**折算,回落既有判定(索引门回落许可表 / range 回落 kind 直比)。 +- **行为保持(本任务主判据;旧二进制 = P3b Task 0 基线 `aa54d26a` 构建)**:新套件 `tests/selfhost/test_xcut_iface.py` 12 例**两二进制逐例同**(正例三路同证 arr/slice/str 索引 + range 产视图 5 例;TK01 软诊断负例 int/bool/ptr/struct 4 例 + range 切片分支 unit 钉子 1 例;F2 字面量越界 / F11 切片界**硬错误**钉子 2 例);全语料 72 档 check 面**零差异**(rc + 诊断码集合);ELF canary `95084e7b…d475` 逐字节同;`.ccr` `54e3856b383301be…`(88943B)逐字节同 ⇒ 零 STR 段增长(形状项只进类型项 DAG,**零 `str_intern`**,`x2.no_str_intern` 钉死)。 +- **收紧/放宽台账**:**收紧 0 · 放宽 0**(本任务为换位批,无新诊断、无新硬门 ⇒ 计划 Global Constraints 第 6 条的 report-only 前置未被触发;range 分支的切片→unit 现状(A.2 #15,未接线)**保持**,其收紧 knob 仍登记)。 +- **未覆盖面(登记,非漏放)**:① 可写**视图**形状不可表达(`ref(mut=1, ⊤ₖ(SEQ))` ⇒ 不变槽遇 ⊤ 落 **-1**,`x2.rw_view_unexpressible` 钉死三态);② 形状**名字面生产路径不注册**(注册名 = 驻留 ni;初始化路径 `str_intern` 会令 `.ccr` STR 段增长 = A.3-② 硬约束 ⇒ 命名消费语法 `T: 可索引` 待命名裁决;本批消费者皆为**无名字**消费点,走 `iface_satisfies_term`);③ `可迭代` 面未接线(checker 的 `EXPR_FOR` 现状零类型检查);④ 用户接口形状项仍不可展开(`sh_iface_shape_term` 恒 -1 = Task 6 Step 1)。 +- **判据(详版 = `.superpowers/sdd/p3b-task2-report.md`)**:`selftest-types` **319/319**(304 → +15 `x2.*`:形状满足 7 + 逐行枚举等价 2 + 注册/复位 2 + 零驻留 1 + 三态 1 + 接口面 1 + 固定性判定面 1)+ 新套件 12 例挂 `run.sh`;`test_iface_ops` 76/76 · `test_iface_satisfies` 17/17 · `test_generic_constr` 15/15 · `test_match_exhaust` 16/16 · `test_optional` 12/12 · `test_impl` 7/7;全语料零差异 + 影子对拍 + 五 CI job + 更宽 selfhost 枚举 + 突变控制(注入 → 预测用例红 → 复位二进制逐字节复原);ELF/`.ccr` 逐字节同。 +- **关联**:计划附录 B.1(Task 2 条目状态更新)/ B.4-16(只读序列视图第二层协变——本批在**形状面**闭合,序列构造子自身仍不变)/ B.4-19(性能面:本批把判定面接到索引热路径 ⇒ 编译耗时实测登记);出处 = P3b Task 2 报告。 + +### 38. R2 P3b Task 6 落地(2026-09-12——impl 契约:签名类型项化 + 形状项逐成员判定 + mangling 退役;含两处裁决与未覆盖面登记) +- **交付面(计划 Task 6 Step 1/3)**:① **签名类型项化(Step 1)**=`g_ifaces` 方法条目新增类型**节点**槽(`OFF_IFM_PARAM_NODES` / `OFF_IFM_RET_NODE` / `OFF_IFM_SELF_MODE`;`ESZ_IFMETHOD` 88→168、`ESZ_IFACEINFO` 1432→2712——**不入 `.ccr` 序列化**,实测逐字节同)+ parser 写点;裸码槽**保留**(S6 站点返回型映射的值域 = 映射层编码,P2b 已显式化钉住);参数裸码槽自本批起**零读者**(登记:参数码面由节点取代)。② **满足判定接引擎 + mangling 退役(Step 3)**:方法解析改 `g_methods` 表查询(`type_has_method` / `check_iface` / `check_impl_for` / 泛型约束路径全走 `iface_find_method`;**零 `str_intern`**——旧 `type_has_method` 对缺失名会增长驻留表);`sh_iface_shape_term` 建**形状项**(方法集 = product of fn,b 槽 = 方法名);判定 = **逐成员包含**(方法名表查询 + 接收者模式显式比较 + 签名 fn 项经引擎结构比较原语 `tt_list_same`)。**为何不用整形状 `ty_sub`**(实测两条):引擎对 `AK_NAMED` 不展开(P0 未覆盖面②)⇒ 命名行实参与 product 形状恒 -1(负例会静默降级「不判」);product 的引擎比较是结构相等而非子集包含 ⇒ 带额外方法的实现方被误拒。**为何不用 `ty_sub` 做逐成员签名比较**:引擎把不变槽的「确定不同」上抛为 **-1**(`tt_list_variance_at` 分支),直接消费会让签名不符退化为「不判」= 静默通过面复活(突变 M3b 实证:8 自测例 + 3 行为例红);签名项为**规范形**(N 不入项 / 泛型应用展开 / 命名行按名)⇒ 同型恒同节点、异型恒异节点 ⇒ `tt_list_same`(0/1 全域)即正确判据。 +- **两处裁决(本批作出,均登记为语言设计面)**:① **用户轴保持结构口径**(方法集包含),**不**采纳 spec §2.3 的**名义口径**(`impl I for T` 声明即满足)——采纳会翻转既有钉死用例(`test_generic_constr.py::iface_constr_legacy_pass`、`test_iface_satisfies.py` 的固有 impl 正例 5+ 例),且属语义争议(spec §0 裁决 6:停下上报);名义化 = 语言设计决策,须维护者裁。② **形状名字面生产路径仍不注册**(Task 2 登记项延续):接口形状项按**接口自身名 ni** 建(parser 已驻留),零新串 ⇒ 无 `.ccr` STR 段增长;命名消费语法(`T: 可索引`)仍待裁决。 +- **收紧台账(旧 = Task 2 基线二进制 `/tmp/p3b2_pre_mut_corec` 逐例实测;新套件 20 例:旧 14/20 → 新 20/20)**:签名面 4 条(**参数类型**命名型不符 / **返回类型**命名型 vs 原生 / **泛型应用实参**不符 / **接收者模式**不符)由 rc=0 零诊断 ⇒ rc=1 + TG02;`test_iface_satisfies.py::inst_encoding_limit_named_ret_pinned`(编码面判「满足」)按台账改为 `..._rejected`(rc=1 + TG02)。**放宽 0**。**崩溃修复(台账外,同一任务收益)**:泛型体内方法调用(`fn f[T: I]` 的 `x.m()`)旧态 = 合成 `"T.m"` 串 + 克隆期文本替换 ⇒ 实例体调用目标悬空,**产物运行 rc=139**(build rc=0)⇒ 本批改为实例化时按具体类型**查表解析**(`CALL_FLAG_IFACE_METHOD` + monomorph 的 EXPR_CALL 克隆分支)⇒ 运行 rc=N(正确值;两例实测 7/9)。**解释器面登记**:`corec run` 的解析面不支持方法调用语句(旧二进制同 rc=1,非本批引入)⇒ 该两例走 check + ELF 两路。 +- **未覆盖面(登记,非漏放)**:① **上限保留**(`MAX_IFACE_METHODS=16` / `MAX_IFACE_METHOD_PARAMS=8`;超限 = **硬错 rc=1**,非静默截断——新套件两例钉死;解除需方法表迁侧表,按 B.4-7「先加护栏、再评估解除」口径);② **接口泛型形参**仍被 parser 丢弃(`interface I[T]` ⇒ 签名含形参行的匹配只在同一声明内成立);③ `dyn` 位图行不入签名规范形(含 dyn 的签名 ⇒ -1 不判);④ **变参签名**不可表达(⇒ -1);⑤ 签名比较走 `type_equal` 之外的 `tt_list_same`(**不变**口径);`&T` 协变等变型规则不在签名面(需要时另裁);⑥ 运行期可选表示未统一(B.7 一等条目)未动;⑦ 泛型方法调用解析失败的兜底(形参未绑定/嵌套形参/具体类型无此方法)保持原状不发明目标(登记)。 +- **判据(详版 = `.superpowers/sdd/p3b-task6-report.md`)**:`selftest-types` **333/333**(319 → +14 `ifc.*`:签名四维 + 形状项三面 + 表查询/同源/三态/零驻留)+ 新套件 `tests/selfhost/test_impl_iface.py` **20 例**(已挂 `run.sh` selfhost-tests)+ 既有套件全绿(`test_iface_ops` 76/76 · `test_iface_satisfies` 17/17(按台账改 1 例)· `test_xcut_iface` 12/12 · `test_generic_constr` 15/15 · `test_match_exhaust` 16/16 · `test_optional` 12/12 · `test_impl` 7/7);**ELF canary `95084e7b…d475` IDENTICAL + `.ccr` 逐字节同(88943B/130974B)——类型层改动零泄漏发射面**;同源对拍(72 档 check 日志 `diff -r` 空)+ 影子对拍(30628/30628 agree,全计数 0;站点覆盖同报)+ 五 CI job(`check` = 既有 2 条 TF01 误报划界)+ 更宽 selfhost 枚举 45/45 + `test_backend_bootstrap` rc=0(附录 B.6 必含档)+ 突变控制 4 条(M1 应用展开 / M2 接收者模式 / M3b `tt_list_same`↔`ty_sub` / M4 monomorph 解析——**M4 为行为级独有牙**,如实登记)→ 复位后二进制逐字节复原。 +- **关联**:计划 Task 6 + 附录 B.1(Task 6 条目)/ B.4-4(MAX_GENERICS 同族口径)/ B.4-6+B.7(可选表示一等缺口,未动);出处 = P3b Task 6 报告。 + +### 39. R2 P3b 收官(2026-09-12——三件提交链 + 全量回归 + 统一台账 + P4 交接包;**P3 全阶段收官**) +- **交付链**:`9aa1786c`(Task 0b `iface_satisfies` 契约回补——P3b 唯一阻塞解除)→ `44100ba4`(Task 2 横切接口——六条形状项 + 索引/切片消费点换位;行为保持)→ `e838f18b`(Task 6 impl 契约——签名类型项化 + 形状项逐成员判定 + mangling 退役 + 泛型体内方法调用崩溃修复)+ 本收官提交。逐件详版 = **#36/#37/#38**。 +- **全量回归(Task 7b 本轮实跑;cwd = 仓库根、`nice -n 19`、一构建一编译串行、比较前 `clean-cache`)**:五 CI job = `selfhost-tests` **rc=0**(21 套件:76/76 · 27/27 · 20/20 · 17/17 · 15/15 · 16/16 · 12/12 · 12/12 等)· `bootstrap-tests` **rc=0**(3 档 29+4+3 全 PASS)· `suite` **rc=0**(21 档 `.cr` 编译+运行全过;`*_mini*`/空 fixture 按既有规则跳过)· `full-bootstrap` **rc=0**(`corec2 == corec3` `cmp` IDENTICAL sha `03c9812fc735ec4b…` 双 2542222B + `--help` rc=1 既有约定)· `check` **rc=1 = 既有 red 划界**(`check src/compiler` 恰 **2 条 TF01 误报** `lits_copy`/`ty_memo_slot_no_grow`——check 命令按 F19 以非零反映任意诊断 ⇒ rc=1 由该既有误报产生,同源对拍两侧一致,非本批引入)【→ **2026-09-13 TF01 收口已修**(#40):`check src/compiler` **rc=0**】;全枚举 **45 selfhost + 7 bootstrap 逐档 rc=0**(含 #31 未挂 CI 档,`test_backend_bootstrap` rc=0 = 附录 B.6 必含档)。 +- **判据(本轮实跑)**:`clean-cache` → ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL**;`corec2/corec3` `cmp` IDENTICAL + **N06=0**(两构建日志各 0 命中;日志 `error[` = 2 = 上述既有 TF01)+ 冒烟 `run 'fn main()->int{return 42;}'` **42**(corec/corec2 双通道)+ `corec3 --help` rc=1;`.ccr` = P3b 三件**零变更**(`ptr_arith` 88943B `54e3856b383301be…` / `generics_test` 130974B `97be2f9e7431fde5…`,与 Task 0/2/6 记录值逐字节同);`selftest-types` **333/333**。 +- **P3b 统一台账(收紧 10 / 放宽 0 / 崩溃修复 1;详版 = 计划 Task 7b)**:**T0 6**(实例化点 `T: I` 违反 rc=0 零诊断 → rc=1 + `error[TG02]`;旧 = P3a 收官构建)· **T2 0/0**(换位批:12 例探针老/新两二进制逐例同 + 72 档 check `diff -r` 空 + 影子旧×新同值)· **T6 4**(签名四维:参数类型/返回类型/应用实参/接收者模式 → rc=1 + TG02;旧 = Task 2 基线构建)+ 崩溃修复 1(泛型体内方法调用旧产物跑 rc=139 → 正确值 7/9)。**全语料零命中** + 同源对拍零差异(T0 30271 / T2 30400 / T6 30628 判定,全计数 0,站点覆盖同报)+ 突变控制 11 条(4+3+4)逐条咬合。**TG02 = 软诊断**(check rc=1 / build rc=0 + 产物照出)——P3b 未新增硬门。 +- **P3 阶段总账(P3a + P3b)**:**合计 收紧 28 · 放宽 8 · 软诊断面 +1(TM04)· 实例名忠实化 6 · 挂死与崩溃修复 3 端**;五项目标能力(定长退役/序列接口/穷尽性·联合·可选/泛型约束/impl 契约)全部接通;`selftest-types` 95 → **333**;新套件 **6 个**(match_exhaust 16 · optional 12 · generic_constr 15 · iface_satisfies 17 · xcut_iface 12 · impl_iface 20)。判据面全程稳定:ELF canary IDENTICAL(全阶段)· 自举链 cmp/N06/冒烟(全阶段)· `.ccr` 变更面 = 仅 T4(STR −10B)与 T5(`generics_test` +370B 反折叠),P3b 三件零变更。 +- **P4 交接(新;P4 无独立计划——实查)**:计划 **附录 C** = 载体盘点(类型项 DAG/`g_ifaces` 节点槽/形状表/`g_impl_for`/`g_methods` 全在进程内存、**零 `.ccr` 序列化**;`ccr_io.cr` 全文件 `iface`/`impl_for`/`g_methods` 命中 = 0)+ **加段四面**(`CCR_SEG_COUNT`/save/load 闸与标量族/`test_ccr_v7` 结构断言;tag 7+ 预留)+ **翻转钉 7 条** + **待裁决 8 条**(形状命名语法 + STR 段增长 · #38 名义 vs 结构 · 13 条表扩列 vs 引擎侧另表 · 整形状 `ty_sub` 路由 · B.7 可选表示 · MAX_* 统一处置 · `never`/符号档 · TK 升格发射面判据)。 +- **未开工(终态;P3 未覆盖面)**:iterable 接线(`EXPR_FOR` 零类型检查,`checker.cr:2810-2821`)· 形状命名消费语法(`T: 可索引`)· 整形状 `ty_sub` 路由(实测不可用)· **#38**(用户轴名义 vs 结构,维护者裁)· B.7 可选表示统一(`Some` 臂双路径 rc=139)· **#32**(`EXPR_LET` 无检查)· **#35**(枚举写侧无护栏)· MAX_* 解除(MAX_GENERICS=4 等)。 +- **关联**:#34(P3a 落地)· #36/#37/#38(P3b 三件详版)· 计划 Task 7b + 附录 C · spec §9 P3 行(本批更新为 P3a+P3b 双 ✅);报告 = `.superpowers/sdd/p3b-task7-report.md`。 + +### 40. TF01 收口:`check src/compiler` 两条长期诊断(2026-09-13——落空分析 + `lits_copy` 类型洗白;**check 门 rc 归零 + P4 Task 2 corearch 清单步解阻**) +- **定性(探针实证,先于修复,旧二进制)**:`lits_copy` = **真·类型洗白**——`alloc` 类型模型 = `() -> string`(`checker.cr` `bi_add("alloc", TI_STR)`),声明 `-> int` 且 `return buf` ⇒ 与模型冲突;消费面(`r64(buf: string, …)`、`lits_contradictory_buf` 另一调用点传 `g_ty_lits : string`)全为字节缓冲 ⇒ 修 = 声明改 `string`(**不是**放宽 `alloc` 模型)。`ty_memo_slot_no_grow` = **checker 误报**——体以**无 break 的 loop** 收尾(只从体内 `return` 出)⇒ 永不落空,而站点 5 以「块末语句类型」判(`EXPR_LOOP` 恒 unit)⇒ 误报 TF01。 +- **修法**:`checker.cr` 新增落空分析 `stmt_cannot_fall_through` + `loop_body_has_break` + `seg_has_break`(保守:只认**不产出值**的不可落空形态——无直系 break 的 loop / 双分支皆不可落空且有 else 的 if / STMT·UNSAFE·block 包裹;**`return` 不判 divergence**(其值类型正是站点 5 的被检对象);break 扫描在嵌套循环处截断(break 归内层)、未枚举形态 fail-closed),站点 5 接线一行;`type_engine.cr` 两处签名改 `string`;`ty_shadow.cr` 头注改写(原文记载该误报面,被本 TODO/计划引用)。 +- **判据**:`check src/compiler` **rc=0**(诊断 0 条);构建 rc=0(GUARD clean:corec 45 / corearch **32** / corelsp 31);`test_backend_bootstrap` rc=0(`error[`=0);`selftest-types` **338/338**;ELF canary `95084e7b…d475` IDENTICAL(清单步前后各一次);全枚举 **47 selfhost + 7 bootstrap 全 rc=0**;`full-bootstrap` rc=0(`corec2==corec3` `991eecc1…b2ba` 双 2604942B + N06=0 + 冒烟 42 + `--help` rc=1);同源对拍(30 档语料 check 输出 + rc)**零差异**;突变控制 5 条逐条单点转红(过接受/过拒绝/嵌套截断/match 臂链/连续段);新套件 `tests/selfhost/test_tf01_fallthrough.py` **19 例**挂 `src/ci/run.sh`。 +- **附带**:`test_iface_ops` 3 条**过期期望**随批更新(少掉的 TF01 恰为同误报类、三条用例的关注面诊断原样保留(含行号)⇒ 76/76 复绿);**P4 Task 2 §9-② 收口路径 (a) 兑现**——`type_engine.cr` 入 corearch 清单两面(31→32 文件),project-mode `error[`=0 门通过(`ty_sub` 族跨进程同值探针面解阻,消费者待接;corearch 二进制变大 = 死码,非判据面)。 +- **登记(本批不修)**:① TF01 对 `build` **非致命**(含 TF01 的源 `corec build` rc=0 且照出可运行产物,实测)⇒ 本批收益面 = `check` 门(CI/工具链/清单门),非运行期行为;② `while <标识符> { … }` 被当**结构体字面量**解析(`parse_while_expr` 缺 `parse_if_expr` 同款 `g_parse_no_struct_literal` 守卫;旧二进制同款复现,既有);③ 非落空体内 `return` 的**值类型**不核对(显式放宽面;逐 return 核对需重跑推断 = 副作用风险,本批不做;套件 `pos_boundary_no_return_value_check` 注释钉住边界)。 +- **关联**:#39(P3b 收官——本批改写其「既有 red 划界」句)· spec `docs/superpowers/specs/2026-09-10-type-shadow-findings.md`(P2b 收紧清单句)· 计划 `docs/superpowers/plans/2026-09-12-r2-p4-carrier.md`(Task 2 §9-② / Task 7 Step 1)· 报告 = `.superpowers/sdd/tf01-cleanup-report.md`。 + +### 41. R2 P4 Task 3:IFACE 段内容落地(2026-09-13——扩列 16 + 形状命名化 + 签名项化 + impl 边/方法表 + corearch 读回) +- **交付**:本提交(路径限定 13 文件 = `src/compiler/{dyn_arr,iface_registry,checker,globals,ccr_types,ccr_io,main,corearch,type_selftest}.cr` + `tests/selfhost/test_ccr_types.py` + `src/ci/run.sh` + 格式 spec)。详版 = `.superpowers/sdd/p4-task3-report.md`。 +- **段布局(D14 五小节;字段宽度逐条;实现权威 = `ccr_io.cr` 头注释 + spec §3.8)**:① 原生条目 **16 行 × 24B** `{ak i32, ti_row i32, name_ni i32, lit_code i32, ops i64}`;② 形状 **6 条 × 8B** `{name_ni i32, term i32}`;③ 用户接口 `{name_ni, method_count, generic_count, pad}` + 方法 **× 80B** `{name_ni, param_count, self_mode, ret_term, param_terms[8], param_codes[8]}`(**字段序取计划「声明行」序**——同块尺寸式两数组次序相反,实施登记);④ impl 边 × 8B;⑤ 方法表 × 12B。实测:`ptr_arith` 452B · 夹具(2 接口/3 方法/1 impl)744B。 +- **扩列(裁决 3 + 7)**:`IFACE_ENTRY_COUNT` 13 → **16**(+`AK_NULL`/`AK_SUM`/`AK_FN`,`ops = 0` = **纯信息面**);`name_ni` 逐名注册(裁决 1 接受 STR 段增长——16 行名 + 4 形状独有名 = 20 名一表);`iface_kind_of` **零改动**(判定面零变化由全类型行枚举 + `TYP_NULL/TYP_OPTIONAL` 行显式 -1 守门)。 +- **形状命名化(裁决 1 + D17)**:六名(`sequence`/`sequence_ro`/`sequence_rw`/`indexable`/`iterable`/`product`)经 `iface_shape_builtin_init` 在 **`init_types` 尾部**注册 + D13 重建重跑;命名**消费语法**(`T: 可索引`)仍未开工(P3 遗留项)。 +- **签名项化**:`sh_iface_sig_ret_term`/`sh_iface_sig_param_term` 按接口声明序装填进 `g_ifaces` 新槽(`OFF_IFM_PARAM_TERMS=168`/`OFF_IFM_RET_TERM=232`;`ESZ_IFMETHOD` 168→**240**、`ESZ_IFACEINFO` 2712→**3864**);**不可建(`dyn` 位图行入签名)⇒ save 拒绝**(先 report-only:61 档零命中后开门)。 +- **读回(corearch)**:五小节重建条目表(`g_iface_registry_ok=1` = **段为真源**)/形状表/接口表(corearch 无 AST ⇒ 节点槽恒 -1、**项槽 = 段值**)/impl 边/方法表;**跨段引用域闸**(`name_ni` 等 < STR 串数、`ti_row` < TYPE 行数、`term` < TYPE 项数);`--dump-ifaces`(双侧共用打印路径)+ `ifaceprobe` 摘要 = 项索引在重建项表上的解析同值。 +- **判据(最终二进制 `6da65418…`)**:构建 rc=0(三面 `error[`=0/undefined=0)· **`check src/compiler` rc=0** · `selftest-types` **338 → 347** · `test_ccr_types` **19 → 28**(T3 新 10 例 + 机制面重定)· ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL** · 自举链 `corec2==corec3` cmp IDENTICAL(双 2737902B `55dc7cda…`)+ N06=0 + 冒烟 42(corec/corec2)+ `corec3 --help` rc=1 · **五 CI job 全 rc=0** + 全枚举 **54/54**(47 selfhost + 7 bootstrap)· 同源对拍 check 72 档 diff 空 + 影子 `decisions=32280=agree`(站点覆盖同报,两侧恒等)· report-only 61 档**零新增拒收** · 突变控制 **3 条逐条转红**(loader 闸关/签名装填跳过/init_types 注册缺失)。 +- **`.ccr` 记录值(冷缓存;P3b/T2 值作废)**:`ptr_arith` 90823B `fb4a3b59103f4e0c…` · `generics_test` 134701B `cafb42781f8a4f9b…` · 自源(同源树)10418710B `3d6178130a88a518…`。**STR 增长实测**(裁决 1 的兑现面):+16 串/+156B(`ptr_arith`)、+155B(`generics_test`)、**0**(自源——编译器源码已驻留全部 20 名);**连带面**:新驻留名使「晚于 `init_types` 驻留」的串 ni 整体后移 ⇒ 用户程序 `.ccr` 的 SYM(名字列)与 NOD(`IR_CALL` 的 `src3` = callee ni)取值重编号(**布局不变、语义不变、ELF canary 不变**;自源前六段逐字节同);TYPE 段 +360~400B(形状项在建项序前段)。 +- **登记(本批不修/承接)**:① 方法记录字段序的计划文两处冲突(取声明行序);② `ESZ_IFMETHOD`/`ESZ_IFACEINFO` 内存扩槽**不入盘、不入 `.cir` 快照**(快照字段清单核对 = vars/DF/SG/instrs/str_consts + 冷/暖两态一致复证)⇒ **不触发 `CIR_CACHE_VER`**;③ corearch 侧 IFACE 段仍**无判定消费者**(项槽/形状项的引擎消费 = P5 的 `atom_of` 承接面,`--dump-ifaces` 是当前唯一读回通道);④ 签名编码面(`isat.axis_c_ret_code`/`inst_encoding_limit_named_ret_rejected`)按计划**只落证据、未改用例**(改动归 P5);⑤ 未用形参项槽 = -1(写侧显式填 + loader 结构域接受)。 +- **关联**:#40(check 门 rc=0——本批 `check` 判据口径由此更新)· #39(P3b 交接附录 C 的翻转钉逐条处置)· 计划 `docs/superpowers/plans/2026-09-12-r2-p4-carrier.md` Task 3 · spec `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md` §3.8。 + +### 42. R2 P4 Task 4:DFNode.TK 迁移期双槽(2026-09-13——允许清单制派生 + `.cir` 快照不落槽(实测推翻计划 D15 前提)+ 发射面零泄漏证明) +- **交付**:本提交(路径限定 9 文件 = `src/compiler/{dyn_arr,dataflow,ir_gen,ty_shadow,cir_cache,dump,main,type_selftest}.cr` + `tests/selfhost/test_ccr_types.py` + `src/ci/run.sh` + 计划/格式 spec)。详版 = `.superpowers/sdd/p4-task4-report.md`。 +- **双槽形态(D15)**:`ESZ_DFNODE` 64 → **72B**;`OFF_DF_TK=40` **语义/字节零变化**(混用码原样),新增 `OFF_DF_TK_TERM=64`(类型项引用,-1 = 无项);唯一写点 = `df_create_node`(emit 传参;`dataflow.cr` 不引用桥接符号)。 +- **允许清单推导(Step 2 产物)**:`instr.cr` 全文件 `ti` 消费者 **5 处**(`IR_CONST(ti==TI_STR)` · `IR_BINARY(ti==TI_DEX)` · `IR_DEREF`/`IR_STORE_PTR` = `e2_ptr_bounds_check` 的 **access_width** · `IR_BOUNDS_CHECK(ti!=0)` 旗标)× 216 个 `emit(` 调用点逐 op 静态审计 × 全语料 NOD 的 (op,tk) 直方图 ⇒ **清单 = {IR_CONST, IR_BINARY}**。**实证陷阱两例**:`ptr_arith` 的 `IR_DEREF/IR_STORE_PTR` 传 `tk=8`(访问宽度,数值恰 = `TI_DEX_S` 行)、`IR_BOUNDS_CHECK` 传 `tk=1`(动态上限旗标,数值恰 = `TI_DEX` 行)——无差别派生即凭空安项。排除面登记:`IR_ALLOC`/`IR_CALL`/`IR_I2F`/`IR_F2I`/`IR_LOAD` 传合法类型行但后端不按 `ti` 分派 ⇒ 暂不入清单(P5 再裁)。 +- **`.cir` 快照 = 不落项槽(**计划 D15 的实测修正**)**:计划原写「快照入项槽 + `CIR_CACHE_VER` 17→18」,前提「不入槽 ⇒ 冷/暖分歧」**被实测推翻且方向相反**:入槽版本 `ptr_arith` 冷 `terms=13 bad_term=0` vs 暖 `terms=10` **`bad_term=329`**(恢复来的项引用全数越出暖进程项表——项表是**进程内**内容寻址表、emit 期按需追加)。修正 = 快照记录仍 **8×8B = 64B**(格式零变化 ⇒ **不 bump**),装载侧在 **emit 等价时点**(逐函数逐节点序)`sh_tk_term_of_code(opcode, tk)` 重派生 ⇒ 冷/暖 `(op,tk,term,tag,a,b,c)` **逐行相同**(实测 1298 行全等、两态 `terms=13 bad_term=0`)。另:落盘 = 「纯函数值与其入参并存」= 第二真源(本仓反复治过的病)。 +- **通道**:新增 corec hidden flag `cir --dump-tk-terms`(只读;逐节点 `op/tk/term/tag/a/b/c` + 头行 `nodes/with_term/bad_term/terms/allow`);带/不带 flag 的产物与 stdout(剔 dump 节)逐字节同。 +- **判据(最终二进制 `1cc18c7d…`)**:构建 rc=0(三面 `error[`=0/undefined=0,两连建逐字节同)· **`check src/compiler` rc=0** · `test_backend_bootstrap` rc=0 + `error[`=0 · `selftest-types` **347 → 355**(`t4.tk_*` 8 例:3 正控 + 4 负控 + 1 计数纪律)· `test_ccr_types` **28 → 34** · **ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` IDENTICAL**(clean-cache)· `.ccr` 八段逐字节同(`ptr_arith` 90823B `fb4a3b59…` · `generics_test` 134701B `cafb4278…`——**用户程序零扰动**)· `--dump-objects` 逐字节同 · 五 CI job 全 rc=0 · 全枚举 **54/54** · 自举链 `corec2==corec3` cmp IDENTICAL(双 2754918B `55237ec1…`)+ N06=0 + 冒烟 42 + `corec3 --help` rc=1 · 同源对拍 check/shadow **两侧 diff 空**(`decisions=32355=agree`;站点覆盖 assign-node=24561 · fn-body-ret=5747 · if-branch=2013 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2)· 编译耗时抽样:6000 语句合成源 min **2957 → 2933ms**(噪声内;小语料 26–36ms 两态同)。 +- **突变控制(/tmp 侧,仓库零改动)**:A(清单门 + hits 复原移除)= ㉖ 转红 + selftest 4 例转红(351/355);B(装载侧重派生移除)= ㉙ 转红(暖态 `with_term=1354` vs 冷 `348`)。 +- **登记/承接(P5)**:① 单槽化时 `tk` 本身 = 项引用 ⇒ 盘面项槽/`.csr` 消费面另定;**项引用的稳定形态**(内容寻址 / 落盘项 ID / 同款重派生)是 P5 前置裁决项——**不得**直接落进程内索引(本任务 `bad_term=329` 即红证);② 本批 `tk_term` 无生产消费者(价值由内容断言兑现,不假装有外部消费者);③ 冷/热 **`cir` 文本 dump** 的渲染差异(变量名 vs 数字)为既有面(TODO #5 末条,baseline 二进制度量 1620 行 diff,非本任务引入)。 +- **关联**:#41(Task 3)· #39(P3b 交接附录 C 待裁决 8 = TK 升格)· 计划 `docs/superpowers/plans/2026-09-12-r2-p4-carrier.md` Task 4(D15)· spec `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §6.2。 + +### 43. R2 P4 Task 5:可选运行期表示——解包侧判表示(2026-09-13——B.7 一等条目闭合 + 三根因连带修) +- **交付**:本提交(路径限定 9 文件 = `src/compiler/{ir_gen,globals,main}.cr` + `src/arch/x86_64/instr.cr` + `tests/selfhost/test_optional.py` + `src/ci/run.sh` + 计划 ×2 + 本条目)。详版 = `.superpowers/sdd/p4-task5-report.md`。 +- **形态(裁决 5 = 解包侧判表示)**:**表示位 = 普通 int IR 变量**(每个可选槽一个;0 = 裸值「槽内即 T 值,Some 臂载荷 = 槽值本身」/ 1 = 装箱「`IR_MAKE_ENUM` 对象,载荷 = 字段 `fi+1`」)。**写点置位**(`EXPR_LET`/`EXPR_ASSIGN`:静态已知 → 常量、源有表示位 → 拷贝)+ **两解包点分派**(`match` 的 Some/None 臂条件与载荷绑定、`?`)。**无表示位的槽 = 逐字走既有路径**(未覆盖面不静默分歧的结构保证)。**跨函数面 = 隐藏全局信道**(返回点写/调用点读、调用点写/被调序言读;纯 IR)。**纯 IR ⇒ 零 ABI/opcode/帧布局改动 ⇒ ELF 与解释器同源**。 +- **零足迹门**:AST 无 `EXPR_OPTIONAL` 且无 `Some`/`None` ⇒ 整面不启用 ⇒ **非可选程序发射面逐字节不变**(ELF canary `95084e7b…d475` **IDENTICAL** + `ptr_arith`/`generics_test` `.ccr` 与基线编译器同源重建**逐字节同** + `--dump-objects` 逐字节同)。 +- **根因(5 条;后 4 条为计划未列面,逐条实测定位)**:① 表示位侧表空槽哨兵——`alloc` 零初始化而哨兵 = -1 ⇒ 未写槽读 0(= 「表示位 var 0」)⇒ 全部置位取 0;修 = 新槽填 -1(照 `tag2l.cr` 先例)。② **ELF 后端逐 op 的全局行分派缺口**(同族三处):(i) `IR_CONST` 无全局行分支(`g2_slot` 对全局返回帧外伪偏移 ⇒ 写栈垃圾 = 静默丢写;解释器直写 `g_ir_vals` ⇒ **双路径分歧**)——本任务首次出现该发射点(形参信道单元);(ii) **`IR_LOAD_ENUM_TAG` 的 tag 读取源同缺**(全局可选槽 + match ⇒ ELF 0 / 解释器 5 = 静默分歧,**基线同病**,本任务 §未覆盖面实测时发现);两处均按 `IR_LOAD`/`IR_STORE` 同款修(源走 `e2_load_var` / RIP 直相对 + patch 记录),目的槽的全局行零发射点(注释登记);`TI_STR` 变体仍零发射点(已注释登记)。③ **负值文件级常量在自举二进制里静默读 0**(= TODO #14 的 live 触发;改用 `oe_bare()/oe_boxed()` 函数)。④ 返回类型判据取 `fi_return_type` **裸码**(`T?` 的类型节点 `type_val` = 0 ⇒ 读成 int 行、判据恒假)⇒ 改走返回类型**节点**(被调序言 + 调用点两侧同改,新增 `callee_ret_optional`)。⑤ 惰性 thunk 把调用推迟到 force 点 ⇒ 返回信道读点悬空 ⇒ **被调返回可选者禁 thunk**(代价仅此一类)。 +- **判据**:构建 rc=0(三面 `error[`=0)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · `selftest-types` **355/355**(用例数不变)· `test_optional.py` **12 → 34/34** · **ELF canary IDENTICAL** · `.ccr` 新记录值 `ptr_arith` **90966B** / `generics_test` **134844B**(与 Task 4 记录值的 +143B **非本任务**——同源用基线编译器重建同得该差,= 期间 `arena_globals` 导入引入的 4 全局 + 1 串,逐段点名)· 可选程序冷/热 `.ccr` IDENTICAL · 自举链 `corec2==corec3` cmp IDENTICAL(双 2783750B `5492d9ad…`)+ N06=0 + 冒烟 42 + `corec3 --help` rc=1 · 全枚举 **54/54** · CI {check,bootstrap-tests,suite,selfhost-tests} 全 rc=0 · 同源对拍 check/shadow 两侧 **diff 空**(`decisions=32564=agree`;站点覆盖 assign-node=24709 · fn-body-ret=5793 · if-branch=2028 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2)· 编译耗时:可选 315→282ms、非可选 55→55ms(无回退)。 +- **台账**:放宽 **0** / 收紧 **0**(判定面零变化由对拍反证);修复 **8 形态**(局部裸值路径 + 返回/形参裸值路径)+ **1 静默错值类**(`?` 解包装箱值:修复前 ELF=8 / interp=200 **各错各的**,现 7/7)。 +- **未覆盖面(登记,不静默)**:结构体字段 / 数组元素 / 全局槽 / match 结果 / 可选的惰性 thunk(**后者已由 ⑤ 关闭**:返回可选的被调不做 thunk)⇒ 不配表示位 ⇒ 解包回落既有装箱假定:裸值形态 **rc=139 双路径同**(响亮)、装箱形态照常正确;`test_optional.py` 两例分别钉。修复面(对象布局里放 packed 表示位 / 数组 stride)归 P5 与 `MAX_*` 定长槽评估同批。 +- **登记面**:⓪ **预存缺陷(实测发现,非本任务引入)**:冷/热缓存下 `corec build src/compiler/main.cr -O 0` **暖态 SIGSEGV**(`load_cir_cache` → `str_len`,gdb 栈实测;**基线二进制同病** 冷 rc=0/暖 rc=139)——本任务「可选面关缓存」顺带规避(仅限可选程序);归 `.cir` 缓存恢复面(TODO #5 末条同族)转主批。**→ 已闭合(R2 P5 Task 1,本批提交 `fix: R2 P5 Task 1——…`)**:根因 = 装载侧逐函数 `read_file` = `alloc(fsize+1)` 而 bump 分配器不回收 ⇒ 946 个快照读缓冲累计 **1.1674GiB > 1GiB 堆** ⇒ `alloc` 耗尽返回 0(`.Lalloc_oom`)⇒ `str_len(0)` → `load64(0,-8)`;修 = `cir_cache.cr::cir_read_snapshot` **单一复用读缓冲**(与写侧 `g_cir_write_buf` 同构;失败 ⇒ `-1` = miss,三态纪律)+ 新套件 `tests/selfhost/test_cir_warm_path.py`(19 例,自源另开 2 例;RED 对照 15 PASS/1 FAIL)+ 突变控制。判据与证据 = `.superpowers/sdd/p5-task1-report.md`(`CIR_CACHE_VER` 保持 17:写读对称表核 ⇒ 格式零变化)。① **可选面启用即关 `.cir` 缓存**(表示位侧表 = 编译期进程内状态、快照不载 ⇒ 命中恢复与冷路径产物分歧,同 Task 4 `bad_term=329` 教训形态;非可选程序照常缓存;P5 若要收回须给快照加表示位面或装载侧重派生);② `IR_CONST` 的 `TI_STR`+全局行未分派(零发射点);③ 影子通道**零覆盖载体面**(正确性证据一律 = 行为探针 + 双路径同值)。 +- **关联**:#42(Task 4,`.cir` 快照面)· #14(本轮**触发实证 ②**:负值文件级常量;连带 live 实例 = `CIR_CACHE_MAGIC` 在自举二进制读 0 ⇒ 落盘 `.cir` magic 实测全 0、写/读自洽故不炸测试)· P3 计划附录 B.7(本节顶部已标 ✅ 闭合)· 计划 `docs/superpowers/plans/2026-09-12-r2-p4-carrier.md` Task 5 · spec `2026-09-10-type-interface-unification-design.md` §9 P3 行(未统一条目已划销)。 + +### 44. R2 P4 收官(2026-09-13——载体批 8 提交链 + 统一台账 + 判据复验 + P5 交接包;**P4 全阶段收官**) +- **提交链(8 提交 + 附带件 1;计划定稿 `3f8885ec`)**:`7c822003`(T0 引擎核纯化拆分:iface 簇 + `iface_kind_of` → `iface_axis.cr`,corearch 链接面开路;零行为变化)→ `97febdbf`(T1 段机制:`TYPE(7)`/`IFACE(8)` 加段 + 版本 7→8 + loader 三闸/必备集;两段空壳)→ `3d604e11`(T2 TYPE 段内容:类型行表 24B + 项 DAG 40B(哈希不落盘)+ D13 确定性装填 + corearch 读回重建)→ `6822102a`(附带件 TF01 收口 = T2 §9-② 路径 (a) 兑现;`check` job 判据自此按 rc=0)→ `8f4af99b`(T3 IFACE 段内容:原生条目扩列 13→16 + 形状命名化 + 签名项化 + impl 边/方法表 + 读回)→ `387abf2e`(T4 DFNode.TK 迁移期双槽:允许清单制 `{IR_CONST,IR_BINARY}`;**计划 D15 被实测推翻**——`.cir` 快照不落项槽、`CIR_CACHE_VER` 保持 17、装载侧 emit 等价时点重派生)→ `2be26cc5` + `cf368259`(T5 可选运行期表示解包侧判表示:B.7 裸值 + `Some` 臂双路径 rc=139 闭合;补 `IR_LOAD_ENUM_TAG` 全局行分派)→ `cc015ae4`(T6 `MAX_*` 写入侧护栏 P022/P023;三上限不解除)。 +- **判据(T7 实跑,2026-09-13;无并发构建窗口;五 CI job + 全枚举 + canary + 自举链 + 同源对拍全部由本实例重跑——碰撞窗未背书项一并复现)**:五 CI job 全 rc=0 · 全枚举 **55/55 rc=0**(48 selfhost + 7 bootstrap)· `selftest-types` **355/355** · `check src/compiler` rc=0 · ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL**(28822B)· 自举链 `corec2==corec3` `cmp` **IDENTICAL**(双 2792078B `4919023b…f9034`)+ 构建日志 **N06=0** + 冒烟 **42**(corec/corec2 双通道)+ `corec3 --help` rc=1 · 构建确定性(`build/corec` = `5d2b15ad…` 与 T6 记录值逐字节同)· 段结构断言(version 8 / seg_count 8 / tag 序 1..8 / 连续 `end == fsize`)。 +- **同源对拍(pre-P4 二进制 `9a583215…` × 当前源 vs 当前二进制 × 当前源;72 档 × {check, shadow})**:影子摘要两侧同值(70 档出摘要,`decisions=32620 agree=32620`,`old_stricter`/`old_looser`/`unknown*`/`replace_*` 全 0);站点覆盖两侧逐项同(assign-node=24721 · fn-body-ret=5828 · if-branch=2037 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2;Σ = 32620);日志面唯一差异 = **5 档语料**的 TF01 收口类(5 处 `ty_memo_slot_no_grow` 误报消失 + 3 档 rc 1→0;rc 分布 31/41 → 34/38)= P4 内**有意修复**,非语义回归。**载体面零覆盖声明(继承)**:影子通道对段/读回/TK/表示零覆盖,P4 载体正确性证据 = 结构性断言 + corearch 读回对拍 + 定向探针。 +- **`.ccr` 记录值(两套命令口径;T7 复测逐条复现)**:`corec ccr F -o O` = `ptr_arith` **90823B** `fb4a3b59…` / `generics_test` **134701B** `cafb4278…` / 自源 **10661888B** `76c76bb1…`(自源随源变更,不与前批直比);`corec build F -o O --static` = **90966B** `592afa31…` / **134844B** `ec8413ec…`(差恰 143B = `--static` 前置 `rt.cr`)。**冷/热勘误(本轮确立判据)**:非可选程序冷≠热为**预存**(pre-P4 二进制同病 89086/88954;TODO #5 末条家族)⇒ 「冷/热两态逐字节同」只对**可选面零足迹**程序成立(可选程序 97604B `2bfbbe2b…` 冷≡热);**TYPE 段「纯函数」承诺项面成立、行面不成立**(T2 §8-③:行表热 12/冷 15,前缀关系)。 +- **统一台账(T0-T6 汇总;逐条出处 = 各任务报告)**:收紧 **10 类**(T1 3:缺 TYPE 段 / 缺 IFACE 段 / 版本≠8 整类拒收;T2 2:装填不可译行 save 拒绝 + loader TYPE 结构不变量;T3 2:签名不可建 save 拒绝 + loader IFACE 五小节/跨段引用域;T6 3:P022 变体 / P022 载荷 / P023 字段)· 放宽 **2**(T2/T3 的 T1 空壳闸退役,非语义放宽)· 崩溃/静默修复 **8 形态 + 1 静默错值类**(T5:裸值路径 rc=139 → 正确值;`?` 解包装箱值双路径各错各的)· 全语料命中 **0**(report-only 逐批:T1 72 档、T2 56 档、T3 61 档、T6 139 文件静态扫描 + suite/examples 逐档——失败集与各自基线逐条相同)。 +- **实施偏差(实测推翻/修正计划文本者,逐条登记)**:(a) T4 的 `.cir` 快照「入项槽 + `CIR_CACHE_VER` 17→18」被推翻(入槽版暖路径 `bad_term=329` 全悬空 ⇒ 不落槽 + 装载侧重派生);(b) T2 的「TYPE 段冷/热逐字节同」半成立(项面成立、行面不成立);(c) T2 spec §3.7 的 `{kind i32…}` 字段宽度与 24B 段体自相矛盾 ⇒ 实施权威 = 8B 字段;(d) T3 方法记录字段序取计划「声明行」序(同块尺寸式两数组次序相反);(e) T1 计划 Interfaces 的段体缓冲延后到 T2 的内容构造者。 +- **P5 交接包** = 计划 **附录 D**(D-1 两套口径 + 冷/热勘误表 · D-2 任务面:单槽化 + 大步删旧路径 + 文档/台账 · D-3 十四项继承项 · D-4 开工前置检查单);指针链 = 统一设计 spec §9 的 P4 行 → 计划 Task 7 → 附录 D → 本条。**必读三项**:D-3-2(项引用稳定形态先裁——`bad_term=329` 红证)、D-3-4(unknown 清零 (a)/(b) 先裁)、D-3-9-(ii)(预存暖态 SIGSEGV:`corec build src/compiler/main.cr -O 0` 暖态 rc=139,`load_cir_cache` → `str_len` 栈,基线同病)——**已闭合(R2 P5 Task 1,本批首提交**:装载侧读缓冲累计 > 1GiB 堆 ⇒ 单一复用缓冲 + 载入失败 ⇒ miss;判据见 `.superpowers/sdd/p5-task1-report.md`)。 +- **关联**:#26(判据重定——P4 继承注见该条)· #40/#41/#42/#43(P4 逐件落地)· #35(T6 划销)· #32(`EXPR_LET` 无检查——未随 P4 收口,P5 继承)· #5 末条(冷/热 `.ccr` 分歧——本轮量化并重定判据)· 报告 = `.superpowers/sdd/p4-task7-report.md`(工作区)。 + +### 45. R2 P5 Task 2:DFNode.TK **单槽化(α)**(2026-09-13——类型面单源 + 辅码分离 + `atom_of` 契约;**零行为变化**) +- **形态(D19)**:`OFF_DF_TK`(40) 语义 = **类型项引用**(类型面唯一真源);`OFF_DF_TK_TERM`(64) 更名 **`OFF_DF_AUX`** = 辅码(旗标/宽度/计数/不可逆行的原码;0 = 无);`ESZ_DFNODE` 保持 72B ⇒ **布局零变化**(`CCR_VERSION=8` / `CIR_CACHE_VER=17` / `.ccr` NOD 36B / `.cir` 快照 64B·节点均不动)。码 = **派生量**:`iri_tk`/盘面一律 `sh_dfn_code_of_slots(项, 辅码)`。 +- **分类表(Task 0 表 A 代码化,`ty_shadow.cr`)**:类型行面 = `{IR_CONST, IR_BINARY}` ∪ **P4 六排除项再裁** `{IR_ALLOC, IR_CALL, IR_LOAD, IR_I2F, IR_F2I}`(P4 的「后端不按 ti 分派」= 发射面判据,不适用于语义类型面;`IR_LOAD_ENUM_TAG` 两调用点传字面量 0 ⇒ 仍「无面」);辅码面 = `{IR_BOUNDS_CHECK(0/1 旗标), IR_DEREF/IR_STORE_PTR(宽度), IR_SPAWN(-1=动态), IR_HOTPATCH_ROUTE}`;其余无面。 +- **F1(协调者裁决:修调用点,不把 UB 烘进表)**:`ir_gen.cr:1758` 5 参 `emit` ⇒ op39 的 tk = **r9 残留**(bootstrap 只为实际实参写寄存器;实测 0/4 两态)⇒ 补显式第 6 实参 `0`;**已裁决偏差** = 含 hotpatch 语料该节点码 **4→0**(全语料 3 节点,零消费者;`hotpatch_test.cr` 的 `.ccr` 相应差 3 处)。 +- **F2(协调者裁决:分类表只覆盖原子行)**:复合行(ptr/ref/array/slice/optional/null/tuple 行 —— 项不可逆)**不建项**(`atom_of` ⇒ -1,不近似),原码进辅码槽**保真**(Task 0 候选 (iii)+(ii))⇒ 派生码逐字节 ≡ 旧混用码。**证明**:5 个 `ti` 消费者谓词的值敏感面 = `{TI_STR=3, TI_DEX=1, 宽度, 旗标}`,复合行码(实测仅 `ptr_ref_first` BINARY tk=10 + 夹具 tk=13)永不落入 ⇒ 不变按构造成立(突变 M1 反证该格可抓)。 +- **D23 新硬错**:类型面行的可译性闸——**仅 emit 路径**计数(装载侧不计数:暖进程类型表可更小,见下)、`ccr_seg_prepare_save` 入口拒绝落盘(rc=1 + 计数诊断);**全语料 0 命中**(94 档 `face_fail=0`,report-only 先行);牙齿 = 突变 M4(越界行 ⇒ rc=1 + 无产物)。 +- **判据(本任务实跑;报告 `.superpowers/sdd/p5-task2-report.md`)**:`selftest-types` **367/367**(355+12)· `test_ccr_types` **40/40**(34+6)· `test_cir_warm_path` 19/19 · `test_cache_identity` 5/5(`VER_EXPECTED=17` 未动)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· `.ccr` 两套口径冷态四条与起点基线逐条同 · `--dump-objects` 逐字节同(2427 行)· **派生码不变量**:94 档逐节点 diff = 仅 F1 的 3 节点(非自源 338,207 节点余者 0)· 全枚举 **56/56** · 五 CI job rc=0 · 自举链 `corec2==corec3` cmp IDENTICAL(2821374B)+ N06=0 + 冒烟 42 · 72 档 `check` 同源对拍(rc 同;日志差 2 档自源行号位移)· 影子对拍 `decisions=agree=32767` + 全分类计数 0 · **突变控制 4 条**逐条转红。 +- **判据重定(2 处,实测驱动)**:① 冷/暖比对由「索引全等」改为「**派生码面(op,tk,aux)逐行同 + 项内容(tag/a/b/c)等价**」——**暖态类型表/项表可小于冷态**(缓存命中跳过 `ir_gen`,而其自身 `alloc_type` 新行 ⇒ 冷态存在的行暖态不存在 ⇒ 该项不可重建;P4 窄清单掩盖、P5 扩面暴露;派生码与发射面/盘面零影响);② `--dump-tk-terms` 头新增 `rows=`/`aux_nonzero=`/`face_fail=` 观测列。 +- **P6 承接**:暖态项面差异的根治 = **β 盘面项索引**(附录 E-1;D20-② 的许可形态);本批盘面只承载派生码(进程内项引用零跨进程)。 +- **关联**:#42(P4 T4 的「P5 再裁」= 本条)· #24/#30(`g_shadow_map`/影子层 = P5 T5 面)· #44(P5 交接包)· 计划 `docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md` Task 2 实施记录。 + +### 46. R2 P5 Task 3:unknown 清零——**命名面判定化**(2026-09-13——身份链 + 可判定面加强;裁-2 (a)) +- **形态**:桥接层给命名原子挂**身份链**(`ty_shadow.cr`):`TYP_NAMED`/`TYP_GENERIC_PARAM` ⇒ `tt_atom(AK_NAMED, 本行, [名字令牌])`;`TYP_GENERIC_APPLY` ⇒ `tt_atom(AK_NAMED, **基型行**, [基名令牌, 实参项…])`(基型行 = **规范形**:两处实例化同型 ⇒ 同节点);**两构造点**(`sh_term_of_ti` / `sh_sig_term_of_ti`)**逐位同构**(停条件③不触发)。引擎 `type_engine.cr`:`lit_implies` 正原子分支 AK_NAMED 守卫 → `te_named_pair`:**同类 ⇒ 链比较**(令牌位 = 节点同一性 / 实参位 = 引擎 `ty_equiv` 三态)、**混类 ⇒ 0**(命名侧带链 ∧ 异类侧非 NEVER/DYN)、**域外 ⇒ -1 + uncovered**(空链 / 令牌位错位 / 实参位 μ 变元 / NEVER·DYN 混类)。**不接结构性展开**(P3a 反证口径保持:展开项结构相等 1 与桥接项 0 双断并存)。 +- **清零判据(实跑)**:语料 72 档 shadow `decisions=agree=32917`、`old_stricter/old_looser/unknown*/replace_*` **全 0**;**探针可判定域 18 档 `unknown_engine`/`replace_unknown` = 0**(RED = 1~2/档:Table D 两形态「异名同形命名」「同实参泛型应用两实例化」+ 异实参应用 / 命名 vs 原生 / 可选异名 / 应用含命名实参 / 嵌套应用 / T 赋 int);rc/诊断**逐条不变**(清零 ≠ 放宽 ≠ 收紧)。 +- **判据**:`selftest-types` **386/386**(367+19;3 处重钉:`t3.named_face_decided_no_fallback` / `unf.nominal_equiv_atomic_decided` / `unf.nominal_type_equal_false`)· 新套件 `tests/selfhost/test_named_face.py` **18/18**(行为断言形态,**不依赖影子通道**——T5 下线后照常)· ELF canary IDENTICAL · `.ccr` `ptr_arith` 逐字节同 / `generics_test` **+640B**(TYPE 段项表 52→68 行;发射面零泄漏:ELF 逐字节同 + `--dump-objects` 同 + `.cir` DOT 同)· check 语料 34×0/38×1 逐档同 · **D26 同源对拍零差异** · 全枚举 57/57 · 五 CI job rc=0 · 自举链 cmp IDENTICAL + N06=0 + 冒烟 42 · **突变控制 3 条**逐条转红 + 精确回滚(报告 `.superpowers/sdd/p5-task3-report.md`)。 +- **收紧面(登记,提请复核)**:混类判定从 -1 变 0 ⇒ 约束站点(`gen_constr_satisfied`/`gen_inst_constr_satisfied`)「命名实参 × 原生约束」由**不判**变 **TG02**(`check` rc=1;`build` rc 不变——TG02 非硬名单)。**全语料 0 命中**;唯一命中 = `test_generic_constr.py` 1 例登记面(已重钉 `struct_inst_named_arg_rejected`)。回退面 = 混类分支回 -1(探针 n10/n20/n21 计数将复红)。 +- **残留 -1(可判定但本任务不动,计划停条件②)**:参数链**不变槽**元素非同形(`[NA;2]` vs `[NB;2]` / `*NA` vs `*NB` / 元组含异名)——行为面早已正确拒绝,残留仅在计数面;建议修法(不变槽元素比较改三态 `te_elem_cmp`)未落地,待单独裁决(证据 = 探针 n13/n17/n18 + 套件 `residual.*` 两例)。 +- **其他登记**:`¬` 面保持 -1(桥接零 TT_NOT ⇒ 不可达)· 链不区分 kind(NAMED vs PARAM 同名 ⇒ 判等;可达性未证实)· APPLY 项 b 槽取基型行 ⇒ DF 载体面该行 face 0→face 1(**派生码不变**)· **预存发现(另单建议)**:非硬名单诊断(TA01/TG02)不阻 `build`(`main.cr:149-170` 硬名单闸;冻结基线同)。 +- **关联**:#24/#30(`type_equal_legacy` 删除 + 影子层下线 = **T4/T5 前置已成立**:清零判据达标)· #45(P5 T2 单槽化)· 计划 Task 3 实施记录 · 报告 `.superpowers/sdd/p5-task3-report.md`。 + +### 47. R2 P5 Task 4:删 `type_equal_legacy` 与全部回落面(2026-09-13——引擎唯一权威 + 残留 -1 政策 **P-A**;桥接副本 + 站点 6 + `ir_gen` 死分支一并删除) +- **政策(Step 1 落纸;计划推荐案 P-A,**待维护者签核**)**:清零后引擎 -1 只剩(a)桥接缺口(行译不成项)、(b)未覆盖面/预算耗尽(Task 0 表 D)⇒ 新码 **`EC_ICE_TY_INDET`(ICE04)**「type judgment indeterminate」+ 反例(两侧项文本)+ **入 `main.cr` 硬名单**(`build` 亦拒绝落盘)。三态纪律:未知不得当 0/1;`return false` 只是 bool 面唯一保守出口(诊断缺席 = 确定 0)。判定点无 AST 位置 ⇒ `--> 0:0`。**回退面 = 删硬名单一行(诊断保留)或整条回 P-B**;report-only 依据 = 全语料 72 档零命中。 +- **删除清单(逐件 + 死亡证据)**:`type_equal_legacy`(`checker.cr:497-573`,注记 10 行 + 函数 67 行,含 **7 自调**)· 回落两处(桥接 / 引擎 -1)· `g_replace_unknown`/`g_replace_bridge`(声明 + 复位 + 影子摘要两字段)· `sh_native_ak_legacy`/`sh_base_ak_legacy`(自测 3 例改钉**冻结期望表** `ts_t4_ak_expected`)· **站点 6**(`checker.cr:2459-2470` 12 行含挂点 + `ir_gen.cr:932-996` 65 行发射分支)· `type_equal` 包装层对照计算(改纯转发)+ `sh_compare` 调用点移除。**死亡证据** = 全语料 `replace_*=0` + 站点 6 三面不可达(`tok2op` 零 `OP_ASSIGN` / `T_EQ`→`EXPR_ASSIGN` / `+=` 族 op∈{ADD,SUB,MUL,DIV})+ 语料 `assign-binary=0`。 +- **清零判据复现(删除前置,本任务自跑)**:`check` 34×0/38×1 · shadow **`decisions=agree=32988`**、全桶 0 · 站点 `assign-node=24998 · fn-body-ret=5905 · if-branch=2051 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`(`assign-binary=0`)。 +- **判据(本实例实跑;报告 `.superpowers/sdd/p5-task4-report.md`,证据 `/tmp/p5t4/`)**:`corec` **`eae13428…`**、构建 rc=0 + `error[`=0 · `selftest-types` **404/404**(401 + `ts_t4_run` 3 例;5 处计数钉重钉「零 ICE04 诊断」)· `check src/compiler` rc=0 · 五 CI job + 全枚举 **57/57** · ELF canary **IDENTICAL**(28822B)· `.ccr` 四条与 T3b 后逐字节同 + `--dump-objects` 同 + `.cir` DOT 同 · 语料日志仅 2 档自源**均匀 −38 行位移** · **冻结基线同源对拍 = 72 档 rc + 日志逐字节零差异** · **探针全集(T3/T3b/T0 三套)pre vs post rc + 日志全同** · 自举链 `corec2==corec3` IDENTICAL + N06=0/0 + 冒烟 42 · **突变控制 3 条**逐条转红 + 精确回滚(M1 去掉 P-A 报告 ⇒ 3 例红;M2 两因措辞合并 ⇒ 1 例红;M3 强制触发 ⇒ `check` rc=1 打印 ICE04、`build` rc=1 无产物 = 硬名单接线证明)。 +- **中间态(D24)**:影子判定对拍**停摆**(`sh_compare` 无调用点 ⇒ 摘要 `decisions=0`;站点直方图不再作判据);判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制;**Task 4/5 必须连续提交**。 +- **关联**:#24/#30(本条划销 ①`type_equal_legacy` 删除、③站点 6、④`sh_*_ak_legacy` 删除三项;②unknown 清零 = #46/T3b;影子层与 `replace_*` 残部 = Task 5)· #45/#46(P5 T2/T3)· #44(P5 交接包)· 计划 Task 4 实施记录。 + +### 48. R2 P5 Task 5:影子层下线 + 回归网切换(2026-09-13——判定面对拍仪器整体退役;替代网 = 冻结基线同源对拍 + 行为探针 + 突变控制) +- **Gate(下线前置,本实例实测)**:`sh_compare` **零调用点**(T4 已移除)⇒ 影子通道判定输出**结构性为空**:起点二进制 × 72 档语料 **70/70 档 `decisions=0 agree=0` + 全差异桶 0**;消费者只剩 `sh_finish`(main.cr,2 调用点)← 两个默认关的 CLI 旗标;无测试驱动。 +- **删除面(逐件)**:**12 函数**(`sh_compare` / `sh_record` / `sh_site_begin` / `sh_report` / `sh_dump_write` / `sh_site_name` / `sh_kind_name` / `sh_count_{agree,old_stricter,old_looser,unknown}` / `sh_map_hits`)+ `sh_finish`(main.cr)+ **17 个 `g_shadow_*` 全局**(含 `g_shadow_hits` 调试计数)+ `SHADOW_RING_CAP` + **2 个 CLI 通道**(`--type-shadow` / `--type-shadow-dump`)+ `g_shadow_on` 赋值 + **checker 9 处 `sh_site_begin` 挂点**(站点 1/2/3/4/5/7/8/9/10;**判定点本体一律保留**)+ hits 存-复原 hack(`sh_tk_split_impl` / `sh_tk_term_of_code` / `sh_map_reset`)。**改名**:`g_shadow_map`/`_cap`/`_entries` → **`g_term_map`/`_cap`/`_entries`**(引用 29 ≤ 30 ⇒ 计划择一条件执行);`SHADOW_MAP_INIT_CAP` → `TERM_MAP_INIT_CAP`。**保留** = 桥接层全族(生产面:判定路径无条件经 `sh_term_of_ti`)+ 展开层全族(生产面)+ `sh_unf_*` 计数(展开层自测读数)。 +- **⚠ 偏离计划 D27 一项(**提请维护者复核**)**:`--verify-named-dedup` / `named_dedup_verify` **未删**——理由 ① **非影子面**(零 `g_shadow_*` 耦合);② **有活消费者** = `tests/selfhost/test_named_dedup.py`(驱动它做「侧表 ↔ `res_type_node`」三组管线内一致性断言)⇒ 删旗标 = 该套件无驱动、杀死既有回归网且违「自测用例只增不减」;③ 边界纪律「不删影子层自身面以外」。**回退面 = 一行**(`main.cr` 旗标 + `run_frontend` 调用块 + 该 py);若仍要清 #24 同族通道,**须同提交另给该断言的替身载体**(`selftest-types` 不跑 `check_all` 全流水线 ⇒ 现无 in-process 替身)。`--verify-evp-nodes` 同理保留。 +- **回归网切换(D26-①;声明落三处)**:`src/ci/run.sh` selfhost-tests 自述块 + `src/compiler/ty_shadow.cr` 文件头 + 本条 ⇒ **判定面回归网(自本任务起)= ① 冻结基线同源对拍(pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源,72 档 `check` rc + 日志逐档 diff)+ ② 行为探针(`tests/selfhost/*.py` + 各批探针语料)+ ③ 突变控制 + ④ 三态纪律(ICE04 硬错)**。**任何后续批次不得引用已下线的影子计数/摘要/站点直方图**;P4 Global Constraints 的「每任务复跑影子对拍」行自此**作废**(以本条替代)。**N 面口径继承(强制)**:影子对 N 面**失明**(同义反复)⇒ **N 面证据只能来自行为探针**(`array_len_*` 判定点 + 异长/同长/嵌套位),不得以「无差异」充当 N 面证据。 +- **CI 接线(本任务新增)**:`tests/selfhost/test_named_dedup.py` **新挂** `selfhost-tests`(原仅在全枚举内;同时部分兑现 **#31** 的点名守卫)。`selftest-types` 计数注释同步(404 不变:两例**重钉**——`t4.tk_derivation_not_a_shadow_site` → `t4.tk_query_purity_and_cache_hit`(hits 断言 → **查询纯度**)+ `bridge.hit_delta` → `bridge.cache_hit_same_term_no_new_entry`(hits 计数 → **同项 + 不新增条目**))。 +- **判据(本实例实跑;报告 `.superpowers/sdd/p5-task5-report.md`,证据 `/tmp/p5t5/`)**:`corec` **`2342fd03…`**(构建 rc=0 + 真实诊断 `error[` = 0;期间全部构建同 sha = 构建确定性)· `test_backend_bootstrap` rc=0 · `check src/compiler` rc=0 · `selftest-types` **404/404** · 五 CI job 全 rc=0 · 全枚举 **57/57** · **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· `.ccr` 四件(`fb4a3b59…`/`592afa31…`/`ddec1ce6…`/`cd2af565…`)与 T4 逐字节同 · `--dump-objects`(两口径 3521 行)IDENTICAL · `.cir` DOT(165915B)IDENTICAL · **冻结基线同源对拍 72 档 rc + 日志零差异** · **探针全集 30 档 vs T4 记录零差异** · 自举链 `corec2==corec3` IDENTICAL(`75da3610…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · **突变控制 2 条**:M1(派生面 `sh_dfn_code_of_slots` 去辅码 ⇒ 自测 400/404 · `test_ccr_types` 35/40 · `.ccr` sha 变)· M2(判定面 `type_equal_engine` 的 `e==0` 判真 ⇒ `test_named_face` 10/18 · 自测 397/404 · **语料同源对拍 12/12 档 DIFF(3 档 rc 1→0 = 拒绝静默消失)**)——两条均**精确回滚**(源 sha 对拍 + 二进制 sha 复原)。 +- **登记(转 P6 / 待裁决)**:① **冻结基线二进制不入库**(`/tmp/p5t0/base/` 为 /tmp 产物)⇒ 该腿跨会话不可复现(复现配方 = `jj` 检出 P4 收官 `9bcb7083` 重建);**建议落进仓库**(`tools/p5-baseline/`:二进制或「重建配方 + sha 白名单」)。**T7 复核(2026-09-14)**:本轮 `/tmp/p5t0/` 仍在位 ⇒ 对拍腿成立(72 档零差异);配方 + sha 白名单已登记 **P6 附录 E-13**(维护者裁定 = **重建,不落二进制**)。**→ 已闭合(R2 P6 Task 1,2026-09-14;见 #50)**:配方 + 白名单 + 两条 runner + 探针语料(**29 档**,U-4 收口)入仓 `tools/baseline/` + `tests/probes/`。② 文件名 `ty_shadow.cr` 与 `sh_` 前缀**保留**(改名触三面清单、收益仅命名)⇒ 登记;若改 = 独立小批。③ 突变控制选样教训:突变语料必须选**该突变语义覆盖到的**面(首轮 6 档全 SAME 的教训,配方见报告 §6/`/tmp/p5t5/mut3.sh`)。 +- **关联**:#24/#30(本条划销「影子层(`--type-shadow`/`replace_*`)同族下线」残项;`--verify-named-dedup` 面 = 上文偏离登记)· #47(T4 = 前置,中间态)· #45/#46(P5 T2/T3)· #31(CI 挂点缺口——本任务补挂一档)· #44(P5 收官/P6 交接)· 计划 Task 5 实施记录。 + +### 49. R2 P5 收官(2026-09-14——全量回归 + 判据复验 + 统一台账 + 文档 + P6 交接包;**P5 全阶段收官**) +- **提交链(12 提交;计划 `docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md`)**:`2b9b5463`(T0 前置侦查 + 冻结基线,源码零改动)→ `11913f8a`(T1 `.cir` 暖态 SIGSEGV 根因修复)→ `7f6c38cb`(T2 DFNode.TK 单槽化 α)→ `461d96f3` + `5a333e0c`(T3 unknown 清零 = 命名面判定化 + 收口注)→ `faca94e0`(T3b 不变槽元素三态 `te_elem_cmp`)→ `e6db38b8`(T4 删 legacy + 回落面 + 桥接副本 + 死分支/站点 6;引擎唯一权威 + ICE04 P-A)→ `b6bf33cb` + `d65ff9ad` + `66316751`(T5 影子层下线 + 回归网切换 + 两收口注)→ `04c20487`(T6 `EXPR_LET` 判定点 = TODO #32 收口)→ 本条目(T7 收官)。 +- **全量回归(本实例实跑;构建确定性 ×2 逐字节同 = `corec f737e26b…`/`corearch 544e9201…`/`corelsp 4251108b…` ⇒ 提交树 == 被测二进制度)**:五 CI job 全 rc=0 · 全枚举 **58/58 rc=0**(51 selfhost + 7 bootstrap)· `selftest-types` **404/404** · `check src/compiler` rc=0 · 自举链 `corec2==corec3` `cmp` IDENTICAL(`5559beef…`,2854910B)+ N06=0 + 冒烟 42(corec/corec3 双通道)。 +- **判据复验(本实例实跑;证据 `/tmp/p5t7/`)**:ELF canary **`95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 两套口径冷态四条(`ptr_arith` 90823B `fb4a3b59…`/90966B `592afa31…`;`generics_test` 135341B `ddec1ce6…`/135484B `cd2af565…`;差恒 143B)· `--dump-objects` 两口径 3521 行 `cmp` IDENTICAL · `.cir` DOT 165915B `b1bdf480…` IDENTICAL · **冻结基线同源对拍 72 档 rc + 日志零差异**(pre-P5 `5d2b15ad…` × 当前源 vs `f737e26b…` × 当前源)· **行为探针全集 30 档零差异** · **突变控制 2 条**:M1 派生面(去辅码 ⇒ 自测 400/404 + `test_ccr_types` 35/40 + `.ccr` sha 变)· M2 判定面(`e==0` 判真 ⇒ `test_named_face` 10/18 + 自测 397/404 + 语料对拍子集 **12/12 档 DIFF**,3 档 rc 1→0 = 拒绝静默消失)——回滚精确、仓库零改动。**冷/热豁免表三类缓存态复测**:缓存启用档(`ptr_arith` 16 条目/`chan_test` 46 条目)真命中、ELF 冷≡暖、`.ccr` 冷≠暖(预存豁免)但各自确定性;含泛型档(`generics_test`)与可选档 `entries=0`(既有策略关缓存)⇒ 冷≡暖。 +- **统一台账(T0–T6 汇总;P5 计划「统一台账」节)**:删除/退役 **6 件 + 1 改名族**(`type_equal_legacy` + 7 自调 + 2 回落 · `g_replace_*` 计数 · 桥接 legacy 副本 · 站点 6 + `ir_gen` 死分支 · 影子层 12 函数 + `sh_finish` + 17 全局 + 2 CLI 旗标 + 9 挂点 · hits 存-复原 hack;`g_shadow_map` → `g_term_map`)· 收紧 **5 类**(D23 保存拒绝 · TG02 混类 · `te_elem_cmp` 不变槽 0 · ICE04 硬错 · TA02 硬错)· **放宽 0** · 修复 **4**(暖态 SIGSEGV / F1 r9 残留 UB / 声明位点静默错产物 / 死分支)· 实施偏差 **8 条**(含「`--verify-named-dedup` 未删」与「P4 六排除项再裁」)。全语料命中逐任务 **0**(report-only 先行)。 +- **文档**:统一设计 spec **§9 P5 行 ✅** + §3.4(单槽化落地形态)/§6.2(单槽落地 + D20 稳定引用形态规范)/§6.3(corearch 读回承接面 = β/P6);format spec §3.3(NOD `tk` = **派生码** + D20 禁令)+ 头部 P5 追加注 + `test_ccr_types` 34→40;findings **§16 P5 收官(文件自冻结)**;本计划:实施状态总表 + 收官总述(一页)+ Task 7 实施记录 + 统一台账 + **附录 E 增补 7 条**。 +- **P6 交接包 = 计划附录 E(16 条;现状/出处/影响面/建议/前置)**:1 盘面项索引 + `atom_of` 读回(β)· 2 对象布局表示位 · 3 `MAX_*` 解除 · 4 可选程序收回 `.cir` 缓存 · 5 `.csr`/TagNode · 6 命名消费语法/iterable/形状可写视图 · 7 `tt_display` 零 `str_intern` + 改名残项 + `MIN_CASES` · 8 陈旧工具/副本 · 9 纪律继承 · **10 ¬ 面过判(T3b)** · **11 `-> never` 调用面(T6)** · **12 #20/F3 调用位点** · **13 冻结基线复现配方(维护者裁定 = 重建,不落二进制)** · **14 ICE04/P-A 签核 + 非硬名单诊断不阻 `build`** · **15 `string == int` 混比 + >1GiB 分片** · **16 设计先例引用面**。 +- **划销/收口**:#24/#30 的 P5 继承项(①②③④)全部落地或注记(`type_equal_legacy`/`sh_*_ak_legacy`/站点 6/unknown 清零)· #32(T6)· #43 ⓪ 暖态 SIGSEGV(T1)· #48 的「回归网切换」已由 T5 落纸、T7 复跑成立。**待维护者签核(本批遗留)**:ICE04/P-A 政策(回退面 = 一行)· `--verify-named-dedup` 偏离 D27 的保留裁决(回退面 = 一行)· 冻结基线配方落位。 +- **关联**:#44(P4 收官/P5 交接)· #45–#48(P5 逐件落地)· #5 家族(暖态 SIGSEGV 闭合;冷/热 `.ccr` 分歧本轮量化并豁免)· #26(判据口径)· 计划 `docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md` · 报告 `.superpowers/sdd/p5-task7-report.md`(工作区)。 + +### 50. R2 P6 Task 1:回归网可复现化(2026-09-14——E-13 配方/白名单/runner/探针语料入仓;U-4「30 档」收口) +- **交付**:本提交(路径限定 8 路径 = `tools/baseline/{REBUILD.md,rebuild.sh,parity_run.sh,probes_run.sh}` + `tests/probes/`(README + 29 档语料)+ `src/ci/run.sh`(自述块改写)+ `TODO.md` + 计划)。详版 = `.superpowers/sdd/p6-task1-report.md`;配方 + 调用方式 = `tools/baseline/REBUILD.md`。 +- **形态(E-13 兑现)**:冻结基线 = **源出 pinned revision `9bcb7083`(P4 收官)的三二进制**,白名单 `corec 5d2b15ad…` / `corearch 493dc490…` / `corelsp 18b94bd9…`;**二进制不入库**(维护者裁定),入仓 = 配方(`rebuild.sh`:`jj workspace add --revision` + `build_selfhost_native.py` + sha 硬闸 + 冒烟 rc=42)+ 白名单 + **换代纪律**(不符不得改白名单对齐)+ 两条 runner + 探针语料。**CI 不挂钩**(`.github/workflows/core-ci.yml` = `actions/checkout` + `fetch-depth: 2` 浅检出 + 无 `jj` ⇒ pinned revision 不可得;本腿为手工判据——评估结论落报告)。 +- **runner 面**:`parity_run.sh `(72 档 `check`,分层/命名/逐档 `clean-cache` 逐字继承迁移期 `/tmp/p3t0_run.sh`;**影子分支剥除**——旗标已随 P5 T5 删除;`nullglob` + 计数断言 72 硬失败)· `probes_run.sh `(29 档,显式清单 + 计数断言;**不再有字面 glob 伪条目**)。 +- **U-4 收口(「30 档」的账)**:正式值 = **29 `.cr`**(`/tmp/p5t0/probes` 11 + `/tmp/p5t3/probes` 18);P5 台账的「30」= 29 + **1 条字面 glob 伪条目**(`_tmp_p5t3b_probes_*.cr`,rc=1,log = `error: cannot read …/*.cr`——未匹配 glob 被当文件名;两态恒等 ⇒ 该条对拍**空洞**)。`/tmp/p5t3b/probes` = T3b **输出**目录(21 log + 6 dump),**非**语料目录 ⇒ 「探针源已丢」的初判**修正**(源全在位)。**「30」此后不得引用**;入仓 29 档 sha 与 T0 清单 **29/29 逐条同**(实拷保真,源内不加注)。 +- **不可复跑面(登记,不假装)**:① T3b 变异态 15 件(`.M{1..4}.log` + `.M2.dump`)需**先重造变异二进制**(配方 = `p5-task3b-report.md` §3.3);② 42 行 `[type-shadow…]` 摘要**无二进制可产**(通道已删)⇒ 不得引用;③ 红态(pre-T3b)复现须用冻结基线(仍带旗标,T0 §3.3 已实测逐字节同)。 +- **关联**:#48 登记①(本条闭合);计划 `docs/superpowers/plans/2026-09-14-r2-p6-tail.md` Task 1;T0 = `.superpowers/sdd/p6-task0-report.md`;`src/ci/run.sh` 自述块与 `REBUILD.md` 互为指针。 + +### 51. R2 P6 Task 3:β 盘面项索引 + `CCR_VERSION` 8→9 + corearch `atom_of` 读回(2026-09-14——E-1 收口;U-2 先裁 = L-a / U-3 = (i)) +- **交付**:`.ccr` NOD 记录 **36 → 40B**(+28 = `item i32` = **TYPE 段文件空间项索引**,-1 = 无项;邻接域顺移 +32/+36)+ `CCR_VERSION` **8 → 9**(旧 v8 及更早整类拒收——D10 先例)+ `tt_atom_of_term` 自 `ty_shadow.cr` **迁入共享层 `type_terms.cr`**(corearch 清单不含桥接层)+ corearch `--dump-nod-items` 读回通道(逐节点 `item → 项表取项 → atom_of → 行`)。写侧装填 = `ccr_types.cr:ccr_nod_item_populate`(**保存期**由盘上派生码经既有分类/建项单源重派生——**不得**沿用 emit 期活表索引:`ccr_type_populate` 的 `tt_layer_reset` 在 save 前作废之,D20-③/`bad_term=329` 红证);读侧 = `load_ccr` 的 TYPE 段后**一致性硬校验**(`item < -1` / `≥ tt_count()` / `atom_of(项) ≠ tk` ⇒ 拒绝 rc=1——**不得**静默当无项)。`.cir` 快照**零改动**(`CIR_CACHE_VER` 保持 17)。 +- **D20 合规(L-a 先裁三条论证)**:① 跨边界引用零新增(只引入文件内项索引,D20-② 唯一许可形态);② 「码 = 派生量」不变(两值同源于单次装填,无第二套建项逻辑);③ **分歧即硬拒**(消灭「盘面值静默胜出」的危害模型)。**登记未取**:更严单源形态 **L-c**(旧槽改载辅码、码完全装载侧重派生——无并存,但改发射路径输入 ⇒ canary/dump 由构造性降为须实测,须维护者追加裁定)。**L-b 排除**:单槽「项索引 ∪ 辅码」+ 分类表判读 = **读侧不可判定**(分类表恰需被丢弃的派生码;反例 = `tests/suite/ptr_ref_first.cr` 的 `IR_BINARY tk=10` 复合行码与项索引同域)。 +- **判据(实测)**:五 CI job 全 rc=0 · 全枚举 **58/58** · `selftest-types` **404/404** · `check src/compiler` rc=0 · **ELF canary `95084e7b…d475`(28822B)IDENTICAL** · **`--dump-objects` 两口径逐字节同**(vs T0 记录 `cmp`·`generics_test` 3521 行)· `.ccr` 两口径四条重锁 + **逐段 delta 归因 = 4B × 节点数**(pa 90823→96015 / 90966→96158;gt 135341→142793 / 135484→142936;口径差恒 143B 不变)· **72 档 `check` 同源对拍零差异**(构造性隔离腿:`check` 在 `main.cr:448/:450` 早退,不读不写 `.ccr`/`.cir`)· 探针 29 档零差异 · 自举链 `corec2==corec3`(2859006B)+ N06=0 + 冒烟 42 + `--help` rc=1 · `test_backend_bootstrap` rc=0 + `error[`=0 · `test_ccr_types` 40→**48** / `test_ccr_v7` 27/27 / `test_region_cfg` 22/22(三处 v8/36B 重锁)· **突变 M1–M5 逐条转红 + 精确回滚**(M1 item 槽写码 ⇒ loader 拒;M2 删整段校验 ⇒ 越界静默接受;**M3 改用 emit 期活表索引 ⇒ loader 拒——save 期重派生的唯一机器抓手**;M4 版本闸放水 ⇒ v8 假文件被接受;M5 只删交叉校验 ⇒ 域内非原子项被接受)。 +- **偏差登记**:① 计划 Task 3 Step 3「项索引与派生码同出 emit 期那一次拆分」与实读时序矛盾(`tt_layer_reset` 在 save 前作废 emit 期引用)⇒ 实施取「保存期由码重派生」;② 计划 Files 漏 `regalloc.cr`/`type_terms.cr`/`tests/selfhost/test_region_cfg.py`,且把 corearch 读回挂在 `main.cr`(实为 `corearch.cr:243` + `src/arch/x86_64/regalloc.cr:877`)⇒ 勘误并入计划 Task 3 节;③ 计划锚点 `src/compiler/ent_kernel.cr` 实为 `src/lattice/ent_kernel.cr`。 +- **残留(登记,不静默)**:暖态(`.cir` 缓存命中跳过 ir_gen)文件对被缓存函数引用、而暖进程未分配的类型行 ⇒ `item = -1`(码由 `tk` 槽保真)⇒ **暖态项面为部分**(冷态完整;各自确定性不变);若未来需暖态完整项面 ⇒ 独立设计轮。 +- **关联**:附录 E-1 · 先裁 U-2/U-3 · P5 T2 §5-④(暖态项面)· D20/D22/D23 · #45/#46(P5 T2 单槽化)· `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §6.3(β ✅)。 + +### 52. R2 P6 Task 4a:`-> never` 调用返回型(2026-09-14——E-11 收口:两站点误报消除;发射面零变化) +- **根因(前置侦查实读)**:`fn boom() -> never` 的**调用**被推断为 unit——声明注册趟的域守卫(`checker.cr` 的 `rt_mapped != TI_NEVER`)把 NEVER 钳成 unit(`never` 在 parser 侧是 ast-kind-0 基类型节点 ⇒ 走裸码路径)⇒ SYM_FN 符号类型 = unit ⇒ 两站点:① `x : int = boom();` 误发 TA02(声明位豁免 `val_ti == TI_NEVER` 不触发);② `return boom();` 误发 TF01(块类型 = unit ≠ TI_NEVER ⇒ 返回位豁免不触发)。**实证**:RED 实跑 rc=1 + `error[TA02]: Variable declared as int, got unit` / `error[TF01]: Function return type mismatch`。 +- **交付**:`src/compiler/checker.cr` **两站点守卫**(直调 `EXPR_CALL` 非泛型尾 + 方法调用姊妹站点;后者同形判定 = 同 `sym_type(si)` / 同 `fi_return_type` 可取(mangled 名经同一 `parse_body`)/ 同发射面消费者 `ir_gen.cr:1662-1663`)。守卫四条:泛型由既有早退排除 · `sym_type(si) == TI_UNIT` · `fi_return_type(fi) == TY_NEVER` · `ast_kind(fi_ast_node(fi)) == EXPR_FN`(**extern 面按裁④ 不动**——extern 亦在 g_funcs 且 `fi_return_type = TY_NEVER`,无 ④ 则被一并透传并破 `t6_S3_never_unit`);方法面补 `fi_generic_count == 0` 对齐泛型面。**嵌套 `if` 为硬约束**(bootstrap 构建的二进制对 `&&` 两侧无条件求值,且 `fi_return_type`/`ast_kind` 无护栏)。**只改推断结果**:`sym_type(si)` 值不动 ⇒ `ir_gen` 的调用结果型零耦合。 +- **重钉 1 条(裁①)**:`tests/selfhost/test_iface_ops.py` 的 `t6_S1_never_unit` 改前钉的是**误报**(rc=1 + TF01@2)⇒ 改判 rc=0(正确行为:`f()` 发散 ⇒ `return x` 不可达)。 +- **用例 +9**:`test_let_check` 23→(实读 24,注释陈旧自纠)→ **28** · `test_tf01_fallthrough` 19→ **22** · `test_iface_ops` 76→ **78**(主判据 5 / 正控 5 / 登记钉 4;见报告)。 +- **判据(实测)**:五 CI job rc=0 · 全枚举 58/58 · `selftest-types` 415/415 · `check src/compiler` rc=0 · **canary `95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 = T3 新锁值(96015 / 96158 / 142793 / 142936;差恒 143B)· **72 档对拍零差异**(误报消失类 diff 的载体 = **套件 leg**,不在 72 档语料)· 探针 29 档零差异 · 自举链 + N06=0 + 冒烟 42 + `--help` rc=1 · `test_backend_bootstrap` rc=0 + `error[`=0 · 突变 Mu1–Mu5 逐条转红 + 精确回滚。 +- **同族未修(裁④ + 新登记)**:泛型调用(`res_call_type` 的 NEVER 钳位 + 自测 `t6.never_cell_diff`)· extern(注册域守卫)· 模块限定(`g_mod_func_tis`)· **`EXPR_ASSIGN`(赋值位)无 never 豁免**——`busy = boom();` 改前改后**都**报 TA01(既有缺口、非本改动引入;声明位/返回位/if 合并三处已有豁免,赋值位是剩余缺口)⇒ 本条目登记,待排期。 +- **E-12 半身(未实施,挂裁-P6-2)**:见附录 E-12 勘误——「非泛型调用位点」**不存在**(已知 SYM_FN 的非泛型调用分支连实参推断都不走)⇒ 实施 = **新增**判定点;锚点与参数型取法已入计划 Task 4 Step 4。 +- **关联**:附录 E-11/E-12 · 计划 Task 4 节勘误 · #20(F3)· #32(声明位判定,同族豁免面)· P3 附录 A(「never 在调用位点被当 unit」的 P3 待裁决项 ⇒ 本批收口)。 + +### 53. R2 P6 Task 5:文档/卫生批(2026-09-14——E-7/E-8/E-16 收口:MIN_CASES 收紧 + 废弃标注 + 陈旧值与先例载体改写;**零行为变化**(重建三二进制逐字节同)) +- **交付**:本提交(路径限定 = `tests/selfhost/test_type_engine.py` · `tools/module_to_ccr.py` · 两 spec(`2026-09-11-explain-predict-incremental-design.md` / `2026-09-11-policy-injection-design.md`)· `TODO.md` · 计划)。工作树 = 合流树 `/tmp/p6mrg`(head `9bdbdf16`);证据 `/tmp/p6t5/`。 +- **逐件**:① `MIN_CASES` 32 → **415**(+「只增不减」注:新增/删除用例须同步下界;判据 = `test_type_engine.py` 绿)② `tools/module_to_ccr.py` 头注废弃块(version=1 / `ccr_io.cr:135` `CCR_VERSION = 8` 整类拒收 / 零 in-repo 调用者 / **保留不删**,删除候选登记)③ `explain-predict` **5 处**(`:371` 三陈旧:旧值 14 → **17**、行号 `:16` → **`:51`**、**magic `C1C1…` 分句删去**(该断言与实测不符——自举二进制落盘 magic 全 0,P4 T5 / TODO #14 登记);`:84`/`:181` 旗标计数 `8 → 7`;`:127`/`:447` 先例载体 → `--verify-*` 家族 + 行为探针)④ `policy-injection:275` 同款改写 ⑤ **`run.sh` 零改动**(逐条实测复核:其全部 `N 例` 注与实测一致——T2/T3/T4a 已各自同步;陈旧「14 档挂点」在**本计划**,不在 `run.sh`)⑥ **计划勘误**(Task 5 节实况化 + 侦查底座陈旧锚(挂点 28 / 自述块行域 / 计数注行号)+ T1 记录里一句不精确)。 +- **判据(本实例实跑)**:**重建 ⇒ `corec ae01de75…` / `corearch 228f82e9…` / `corelsp 90eb19c6…` 与编辑前逐字节同**(T5 零 `.cr` 改动 ⇒ 零行为变化的**构造性证据**)· 五 CI job 全 rc=0 · 全枚举 **58/58** · `selftest-types` **415/415** · `check src/compiler` rc=0 · canary **`95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 = T3 新锁值(96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…`;差恒 143B)· **腿① 同源对拍 72 档零差异**(冻结基线 `/tmp/p6t1/base/corec` × 当前源 vs 当前二进制 × 当前源;rc 34×0/38×1 两侧同)· `test_backend_bootstrap` rc=0 + `error[`=0 · 自举链 `corec2==corec3` IDENTICAL(`0b2e06d0…`)+ N06=0 + 冒烟 42 + `--help` rc=1。 +- **grep 复核(实跑)**:`--type-shadow` **无活跃引用**(`src/` 8 条 + `tests/` 3 条**全为**「已下线/历史」注;旗标注册面三处实核 = `main.cr`/`corearch.cr`/`targets/x86_64-linux/main.cr`,`--type-shadow*` 零在位);陈旧版本值(14)在 `docs/ src/ tests/` **零引用**(修靶后);`MIN_CASES` = 修靶(`test_type_engine.py:20` = 415)+ 邻域(`test_purity.py:27` = 26)。 +- **登记(转 T6 / 待办,本任务不代改他节)**:① `tools/module_to_ccr.py` **删除候选**(本任务只标废弃;删除 = 独立决定)② `test_purity.py` 的 `MIN_CASES = 26`(同形面,未纳入 E-7 点名)③ **T0 缺失实施记录归 T6**(T5 只登记、不动 T0 节——理由:章程边界 / 与 T6「统一台账」同类 / 避免计划文件被两批各改一次)④ **T2 无 TODO 条目**(#50=T1 / #51=T3 / #52=T4a ⇒ 建议 T6 补或 T2 自补)⑤ `pkg/` 非入库面(`.gitignore` 覆盖;仅报告登记,不进提交)。 +- **关联**:附录 E-7/E-8/E-16 · 计划 Task 5 实施记录 · T1 报告 §9-5(T0 记录)· T0 报告 §3/表 D-4(探针 29 / 挂点 28)。 + +### 55. TC02 收口:`if` 分支相容判定的**发散豁免**(2026-09-14——真误报修复;fail-closed 面 TC02 豁免条可撤) +- **现象**(归因 = `/tmp/fct1/tc02_attribution.md`,证据同目录):`if { } else { return 1; }`(及镜像形)在 `check` 面恒报 `error[TC02]`,而语义上 else 支**发散**(不产出值)⇒「分支值类型须相容」无对象。**与 float 无关**(纯 int 条件同报)、**预存**(冻结基线 `5d2b15ad…` 同值)、**对称**(then 发散同样命中)。 +- **根因**:`EXPR_RETURN` 的推断 = **所返回值类型**(`checker.cr` 的 EXPR_RETURN 分支)⇒ `{ return 1; }` 的块类型 = `int`;而 `if` 的值类型定义为 **then_ti**(`infer_expr` 的 EXPR_IF 合并点 `return then_ti`)⇒ 合并点只看 `then_ti != TI_NEVER && else_ti != TI_NEVER`(`:2907`)⇒ 发散支的幻影类型参与相容判定 ⇒ 误报。 +- **修法(P3,维护者 2026-09-14 裁)**:新增发散谓词 `stmt_diverges`(return 族 + 包裹层 + 块内任一句;**只服务本判定点**——**不得**用于 TF01:那里豁免 return 会洗白真错面,见 `stmt_cannot_fall_through` 头注明令),`:2907` 加嵌套 `if` 守卫:**仅 else 支发散 ⇒ 不报**。**不对称是有意的**:then 支发散时 then_ti 本身即幻影(模型面,另案)⇒ 仍报,保留真信号。**明令不做** = 改 `EXPR_RETURN` 推断(会连带关掉 TF01 ⇒ 静默错产物)。 +- **判据(本实例实跑)**:新套件 `tests/selfhost/test_tc02_branch.py` **15/15**(挂 `run.sh` selfhost-tests)· 五 CI job 全 rc=0 · 全枚举 **58/58**(`test_native_float` 两正例 `float_normal_cmp`/`float_arith` 在 fail-closed 裸反转口径下**由红转绿**)· **ELF canary `95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 **96015/96158/142793/142936**(差恒 143B,逐字节同)· `--dump-objects` **3521 行** · `.cir` DOT **165915B** `b1bdf480…` · `selftest-types` **415/415** · 自举链 `corec2==corec3` **IDENTICAL**(`c8c63985…`,2880022B)+ N06=0 + 冒烟 42 + `--help` rc=1 · 72 档对拍**零差异**(34×0/38×1)· 探针 29 档零差异 · **report-only 全语料扫面**(590 内联源两态):**仅 2 处变化、均为 TC02 消失、均在 `test_native_float`**(无新增码、无非 TC02 变化)· 突变 **M1**(去守卫)⇒ 新套件恰 3 例复红(`tc02_else_return_silenced`/`_after_stmt`/`_stmt_position_multi`)+ `c01/c02/c05` 复报;**M2**(放宽为「任一支发散」= P1 形态)⇒ 新套件 `tc02_then_return_kept` 转绿 = **越界信号被抓**(14/15);两突变均仓库外实拷贝,复位后二进制复原。 +- **登记**:`stmt_diverges` 未覆盖面 = `break`/`continue` 收尾的分支(需循环上下文);`x := if c { } else { return 1; }`(值被用且 else 发散)形态按 P3 不报——if 型 = `then_ti` = unit 是真值 ⇒ 误用由 TA02/TF01 别处兜住(**未构造端到端用例**,实施时登记)。 +- **对 fail-closed 线的影响**:`/tmp/fct1/exemption_draft.tsv` 的 **TC02 豁免条可撤**(退出条件「逐例归因后」已达成)——由 FC 批 T2 统一调整。 + +### 54. R2 P6 收官(2026-09-14——尾批六任务链 + 统一台账 + 尾批终态陈述;**P6 全阶段收官**) +- **提交链**(计划 `docs/superpowers/plans/2026-09-14-r2-p6-tail.md`):`bc4f0a43`(计划定稿 = 附录 E 16 项分诊:实施 7 / 待裁 7 / 维持登记 1.5)→ **T0** 前置侦查 + 冻结基线重建(pinned `9bcb7083`;源码零改动;四表 + 守门复验)→ `e5ce8f9e`(**T1** 回归网可复现化 = E-13)→ `911c692d`(**T2** ¬ 面过判 = E-10)+ `0819d506`(**T3** β = E-1)**兄弟提交**经合流态由 `9bdbdf16`(**T4a** `-> never` 调用返回型 = E-11;**双亲合并提交**,一次落地)→ `4b909996`(**T5** 文档/卫生 = E-7/E-8/E-16)→ 本条目(**T6** 收官)。 +- **判据(本实例实跑;工作树 = 合流树 `/tmp/p6mrg`;证据 `/tmp/p6t6/`)**:构建确定性 ×2 **逐字节同** · 五 CI job 全 rc=0 · 全枚举 **58/58** · `check src/compiler` rc=0 · `selftest-types` **415/415** · canary `95084e7b…d475`(28822B)**IDENTICAL** · **入仓配方重建**(`tools/baseline/rebuild.sh`)三 sha = 白名单(`5d2b15ad…`/`493dc490…`/`18b94bd9…`)+ 冒烟 42 + 零残留 · **腿① 同源对拍 72 档零差异**(两侧 34×0/38×1)· **探针 29 档两态零差异**(17×0/12×1)· `.ccr` 四条 = **T3 新锁值**(96015 `680a6f98…`/96158 `76f36e6a…`/142793 `41e9d845…`/142936 `704316c8…`;差恒 143B)· `--dump-objects` 3521 行 · `.cir` DOT 165915B `b1bdf480…` · 自举链 `corec2==corec3` IDENTICAL(`0b2e06d0…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · `test_backend_bootstrap` rc=0 + `error[`=0。 +- **统一台账(KPI)**:实施 **6** · 删除 **0**(+1 候选)· 迁移 **1**(`sh_atom_of_term` → 共享层 `tt_atom_of_term`)· 收紧 **3 类**(¬ 面异身份不交 / `.ccr` 版本闸 v9 整类拒收 / loader 一致性硬校验)· 放宽 **1 类**(`-> never` 调用返回型)· 修复 **1 件**(+1 并记)· 登记 **19** · 偏差 **9** · **未取裁 7**(逐条 = 计划「统一台账」§1–§7 + 「尾批终态陈述」节)。 +- **本批特有(记在账上)**:① **事故** = `.superpowers/sdd/p6-incident-2026-09-14-t3-preempt.md`(T3 抢跑:零提交/零还原;护栏 = 「含先裁的任务把护栏写进任务卡正文」)② **提交形态** = T2/T3 兄弟提交 + T4a 双亲合并提交(协调者裁:接受,不拆)③ **T4a 过程事故** = CI job 执行期间改 `run.sh` ⇒ bash 读截断 rc=2 ⇒ 源冻结后重跑五 job。 +- **补记**:**T0 实施记录**已由 T6 回填计划 Task 0 节(六步勾选 + 记录块)——T0 当批只出报告(`.superpowers/` 不入库)⇒ 计划内零留痕;归属裁定 = T6。 +- **待维护者处置**:① **四裁定门全未取裁** = 裁-P6-1 容量批归属(推荐转独立批)· 裁-P6-2 政策五问(ICE04 签核 / 非硬名单诊断 / 入闸判据 / `*T←0/None` / `--verify-named-dedup`)· 裁-P6-3 `string == int` 混比 · 裁-P6-4 `EXPR_FOR`/iterable;另 U-2 的 **L-c** 备选 + T2 正×正面候选(`lit_empty_pair`)② **主树遗留** = 工作副本 9 未提交文件(T2 过时草稿 + T3 抢跑残留)+ `build/*` 为 T3 味(`aa7bdd5d…`/`28c5d669…` ≠ P6 起点白名单)⇒ 最终整合涉**还原** ⇒ 须维护者明确许可。 +- **建议补条目**:**#55 = T2(¬ 面过判,E-10)的落地登记**(#50=T1 / #51=T3 / #52=T4a / #53=T5,**T2 独缺**;内容可自 `.superpowers/sdd/p6-task2-report.md` §2–§5 摘录)。 +- **关联**:#49(P5 收官 / P6 交接)· #50–#53(P6 逐件)· 计划附录 E(16 项)· spec §9 **P6 行** · 计划「统一台账」/「尾批终态陈述」· 下一批指向 = **容量批 或 验证切片轮**(按裁-P6-1)。 + +### 56. ⚠ 指针写可选槽 × 表示位/规范化互斥:`*p = Some(9)` **双路径分歧的静默错值**(容量批 T2 登记——**今天就存在 · 预存 · 本批不修**) +- **现象(本实例实测;证据 `/tmp/capt2/probes_out*/c3_ptr_local_boxed_write.*`)**:`x : int? = 5; p := &x; *p = Some(9); return match x { Some(v) => v, None => 0 };` ⇒ **ELF rc=8 / interp rc=216**(双路径分歧 + 双错值)。成因 = `x` 的表示位为 **0(裸)**而经指针写入的是**装箱值** ⇒ 解包按裸分支把对象指针当载荷。 +- **三子形态(同一根因)**:① `c1`(位=0,写裸 `7`)**今天返回 7 = 正确但脆弱**(依赖位与值恰好同态)· ② `c2`(位=1,写裸 `7`)**139 响亮** · ③ `c3`(位=0,写 `Some(9)`)**静默分歧**(本条)。 +- **根因链**:表示位只在**直接写点**置位(`emit_rep_set`)⇒ 指针写(`IR_STORE_PTR`,`ir_gen.cr` EXPR_ASSIGN 的 UOP_DEREF 分支)**绕过**表示位;同一 IR 站点同时服务**局部**(权威 = 表示位)与**聚合槽**(容量批 T2 起权威 = 恒装箱);且「指针指向哪类存储」**别名不可静态闭合**(`q := p` 拷贝即漏判)⇒ 无条件装箱会**新增**静默错值(`c1` 由正确转错)⇒ 容量批 T2 裁 (A):**登记不修**(彻底闭合需「表示位随存储走」= 布局变更,与裁-CAP-1 (a) 零布局变更互斥)。 +- **关联未覆盖面(同批登记)**:数组元素经指针(`p := &a[0]; *p = 7` ⇒ 139/139)· 全局经指针(`p := &g; *p = 5` ⇒ ELF 0 / interp 139 分歧)· **`&s.a` 写入丢失**(`UOP_REF` 无字段分支 ⇒ 取到**临时量**地址,`*p = 5` 静默无效——独立预存缺陷)· 局部经指针 139。**待裁候选 (D)**:语言面拒绝 `*p = v`(pointee 为 `T?`)——已实读影响面:指针路径下的聚合**今天无一正确**(139/0)⇒ (D) 的真实影响 = 拒绝**已错**形态(非拒绝正确代码),留维护者裁定(容量批 T2 报告 §8-4)。 +- **出处**:容量批计划 `docs/superpowers/plans/2026-09-15-capacity-batch.md` Task 2 · 报告 `.superpowers/sdd/cap-task2-report.md` §8-1/8-3。 + +### 57. E-4 可选程序 `.cir` 缓存门:**机制实证证伪 ⇒ 维持现状(裁 (c))**;「移除门」登记为待裁候选(容量批 T4) +- **现状**:`main.cr:484-490` `if g_optrep_on != 0 { cache_enabled = 0; }` —— 可选程序**关 `.cir` 缓存**(代价 = 每次全量重建)。原依据(R2 P4 Task 5)=「表示位侧表是编译期进程内状态、快照不载 ⇒ 命中恢复路径与冷路径**产物分歧**」。 +- **本批实证(裁-CAP-3「先机制实证」)**:沙箱 = 全树副本 + **唯一改动**(注释掉该门三行);冷/暖(全命中,条目数不变为证)× **16 档**(4 基础可选含跨函数信道/聚合面/条件写 + T2 聚合面 12 档)⇒ **ELF 逐字节全同**;**混态**(删 `main`/被调/全部条目制造 miss)3 态 ×2 程序 ⇒ **ELF 逐字节全同**。⇒ **原机制未能复现**(与静态枚举一致:`irv_rep`/`irv_set_rep` 全部读取点在同函数生成内 ⇒ 命中恢复的函数 IR 自含表示语义)。 +- **处置(裁 (c))**:门**原样保留**(保守,零源码改动)。 +- **待裁候选(性能面)**:**移除门**(可选程序恢复缓存)。收益 = 编译耗时;风险 = 依赖「命中路径自含」不变量(本次 16 档 + 6 混态未证伪,但**非全语料**);若裁可 ⇒ 需补:自源可选程序冷/暖对拍 + `test_optional` 同路径冷/暖对拍 + canary/`.ccr` 判据。 +- **出处**:容量批计划 Task 4 · 报告 `.superpowers/sdd/cap-task4-report.md` · 证据 `/tmp/capt4/**`(沙箱 `/tmp/capt4/sbx`)。 + +### 59. FC 批 T2:fail-closed 闸门落地(2026-09-15——**默认阻断 + 豁免登记表 9 条**;旧正向名单 11 码退役) +- **语义**:任何**不在豁免表内**的类型面诊断 ⇒ `rc=1` + 零产物(判据成文化 =「判定继续 ⇒ 产出静默错产物」,五组先例:R002 / TS01-04+TK02 / TM03 / ICE04 / TA02)。**语法面闸(`main.cr:134/:135`)与安全检查面闸(`:594-603`)本就不变**(后者已是「任意新诊断 ⇒ rc=1」)。 +- **落点**:`src/compiler/main.cr`(硬判定循环改为 `diag_gate_exempt(ec, GATE_SCOPE_BUILD) == 0 ⇒ hard = 1`;旧 6 条语句行/11 码名单整体退役)· `src/compiler/diag.cr::diag_gate_exempt`(**豁免表 9 条**:build ×6 = TF01/TF07/TB01/TM04/TK01/B04;check ×3 = N01/N06/N11 **仅登记**——裁-FC-1 = (C) 终局,check 面 rc 规则(`:443-451`)不消费本表 ⇒ 72 档基线不换代)· `src/compiler/globals.cr`(`GATE_SCOPE_BUILD/CHECK` 常量)· `main.cr:702` 后(零产物:corearch 失败 ⇒ 删**本次**写下的 `.ccr`;既有旧文件不删——裁-FC-6)· 新套件 `tests/selfhost/test_diag_gate.py` **17 例** + `run.sh` 挂点。 +- **判据(本实例实跑)**:五 CI job 全 rc=0(冻结源后重跑)· 全枚举 **60/60** · `selftest-types` **415/415** · **canary `95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 96015/96158/142793/142936(差恒 143B)· `--dump-objects` 3521 · `.cir` DOT 165915B · 自举链 `corec2==corec3` IDENTICAL(`5154425478…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · `test_backend_bootstrap` rc=0 · **72 档 rc 零差异**(34×0/38×1;日志差异 2 档 = **纯行号偏移 +53**(= 本批编辑编译器自身源码 ⇒ 语料含编译器 ⇒ 行号位移;先例照 P6 T2 的 +146)· 探针 1 档 = 进度行(旧硬名单码档,见下))· 突变 **4 条**逐条咬合(M1 删 B04 条 ⇒ ptr_arith 复红 · M2 scope 失效 ⇒ N01 在 build 面静默放行被抓 · M3 去零产物删除点 ⇒ `.ccr` 残留 · M4 全放行 ⇒ 负控转绿)。 +- **report-only 对表(T1 §5)**:**无表外命中**——T1 `would_block.tsv` 的 7 条 build 面行在 T2 下**全部放行**(逐档 rc=0 + 产物实测);34 条 check 面行**逐条不变**(rc + 诊断同);T1 的 6 档红套件(ccr_types/interp_float/interp_parity/match_exhaust/native_float/xcut_iface)**全绿**(枚举 60/60)。 +- **进度行保真(本批的一处细节修正)**:`[5/5] frontend done` 改在「诊断之后、返回之前」打印(原行仅覆盖 `hard==0` 路径)⇒ 72 档语料日志除行号偏移外零差异;**唯一可见差异 = 探针 `n05_recursive.cr` 多一行**(其诊断 `TS03` 属旧硬名单 ⇒ S0 提前返回无该行)。 +- **登记(本批不修)**:① `stmt_diverges` 之外的豁免表**只减不增**纪律(撤条须带根因证据;加条须维护者批);② `scope=check` 3 条在 (C) 下**惰性**(不得据此实现 (B) 活分支)。 + +### 62. FC 批收官(2026-09-15——fail-closed 判据线 T1–T6 全阶段收官;**T4/T5 折入 T2**) +- **交付**:闸门反转(默认阻断 + 豁免表 9 条)· 零产物 · 隐藏通道(默认关)· 新套件 17 例 · 统一台账 + 批终态 = 计划 `docs/superpowers/plans/2026-09-15-fail-closed-diagnostics.md`(「统一台账」「批终态陈述」两节)。 +- **提交链**:`b01a948c`(T2)→ `3990a0a5`(T3)→ 本条(T6 文档/台账)。 +- **判据终值(T6 实测)**:五 CI **5/5 rc=0** · 枚举 **60/60** · `selftest-types` **415/415** · canary **`95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 96015/96158/142793/142936(差恒 143B)· dump 3521 · cir 165915B · 链 `24802386a1…` + N06=0 + 冒烟 42 · `backend_bootstrap` rc=0 · 72 档 rc 零差异 · 探针 29 零差异。 +- **KPI**:实施 3 · 收紧 1 类 · 放宽 0 · 修复 1(TC02,前批)· 豁免 9 条(带退出条件)· 登记 4 · 偏差 6 · 环境干扰 1 起。 +- **T4/T5 折入声明**(维护者 2026-09-15 批准):T4(命中面处置)= 由 T2 的豁免表 + 退出条件覆盖;T5(开门 + 零产物 + CI 同步)= 已在 T2 生效/落定;理由见台账 §2(不静默略过)。 +- **待维护者**:① TODO #60(预存缓存静默缺陷 + runner 暖态腿建议)→ 独立批候选(**已开跑**:2026-09-15 #60 批,T1 实证 + T2 落地见 #60 条);② **环境干扰事件**:`build/` 被外部删两次 + `examples/*.{ccr,cir,o}` 等 **12 个 tracked 文件**被删(工作副本 `D`)——**未提交、未还原**(铁律 #3),提请处置。 + +### 61. FC 批 T3:隐藏通道 `--diag-gate-report`(2026-09-15——report-only 二次核对载体;**默认关、两态零差异**) +- **形态**:`main.cr` 注册 `cli_flag_bool("diag-gate-report", …)`(与 `--verify-*` 家族同址同式)+ 闸门内只读报告:`[diag-gate] face=build blocked=N total=M codes=<用户面码,...>`(用 `error_cat_prefix`/`pad_diag_num` 渲染,覆盖 `g_diag_count > 0` 的前端类型面)。 +- **判据(本实例实跑)**:**默认关两态零差异**——72 档 `diff -rq` **零差异** · 探针 29 档零差异(与 T2 基线逐字节同);**开态**:rc 与产物 sha 逐字节同(ptr_arith canary 两态同 `95084e7b…`、arena_test/ptr_ref_first 同),日志仅多一行标记 ✓。 +- **重放对表(vs T1)**:101 档(72 语料 + 29 探针)重放 ⇒ **无表外命中**:语料面 25 档 blocked(= T1 `would_block.tsv` 34 行 − 7 行 build 面豁免 − 2 行 P21[**解析阶段闸**,不属本报告面])逐条对齐;探针面 11 档(TA01/TS03)为 T1 表**未覆盖面**(口径差,已登记)。 +- **判据全套**:五 CI job 5/5 rc=0 · 枚举 60/60 · selftest 415/415 · canary `95084e7b…d475` IDENTICAL · `.ccr` 四条 96015/96158/142793/142936 · dump 3521 · cir 165915B · 链 `24802386a1…` IDENTICAL · `backend_bootstrap` rc=0。 +- **登记**:本通道**只读**(不改 rc/产物);`blocked` 计数含重复码(逐诊断计数,非去重)——同一码多次命中会重复出现(照原样保留,便于定位)。 + +### 60. **【高危·预存】安全面诊断随 `.cir` 暖缓存静默消失**(2026-09-15 FC 批 T2 施工中发现) +- **现象**:同一源、同一二进制,`clean-cache` 与否是唯一变量——**冷态 `error[TU03]` 报出(rc=1、无产物);暖态诊断消失 → rc=0 且照常产出 ELF**(28822B)。`build --static` 与 `ccr` **两面同病**。 +- **复现源**:`fn main() -> int { p := 4096 as *int; return *p; }`(= `tests/selfhost/test_pointer_safety.py:131-138` 的形态)。 +- **性质**:**预存**(P6 期二进制 `ae01de75…` 同值复现 ⇒ 非 TC02/容量批/FC-T2 引入);后果 = **不安全程序第二次编译起静默通过**(「判定继续 ⇒ 静默错产物」的教科书形态,正落在 FC 线靶心)。 +- **根因(#60 批 T1 实证,2026-09-15——推翻本条原「疑似根因」的侧表指向)**:**`g_types` 行内容**。缓存命中跳过 `ir_gen_func` ⇒ ir_gen 期 `alloc_type` 出的类型行(`TYP_PTR extra=1` 等)**不重建** ⇒ `provenance_verify.cr:66` 的判定(`get_type_kind(ti)==TYP_PTR && get_type_extra(ti)!=0`)落空。E1 实锤:冷 13 行 / 暖 11 行,**恰缺** `row 11 (extra 0)` 与 `row 12 (extra 1)`(`.ccr` −174B)。**非** `g_pts`/`ptr_analysis`:TK01 类检查冷/暖均报(不依赖生成期行)。暖态静默面 = **1 类码(TU03)× 2 形态(load/store)**,`build --static` 与 `ccr` 两面同病。报告 = `/tmp/fct4/task1-report.md`。 +- **为何至今未暴露(T1 方法学发现)**:语料/探针 101 档冷/暖**码集零差异**——① 语料无 `as *T` 形态;② runner(`parity_run.sh:35` 等)逐档 `clean-cache`;③ 套件用例用**唯一 temp 路径** ⇒ 缓存键唯一 ⇒ **结构性无暖态**。真触发 = **同一路径重复编译**(增量构建场景)。 +- **T2 落地(2026-09-15,#60 批)**:**见证式一般化**(裁-W1 = (b) 先行;零格式变更/零发射面变更)= `main.cr` miss 分支以 `tc0 := g_type_count` 见证「本函数 ir_gen 期是否分配过共享类型行」,**有 ⇒ 不写条目**(下跑必 miss ⇒ 重放全部生成期副作用;「宁可 miss 不可静默」)。命中率代价实测 = 语料 101 档 **989→986 条目(0.3%)** · 编译器自源 **110→109(0.9%)**(被抑制者全为分配复合行的 `main`)。用例 = `tests/selfhost/test_warm_cache_gate.py`(6 例:冷/暖同 + 机制钉 + 正控真命中 + TK01 旁面 + `ccr` 面)+ `test_ccr_types.py` ㊳(能力变更钉)+ ㉟ 收口(暖态丢项**归零**,钉死)。 +- **T3 落地(2026-09-15)**:**判据网暖态腿**(本缺陷之所以能藏这么久 = 全部 runner 逐档 `clean-cache` ⇒ 判据面对暖态零覆盖)。入仓语料 `tests/probes/warm/`(**7 档**,**定路径**是该语料的设计要点)+ 腿本体 `tools/baseline/warm_leg.sh`(两层:广度层 = 父 runner 清单,牙齿层 = 入仓语料 + 期望值断言)+ 专用入口 `tools/baseline/warm_run.sh`;`parity_run.sh`/`probes_run.sh` 各附广度层(`logs/` 格式不变 ⇒ 历史对拍可比;`WARM_LEG=0` 关)。**实测**:牙齿层 7/7 档暖态生效、0.9s;广度层 72 档 40 档生效(+125s)/29 档 15 档生效(+3.5s)。**CI 决定** = 只挂最小面(`tests/selfhost/test_warm_cache_gate.py`,10 例,0.5s,已入 `selfhost-tests`);全量扫描留手工判据。 +- **(c)/(d) 裁决(2026-09-15 维护者裁,依 T2 报告判据)**:**(c)/(d) = 性能面、非正确性 ⇒ 本批不实施**,挂**两个可证伪触发条件**:① 任一未判共享面(`g_ir_locals`/字符串池/外链重定位/region)构造出暖态消费者;② 缓存命中率成实测瓶颈。触发后**先试「加一行见证」的廉价路径**(`main.cr` miss 分支扩展点),不够再转快照承载类型行(格式变更 + `CIR_CACHE_VER` bump,与 TODO #5 编译器身份字段同批评估)。 +- **未决/登记((c)/(d) 待触发,判据见 `.superpowers/sdd/warm-task3-report.md` §(d))**:见证把「分配行的函数」整体移出缓存 ⇒ **复合 mint 行**(唯一来源 = ir_gen 分配行)**结构上不落盘**(101 档扫描:仅 `ptr_ref_first.cr` 有复合行、全缺 ⇒ ㊲ 重钉)。 +- **处置**:**本批**(#60 批:T1 测量 → T2 (b) 落地 → T3 暖态腿入仓 → T4 收官)——**已全阶段收官**,台账/终态见 #63。退出条件 = 冷/暖两态诊断一致 + 全语料对拍(含**定路径**暖态腿)**均已满足**(T4 实跑)。 + +### 63. #60 批收官:暖缓存静默缺陷修复((b) 见证式)+ 判据网**暖态腿**入仓(2026-09-15——**独立批 T1–T4 全阶段收官**) +- **提交链**:`e0e92eef`(T2 = 修复 + 套件 + TODO)→ `227b747e`(T3 = 定路径语料 + 暖态腿 + 文档)→ 本条目(T4 收官:台账 + 终态 + spec/REBUILD 指针)。计划+统一台账 = `docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md`(§8 台账 / §9 终态);报告 = `.superpowers/sdd/warm-task{1,3,4}-report.md` + `/tmp/fct4/task{1,2}-report.md`。 +- **根因(T1 E1 实锤)**:快照载 var 的 `type` 行**号**不载行**内容** + `alloc_type` 只追加 ⇒ 命中跳过 `ir_gen_func` ⇒ 生成期分配的类型行缺席(实测冷 13 行 / 暖 11 行,恰缺 `TYP_PTR extra 0/1` 两行)⇒ `provenance_verify.cr:66` 判定落空 ⇒ TU03 静默(冷 rc=1 → 暖 rc=0 + 照常出 ELF)。 +- **修法**:裁-W1 = (b)(**零格式/零发射面**)——`main.cr` miss 分支以 `tc0 := g_type_count` 见证「生成期是否分配过共享类型行」⇒ 分配过则不写条目(下跑必 miss 重放)。代价实测 **0.3%**(101 档 989→986)/ **0.9%**(自源 110→109)。正确性 = 行号空间守恒(归纳:落盘者零分配 ⇒ 命中零分配;未落盘者重放 ⇒ 逐行恒等)。 +- **判据网盲区三因(补齐「为何从未现身」)**:语料无 `as *T` 形态 · runner 逐档 `clean-cache` · 套件唯一 temp 路径(缓存键唯一 ⇒ 结构性无暖态)。**决定性实核**:`check` 面写 **0 条**缓存条目 ⇒ 暖态腿必须走 `ccr` 面。 +- **暖态腿(T3)**:入仓语料 `tests/probes/warm/`(7 档,**定路径 = 承重设计**,M3 实证)+ `tools/baseline/warm_leg.sh`(两层)/`warm_run.sh`;CI 只挂最小面(套件 10 例 / 0.5s);**腿本体手工判据**,「何时该跑」四条见 `tools/baseline/REBUILD.md`。暖态生效档数实测 = 牙齿层 7/7 · parity 广度层 40/72 · probes 广度层 15/29。 +- **终态判据(T4 实跑)**:canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条冷态 96015/96158/142793/142936 · dump 3521 · cir 165915B `b1bdf480…` · 72 档/探针 vs T1 **零差异** · 枚举 **61/61** · selftest 415/415 · 五 CI job 5/5 rc=0 · 链 `corec2==corec3` `006e58e1…` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1(链 sha 与批前不同 = 本批改了编译器源码,自洽性不变);**发射面零泄漏**(canary/四条构造性同)。 +- **(c)/(d) 裁决**:性能面、非正确性 ⇒ 本批不实施;两个可证伪触发条件(未判面构造出暖态消费者 / 命中率成瓶颈),触发后先试「加一行见证」。 + +### 58. 容量批(CAP)收官:E-2 表示面聚合 + E-3 记录布局迁侧表 + E-4 缓存门(2026-09-15——**独立批全阶段收官**) +- **提交链**:`4607624c`(T1 白名单换代)+ 报告 → `432c2e40`(T2 = E-2)+ `7c4d6603`(#56)→ `0b0d9aef`(T3 = E-3)→ `15357b04`(T4 = E-4 收口 + #57)→ 本条目(T5 收官)。计划 = `docs/superpowers/plans/2026-09-15-capacity-batch.md`(含**统一台账**与**终态陈述**)。 +- **交付**:**E-2**(裁-CAP-1 (a))可选聚合槽(字段/数组·切片元素/元组元素/枚举载荷/全局槽)**写点装箱**(纯 IR、零布局变更)⇒ 原双路径 139 转正确值;同批修**两条预存根因**(读点常量折叠 + 写点静态度量初值)。**E-3**(裁-CAP-2 (a))字段/变体/载荷**迁侧表**(记录尺寸与 `OFF_*` 不变 ⇒ 110 访问器零改动)+ 覆盖位**无界位图**(旧态 70 变体 SIGFPE rc=136)+ 三 `MAX_*` 退役 + P022/P023 停发 + 四死镜像删除;**`.ccr` 不 bump**。**E-4**(裁-CAP-3)机制实证**证伪** ⇒ 维持关缓存。 +- **判据(T5 实跑)**:canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条 96015/96158/142793/142936(冷+暖)· **换代基线 × 当前源 72 档零差异** · 探针 29 档零差异 · 五 CI job rc=0 · 枚举 **59/59** · `selftest-types` **415/415** · `check src/compiler` rc=0 · 链 `f69240ae…` + N06=0 + 冒烟 42 + `--help` rc=1 · `test_optional` **48/48** · `test_enum_limit` **18/18** · 突变 7 条(T2 4 + T3 3)全按设计转红。 +- **台账 KPI**:实施 **4** · 删除 **3 类** · 迁移 **2 类**(+ selftest 44 处)· 收紧 **0** · 放宽 **2 类** · 修复 **3 件** · 登记 **12** · 偏差 **6** · **未取裁 2**。 +- **待裁 2**:(D) 语言面拒绝 `*p = v`(pointee `T?`;已上报;影响面实读 = 拒绝**已错**形态)· T4「移除门」候选(性能面;附补证清单)。 +- **关联**:#56(c3 静默错值,高优先预存)· #57(移除门候选)· spec §9 **容量批行** · 计划台账/终态陈述 · 下一批指向 = 验证切片轮 或 按维护者裁定。 + ## 第四轮 CompCert 对照遗留项(2026-08-17 记) 来源:`docs/compcert-round4-findings.md`(F1-F20 修复后残留)+ 波 1-3 修复审查产出。F1-F20 已全部修复,以下为范围外/需 IR 形态演进的遗留项: @@ -137,6 +666,7 @@ ### 文档更新 - `docs/memory-model.md` — 设计文档(待同步实现细节) +- **docs 重组合并时的连带改点(2026-09-11 登记,`feature/docs-reorg` 已推送 `9eab0757` 待合)**:该分支把 `docs/error-codes.md` → `docs/developer/errors.md`、`docs/compcert-reference.md` → `docs/archive/compcert-reference.md`,故**合并该分支时必须同步两处 src 注释**:`src/compiler/ast.cr:315`、`src/compiler/ptr_analysis.cr:235`(在本线/当前 develop 上这两个路径**仍然正确**,故不在 docs 分支内改——避免把 src/ 拉进 docs 分支,也避免在本线留下悬空引用)。 - `docs/pointer-model.md` — 指针安全完整设计 - `docs/language-syntax.md` — 指针、@ 内建语法已更新 - `docs/at-intrinsics.md` — @ 内建原语完整规格 @@ -160,6 +690,11 @@ - 动态偏移使用 points-to 目标的实际 allocation base 生成 ELF 检查;多目标无法唯一定位基址时保守拒绝 - 回归见 `tests/selfhost/test_pointer_safety.py` +### 测试清单陈旧 + exists 静默跳过(2026-09-10 R1 Task 5 发现——**已修,登记备查同类面**;本小节编号系文档区既有序列,与「预存 Bug」区 #8 无关) +- **已修**:`tests/selfhost/test_compile.py` `concat_sources()` 原以 `if os.path.exists(path)` **静默跳过**缺失条目——三条 `src/compiler/backend/**`(x86_64.cr / x86_64/instr.cr / resolve.cr)自波 1 三轴搬迁后长期不存在,清单无声缺斤短两而测试始终「全绿」。现改为**存在性断言**(`build_selfhost_native.py` guard_manifest 同款,缺失即 rc=1;负对照实证有牙:插入伪路径 → 精确报出该条目名)。死条目已删,清单条目全部现存。 +- **为何不补入三轴文件(实证)**:补入 `src/arch/x86_64/*` + `src/format/elf/*` + `src/os/linux/*` 后该单元缺 **68 个 HIT 引擎符号**(`src/arch/hit/hit.cr` / `lower_to_core.cr` 属独立清单段)——三轴文件归属 **corearch 编译单元**,本清单 = corec 前端单元闭包,故原为**刻意裁剪**(就地注释已写明归属);已按 brief 的 fallback 分支处理(不硬塞、不静默跳过)。 +- **同类面建议**:其余「文件清单 + exists 静默跳过」形态应同法改存在性断言(波 1 评审曾清点同类 rc-only 面:`test_mw_task2.py:172` / `test_compile.py:127` / `test_live_ranges.py:52,73` / `test_directory_build.py:50,80,100`——那是 rc 门面,本条是**清单完整性**面,二者互补)。 + ### 内存模型方向:能力 + 格(v4 发布,2026-08-27) - 备忘:`docs/memory-model-capability-lattice.md`(v1 2026-08-16 → v2 2026-08-20 → v3 2026-08-26 → **v4 发布**;上下文包已删并入) - **v4 定稿**:**规则封闭对象开放**(层规则零签名 = 缓存七条;对象无界;新范式三件事 = 图标注 / CIC 内部定义 / 硬件映射,无枚举);**无格承诺**(格代数 = 映射参数,判定/定理只在关系层面);**M1 定论:能力不提升一等公民**(语义还原图上:身份 = 图节点 / 无配方 = 标注 / 授权归治理层;可逆、暂不执行);**M4 输入:图内不可重算条款**(已并入 memory-model.md 条款 4b);**寄存器分配 = 缓存语义映射实例**(`docs/regalloc-cache-mapping.md` 正式参考 + `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md` 设计记录) @@ -172,20 +707,19 @@ - 寄存器分配实现规划:共存关系落定(图活性)→ 判定规约 → 贪心放置(opt.cr 升级)→ checker 自检 - 实现规划(远期):能力流分析(PointerAnalysis 推广)+ 三 pass 能力化重写——内存模型层面大改 -### 格形态 IR 升级(C 路线,2026-08-27 记) -- 设计:`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`——`.ccr` 升级为格形态(v6);三层形态:图 = `.cir` / 格 = `.ccr`(格形态)/ 编码 = ELF -- 命名已定案(2026-08-27):方案 A——扩展名 `.ccr` 保留,缩写展开 = Core Region Representation;扩展名 / magic / CLI 不动 -- 执行人:第二维护者(TODO 横向扩展);验证闭环:DslsDZC -- 事项: - - 命名落地(方案 A 已定案:扩展名 / magic / CLI 不动;展开 = Core Region Representation,文档已先行更新,执行阶段复核) - - 格式 v6 序列化/反序列化(ccr_io.cr)+ v5 → v6 一次性转换工具(数学结构大改:线性程序 → 存在结构,不承诺后向兼容) - - v6 存在结构段:条目版本 + 存在区间(图活性)+ 共存 + home + 无配方标记(memory-model 条款 4b) - - ELF 后端适配(src/arch/linux/ld/) - - CLI / corearch 参数迁移(若走方案 B) - - 自举管线(build_selfhost_native.py、bootstrap/corec/ir/ccr.py) - - 测试迁移(tests/selfhost/、tests/bootstrap/) - - 文档复核(coreir-schema / CLAUDE.md / project-book / compcert-reference——描述已先行更新) - - 寄存器分配判定接入格形态(存在区间/共存消费 + 贪心放置,docs/regalloc-cache-mapping.md §4) +### 格形态 IR 升级(C 路线)——**v6 主线 Task 1-6 完成(2026-09-06)** +- 设计:`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md` + 格式定稿 `specs/2026-09-05-lattice-ir-v6-format.md`(B 方向:存在结构主体;格层语义范式无关承诺/编码层字节 = hw-map 域,层定位补定) +- 命名已定案:方案 A(扩展名/magic/CLI 不动;.ccr = Core Region Representation) +- 事项完成核对: + - ~~格式 v6 序列化/反序列化~~(2026-09 Task 4:段表架构 + ENT 28B 落盘,v6-only——v5→v6 转换工具按定稿取消:.ccr 中间产物零生态) + - ~~存在结构段:条目版本/存在区间/共存/home/无配方标记~~(Task 1-3 数据面 + Task 4 落盘;无配方标记 = ENT flags bit0 字段就位) + - ~~ELF 后端适配~~(v6 段表内 NOD 重建内存态,发射语义零改动;自举闭环 byte-identical 实证) + - ~~CLI~~(方案 A:不动;corearch 加 --table/--dump-events 属 HIT 线) + - ~~自举管线~~(v6 全程接管 build;bootstrap/corec/ir/ccr.py 不存在——.ccr 仅 self-hosted 读写) + - ~~测试迁移~~(test_ccr_v6/region_cfg walker v6/全链回归) + - ~~文档复核~~(coreir-schema/CLAUDE.md/project-book/compcert-reference 已按实现形状同步——Task 6) + - ~~寄存器分配判定接入格形态~~(Task 5:regalloc-consistency.corespec 规约 + sweep 自检;**贪心放置 = 后续**——上下文贪心升级挂账) +- 后续挂账:SYM 归并/REG 坐标化(v6.0 保守路径,Task 4 评审 I-1)、ENT flags 无配方完整语义(条款 4b)、判定 R3/R4(驱逐配对/调用失效) ### 寄存器分配 = 缓存语义映射实例(落地,2026-08-27 记) - 设计:`docs/regalloc-cache-mapping.md`(正式参考)+ `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`(设计记录)——分配 = 格 → 编码的映射实例(条款 7);驱逐不变量 = order-free 语义保持;判定单层、分配算法定案 = 上下文贪心(零证明) @@ -225,15 +759,78 @@ ### 设计定稿待实现(补挂账,2026-08-28 审计) - **概率性 pass**(probabilistic-pass,2026-07-30 已批准):src/ 零实现,仅文档(`docs/probabilistic.md`)——范式相关,优先级低,待排期 -- **硬件映射表**(hw-map,2026-08-23 已批准):无实现——**依赖 crasm 的映射表**(crasm 未实现则链式阻塞)。关联 `docs/superpowers/specs/2026-08-23-hw-map-design.md` +- **硬件映射表**(hw-map,2026-08-23 已批准):MMIO 设备语义表 + 投影表——crasm 依赖已解除(2026-09-05 crasm 退役);与硬件接口表(HIT)同族——HIT = 指令/运行层接口(`specs/2026-09-05-hardware-interface-table.md`),hw-map = 设备层接口,同构(语义表范式无关 + 投影表实例)。实现仍待排期。关联 `docs/superpowers/specs/2026-08-23-hw-map-design.md` - **平台桥抽象**(platform-abstract,2026-08-16 定案):设计定案待实现——语义接口 + 后端实现原则;程序 IO = 流转导器。关联 `docs/superpowers/specs/2026-08-16-platform-abstract-design.md` - ~~**错误码体系**(error-codes,2026-08-08 已批准)~~(2026-09-05 复核:体系完整——ast.cr 定义 `EC_R_*`,checker.cr:2080/2095 发射 R002(编译期越界),main.cr:148 硬错误路径(R002/TK05/TK06 拦编译),diag.cr `error_cat_prefix`/`pad_diag_num` 完整打印 R001-R004;BC17「全仓库无引用」为历史旧况,已过时) - ~~**裸指针 asp 标记**(bare-ptr-model)~~(2026-09-05 落点核实:ir_gen.cr:2111-2117 整数转指针 asp=1 且从 `source_ti` 的 type extra 继承/清零,checker.cr:2244-2246 同步——与 pointer-model 设计一致) - **dex 任意精度精确小数未实现**(dex-precision,2026-08-28 审计):设计 = 无上限小数位精确小数;实现 = 定点 S=10⁶(`src/stdlib/dex.cr:3-18`,注释「定点方案(执行时定稿,2026-08-16)」)——6 位小数 + int64 硬上限(加减 ≤9.2e12、乘 |a|·|b| ≤9.2e6);设计意图与实现为语义级偏差。修复方向 = 动态位数表示(任意精度运算,重活,排期) - **apx 降级策略被实现为报错**(apx-degrade,2026-08-28 审计):设计 = 只支持精确的环境直接忽略 apx 标签、走精确语义(优雅降级);实现 = 解释器对 apx 的 I2F/F2I 显式报错(`src/stdlib/dex.cr:32-33`;报错本身是 Task 6 安全修复——替代静默跳过致 SIGFPE,但方向与设计相悖)。修复方向 = 解释器忽略 apx 标签走精确路径。注:apx 结果跨环境可不同(native 走 binary64),为标签显式代价,账本如实记录 -- **宽度类型移出语言**(width-out-of-language,2026-08-30 定案):设计决定——int 无上限为默认(已定);宽度(i64/u32/w32 等)不进入语言类型/标签体系,避免第二标签范式(单标签单范式原则);机器形状全部归 hw-map(硬件映射表,`docs/superpowers/specs/2026-08-23-hw-map-design.md`——依赖 crasm,链式阻塞现状不变);apx 保持单一语义 = 精度降级开关,不扩张为宽度标签。三层映射对应:图/格 = 纯数学,编码 = hw-map 领域。语言侧清理(不阻塞,可立即做):ast.cr `T_INT_I8..T_INT_U64` / `T_FLOAT_F32/F64` 死条目移除(勿重编号)、`_f32/_f64` 后缀死路径处理、`1_000` 两编译器分歧复核;v6 规格影响:编码层宽度由目标自动决定,hw-map 为未来显式控制通道(v6 格式规格先行更新) +- **宽度类型移出语言**(width-out-of-language,2026-08-30 定案):设计决定——int 无上限为默认(已定);宽度(i64/u32/w32 等)不进入语言类型/标签体系,避免第二标签范式(单标签单范式原则);机器形状全部归 hw-map/硬件接口表(`docs/superpowers/specs/2026-08-23-hw-map-design.md` 设备层 + `specs/2026-09-05-hardware-interface-table.md` 指令/运行层;crasm 退役后链式阻塞解除);apx 保持单一语义 = 精度降级开关,不扩张为宽度标签。三层映射对应:图/格 = 纯数学,编码 = hw-map 领域。语言侧清理(不阻塞,可立即做)——**✅ 2026-09-10 语言面收窄 R1 全部完成**:~~ast.cr `T_INT_I8..T_INT_U64` / `T_FLOAT_F32/F64` 死条目移除(勿重编号)~~(落点 ce3e541c——ast.cr 墓碑注释 + `W_*` 值域 + parser 两处死分支)、~~`_f32/_f64` 后缀死路径处理~~(落点 51d74be3——侦查裁决 = 纯机器宽度 → 退役并响亮报错;后缀位宽在词法层即丢弃、data 槽恒 0,无语言语义承载)、~~`1_000` 两编译器分歧复核~~(落点 51d74be3——`_` 分隔符不支持,bootstrap/self-hosted **统一响亮报错**:原 self-hosted 静默消费成值 0、bootstrap 静默接受成 1000,双向皆错;新增 `tests/selfhost/test_lexer_parity.py`);v6 规格影响:编码层宽度由目标自动决定,hw-map 为未来显式控制通道(v6 格式规格先行更新) +- **定长数组裁决**(fixed-array-retire,2026-09-10 用户拍板):`[T; N]` **退役类型构造器身份、保留为「内联容量存储」表示提示**(语义归处 = product / 序列接口 + 长度 where / 图 F11 长度来源链;表示层归处 = 映射参数,与 hw-map 同层);自举 10 处用法零改造。**R1 已办(2026-09-10)**:裁决注记(aa76f7f5——ast/checker/parser 七处「表示层概念」注释,零行为)+ 全局定长路径修复(71cb6278,见下行「全局初始化机制」)。**未办 = 类型身份退役本身**:`[T; N]` 从类型相等/子类型判定中降格 = **接口化轮 R2 落实**(checker 类型判定重做时自然剔除——R1 明确不做)。设计见 `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md`(状态 = 已实施(R1))。**✅ R2 P3a 已办(2026-09-12,Task 1 `ef61f002`;TODO #34)**:N 与「固定/视图」维度**已从等价身份剔除**、入**变型/方向**规则——`ty_variance_of`(只读协变 / 可写不变 / 表外默认不变)+ `array_len_constraint_ok` 方向档(`[T;N] <: [T]` 放行、**视图→固定拒绝**、异长常量档拒绝);`&T`/`&mut T` 进参数链(mut = 等式判定维度);「切片→固定长」静默放宽关闭(P2a 洞,收紧清单 T1-1~T1-6);自举 10 处 `[T;N]` 零改造复验(全语料零命中)。**剩余面** = 符号档长度约束(VC 义务,显式登记不实现——计划 §侦查 3 裁决)+ `EXPR_LET` 站点无检查(另立 #32) +- **全局初始化机制**(global-init,2026-09-10 R1 Task 4 落地 71cb6278):文件级全局的运行期初始化 = **main 序言 IR 注入**(需运行期初始化的全局:聚合 / 非常量初值 → `alloc + 逐元素/求值存 + 写槽` 序列)+ **解释器常量阶段**(标量常量初值)。两路径语义一致(判据 `tests/selfhost/test_global_init.py` 15 例双路径精确 rc)。原缺陷:`[T; N]` 全局 run rc=139(SIGSEGV)或静默 0;同面根因四修 = ELF 字段访问全局基址(`e2_load_var` 规约,原读栈垃圾)/ 解释器 callee 内联缺聚合族 opcode / callee 返回值暂存槽覆写首个全局 / 类型别名一层解析。**约束**:注入点选 main 序言的理由 = 无新 IR 函数 / 无 .ccr 段变更 / 无 `_start` 字节扰动——**未来若支持非 main 入口或库形态,须迁移到独立 init 区**(届时本条目的注入点假设失效)。**缓存交叉引用(终审提示,2026-09-11 随 TODO #5 修订)**:注入点在 `ir_gen_func(main)`,而函数命中 cir 缓存时根本不走该函数(`main.cr` cache-hit 分支)——**同二进制内**命中恢复的是注入后的快照(含注入),无碍;**跨编译器升级面已随 TODO #5 修复关闭**(提交 `3be4cb48`:头部编译器身份字段 = 运行中 ELF 内容哈希 ⇒ 重建后旧条目自动 miss,不再依赖人工 `clean-cache` 兜底。批前该场景本就 139,非回归);比较 `.ccr` 前仍须 `clean-cache`(理由改由 #5 末条承担:冷/热缓存态 `.ccr` 分歧未修) + - **记录(Task 4 评审 Minor,不修)**:① Task 4 报告 §6.5 称注入在 `g_cur_ret_ti = ret_ti` **之后**,实际在**之前**(`ir_gen.cr:2282`,= brief 指定位置)——报告措辞与代码不符(读码无歧义,代码正确、报告笔误);② `alloc_type(TYP_ARRAY, TI_INT, cnt)` 硬编码元素类型 `TI_INT`(无观测差异——当前元素尺寸恒 8;未来出现非 8 字节元素类型时需参数化) - **类型系统方向定案**(type-system-direction,2026-08-30 定稿):图本体 + 接口统一总纲——图 = 唯一真相层(类型 = 图标注、接口 = 图上契约、类型检查 = 图良构性验证 pass,与指针三 pass 同级);接口注册表(int/dex/string = 原生接口条目,规则内建、公理引用规约层、用户不可实现);where 值约束三档语义(常量→编译错误 / 符号→VC / 动态→运行时检查);泛型 = 编译期接口具体化(无运行期字典);宽度移出语言(见 width-out-of-language)。损失账本(免费午餐债 5 项)+ 演进顺序(where 值约束 → 泛型=编译期接口 → 验证切片 → v6 格形态 → 类型概念收敛,两条根基革命不得同时进行)+ 学术支撑(PLDI 2025 Webs 平行印证、语义子类型 = 完整补偿、ISO TS 6010 provenance 对齐)详见 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md` - **边界判定图 pass**(bounds-inference,2026-09-05 挂账):在 HDFG 上做下标/切片长度的符号传播 pass(与指针三 pass 同级)——idx 来源链 + slice 长度来源链(F11 侧表传播的图化升级):证明恒安全 → 不发射检查(零成本);证明恒越界 → 编译期 R002 拒绝;证明不了 → 保留运行时检查并带图推导必要性标注。目标:运行时 trap 面最小化(trap = 推导不出才留的兜底,兼作验证证据);核心_pattern 桌面挂起坑的根治方向(见 :125 划销注)。现状基础:pass_before_array_access/ext_safety 检查发射钩子、slice_len 侧表(字面量/长度变量编码)、checker R002 编译期越界 +## HIT 最小核(2026-09-06 M1 完成 → M2 挂账) + +硬件接口表(`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`)M1 最小核运行闭环已完成:表文件/解析(`src/arch/hit/`)、表驱动编码器(`emit_instr_tabled` 预检+分层)、合成层(IR 直线子集 → 4 核事件流 + 常量池,add→sub 反减)、冒烟闭环(sub/add/mem exit 2/12/5,9/9 测试)。 + +**M2 挂账**: +- 控制流/函数调用/移位/乘除等全 op 合成(M1 子集边界 = main 入口函数;stdlib 闭包辅助函数现走旧路径混合模式) +- nand 真语义(x86 and+not 两步序列投影)——现单步 and 占位;and→nand/or 合成规则(源语言无位与/或运算符载体) +- ~~`--table` × `--link/--shared` 组合~~(2026-09-06 已处理:显式拒绝 stderr+exit 1——test_hit_table 三形态覆盖) +- 全量切换:无 --table 旧路径退役(emit 循环门控翻转) +- corec build 透传 `--table`;事件流/表数据入 v6 段(NOD op ↔ HIT event_id 对齐) +- M2 挂账细节(2026-09-06 已清 3/4):~~hit_w32 负值哨兵~~(存 0)、~~add 反减 dest-fresh 前提~~(d==s1 落 0 事件防御)、~~'events emitted' 计数改名~~;**负值/宽常量池载体**(仍待——M1 冒烟无负常量形态) + +## int 多字 M1(2026-09-07 M1 完成 → M2 挂账) + +int 无上限语义(`docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md`)的编码层投影 M1 已完成:add/sub 溢出全链——64 位快路径零开销直算 + jo 溢出检测 → 自动提升固定 2-limb(128 位)多字表示(tagged 槽:快值/堆对象指针 + 帧 tag 字节;tag 卫生四类定值点;128 位混合算术/比较;return 确定性截断语义)。实施 plan `docs/superpowers/plans/2026-09-06-int-multiword-m1.md` 六任务收官,设计定稿 `docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md`(§8 M2 挂账全文);快路径零变化:64 位内程序逐字节不变(test_mw_task2 z 用例 + backend_bootstrap stage1→3 byte-identical);测试 `tests/selfhost/test_mw_task{1..6}.py`(含 i64max±1 边界套件)全绿。 + +**M2 挂账**(摘要——细节见 spec §8): +- mul/div 溢出链与 2L 参与(M1 只 add/sub);UOP_NEG −2^63 环绕链 +- 2L 值打印(int_str 超 64) +- D4:超 64 编译期常量(IR_CONST 64 位槽 = 格层表示,需 v6 常量段——与 dex 精度同族);超 128 码域动态增长(现编码层限制错) +- return 全 128 内部传递(现确定性截断低 64);出逃写/跨函数 2L(全局/堆/数组) +- D1c 静态区间证明免 tag(保守 tagged 集:每变量行 1B tag + 每站 jo 6B + 块 ~45-160B) +- 共享块/模板压缩(函数尾冷区线性增长,非正确性项) +- 表(HIT)模式 × 运行时 2L 组合验证(tagged 路径整条落旧路径——语义正确未验组合) +- arena 生命周期债(2L 对象跨 arena_reset 悬挂;正确方向 = 永久 bump 或跨 reset 复制) + +## 寄存器分配移后端(2026-09-07 完成 → 挂账) + +编码层资源决策(寄存器分配数据面 + CAG 分配 + 一致性判定)从 corec(opt.cr) +归位 corearch(`src/arch/linux/ld/regalloc.cr`)完成:.ccr 不再携带分配结果与 +条目(ENT 恒空 / opt_meta 恒 0 / func·REG first_ent 恒 -1 / param_ents 恒 -1—— +格式结构保留、loader 空表语义已支持、零 version bump);corearch O2 = load 后 +自算 alloc + verify(emit 前,违反 = 编译错误,成功静默);判定/条目调试通道 +随迁(--dump-entries/--dump-coexist/--check-regalloc/--inject-*/--dump-regassign +同名 flag);pass_stack_share 停用(产物死路径实证——g_stack_map 不落盘、 +corearch 恒空跑,零产物差异)。设计定稿 `docs/superpowers/specs/2026-09-07-regalloc-backend-design.md` +(R1-R5/D-3 拍板记录 + 实施完成);plan `docs/superpowers/plans/2026-09-07-regalloc-backend.md` +六 Task 收官;回归全绿(mw1-6 + live_ranges 13/13 + ccr_v6 8/8 + region_cfg +22/22 + compile + backend_bootstrap stage0-2 + hit_table 11/11 + bootstrap 三套)。 + +**挂账(残余)**: +- 栈共享恢复(pass_stack_share 停用——恢复 = corearch 内以 alloc 同 seam 实现并启用) +- CSE 语义实证:pass_cse 对 .ccr 产物零效果(NOD O0/O1/O2 逐字节同——lower 自 + df 重建丢弃其 NOP/替换)——O1 门语义现为进程内运行、产物不承载;产物级 CSE + 生效需专项决策(重建链语义层优化归位) +- R4:compile-time-linearity 的 corec 侧减负实测(分配/数据面计算已移 corearch) +- v6 格式文档(specs/2026-09-05-lattice-ir-v6-format.md)ENT/opt_meta 载荷语义 + 同步(恒空态——ccr_io.cr 头注释为实现权威已先行) +- docs/project-book.md 后端分层哲学表述(「机械翻译器」措辞与分配归位后现实) + 校对 + +## 规约语法并入 .cr(2026-09-06 .corespec 退役挂账) + +独立 .corespec 格式/语言退役(crasm 同构:规约只有一种表达 = Core 语言,无第二套文件/语言): +- ~~grammar/corespec.ebnf~~(退役标注——内容保留为迁移源)→ **迁移:规约语法(requires/ensures/old/result/where/SpecImplies)并入 grammar/core.ebnf** +- ~~spec/*.corespec 4 文件~~(退役标注)→ 组件行为契约迁移至对应 .cr 注释/契约文档(先例:src/compiler/regalloc-consistency.cr) +- **.cr 规约语法实现**(requires/ensures/where 子句 = 语言实现——原类型系统方向定案线:where 值约束三档 → 验证切片;TagNode 图节点已在设计 coreir-schema「图即验证」) +- .csr(图约束二进制)保留——它是 .cr 规约编译进图后的序列化,非独立语言 +- 文档引用(10+ 文件)随语法并入逐步同步 + ## 待实现特性 ### 控制流自动惰性(2026-08-09 记) @@ -242,6 +839,7 @@ - IR_LAZY_THUNK(46)/IR_LAZY_FORCE(47) 已存在(ast.cr:571);ir_gen 在纯函数调用后包 THUNK(ir_gen.cr:1106),`force_if_thunk()` 在所有操作数位置发 FORCE - **但当前 thunk 不产生实际延迟**:IR_CALL 在 THUNK 之前已急切发射,ELF 后端(instr.cr:1091,注释明言 "Calls are currently emitted eagerly… typed value transfer")和解释器(interp.cr:194)都按纯值搬运降级——语义上是 no-op,只保证输出不变 - **use_count 时序问题**:`compute_usage_counts()`(dataflow.cr:325)在全部 ir_gen 之后才运行(dataflow.cr:381),而 THUNK 判定在 ir_gen 当时读 `g_var_use_count`(ir_gen.cr:1108)→ 判定时恒 0/未初始化,`use_count <= 1` 恒真,实际每个纯调用都被包——"单次使用"条件名存实亡 + - **行号复核(2026-09-11,效应/纯度修正批 Task 4)**:上条的源码锚点为 2026-08-09 快照、已漂移——THUNK 判定现 `ir_gen.cr:1588-1599`(`fi_ispure` 读 `:1590`、`g_var_use_count` 读 `:1592`);`compute_usage_counts()` 现 `dataflow.cr:405`(调用点 `:461`)。**本缺陷独立于效应/纯度批**:该批(#27)已把 `fi_ispure` 改为真计算,但**有意冻结**本节的 lazy 判定(`ir_gen.cr` 零改动 ⇒ 生成期读到的仍是 checker 的乐观默认值)且未动 use_count 时序 ⇒ 本缺陷保持未修,待独立批(含 ELF 判据重定——见 #26/#27)。 - 无 `lazy()` 显式惰性内建(旧条目"共存策略"为过时信息;docs/lazy.md 明确"无关键字") - 控制流级惰性(if/while/for 分支表达式)与循环体内条件性惰性均未实现——即本条目 - 方向(2026-08-09 定为编译期指令下沉路线,不做运行时实现): @@ -258,7 +856,7 @@ ### 性能自动化(2026-08-30 记) - **自动并发**(auto-parallel,2026-08-30):数据依赖 + state edges 已显式化 → 无依赖 region 的可并行性**可判定**(区别于传统自动并行化的依赖猜测——四十年失败史的根源)。算法:扫描图 → 找无依赖独立 region → 自动分派 goroutine(go/sched 机制已有)。与 R-HLS(IEEE 2024,RVSDG 动态调度)平行;「并发异步」= 8 项验证清单的实证场景。注意:并行粒度成本模型(调度开销 vs 收益)需启发式;起步 = 显式 go 保持 + safe 子集自动并行 -- **自动记忆化**(auto-memo,2026-08-30):图显式纯度判定(无副作用边)→ 多次使用的纯节点自动缓存结果。与自动惰性同机制(惰性 = 延迟执行,memo = 缓存结果),可共用判定/下沉基础设施 +- **自动记忆化**(auto-memo,2026-08-30):图显式纯度判定(无副作用边)→ 多次使用的纯节点自动缓存结果。与自动惰性同机制(惰性 = 延迟执行,memo = 缓存结果),可共用判定/下沉基础设施。**判定事实现状(2026-09-11 效应/纯度批后,见 #27)**:state 链判据面已覆盖全部保守效应——可证纯调用不入链;extern/spawn/yield/hotpatch/**间接调用 `IR_DYN_DISPATCH`**/裸指针写一律保守入链 ⇒ 「无副作用边 ⇒ 纯」不再有乐观常量漏洞(本批修的 P0 症状即「memo 会把可观测效应缓存掉」)。**消费侧注意**:若实现读 `fi_ispure` 旗标而非链边,须在 IR 生成之后读(`compute_all_purity` 写回后)——生成期该字段是冻结的乐观默认值(lazy 判定解耦,见「控制流自动惰性」节)。 - **PGO 自动剖析**(pgo,2026-08-30):编译器自动插桩收集热路径 → 自动内联/特化/字段布局。标准基础设施(LLVM 成熟路线),零用户标注 - **自动向量化**(auto-vectorize,2026-08-30):可向量化 region 检测 → SIMD 发射(hw-map 编码层落地后接入)。标准技术,优先级低 - **自动内联**(auto-inline,2026-08-30):热路径自动内联(PGO 配套);`@inline` 显式保留 @@ -273,18 +871,19 @@ - **测试生成自动**(test-gen,2026-08-30):where 约束/规约 → 约束求解器自动生成测试用例(验证管线的副产品,近零成本) - **序列化/打印自动**(serialize-auto,2026-08-30):从接口声明自动生成序列化代码与打印格式(serde 式派生;`dex_str` 已是雏形)——类型驱动代码生成家族 -### .crasm 统一汇编抽象层(2026-08-09 记) -- 目标:内核路线(project-book 第五阶段)的汇编级能力——MMIO、特权指令、中断。跨平台统一指令集 + 无限虚拟寄存器 + 平台映射表,寄存器分配按 v4 方向(缓存语义映射实例,`docs/regalloc-cache-mapping.md`——无限虚拟寄存器 + 平台映射表正是映射实例形态;现 `alloc_registers` 线性扫描器为升级起点) -- 现状:设计已批准(2026-08-08 brainstorming 逐节确认),2026-08-09 整理为正式文档 `docs/crasm.md`;**尚未实现**——lexer/parser 无 asm 语法,汇编仅存在于 rt.s 手工汇编与 ELF 后端机器码发射 -- 方向(按里程碑顺序): - 1. `.crasm` 词法/解析(结构化指令 → AST 复用) - 2. 寄存器生命周期 pass + 测试(未初始化读/重复写/生命周期逃逸/宽度一致/分支一致性) - 3. 特权约束表 + 用法验证 + unsafe 隔离 - 4. x86-64 映射表(翻译器)+ 字节级测试 - 5. .cr extern 接口接线 + 端到端 - 6. (后补)ARM64/RISC-V 映射表 -- 明确不做(YAGNI):模拟器/调试器、C 生态兼容、指令级时序验证、特权副作用验证(隔离,人工保证) -- 参考:`docs/crasm.md`(正式文档)、`docs/superpowers/specs/2026-08-08-crasm-design.md`(批准记录) +### ~~.crasm 统一汇编抽象层~~(2026-08-09 记|**2026-09-05 退役**) +- **退役(2026-09-05)**:独立 .crasm 格式取消——绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表),被**硬件接口表(HIT)**吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件;事件可读形态 = v6 NOD 文本 dump。正式声明 = `docs/crasm.md` 状态行(已废弃,保留为历史记录);依赖解除引用见本文件「硬件映射表」与「宽度类型移出语言」两条。**原里程碑方向全部作废(保留下文备查),无实施计划。** +- (历史)目标:内核路线(project-book 第五阶段)的汇编级能力——MMIO、特权指令、中断。跨平台统一指令集 + 无限虚拟寄存器 + 平台映射表,寄存器分配按 v4 方向(缓存语义映射实例,`docs/regalloc-cache-mapping.md`——无限虚拟寄存器 + 平台映射表正是映射实例形态;现 `alloc_registers` 线性扫描器为升级起点) +- (历史)现状:设计已批准(2026-08-08 brainstorming 逐节确认),2026-08-09 整理为正式文档 `docs/crasm.md`;实现从未落地(lexer/parser 无 asm 语法)——2026-09-05 整体退役 +- ~~方向(按里程碑顺序):~~ + - ~~1. `.crasm` 词法/解析(结构化指令 → AST 复用)~~ + - ~~2. 寄存器生命周期 pass + 测试(未初始化读/重复写/生命周期逃逸/宽度一致/分支一致性)~~ + - ~~3. 特权约束表 + 用法验证 + unsafe 隔离~~ + - ~~4. x86-64 映射表(翻译器)+ 字节级测试~~ + - ~~5. .cr extern 接口接线 + 端到端~~ + - ~~6. (后补)ARM64/RISC-V 映射表~~ +- 明确不做(YAGNI,退役前定案保留):模拟器/调试器、C 生态兼容、指令级时序验证、特权副作用验证(隔离,人工保证) +- 参考:`docs/crasm.md`(**已废弃 2026-09-05**)、`docs/superpowers/specs/2026-08-08-crasm-design.md`(批准记录,历史) ### 对照 CompCert 审查发现的未修复 bug(2026-08-11 记,详见 docs/compcert-reference.md) diff --git a/bootstrap/corec/backend/x86_64_stack_asm.py b/bootstrap/corec/backend/x86_64_stack_asm.py index ec543219..6d99d852 100644 --- a/bootstrap/corec/backend/x86_64_stack_asm.py +++ b/bootstrap/corec/backend/x86_64_stack_asm.py @@ -505,13 +505,20 @@ def gen_function(self, func: FunctionDef): for blk in blocks: if blk != func.entry: self.emit(f"{blk.name}:") + # TODO #29 附带修复:ReturnInstr 是**块终结符**——其后同块指令是死代码,既不得 + # 继续发射(否则其 `mov rax, ...` 会把该函数返回值覆盖成**最后**一个 return 的 + # 值:死代码赢,静默错值——实测 checker.cr 的 unify_types `return true; return + # false;` 被编成恒返 false),也不得让执行流落入下一个块。修复前仅在「块的最后 + # 一条恰好是 return」时补 epilogue,同块双 return 即命中。 + returned_in_block = False for instr in blk.instrs: self.gen_instr(instr) - # If block ends with a ReturnInstr, emit epilogue immediately - # (otherwise execution would fall through to the next block) - last_block_had_return = self._block_ends_with_return(blk) - if last_block_had_return: - self._emit_epilogue(func.name) + if isinstance(instr, ReturnInstr): + # 块终结:补 epilogue 后停止发射(否则执行流落入下一个块) + self._emit_epilogue(func.name) + returned_in_block = True + break + last_block_had_return = returned_in_block # Only emit final epilogue if the last block didn't already return if not last_block_had_return: @@ -530,11 +537,6 @@ def _emit_epilogue(self, func_name: str): self.emit(" pop rbp") self.emit(" ret") - def _block_ends_with_return(self, blk: BasicBlock) -> bool: - if not blk.instrs: - return False - return isinstance(blk.instrs[-1], ReturnInstr) - def gen_instr(self, instr): if isinstance(instr, ConstInstr): self.gen_constant(instr) elif isinstance(instr, BinaryInstr): self.gen_binary(instr) diff --git a/bootstrap/corec/frontend/lexer.py b/bootstrap/corec/frontend/lexer.py index 91a7077f..68c8a237 100644 --- a/bootstrap/corec/frontend/lexer.py +++ b/bootstrap/corec/frontend/lexer.py @@ -89,33 +89,44 @@ def read_number(self, first: str) -> Token: if is_prefixed: digits = '0123456789abcdefABCDEF' if base == 16 else ( '01234567' if base == 8 else '01') - while self.current() in digits or self.current() == '_': + while self.current() in digits: c = self.current() - if c in digits or c == '_': + if c in digits: n += self.advance() else: break + # 数字词法收窄(2026-09-10 语言面收窄 §2):'_' 分隔符不支持——响亮报错, + # 不再消费(与 self-hosted lexer 同款;修复前被静默并入词素)。 + if self.current() == '_': + self.error("invalid character '_' in numeric literal (digit separators not supported)") if n[-1] in 'xXoObB' or not any(c != '_' for c in n[2:]): self.error("invalid integer literal") if self.current().isalnum() or self.current() == '_': self.error("invalid digit in integer literal") else: - while self.current().isdigit() or self.current() == '_': + while self.current().isdigit(): n += self.advance() + if self.current() == '_': + self.error("invalid character '_' in numeric literal (digit separators not supported)") if self.current() == '.' and self.peek().isdigit(): is_float = True n += self.advance() - while self.current().isdigit() or self.current() == '_': + while self.current().isdigit(): n += self.advance() + if self.current() == '_': + self.error("invalid character '_' in numeric literal (digit separators not supported)") if self.current().isalpha(): - suffix = "" - while self.current().isalpha() or self.current().isdigit(): - suffix += self.advance() - n += suffix + # 宽度后缀退役(2026-09-10 语言面收窄 §2)——与 self-hosted lexer 同款 + # 响亮报错;此前后缀被并入词素('10f32')→ parser int() 抛 ValueError。 + self.error("invalid suffix on numeric literal (width suffixes retired 2026-09-10)") n = n.replace('_', '') - # 前缀进制(0x/0o/0b)整数字面量溢出拒绝——i64 语义,与 self-hosted - # lexer 的 base 校准守卫一致(P2)。十进制保持原样词法:self-hosted - # 对十进制幅值边界有既有处理(-9223372036854775808),不一致处理。 + # 前缀进制(0x/0o/0b)整数字面量超形状拒绝——与 self-hosted lexer 的 + # base 校准守卫一致(P2)。层语义(int-unbounded-semantics 定稿): + # 语言 int = 无上限数学整数,本守卫是编码层限制错误(本编译器内部 + # 表示 = 经典 64 位机器字投影;超形状 = 该投影无载体),非语义溢出。 + # 十进制保持原样词法(Python int 任意精度 = 同语义的另一份投影实例): + # self-hosted 对十进制幅值边界有既有处理(-9223372036854775808), + # 不一致处理——两编译器差异 = 各自投影形状差异,非语义分歧。 if is_prefixed: val = int(n[2:], base) if val > 9223372036854775807: diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 56616e11..f9449d3a 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -12,14 +12,129 @@ 2. Run through bootstrap Lexer → Parser → NameResolver → TypeChecker → IRGen 3. Generate x86-64 assembly via X86_64StackAsmGen 4. Assemble + link with rt.s using as + ld + +清单分段(x86 实例化设计 §3——三轴组合;波 1 结构波已实施,Task 1-7 收官): + 段内/段间**相对顺序 = 行为定义面**(平铺编译单元的声明序/全局序),分段只做 + 命名与按序组合,顺序与拆分前逐一对应,不可乱序。 + 架构轴 `arch_x86_64_files` × 格式轴 `format_elf_files` × OS 轴 `os_linux_files` + (波 1 落位:Task 1 三轴搬迁 + 组合根 / Task 3/4 frame·tag2l 入架构轴 / + Task 2/5/6 entry·callseq·syscall 入 OS 轴——**全轴已就位**)+ 组合根 + `x86_linux_target_files`(target triple 命名:project-mode 入口链,不入 + concat——见段注释)。 """ -import sys, os, subprocess +import sys, os, subprocess, io, contextlib sys.path.insert(0, 'bootstrap') BASE = os.path.dirname(__file__) +# ── 清单分段(按三轴组织——concat = 按确定顺序组合)──────────────────── + +# 与轴无关的公共面:runtime + stdlib + 前端共享数据结构。 +common_files = [ + 'src/runtime/arena_globals.cr', + 'src/runtime/rt.cr', + 'src/stdlib/io.cr', + 'src/stdlib/fmt.cr', + 'src/stdlib/cli.cr', + 'src/stdlib/toml.cr', + 'src/compiler/ast.cr', + 'src/compiler/globals.cr', + 'src/compiler/dyn_arr.cr', +] + +# HIT 表引擎段:引擎留 src/arch/hit/(未来实例可复用的框架), +# 表数据 core-x86.toml 归架构轴 src/arch/x86_64/(波 1 Task 1 迁移裁决)。 +hit_engine_files = [ + 'src/arch/hit/hit.cr', + 'src/arch/hit/lower_to_core.cr', +] + +# 语义内核段(lattice——范式无关,corec/corearch 双 concat 共享)。 +kernel_files = [ + 'src/lattice/ent_kernel.cr', +] + +# ① 架构轴 x86_64:寄存器分配 CAG + 指令编码 + 字节尺寸单源(sizes.cr) +# + int 多字 M1 tag/2L 编码族(tag2l.cr——波 1 Task 4 自 instr.cr 整搬)。 +# 段内顺序 = 「被依赖者先」惯例(函数可见性与顺序无关):tag2l 消费 instr 的 +# 编码原语 → instr 之后;frame.cr → tag2l(pf_epilogue 函数尾块调 e2_mw_*; +# tag2l 反向消费 frame 的 g2_tag_off——同轴双向引用,波 1 Task 4 裁定可接受)。 +arch_x86_64_files = [ + 'src/arch/x86_64/regalloc.cr', + 'src/arch/x86_64/sizes.cr', + 'src/arch/x86_64/instr.cr', + 'src/arch/x86_64/tag2l.cr', + 'src/arch/x86_64/frame.cr', +] + +# ② 格式轴 ELF:重定位 + 容器/段/phdr + 链接机制。 +format_elf_files = [ + 'src/format/elf/resolve.cr', + 'src/format/elf/elf.cr', + 'src/format/elf/ld.cr', +] + +# ③ OS 轴 Linux:syscall/callseq/entry(波 1 Task 2/5/6 已全部落位——波 2 参数化待启)。 +# entry.cr(Task 2 落位)= _start 发射序 emit_start/emit_start_size—— +# 自 src/format/elf/elf.cr 整函数搬迁;跨轴调用经本 concat 扁平单元解析。 +# callseq.cr(Task 5 落位)= SysV AMD64 调用约定序列(参数分派/栈参/栈清理/ +# 返回值/直接调用——自 instr.cr emit_instr 内联段落抽出;三处同源合流见文件 +# 头注);跨轴消费 x86_64/instr.cr 编码原语。 +# syscall.cr(Task 6 落位)= Linux syscall 约定发射序(syscall3/syscall4 内置 +# 体——自 instr.cr 内置体分派链抽出;rax 号 + rdi/rsi/rdx(/r10) 参数序 + 0F 05 +# + rax 回存;内置体名索引扫描段留 elf.cr 原位);跨轴消费 x86_64/instr.cr +# 编码原语(e2_mov/e2_w8/e2_store_ret)。 +os_linux_files = [ + 'src/os/linux/entry.cr', + 'src/os/linux/callseq.cr', + 'src/os/linux/syscall.cr', +] + +# 后端收尾段:ccr 载入 + 运行时 stdlib 桥 + corearch 入口。 +# R2 P3 Task 5:monomorph.cr 自本段**迁出**(→ corec_files),理由 = ① 它的类型项化改造需要 +# 类型表读取器/节点合成(get_type_kind/alloc_node——checker/parser 层,corearch 清单无此层); +# ② corearch 侧对它**零引用**(本批以脚本逐符号核对 corearch 链接集:monomorph.cr 的全部 +# 定义符(gen_*/grow_gen_*/g_gen_*)在链接集内只出现在 globals.cr 的**注释**里,无一是代码 +# 引用;单态化本就是前端 ir_gen 期的动作,后端消费已特化的 .ccr)。corearch 行为不变由 +# ELF canary 逐字节 + 全套回归独立验证(本次迁出的唯一产物面影响 = corearch 二进制不再含 +# 这段死码)。 +backend_support_files = [ + # R2 P4 Task 2(TYPE 段读回):ccr_io.cr 的 load 侧要重算项哈希(tt_hash5)、 + # 重建索引(grow_tt_index→tt_reindex)、复位项层(tt_layer_reset),--dump-types + # 通道还要跑项层原语(tt_norm/tt_is_dnf)⇒ type_terms.cr **必须先于 ccr_io.cr** + # 入 corearch 清单(清单顺序 = 常量可见性约束——ESZ_TYPE_TERM/OFF_TT_*/TT_* + # 在本文件;iface_registry.cr:18-23 先例)。 + # **TF01 收口(2026-09-13)**:type_engine.cr 本条**入 corearch**——原阻塞 = 该文件 + # 带两条诊断(lits_copy 真·类型洗白 TF01 + ty_memo_slot_no_grow 的 loop-落空误报) + # ⇒ 入清单即触发 project-mode `error[`=0 门(test_backend_bootstrap)。两条已修 + # (lits_copy 返回型改 string;落空分析入 checker.cr)⇒ 判定原语(ty_sub 族)跨进程 + # 同值面**解阻**(T2 报告的 Task 3/4/P5 收口路径 (a);探针可另批接)。 + # 位置 = type_terms 之后(tt_* 常量可见性)、ccr_io 之前(读回路径无关,仅为阅读序)。 + 'src/compiler/type_terms.cr', + 'src/compiler/type_engine.cr', + 'src/compiler/ccr_io.cr', + 'src/stdlib/hotpatch.cr', + 'src/stdlib/arena.cr', + 'src/stdlib/goroutine.cr', + 'src/stdlib/chan.cr', + 'src/stdlib/sched.cr', + 'src/compiler/corearch.cr', +] + +# 组合根(x86_64-linux target triple):project-mode 入口链——`corec build +# src/targets/x86_64-linux` 的入口 = 该目录 main.cr(load_project 直读)+ +# _import.cr(load_imports 直读)+ Core.toml(工程名)。**不入 concat**: +# main.cr 的 fn main 与 concat 追加的 wrapper 冲突,且其 .cr 经 project-mode +# 独立收编(stage0/stage 链即此入口)。仅纳入清单存在性守卫。 +x86_linux_target_files = [ + 'src/targets/x86_64-linux/main.cr', + 'src/targets/x86_64-linux/_import.cr', + 'src/targets/x86_64-linux/Core.toml', +] + + def concat(files, wrapper_fn=None): """Concatenate .cr files, optionally appending a main wrapper.""" parts = [] @@ -43,45 +158,69 @@ def concat(files, wrapper_fn=None): return src -def compile_and_assemble(src, label, out_name): - """Run full pipeline on src, produce binary at build/{out_name}.""" - from corec.frontend.lexer import Lexer - from corec.frontend.parser import Parser - from corec.frontend.name_resolver import NameResolver - from corec.frontend.desugar import MatchDesugarer - from corec.frontend.type_checker import TypeChecker - from corec.frontend.ir_gen import IRGen - from corec.backend.x86_64_stack_asm import X86_64StackAsmGen - from corec.utils.module_loader import resolve_imports +# ── 构建守卫(x86 实例化设计 §3——吸收 ld 单元静默缺陷教训)────────────── - print(f"--- {label} ---") +def guard_manifest(files, label): + """守卫①:清单文件存在性断言——缺失 = 构建早失败(防路径漂/搬迁漏改)。""" + missing = [f for f in files if not os.path.exists(os.path.join(BASE, f))] + if missing: + print(f"[GUARD FAIL] {label}: {len(missing)} manifest entr(y|ies) missing:") + for f in missing: + print(f" - {f}") + sys.exit(1) + print(f"[GUARD] {label}: manifest OK ({len(files)} files)") + + +# 守卫②:**bootstrap-concat 日志**诊断计数非零 = 失败门(TODO #6 建议③的 +# concat 面落地)。背景:project-mode 单元曾以 rc=0 + 产物正常 + 全套互测 +# byte-identical 通过而**静默吞掉** 34 行 error[(N06 未定义函数 / N01 未定义名)。 +# 计数面 = `error[`(self-hosted corec 诊断前缀,src/compiler/diag.cr:134) +# + Python bootstrap 面的等价未定义符号消息(bootstrap 诊断不带 `error[` +# 前缀,其 checker 的未定义名消息 = 同一类静默未定义信号)。 +# **作用域限制**:本门只见本文件 concat 源经 Python bootstrap 管线的日志 +# (compile_and_assemble 的 _LogTee 缓冲)——self-hosted corec 的 project-mode +# 构建(`corec build `,**TODO #6 ③ 的真正事发层**)不走此路径,故本门 +# 对它零覆盖;该面的同类门 = tests/selfhost/test_backend_bootstrap.py 的 +# run_checked(rc=0 时扫 stdout+stderr 的 `error[`)——test_backend_bootstrap +# 的 stage 链(含各核心单元 project-mode 构建)正是该门的被执行面。两门互补, +# 改一处须同步另一处。 +BOOTSTRAP_UNDEFINED_MARKERS = ("Undefined name:", "Undefined function") - lex = Lexer(src) - tokens = lex.tokenize() - print(f" Tokens: {len(tokens)}") - ast = Parser(tokens).parse_compilation_unit() - resolve_imports(ast) - resolver = NameResolver() - resolver.resolve(ast) - desugarer = MatchDesugarer(resolver.symtab) - ast = desugarer.desugar(ast) - checker = TypeChecker(resolver.symtab) - checker.check(ast) - if resolver.errors: - print(" Errors:", resolver.errors) + +def guard_build_log(log_text, label): + """守卫②:本阶段(bootstrap-concat 面)构建日志 error[/未定义符号计数必须为 0。 + + project-mode 面(`corec build ` 的 self-hosted 构建日志)不经此函数—— + 见 tests/selfhost/test_backend_bootstrap.py run_checked 内的对应门。 + """ + hits = [ln for ln in log_text.splitlines() if "error[" in ln] + undef = sum(log_text.count(m) for m in BOOTSTRAP_UNDEFINED_MARKERS) + if hits or undef != 0: + print(f"[GUARD FAIL] {label}: build log diagnostics — " + f"error[ lines = {len(hits)}, undefined-symbol marks = {undef}") + for ln in hits[:40]: + print(f" {ln}") sys.exit(1) - if checker.errors: - print(" Checker warnings (non-fatal):", checker.errors) - print(" Type check passed") + print(f"[GUARD] {label}: build log clean (error[ = 0, undefined = 0)") - ir_gen = IRGen(resolver.symtab) - mod = ir_gen.gen_module(ast) - print(f" Functions: {len(mod.functions)}") - asm_gen = X86_64StackAsmGen(mod) - asm = asm_gen.generate() - print(f" Assembly: {len(asm)} bytes") +class _LogTee: + """stdout 分流:终端(实时可见)+ 内存缓冲(供守卫②扫描本阶段日志)。""" + + def __init__(self, stream, buf): + self._stream = stream + self._buf = buf + + def write(self, s): + self._buf.write(s) + return self._stream.write(s) + def flush(self): + self._stream.flush() + + +def emit(asm, out_name): + """Assemble + link the generated assembly into build/{out_name}.""" os.makedirs('build', exist_ok=True) asm_path = f'build/{out_name}.s' with open(asm_path, 'w', encoding='utf-8') as f: @@ -104,6 +243,53 @@ def compile_and_assemble(src, label, out_name): print() +def compile_and_assemble(src, label, out_name): + """Run full pipeline on src, produce binary at build/{out_name}.""" + from corec.frontend.lexer import Lexer + from corec.frontend.parser import Parser + from corec.frontend.name_resolver import NameResolver + from corec.frontend.desugar import MatchDesugarer + from corec.frontend.type_checker import TypeChecker + from corec.frontend.ir_gen import IRGen + from corec.backend.x86_64_stack_asm import X86_64StackAsmGen + from corec.utils.module_loader import resolve_imports + + print(f"--- {label} ---") + + log = io.StringIO() + with contextlib.redirect_stdout(_LogTee(sys.__stdout__, log)): + lex = Lexer(src) + tokens = lex.tokenize() + print(f" Tokens: {len(tokens)}") + ast = Parser(tokens).parse_compilation_unit() + resolve_imports(ast) + resolver = NameResolver() + resolver.resolve(ast) + desugarer = MatchDesugarer(resolver.symtab) + ast = desugarer.desugar(ast) + checker = TypeChecker(resolver.symtab) + checker.check(ast) + if resolver.errors: + print(" Errors:", resolver.errors) + sys.exit(1) + if checker.errors: + print(" Checker warnings (non-fatal):", checker.errors) + print(" Type check passed") + + ir_gen = IRGen(resolver.symtab) + mod = ir_gen.gen_module(ast) + print(f" Functions: {len(mod.functions)}") + + # 守卫②:诊断面(error[/未定义符号)在本阶段日志内必须为零。 + guard_build_log(log.getvalue(), label) + + asm_gen = X86_64StackAsmGen(mod) + asm = asm_gen.generate() + print(f" Assembly: {len(asm)} bytes") + + emit(asm, out_name) + + def build_runtime(): """Build the shared runtime .o once.""" print("--- Runtime (rt.s) ---") @@ -122,75 +308,90 @@ def main(): build_runtime() # corec — frontend: .cr → .ccr/.cir + corec_files = [ + 'src/runtime/arena_globals.cr', + 'src/runtime/rt.cr', + 'src/stdlib/io.cr', + 'src/stdlib/fmt.cr', + 'src/stdlib/cli.cr', + 'src/compiler/ast.cr', + 'src/compiler/globals.cr', + 'src/compiler/dyn_arr.cr', + 'src/compiler/lexer.cr', + 'src/compiler/parser.cr', + 'src/compiler/checker.cr', + # R2 P0 类型项引擎(建层):类型项 DAG 表 + 构造去重 → 判定引擎(桩)→ 自测通道。 + # 置于 checker 之后(计划 Step 9 指定位置);引擎尚无 checker 侧消费者, + # 本批为纯增量(checker/ir_gen/后端零改动 = 产物零变化)。 + 'src/compiler/type_terms.cr', + 'src/compiler/type_engine.cr', + # R2 P2b Task 1 本质条目表(native interface entries)+ iface_* 查询 API:**静态 + # 数据表 + 只读查询**,零消费者零行为变化(不 alloc g_types 行 ⇒ .ccr 零扰动)。 + # 置于引擎之后、桥接层之前(Task 2 起桥接层委托 iface_by_ty_code)。 + 'src/compiler/iface_registry.cr', + # R2 P4 Task 0:iface 满足判定簇 + `iface_kind_of` 的 corec-only 宿主(链接面纯化—— + # type_engine.cr 留纯核、iface_registry.cr 除该函数;corearch 清单**不含**本文件)。 + 'src/compiler/iface_axis.cr', + # R2 P1 桥接层(checker ti → 引擎类型项 + per-ti 缓存)。文件名 = 历史遗留(前身含 + # R2 P1 影子对拍挂点,已随 R2 P5 Task 5 下线);现为**生产面**(判定路径无条件经它译项)。 + # **corelsp 清单也必须含本层**(checker.cr 的判定入口引用 sh_term_of_ti;corearch 无 + # checker 故仍不含)。相对顺序与 corec 一致(checker → 引擎 → 桥接)。 + 'src/compiler/ty_shadow.cr', + # R2 P4 Task 2:TYPE(7) 段内容构造(D13 确定性装填 + 段体缓冲 + corec-only + # dump 节)。位置 = 影子层之后(装填引用 sh_term_of_ti 与 checker 类型行表; + # 函数跨文件可见,顺序只为常量可见性与阅读序)、type_selftest 之前(自测段 + # ts_ccr_run 调本文件函数)。**corearch 清单不含本文件**(D18:装填引用 + # 桥接层——corearch 无此层)。 + 'src/compiler/ccr_types.cr', + 'src/compiler/type_selftest.cr', + 'src/compiler/purity_selftest.cr', + 'src/compiler/opt.cr', + 'src/compiler/ptr_analysis.cr', + 'src/compiler/region_check.cr', + 'src/compiler/provenance_verify.cr', + 'src/compiler/diag.cr', + 'src/compiler/ext_mgr.cr', + 'src/compiler/ext_safety.cr', + 'src/compiler/ir_gen.cr', + # R2 P3 Task 5:自 backend_support_files 迁入(原因与 corearch 零引用证据见该段注)。 + # 位置 = ir_gen 之后(函数跨文件可见与顺序无关;本文件的自有全局只在本文件使用)。 + 'src/compiler/monomorph.cr', + 'src/compiler/pass.cr', + 'src/compiler/dataflow.cr', + 'src/compiler/ccr_io.cr', + 'src/lattice/ent_kernel.cr', + 'src/compiler/module.cr', + 'src/stdlib/toml.cr', + 'src/stdlib/hotpatch.cr', + 'src/stdlib/arena.cr', + 'src/stdlib/goroutine.cr', + 'src/stdlib/chan.cr', + 'src/stdlib/sched.cr', + 'src/compiler/project.cr', + 'src/stdlib/os.cr', + 'src/compiler/interp.cr', + 'src/compiler/dump.cr', + 'src/compiler/cir_cache.cr', + 'src/compiler/main.cr', + ] + guard_manifest(corec_files, 'corec') compile_and_assemble( - concat([ - 'src/runtime/arena_globals.cr', - 'src/runtime/rt.cr', - 'src/stdlib/io.cr', - 'src/stdlib/fmt.cr', - 'src/stdlib/cli.cr', - 'src/compiler/ast.cr', - 'src/compiler/globals.cr', - 'src/compiler/dyn_arr.cr', - 'src/compiler/lexer.cr', - 'src/compiler/parser.cr', - 'src/compiler/checker.cr', - 'src/compiler/opt.cr', - 'src/compiler/ptr_analysis.cr', - 'src/compiler/region_check.cr', - 'src/compiler/provenance_verify.cr', - 'src/compiler/diag.cr', - 'src/compiler/ext_mgr.cr', - 'src/compiler/ext_safety.cr', - 'src/compiler/ir_gen.cr', - 'src/compiler/pass.cr', - 'src/compiler/dataflow.cr', - 'src/compiler/ccr_io.cr', - 'src/compiler/module.cr', - 'src/stdlib/toml.cr', - 'src/stdlib/hotpatch.cr', - 'src/stdlib/arena.cr', - 'src/stdlib/goroutine.cr', - 'src/stdlib/chan.cr', - 'src/stdlib/sched.cr', - 'src/compiler/project.cr', - 'src/stdlib/os.cr', - 'src/compiler/interp.cr', - 'src/compiler/dump.cr', - 'src/compiler/cir_cache.cr', - 'src/compiler/monomorph.cr', - 'src/compiler/main.cr', - ], wrapper_fn='compiler_main'), + concat(corec_files, wrapper_fn='compiler_main'), label='corec', out_name='corec', ) # corearch — backend: .ccr → binary/asm + # 段顺序 = 行为定义面:common → hit → kernel → 架构轴 → 格式轴 → OS 轴 + # → 后端收尾(与拆分前清单逐一对应)。组合根 x86_linux_target_files 不入 + # concat(见段注释)——单独纳入存在性守卫。 + corearch_files = (common_files + hit_engine_files + kernel_files + + arch_x86_64_files + format_elf_files + os_linux_files + + backend_support_files) + guard_manifest(corearch_files, 'corearch') + guard_manifest(x86_linux_target_files, 'x86_64-linux target') compile_and_assemble( - concat([ - 'src/runtime/arena_globals.cr', - 'src/runtime/rt.cr', - 'src/stdlib/io.cr', - 'src/stdlib/fmt.cr', - 'src/stdlib/cli.cr', - 'src/stdlib/toml.cr', - 'src/compiler/ast.cr', - 'src/compiler/globals.cr', - 'src/compiler/dyn_arr.cr', - 'src/arch/linux/ld/sizes.cr', - 'src/arch/linux/ld/instr.cr', - 'src/arch/linux/ld/resolve.cr', - 'src/arch/linux/ld/elf.cr', - 'src/arch/linux/ld/ld.cr', - 'src/compiler/ccr_io.cr', - 'src/compiler/monomorph.cr', - 'src/stdlib/hotpatch.cr', - 'src/stdlib/arena.cr', - 'src/stdlib/goroutine.cr', - 'src/stdlib/chan.cr', - 'src/stdlib/sched.cr', - 'src/compiler/corearch.cr', - ], wrapper_fn='corearch_main'), + concat(corearch_files, wrapper_fn='corearch_main'), label='corearch', out_name='corearch', ) @@ -201,35 +402,46 @@ def main(): # (ast/dyn_arr/toml/os/hotpatch——hotpatch 为 rt.s 的 SIGHUP 处理器必需)。 # 注意:不含 src/compiler/main.cr 与 entry.cr——corec_main 未被 lsp 使用, # 且 entry.cr 的 fn main() 与 concat 追加的 wrapper 冲突。 + corelsp_files = [ + 'src/runtime/arena_globals.cr', + 'src/runtime/rt.cr', + 'src/stdlib/io.cr', + 'src/stdlib/fmt.cr', + 'src/stdlib/toml.cr', + 'src/stdlib/os.cr', + 'src/stdlib/hotpatch.cr', + 'src/stdlib/panic.cr', + 'src/stdlib/arena.cr', + 'src/stdlib/goroutine.cr', + 'src/stdlib/chan.cr', + 'src/stdlib/sched.cr', + 'src/compiler/ast.cr', + 'src/compiler/globals.cr', + 'src/compiler/dyn_arr.cr', + 'src/compiler/lexer.cr', + 'src/compiler/parser.cr', + 'src/compiler/checker.cr', + # R2 P1:checker 的判定入口引用桥接层(sh_term_of_ti;影子挂点已随 P5 T5 下线)→ + # corelsp 也必须链接 引擎 + 桥接层(相对顺序与 corec 一致;corearch 无 checker 仍不含)。 + 'src/compiler/type_terms.cr', + 'src/compiler/type_engine.cr', + # R2 P2b Task 1:注册表(引擎之后、影子层之前;Task 2 起影子层委托本层) + 'src/compiler/iface_registry.cr', + # R2 P4 Task 0:iface 簇 + iface_kind_of(corec-only;checker.cr 经本层消费 iface_kind_of) + 'src/compiler/iface_axis.cr', + 'src/compiler/ty_shadow.cr', + 'src/compiler/diag.cr', + 'src/compiler/module.cr', + 'src/lsp/_import.cr', + 'src/lsp/json.cr', + 'src/lsp/rpc.cr', + 'src/lsp/lsp.cr', + 'src/lsp/analysis.cr', + 'src/lsp/main.cr', + ] + guard_manifest(corelsp_files, 'corelsp') compile_and_assemble( - concat([ - 'src/runtime/arena_globals.cr', - 'src/runtime/rt.cr', - 'src/stdlib/io.cr', - 'src/stdlib/fmt.cr', - 'src/stdlib/toml.cr', - 'src/stdlib/os.cr', - 'src/stdlib/hotpatch.cr', - 'src/stdlib/panic.cr', - 'src/stdlib/arena.cr', - 'src/stdlib/goroutine.cr', - 'src/stdlib/chan.cr', - 'src/stdlib/sched.cr', - 'src/compiler/ast.cr', - 'src/compiler/globals.cr', - 'src/compiler/dyn_arr.cr', - 'src/compiler/lexer.cr', - 'src/compiler/parser.cr', - 'src/compiler/checker.cr', - 'src/compiler/diag.cr', - 'src/compiler/module.cr', - 'src/lsp/_import.cr', - 'src/lsp/json.cr', - 'src/lsp/rpc.cr', - 'src/lsp/lsp.cr', - 'src/lsp/analysis.cr', - 'src/lsp/main.cr', - ], wrapper_fn='lsp_main'), + concat(corelsp_files, wrapper_fn='lsp_main'), label='corelsp', out_name='corelsp', ) diff --git a/docs/compcert-reference.md b/docs/compcert-reference.md index ba42ab54..0c68ad06 100644 --- a/docs/compcert-reference.md +++ b/docs/compcert-reference.md @@ -39,7 +39,7 @@ compcert/ |---|---|---| | `backend/RTL.v` | 3 地址 IR | `src/compiler/dataflow.cr`(HDFG) | | `backend/Allocation.v` + `Allocproof.v` | 寄存器分配 + 正确性证明 | `src/compiler/opt.cr`(寄存器分配器) | -| `backend/Linearize.v` | 图 → 线性序列 | `src/compiler/ccr_io.cr` 的格层线性投影(v5;C 路线升级格形态 v6) | +| `backend/Linearize.v` | 图 → 线性序列 | `src/compiler/ccr_io.cr` 的格形态 .ccr(v6:段表架构 + ENT 存在结构段;v6-only,见 `specs/2026-09-05-lattice-ir-v6-format.md`) | | `x86/Asm.v` | 每条机器指令的语义模型 | `src/arch/linux/ld/instr.cr`(指令编码) | | `x86/Asmgen.v` + `Asmgenproof.v` | 指令生成 + 生成正确性 | `corearch.cr` 的发射逻辑 | | `x86/TargetPrinter.ml` | 汇编文本打印 | `src/arch/linux/ld/elf.cr`(ELF 输出) | diff --git a/docs/crasm.md b/docs/crasm.md index f44f5ea9..c5184721 100644 --- a/docs/crasm.md +++ b/docs/crasm.md @@ -1,5 +1,9 @@ # .crasm 统一汇编抽象层 +> **状态:已废弃(2026-09-05)**——独立 .crasm 格式取消,绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表)。 +> 被硬件接口表(`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`)吸收:跨平台 = HIT 事件 + 投影表; +> MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件;事件可读形态 = v6 NOD 文本 dump。本文保留为历史记录。 + ## 概述 Core 的内核路线(project-book 第五阶段)需要汇编级能力(MMIO、特权指令、中断), diff --git a/docs/error-codes.md b/docs/error-codes.md index 2cc494fd..92a9b983 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -3,6 +3,13 @@ 每一个错误码对应编译器中 **一个唯一可区分的检查点**。 同一条性质但不同上下文(赋值 vs 传参 vs 初始化)的报错各占独立码。 +> **fail-closed 语义(2026-09-15,FC 批 T2 起)**:**默认阻断**——任何**不在豁免登记表**内的 +> 类型面诊断 ⇒ `rc=1` + **零产物**(不落目标 ELF、不落本次 `.ccr`)。豁免表 = +> `src/compiler/diag.cr::diag_gate_exempt`(码级 9 条:TF01 · TF07 · TB01 · TM04 · TK01 · B04 +> [build 面] + N01 · N06 · N11 [check 面,仅登记];每条带位点证据/理由/退出条件,**只减不增**)。 +> 语法面(P/L 族)与安全检查面(B11/TU03…)本就「任一即 rc=1」;`check` 面的 rc 规则 +> (计数 > 0)不消费豁免表 ⇒ check 基线不换代(裁-FC-1 = (C))。 + --- ## L0xx — 词法 (Lexer) @@ -44,6 +51,10 @@ | P017 | fileid 声明格式 | `Invalid fileid declaration` | | P018 | 变量声明语法 | `Invalid variable declaration syntax` | | P019 | 字面量后缀溢出 | `Numeric literal overflow` | +| P020 | 形参数目超上限(> `MAX_FN_PARAMS=64`) | `too many parameters`(TODO #8 修复:修复前 ≥18 形参静默误编译——越界写踩 return_type/ast_node) | +| P021 | 函数体内嵌套 `fn` 声明(不属语言面) | 定位拒绝(TODO #16 修复:修复前 parse 失步 → bump allocator 耗尽 → `rep movsb` 向 NULL 拷 → rc=139) | +| P022 | 枚举变体数 / 变体载荷类型数超上限(> `MAX_ENUM_VARIANTS=16` / `MAX_VARIANT_TYPES=16`) | `Enum has too many variants (17 > 16)` / `Enum variant has too many payload types (17 > 16)`(TODO #35 修复:修复前写入侧**无界**——第 17 变体槽起点 = `variant_count` 自身、槽尾越记录尾 224B 的静默越界写) | +| P023 | 结构体字段数超上限(> `MAX_STRUCT_FIELDS=16`) | `Struct has too many fields (17 > 16)`(TODO #35 同族:第 17 字段踩 `OFF_SI_FIELD_COUNT`/泛型槽与邻记录;修复前 check rc=0 零诊断) | ## N0xx — 名字解析 (Name Resolution) @@ -84,10 +95,17 @@ ## TA0xx — 类型检查:赋值与绑定 +> TA02 自 2026-09-13(R2 P5 Task 6 / TODO #32)起**实现**并列入 run_frontend 硬错误名单 +> (rc=1 且不产出产物):修复前 `EXPR_LET` 站点**无任何兼容检查**(符号取注解行、后端按注解 +> 行发射 = 静默错产物;`EC_TA_DECL` 定义零 raise)。判定点 = `checker.cr::check_let_annot_compat` +> (局部 `EXPR_LET` + 全局 `check_global_let` 两调用点共用),组合函数 = `type_compat_strict` +> (身份 + 常量档长度 + 可选目标注入)。豁免:无注解/auto/无初值 · 注解 `dyn` · 值 `never` +> (底部 + 错误标记=级联抑制)· 注解为泛型形参。report-only 全语料零命中后开门。 + | 码 | 检查点 | 消息模板 | |----|--------|---------| | TA01 | 赋值号类型不匹配 | `Cannot assign {T2} to {T1}` | -| TA02 | 变量声明类型与初始值不符 | `Variable declared as {T1}, got {T2}` | +| TA02 | 变量声明类型与初始值不符(**2026-09-13 R2 P5 Task 6 起实现**:`EXPR_LET` 判定点——局部与全局初始化器共用;`-1` 分支 = 常量档数组长度约束,措辞同 TS03/TK02) | `Variable declared as {T1}, got {T2}` / `Array length constraint not satisfied` | | TA03 | 批量声明类型不一致 | `Batch declaration has mixed types: {T1} vs {T2}` | | TA04 | 赋值给不可变变量 | `Cannot assign to immutable variable '{name}'` | | TA05 | 变量未声明 mutable | `Variable '{name}' is not mutable` | @@ -144,7 +162,7 @@ | 码 | 检查点 | 消息模板 | |----|--------|---------| | TC01 | if 条件不是 bool | `If condition must be `bool`, got {T}` | -| TC02 | if/else 分支类型不一致 | `If branches have different types: {T1} vs {T2}` | +| TC02 | if/else 分支类型不一致(**2026-09-14 起带「else 支发散」豁免**:`EXPR_RETURN` 推断 = 所返回值类型 ⇒ `{ return X; }` 支的「类型」是幻影,而 if 的值类型定义为 `then_ti` ⇒ **else 支确定发散时不判**;判定谓词 = `stmt_diverges`(checker.cr,**只服务本判定点**)。**不对称是有意的**:then 支发散时 `then_ti` 本身即幻影(模型面)⇒ 仍报,保留真信号) | `If branches have different types: {T1} vs {T2}` | | TC03 | if 单分支不能有返回值 | `If without `else` cannot return value` | | TC04 | while 条件不是 bool | `While condition must be `bool`, got {T}` | | TC05 | loop 内 break 带值不一致 | ``break` with value conflicts with previous `break` without value` | @@ -170,7 +188,7 @@ | 码 | 检查点 | 消息模板 | |----|--------|---------| | TK01 | 下标索引非数组 | `Cannot index type {T}` | -| TK02 | 数组元素类型不一致 | `Expected array element type {T1}, got {T2}` | +| TK02 | 数组元素类型不一致(2026-09-11 TODO #29 起字面量处**实现**:元素类型取首元素,后续逐个比对;硬错误) | `Expected array element type {T1}, got {T2}` | | TK03 | 数组大小不是整数 | `Array size must be `int`` | | TK04 | 数组大小为负数 | `Array size must be positive, got {size}` | | TK05 | 切片越界 | `Slice start {N} is out of bounds (length {L})` | @@ -180,6 +198,10 @@ ## TS0xx — 类型检查:结构体字面量 +> 四码自 2026-09-11(TODO #29)起**全部实现**并列入 run_frontend 硬错误名单(rc=1 且不产出 +> 产物):修复前字段名被丢弃(值按声明位序绑定 = 静默错值),四校验均不存在。字段值现按 +> **名字**绑定(与 Python bootstrap 的 gen_struct_lit 同语义;求值顺序仍为源序)。 + | 码 | 检查点 | 消息模板 | |----|--------|---------| | TS01 | 缺少必要字段 | `Missing field '{name}' in struct literal {struct}` | @@ -245,6 +267,7 @@ | ICE01 | 不应该发生的状况 | `Internal compiler error: {detail}` | | ICE02 | 全局缓冲区溢出 | `Compiler limit: {buffer} overflow (max {max})` | | ICE03 | IR 生成缺实现 | `Unsupported expression: {kind}` | +| ICE04 | 类型判定不可判(引擎 `-1`:未覆盖面/预算耗尽;或桥接缺口 = 该行译不成类型项)——R2 P5 Task 4 的 P-A 政策:**未知不当 0/1**(legacy 回落面已删),硬错拒绝落盘;反例(两侧类型项文本)随消息,判定点无 AST 位置 ⇒ `--> 0:0` | `type judgment indeterminate: {term_a} vs {term_b} ({cause})` / `type judgment indeterminate: no type term for type row {t1} / {t2} (bridge gap)` | --- @@ -253,7 +276,7 @@ | 段 | 范围 | 数量 | 说明 | |----|------|------|------| | L | L001–L011 | 11 | 词法 | -| P | P001–P019 | 19 | 语法 | +| P | P001–P023 | 23 | 语法 | | N | N001–N021 | 21 | 名字解析 | | I | I001–I006 | 6 | 类型推断 | | TA | TA01–TA08 | 8 | 赋值与绑定 | @@ -268,5 +291,5 @@ | B | B001–B010 | 10 | 借用 | | R | R001–R004 | 4 | 运行时 | | E | E001–E004 | 4 | I/O | -| ICE | ICE01–ICE03 | 3 | 编译器内部 | -| **总计** | | **~145** | | +| ICE | ICE01–ICE04 | 4 | 编译器内部 | +| **总计** | | **~146** | | diff --git a/docs/ir-schema/coreir-schema.md b/docs/ir-schema/coreir-schema.md index 826b5d9e..5f909c51 100644 --- a/docs/ir-schema/coreir-schema.md +++ b/docs/ir-schema/coreir-schema.md @@ -7,11 +7,11 @@ Core 编译器使用两种中间表示: | 格式 | 全称 | 用途 | 生产者 | 消费者 | |------|------|------|--------|--------| | `.cir` | Core IR Graph | HDFG + 规约约束(验证 IR) | `corec`(前端) | 验证工具 / `corearch` / 解释器 | -| `.ccr` | Core Region Representation(C 路线:格形态,v6 演进中) | 格层存在结构的线性投影(region 段;升级设计见 `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`) | `corec`(前端) | `corearch`(后端) | +| `.ccr` | Core Region Representation(C 路线:格形态,v7 = 真图载体) | 格层**图载体**——NOD+EDG(节点 + 数据/state 边)+ ENT(条目 × 版本)+ REG(区域),存在结构与图语义落盘;文件序 = 合法拓扑调度,**执行序 = 投影**(字节权威 = `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`) | `corec`(前端) | `corearch`(后端) | **`.cir` 是 Core 的验证核心。** 它承载程序的完整语义(HDFG)。验证工具消费 `.cir` + `.csr`(规约约束元数据)进行验证。 -自托管编译器在 IR 生成期间同时构建 HDFG(`.cir`,图形态)和格层线性投影(`.ccr`,格形态 v5),然后 `lower_to_ccr()` 将图节点拷贝为线性指令数组供 x86-64 后端消费。 +自托管编译器在 IR 生成期间同时构建 HDFG(`.cir`,图形态)和格层图载体(`.ccr`,格形态 v7——NOD/EDG/ENT/REG 段,节点 + 边 + 条目落盘),然后 `lower_to_ccr()` 将图节点镜像投影为文件序线性数组(= 合法拓扑调度)供 x86-64 后端消费——执行序是图的投影,不再是文件唯一语义主干。 --- @@ -246,13 +246,39 @@ struct 类型关联布局描述符。**默认由编译器推导自然布局**( --- -## 六、线性化(HDFG → `.ccr`) +## 六、图载体投影(HDFG → `.ccr`) -`lower_to_ccr()` 将HDFG线性化为线性 IR 指令数组供后端消费。规约约束节点(opcode ≥ 30)照常线性化,但 `corearch` 后端在代码生成时跳过它们。 +`lower_to_ccr()` 将 HDFG 镜像投影为文件序线性数组供后端消费——v7 起文件序 = 生产者选择的合法拓扑调度(沿 EDG 边约束,DAG 前向),**执行序 = 图的投影**,不再是文件唯一语义主干(格形态语义见 `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md`)。规约约束节点(opcode ≥ 30)照常线性化,但 `corearch` 后端在代码生成时跳过它们。 -### `.ccr` 二进制序列化(v5,2026-08) +### `.ccr` 二进制序列化(v7,2026-09;v7-only——v6 中间态已退役) -`.ccr` 文件头 magic 为 `0x31524343`(ASCII `CCR1`)。序列化 v2(版本号 5):文件尾部追加 SG region 段(`sg_count × 24B`:kind/enter/exit/parent/nstart/ncount 六个 i32,即 `ESZ_SG_DISK`),v4 文件兼容加载。**DFEdge 不落盘 `.ccr`**——CCR1 writer(`save_ccr`,ccr_io.cr)只序列化指令/变量/字符串等数组;边仅在内存与 `.cir` 缓存中(v5 按 4×8B:from/to/next/kind,0=data, 1=state)。 +`.ccr` 文件头 magic 为 `0x31524343`(ASCII `CCR1`)。v7 = 段表架构 + 真图载体(字节权威 = `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md` + `ccr_io.cr` 头注释): + +``` +[header 16B]: magic u32="CCR1" | version u32=7 | seg_count u32=6 | reserved u32=0 +[seg table 6×12B]: {tag u32, offset u32, size u32}——规范序(tag=行号 1..6) +[seg bodies]: + STR(1) 字符串表: [str_count] [× {len u32, data}] + SYM(2) 符号面: globals(16B {name,type,init}) + funcs(内嵌 var 声明区、 + param_ents、first_ent/last_ent、root_region)+ str_consts + /structs/enums/opt_meta 自描述拼接 + NOD(3) 节点表: [nod_count] [×36B {op i32,dest i32,src1 i64,src2 i32, + src3 i32,tk i32,first_edge u32,edge_count u32}]——28B + 语义字段 + 邻接索引;NOD id = 文件序 = 图坐标(D1) + ENT(4) 条目表: [ent_count] [×28B {var_id i32,version u32,def_nod i32, + live_start u32,live_end u32(半开:最后使用点+1),home i32, + flags u32}]——存在结构(corec 产实记录;home 恒 -1 + 直通 = 实例层注记) + REG(5) region 表: [sg_count] [×24B kind/parent/enter_nod/exit_nod/ + first_ent/last_ent](enter/exit = NOD id,nstart/ncount + 由 span 派生) + EDG(6) 边表(v7 必落):[edg_count] [×8B {to_nod u32, kind u32}]——节点出边 + 连续成段(first_edge = 前缀累计);每条边 to_nod > 所属 + 节点(前向拓扑);kind 0=数据 1=state(branch/jump 目标 + = 操作数字段非边) +``` + +v7-only:load 校验 version==7(无 v6 兼容/转换——.ccr 为管线中间产物现生成)。v6 中间态(5 段、NOD 28B、ENT 恒空、**DFEdge 不落盘**——边仅在内存与 `.cir` 缓存中)已被取代:v7 起 EDG 段必落(P3 修复),ENT 携带实记录(P2 修复)。 --- diff --git a/docs/numeric-migration-inventory.md b/docs/numeric-migration-inventory.md index b9fe4234..1c56b78d 100644 --- a/docs/numeric-migration-inventory.md +++ b/docs/numeric-migration-inventory.md @@ -4,6 +4,8 @@ 依据:[2026-08-16-numeric-types-design.md](superpowers/specs/2026-08-16-numeric-types-design.md)(已批准) 用途:Task 5/6 的迁移契约——每个站点按本表逐点执行,**禁止机械替换(sed/全局替换)**。 +> **追记(2026-09-10,语言面收窄 R1)**:本盘点为 2026-08-16 历史快照——下述分类结论按当时约束固化,**不在本文件改写历史**(原文保留)。R1(计划 `docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md`)对其中**宽度三项**改判并落实:`T_FLOAT_F32/T_FLOAT_F64`(与 `T_INT_I8..T_INT_U64` 同族)及 `W_I8..W_F64` **删除**(ce3e541c——编号保留勿复用)、`_f32`/`_f64` 后缀**退役 + 响亮报错**(51d74be3——宽度 = 机器形状,归映射层)。故本表原标 `保留` 的三处(`ast.cr:93-94`、`ast.cr:117-118`、分类规则表「`_f32`/`_f64` 后缀 → 保留」行)**以本次改判为准**;下文相关行逐点标注 R1 落点与一处实测修正(`analysis.cr:895` 行)。 + ## 分类规则 | 分类 | 含义 | 判定标准 | @@ -26,10 +28,10 @@ |---|---|---|---| | ast.cr:8 `T_FLOAT : int = 3` | 字面量令牌 kind | `dex` | `3.14` 字面量默认语义 → 精确 dex;令牌更名/改义(T_DEX),默认路径不再产 binary64 位模式 | | ast.cr:84 注释 | 注释 | `保留` | 后缀令牌说明(`_i32/_u64/_f32`),历史注释 | -| ast.cr:93-94 `T_FLOAT_F32/T_FLOAT_F64 = 85/86` | 后缀字面量令牌 | `保留` | `_f32/_f64` 后缀 → apx CPU 位宽标注,令牌保留 | +| ast.cr:93-94 `T_FLOAT_F32/T_FLOAT_F64 = 85/86` | 后缀字面量令牌 | `保留` | `_f32/_f64` 后缀 → apx CPU 位宽标注,令牌保留。**2026-09-10 R1 改判 = 删除**(ce3e541c——lexer 从不发射,宽度后缀退役改响亮报错,编号 85/86 保留勿复用;见行首追记) | | ast.cr:99 `T_FLOAT_TYPE : int = 91` | 类型关键字令牌 kind | `删除` | float 关键字消亡;该常量**从未被 lexer 发射**(类型名按 lexeme 匹配,见 parser.cr:84)——死常量残留。删除时勿重编号(保持 LSP 连续区间 90..95 语义,见 analysis.cr:895 说明) | | ast.cr:108 注释 | 注释 | `保留` | 宽度常量说明(EXPR_INT/EXPR_FLOAT) | -| ast.cr:117-118 `W_F32/W_F64 = 9/10` | 位宽常量 | `保留` | `_f32/_f64` → apx CPU 位宽标注 | +| ast.cr:117-118 `W_F32/W_F64 = 9/10` | 位宽常量 | `保留` | `_f32/_f64` → apx CPU 位宽标注。**2026-09-10 R1 改判 = 删除**(ce3e541c——`W_I8..W_F64` 全族随宽度分支一并清) | | ast.cr:122 `TY_FLOAT : int = 1` | 核心类型常量 | `dex` | 类型本身更名 TY_DEX,默认精确(语义变更);设计 §5:float 类型名删除、类型迁为 dex | | ast.cr:206 `EXPR_FLOAT : int = 27` | 字面量 AST 节点 kind | `dex` | 字面量节点语义 → dex;注释已预示"int_val = value (as scaled int)"(缩放整数 = dex 表示) | | ast.cr:296 `TI_FLOAT : int = 1` | IR 类型索引 | `dex` | 随类型更名 TI_DEX;单一类型(dex 永远是 dex,apx 只是附注) | @@ -48,7 +50,7 @@ | 站点 | 角色 | 分类 | 理由 | |---|---|---|---| | parser.cr:84 `lex == "float"` → TY_FLOAT | 类型名解析 | `dex` | 用户可见类型名;`float` → `dex`(默认精确,语义变更) | -| parser.cr:405-411 `T_FLOAT/T_FLOAT_F32/F64` 字面量解析 | 字面量节点构建 | `dex` | 字面量节点 EXPR_FLOAT 语义 → dex(默认精确);其中 409-410 的 W_F32/W_F64 宽度传递保留(后缀标注不动,parser 404 行的 T_FLOAT_F32/F64 分支配对保留) | +| parser.cr:405-411 `T_FLOAT/T_FLOAT_F32/F64` 字面量解析 | 字面量节点构建 | `dex` | 字面量节点 EXPR_FLOAT 语义 → dex(默认精确);其中 409-410 的 W_F32/W_F64 宽度传递保留(后缀标注不动,parser 404 行的 T_FLOAT_F32/F64 分支配对保留)。**2026-09-10 R1 更新**(ce3e541c):T_FLOAT_F32/F64 分支配对与 W_* 宽度传递**已删除**——现 `parse_primary` 只认 `T_DEX`,`data` 槽恒 0(后缀退役改响亮报错,51d74be3) | ### src/compiler/checker.cr(12 处,全部 `dex`) @@ -145,7 +147,7 @@ | analysis.cr:312 `analysis_type_of_expr` EXPR_FLOAT → "float" | 字面量类型显示 | `dex` | 字面量语义 → dex,显示 "dex" | | analysis.cr:848 注释("运算符与 int/float 字面量令牌的 lexeme = -1") | 注释 | `保留` | 注释(令牌跨度扫描说明) | | analysis.cr:864-881 注释块(tokenType 映射表:864 含 T_FLOAT_F64、865 内置类型名 int/float/…、881 "number (7): T_INT T_FLOAT") | 注释 | `保留` | 映射表注释;legend 结构本身不改(number 类保留) | -| analysis.cr:895 `(k >= T_INT_I8 && k <= T_FLOAT_F64)` 区间 | semanticTokens legend 分派 | `保留` | 区间含 T_FLOAT_F32/F64(后缀令牌保留 → 区间保持有效,无需改动);另含 T_INT_TYPE..T_AUTO_TYPE 区间——若 T_FLOAT_TYPE 删除但不重编号,区间仍正确(见 ast.cr:99 行理由) | +| ~~analysis.cr:895 `(k >= T_INT_I8 && k <= T_FLOAT_F64)` 区间~~ | semanticTokens legend 分派 | ~~`保留`~~ **该行本身即过时(实测不存在)** | **2026-09-10 R1 实测修正**:`src/lsp/analysis.cr` 中 `T_INT_I8` / `T_FLOAT_F32` / `T_FLOAT_F64` **零出现**(grep 实证),原表所引区间不存在——该处实际只有 `(k >= T_INT_TYPE && k <= T_AUTO_TYPE)`(analysis.cr:981,90..95 类型关键字区间),与宽度令牌无关;且 analysis.cr:864 处原称含 T_FLOAT_F64 的注释块同样已不存在。宽度令牌删除对 LSP 零影响(本行可按「不适用」结案)。 | | analysis.cr:900 `k == T_FLOAT` → number legend(7) | 令牌→legend 分派 | `dex` | T_FLOAT 令牌更名 T_DEX 后同步(仍映射 number) | | analysis.cr:908 `str_eq(s, "float")` 内置类型名判定 | 内置类型名清单 | `dex` | 名字改 "dex"(与 parser.cr:84 同源清单) | | analysis.cr:955 `k == T_FLOAT` 令牌跨度扫描 | 令牌长度计算(镜像 lexer) | `dex` | 随令牌更名同步;后缀令牌分支(956 起小数扫描)保留 | @@ -187,7 +189,7 @@ | 层 | dex(默认精确,语义变更) | apx(授权近似,binary64 快路径保留) | 涉及站点 | |---|---|---|---| | 字面量 | `3.14` → 缩放整数常量(精确解析,新路径) | `3.14` → binary64 位模式(str_to_f64_bits 保留) | lexer:121/331、ir_gen:482-483、parser:405-411、ast:8/206 | -| 词法令牌 | T_FLOAT → T_DEX(字面量令牌) | T_FLOAT_F32/F64 保留(apx CPU 位宽标注) | ast:8/93-94 | +| 词法令牌 | T_FLOAT → T_DEX(字面量令牌) | ~~T_FLOAT_F32/F64 保留(apx CPU 位宽标注)~~ **2026-09-10 R1 退役**(字母后缀一律响亮报错;编号 85/86 保留勿复用) | ast:8/93-94 | | 类型系统 | TY_FLOAT→TY_DEX、TI_FLOAT→TI_DEX(单一类型,dex 永远是 dex) | —(apx 是变量级标签,不进类型) | ast:122/296、checker:22 | | 类型名解析 | "float"→"dex"(parser/monomorph/ir_gen/module/LSP 清单同源改) | — | parser:84、monomorph:101、ir_gen:291、module:366/380、lsp:908 | | 检查器 | 算术规则 int\|dex、消息更新、推断 TI_DEX;apx 标签透传零规则 | — | checker 12 处 | @@ -229,6 +231,8 @@ grep -rn "EXPR_FLOAT" src/ # ast:206、parser:411、check grep -rn "IR_I2F\|IR_F2I" src/ # ast:576-577、ir_gen:691/696、instr:418/426 ✓ grep -rn "T_FLOAT_F32\|T_FLOAT_F64" src/ # ast:93-94、parser:405/409-410、lsp:864(注)/895 ✓ grep -rn "W_F32\|W_F64" src/ # ast:117-118、parser:409-410 ✓ +# 2026-09-10 R1 复跑(同上两条 grep):src/ 仅剩 ast.cr 墓碑注释两行(77-86 / 1..10 编号说明); +# parser 与 lsp 面零命中(lsp 原声称的区间实测本就不存在——见主表 analysis.cr:895 行修正)。 grep -rn "_f32\|_f64" src/ tests/ grammar/ # 3 处:ast.cr:84(注释)、lexer.cr:121(str_to_f64_bits)、lexer.cr:331(str_to_f64_bits 调用)——后两者已入主表(lexer 行),原"仅 ast.cr:84"断言修正 ✓ grep -rn "FLOAT_LIT" grammar/ # tokens.ebnf:24、core.ebnf:57/59——全大写,原大小写敏感 "float" grep 不命中,本轮补入表(保留×3)✓ grep -rn "float" tests/suite/ examples/ vscode-core/ spec/ src/runtime/ # 0 命中 ✓ @@ -241,13 +245,13 @@ grep -rn "float\|FLOAT_LIT" bootstrap/corec/ | wc -l # 22——附录 8 文件 ## 分类争议点 -1. **parser.cr:405-411(字面量节点)标 `dex`**:该行同时含后缀宽度传递(W_F32/W_F64,保留)与 T_FLOAT 分支(dex 语义)。按"节点语义归属"标 dex,后缀部分在理由中注明不动。 +1. **parser.cr:405-411(字面量节点)标 `dex`**:该行同时含后缀宽度传递(W_F32/W_F64,保留)与 T_FLOAT 分支(dex 语义)。按"节点语义归属"标 dex,后缀部分在理由中注明不动。**2026-09-10 R1 结案**:后缀宽度传递(及 T_FLOAT_F32/F64 分支)已删除(ce3e541c),本争议点消解——该处现为单一 dex 字面量节点构建(`data` 槽恒 0)。 2. **ir_gen.cr:482-483(字面量→IR_CONST)标 `dex,apx`**:发射的 binary64 位模式是 apx 表示(保留),但 TI_FLOAT→TI_DEX 更名属 dex 侧。若执行时希望整体按语义层处理可改 `dex`——建议以"位模式保留 = 快路径"为准标 dex,apx。 3. **ast.cr:99 T_FLOAT_TYPE 标 `删除`**:从未被发射的死常量(float 关键字实际按 lexeme 匹配)。唯一风险是 LSP 连续区间 `T_INT_TYPE..T_AUTO_TYPE`(90..95)——删除不重编号则区间语义不变,故删除安全。若保守处理可标保留,但契约建议删除。 4. **计划文件 Task 5 的括号示例把 monomorph/module/dump/ccr_io/parser/lexer 列为 `dex,apx` 站点**:本表按 Global Constraints 逐点判定——这些文件的**类型名/常量更名**站点属用户可见类型语义(`dex`),仅其**binary64 机制**部分(lexer 位转换、ir_gen 运算生成)为 `dex,apx`。计划原文"按分类表逐点执行",以本表为准。 5. **module.cr:361/366 编码表注释与代码不符**(注释 float=2、代码 =3):既有 bug,非本任务修复项;Task 5 改注释时需核对实际 ABI 编码。 6. **bootstrap 范围外但构建关键**(见附录):若 Task 5 按表迁移 src/compiler 而 bootstrap 未先支持 dex,`build_selfhost_native.py` 自举失败——需在计划层面确认 bootstrap 的 dex 落地任务归属。 -7. **T_FLOAT_TYPE 删除 vs T_FLOAT_F32/F64 保留的判据对称性**:三者同为"从未被发射"的令牌常量——lexer **只发 `T_FLOAT`**(lexer.cr:330-331 唯一发射点:小数点或 `f32/f64` 后缀一律汇入 `add_tok_int(T_FLOAT, str_to_f64_bits(...))`);parser.cr:409-410 的 `T_FLOAT_F32→W_F32 / T_FLOAT_F64→W_F64` 宽度分支是**死代码**(两个后缀令牌从未到达 parser,`w` 恒为 0);`_f32/_f64` 后缀在 lexer 即被消费(num_str 已剔除后缀,见 lexer.cr:325-327),位宽信息在词法层丢失。分类差异的判据:`保留` 是约束原文("_f32/_f64 后缀 → 保留")的明确要求,且后缀令牌在设计中有迁移后指称(apx CPU 位宽标注角色);T_FLOAT_TYPE 无任何迁移后指称(float 关键字消亡、从未发射),故标 `删除`。**风险提示**:当前 `_f32/_f64` 后缀标注实际不生效(宽度丢失、w=0)——apx 位宽标注需在 lexer 增加发射路径(suffix 分支发射 T_FLOAT_F32/F64 或等价标注令牌),属 Task 5/6 实现项;本表分类仅按约束原文执行,不改变此实现缺口。 +7. **T_FLOAT_TYPE 删除 vs T_FLOAT_F32/F64 保留的判据对称性**(**2026-09-10 R1 结案:两者殊途同归——T_FLOAT_F32/F64 及 `W_*` 全族一并删除(ce3e541c),`_f32/_f64` 后缀退役改响亮报错(51d74be3);下述"保留 = 约束原文要求"的判据被 R1 用户裁决(宽度 = 机器形状,归映射层)覆盖,"宽度丢失需 lexer 新发射路径"的实现缺口随之消解——不再需要发射路径**):三者同为"从未被发射"的令牌常量——lexer **只发 `T_FLOAT`**(lexer.cr:330-331 唯一发射点:小数点或 `f32/f64` 后缀一律汇入 `add_tok_int(T_FLOAT, str_to_f64_bits(...))`);parser.cr:409-410 的 `T_FLOAT_F32→W_F32 / T_FLOAT_F64→W_F64` 宽度分支是**死代码**(两个后缀令牌从未到达 parser,`w` 恒为 0);`_f32/_f64` 后缀在 lexer 即被消费(num_str 已剔除后缀,见 lexer.cr:325-327),位宽信息在词法层丢失。分类差异的判据:`保留` 是约束原文("_f32/_f64 后缀 → 保留")的明确要求,且后缀令牌在设计中有迁移后指称(apx CPU 位宽标注角色);T_FLOAT_TYPE 无任何迁移后指称(float 关键字消亡、从未发射),故标 `删除`。**风险提示**:当前 `_f32/_f64` 后缀标注实际不生效(宽度丢失、w=0)——apx 位宽标注需在 lexer 增加发射路径(suffix 分支发射 T_FLOAT_F32/F64 或等价标注令牌),属 Task 5/6 实现项;本表分类仅按约束原文执行,不改变此实现缺口。 ## Task 6 执行补记(2026-08-16,float 移除收尾 + 测试迁移) diff --git a/docs/project-book.md b/docs/project-book.md index 26b216b7..1d0f3f99 100644 --- a/docs/project-book.md +++ b/docs/project-book.md @@ -160,7 +160,7 @@ graph TD 实现层 IR 是程序执行语义的完备编码。它包含: - 类型定义节点:结构体、枚举、接口、代数数据类型。每个节点完整记录实现关系、成员签名、可见性。具体内存布局由后端决定,但做出布局决策所需的所有语义信息均被保留。 -- 函数与方法的执行结构:region 嵌套(SG 段 = 图锚定存在结构)+ 线性指令(现 v5 形态;C 路线升级为格形态 v6——存在区间/共存/驱逐入格式,见 `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`)。指令为高级语义原语——静态调用、对象分配、字段读写、数组读写、分支、循环。每一条指令的源操作数和目标操作数均为完全解析的符号引用。 +- 函数与方法的执行结构:region 嵌套(SG 段 = 图锚定存在结构)+ 格形态 .ccr(v6:段表 + ENT 存在结构段,v6-only——存在区间/共存/驱逐承载,见 `specs/2026-09-05-lattice-ir-v6-format.md` + `2026-08-27-lattice-form-ir-design.md`)。指令为高级语义原语——静态调用、对象分配、字段读写、数组读写、分支、循环。每一条指令的源操作数和目标操作数均为完全解析的符号引用。 - 图拓扑元数据:标注每个子图的结构特征(DAG、静态循环、动态生长),为后端选择执行策略提供依据。 - 全局符号表:模块间引用全部解析为直接索引。无重复、无歧义。 diff --git a/docs/pseudocode/compiler/ast.md b/docs/pseudocode/compiler/ast.md index 5ba8479a..01bd2067 100644 --- a/docs/pseudocode/compiler/ast.md +++ b/docs/pseudocode/compiler/ast.md @@ -84,16 +84,16 @@ | 减等号 | T_MINUS_EQ | (全局常量节) | | 星等号 | T_STAR_EQ | (全局常量节) | | 斜杠等号 | T_SLASH_EQ | (全局常量节) | -| 后缀整数 i8 | T_INT_I8 | (全局常量节) | -| 后缀整数 i16 | T_INT_I16 | (全局常量节) | -| 后缀整数 i32 | T_INT_I32 | (全局常量节) | -| 后缀整数 i64 | T_INT_I64 | (全局常量节) | -| 后缀整数 u8 | T_INT_U8 | (全局常量节) | -| 后缀整数 u16 | T_INT_U16 | (全局常量节) | -| 后缀整数 u32 | T_INT_U32 | (全局常量节) | -| 后缀整数 u64 | T_INT_U64 | (全局常量节) | -| 后缀浮点数 f32 | T_FLOAT_F32 | (全局常量节) | -| 后缀浮点数 f64 | T_FLOAT_F64 | (全局常量节) | +| 后缀整数 i8 | T_INT_I8 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i16 | T_INT_I16 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i32 | T_INT_I32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i64 | T_INT_I64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u8 | T_INT_U8 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u16 | T_INT_U16 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u32 | T_INT_U32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u64 | T_INT_U64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀浮点数 f32 | T_FLOAT_F32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀浮点数 f64 | T_FLOAT_F64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | | 无值(None)关键字 | T_NONE | (全局常量节) | | 有值(Some)关键字 | T_SOME | (全局常量节) | | 旧声明关键字 | T_LET | (全局常量节) | @@ -106,16 +106,16 @@ | 引用关键字 | T_REF | (全局常量节) | | 动态关键字 | T_DYN | (全局常量节) | | 外部关键字 | T_EXTERN | (全局常量节) | -| 宽度 I8 | W_I8 | (全局常量节) | -| 宽度 I16 | W_I16 | (全局常量节) | -| 宽度 I32 | W_I32 | (全局常量节) | -| 宽度 I64 | W_I64 | (全局常量节) | -| 宽度 U8 | W_U8 | (全局常量节) | -| 宽度 U16 | W_U16 | (全局常量节) | -| 宽度 U32 | W_U32 | (全局常量节) | -| 宽度 U64 | W_U64 | (全局常量节) | -| 宽度 F32 | W_F32 | (全局常量节) | -| 宽度 F64 | W_F64 | (全局常量节) | +| 宽度 I8 | W_I8 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I16 | W_I16 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I32 | W_I32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I64 | W_I64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U8 | W_U8 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U16 | W_U16 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U32 | W_U32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U64 | W_U64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 F32 | W_F32 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 F64 | W_F64 | (全局常量节) ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | | 整数类型 | TY_INT | (全局常量节) | | 浮点数类型 | TY_FLOAT | (全局常量节) | | 布尔类型 | TY_BOOL | (全局常量节) | @@ -439,19 +439,21 @@ ### 后缀整数/浮点数词法单元(数值 77~86) > 词法分析器遇到 `_i32`、`_u64`、`_f32` 等后缀时产生这些类别。 +> +> **2026-09-10 R1 更新(语言面收窄 §2)**:宽度后缀已退役——lexer 对数字字面量的字母后缀一律**响亮报错**,下列 77~86 类别**不再被任何路径发射**,编号保留勿复用(`ast.cr` 墓碑注释在案,commit ce3e541c)。 | 常量 | 数值 | 含义 | |------|------|------| -| 后缀整数 i8(T_INT_I8) | 77 | i8 后缀整数 | -| 后缀整数 i16(T_INT_I16) | 78 | i16 后缀整数 | -| 后缀整数 i32(T_INT_I32) | 79 | i32 后缀整数 | -| 后缀整数 i64(T_INT_I64) | 80 | i64 后缀整数 | -| 后缀整数 u8(T_INT_U8) | 81 | u8 后缀整数 | -| 后缀整数 u16(T_INT_U16) | 82 | u16 后缀整数 | -| 后缀整数 u32(T_INT_U32) | 83 | u32 后缀整数 | -| 后缀整数 u64(T_INT_U64) | 84 | u64 后缀整数 | -| 后缀浮点数 f32(T_FLOAT_F32) | 85 | f32 后缀浮点数 | -| 后缀浮点数 f64(T_FLOAT_F64) | 86 | f64 后缀浮点数 | +| 后缀整数 i8(T_INT_I8) | 77 | i8 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i16(T_INT_I16) | 78 | i16 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i32(T_INT_I32) | 79 | i32 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 i64(T_INT_I64) | 80 | i64 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u8(T_INT_U8) | 81 | u8 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u16(T_INT_U16) | 82 | u16 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u32(T_INT_U32) | 83 | u32 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀整数 u64(T_INT_U64) | 84 | u64 后缀整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀浮点数 f32(T_FLOAT_F32) | 85 | f32 后缀浮点数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 后缀浮点数 f64(T_FLOAT_F64) | 86 | f64 后缀浮点数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | ### 扩展词法单元类别(数值 87~100) @@ -475,19 +477,21 @@ ## 宽度常量(Width constants) > 存入整数字面量/浮点数字面量节点的 数据(data) 字段,表示具体数值宽度。 +> +> **2026-09-10 R1 更新(语言面收窄 §2)**:`W_I8..W_F64`(1..10)随宽度后缀退役一并删除——数字字面量节点的 `data` 槽**恒 0**(`parser.cr` 宽度分支已清,commit ce3e541c)。宽度 = 机器形状,归映射层。 | 常量 | 数值 | 含义 | |------|------|------| -| 宽度 I8(W_I8) | 1 | 有符号 8 位整数 | -| 宽度 I16(W_I16) | 2 | 有符号 16 位整数 | -| 宽度 I32(W_I32) | 3 | 有符号 32 位整数 | -| 宽度 I64(W_I64) | 4 | 有符号 64 位整数 | -| 宽度 U8(W_U8) | 5 | 无符号 8 位整数 | -| 宽度 U16(W_U16) | 6 | 无符号 16 位整数 | -| 宽度 U32(W_U32) | 7 | 无符号 32 位整数 | -| 宽度 U64(W_U64) | 8 | 无符号 64 位整数 | -| 宽度 F32(W_F32) | 9 | 32 位浮点数 | -| 宽度 F64(W_F64) | 10 | 64 位浮点数 | +| 宽度 I8(W_I8) | 1 | 有符号 8 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I16(W_I16) | 2 | 有符号 16 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I32(W_I32) | 3 | 有符号 32 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 I64(W_I64) | 4 | 有符号 64 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U8(W_U8) | 5 | 无符号 8 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U16(W_U16) | 6 | 无符号 16 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U32(W_U32) | 7 | 无符号 32 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 U64(W_U64) | 8 | 无符号 64 位整数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 F32(W_F32) | 9 | 32 位浮点数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | +| 宽度 F64(W_F64) | 10 | 64 位浮点数 ——已删除(2026-09-10 R1 语言面收窄 §2;编号保留勿复用) | ## 类型常量(类型(Type) constants) diff --git a/docs/pseudocode/compiler/parser-2.md b/docs/pseudocode/compiler/parser-2.md index 5defda54..82a2a36c 100644 --- a/docs/pseudocode/compiler/parser-2.md +++ b/docs/pseudocode/compiler/parser-2.md @@ -164,8 +164,9 @@ ## 函数 解析基本表达式(parse_primary) ### 作用 解析所有可能作为后缀链起点的基本表达式。支持的种类: -- 整数字面量(整数字面量(T_INT) 及带位宽后缀 8位整数字面量(T_INT_I8)~64位无符号整数字面量(T_INT_U64)):构造 整数字面量表达式(EXPR_INT),宽度信息写入 数据(data) 字段 -- 浮点字面量(浮点字面量(T_FLOAT) / 32位浮点字面量(T_FLOAT_F32) / 64位浮点字面量(T_FLOAT_F64)):构造 浮点字面量表达式(EXPR_FLOAT),宽度写入 数据(data) +- 整数字面量(整数字面量(T_INT)):构造 整数字面量表达式(EXPR_INT),数据(data) 槽恒 0 +- 浮点字面量(浮点字面量(T_FLOAT)):构造 浮点字面量表达式(EXPR_FLOAT),数据(data) 槽恒 0 +> **2026-09-10 R1 语言面收窄 §2 更新**:带位宽后缀的整数(8位整数字面量(T_INT_I8)~64位无符号整数字面量(T_INT_U64))与浮点(32位浮点字面量(T_FLOAT_F32)/64位浮点字面量(T_FLOAT_F64))字面量类别**已删除**——lexer 对数字字面量的字母后缀一律响亮报错,这些类别无生产者(commit ce3e541c);节点 数据(data) 槽不再承载宽度标注(恒 0)。下方逻辑段中对应宽度分支同批删除。 - 字符串字面量(T_STRING):构造 字符串字面量表达式(EXPR_STRING) - 布尔字面量(真关键字(T_TRUE)→数据(data)=1, 假关键字(T_FALSE)→数据=0):构造 布尔字面量表达式(EXPR_BOOL) - 无值(None) 字面量(T_NONE):直接构造 枚举构造表达式(EXPR_ENUM_CONSTRUCTOR)("无值") @@ -182,28 +183,14 @@ ` 函数 解析基本表达式(parse_primary)() -> 整数 令 当前词法单元索引(t) = 当前词法单元(cur_tok()) - -- 整数字面量(含带位宽后缀的类型化整数) - 如果 词法单元类别(tok_k(当前词法单元索引)) 等于 整数字面量(T_INT) 或 (词法单元类别(tok_k(当前词法单元索引)) 大于等于 8位整数字面量(T_INT_I8) 且 词法单元类别(tok_k(当前词法单元索引)) 小于等于 64位无符号整数字面量(T_INT_U64)),那么: + -- 整数字面量(2026-09-10 R1 语言面收窄 §2:带位宽后缀分支已删除——lexer 不再发射这些类别) + 如果 词法单元类别(tok_k(当前词法单元索引)) 等于 整数字面量(T_INT),那么: 前进词法单元(advance_tok)() - 令 类别(kn) = 词法单元类别(tok_k(当前词法单元索引)) - 令 宽度(w) = 0(可变) - 如果 类别 等于 8位整数字面量(T_INT_I8),那么:宽度 = 8位有符号宽度(W_I8) - 否则如果 类别 等于 16位整数字面量(T_INT_I16),那么:宽度 = 16位有符号宽度(W_I16) - 否则如果 类别 等于 32位整数字面量(T_INT_I32),那么:宽度 = 32位有符号宽度(W_I32) - 否则如果 类别 等于 64位整数字面量(T_INT_I64),那么:宽度 = 64位有符号宽度(W_I64) - 否则如果 类别 等于 8位无符号整数字面量(T_INT_U8),那么:宽度 = 8位无符号宽度(W_U8) - 否则如果 类别 等于 16位无符号整数字面量(T_INT_U16),那么:宽度 = 16位无符号宽度(W_U16) - 否则如果 类别 等于 32位无符号整数字面量(T_INT_U32),那么:宽度 = 32位无符号宽度(W_U32) - 否则如果 类别 等于 64位无符号整数字面量(T_INT_U64),那么:宽度 = 64位无符号宽度(W_U64) - 返回 分配 AST 节点(alloc_node)(整数字面量表达式(EXPR_INT), 0, 0, 0, 词法单元整数值(tok_iv(当前词法单元索引)), 整数类型(TY_INT), 宽度, 词法单元行号(tok_ln(当前词法单元索引)), 词法单元列号(tok_cl(当前词法单元索引))) - -- 浮点字面量 - 如果 词法单元类别(tok_k(当前词法单元索引)) 等于 浮点字面量(T_FLOAT) 或 词法单元类别(tok_k(当前词法单元索引)) 等于 32位浮点字面量(T_FLOAT_F32) 或 词法单元类别(tok_k(当前词法单元索引)) 等于 64位浮点字面量(T_FLOAT_F64),那么: + 返回 分配 AST 节点(alloc_node)(整数字面量表达式(EXPR_INT), 0, 0, 0, 词法单元整数值(tok_iv(当前词法单元索引)), 整数类型(TY_INT), 0, 词法单元行号(tok_ln(当前词法单元索引)), 词法单元列号(tok_cl(当前词法单元索引))) + -- 浮点字面量(2026-09-10 R1:T_FLOAT_F32/F64 分支与 W_F32/W_F64 宽度赋值已删除) + 如果 词法单元类别(tok_k(当前词法单元索引)) 等于 浮点字面量(T_FLOAT),那么: 前进词法单元(advance_tok)() - 令 类别(kn) = 词法单元类别(tok_k(当前词法单元索引)) - 令 宽度(w) = 0(可变) - 如果 类别 等于 32位浮点字面量(T_FLOAT_F32),那么:宽度 = 32位浮点宽度(W_F32) - 否则如果 类别 等于 64位浮点字面量(T_FLOAT_F64),那么:宽度 = 64位浮点宽度(W_F64) - 返回 分配 AST 节点(alloc_node)(浮点字面量表达式(EXPR_FLOAT), 0, 0, 0, 词法单元整数值(tok_iv(当前词法单元索引)), 浮点类型(TY_FLOAT), 宽度, 词法单元行号(tok_ln(当前词法单元索引)), 词法单元列号(tok_cl(当前词法单元索引))) + 返回 分配 AST 节点(alloc_node)(浮点字面量表达式(EXPR_FLOAT), 0, 0, 0, 词法单元整数值(tok_iv(当前词法单元索引)), 浮点类型(TY_FLOAT), 0, 词法单元行号(tok_ln(当前词法单元索引)), 词法单元列号(tok_cl(当前词法单元索引))) -- 字符串字面量 如果 词法单元类别(tok_k(当前词法单元索引)) 等于 字符串字面量(T_STRING),那么: 前进词法单元(advance_tok)() @@ -725,20 +712,10 @@ 如果 词法单元类别(tok_k(起始词法单元索引)) 等于 下划线(T_UNDERSCORE),那么: 前进词法单元(advance_tok)() 返回 分配 AST 节点(alloc_node)(通配模式(EXPR_WILDCARD), 0, 0, 0, 0, 0, 0, 词法单元行号(tok_ln(起始词法单元索引)), 词法单元列号(tok_cl(起始词法单元索引))) - -- 整数字面量模式 - 如果 词法单元类别(tok_k(起始词法单元索引)) 等于 整数字面量(T_INT) 或 (词法单元类别(tok_k(起始词法单元索引)) 大于等于 8位整数字面量(T_INT_I8) 且 词法单元类别(tok_k(起始词法单元索引)) 小于等于 64位无符号整数字面量(T_INT_U64)),那么: + -- 整数字面量模式(2026-09-10 R1 语言面收窄 §2:带位宽后缀区间分支已删除——lexer 不再发射这些类别) + 如果 词法单元类别(tok_k(起始词法单元索引)) 等于 整数字面量(T_INT),那么: 前进词法单元(advance_tok)() - 令 类别(kn) = 词法单元类别(tok_k(起始词法单元索引)) - 令 宽度(w) = 0(可变) - 如果 类别 等于 8位整数字面量(T_INT_I8),那么:宽度 = 8位有符号宽度(W_I8) - 否则如果 类别 等于 16位整数字面量(T_INT_I16),那么:宽度 = 16位有符号宽度(W_I16) - 否则如果 类别 等于 32位整数字面量(T_INT_I32),那么:宽度 = 32位有符号宽度(W_I32) - 否则如果 类别 等于 64位整数字面量(T_INT_I64),那么:宽度 = 64位有符号宽度(W_I64) - 否则如果 类别 等于 8位无符号整数字面量(T_INT_U8),那么:宽度 = 8位无符号宽度(W_U8) - 否则如果 类别 等于 16位无符号整数字面量(T_INT_U16),那么:宽度 = 16位无符号宽度(W_U16) - 否则如果 类别 等于 32位无符号整数字面量(T_INT_U32),那么:宽度 = 32位无符号宽度(W_U32) - 否则如果 类别 等于 64位无符号整数字面量(T_INT_U64),那么:宽度 = 64位无符号宽度(W_U64) - 返回 分配 AST 节点(alloc_node)(整数字面量表达式(EXPR_INT), 0, 0, 0, 词法单元整数值(tok_iv(起始词法单元索引)), 整数类型(TY_INT), 宽度, 词法单元行号(tok_ln(起始词法单元索引)), 词法单元列号(tok_cl(起始词法单元索引))) + 返回 分配 AST 节点(alloc_node)(整数字面量表达式(EXPR_INT), 0, 0, 0, 词法单元整数值(tok_iv(起始词法单元索引)), 整数类型(TY_INT), 0, 词法单元行号(tok_ln(起始词法单元索引)), 词法单元列号(tok_cl(起始词法单元索引))) -- 字符串字面量模式 如果 词法单元类别(tok_k(起始词法单元索引)) 等于 字符串字面量(T_STRING),那么: 前进词法单元(advance_tok)() diff --git a/docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md b/docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md new file mode 100644 index 00000000..58c9d082 --- /dev/null +++ b/docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md @@ -0,0 +1,358 @@ +# 硬件接口表(HIT)M1:最小核运行闭环实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 用最小核表(`sub`/`nand`/`load`/`store` 4 事件)驱动 corearch 发射,跑通第一个 Core 冒烟程序(直线加减/位运算/内存读写),验证「表 = 运行的唯一平台依赖」架构。 + +**Architecture:** 三层——(1) HIT 表文件(toml 文本,4 事件 × x86 投影条目);(2) corearch 表驱动编码器(读表 → 按模板解释发射,与现有 `emit_instr` 硬编码路径并行、逐字节对照验证);(3) 合成层(IR 直线子集 → 4 核事件)。验证策略:表驱动输出与手写 e2_* 输出**逐字节一致**为表驱动正确性判据,冒烟程序运行正确为闭环判据。 + +**Tech Stack:** Core 自举编译器(`src/compiler/`)、Python bootstrap、x86-64 ELF 后端(`src/arch/linux/ld/`)。表文件解析用现有 `src/stdlib/toml.cr`。 + +## Global Constraints + +- 版本控制用 `jj`(铁律 #2,git 被 hook 拦截)。提交 `jj commit -m ''`,不手动动 bookmark(协调者管) +- 长时间构建/测试用 `nice -n 19`(铁律 #6);trap 测试进程需 `ulimit -c 0` 前缀(本机 core_pattern 坑,TODO:125) +- 设计依据:`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(HIT 定稿)——最小核事件:`sub=1 nand=2 load=3 store=4`;事件签名无宽度/寄存器/寻址;换表 = 换平台 +- 改动后重建 corec/corearch 再测:`nice -n 19 python3 build_selfhost_native.py`(约 2 分钟;corearch 改动只重建 corearch 的也可全量) +- 现有 `emit_instr`(instr.cr:433)硬编码路径**保持可用**——表驱动路径并行存在,全套测试不得破 +- IR op 全集见 ast.cr:538-588(约 45 个,含空洞);`IR_BINARY`(2) 的 s3 = 子操作(`OP_ADD=1 OP_SUB=2 OP_AND=12` 等,见 ast.cr OP_* 常量);变量 = 内存槽模型(IR_STORE/IR_LOAD = 槽写读) +- M1 范围边界(超出即拒绝编译并报「需要扩展事件」):**直线程序**——无 IR_BRANCH/JUMP/LABEL/PHI/CALL/RETURN 值/SPAWN;运算仅加减位与(IR_BINARY OP_ADD/OP_SUB/OP_AND/OP_OR/OP_XOR/OP_NOT);常量仅小整数(合成见 Task 3) +- 合成/控制流/函数调用/复杂运算的完整覆盖 = M2(本计划不覆盖) + +--- + +## 现状事实(执行前必读) + +- `corearch_main()`(`src/compiler/corearch.cr:17`):cli 解析 → `load_ccr(buf, fsize)`(`ccr_io.cr`)→ `init_backend_arrays()` → `elf_gen()`(`src/arch/linux/ld/elf.cr`) +- `emit_instr(instr_idx, buf, pos)`(`src/arch/linux/ld/instr.cr:433`):按 `iri_op()` 分发(约 50 个 `if op == IR_*` 块)——**表驱动路径从这里插入**(新函数 `emit_instr_tabled`,`emit_instr` 加开关) +- 栈槽/寄存器映射:`g2_slot(var)`(槽解析)、`e2_load_var/e2_st`(变量 → 寄存器/栈)——表驱动编码器复用这些(槽 = 操作数解析的现有机制) +- `IR_CONST`(1):s1 = i64 值。合成层把它变成 `load` 事件 + 编译期常量区布局 +- corec `build` 命令(`main.cr:571-592`):产出 .ccr 后 `system("corearch --elf")`——corearch 加 `--table` 参数后由 corec 透传或手动测试 + +## 表文件格式(本计划定稿,toml) + +> 数值真源警示(2026-09-06 M2 收尾修正):下方示例仅为计划期快照——**字节值与 +> 角色以 `src/arch/hit/core-x86.toml` 为唯一真源**(表驱动编码器直读该文件)。 +> 快照与真源已同步:M1 寄存器惯例 r10(累加/结果,rm 字段)+ r11(reg 字段)为 +> 8 号以上寄存器对 → 投影字节需 **REX.W+R+B**(sub = `4D 29`,非早期草案的 +> `48 29`);sub 事件 reg 字段角色 = `src2`(非 src1)。 + +`src/arch/hit/core-x86.toml`(M1 第一份投影表): + +```toml +[table] +name = "core-x86-min" +events = 4 # 最小核契约:实现 1-4 即可运行 + +[[event]] +id = 1 # sub +name = "sub" +arith = "sub" # 语义锚:规约层引用(M1 不消费) +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] # x86 投影:sub r/m64, r64 = r/m ← r/m − r(REX.W+R+B + 29 /r) +isa = "x86-64" +opcode = [0x4D, 0x29] # REX.W+R+B + sub r/m64,r64(r10/r11 对需 R/B 扩展位) +modrm_reg_role = "src2" # reg 字段 = 第二输入(被减数,驻 r11) +modrm_rm_role = "dst" # rm 字段 = 目标/结果寄存器(r10,运算前驻第一输入) +rm_mode = 0 # 0 = rm 为寄存器(加载到 r10);1 = rm 为 rbp+disp32(槽/常量池寻址) +# 操作数解析规则:dst/src 均为「值槽」——由调用方传 var 索引,编码器用 g2_slot/e2_load_var 解析 +# 模板解释器规则见 Task 2:M1 支持 [opcode...] + modrm 两角色 + rm_mode 两种形态 + +[[event]] +id = 2 +name = "nand" +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +# M1 占位:x86 无 nand 指令——单步 and(opcode 4D 21 /r: and r/m64,r64, +# 同 sub 的 r10/r11 双寄存器形态)。投影到语义 nand 的完整序列(and+not 两步) +# + 事件流语义闭合 = M2 +opcode = [0x4D, 0x21] +modrm_reg_role = "src2" +modrm_rm_role = "dst" +rm_mode = 0 + +[[event]] +id = 3 +name = "load" +inputs = 1 # 地址 +outputs = 1 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +# rm_mode=1:rm = rbp+disp32(槽/常量池寻址)。reg 字段寄存器 = dst(r10 → REX.W+R = 0x4C) +opcode = [0x4C, 0x8B] # REX.W+R + mov r64, r/m64(读:dst ← [rbp+disp32]) +modrm_reg_role = "dst" +modrm_rm_role = "addr" +rm_mode = 1 + +[[event]] +id = 4 +name = "store" +inputs = 2 # 地址, 值 +outputs = 0 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +# rm_mode=1:rm = rbp+disp32(槽寻址)。reg 字段寄存器 = val(r10 → REX.W+R = 0x4C) +opcode = [0x4C, 0x89] # REX.W+R + mov r/m64, r64(写:[rbp+disp32] ← val) +modrm_reg_role = "val" +modrm_rm_role = "addr" +rm_mode = 1 +``` + +M1 模板解释器形态 = 每事件单投影步 `{opcode 字节(≤2), reg 角色, rm 角色, rm_mode}`,无 imm/REX 变体(超出报错 = 该事件形态未实现)。多步序列投影(如 nand = and+not)留 M2(nand 事件 M1 以单步 and 占位)。 + +--- + +### Task 1: HIT 表文件 + toml 解析器接线 + +**Files:** +- Create: `src/arch/hit/core-x86.toml`(上节格式全文件,含 4 事件;nand = 单步 and 占位 + M2 注释) +- Modify: `src/compiler/globals.cr`(表加载全局) +- Create: `src/arch/hit/hit.cr`(表结构 + `load_hit_table(path)` + `hit_event_lookup(event_id)`) +- Test: `tests/selfhost/test_hit_table.py` + +**Interfaces:** +- Produces: `fn load_hit_table(path: string) -> int`(0=成功;填充 `g_hit_events`(string buffer,每事件 32B:id/name_ni/inputs/outputs/side_effect/proj_isa/step_count/step_off)、`g_hit_event_count`);`fn hit_event_lookup(id: int) -> int`(返回事件槽 -1=无);`fn hit_proj_step(event_slot: int, step_i: int, out_opcode: ptr, out_reg_role: ptr, out_rm_role: ptr, out_rm_mode: ptr) -> int`(取投影步编码与 rm 形态) +- `hit.cr` 需要 `import toml`(现有 `src/stdlib/toml.cr` 解析器) + +- [ ] **Step 1: 写失败测试** + +Create `tests/selfhost/test_hit_table.py`: + +```python +#!/usr/bin/env python3 +"""HIT 表解析测试——core-x86.toml 最小核 4 事件加载。""" + +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def main() -> int: + if not COREC.exists(): + print("[FAIL] missing build/corec") + return 1 + # 通过 corec 的隐藏调试命令或独立验证:先以文件存在性 + 构建冒烟占位 + # Task 1 完成判据 = 构建通过 + corec check src/arch/hit/hit.cr 无错 + print("0/1 passed (Task 1 判据为编译 + Task 2 接线后行为测试)") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) +``` + +- [ ] **Step 2: 跑测试确认失败** + +Run: `nice -n 19 python3 tests/selfhost/test_hit_table.py` +Expected: `0/1 passed`,exit 1 + +- [ ] **Step 3: 写表文件** + +按「表文件格式」节创建 `src/arch/hit/core-x86.toml`(4 事件,nand = 单步 and 占位 + M2 注释)。 + +- [ ] **Step 4: 写 hit.cr + globals + 接线构建** + +`src/arch/hit/hit.cr`:结构(仿 `cir_cache.cr` 的 buffer 表风格): +- `g_hit_events : string, mut; g_hit_event_count/cap`——每事件 32B:`[id i32, name_ni i32, inputs i32, outputs i32, side_effect i32, proj_isa i32, step_count i32, step_off i32]` +- `g_hit_steps : string, mut; g_hit_step_count/cap`——每步 20B:`[op0 i32, op1 i32(=0 无), reg_role i32, rm_role i32, rm_mode i32]`(opcode 至多 2 字节前缀,超出报错;rm_mode 0=寄存器 1=rbp+disp32) +- `load_hit_table(path)`:read_file → toml 解析(查 `src/stdlib/toml.cr` API 确认遍历语法,跟随现有使用点如 project.cr)→ 填两表 +- 注册:`build_selfhost_native.py` 的 corearch concat 列表加 `src/arch/hit/hit.cr`(corec 不需要);`src/compiler/globals.cr` 或 hit.cr 顶部声明全局 +- 错误:文件缺/事件缺 id → println + return 1 + +- [ ] **Step 5: 构建 + 自检** + +Run: `nice -n 19 python3 build_selfhost_native.py && ./build/corec check src/arch/hit/hit.cr` +Expected: BUILD SUCCESS + check ok(hit.cr 编译干净) + +- [ ] **Step 6: 提交** + +```bash +jj commit -m 'feat: HIT 表文件 + toml 解析——core-x86.toml 最小核 4 事件(M1 Task 1)' +``` + +--- + +### Task 2: 表驱动编码器(模板解释器)+ 逐字节对照 + +**Files:** +- Modify: `src/arch/linux/ld/instr.cr`(新增 `emit_instr_tabled`) +- Modify: `src/arch/linux/ld/elf.cr`(`elf_gen` 加表路径入口)或 `corearch.cr` +- Modify: `src/compiler/corearch.cr`(`--table ` 参数) +- Test: `tests/selfhost/test_hit_table.py`(替换为行为测试) + +**Interfaces:** +- Consumes: Task 1 的 `load_hit_table` / `hit_event_lookup` / `hit_proj_step` +- Produces: `fn emit_instr_tabled(instr_idx, buf, pos) -> int`(与 `emit_instr` 同签名;返回字节数;**对表驱动的每事件,输出 = 该事件投影编码**);`fn hit_map_ir_op(ir_op) -> event_id`(IR op → 事件映射,Task 3 前先直通 sub 形态) + +- [ ] **Step 1: 写失败测试(替换占位)** + +`test_hit_table.py` main 替换:构造一个极小 .ccr(Task 2 先用手工汇编指令对照——用现有编译器编一个 `fn main()->int{x:=5;y:=3;return x-y;}` 的 .ccr),corearch 用 `--table src/arch/hit/core-x86.toml` 输出二进制与无表输出**逐字节比较**——但 M1 表只有 sub/nand/load/store 4 事件,IR 的 add/const/alloc 未映射 → **Task 2 的对照面 = 表驱动路径仅处理 IR_BINARY(OP_SUB) + IR_LOAD/IR_STORE 形态**,其余 op 走旧路径(混合模式)。对照测试:程序只用 sub/load/store(IR 层面),两路径 ELF 输出逐字节一致。 + +```python +def build_both(src: str) -> tuple: + """corearch 旧路径 vs --table 路径,返回两二进制字节。""" + # 1) corec build 出 .ccr + # 2) corearch --elf -o out_old + # 3) corearch --elf --table src/arch/hit/core-x86.toml -o out_new + # 4) 读两文件比较 +``` + +(M1 混合模式:表覆盖到的 op 走表、未覆盖走旧路径——逐字节一致判据只对「表覆盖形态」严格,混合模式以冒烟正确性为准——Task 4 细化。) + +- [ ] **Step 2: 跑测试确认失败** + +Expected: FAIL(--table 未实现) + +- [ ] **Step 3: 实现编码器** + +`emit_instr_tabled`:查 `hit_map_ir_op(iri_op)` → 无映射回 -1(调用方走旧路径);有映射 → `hit_event_lookup` → 逐投影步发射:每步按 `{opcode, reg_role, rm_role}` 组装——操作数角色从 IR 指令取(dst/src1/src2 依角色名),变量 → 寄存器/栈用现有 `g2_slot` + `e2_load_var` 机制(读 instr.cr 现有 e2_* 的槽解析复用)。sub 模板 `48 29 /r`:modrm = reg=src1 寄存器(沿用 e2 家族 r10=左操作数/r11=右操作数 的寄存器加载惯例)。M1 形态集:操作数经栈槽加载到固定寄存器对(r10/r11)再 opcode+modrm(rm=r10 型)+ load/store 的 rbp+disp 寻址形态——共两种 modrm 形态,模板字段 `rm_mode`(0=reg / 1=rbp+disp32)在 Task 1 表格式中补齐。nand 事件 M1 = 单步 and 占位(表文件已定);nand 语义经合成层在 Task 3 用「and 步 + 取反组合」表达前,表模式不发射真 nand 程序(偏差记录见计划尾)。 + +- [ ] **Step 4: 接线 --table 参数** + +corearch:`cli_flag("table", "", "HIT table path")`;有值时 `load_hit_table(path)` 失败即退出;`elf_gen` 内 emit 循环改调 `emit_instr_tabled`(返回 -1 落旧路径)。 + +- [ ] **Step 5: 重建 + 对照测试** + +Run: `nice -n 19 python3 build_selfhost_native.py && nice -n 19 python3 tests/selfhost/test_hit_table.py` +Expected: 对照用例 PASS(两路径二进制一致,若 M1 混合模式放宽则冒烟正确性为判据) + +- [ ] **Step 6: 提交** + +```bash +jj commit -m 'feat: 表驱动发射框架——corearch --table + emit_instr_tabled 模板解释(M1 Task 2)' +``` + +--- + +### Task 3: 合成层(IR 直线子集 → 4 核事件) + +**Files:** +- Create: `src/arch/hit/lower_to_core.cr`(事件降低规则;corearch 侧) +- Modify: `src/arch/linux/ld/elf.cr` 或 corearch(表模式下发射前先过降低) +- Test: `tests/selfhost/test_hit_table.py`(追加降低规则单测——通过 cir dump 或专门验证) + +**Interfaces:** +- Produces: `fn lower_ir_to_core(instr_idx) -> int`(0=已降低/直通;1=需合成已改写指令流;-1=不可合成报错);合成规则表: + - `OP_ADD` → sub 反减:`add(a,b) := sub(a, sub(0,b))`——0 从常量合成(见下) + - `OP_SUB` → 直通 `sub` + - `OP_AND` → `nand(nand(a,b), nand(a,b))` + - `OP_OR` → 德摩根:`nand(nand(a,a), nand(b,b))` + - `OP_XOR`/`OP_NOT`/移位 → M1 报错「需扩展事件」(不覆盖) + - `IR_CONST n` → 合成「常量 = 只读区 load」:编译期布局 `g_const_pool`(每常量 8B 槽),发射前把常量池写入 ELF rodata,`IR_CONST` 替换为 `load(pool_addr(n))`——pool_addr 由布局 pass 填 + - `IR_LOAD/IR_STORE`(槽读写)→ 直通 `load/store` 事件 + - 变量槽地址 → load/store 的「地址操作数」= 槽地址(栈基址+偏移,由投影解析——x86 投影的 addr 角色 = rbp+disp 形态……**形态扩展**:Task 2 的单形态(rm=寄存器)不够——槽读写要 rbp+disp 寻址。修正:Task 2 实现时 load/store 模板需支持 disp 寻址形态(modrm rm=101 + disp32 相对 rbp)——Task 2 的形态范围含「rm = rbp+disp32」一种) +- 合成实现在 IR 层(corearch load_ccr 后、发射前的指令数组改写)——新指令用现有 IR 结构(g_ir_instrs)表达(合成的 sub/nand/load 仍是 IR 指令?不——是事件。**合成输出 = 事件级指令**:表模式下的发射中间层 = 事件流。corearch 加事件流数组 `g_hit_ev_stream`(每条:event_id + 操作数 var 引用 + 常量地址)——降低 pass 把 IR 指令翻译/合成为事件流,编码器消费事件流) +- 这是本计划最关键结构决策:**表模式下 corearch 内部 = IR →(降低)→ 事件流 →(表投影)→ 字节**;旧模式 = IR → 字节 + +- [ ] **Step 1: 写失败测试** + +测试载体:`x := 7; y := 5; z := x - y;`(纯 sub 直通 + const/槽)+ `w := x & y`(and → nand 合成)。断言:表模式编译运行结果正确(exit = 期望值)+ 事件流可 dump(加 `--dump-events` 调试输出)。 + +- [ ] **Step 2: 实现事件流 + 降低** + +事件流结构 + `lower_ir_to_core`(规则按上表);常量池布局 + rodata 写入(读 elf.cr rodata 段机制)。 + +- [ ] **Step 3: 编码器消费事件流** + +`emit_instr_tabled` 改为消费事件流(event_id + 操作数槽);sub 直通正确、load/store 槽寻址正确。 + +- [ ] **Step 4: 冒烟断言** + +Run: `nice -n 19 python3 tests/selfhost/test_hit_table.py` +Expected: 加减程序 exit 正确;`&` 程序(经 nand 合成)exit 正确 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m 'feat: 合成层——IR 直线子集降到 4 核事件流(add→sub 反减/and→nand/常量池 load;M1 Task 3)' +``` + +--- + +### Task 4: 冒烟闭环(端到端正确性) + +**Files:** +- Create: `tests/hit/smoke_add.cr`、`tests/hit/smoke_mem.cr`(冒烟源) +- Modify: `tests/selfhost/test_hit_table.py`(端到端用例) +- Modify: `src/compiler/main.cr`(corec build 透传 `--table` 或测试直接调 corearch——测试直接调 corearch 即可,corec 透传 = M2) + +**Interfaces:** +- Consumes: Task 2/3 全部 + +- [ ] **Step 1: 写冒烟源** + +`smoke_add.cr`:`fn main()->int{a:=7;b:=5;return a-b;}`(期望 exit 2) +`smoke_mem.cr`:含内存读写 + 位与:`fn main()->int{a:.,mut=6;b:=3;c:=a&b;a=c+1;return a;}`(期望 exit 1:6&3=2,+1=3?——`2+1=3`;数值经合成链正确即断言 exit 3——写死期望值并注释手算) + +- [ ] **Step 2: 端到端测试函数** + +corec build → corearch `--table` → 运行断言 exit code + +- [ ] **Step 3: 跑通 + 修正** + +Run: `nice -n 19 python3 tests/selfhost/test_hit_table.py` +Expected: 冒烟 exit 断言全过;修正合成/模板 bug 直至绿 + +- [ ] **Step 4: 回归不破** + +Run: `nice -n 19 python3 tests/selfhost/test_compile.py && nice -n 19 python3 tests/bootstrap/test_pipeline.py && ./build/corec check src/compiler` +Expected: 全绿(旧路径不受影响) + +- [ ] **Step 5: 提交** + +```bash +jj commit -m 'feat: M1 冒烟闭环——最小核表端到端运行正确 + 回归不破(M1 Task 4)' +``` + +--- + +### Task 5: 文档 + 收尾 + +**Files:** +- Modify: `docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(实施状态:M1 完成注记、表文件路径、事件流结构定稿回填) +- Modify: `docs/superpowers/plans/2026-09-05-lattice-ir-v6.md`(若引用 crasm/HIT 处状态同步) +- Modify: `TODO.md`(M1 完成记录 + M2 挂账:控制流/调用合成、全 op 降低、nand 真语义、corec 透传 --table、事件流入 v6 段) + +- [ ] **Step 1: 更新文档** + +- [ ] **Step 2: 全量回归** + +Run: bootstrap 三套 + `test_compile` + `test_slice_bounds` + `corec check src/compiler` +Expected: 全绿 + +- [ ] **Step 3: 提交** + +```bash +jj commit -m 'docs: HIT M1 完成记录 + M2 挂账(最小核表端到端闭环落地)' +``` + +--- + +## 计划内已知偏差(评审注意,2026-09-06 更新) + +0. **and→nand / or→德摩根 合成规则未实现(Task 3 评审确认)**:源语言无位与/位或运算符(`&` 是取址、`&&`/`||` 恒短路分支化超 M1 直线子集)——IR 层规则无源码载体,推迟 M2;IR 若出现 OP_AND 会大声拒绝('needs more events')而非静默错码 +1. **M1 子集边界 = main 入口函数**:.ccr 自仓库根编译无条件带 stdlib fmt 闭包辅助函数(含分支/除法)——辅助函数走旧路径(混合模式 plan 偏差 #2 落地);纯化 = M2 全量切换 +2. **`--table` × `--link/--shared` 组合未测试**:池 mov rip disp 在链接路径 ctx 重布局下的指向无覆盖——M2 记录或显式拒绝 +3. **负值/宽常量池无载体**:M1 冒烟无负常量形态(hit_pool_w64 借位链论证正确无实证) +4. **M2 挂账(评审 Minor)**:hit_w32 负值哨兵写入实现分裂(store dst/load s2 未用哨兵 −1 读回 255 vs 0xFFFFFFFF)、预检对称性断言、add 反减 dest-fresh 前提固化、'events emitted' 计数改名、REX 注释 W+R+B + +1. **nand 的 x86 投影**:x86 无 nand 指令——M1 以 and 单步占位,nand 语义由合成层(and→nand 反推)在 Task 3 覆盖前**不发射 nand 事件程序**;完整 nand 序列投影(and+not 两步)与事件流语义闭合 = M2 事项 +2. **混合模式**:M1 表驱动与旧路径并行(未映射 op 落旧路径)——「表 = 唯一平台依赖」的纯化(全 op 走表)是 M2 的 `--table` 全量切换 +3. **常量注入**:设计上常量 = 只读区 load(最小核无 const 事件)——M1 常量池布局先行,图常量区(v6 语义)后续 +4. **地址形态**:load/store 的槽/池寻址 = 投影层 disp 形态(M1 支持 rbp+disp 一种) diff --git a/docs/superpowers/plans/2026-09-05-lattice-ir-v6.md b/docs/superpowers/plans/2026-09-05-lattice-ir-v6.md new file mode 100644 index 00000000..139f218d --- /dev/null +++ b/docs/superpowers/plans/2026-09-05-lattice-ir-v6.md @@ -0,0 +1,361 @@ +# 格形态 IR v6(存在结构)实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `.ccr` 从 v5(格层线性投影)升级为 v6(存在结构段:条目版本/存在区间/共存/home/无配方标记),供寄存器分配判定直接消费。 + +**执行顺序(2026-09-05 定):格数据面先行**——Task 1-3(存在区间/版本化/共存推导)为 Phase 1 立即执行(纯编译器内推导,不依赖格式 IO);v6 格式 IO(Task 4)与 HIT(`2026-09-05-hit-minimal-core-m1.md`)在数据面落地后推进;HIT 事件流与 v6 NOD 同构对齐(交汇点 = 事件层)。坐标说明:现管线 DF 节点序 = IR 指令序(`lower_to_ccr` node i → instruction i)——本计划「指令序」即 v6 格式定稿的「NOD 坐标」,落地后同一坐标零迁移。 + +**Architecture:** v6 = v5 全部基础段 + 新增存在结构段,version 字段 5→6,magic(`CCR1`)/扩展名/CLI 不动(方案 A 定案)。存在结构的数据(版本化条目 × 存在区间)由新推导 pass 从线性 IR 重建(`alloc_registers` 的 live-interval 雏形升级为独立 pass),共存按设计文档「推导规则优先,避免冗余存储」。ELF 后端只消费线性段(v6 保留),故后端改动面小;存在段是分配的判定输入(`docs/regalloc-cache-mapping.md` §4 一致性四条)。 + +**Tech Stack:** Core 自举编译器(`src/compiler/`)、Python bootstrap(`bootstrap/corec/`)、x86-64 ELF 后端(`src/arch/linux/ld/`)。无外部依赖。 + +## Global Constraints + +- 版本控制用 `jj`(铁律 #2,git 被 hook 拦截)。提交:`jj commit -m ''`;推送:`jj git push ...` +- 长时间编译/测试用 `nice -n 19`(铁律 #6;无 cpulimit 时) +- `.ccr` 扩展名 / magic `CCR1` / CLI / 文件路径**不动**(2026-08-27 方案 A 定案) +- v6 **不承诺 v5 后向兼容**;v6 落地即 v6-only(.ccr 为管线中间产物,corec→corearch 现生成、零持久生态,无需 v5→v6 转换工具——2026-09-05 计划定稿取消) +- 宽度标签(`T_INT_I8..U64` / `W_*`)不进入 v6(width-out-of-language 定案;v5 格式本身无宽度字段——parser 的宽度标注在 AST 节点,不落 .ccr) +- 共存不落盘冗余存储——推导规则(存在区间相交)优先(设计文档 §4.2 第 2 条) +- 内存数组操作沿用仓库模式:动态 byte buffer + `grow_*`/`w64`/`r64`/`i32` 字段访问器,无 `MAX_*` 硬限 +- 所有 i32 落盘字段过 `ccr_i32_fits`/`ccr_validate_i32_fields` 校验(v5 先例,`test_ccr_writer_rejects_i32_overflow_inputs` 模式) +- 测试:修改后跑 `tests/bootstrap/` 三套 + `tests/selfhost/`(`test_compile.py` 为首)+ `./build/corec check src/compiler` 自检;trap 类测试进程内置 `RLIMIT_CORE=0`(core_pattern 坑,见 TODO:125) +- 参考文档:`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(方向定稿)、`docs/regalloc-cache-mapping.md`(判定四条 §四)、`docs/memory-model-capability-lattice.md` v4(条款 4b)、`docs/ir-schema/coreir-schema.md`(v6 落地后同步) + +--- + +## 现状事实(执行前必读) + +- **v5 段序列**(`src/compiler/ccr_io.cr:6-20` 注释):`magic "CCR1"` → `version=5` → `strings` → `func_meta`(7×u32/函数)→ `instrs`(op i32, dest i32, s1 i64, s2 i32, s3 i32, tk u32 = **28B/条**)→ `vars` → `str_consts` → `structs` → `enums` → `globals`(16B/条)→ `opt_meta` → `sgs`(**ESZ_SG_DISK=24B**/条:kind/enter/exit/parent/nstart/ncount) +- **线性投影机制**(`dataflow.cr:351 lower_to_ccr`):图节点创建序 = 指令序直投(node i → instruction i,df 边界 = ir 边界)——v6 保留此机制 +- **活区间雏形**(`opt.cr:198 alloc_registers`):每函数每变量 `[first_ref, last_ref]`(指令序 ii),5 个 callee-saved 寄存器(rbx/r12-r15)First-Fit;`g_opt_level < 1` 时跳过。**升级点**:变量可能多次 store(非 SSA)——单区间近似;版本化 = 每次 store 切分条目 +- **RegionCheck cur_seq/exit_seq**:设计文档声称存在(`regalloc-cache-mapping.md` 表格引用),**代码中不存在**——区间推导不依赖它,从指令序直接推导 +- **无配方/驱逐/home 标注**:代码零实现 +- **save/load**:`save_ccr`(ccr_io.cr:193)、`load_ccr`(:376)、`calc_ccr_size`(:130)、字段校验 `ccr_i32_fits`(:99)/`ccr_validate_i32_fields`(:104) +- **消费方**:corearch(后端)读 .ccr → ELF;`.cir` 缓存走 `cir_cache.cr`(独立,不动) + +## v6 格式布局(2026-09-05 定案:**以格式定稿 spec 为唯一真相**) + +> 本文档早前的「追加式 24B」布局是草案残留,已废弃。**权威布局 = `docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md`**(B 方向:存在结构主体): +> +> - Header 16B(magic `CCR1` / version 6 / seg_count)+ 段表(每段 {tag, offset, size})——段序自由 +> - 段:STR(字符串)/ SYM(符号表,函数含 first_ent/last_ent)/ NOD(图事件 + 显式边)/ **ENT(条目表 28B/条:var_id, version, def_nod, live_start, live_end(半开), home, flags)** / REG(region,NOD 坐标) +> - 坐标 = NOD id(图节点序 = 现 IR 指令序,见计划头注记) +> - 共存不落盘(判定现算,sweep 不超线性) +> - v6-only(无转换工具) + +--- + +### Task 1: 存在区间推导 pass(liveness 升级为独立函数) + +**Files:** +- Modify: `src/compiler/opt.cr`(新增函数;`alloc_registers` 改为调用它) +- Create: `tests/selfhost/test_live_ranges.py` +- Modify: `src/compiler/globals.cr`(区间表全局) + +**Interfaces:** +- Produces: `fn compute_live_ranges()` — 填充全局 `g_ir_live_ranges`(string buffer,每函数每变量两条 i64:first_ref/last_ref,指令序),`g_live_range_count/g_live_range_cap`;`fn live_first(func_i, var_i) -> int` / `fn live_last(func_i, var_i) -> int` 查询(未使用返回 -1)。从 `alloc_registers`(opt.cr:210-241)提取同一逻辑,**行为不变**(alloc_registers 改读表) + +- [ ] **Step 1: 写失败测试** + +Create `tests/selfhost/test_live_ranges.py`: + +```python +#!/usr/bin/env python3 +"""存在区间推导(v6 数据基础)——通过 ccr dump 校验每变量区间表。""" + +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def main() -> int: + if not COREC.exists(): + print(f"[FAIL] missing native compiler: {COREC}") + return 1 + # 占位:Task 1 结束前替换为真实断言(见 Step 4) + print("0/1 passed (placeholder)") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) +``` + +- [ ] **Step 2: 跑测试确认失败** + +Run: `nice -n 19 python3 tests/selfhost/test_live_ranges.py` +Expected: `0/1 passed (placeholder)`,exit 1 + +- [ ] **Step 3: 实现 compute_live_ranges** + +在 `src/compiler/opt.cr` 的 `alloc_registers`(:198)上方新增(表全局声明放 `src/compiler/globals.cr`,与 `g_ir_slice_lens` 同风格;opt.cr 只放函数)。把 :210-241 的区间构建循环搬入(同样的扫描:逐函数逐指令,dest/s1/s2 在函数变量范围内则扩展 [first_ref,last_ref]),结果写全局表;`alloc_registers` 改为读表(删除其内部区间构建,保留寄存器指派逻辑): + +```core +// v6 数据基础:存在区间推导(指令序 [first_ref, last_ref])。 +// 与 alloc_registers 共用——后者改读本表。 +// 表布局:每函数一段,段内每「函数内 var」16B(段偏移 = 前缀 var 数累计,见 +// live_range_slot——func_i 段起始 = Σ var_count[0..func_i),不乘固定稠密系数)。 +g_ir_live_ranges : string, mut; +g_live_range_cap : int, mut; + +fn grow_live_ranges(needed: int) { + if needed < g_live_range_cap { return; } + nc := g_live_range_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 16); _dyncpy(g_ir_live_ranges, g_live_range_cap * 16, nb); + g_ir_live_ranges = nb; g_live_range_cap = nc; +} + +fn live_range_slot(func_i: int, var_i: int) -> int { + // 函数内 var 索引 = var_i − var_start[func_i];func_i 段起始 = 前缀 var_count 累计 + off : ., mut = 0; + fi : ., mut = 0; + loop { if fi >= func_i { break; } + off = off + r64(g_ir_func_var_count, fi * 8); + fi = fi + 1; } + return (off + (var_i - r64(g_ir_func_var_start, func_i * 8))) * 16; +} + +fn live_first(func_i: int, var_i: int) -> int { + if func_i < 0 || var_i < 0 { return -1; } + return r64(g_ir_live_ranges, live_range_slot(func_i, var_i)); +} + +fn live_last(func_i: int, var_i: int) -> int { + if func_i < 0 || var_i < 0 { return -1; } + return r64(g_ir_live_ranges, live_range_slot(func_i, var_i) + 8); +} +``` + +`compute_live_ranges()` 实现:先按 Σ var_count 总量 grow 并首遍写 -1(区间未知 = -1,沿用 alloc_registers 的 iv_buf 初始化语义);再逐函数逐指令扫描:op/dest/s1/s2 落在 `[var_start, var_start+var_count)` 内则扩展区间(首见写 first,之后推进 last——与 opt.cr:210-241 逻辑逐行一致)。末尾把 `alloc_registers` 的重复扫描删除并改读 `live_first/live_last`。 + +- [ ] **Step 4: 补真实测试断言** + +把 `test_live_ranges.py` 的 main 替换为真实校验——用 `cir` dump 的指令序计算期望区间不现实(脆弱),改为**用 O1 编译冒烟 + 回归套件验证行为不变**:直接跑现有 `test_compile.py`/`test_dex_arith.py`(O1 路径覆盖 alloc_registers → 现在读新表)。测试文件保留为「smoke: 编译+运行通过即区间表不破坏分配」: + +```python +def build_and_run(source: str) -> int: + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(source) + src = f.name + out = src[:-3] + try: + b = subprocess.run([str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=120) + if b.returncode != 0: + return 999 + r = subprocess.run([out], capture_output=True, text=True, timeout=10) + return r.returncode + finally: + for p in (src, out, out + ".ccr"): + try: os.unlink(p) + except FileNotFoundError: pass + + +def main() -> int: + if not COREC.exists(): + print("[FAIL] missing build/corec") + return 1 + src = "fn fib(n:int)->int{if n<2{return n;}return fib(n-1)+fib(n-2);}\n" \ + "fn main()->int{return fib(10);}\n" + rc = build_and_run(src) + ok = rc == 55 + print(f"[{'PASS' if ok else 'FAIL'}] O1 live-range smoke: rc={rc}") + return 0 if ok else 1 +``` + +(fib(10)=55 需要 O1——`corec build` 默认 opt 级别见 main.cr;若默认非 O1,命令加 `--opt-level 1`,先确认 CLI 标志名。) + +- [ ] **Step 5: 跑回归** + +Run: `nice -n 19 python3 tests/selfhost/test_compile.py && nice -n 19 python3 tests/selfhost/test_live_ranges.py && nice -n 19 python3 tests/bootstrap/test_pipeline.py` +Expected: 全部通过(区间表行为与内联扫描一致) + +- [ ] **Step 6: 提交** + +```bash +jj commit -m 'feat: 存在区间推导 pass——compute_live_ranges 独立表,alloc_registers 改读表(v6 数据基础 Task 1)' +``` + +--- + +### Task 2: 条目版本化(变量 × 定值点) + +**Files:** +- Modify: `src/compiler/opt.cr` +- Test: `tests/selfhost/test_live_ranges.py`(追加断言) + +**Interfaces:** +- Consumes: Task 1 的 `compute_live_ranges` +- Produces: `fn compute_entries(func_i) -> int`(返回该函数条目数)填充全局条目表 `g_ir_entries`(24B/条内存表:var_idx(全局), def_instr(全局), live_start, live_end, home, flags——盘上 28B/含 version 由 Task 4 转换,见「v6 格式布局」);`fn entry_count(func_i) -> int` + +- [ ] **Step 1: 设计确认(读代码)** + +读 `opt.cr` alloc_registers 全文,确认 IR 变量定值形态:`IR_ALLOC`(局部槽初定值)后续 `IR_STORE` 重定值。条目版本切分规则:**每个对该变量的 STORE/ALLOC 指令 = 一个版本条目**;版本存在区间 = 该版本定值点到下一版本定值点(或最后使用)。 + +- [ ] **Step 2: 写失败测试** + +`test_live_ranges.py` 追加:一个 `x := 1; x = x + 1; x = x + 2; return x;` 程序 → 期望 x 有 **4** 个条目——真实 IR 里 LET 初始化 `x := 1` 发射 `IR_ALLOC` + `IR_STORE` 两条定值(ir_gen EXPR_LET 路径),故 x = ALLOC 定值 + 3 次 STORE = 4 版本(2026-09-05 勘误:原写 3 系误按「LET 只发 ALLOC」的错误 IR 模型估算;规则原文含 ALLOC 定值,实现按规则得 4,测试断言 4)。 + +**定值识别假设注记**(评审 Important):"函数段内 var 只被 ALLOC/STORE 写"成立范围有限——`IR_ALLOC_ARRAY/IR_ALLOC_STRUCT` 的 dest 也直写 var(`x : [3]int;` 无初值声明不发 IR_ALLOC)。若此类 var 后续被 STORE 重定值且其间有读,窗口无条目覆盖。当前影响小(内存对象非寄存器候选),Task 5 读点判定前需处理(格式定稿 §4.1「dest≥0 = 定值点」规则可消解)。 + +- [ ] **Step 3: 实现 compute_entries** + +扫描函数指令:对每条 `IR_STORE`/`IR_ALLOC` 的 dest(函数内 var)登记新版本条目(版本号递增),版本存在区间端点 = 定值指令号与「下一版本定值前最后引用」——实现时用 Task 1 区间表与定值点表合并。版本间切割:条目 k 的 [start,end] = [def_k, def_{k+1}-1] 与变量全局 last_ref 的截断。 + +- [ ] **Step 4: 跑测试** + +Run: `nice -n 19 python3 tests/selfhost/test_live_ranges.py` +Expected: 新断言 PASS,旧 smoke 仍 PASS + +- [ ] **Step 5: 提交** + +```bash +jj commit -m 'feat: 条目版本化——STORE/ALLOC 定值切分版本条目(v6 Task 2)' +``` + +--- + +### Task 3: 共存推导规则(区间相交) + +**Files:** +- Modify: `src/compiler/opt.cr` +- Test: `tests/selfhost/test_live_ranges.py`(追加断言) + +**Interfaces:** +- Consumes: Task 2 条目表 +- Produces: `fn entries_coexist(func_i, e1, e2) -> int`(1=相交 0=不相交,纯区间比较);文档确认共存不落盘(推导规则) + +- [ ] **Step 1: 写失败测试** + +追加断言:`fn main()->int{a:=1;b:=2;return a+b;}` 中 a/b 条目共存(区间相交);顺序重用的程序(`a:=1;tmp:=a;a:=2;`)中 a 的两版本不共存。 + +- [ ] **Step 2: 实现 entries_coexist** + +```core +fn entries_coexist(func_i: int, e1: int, e2: int) -> int { + // 存在区间 [s1,e1) 与 [s2,e2) 相交 ⟺ s1 < e2 && s2 < e1(半开区间) + s1 := ; en1 := ; + s2 := ; en2 := ; + if s1 < 0 || s2 < 0 { return 0; } + if s1 < en2 && s2 < en1 { return 1; } + return 0; +} +``` + +- [ ] **Step 3: 跑测试 + 提交** + +Run: `nice -n 19 python3 tests/selfhost/test_live_ranges.py` +Commit: `jj commit -m 'feat: 共存推导规则(存在区间相交,不落盘冗余)——v6 Task 3'` + +--- + +### Task 4: v6 entries 段序列化/反序列化(ccr_io.cr) + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(save_ccr:193 / load_ccr:376 / calc_ccr_size:130) +- Modify: `src/compiler/globals.cr`(entry 表全局) +- Modify: `src/compiler/dataflow.cr`(lower_to_ccr 尾部调 compute_entries?——**不**:entries 由 save_ccr 前显式计算,见 Step 3) +- Test: `tests/selfhost/test_region_cfg.py` 同款 ccr round-trip 模式或新 `test_ccr_v6.py` + +**Interfaces:** +- Produces: version 常量升 6;**ENT 段按格式定稿 spec 落盘**(`docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md` §3.4:28B/条含 version + 半开区间;段表架构 Header+seg 表);`load_ccr` 校验 version==6 + 段表完整性(越界拒绝)后按段表解析填充条目表 + +- [ ] **Step 1: 写 round-trip 测试** + +Create `tests/selfhost/test_ccr_v6.py`:编译一个含多版本变量的程序 → `ccr` 命令出 .ccr → 用 `corearch` 或 `corec` 读回(现有 `test_region_cfg.py::test_ccr_v2_sg_section` 模式)→ 断言字节含 version=6 且 round-trip 后 ELF 可跑、退出码正确。 + +- [ ] **Step 2: 更新格式注释与 version** + +`ccr_io.cr` 头注释按 spec 更新段布局;version 写 `6`;`calc_ccr_size` 按 spec 段表 + 各段大小(ENT = entry_count × 28)计算。 + +- [ ] **Step 3: save 前计算 + 落盘** + +save_ccr 按 spec 段表写各段(STR/SYM/NOD/ENT/REG);ENT 条目(28B/条:var_id/version/def_nod/live_start/live_end/home/flags——version = 组内定值升序序数,半开 live_end = 最后使用点+1,由 Task 2 内存表转换)。**调用点**:save_ccr 由 corec/corearch 调用,条目表须在 save 前就绪——在 `lower_to_ccr()` 尾部(dataflow.cr:351 函数末尾)显式调 `compute_live_ranges()` + 各函数 `compute_entries()`(compute 与 opt 门控解耦,无条件在 lower 尾部算)。 + +- [ ] **Step 4: load_ccr 读 entries** + +load_ccr 按段表定位 ENT 段:校验 `entry_count ≤ 段大小/28`(越界拒绝,先例 ccr 校验风格),填充条目表;ELF 后端只读 NOD 段(= 原线性指令消费路径的坐标化),不依赖 ENT。 + +- [ ] **Step 5: 跑测试 + 回归 + 提交** + +Run: `nice -n 19 python3 tests/selfhost/test_ccr_v6.py && nice -n 19 python3 tests/selfhost/test_region_cfg.py && nice -n 19 python3 tests/selfhost/test_compile.py` +Commit: `jj commit -m 'feat: .ccr v6 entries 段 save/load——版本 6 落盘(v6 Task 4)'` + +--- + +### Task 5: 判定消费最小闭环(.corespec + checker 自检) + +**Files:** +- Create: `spec/regalloc-consistency.corespec`(判定四条规约,先规格后实现——TODO 判据「判定规约:一致性四条写成 .corespec」) +- Modify: `src/compiler/checker.cr`(debug 自检钩子)或独立 pass 文件 +- Test: `tests/selfhost/test_live_ranges.py`(追加判定用例) + +**Interfaces:** +- Produces: 判定实现 `fn verify_regalloc_consistency(func_i) -> int`(0=一致 1=违反):消费条目表(存在区间/共存/home)+ 当前分配(alloc_registers 结果)——四条:① 共存互斥(同寄存器条目不共存)② 读点无陈旧 ③ 驱逐配对 ④ 调用失效。debug 模式(环境变量或 `g_opt_level ≥ 2`)下 checker 自检调用;违反即编译错误(诊断)。 + +- [ ] **Step 1: 写 .corespec** + +Create `spec/regalloc-consistency.corespec`:四条判定以规范语言陈述(参考 `spec/` 现有文件风格),附条目/区间/共存定义。 + +- [ ] **Step 2: 写失败测试** + +`test_live_ranges.py` 追加:O2 编译已知正确程序 → 自检静默通过(exit 0);构造错误 home 冲突(测试内直接调 verify 函数注入冲突条目——通过导出测试钩子)→ 返回违反。 + +- [ ] **Step 3: 实现 verify_regalloc_consistency** + +对每函数:① 遍历条目对(同 home 且非 -1)→ entries_coexist 为真则违反;② 读点(使用指令)所在条目版本须是活跃版本(无陈旧);③④ 按驱逐/调用点标注(v6 先以 home=-1 与调用点指令列表近似——标注字段 flags 预留,精确驱逐语义与贪心放置同批)。范围控制:**本任务只做 ①(共存互斥)+ ② 的框架**,③④ 留 TODO 注记——YAGNI,四条规约先行落文档,代码验证主路径。 + +- [ ] **Step 4: 测试 + 提交** + +Run: `nice -n 19 python3 tests/selfhost/test_live_ranges.py` +Commit: `jj commit -m 'feat: 判定一致性自检(共存互斥闭环)+ regalloc-consistency.corespec 规约——v6 Task 5'` + +--- + +### Task 6: 自举管线 / 测试迁移 / 文档同步 + +**Files:** +- Modify: `docs/ir-schema/coreir-schema.md`(v6 段布局 + ENT 记录 28B 字段表,按格式定稿 spec) +- Modify: `CLAUDE.md`(`.ccr` 描述 v5 → v6:格层存在结构) +- Modify: `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(状态:执行中→按实施结果复核 §4.2/§5;**§4.2/§5 的「v5→v6 一次性转换工具」按计划定稿取消**(.ccr 为管线中间产物、零持久生态)——同步删除该事项并注记) +- Modify: `docs/project-book.md`、`docs/compcert-reference.md`(如提及 .ccr v5 线性投影描述处) +- Modify: `TODO.md`(v6 事项划销:见「格形态 IR 升级」节 8 事项逐条核对) +- 验证:`bootstrap/corec/ir/ccr.py`(Python bootstrap 的 .ccr 读写——查它是否真的读写 .ccr 还是仅占位) + +- [ ] **Step 1: 核对 bootstrap 侧 .ccr 参与度** + +Run: `grep -n "ccr\|CCR_MAGIC\|save\|load" bootstrap/corec/ir/ccr.py | head -20` +判定:Python bootstrap 是否实际产出/消费 .ccr(若仅定义未接线,标注「自举管线不受 v6 影响」并跳过该迁移面;若接线则同步格式)。 + +- [ ] **Step 2: 更新 coreir-schema.md** + +按本文「v6 格式布局」写入 entries 段字段表与 version 6 说明;附共存推导规则(不落盘)注记。 + +- [ ] **Step 3: 更新 CLAUDE.md / 设计文档状态 / TODO** + +CLAUDE.md 的 `.ccr` 描述更新(Architecture 节 + Key Conventions);lattice-form-ir-design.md 状态行改「执行中(2026-09 起)」并在 §4.2 标实施结果;TODO「格形态 IR 升级」8 事项按完成状态划销/标注。 + +- [ ] **Step 4: 全量回归 + 自检 + 提交** + +Run: `nice -n 19 python3 tests/bootstrap/test_pipeline.py && nice -n 19 python3 tests/bootstrap/test_borrow.py && nice -n 19 python3 tests/bootstrap/test_generics.py && ./build/corec check src/compiler` +Commit: `jj commit -m 'docs: v6 落地同步——coreir-schema/CLAUDE.md/设计文档状态/TODO 划销(v6 Task 6)'` + +--- + +## 后续(本计划外,挂 TODO) + +- **SYM 归并 / REG 重排 + 区内条目范围**(Task 4 评审 I-1 条件,2026-09-06):v6.0 保守路径未做 spec §3.2/§3.5 目标形状——SYM 现为 v5 符号面装入段表、REG 为 v5 sgs 原序。坐标化全量目标须挂账(spec 与代码避免永久脱节) +- Task 6 schema 同步以**实现形状**为准落笔(不写编译器永不产出的格式) + +- **贪心放置升级**(`opt.cr` alloc_registers → 上下文贪心:存在结构上的 First Fit + 驱逐写回 + 调用点失效)——消费 v6 条目/共存/home;解锁 caller-saved +- **证书层**:最优性证书(DP 表重放 / ILP 对偶) +- **无配方条目完整语义**(条款 4b 边界 + 驱逐必写回)——v6 flags 位 0 字段已预留 +- **v6.1**:驱逐/填充点标注段(设计文档 §4.2 第 5 条,可选) +- 宽度标签清理(width-out-of-language 语言侧)与 v6 无交互——v6 格式无宽度字段,可并行 diff --git a/docs/superpowers/plans/2026-09-06-core-maintainer-docs-reorg.md b/docs/superpowers/plans/2026-09-06-core-maintainer-docs-reorg.md new file mode 100644 index 00000000..90089947 --- /dev/null +++ b/docs/superpowers/plans/2026-09-06-core-maintainer-docs-reorg.md @@ -0,0 +1,437 @@ +# 核心维护文档重组与首批重写 实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `docs/` 平铺的 156 份 md 按「主题 × 读者 × 状态」分类树重组,并完成首批新增/重写范本(含 jj 提交策略,遵守仓库铁律)。 + +**Architecture:** 机械性重组(目录移动 + 链接修复)先行并独立提交;内容性工作(新建手册/ADR/重写范本)逐个任务产出,每任务可独立验收。全程用 `jj` 不用 `git`(CLAUDE.md 铁律 #2)。 + +**Tech Stack:** jj 版本控制、bash(grep/sed/mv)、Markdown。无代码构建。 + +**Spec:** `docs/superpowers/specs/2026-09-06-core-maintainer-docs-reorg-design.md` + +## Global Constraints + +1. 版本控制只用 `jj`(禁 `git`,铁律 #2);提交必须经用户许可,计划中的提交步骤执行前先向用户确认工作副本 src 改动的归属 +2. 文件永久不允许还原(铁律 #3)——移动不复制,mv 前确认目标不存在 +3. 每份文档头部强制加定位声明:受众(users/maintainers/contributors)+ 状态(active/superseded/deprecated/archive/proposal)+ 真源声明 +4. 一份文档一个职责;状态与内容冲突时更新文档,不添加免责段 +5. docs/README.md 为唯一导航索引,与目录树一致 +6. docs/pseudocode/ 与 docs/superpowers/ 不动;coq/ ir-schema/ verifier/ 原样只加头注 +7. 文档互链一律相对路径,指向新位置;旧路径引用必须在引用方(含 CLAUDE.md/README.md/TODO.md/CHANGELOG.md)同步更新,验收用 grep 断链检查 + +--- + +### Task 1: 建目录骨架 + 存量文件归位(机械移动) + +**Files:** +- Create 目录:`docs/language/ docs/design/ docs/proposals/ docs/archive/ docs/maintainer/ docs/adr/` +- Move(29 份顶层 md → 新位置,见下) +- 不触碰:`docs/pseudocode/ docs/superpowers/ docs/coq/ docs/ir-schema/ docs/verifier/ docs/LICENSE` + +**归位表(源 → 目标,逐条执行,先查目标不存在再 mv):** + +| 源 | 目标 | +|---|---| +| language-syntax.md | language/syntax.md | +| learning-path.md | language/learning-path.md | +| error-codes.md | language/error-codes.md | +| editor-setup.md | language/editor-setup.md(用户配置部分;corelsp 架构段先随文件走,Task 5 拆分) | +| project-book.md | design/project-book.md | +| execution-model.md | design/execution-model.md | +| dataflow-design.md | design/dataflow-design.md | +| memory-model.md | design/memory-model.md | +| pointer-model.md | design/pointer-model.md | +| regalloc-cache-mapping.md | design/regalloc-cache-mapping.md | +| ir-op-semantics.md | design/ir-op-semantics.md | +| glossary.md | design/glossary.md | +| spec-design.md | design/spec-design.md | +| verifier-kernel.md | design/verifier-kernel.md | +| at-intrinsics.md | design/at-intrinsics.md | +| crasm.md | design/crasm.md | +| generics.md | proposals/generics.md | +| comptime.md | proposals/comptime.md | +| ffi.md | proposals/ffi.md | +| concurrency.md | proposals/concurrency.md | +| dynamic-typing.md | proposals/dynamic-typing.md | +| lazy.md | proposals/lazy.md | +| distributed.md | proposals/distributed.md | +| probabilistic.md | proposals/probabilistic.md | +| compcert-reference.md | archive/compcert-reference.md | +| compcert-round4-findings.md | archive/compcert-round4-findings.md | +| numeric-migration-inventory.md | archive/numeric-migration-inventory.md | +| memory-model-capability-lattice.md | archive/memory-model-capability-lattice.md | + +z-vision.md 保留 docs/ 顶层(愿景入口)。 + +- [ ] **Step 1: 自检 git 状态,确认基线** + +```bash +cd /home/DslsDZC/core +jj st # 预期:docs/superpowers/specs/2026-09-06-...-design.md(新增)+ src/compiler 三份改动(用户工作,勿动) +``` + +- [ ] **Step 2: 建目录** + +```bash +mkdir -p docs/language docs/design docs/proposals docs/archive docs/maintainer docs/adr +``` + +- [ ] **Step 3: 逐条 mv(示例,共 28 条,按归位表)** + +```bash +mv docs/language-syntax.md docs/language/syntax.md +# ...按表逐条... +mv docs/memory-model-capability-lattice.md docs/archive/memory-model-capability-lattice.md +``` + +每条执行前:`[ -e docs/<目标> ] && echo 目标已存在,中止`——目标存在则停下报告,不覆盖(铁律 #3)。 + +- [ ] **Step 4: 验证文件数守恒** + +```bash +before=$(find docs -name '*.md' | wc -l) # 移动前记下 +find docs -name '*.md' | wc -l # 应与 before 相等 +ls docs/*.md # 只剩 README.md? + z-vision.md +``` + +- [ ] **Step 5: 不提交,继续 Task 2(批量移动 + 链接修复一次验收后一起提)** + +--- + +### Task 2: 全仓 docs 引用路径修复 + CLAUDE.md 同步 + +**Files:** +- Modify: 所有引用旧路径的 md(引用源清单见下) +- Modify: `CLAUDE.md`(Key Conventions 段,见 Step 3) + +**旧 → 新路径映射(引用方需全部替换):** +`docs/language-syntax.md→docs/language/syntax.md`、`docs/project-book.md→docs/design/project-book.md`、`docs/memory-model.md→docs/design/memory-model.md`、`docs/pointer-model.md→docs/design/pointer-model.md`、`docs/execution-model.md→docs/design/execution-model.md`、`docs/dataflow-design.md→docs/design/dataflow-design.md`、`docs/regalloc-cache-mapping.md→docs/design/regalloc-cache-mapping.md`、`docs/spec-design.md→docs/design/spec-design.md`、`docs/verifier-kernel.md→docs/design/verifier-kernel.md`、`docs/glossary.md→docs/design/glossary.md`、`docs/crasm.md→docs/design/crasm.md`、`docs/error-codes.md→docs/language/error-codes.md`、`docs/editor-setup.md→docs/language/editor-setup.md`、`docs/at-intrinsics.md→docs/design/at-intrinsics.md`、`docs/ir-op-semantics.md→docs/design/ir-op-semantics.md`、`docs/learning-path.md→docs/language/learning-path.md`、`docs/z-vision.md` 不变、`docs/compcert-reference.md→docs/archive/compcert-reference.md`、`docs/compcert-round4-findings.md→docs/archive/compcert-round4-findings.md`、`docs/numeric-migration-inventory.md→docs/archive/numeric-migration-inventory.md`、`docs/memory-model-capability-lattice.md→docs/archive/memory-model-capability-lattice.md`(另:generics/comptime/ffi/concurrency/dynamic-typing/lazy/distributed/probabilistic → docs/proposals/ 同名) + +- [ ] **Step 1: 找出全部引用方,逐个 sed 替换** + +引用方(2026-09-06 扫描):CLAUDE.md、README.md、TODO.md、CHANGELOG.md 及 docs/ 内各文件(含 language/design/proposals/archive 下移动后的、docs/ir-schema/ 两份、docs/learning-path.md 等)。逐文件按映射做精确替换,例如: + +```bash +cd /home/DslsDZC/core +sed -i 's|docs/language-syntax\.md|docs/language/syntax.md|g' CLAUDE.md README.md TODO.md CHANGELOG.md +# 其余映射同理;对 docs/ 树用: +grep -rl 'docs/\(language-syntax\|project-book\|memory-model\|pointer-model\|execution-model\|dataflow-design\|regalloc-cache-mapping\|spec-design\|verifier-kernel\|glossary\|crasm\|error-codes\|editor-setup\|at-intrinsics\|ir-op-semantics\|learning-path\|compcert-reference\|compcert-round4-findings\|numeric-migration-inventory\|memory-model-capability-lattice\|generics\|comptime\|ffi\|concurrency\|dynamic-typing\|lazy\|distributed\|probabilistic\)\.md' --include='*.md' . 2>/dev/null | grep -v build/ | grep -v docs/superpowers/ +# 逐文件替换对应映射(可用 sed -i 循环;注意 .corespec 后缀文件不在本次范围) +``` + +注意:执行顺序必须先于 Task 1 之外的所有读者可见产出;Task 1 的移动与此步必须同一次验收。 + +- [ ] **Step 2: 验证零残留旧路径** + +```bash +grep -rn 'docs/\(language-syntax\|project-book\|memory-model\|pointer-model\|execution-model\|dataflow-design\|regalloc-cache-mapping\|spec-design\|verifier-kernel\|glossary\|crasm\|error-codes\|editor-setup\|at-intrinsics\|ir-op-semantics\|learning-path\|compcert-reference\|compcert-round4-findings\|numeric-migration-inventory\|memory-model-capability-lattice\)\.md' --include='*.md' --include='CLAUDE.md' --include='README.md' --include='TODO.md' --include='CHANGELOG.md' . 2>/dev/null | grep -v build/ +``` + +预期:零输出。允许残留:`.corespec` 引用(另案)、`docs/superpowers/` 内历史计划对旧路径的引用(历史档案不改)。 + +- [ ] **Step 3: CLAUDE.md Key Conventions 与 Known Issues 段同步** + +CLAUDE.md 中若出现已移动路径(如 "Spec files in `spec/`" 段、Architecture 段内 `docs/superpowers/specs/...` 引例),逐处按新路径更新;并把 docs/ 分类树一句话加入 CLAUDE.md(docs 区定位:language/ 用户向、design/ 定稿、proposals/ 讨论、archive/ 任务产物、maintainer/ 手册、adr/ 决策记录、pseudocode/superpowers 工具链目录)。 + +- [ ] **Step 4: 断链复查** + +```bash +# 对每个新目标路径抽查引用存在性(新路径在引用方出现、文件确实在新位置) +for f in docs/language/syntax.md docs/design/project-book.md docs/archive/compcert-reference.md; do + [ -f "$f" ] && echo "OK $f" || echo "MISSING $f" +done +``` + +- [ ] **Step 5: 提交(先向用户确认 src 改动归属与许可)** + +```bash +jj commit -m "docs: 分类树落地——docs/ 按主题×读者重组(29 份顶层文档归位 + 全仓引用路径修复 + CLAUDE.md 同步)" +``` + +--- + +### Task 3: docs/README.md 导航索引(新建) + +**Files:** +- Create: `docs/README.md` + +- [ ] **Step 1: 起草 README.md** + +内容结构(与分类树一致,引用真实新路径): + +```markdown +# Core 文档中心 + +> 分类规则:按「主题 × 读者 × 状态」组织;每份文档头部有定位声明(受众/状态/真源)。 +> 一份文档一个职责;实现与文档冲突时以源码为准(既有惯例)。 + +## 用户向(language/) +- [语言语法](language/syntax.md)— 词法/类型/声明/表达式/规约,真源 = grammar/core.ebnf + src/compiler/ +- [学习路径](language/learning-path.md) +- [错误码参考](language/error-codes.md) +- [编辑器与 LSP 配置](language/editor-setup.md) + +## 定稿参考(design/)— 维护者向 +- [项目书](design/project-book.md)— 项目定位 +- [执行模型](design/execution-model.md)(dataflow-design.md 已被其取代,留档) +- [内存模型](design/memory-model.md)、[指针模型](design/pointer-model.md)、[寄存器分配缓存映射](design/regalloc-cache-mapping.md) +- [IR 操作语义](design/ir-op-semantics.md)、[术语表](design/glossary.md) +- [规约系统设计](design/spec-design.md)、[验证内核](design/verifier-kernel.md)、[@ 内建原语](design/at-intrinsics.md) +- corelsp 设计(editor-setup 拆分后,见 design/corelsp.md) + +## 特性提案(proposals/) +generics / comptime / ffi / concurrency / dynamic-typing / lazy / distributed / probabilistic(状态见各文件头注) + +## 任务产物归档(archive/) +compcert 对照审查四轮材料与修复记录、数值类型迁移盘点、内存模型能力格讨论备忘 + +## 维护者手册(maintainer/) +- onboarding.md — 新维护者入门 +- testing.md — 测试/验证操作手册 + +## 决策记录(adr/) +ADR-0001 起,见 [adr/](adr/) + +## 工具链目录(不动) +pseudocode/(TDD 交付物)、superpowers/(specs + plans)、coq/、ir-schema/、verifier/ +``` + +- [ ] **Step 2: 按 Task 4-8 实际产出校正条目(Task 4-8 完成后回填缺失链接;新文档完成后把对应行补真)。** 本步只保证:README 提到的每个路径在 Task 1-2 完成后真实存在。 + +- [ ] **Step 3: 提交(连同 Task 1-2 一次提交,或本步独立小提交,依用户许可)** + +```bash +jj commit -m "docs: docs/README.md 导航索引(分类规则 + 目录树)" # 或并入 Task 2 提交 +``` + +--- + +### Task 4: 新建 maintainer/onboarding.md(新维护者入门手册) + +**Files:** +- Create: `docs/maintainer/onboarding.md` + +素材:CLAUDE.md(铁律/分支模型/构建命令/架构树)、TODO.md、docs/design/ 各文件头注、README.md(仓库结构)。 + +- [ ] **Step 1: 起草内容(章节固定,每节 3-10 行,链接真实路径)** + +1. 定位声明块:受众 maintainers;状态 active;真源 = 源码 + CLAUDE.md +2. 仓库地图:`src/compiler/*.cr` 各模块一句话职责(ast/lexer/parser/checker/ir_gen/dataflow/ccr_io/opt/pass/diag/module/project/interp/dump/dyn_arr/globals/entry/main/corearch/_import);`src/arch/linux/ld/`(elf/instr/sizes/resolve/ld);`src/stdlib/`、`src/runtime/`(rt.s/rt.cr);`bootstrap/corec/` 定位;tests 三套;grammar/;docs/ 分类树(链接 README.md) +3. 构建管线:三条命令(build_selfhost_native.py → build/corec+corearch;./build/corec build FILE.cr;./build/corearch FILE.ccr)+ 三级自举 Stage0/1/2 含义 +4. 分支模型与 jj 速查:feature → develop → main;`jj bookmark create feature/x`、`jj git push -b feature/x`、`jj git fetch && jj bookmark move develop -r develop@origin`;SSH 自动签名说明 +5. 已知雷区:corec2 tokenizer 死循环(9 个全局变量未注册,详见 TODO.md);pass_cse/O1 稳定性;core_pattern/trap 测试禁 core dump(近期修复) +6. 贡献清单:读 README → 挑 TODO → feature 分支 → 开发+测试 → PR → develop(检查列表) + +- [ ] **Step 2: 验证:全部互链路径真实存在** + +```bash +grep -oE '\]\([^)]+\)' docs/maintainer/onboarding.md # 逐条 [ -f ] 检查 +``` + +- [ ] **Step 3: 提交** + +```bash +jj commit -m "docs: maintainer/onboarding.md——新维护者入门(仓库地图/构建/分支/jj 速查/雷区)" +``` + +--- + +### Task 5: 新建 maintainer/testing.md + 拆分 editor-setup + +**Files:** +- Create: `docs/maintainer/testing.md` +- Create: `docs/language/editor-setup.md`(纯用户配置,截自原 editor-setup.md §1-§7 配置部分) +- Create: `docs/design/corelsp.md`(corelsp 架构/维护向部分:构建服务器、LSP 协议细节) +- Delete: 原 editor-setup.md(已随 Task 1 移到 docs/language/,本任务原地改写为纯用户版,并把架构内容抽到 design/corelsp.md) + +- [ ] **Step 1: 读原 editor-setup.md,按 §划分两类内容;rewrite docs/language/editor-setup.md 为用户配置版(Neovim/VS Code/Zed 接入步骤 + 触发表),新建 docs/design/corelsp.md 收架构向内容(构建服务器/诊断通道/LSP 细节),两文件头部互链** + +- [ ] **Step 2: 起草 testing.md**(章节固定) + +1. 定位声明:受众 maintainers;状态 active;真源 = 源码 +2. 测试三套定位:tests/bootstrap(test_pipeline/test_borrow/test_generics,Python 驱动内联源码)、tests/selfhost(test_compile/test_impl/test_borrow,自举管线)、tests/suite(.cr 集成走 ./build/corec) +3. 跑法命令清单(逐条真实命令,自 CLAUDE.md Build & Test) +4. 回归流程:自举三阶段 + 字节一致验证(corec→corec2→corec3,corearch 逐字节一致);CPU 限制:`cpulimit -l 10`/`nice -n 19`(铁律 #6) +5. 改测试指引:加用例 = 内联 Core 源码字符串 + 断言;borrow 测试按 7 规则分类 +6. 失败定位常见路径:tokenizer 死循环(检查 g_pos 递增)、O1 崩溃(pass_cse)、ELF 字节不一致(先查 instr 编码) + +- [ ] **Step 3: 验证** + +```bash +[ -f docs/language/editor-setup.md ] && [ -f docs/design/corelsp.md ] && [ -f docs/maintainer/testing.md ] +grep -rn 'docs/editor-setup\.md' --include='*.md' . | grep -v build/ | grep -v superpowers # 应全部指向 docs/language/editor-setup.md 或 docs/design/corelsp.md +``` + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "docs: maintainer/testing.md 测试手册 + editor-setup 拆分(用户配置/design 两部分)" +``` + +--- + +### Task 6: ADR 目录 + 首批 4 份决策记录 + +**Files:** +- Create: `docs/adr/README.md`(ADR 模板 + 编号规则:0001 起,accepted/superseded/date/决策者/背景/决策/后果/关联) +- Create: `docs/adr/adr-0001-corespec-crasm-retired.md` +- Create: `docs/adr/adr-0002-ccr-v6-segment-table.md` +- Create: `docs/adr/adr-0003-selfhost-elf-direct-emit.md` +- Create: `docs/adr/adr-0004-corec-corearch-split.md` + +- [ ] **Step 1: 挖 git 历史取决策素材** + +```bash +jj log --limit 40 # 近 40 提交扫决策类提交(corespec 退役 2026-09-06 / crasm 退役 2026-09-05 / v6 段表 2026-09-05 / corec-corearch 拆分 / ELF 直出) +jj show --stat # 需要细节时展开具体提交 +``` + +- [ ] **Step 2: 逐份起草,每份格式**(20-60 行): + +```markdown +# ADR-000N: 标题 + +- 日期:YYYY-MM-DD +- 状态:accepted(或 superseded by ADR-000M) +- 决策者:DslsDZC(或 + 第二维护者) + +## 背景 +(问题/上下文,2-5 句,链接 design/ 相关文档) + +## 决策 +(选择与理由,含被否选项一句) + +## 后果 +- 正面: +- 负面: +(各 1-3 条) + +## 关联 +- 相关 ADR:ADR-000X +- 文档:docs/design/... +``` + +四份草案要点: +- **ADR-0001**:.corespec/.crasm 独立格式退役(2026-09-05/06);规约并入 .cr 语法、crasm 能力由硬件接口表(HIT)吸收;理由:独立规约格式同构于 crasm,语法唯一表达;后果:grammar/core.ebnf 并入、spec/ 目录 .corespec 残留待清、CLAUDE.md 已同步 +- **ADR-0002**:.ccr v6 段表架构(2026-09-05);ENT 存在结构段;REG 坐标化(SYM 归并/REG kind/parent/enter/exit/first/last/root_region span);后果:v5 前 .ccr 不兼容、ccr_io.cr 读写双端、spec/2026-09-05-lattice-ir-v6-format.md 为格式权威 +- **ADR-0003**:自举后端 x86-64 ELF 直出(2026-08,三阶段字节一致贯通);放弃 .s 汇编中介;理由:跳过 as/ld 依赖、字节级可复现;后果:rt.s 手工汇编保留、legacy_asm_backend/ 移出 +- **ADR-0004**:corec/corearch 前端后端二进制拆分(2026-08);.ccr 为接口契约;理由:自举需要后端独立驱动;后果:CLI 分工(main.cr/corearch.cr)、.ccr 为格式边界 + +- [ ] **Step 3: 验证:格式一致 + README 索引链接** + +```bash +for f in docs/adr/adr-*.md; do head -1 "$f" | grep -q '^# ADR-000' || echo "BAD HEADER $f"; done +``` + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "docs: adr/ 目录 + ADR-0001~0004(corespec/crasm 退役·ccr v6 段表·ELF 直出·corec 拆分)" +``` + +--- + +### Task 7: 重写范本 A——project-book.md 拆职责 + +**Files:** +- Rewrite: `docs/design/project-book.md`(已随 Task 1 到位) +- Modify: `docs/z-vision.md`(愿景承接段,若 project-book 原含愿景/哲学) + +- [ ] **Step 1: 读现 project-book.md 全篇,按职责切分:** + +- 愿景/哲学段 → 若与 z-vision.md 重复则删,不重复的并入 z-vision.md +- IR 系统/执行模型/验证架构细述 → 不复制,链接 design/ 对应文件(execution-model.md、spec-design.md、verifier-kernel.md、memory-model.md、pointer-model.md、ir-op-semantics.md) +- 项目定位保留重写:目标 ≤150 行,结构 = 定位声明块 + 一、项目概述(是什么/不是承诺一句) + 二、核心思想三句话(语义保鲜/单一事实源/单执行模型,各链设计文档) + 三、系统骨架图(文本图:前端→IR→后端→验证,标注对应 .cr 模块与 design/ 文档) + 四、状态速览(自举阶段 + 链 TODO.md + README.md 状态表) + +- [ ] **Step 2: 写新 project-book.md(≤150 行,无免责段堆叠),改 z-vision.md 关联段** + +- [ ] **Step 3: 验证** + +```bash +wc -l docs/design/project-book.md # ≤150 +grep -n 'docs/design/\|docs/language/' docs/design/project-book.md # 链接全部指向新路径 +``` + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "docs: project-book.md 重写——定位导航化(≤150 行,愿景归 z-vision,细节链 design/)" +``` + +--- + +### Task 8: 重写范本 B——glossary.md 校准 + 范本 C——crasm.md 废弃收尾 + +**Files:** +- Rewrite: `docs/design/glossary.md`(17 节 → 校准) +- Rewrite: `docs/design/crasm.md`(205 行 → ≤40 行废弃说明) + +- [ ] **Step 1: 校准 glossary.md** + +读全篇,逐节核对:术语定义与 design/ 定稿文档(execution-model/memory-model/pointer-model/spec-design)表述一致;关键字类术语与 `src/compiler/lexer.cr` 核对(唯一真源惯例);失效条目删或标注 archive;演进记录节(十七)压缩为一行指向 ADR 与 git 历史。目标体积:较现 241 行只减不增,头部加定位声明(受众 maintainers;状态 active;真源 = design/ 各文档 + src/compiler/lexer.cr)。 + +- [ ] **Step 2: 压缩 crasm.md → 废弃收尾版** + +结构(≤40 行):定位声明块(状态 deprecated,2026-09-05,原因一句)+ 是什么(一段)+ 为什么废弃(绑定经典硬件过深;被硬件接口表 HIT 吸收,链接 docs/superpowers/specs/2026-09-05-hardware-interface-table.md)+ 去向(规则并入 .cr 语法 + unsafe;另见 ADR-0001)+ 历史正文指针(原全文已删,需要细节看 jj 历史)。原 205 行设计细节删除。 + +- [ ] **Step 3: 验证** + +```bash +wc -l docs/design/crasm.md # ≤40 +head -5 docs/design/crasm.md # 含 deprecated 定位声明 +grep -rn 'crasm' CLAUDE.md README.md docs/README.md | head # 上层引用一致性 +``` + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "docs: 重写范本 B/C——glossary 校准(源对照)+ crasm 废弃收尾(ADR-0001 衔接)" +``` + +--- + +### Task 9: 总验收 + README 回填 + +**Files:** +- Modify: `docs/README.md`(Task 4-8 产出的新条目回填) + +- [ ] **Step 1: 回填 README.md**(onboarding/testing/corelsp/ADR 目录项,设计分节里已列) + +- [ ] **Step 2: 总验收命令** + +```bash +cd /home/DslsDZC/core +# 1) 旧路径零残留(排除 build/、docs/superpowers/ 历史) +grep -rn 'docs/\(language-syntax\|project-book\|memory-model\|pointer-model\|execution-model\|dataflow-design\|regalloc-cache-mapping\|spec-design\|verifier-kernel\|glossary\|crasm\|error-codes\|editor-setup\|at-intrinsics\|ir-op-semantics\|learning-path\|compcert-reference\|compcert-round4-findings\|numeric-migration-inventory\|memory-model-capability-lattice\)\.md' --include='*.md' --include='CLAUDE.md' . 2>/dev/null | grep -v build/ | grep -v superpowers || echo PASS +# 2) 新路径文件数守恒(移动前后一致) +# 3) 新增文档齐备 +for f in docs/README.md docs/maintainer/onboarding.md docs/maintainer/testing.md docs/design/corelsp.md docs/adr/adr-0001-corespec-crasm-retired.md docs/adr/adr-0002-ccr-v6-segment-table.md docs/adr/adr-0003-selfhost-elf-direct-emit.md docs/adr/adr-0004-corec-corearch-split.md; do [ -f "$f" ] && echo "OK $f" || echo "MISSING $f"; done +# 4) 定位声明抽查:每个移动文件头部有受众/状态/真源 +for f in docs/language/*.md docs/design/*.md docs/proposals/*.md docs/archive/*.md; do grep -q '受众' "$f" || echo "NO-DECL $f"; done +# 5) crasm ≤40 行、project-book ≤150 行 +wc -l docs/design/crasm.md docs/design/project-book.md +``` + +- [ ] **Step 3: 编译相关改动零牵连验证** + +```bash +jj st # 除 docs/ 变更与用户 src 三件套外无意外文件 +jj diff --stat | tail -5 +``` + +- [ ] **Step 4: 提交 README 回填(如与 Task 3 未合并)** + +```bash +jj commit -m "docs: README 索引回填(Task 4-8 产物)+ 总验收通过" +``` + +--- + +### Task 10: 收尾——提交策略与用户确认 + +- [ ] **Step 1: 汇总提交清单给用户,列出每提交改动的文件数与 docs/ 范围** + +- [ ] **Step 2: 向用户确认:工作副本中 src/compiler 三件套(ccr_io.cr/opt.cr/regalloc-consistency.cr)改动归属(用户自留/另行提交),以及 docs 各提交是否推送 develop** + +- [ ] **Step 3: 待用户许可后执行最终 jj 操作(如用户要求 push feature 分支:jj git push -b feature/docs-reorg)** diff --git a/docs/superpowers/plans/2026-09-06-int-multiword-m1.md b/docs/superpowers/plans/2026-09-06-int-multiword-m1.md new file mode 100644 index 00000000..c155136d --- /dev/null +++ b/docs/superpowers/plans/2026-09-06-int-multiword-m1.md @@ -0,0 +1,169 @@ +# int 多字表示 M1 实施计划(add/sub 全链:64 快路径 + 溢出 2-limb 扩展) + +> **状态:M1 六任务全部完成(2026-09-07,Task 6 收官)**——Task 2-6 各自回归 + 收官全量回归绿(快路径 64 位内程序逐字节零变化,task2 z 用例 + backend_bootstrap stage1→3 byte-identical 为证);M2 挂账清单见 Task 6 执行记录与 spec §8。 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 后端表示策略 B 的 M1:int 算术 add/sub 在 64 位快路径零开销直算,溢出时自动提升到固定 2-limb(128 位)多字表示——语义层(前端/图/格)零改动,纯编码层投影。 + +**Architecture:** 语义 int 无上限(`specs/2026-09-06-int-unbounded-semantics.md` 定稿)→ 编码层投影(本计划):D1a tagged 值槽(快值 64 位 / 多字对象指针 + 旁路 tag);D2b 纯后端溢出检测发射(add/sub + jo → 慢路径);D3 固定 2-limb 128 位(超 128 报编码层错);D4 超 64 字面量 = 词素重建进 rodata 2-limb + IR 常量池引用(复用 IR_LOAD 常量地址机制——HIT 常量池先例);D5 分期 add/sub;D6 寄存器分配形态(Task 5 定案:tagged 变量可驻寄存器——指针 + 帧 tag 自洽,见 Task 5 节)。 + +**Tech Stack:** Core 自举编译器后端(src/compiler/opt.cr、src/arch/linux/ld/instr.cr/elf.cr、src/runtime/rt.s)、Python bootstrap(构建)。参考:HIT 常量池(src/arch/hit/lower_to_core.cr——rodata 2-limb 槽与引用先例)、CAG 分配器(opt.cr——D6 多字只栈接入点)。 + +## Global Constraints + +- 版本控制用 `jj`(铁律 #2)。提交 `jj commit -m`;不动 bookmark +- 构建 `nice -n 19 python3 build_selfhost_native.py` 每次改动重建(~2 分钟);测试 `bash -c 'ulimit -c 0; ...'` +- **语义层零改动**:前端/IR 语义/图/格不变(int 算术仍数学整数 IR——仅 IR_CONST 超 64 字面量走常量池引用路径) +- 快路径行为零变化:64 位内程序 exit/输出与现状完全一致(全套回归) +- 溢出检测发射仅后端(IR 不加溢出感知操作) +- 设计定稿:`specs/2026-09-06-int-multiword-backend-design.md`(决策点按本计划头拍板) +- M1 范围:add/sub 溢出全链;mul/div/比较多字、打印多字、动态增长 = M2+ +- 测试:trap 类 `ulimit -c 0`;回归 compile/ccr_v6/region_cfg/live_ranges/hit_table/backend_bootstrap + corec check src/compiler + +--- + +## 现状事实(执行前必读) + +- int 变量 = 64 位栈槽(g2_slot 偏移布局 init_backend_arrays/opt 定);寄存器分配负编码(CAG 后真实分配) +- IR_BINARY(OP_ADD/SUB) 后端发射:e2_load_var(r10,s1)+e2_load_var(r11,s2)+opcode+modrm+e2_st(r10, slot d)(instr.cr 双寄存器累加形;x86 sub 4D 29 DA、add 4D 01 DA 形) +- x86 溢出检测:add/sub 后 jo(实现 = 0F 80 rel32 6B——Task 2 决策:rel8 ±127 对大函数不可达;下文「rel8」括注同义) +- HIT 常量池先例:rodata 尾追加 8B/槽 + rip 位移回填(lower_to_core.cr + elf.cr) +- IR_CONST s1 = i64 槽(64 位)——超 64 字面量现状在 lexer 被拒(P2 守卫 = 编码层限制错误,见 int-unbounded 定稿) + +## M1 设计拍板(依草案倾向) + +- **D1a**:值槽 64 位(现状不变)+ **旁路 tag 表**(每函数潜在多字变量一个 tag 槽位——栈帧扩展:函数 var_count 不变,tag 区 = 栈帧尾附加区域按「可能溢出变量」位图/每变量 1 槽) +- **D2b**:纯后端发射——快路径 add/sub 后 jo → 慢路径块(2-limb 例程,代码内联或调用) +- **D3**:2-limb 128 位固定(低 limb = 快值、高 limb = 符号扩展);超 128 溢出 → 运行时错误(编码层 128 限制,M1) +- **D4**:超 64 字面量 = 词素(.ccr 已存?——查:NOD src1 i64 装不下——**ccr_io 常量扩展**?最小:lexer 不再拒但 IR_CONST 无法承载 → **M1 决策:超 64 字面量仍前端拒绝(编码层错误消息保留)**——常量多字 = M2(D4 推迟,因 IR_CONST 64 位槽是格层表示,扩展需 v6 常量段——与 dex 精度同族待 v6)——**M1 只做运行时溢出(运行时值运算结果超 64)**,编译期超 64 常量 M2 +- **D5**:add/sub +- **D6**:可能溢出变量(慢路径可达)不进寄存器——分配器静态标记:函数含可能大值运算的变量 = 只栈(保守 M1:**含 add/sub 结果的所有 int 变量 = 潜在多字 = 只栈?太宽**——M1 精化:慢路径输出变量 tagged——分配器对 tagged 槽变量不分配寄存器(保守可接受 M1 性能)——**Task 5 定案修订:不排除(实证见 Task 5 节)** + +## 慢路径形态(M1 设计) + +``` +快路径:add r10,r11 → jo slow_n (jo(rel32 0F 80——Task 2 定案)) +慢路径块 slow_n:值已在 r10/r11(环绕结果) + → 正确 128 值 = 符号扩展重建:[r10 结果, 高位 = (r10<0 ? -1 : 0)](add 溢出结果低 64 位 = r10 环绕值——数学和 = 环绕低 64 + 进位修正?——add 溢出 jo 后 r10 = 环绕和;真值 = 环绕和 + (2^64 or -2^64)——由 CF/OF 判定) + → 存 2-limb 到槽对(低槽 = 环绕值 + 高槽 = 修正) + → 设 tag(该槽对 = 多字态) +``` + +实现简化候选:**槽对模型**——每个「潜在多字」int 变量占 2 槽(低 64 + 高 64 符号扩展 = 128 值)——快路径只写低槽且高槽 = 符号扩展隐含?不——快路径值高槽恒符号扩展(写低槽时同步写高槽 = 2 store?性能损)……**tag 方案**:值槽 64 保持快路径单 store;多字时值槽 = 指针到 2-limb 堆对象 + tag 置位。tag 位放哪?旁路(每潜在变量 1 字节 tag 区)——慢路径写 tag+指针,快路径不碰 tag。 + +**最终 M1 表示**(实现者按此细化): +- 潜在多字变量(慢路径可达 = 本函数有 add/sub 且变量为其结果链)→ 栈帧附带 tag 字节 + 值槽 64(快值或 2-limb 堆指针) +- 慢路径:jo → 内联修正代码(数学值 = r10 环绕 + OF? ±2^64 判定——用 jo 后 r10 与 CF 重建高 limb 数学正确)→ 分配 16B 堆(arena)→ 写 2-limb → 值槽存指针 + tag 置位 +- 消费者(后续 add/sub/return)须读 tag:tag=0 快值直用;tag=1 解指针 2-limb(M1:慢路径结果再参与算术 = 需 2-limb 算术原语——**M1 范围**:慢路径结果只允许 return/比较(不再次算术)?——限制丑陋——真 2-limb add 原语 = M1 必要(2-limb+快值混合 add)——实现者按最小正确:2-limb add/sub 例程(值 = a+b 数学:低+低 进位链 + 高+高)——放 rt.s 或内联例程) + +M1 完整性边界(实现者执行时若超界披露):add/sub 结果超 64 → 2-limb 表示正确 → 后续 add/sub/比较/return 正确处理 2-limb → 打印(int_str)超 64 值正确(M1 含?——不含则 return 大值 exit 码截断仍「正确」(低 8 位)——打印多字 = M2)——**M1 验收 = 运算链正确(最终值数学正确——经 return 低 8 位或测试钩子验证全 128)** + +--- + +### Task 1: tagged 槽框架(潜在多字变量识别 + tag 区 + 栈布局) + +**Files:** opt.cr(识别 + tag 区布局)、instr.cr/elf.cr(槽偏移)、测试(**执行偏差**:识别 + 布局实现在 corearch 侧 instr.cr(mw_setup_tags)/elf.cr——opt.cr 不在 corearch 构建闭包;论证见 Task 1 报告 §2 与 Task 5 节执行注记) + +- 识别:函数内 add/sub 的 dest 变量链 = 潜在多字(保守:所有 add/sub dest + 传播到使用它的变量?——M1 保守 = 函数内有 add/sub 则哪些变量 tagged——实现者定最小正确集并论证) +- tag 区:栈帧附加(每 tagged 变量 1 字节)——g2_slot 布局扩展 +- 测试:tag 区布局正确(无 tag 程序栈布局不变 = 快路径零变化) + +### Task 2: 快路径溢出检测发射(add/sub + jo → 慢路径) + +**Files:** instr.cr、sizes.cr(sz 同步)、测试 + +- add/sub 发射后 jo(rel8)→ 慢路径块;sizes 同步 +- 测试:溢出触发(构造 2^63 附近 add)反汇编/行为 + +### Task 3: 慢路径 + 2-limb 表示运行时 + +**Files:** 慢路径代码(内联例程 instr.cr 或 rt)+ 2-limb 堆(arena)+ tag 写 +- jo 后数学修正(OF 判定符号/进位) +- 2-limb add/sub 例程(慢值再运算) +- 测试:溢出值数学正确(测试钩子验全 128) + +### Task 4: 消费者适配(读 tag:return/比较/后续算术) + +**Files:** instr.cr return/比较发射、tag 读路径 +- return 大值:exit 低 8 位正确(现机制天然)+ tag 清理 +- 比较:2-limb 比较(先高 limb 后低) +- 测试:溢出值比较/链式运算正确 + +### Task 5: 分配器适配(D6:tagged 变量只栈) + +**Files:** opt.cr alloc_registers(执行注记:plan 此节沿 Task 1 的偏差——识别实现于 +corearch 侧 instr.cr(opt.cr 不在 corearch 构建闭包,跨进程需 .ccr 通道), +Task 5 消费点 = 发射侧 g2_slot/get_reg_for_var,非 opt.cr——见 Task 1 报告 §2) +- tagged(潜在多字)变量排除寄存器分配(只栈)——CAG 分配器加标记输入 +- 测试:O2 自举 byte-identical + 溢出程序 O2 正确 + +**执行记录(Task 5 完成,2026-09-07)——定案:不改,D6 排除不实施** +- 现状核实(实证):O2 下 tagged 变量**确实**被 CAG 分配寄存器——含运行时 2L + 态(循环携带值、参数行、双 2L 对象,驻 rbx/r12-15);O1 无寄存器分配 + (alloc_registers 门 = opt_level≥2,opt.cr optimize_all)→ O1 tagged 恒只栈。 +- 裁决依据:D6 原始动机(多字值不可驻单寄存器)被最终 D1a 表示消解(槽/寄存 + 器驻留 = 快值或 2-limb 堆指针,128 位载荷在堆对象,tag 在帧字节);值读写 + 单 seam g2_slot + tag 单 seam g2_tag_off,reg 形态状态完整(jo 慢路径回存 + e2_mov(dst_reg,rax) / 2L 块装载 / return 解指针全经 g2_slot);表路径 preflight + 已拒 reg 形态变量。排除零收益(无独立发射路径可删)有代价(tagged = 最常见 + add 循环行)。 +- 测试加固:test_mw_task5.py——O2 元断言(oracle tagged ∩ REG_ASSIGN ≠ ∅ 于 + 运行时 2L 溢出函数 + 16B 通道行为同用例链接)+ O1 元断言(REG_ASSIGN = 0)+ + 三类用例 O0/O1/O2 全绿。将来翻案改只栈 → O2 元断言红(须同步更新并附新证据)。 +- 注记同步:spec int-multiword-backend-design §6(D6 条目)+ instr.cr mw_setup_tags + 消费方接口注释(D6 定案)。 + +### Task 6: 验证 + 回归 + 文档 + +- 溢出程序套件(构造边界值:i64max+1 链、负数、混合快慢) +- 快路径零变化回归全套 + O2 自举 +- TODO/spec 状态更新(M1 完成、M2 挂账:mul/div/比较多字、打印、D4 常量多字、动态增长、D1c 静态免 tag) + +**执行记录(Task 6 收官,2026-09-07)——M1 完成** +- **边界套件** `tests/selfhost/test_mw_task6.py`(8 用例 × O0/O1/O2 = 24 组全绿): + task3/4 已覆盖 CF/op 判别矩阵与消费者判别,本套件补**边界值组合**——e1 i64max+1 + 恰好一步越界;e2 上边界三步越界链 + 降级回 i64max + 再越界(fits 判据 lo 符号位 + 往返);e3 下边界邻域(−(2^63−1)+−1 = −2^63 **快值不溢出**——负 OF 只发生在 < −2^63, + 字面量 −2^63 被 lexer 拒故须此构造)+ add/sub 连续越界 + 回升降级;e4 sub 形式 + (i64max − −1);e5 上下边界同函数双站 + 2L vs 2L 高 limb 判别;e6 i64max 边界 + fast↔2L 比较真值表(LT/GT 双分派 + EQ/GE 假性 −10 哨兵);e7 −2^63 快 vs + −2^63−1 2L 同 hi 低 limb 无符号判别;e8 循环携带越上边界。期望值 = Python 独立 + 计算(limb_bytes),16B 通道验全 128 + exit 数学低字节。 +- **收官全量回归**(本任务一次性全套):mw task1-6 + hit_table + compile + + backend_bootstrap(stage1→3 byte-identical——快路径零变化最强确定性证明)+ + ccr_v6 + region_cfg + slice_bounds + `corec check src/compiler`——全绿 + (各任务期间各自回归绿之外,收官全量在案)。 +- **文档同步**:本 plan(状态行 + 本执行记录)、spec(§8 M2 挂账 + 状态行)、 + TODO.md(新节「int 多字 M1(2026-09-07 M1 完成 → M2 挂账)」)。 +- **验收报告**:`.superpowers/sdd/mw-m1-task-6-report.md`(M1 六任务全链证据)。 + +**M2 挂账(M1 边界外清单——详见 spec §8)**:mul/div 溢出链与 2L 参与;2L 值打印 +(int_str 超 64);D4 超 64 编译期常量(IR_CONST 64 位槽 = 格层表示,需 v6 常量段 +——与 dex 精度同族);超 128 码域动态增长(现报编码层错);D1c 静态区间免 tag +(保守 tagged 集每变量行 1 字节栈开销 + jo/块字节的静态代价);共享块/模板压缩 +(jo/2L 块 ~45-160B/站点函数尾冷区线性增长);表(HIT)模式 × 运行时 2L 组合验证 +(现 tagged 路径整条落旧路径——语义正确、组合未验);arena 生命周期债(2L 对象 +跨 arena_reset);return 全 128 内部传递(现确定性截断低 64);UOP_NEG −2^63 +环绕链;出逃写/跨函数 2L(全局/堆/数组/调用实参返回值)。 + +--- + +## 已知偏差/风险(评审注意) + +- jo 慢路径的数学修正(环绕 + ±2^64)正确性是核心——评审重点 + → **已验**(Task 3):sbb 推演闭式(add hi=CF?−1:0 / sub hi=CF?0:−1)+ 8 万+ + 随机样本零偏差 + CF/op 判别矩阵(m1-m6)+ 边界对 brute-force;编码级经 + task2 jo rel32/task3 块发射逐字节核对。 +- M1 保守 tagged 集可能宽(性能)——静态精化 = M2(D1c) + → **代价已记录**(Task 1/3/4):tag = 每 tagged 变量行 1 字节栈开销(帧公式 + 无 tag 程序逐字节不变)+ 每站点 jo 6B + 函数尾块 ~45-160B 冷区线性增长; + 精化(区间证明免 tag 免检测)= M2(D1c)。 +- 2-limb 堆对象 arena 归属(函数 arena reset 时机——慢路径分配的生命周期) + → **已核**(Task 3/4):慢路径经标准 alloc 双路——无激活 arena(纯算术作用域, + sg_alloc_total=0 → arena 不建)落全局 bump = 进程生命期;arena 激活时与周围 + 分配同 chunk 同生命周期。含分配作用域 + 2L 跨 reset = M2 债(spec §8)。 +- return 大值验证只能低 8 位(exit 码)——全 128 验证需测试钩子(扩展 dump 或专用断言通道) + → **已解**(Task 3):测试钩子 = syscall3(1,1,x,16) 原始 16B 通道(x 槽 = 2L + 对象指针的直接可观测面,零 runtime 改动)+ spec §5 return 出逃边界句(M1 内 + return-2L 到内部调用方 = 确定性截断低 64;全 128 return = M2)。 diff --git a/docs/superpowers/plans/2026-09-07-hit-m2.md b/docs/superpowers/plans/2026-09-07-hit-m2.md new file mode 100644 index 00000000..0095fc75 --- /dev/null +++ b/docs/superpowers/plans/2026-09-07-hit-m2.md @@ -0,0 +1,437 @@ +# HIT M2 实施计划(运行时核事件集 + 全表驱动收口) + +> **状态:待执行(2026-09-07 计划定稿)** +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 表模式从「直线数据子集」升级为可运行完整 Core 程序(含函数调用/控制流/系统边界)的真实后端路径,并把 instr.cr 硬编码发射器数据化收口——「表 = 唯一平台依赖」形态达成,验收 = 表模式自举(corec 经表模式编译自己成功运行)。 + +**Architecture:** 运行时核 9 事件(4 数据事件保持 + jump/branch/call/ret/extern 控制/调用/系统事件);投影模板语言 schema v2(前缀/REX/opcode/ModRM/disp/imm/rel32/两步序列);lower_to_core 全 op 覆盖(直通优先、4 核合成兜底、标签→事件序回填);发射收口 = 单指令编码形态入表、序列算法保留(prologue/参数拷贝/2L 慢路径 = 模板解释器代码调模板);运行时条目最小描述 + x86 ABI 一档。执行分四段 M2a-d(依赖序强,每段可独立提交评审)。 + +**Tech Stack:** Core 自举编译器(src/arch/hit/hit.cr + lower_to_core.cr + core-x86.toml、src/arch/linux/ld/instr.cr + elf.cr、src/compiler/corearch.cr)、Python 测试。 + +**设计 spec:** `docs/superpowers/specs/2026-09-07-hit-m2-design.md`(方案拍板 + 节 1-3 逐节确认 + M2a-d 分段——本计划按段展开任务;执行时 spec 为字段/接口的权威定义源)。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2,git 被 hook 拦截)。每任务 `jj commit -m ''`;不动 bookmark +- 构建 `nice -n 19 python3 build_selfhost_native.py`(每次改动后重建,约 2-3 分钟;只改 corearch 侧也可全量);测试 `nice -n 19` 前缀(铁律 #6) +- **行为等价硬约束**:旧路径(无 --table)产物在任何阶段零变化——数据化/模板迁移判据 = 同源程序旧路径 vs 表路径 ELF **逐字节一致**(M1 判据延续);回归面 = mw1-6 + live_ranges + ccr_v6 + region_cfg + compile + backend_bootstrap + hit_table(11) + slice_bounds + bootstrap 三套 +- 表模式恒 O0 全栈发射(spec §7.3 拍板——优化 = 格形态层;O2×表组合恢复挂账 M3);`--table` × `--link/--shared` 拒绝保持(M1 偏差 #2,extern 重定位解锁 = M2d 专项前不放开) +- 每次任务验证 = 构建 + 该任务判据 + hit_table 回归(表路径专项套件);段末 = 全量回归 +- IR op 全集 / 函数边界(REG root span)/ 事件流现状 = 源码真源(ast.cr 常量、ccr_io.cr 段布局、hit 层实现);Task 0 盘点先行,后续任务按盘点清单执行 + +--- + +## Task 0: 盘点(形态族 + 表化边界 + 降低面)——计划锚点 + +**Files:** 分析产出(无代码改动) +- 读 `src/arch/linux/ld/instr.cr` 全 2381 行:分类全部 e2_*/sz_* 函数族——(a) 单指令编码形态(REX/ModRM/SIB/disp/imm/rel 组合清单——schema v2 字段集按此收敛,spec §3 字段若有缺在此补);(b) 序列算法(prologue/epilogue/参数拷贝/2L 慢路径块/调用点序列——数据化边界外,清单标注);(c) IR op → 发射块映射全集(表直通候选清单) +- 读 `src/arch/hit/hit.cr` + `lower_to_core.cr` + `core-x86.toml`:事件流/池/回填机制现状(M2a 扩展的接缝——M1 seam 入口 = hit.cr `hit_event_lookup`/`hit_proj_step` + lower_to_core.cr `hit_ev_map_cnt`/`hit_ev_map_start`,后续任务 brief 按名引用) +- 读 `elf.cr` 发射循环 + 函数帧布局(g2_slot 公式/2L tag 区/join 块回填):控制流/调用事件与帧的交互面 +- 产出清单(三张表):(1) 形态族清单 → schema v2 字段定稿确认;(2) 序列算法清单(保留代码边界);(3) IR op → 事件/合成映射初稿(直通/合成/保留旧路径三分类——M2b 直通表与 M2c 迁移块划分依据) +- 交付:清单并入本 plan 附注(文末「Task 0 盘点结果」节),M2a-c 任务按清单执行;差异(spec §3 字段缺形态)→ 字段扩展先于 Task 2 + +--- + +## M2a 段:事件集 + 模板语言 v2 + +### Task 1: core-x86.toml schema v2 + 表加载扩展(hit.cr) + +**Files:** +- Modify: `src/arch/hit/core-x86.toml`(4 数据事件模板迁移到 v2 字段 + 控制/调用/系统事件条目 5-9 声明——先以「形态占位未实现 → 加载通过、发射拒绝」两阶段落地) +- Modify: `src/arch/hit/hit.cr`(记录布局/解析扩展:多 proj 条目、step 新字段、runtime 小节) +- Test: `tests/selfhost/test_hit_table.py`(追加 v2 加载 walker + 字段解析断言) + +**Interfaces:** +- Produces: `fn hit_proj_count(event_slot: int) -> int`(事件 proj 形态数);`fn hit_proj_step_count(event_slot: int, proj_i: int) -> int`;`fn hit_proj_step_get(event_slot: int, proj_i: int, step_i: int, out: ptr) -> int`(out 指向 v2 step 记录——字段布局 = spec §3 字段序;返回 0 = 成功、-1 = 越界);`fn hit_runtime_get(key: string, out: ptr) -> int`(运行时条目查询,runtime 小节字段化) +- 旧接口 `hit_proj_step`(M1 单 proj 单步)迁移到新接口之上或保留别名(hit 调用方 = emit_instr_tabled/测试——按 Task 0 盘点实际调用面定,文档同步) +- 验证:表加载(事件数含 9 + 直通可选)、字段解析逐项、缺事件/坏字段拒绝;corearch `--table` 加载打印事件计数变化测试同步更新 + +- [ ] **Step 1:** 盘点结果定稿 schema v2 字段集(Task 0 清单表 1;spec §3 字段缺形态在此补齐并回填 spec) +- [ ] **Step 2:** 写失败测试(v2 加载 walker:解析 core-x86.toml v2 全字段;坏文件拒绝;事件 5-9 存在) +- [ ] **Step 3:** 跑测试确认失败(hit.cr 未支持 v2 字段) +- [ ] **Step 4:** 扩展 `hit.cr` 记录布局与解析(事件/步记录加宽 + proj 多条目 + runtime 小节);toml 解析按 v2 键读取;旧字段兼容读取(M1 表形态 = v2 子集) +- [ ] **Step 5:** 更新 `core-x86.toml` 到 v2(4 数据事件迁移 + 事件 5-9 声明 + runtime 小节 x86 档)——事件 5-9 的模板先声明真实 x86 投影(jump = E9 rel32;branch = cmp + jcc 序列;call = E8 rel32;ret = C3;extern = E8 + 外部重定位标记),但发射器未支持前 `emit_instr_tabled` 对 5-9 返回拒绝(同 M1 'needs more events' 通道) +- [ ] **Step 6:** 构建 + 测试绿(加载/字段/拒绝面) +- [ ] **Step 7:** 提交 `feat: HIT schema v2——投影模板语言扩展 + 运行时核事件声明(M2a Task 1)` + +### Task 2: 模板解释器扩展——新形态字段发射 + +**Files:** +- Modify: `src/arch/linux/ld/instr.cr`(emit_instr_tabled 侧:按 v2 step 字段发射;多 proj 形态选择;rel32 回填登记) +- Modify: `src/arch/linux/ld/elf.cr`(回填表:事件流内部目标/函数起点/外部符号三表——登记点与回填时机) +- Test: `tests/selfhost/test_hit_table.py`(每新形态定向对照:同源程序表路径 vs 旧路径逐字节) + +**Interfaces:** +- Consumes: Task 1 `hit_proj_step_get`/`hit_proj_count` +- Produces: `fn emit_instr_tabled` 扩展(支持字段:prefix/rex{w,r,x,b}/opcode≤3/modrm 双角色/rm_mode 0-2/disp/imm/rel/cond——逐形态一个对照测试);回填登记 `fn hit_rel_add(kind: int, pos: int, target: int)`(kind: 0=事件流位置、1=函数索引、2=外部符号序) +- **事件流注入测试通道**(本任务新增——模板对照与降低器解耦的关键):corearch 隐藏 flag `--hit-events-file `——文本事件流文件(`--dump-events` 输出的逆格式:每行 `ev dst= s1=[pool]n s2=[pool]n` + 池行)→ 构造 g_hit_ev_stream/g_hit_pool,跳过 lower 直接 emit。测试专用;真实构建路径不启用(与 --inject-* 通道同哲学)。此后 Task 3/6 的对照与 dump 断言全部经此通道(无需降低器先支持) +- 形态选择规则:proj 条目顺序 = 优先序;发射前按操作数形状(槽寻址=rm_mode1/2、寄存器对=0;branch 双目标=rel 两角色)选首个可匹配 proj;无匹配 → 拒绝('no projection for shape') +- 验证:数据形态扩展(disp/imm——经注入通道构造)+ jump 事件逐字节对照;错误面(坏 rex 组合/越界 opcode)拒绝 + +- [ ] **Step 1:** 写失败测试:注入通道读文件 → 跳过低 lower 发射(jump 事件程序 vs 旧路径对照——现无通道无 v2 发射 → FAIL) +- [ ] **Step 2:** 实现注入通道(文本解析 = dump-events 逆格式;~40 行;corearch 接线同 --dump-events 三态 dispatch) +- [ ] **Step 3:** 实现 v2 step 发射(每字段按 instr.cr 现有编码辅助——REX 位组装/ModRM/SIB 复用旧路径函数,Task 0 清单表 2 标注可复用族) +- [ ] **Step 4:** 实现 rel32 回填登记 + elf.cr 回填(事件流内部目标先落地;函数/外部目标接口留空登记——Task 4/5 消费) +- [ ] **Step 5:** 对照测试绿(注入通道:jump + disp/imm 形态)+ 旧路径回归零变化 +- [ ] **Step 6:** 提交 `feat: 模板解释器 v2 形态 + 事件流注入测试通道——REX/ModRM/SIB/disp/imm/rel(M2a Task 2)` + +### Task 3: branch/call/ret/extern 定向模板对照 + +**Files:** +- Modify: `src/arch/linux/ld/instr.cr`(cond 角色发射——cmp/test 序列 + jcc;call/ret 模板;extern 重定位登记) +- Modify: `core-x86.toml`(事件 6-9 模板细化:branch = cmp(cond 槽,rm 形态按操作数) + jcc 序列;call/ret/extern 定稿) +- Test: `tests/selfhost/test_hit_table.py`(每事件定向对照:含分支程序/含调用程序/ret/extern 拒绝面) + +**Interfaces:** +- Produces: 事件 6-9 完整发射;`hit_rel_add(kind=2)` 外部符号 → 与旧路径 ext_rel 表同源(M2d 解锁 --link 的前置登记面;本任务 extern 发射 = 仅静态下可解析符号?——**M2 拍板:extern 模板先落地登记,`--table`×`--link` 解锁 = M2d 组合验证专项(Task 17),此前 extern 程序在 --table 下拒绝**) +- 验证(全部经 Task 2 注入通道——不依赖降低器):branch(条件 + 双目标回填)、call/ret(无参函数——**call 事件语义 = 跳转至函数事件流位置 + ret 返回;参数传递/栈序列 = Task 5 降低器配套,不在本任务对照面**)、extern(登记面——发射到 ext_rel 表;`--table`×link 解锁 = Task 15) + +- [ ] **Step 1:** 写失败测试:branch(条件分支含双目标回填,经注入通道)逐字节对照 +- [ ] **Step 2:** 实现 branch 发射(cond 槽装载 + cmp + jcc 双目标——模板序列 step1=cmp step2=jcc;rel 双角色回填) +- [ ] **Step 3:** 对照绿 + 回归 +- [ ] **Step 4:** 写失败测试:call/ret(无参函数,注入通道)逐字节对照 +- [ ] **Step 5:** 实现 call(E8 rel32,目标 = 函数事件流位置,Task 2 回填 kind=1 消费)/ret(C3) +- [ ] **Step 6:** 对照绿 + 回归;extern 登记面测试(拒绝保持——解锁 Task 15) +- [ ] **Step 7:** 提交 `feat: 控制/调用事件模板——branch 双目标 + call/ret + extern 登记(M2a Task 3)` + +### M2a 段末验收 + +- [ ] hit_table 全绿 + 每事件定向对照绿 + 旧路径回归零变化(compile/ccr_v6/mw1-6 快子集) +- [ ] 提交(如段内已逐任务提交则跳过);plan 状态行 M2a 完成 + +--- + +## M2b 段:降低层全 op(控制流/调用/合成完备) + +### Task 4: 降低器控制流——标签映射 + 事件流回填 + +**Files:** +- Modify: `src/arch/hit/lower_to_core.cr`(IR_LABEL/JUMP/BRANCH → jump/branch 事件;标签 → 事件流位置表;函数序 → 事件流段排布 + call 目标回填) +- Test: `tests/selfhost/test_hit_table.py`(控制流程序 end-to-end:if/while/嵌套——行为 exit 断言 + 混合过渡态对照) + +**Interfaces:** +- Produces: `fn hit_label_pos(id: int) -> int`(IR label id → 事件流位置;降低期填表);lower_to_core 对 IR_JUMP/IR_BRANCH/IR_LABEL 不再拒绝;两遍法(遍 1 记位置、遍 2 经 Task 2 回填登记写 rel 目标) +- Consumes: Task 2/3 rel 回填(kind=0 事件流内部) +- 验证:if-else/while/break/continue 程序 exit 正确(行为判据——控制流无旧路径逐字节可比?**有**:同源程序旧路径 ELF exit 相同 + 关键跳转位置 dump 断言(--dump-events 扩展显示 rel 目标事件序)) + +- [ ] **Step 1:** 写失败测试:if/while 程序表模式 exit 断言(现拒绝 'needs more events' → FAIL) +- [ ] **Step 2:** 实现标签→位置两遍降低 + jump/branch 事件生成 + 回填 +- [ ] **Step 3:** 测试绿(if/while/break/continue/嵌套) +- [ ] **Step 4:** 提交 `feat: 降低层控制流——标签映射 + jump/branch 事件 + rel 回填(M2b Task 4)` + +### Task 5: 函数调用降低——call/ret + 参数/返回序列 + +**Files:** +- Modify: `src/arch/hit/lower_to_core.cr`(IR_CALL/IR_RETURN 值 → call/ret 事件 + 参数传递序列事件化;callee 帧布局 = 现有 g2_slot 全局 var 行语义) +- Modify: `src/arch/linux/ld/elf.cr`(函数事件流段排布 + call 目标回填 kind=1 消费;callee 事件流 = .ccr 函数序展开) +- Test: `tests/selfhost/test_hit_table.py`(调用程序:带参/返回值/递归/多层——行为断言) + +**Interfaces:** +- Produces: call 事件语义落地——参数拷贝序列(实参槽 → callee 参数槽,按 x86 ABI 一档栈/寄存器传参由运行时条目参数化,Task 14 前 = 现行 ABI 固定序列)、返回值槽约定、callee-saved 保存(复用旧路径 prologue 算法——Task 0 清单表 2 标注的序列算法,表模式下以「序列模板」调用同一算法函数) +- 自举面注意:corec 自用 IR_CALL 密集(递归/多态包装)——Task 5 完成后函数调用语言面即通 +- 验证:fib(10)=55 表模式(旧路径对照 exit);递归/参数多档程序 + +- [ ] **Step 1:** 写失败测试:fib 表模式 exit 55(现 call 拒绝/无序列 → FAIL) +- [ ] **Step 2:** 实现调用降低(参数序列 + call + 返回序列 + ret)——序列算法复用旧路径函数(Task 0 清单表 2) +- [ ] **Step 3:** 测试绿(fib/参数/递归/多层调用)+ 旧路径回归 +- [ ] **Step 4:** 提交 `feat: 降低层函数调用——call/ret + 参数序列(M2b Task 5)` + +### Task 6: 合成规则补齐 + 直通表 + +**Files:** +- Modify: `src/arch/hit/lower_to_core.cr`(数据 op 合成规则全表 + 直通优先查询) +- Modify: `core-x86.toml`(数据直通扩展事件声明——add/and/or/xor/not/mul/div/cmp/shift 等 x86 直通投影) +- Test: `tests/selfhost/test_hit_table.py`(合成 vs 直通双路径对照:同一 op 合成表 vs 直通表产物——直通 = 扩展表加载时生效、合成 = 仅 4 核表) + +**Interfaces:** +- Produces: `fn hit_synth(ir_op: int, dst/s1/s2: int) -> int`(合成规则:返回 0 = 已合成(事件流已改写)、-1 = 不可合成(拒绝));直通查询 = 事件 id 10+ 存在 → 直发(hit_event_lookup 已有);nand 真语义 = and+not 两步序列(Task 2 序列发射消费);const 池负值/宽值(hit_pool_intern 扩 16B 槽 + 借位链——M1 偏差 #3 落地) +- 规则覆盖清单(Task 0 清单表 3 定稿):add 反减已有;and/or/xor/not/移位/比较/mul/div/select 按 IR op 全集逐项落规则或直通 +- 验证:位运算程序(& | ^ ~ << >> 语义 = Core 源无这些运算符?——**按 M1 偏差 #0:IR OP_AND 等无源码载体,规则以 IR 层测试载体(合成事件流 dump 断言)验证**);const 负值/宽值程序(M1 偏差 #3 载体——hit_pool_w64 借位链 + 16B) + +- [ ] **Step 1:** 写失败测试:合成规则直通表加载 + 事件流 dump 断言(add 反减链/and→nand 两步) +- [ ] **Step 2:** 实现合成规则表 + 直通优先查询(lower_to_core 数据 op 分支重构——Task 0 清单表 3) +- [ ] **Step 3:** nand 两步序列模板落地(core-x86.toml + 发射)+ const 池负值/宽值 +- [ ] **Step 4:** 测试绿 + dump-events 断言 + 回归 +- [ ] **Step 5:** 提交 `feat: 降低层数据 op 完备——合成规则全表 + 直通优先 + nand 真语义 + 池负宽值(M2b Task 6)` + +### Task 7: suite + 自写覆盖全绿(混合过渡态验收) + +**Files:** +- Create: `tests/hit/m2_*.cr` 自写覆盖(控制流/调用/内存/打印/位运算组合) +- Test: `tests/selfhost/test_hit_table.py`(suite 表模式跑批:编译 tests/suite/*.cr → --table → exit 断言对照旧路径) +- Modify: `src/arch/hit/lower_to_core.cr`(残余拒绝面按 suite 实证清点——仍有 op 拒绝 → 补规则或显式挂账) + +**Interfaces:** +- Consumes: Task 4-6 全部 +- 验证:suite 全集(tests/suite/*.cr 编译 + 表模式运行 exit == 旧路径);自写覆盖组;syscall3/extern 面(suite 用 print/syscall → extern 拒绝面冲突?——**处置:suite 跑批分两级:纯数据/控制程序全跑;extern 程序(io 族)在 M2d extern 解锁前跳过并清单记录**)——验收判据 = 非 extern 面 suite 全绿 + +- [ ] **Step 1:** 跑批脚本(suite 编译 → 双路径 exit 对照;extern 面跳过清单) +- [ ] **Step 2:** 清点拒绝面(未覆盖 op 清单 → 补规则/挂账;挂账 = 注释 + TODO 条目) +- [ ] **Step 3:** 全绿 + 回归 +- [ ] **Step 4:** 提交 `feat: M2b 验收——suite 混合过渡态全绿 + 拒绝面清点(M2b Task 7)` + +### Task 8: 自举冒烟(混合态先行) + +**Files:** +- Test: `tests/selfhost/test_backend_bootstrap.py` 或新 `tests/selfhost/test_hit_bootstrap.py`:corec 源码经表模式编译出 ELF → 运行冒烟(打印/编译小文件自验) +- Modify: `src/compiler/main.cr`(`build` 命令 `--table` 透传——M1 挂账「corec build 透传 --table」落地;`-O` 表模式恒 0 强制) + +**Interfaces:** +- Produces: corec `build --table `(透传给 corearch --table);表模式自举产物 = 混合态(未覆盖 op 落旧路径——Task 0 清单表 3 的「保留旧路径」类) +- 验证:表模式编译 corec 源码 → 产物可运行(打印版本串/编译一个冒烟 .cr);extern 面(io 密集)依赖 M2d 解锁——**自举冒烟先行判据 = 能跑通「corec check/run 小样本」或打印级冒烟;全功能自举 = M2d 终验** + +- [ ] **Step 1:** corec build --table 透传 + -O 强制 0 +- [ ] **Step 2:** 自举冒烟脚本(表模式编译 corec → 运行冒烟样本) +- [ ] **Step 3:** 清点混合态残留(自举语料中落旧路径的 op/序列清单——M2c 数据化优先级输入) +- [ ] **Step 4:** 提交 `feat: M2b 自举冒烟(混合态)+ corec --table 透传(M2b Task 8)` + +### M2b 段末验收 + +- [ ] suite 非 extern 面全绿 + 自举冒烟绿 + 拒绝面清单更新;hit_table + 全量回归快子集 +- [ ] plan 状态行 M2b 完成 + +--- + +## M2c 段:发射数据化收口 + +### Task 9: 形态族迁移第 1 批(数据直通事件族) + +**Files:** +- Modify: `core-x86.toml`(直通事件模板由 Task 6 占位升级为完整 v2 形态——按 Task 0 清单表 1 分批) +- Modify: `src/arch/linux/ld/instr.cr`(对应形态族的旧硬编码块改为读表——**每族迁移 = 删旧块前先以「双源对照」验证**:表模板产物 == 旧块产物逐字节,再删旧块) +- Test: `tests/selfhost/test_hit_table.py`(每族对照用例 + dump 断言) + +**Interfaces:** +- Consumes: Task 0 清单表 1(形态族分块)+ Task 2 解释器 +- 验证:每块迁移 = 旧路径 byte-identical(全套 compile 回归逐块绿);表路径产物不变 +- 批次划分(示例,按 Task 0 清单定稿):imm 族/寄存器对算术族/disp 槽寻址族/SIB 索引族/cond 族/rel 族 + +- [ ] **Step 1:** 批 1 族清单 + 双源对照测试(写失败测试:表模板 vs 旧块逐字节) +- [ ] **Step 2:** 模板定稿(core-x86.toml 批 1 条目) +- [ ] **Step 3:** 解释器读表发射批 1 形态 + 旧块删 +- [ ] **Step 4:** 对照绿 + compile/backend_bootstrap 回归 +- [ ] **Step 5:** 提交 `refactor: 形态族批 1 数据化(族名)——byte-identical 对照(M2c Task 9)` + +### Task 10-12: 形态族迁移批 2-4(同 Task 9 模式,每批一提交) + +- [ ] 批 2(按 Task 0 清单):提交同上模式 +- [ ] 批 3:同上 +- [ ] 批 4:同上 +- 每批判据:双源对照绿 + 旧路径 byte-identical + 全量回归;批次总数与边界以 Task 0 清单表 1 为准(4 批为上限估计,清单定稿可增减——增减时同步本计划状态行注记) + +### Task 13: 汇合——emit 单路径(混合模式消灭) + +**Files:** +- Modify: `src/arch/linux/ld/instr.cr`(emit_instr 分发 → 表查询 + 模板解释器;旧路径 e2_* 残留族删净——Task 0 清单表 2 的「序列算法」族保留为解释器调用的序列函数) +- Modify: `src/arch/linux/ld/elf.cr`(发射循环单路径——表模式概念消失于发射层:表 = 唯一发射真源;`--table` flag 语义 = 强制加载核心表 + 拒绝缺失事件(无表默认 = 内置 x86 表?——**拍板:无 --table = 内置默认表(编译期嵌入 core-x86.toml 数据);--table 加载失败 = 拒绝;旧「表模式/非表模式」二元性消灭**)) +- Test: 全量回归 + backend_bootstrap + 自举(汇合后产物 == 旧路径产物逐字节——最强判据) + +**Interfaces:** +- Produces: 内置默认表(embed core-x86.toml v2 数据);`--table` = 覆盖/自定义表;发射路径零分支(每 op = 表条目/合成/序列算法三选一,无「落旧路径」) +- 验证:全套回归绿;自举(混合态已冒烟)在汇合后重跑 = 行为不变;逐字节判据(backend_bootstrap stage 链) + +- [ ] **Step 1:** 内置默认表接入(无 --table 路径 = 加载内嵌数据) +- [ ] **Step 2:** emit_instr 汇合(分发 → 表;残留旧族删/转序列函数) +- [ ] **Step 3:** 全量回归 + 自举重跑 +- [ ] **Step 4:** 提交 `refactor: 发射汇合——表 = 唯一发射真源,混合模式消灭(M2c Task 13)` + +### M2c 段末验收 + +- [ ] 全量回归绿(旧路径产物逐字节不变 = 最强判据)+ plan 状态行 M2c 完成 + +--- + +## M2d 段:运行时条目 + 纯表自举收口 + +### Task 14: 运行时条目数据化 + call/ret 序列参数化 + +**Files:** +- Modify: `core-x86.toml`(runtime 小节定稿——spec §6 字段:call 机制/param 传递/save 集/extern 入口) +- Modify: `src/arch/linux/ld/instr.cr`(call/ret 序列函数按运行时条目参数化——x86 档实例;校验:事件集必备性/runtime 字段完整性) +- Test: `tests/selfhost/test_hit_table.py`(运行时条目加载 + 校验 + x86 档序列行为) + +**Interfaces:** +- Produces: `fn hit_runtime_param_mode() -> int` 等查询(序列函数分支按条目——x86 一档实现,非 x86 条目 = 校验拒绝 'unsupported runtime profile') +- 验证:调用程序行为回归;坏 runtime 条目拒绝 + +- [ ] **Step 1:** 写失败测试(runtime 加载/坏条目拒绝) +- [ ] **Step 2:** runtime 小节解析 + 校验 + x86 档查询 +- [ ] **Step 3:** call/ret 序列按条目参数化(行为不变回归) +- [ ] **Step 4:** 提交 `feat: 运行时条目数据化——runtime 小节 + 序列参数化 x86 档(M2d Task 14)` + +### Task 15: extern 解锁 + 组合验证 + +**Files:** +- Modify: `src/arch/linux/ld/instr.cr`/`ld.cr`(extern 事件重定位 → 旧路径 ext_rel 同源面;`--table` × `--link/--static` 解锁——M1 偏差 #2 清除) +- Modify: `src/compiler/corearch.cr`(拒绝分支删除) +- Test: `tests/selfhost/test_hit_table.py`(extern 程序静态/链接两形态 exit 对照;`--table` × `--link` 组合用例;HIT × 2L 组合(2L 快路径走表程序 + 慢路径块);O2 × 表拒绝保持(表模式恒 O0 注记测试)) + +**Interfaces:** +- Consumes: Task 3 extern 登记 + Task 13 汇合 +- 验证:io/print/syscall 程序(suite extern 面补跑);2L 溢出程序表模式行为 = 旧路径;组合矩阵用例全绿 + +- [ ] **Step 1:** extern 重定位接通(ext_rel 同源) +- [ ] **Step 2:** --table × link/static 组合测试绿(拒绝删除) +- [ ] **Step 3:** HIT × 2L 组合用例(M1 偏差清点) +- [ ] **Step 4:** suite extern 面补跑全绿 +- [ ] **Step 5:** 提交 `feat: extern 解锁 + 组合验证(HIT×link/2L)——M1 偏差清点(M2d Task 15)` + +### Task 16: 纯表自举收口(终验) + +**Files:** +- Test: `tests/selfhost/test_hit_bootstrap.py`(正式化:表模式编译 corec 全量 → 产物编译冒烟样本 + 自举链(stage0→stage1 表模式)) +- Modify: 清点面修复(纯表自举暴露的残余:Task 8 混合态清单逐项消除——未覆盖 op → 合成/直通补齐;序列算法残留确认在边界内) + +**Interfaces:** +- Consumes: 全部 +- 验证:corec(经表模式编译的产物)成功编译冒烟程序并运行正确;stage 链 byte 判据(表模式自举产物 vs 旧路径自举产物行为等价);全量回归 + +- [ ] **Step 1:** 纯表自举脚本(全量 corec 表模式编译) +- [ ] **Step 2:** 清点修复(混合态残留 → 补齐,循环直至零残留编译) +- [ ] **Step 3:** 自举链验证(stage0→1 表模式) +- [ ] **Step 4:** 全量回归 +- [ ] **Step 5:** 提交 `feat: 纯表自举收口——表模式编译 corec 全链绿(M2d Task 16)` + +### Task 17: 文档收尾 + M1 偏差清账 + +**Files:** +- Modify: `docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(§2 事件模型修订回填:运行时核 9 事件表述;§6 M2 状态完成 + M2a-d 记录;§3.3 运行时条目) +- Modify: `docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md`(偏差四项清账注记:nand 真语义 ✓/混合模式消灭 ✓/常量池负宽值 ✓/--link 解锁 ✓) +- Modify: `TODO.md`(HIT 节 M2 挂账逐项划除;新挂账:表模式 O2 恢复(发射器汇合后自然解锁)挂 M3、第二张表 M3) +- Modify: `docs/superpowers/specs/2026-09-07-hit-m2-design.md`(状态:实施完成;执行记录回填) + +- [ ] **Step 1:** 文档更新 +- [ ] **Step 2:** 全量回归终跑 +- [ ] **Step 3:** 提交 `docs: HIT M2 收官——spec/TODO/M1 偏差清账(M2d Task 17)` + +### M2d 段末验收(= 计划终验收) + +- [ ] 纯表自举绿(corec 经表模式编译自己成功运行 = 用户拍板验收锚) +- [ ] 全量回归绿 + 文档清账;plan 状态行全部完成 + +--- + +## 风险注记(执行评审) + +- Task 0 盘点产出驱动 M2a schema 字段与 M2c 批次数——盘点不实 → 后段计划漂移(段内以清单为准,段界差异回填本计划状态行) +- 自举为终极验收:混合态冒烟(Task 8)先行降险;纯表终验(Task 16)暴露面 = 语言全特性(IR op 全集固定——降低器按 op 全集覆盖即可,与源特性正交) +- 2L/多字 × 表组合:2L 快路径 = 表直通/合成;慢路径块 = 序列算法(Task 0 清单表 2 标注边界)——组合验证 Task 15 +- 逐字节对照判据在 M2c 最强(旧路径产物零变化);M2b 行为判据(exit 对照)宽松——控制/调用程序以 dump-events + exit 双断言 +- 表模式恒 O0:分配器/发射器表化后 O2 解锁 = M3 挂账(不扩本计划) + +--- + +## Task 0 盘点结果(执行时回填) + +> 盘点完成(2026-09-07)。基源 = instr.cr 全 2381 行(e2_*/sz_* 全部函数族)+ elf.cr 发射循环/回填循环 + hit 层(hit.cr/lower_to_core.cr/core-x86.toml)+ ast.cr IR op 全集(编号 0-51,op 40 空号 = 51 个 op)。M2a-c 任务按下列三张清单执行;schema v2 字段差异已回填 spec §3(清单 1 节末「判定汇总」)。 + +### 清单 1:形态族 → schema v2 字段定稿(单指令编码原子;M2c 批次划分依据) + +盘点最小单位 = **单指令编码形**(表 step 原子)——旧路径「载入→运算→存回」等多指令块 = 事件的 step 序列(模板级序由事件定义,原子 = 本清单)。每族:x86 形 → instr.cr 现状发射点(函数/段)→ 1-2 IR op/类示例 → schema v2 覆盖判定(✓ = 现有字段可表;补 = 需精确补字段,编号见「判定汇总」)。sizes.cr sz_* 族 = 各形族尺寸函数(sz_mov/sz_ld/sz_st/sz_li/sz_lr/sz_lrb/sz_lb/sz_call/sz_jmp/sz_je/sz_alu/sz_jo/sz_syscall 等,与 e2_* 一一对应)——随族迁移删/改,不单列;编码基元 e2_w8/w16/w32/w64 + emit_rex/emit_modrm/emit_sib = 字节拼装工具(模板解释器复用同款——保留代码,非形态族)。 + +**[A] 寄存器对算术族(mod=3 双寄存器 + REX.W/R/B 变体)** +- **A1 定对 ALU**:src2→r11 居 modrm.reg、dst→r10 居 rm(REX.WRB = 4D、modrm(3,3,2),3B 定长);单字节 opcode 0x01 add/0x29 sub/0x21 and/0x09 or/0x39 cmp——`e2_alu`;例 IR_BINARY OP_ADD/SUB/AND/OR、EQ..GE(cmp 段);✓(M1 sub 模板同形;角色 = 事件 dst/src2) +- **A2 三字节 opcode ALU**:imul r10,r11 = 4D 0F AF DA——emit_instr MUL 段;✓(opcode ≤3 已有) +- **A3 移位/乘除定式**:C1 /7 sar r10,imm8(PTR_DIFF 除 8);D3 /4|/5 可变移位(先 mov rcx,r11 = 4C 89 D9——rcx 隐含计数);6B /r imul r11,imm8(PTR_SUB 缩放 8——4D 6B DB 08);F7 /3 neg;DIV/MOD = e2_mov rax,r10 + cqo 48 99 + F7 /7 idiv r11 + 结果搬运(rax/rdx:rax 隐含对)——emit_instr 各段;补①(隐含/字面量寄存器:rcx 计数、rax/rdx 对) +- **A4 任意寄存器对移动**(mod=3,REX.W/R/B 按 8 位寄存器号自由组合——ABI 寄存器 rdi/rsi/rdx/rcx/r8/r9/rax × r10/r11)——`e2_mov` + IR_CALL syscall3/4 参数预置链/DIV-MOD 结果搬运;补①(寄存器字面量角色——调用点预置序列本身 = 清单 2 R6) + +**[B] imm 定值族** +- **B1 imm32→槽/reg**(C7 /0,mod 01/10/11 三形,符号扩展 64 位;超界走 B2)——`e2_li`、IR_ARENA_NEW 的 mov rax,-1(48 C7 C0);例 IR_CONST 快值;✓(imm size 4 + /digit 补②) +- **B2 movabs imm64→reg**(B8+r 系:48 B8 rax、48 BF rdi、48 B9 rcx、49 BA r10、49 BB r11——IR_FNADDR、alloc 尺寸、OOM -1、ptr 检查常量)——`e2_li` 超界支路、`e2_movabs_rdi`、IR_FNADDR/IR_DEREF 检查段;补③(imm64 回填 kind——函数绝对地址 fnaddr_patch) +- **B3 字节 imm 定值**(C6 /0 [rbp+disp8/32],tag 置 1)——`e2_mw_b8`;✓(imm size 1 + /digit 补②) + +**[C] disp 槽寻址族(帧内槽 = [rbp+disp],disp8/32 按槽偏移幅度选——回填规则与旧路径一致)** +- **C1 64 位槽 load/store**(8B/89,[rbp+disp8/32],dest/src 任意 reg 含调用参数寄存器——REX.R 随 reg)——`e2_ld`/`e2_st`/`e2_load_var`(IR_LOAD/STORE 快值面、IR_CALL 参数面、IR_BINARY 载入段);✓(rm_mode 1 = rbp+disp32 形 M1 已有——M1 固定 disp32 = 规则④ auto 的退化实例);规则④(disp.size auto:按槽回填偏移幅度选 1|4——解释器规则,与旧路径 e2_ld/e2_st 选择一致) +- **C2 8 位字节槽 op**(tag 区:F6 /0 test byte [rbp+off],imm8、C6 /0、8A/88 byte mov——8B/88 族 W=0 形)——`e2_mw_t8`/`e2_mw_ld8`/`e2_mw_st8`/`e2_mw_tag_clr`/`e2_mw_tag_cpy`/`e2_mw_oc_check`(test 面);✓(rex.w=0 已有——rex 记录缺省 = 无 REX,w:0 显式可表);补②(/0 digit) + +**[D] 非 rbp 基 + disp 载荷族(mod 01/10,rm = 槽值寄存器——r10/r11/rdi,B 位随基)** +- **D1 载荷存取 [reg+disp8/32]**:8B/89 64 位、0F B6 movzx/88 字节(field/enum tag/const 下标——idx*8 或字节 idx 并入 disp/do 槽 +8)——emit_instr IR_LOAD_FIELD/STORE_FIELD/LOAD_ENUM_TAG/LOAD_INDEX/STORE_INDEX/IR_DYN_TAG/DYN_VAL/MAKE_ENUM 的 C7 [r10+0];补⑤(rm_mode 1 泛化 base_role——缺省 rbp,可 = 槽值寄存器(r10 等),mod=10 rm=reg 形) + +**[E] SIB 索引族(scale × index + base;base/index = 槽值或 ABI 寄存器)** +- **E1 SIB 寻址/载荷**:lea 8D r10,[r10+r11*8](IR_ADDR_INDEX)、8B/0F B6/89/88 SIB(scale 0/1/3,[rdi+rsi]、[r10+r11*8] 等——IR_LOAD_INDEX_VAR/STORE_INDEX_VAR/内置体 load8/store8/load64/w64/load_str_ptr)——emit_instr 各段 + IR_CALL 内置体;补⑥(rm_mode 2 需显式 SIB 字段组 {scale, index_role, base_role}——现 schema 仅 rm_mode 枚举无字段) +- **E2 get_arg 内联循环**(SIB rdi*8 索引 + 字节拷贝循环 + alloc call + rip-patch lea)——IR_CALL get_arg 内置体 = 序列算法(清单 2 R6;内含 E1 原子,循环 = 算法) + +**[F] rip 基族(全局/池/rodata 引用——mod=00 rm=101 与 mod=01/10 基 r10 变体)** +- **F1 lea reg,[rip+rel32]**(4C 8D 15/1D——dest r10/r11)——`e2_lr`/`e2_lrb`(IR_CONST str → rodataref、IR_RETURN 全局、IR_LOAD/STORE 全局面);补⑦(base_role=rip——mod=00 rm=101 形) +- **F2 池/rodata/全局 load/store [rip+rel32]**:池 mov = 4C 8B 15 disp32(mod=00 rm=101,M1 模板字节)——`hit_ev_emit_pool_mov`(g_hit_pool_patch 回填);全局访问(emit_instr IR_LOAD/STORE 面)= F1 lea 先导 + [reg] 间接存取(4D 8B 12 等 mod=00 rm=reg 形——D1 同形)或 lrb+st 两步;补⑦(表化 = load/store 事件 rip 基 proj——M1 池 mov 现为硬编码模板字节,同族归并) + +**[G] cond/rel 族** +- **G1 cond 结果原子**:cmp + setcc(0F 9x al)+ movzx r10d,al(44 0F B6 D0——EQ..GE 六向 sop = 94/95/9C/9F/9E/9D);test 85 + sete(UOP_NOT);2L 块内 xor/or/mov eax 布局原子——emit_instr EQ..GE/NOT 段 + `e2_mw_opnd_block`;✓(cond {role} + seq 两步——cmp/test 序列 = 事件 seq 表达);补⑧(rel8 = 序列块内短跳原子,表化非强制) +- **G2 rel 定长形**:rel32(E9/E8 5B;0F 84/85/83/80 系 6B——je/jne/jae/jo)——`e2_jmp`/`e2_call`/`e2_je`/`e2_jae`/`e2_mw_jo`;rel8 jcc(74/75/7C/72 等 + skip 偏移 imm 字面量 2/3/5:ptr 检查 je+2、tag deref je+3、cmp 链 jl 7C 05)——各检查/2L 块内;✓(rel {role, kind} 已含 rel32 三角色 = 事件流/函数/外部符号);补⑧(rel.size {8|32}——rel8 序列块内原子,表化非强制) + +**[H] 栈调整/系统原子(仅出现在序列算法内——表外裁定组)** +- **H1** push/pop(55/5D、53/5B rbx..r15、41 54-5F、push r10 = 41 52、push imm 6A)、add/sub rsp imm8/32(81/83 EC/C4——prologue 尺寸回填 g_x86_sub_rsp_pos、调用清理 131/129)、ret C3、ud2 0F 0B、syscall 0F 05、push_xmm0(sub rsp,8 + F2 0F 11 04 24)——elf.cr prologue/epilogue、IR_CALL 清理、检查块;判定:仅存在于序列算法内(R1/R6/R9)→ 表条目非必需,保留为序列函数字节(spec §5 边界同款;Task 13 汇合时终裁) + +**schema v2 判定汇总**(✓ 已覆盖 = A1/A2/B1/B3/C1(disp32 形)/G1(cond)/G2(rel32 三角色)/F2(池 mov);补字段与解释器规则 = ①②③④⑤⑥⑦⑧——回填 spec §3,Task 1 Step 1 落地): +① 寄存器字面量/隐含角色(rcx 移位计数、rax/rdx:rax idiv/cqo 对、ABI 寄存器——A3/A4/movabs 目标);② /digit 操作码扩展 + 无 ModRM 标记(C6/C7/F6/F7/D3/C1 的 reg 域数字;E8/E9/C3/0F05/0F0B 无 modrm——modrm 字段可缺 + reg_role 可为数字);③ imm64 回填 kind(movabs 函数绝对地址——fnaddr_patch 同款);④ disp.size auto(按槽回填偏移幅度选 1|4——与旧路径 e2_ld/e2_st 同规则,解释器规则);⑤ rm_mode 1 泛化 base_role(缺省 rbp;D 族 [槽值寄存器+disp] 载荷形);⑥ SIB 字段组 {scale, index_role, base_role}(rm_mode 2 细化——E 族);⑦ base_role=rip(mod=00 rm=101——F 族池/全局/rodata);⑧ rel.size {8|32}(G2 短跳原子)。 + +**M2c 批分**(4 批 = 计划 Task 9-12 上限内;每批 = core-x86.toml 直通条目 + 解释器步支持 + 旧块「双源对照」删——判据同 Task 9): +- **批 1(Task 9——数据直通事件族)**:A 族 + B 族 + C 族(ALU/imm/槽寻址 = 直通数据事件模板原子集——x86 大表数据事件条目全量声明的原子来源;含 load/store 事件槽形延续) +- **批 2(Task 10——cond/rel 族)**:G1 + G2(控制面单指令原子;branch/jump/call/ret/extern 事件模板 Task 1-3 已建——本批 = 旧 IR_BRANCH/JUMP/LABEL/RETURN 发射块迁模板解释器 + g_label_* 回填对接) +- **批 3(Task 11——寻址扩展族)**:D 族 + E 族(依赖补⑤⑥字段——Task 1 Step 1 先补后行) +- **批 4(Task 12——收尾族 + 表外裁定)**:F 族(rip 基——补⑦后行);H 族 + SSE 面(清单 3 (e) 项)汇合前裁定(进表 or 序列函数字节/挂账) + +### 清单 2:序列算法边界(保留代码调模板) + +表化边界 = 单指令形(清单 1);下列块保留为解释器算法/序列函数(spec §5 边界 + Task 13「残留 e2_* 转序列函数」的目录)。每行:算法 | 现状 owner | 组成判定(原子 = 清单 1 族)+ O0 相容注。 + +- **R1 函数帧 prologue/epilogue**:owner = elf.cr Phase 3 函数发射序(push rbp/mov rbp,rsp/sub rsp 占位 + g_x86_sub_rsp_pos 回填、epilogue add rsp 81/83 C4 + opt≥1 六寄存器 pop 逆序);帧布局公式 = g2_slot = -(v+1-g_current_func_var_start)*8(instr.cr——无加项;mw_frame_size(vc) = 帧总字节(tag 区 + 16 对齐,sub rsp 尺寸等他用,非 g2_slot 成分);tag 字节位于槽区之下,偏移 -(vc*8+1+k);对齐规则 opt≥1 ≡8 mod 16 / O0 ≡0);原子 = H1;保留(帧布局 = spec §5 明列;sub rsp 尺寸 = 体长回填算法);O0 注:六寄存器保存 = opt≥1 专属,恒不触发 +- **R2 参数拷贝(调用点 + 被调入口)**:owner = instr.cr IR_CALL 参数分派(int 6 档 rdi/rsi/rdx/rcx/r8/r9 + xmm 8 档双编号 ir_cnt/fr_cnt;栈参右到左压 + 清理 add rsp 131/129)+ elf.cr 参数保存循环(caller_off = 16+(pi-6)*8 [opt≥1 +40]、强制 po2 槽、xmm frn 计数、tagged TI_INT 参数 tag 清、O1+ 寄存器实参重载);保留——Task 14 前 = 现行 ABI 固定序列,后按 runtime 条目 call/param 参数化(§6);O0 注:实参重载恒不触发;槽公式恒帧偏移形(无 E2_REG_SLOT_BASE 哨兵参与——哨兵仅 opt≥2 元数据面) +- **R3 2L jo 慢路径块**:owner = instr.cr `e2_mw_jo`(0F 80 rel32 记录)+ `e2_mw_slow_block`(sbb/not 判 hi → push r10/r11 → mov edi,16 + alloc call → 2L 槽写 [rax]/[rax+8] → tag=1 C6/0 → E9 rel32 resume)+ elf.cr 块发射与 rel32 回填(g_x86_mw_jo_*);保留(spec §4:溢出慢路径块 = 序列算法);原子 = B3/G2/H1 表化候选 +- **R4 2L 操作数块(oc-check 消费面)**:owner = instr.cr `e2_mw_oc_check`(test tag + 0F 85 rel32)+ `e2_mw_opnd_block`(双/单 tagged rel8 分派、128 位 add/adc/sub 链、fits-64 = sar63+cmp+je、alloc+2L 存储、EQ/NE xor/or/sete、LT..GE = cmp hi 有符号 jl/jg + cmp lo 无符号 jae/ja/jbe/jb + mov eax,1 布局、44 0F B6 movzx)+ elf.cr g_x86_mw_oc_* 块发射/回填;保留;块内含原子:`e2_mw_ld2` = 2L 载荷 mov [rax+0/8](D1 形)、`e2_mw_sext` = mov hi,lo + C1 /7 imm8 63(A3/A4 原子);注:2L 快路径进位链原子(add/adc/sbb)走表 = spec §4「2L 快路径走表」落地通道——x86 大表声明进位事件(批 1 A 族扩展,M2b Task 6 直通声明时定) +- **R5 tag 卫生与静态标记**:owner = instr.cr `mw_setup_tags`(规则 A/B/B' 定点扫描 = 分析算法,elf.cr Phase 2/3 每函数跑)+ `mw_oc_new`/`e2_mw_oc_check`(oc 标记点)+ 4 定义点卫生(BINARY 定值后清、LOAD/STORE tag_cpy、RETURN deref、参数入口清——`e2_mw_tag_clr`/`e2_mw_tag_cpy`);保留;原子 = C2 +- **R6 调用点序列 + 内置体**:owner = instr.cr IR_CALL 块 + IR_CALL_EXTERN 块——内置体:syscall3/4(A4 预置链 + 0F 05)、load8/store8/load64/load_str_ptr/store_str_ptr/w64/r64(E1 SIB mov)、get_arg/_dyncpy(内联字节循环 + alloc call + rip-patch lea——循环 = 算法)、goroutine_wrapper_addr(B2 + fnaddr_patch)、generic(call_patch/ext_rel + E8);保留——内置 → extern/内部符号事件化 = M2d 清点(id 9 事件 + 内部符号回填表);原子 = A4/B2/E1/G2 +- **R7 return 序列**:owner = instr.cr IR_RETURN 块(dex → sd_load;全局 → rip 取;常规 → e2_ld rax + tagged deref test+je+3 + mov [rax])+ `e2_store_ret` + elf.cr ret_patch 循环(jmp → epilogue 定向回填);保留——ret 事件化后 = 返回值槽协议序列(Task 5);O0 注:2L 返回 deref 恒在 +- **R8 常量池/rodata/rip 回填**:owner = instr.cr `e2_lr`/`e2_lrb` + `hit_ev_emit_pool_mov` + elf.cr 回填循环(rodataref 补丁、rodata 布局 = g2_str_off/g2_rodata_sz(8B 头 + pad8)、HIT 池 g_hit_pool_patch(rel = cp+k*8-(pp+7))+ w64 写、bss_va/rip_patch lea_check 校验);保留 = 布局/回填算法——M2b 事件流 rel 回填 = 同族新算法(作用域 = 事件流字节;先例 = g_label_poses/g_pending 单遍回填);原子 = F1/F2 +- **R9 越界/空指针检查序列**:owner = instr.cr `e2_ptr_bounds_check`(lea rax + movabs rcx 48 B9 + cmp + e2_jae + test + jne rel8 + ud2 回填)/`e2_ptr_null_check`(test r10,r10 + jne +2 + ud2)+ IR_BOUNDS_CHECK 段(movabs 49 BB 或动态 load r11 + cmp + 72 jb +2 + ud2);保留(语义插入面;块内原子 = B2/G2/H1——整面事件化 = M3 挂账候选,Task 13 裁定) +- **R10 运行时 stub 发射体(call 事件目标 = 内部函数体,非事件流内容)**:owner = elf.cr `emit_alloc_body`(+simple)/`emit_heap_expand`(mmap 直 syscall)/`emit_sched_call`+sched_tramp_sz/`emit_curg_stubs`(11B)/`emit_goroutine_stubs`(fiber_init 20B/fiber_switch 27B/entry_wrapper + call patch)/`emit_m_start_workers`(clone 发射器)/`_init_globals`(6B 桩)+ arena_new/arena_reset 后备桩(arena.cr 未导入时——str_intern 注册函数偏移)/`emit_start`+emit_start_size(argc/argv、全局 const 初始化 lea+C7/movabs、call main、exit syscall)/`bss_init`(rep stosb F3 AA);保留——M2d runtime 条目(allocator/save 集)数据化后体仍代码 +- **R11 回填/补丁循环族(布局算法)**:owner = elf.cr g_label_poses/g_pending(BRANCH/JUMP rel32 单遍回填——函数级 label 状态随 g2_init 节拍重置)、ret_patch、call_patch(函数体偏移——IR_HOTPATCH_ROUTE/SPAWN/generic)、fnaddr_patch(TEXT_BASE+pos movabs)、rip_patch、rodataref、jo/oc rel32(g_x86_mw_jo_*/oc_*)、alloc_patch(emit_alloc_body 偏移 fixup);保留——数据化后 = 事件流回填 + 函数起点表消费端(Task 4/5 两遍法 + 三张回填表) +- **R12 函数级 label 扫描先例**:regalloc.cr stage 2 回边扫描(IR_LABEL s1 → 指令位置 lab_id/lab_ps 收集 + label_pos_of over 函数 span + 16B 回边记录)——事件流标签映射(Task 4)与旧 rel32 回填共享「label id → 位置」先例;保留 + +**O0 相容总注**:表模式恒 O0 → E2_REG_SLOT_BASE 哨兵/get_reg_for_var/alloc_registers 元数据恒不触发,全槽 = rbp+disp 帧偏移形(C 族全覆盖);O1+ 专属序列(R1 六寄存器保存、R2 实参重载)恒不执行——代码路径保留完整(非表模式回归面零变化);tagged/2L(R3-R5)与 opt 无关,O0 表模式下整条 gating 语义不变(mw_int_arith_jo_needed 现行门在汇合前原样保留)。 + +### 清单 3:IR op → 事件/合成映射初稿(ast.cr 编号 0-51 全分类;51 个 op + 空号 40) + +分类号:(a) 直通候选(数据事件 id 10+,x86 形 = 清单 1 族);(b) 4 核合成兜底(仅 4 事件表时——spec §4 规则,M2b Task 6 规则表);(c) 控制/调用事件(id 5-9,模板 Task 1-3 落地);(d) 序列算法(owner = 清单 2 行);(e) 保留旧路径候选(行内给汇合裁定/挂账条件——Task 13 前逐项消灭或挂账)。 + +| IR op | 现状发射(owner/形) | 初稿分类 | 载体/注 | +|---|---|---|---| +| IR_NOP(0) | 0 字节(emit_instr 空返) | (d) 表外零字节直通——降低器跳过 | 编译期占位 | +| IR_CONST(1) | e2_li(B1/B2);str → rodataref + e2_lr(F1) | (a) 直通 const 事件(imm 直载 B1、宽值 movabs B2;str/负值/宽值 → 池 load F2——M1 通道 + 16B 借位链 = M1 偏差 #3 载体,M2b) | 64 位快值恒;M1 现走池 load 事件 | +| IR_BINARY(2) TI_INT 快值面 | e2_alu + 各段(A1/A2/A3;EQ..GE = cmp+setcc+movzx G1;DIV/MOD = mov+cqo+idiv 隐含 A3) | (a) 直通数据事件:ADD/SUB/MUL/AND/OR/XOR/SHL/SHR/PTR_ADD/PTR_SUB/PTR_DIFF/EQ..GE/DIV/MOD——x86 大表全量声明(= M2c 批 1 产物);无直通表兜底 (b):ADD 反减(M1 已有)、AND = nand 两步、OR 德摩根、EQ = sub+符号判别、NOT = sub(1,x)、const 负宽值 | 注:M1 偏差 #0——OP_AND/OR/XOR/SHL/SHR/MUL/DIV/MOD 部分无源码运算符载体 → 合成规则以 IR 层测试载体(事件流 dump 断言)验证(Task 6 同款) | +| IR_BINARY(2) tagged/2L 面(add/sub/cmp 涉 tagged——mw_int_arith_jo_needed 现行整条 gating) | jo 慢路径(R3)+ oc-check/2L 块(R4)+ tag 卫生(R5) | (d) 慢路径/块保留;快路径 64 位原子 + jo rel32 = 表化候选;2L 快路径进位链走表 = spec §4 通道(adc/sbb 事件声明) | tagged/2L 多字(M1 面) | +| IR_BINARY(2) TI_DEX 面 | SSE2(F2 0F 58/5C/59/5E;66 0F 2F comisd + setnp/sete + and/or 双 setcc;xmm0/1/rn;movsd 系) | (e) 保留旧路径至 Task 13 汇合裁定:进表需 ① xmm0-7 字面量角色(可表化);或显式挂账 M3——理由 = 自举语料面零浮点(Task 8 冒烟实证;suite 浮点程序在覆盖面则表化优先) | float 源(TI_DEX) | +| IR_UNARY(3) | NEG = F7 /3(A3);NOT = test+sete+movzx(G1) | (a) 直通 neg/not 事件;兜底 (b) NOT = sub(1,x) | UOP_REF/DEREF(opkind 3/4)不达发射面(盘点注记——同 IR_PHI) | +| IR_CALL(4) | 参数分派 + E8 + 清理(R2)+ 内置体(R6) | (c) call 事件(目标 = 函数索引——REG root span 行序;回填 kind=1 函数起点表)+ (d) R2/R6 序列 | 调用面(核心;Task 5 起覆盖) | +| IR_RETURN(5) | 值序列(R7)+ ret_patch | (c) ret 事件(id 8)+ (d) R7(返回值槽协议 = Task 5 定) | 返回面 | +| IR_ALLOC(6) | 0 字节 | (d) 表外零字节——bump 在运行时函数体(= R10 的 alloc call 面) | 编译器自用 | +| IR_ALLOC_STRUCT(7)/IR_ALLOC_ARRAY(8) | movabs rdi 尺寸 + alloc call + st(R10;ARRAY = s1*8 溢出 → movabs -1 OOM) | (d) R10 调用序列——call 事件化候选(目标 = 内部符号 alloc_code_off,回填表 2) | alloc = 运行时函数 | +| IR_LOAD(10)/IR_STORE(9) | local:e2_ld/e2_st + tag 卫生(C1/C2/R5);global:lr+mov / lrb+st(F1/F2) | (a) local = load/store 事件(id 3/4——M1 槽形 rm_mode 1 已有)+ tag 面 (d) R5;global = F 族 rip 基 proj(补⑦后表化候选;否则 (d) lr+mov 两指令序列) | 快值/字符串;global 面 M1 表外(hit_ev_slot_addr_ok 限 local) | +| IR_LOAD_FIELD(11)/IR_STORE_FIELD(12) | 8B/89 [reg+disp32](D1) | (a) load/store 事件非 rbp 基 proj(补⑤)+ 对象指针载入 (d) 先导 | 结构体字段 | +| IR_LOAD_INDEX(13)/STORE_INDEX(14)(const 下标) | 8B/0F B6/88 [r10+disp32](idx 定标并入 disp——D1) | (a) D1 同款 proj + (d) 指针先导 | 数组/字符串定标访问 | +| IR_LOAD_INDEX_VAR(15)/STORE_INDEX_VAR(16) | SIB scale3 / scale0 字节(E1;STORE 侧 3-load 先导) | (a) SIB proj(补⑥)+ (d) 先导序列 | 运行时下标 | +| IR_MAKE_ENUM(17) | alloc call + C7 [r10+0] tag 定值(R10/B1) | (d) R10 + tag 定值原子 | 枚举构造 | +| IR_REF(18) | lea r10,[rbp+off](4C 8D 65——e2_lb) | (a) 直通候选(lea = load 事件地址 proj——C 族同形扩展) | 引用取址 | +| IR_BRANCH(19) | test 85 + e2_je(0F 84 rel32 → s3 假支)+ e2_jmp(E9 → s2 真支)+ g_label_pending(R11) | (c) branch 事件 id 6(cond 序列 + 双 rel32——IR_BRANCH s1 条件/s2 真/s3 假天然双路;Task 2/3 模板已建) | 控制流核心 | +| IR_JUMP(20) | E9 rel32 + pending | (c) jump 事件 id 5 | 无条件转移 | +| IR_LABEL(21) | 0 字节 + g_label_poses 位置记录 | (c) 锚——标签 → 事件流位置(Task 4 两遍法;旧单遍回填 R11 保留至汇合) | 函数级 label id(s1);regalloc 回边扫描同源先例 | +| IR_PHI(22) | 无发射(emit_instr 落空;dump/dataflow/ptr_analysis/regalloc 显示用) | 表外:后端不可达(ir_gen 不产——盘点注记;Task 6 全 op 覆盖清单不含) | 结构/图分析 | +| IR_LOAD_ENUM_TAG(23) | 8B [r10+disp32 0](D1) | (a) D1 同形(tag 载荷 = field 特例) | 枚举判型 | +| IR_SLICE(24) | ld + shl r11,3(A3)+ add(A1)+ st | (d) 序列(原子 = A 族表化候选) | 切片构造 | +| IR_DEREF(25)/IR_STORE_PTR(26) | 检查序列(R9)+ 8B/89 [r10](D1) | (d) R9 + 载荷原子;ptr 语义面事件化 = M3 挂账候选(Task 13 裁定) | 指针解引用/写 | +| IR_SPAWN(27) | 参数拷贝 + call_patch + goroutine 包装(R6/R10) | (d) R6 同族——call 事件化候选(内部符号 goroutine 包装;spec §4:接运行时函数表达) | 自用面(自举语料) | +| IR_YIELD(28)/IR_AWAIT(29)/IR_LAZY_THUNK(46)/IR_LAZY_FORCE(47) | 槽拷贝(条件式 d≥0 && s1≥0) | (d) 表外槽拷贝序列(原子 = load+store 事件对或帧内 mov——事件化候选;零字节门面同旧路径) | 自用面(协程/惰性——自举语料) | +| IR_BOUNDS_CHECK(30) | movabs 49 BB 或动态 load r11 + cmp + 72 jb +2 + ud2(R9) | (d) R9(语义插入面;事件化 = M3 挂账候选) | 越界防护 | +| IR_ADDR_INDEX(31) | 2 ld + lea SIB r10,[r10+r11*8](E1) | (d) + lea SIB 原子表化候选(补⑥) | 数组取址 | +| IR_ARENA_NEW(32)/IR_ARENA_RESET(33) | s1>0 → call arena_new(R10);s1<0 → mov rax,-1(48 C7 C0 = B1 reg 形)+ st;RESET = ld rdi + test rdi,rdi + 7C 05 跳过链 + call arena_reset | (d) R10(内部函数调用;test/jl = G2 原子) | arena 内存模型 | +| IR_INLINE(34)/NO_BOUNDS_CHECK(35)/FAST(36)/UNROLL(37)/SECTION(38)/APPROX(51) | 0 字节 | (d) 表外零字节(降低器跳过——同旧路径 0 字节语义) | 编译期属性标记 | +| IR_HOTPATCH_ROUTE(39) | E8 call_patch(R6/R11) | (d)(call 事件化候选——hotpatch 路由内部符号) | 自用面(热补丁) | +| 空号 op 40 | 无常量 | — | ast.cr 编号间隙(盘点注记) | +| IR_DYN_TAG(41)/IR_DYN_VAL(42)/IR_DYN_PACK(43) | ld [do+8] / ld [do] / st + C7 tag 定值(D1/B1) | (a) D1 load 事件 proj + (d) do 指针载入/tag 定值(R5 同族) | dynamic 面(编译器自用) | +| IR_DYN_DISPATCH(44) | cmp imm8 链 ×3(83 /7 + je rel8)+ xor r10d + jmp + case_common 块 + 补丁(G1/G2) | (d) 比较链序列(原子 = cond 形表化候选;块序保留) | dynamic 面 | +| IR_CALL_EXTERN(45) | 参数 + ext_rel E8(R6) | (c) extern 事件 id 9(外部符号回填表 3——复用 ext_rel 重定位;M2d 解锁 --link/--shared 前拒绝保持) | 系统边界 | +| IR_FNADDR(48) | movabs r10 49 BA + fnaddr_patch(B2) | (d) R11 回填 + movabs 原子(imm64 函数地址回填 kind——补③) | 函数指针 | +| IR_I2F(49)/IR_F2I(50) | SSE 转换(F2 48 0F 2A/2C xmm0,[rbp+off] + st / sd_load 先导) | (e) 同 TI_DEX SSE 面(行内裁定同款——自举语料零浮点则 M3 挂账,否则 ① xmm 角色后表化) | float 转换 | + +**映射要点注记**:① (a) 类直通事件 = M2b Task 6 直通表与 M2c 批 1 的共源——Task 6 先以「占位模板 + 事件流 dump 断言」落地,批 1 以完整 v2 形态 + byte-identical 对照收口;② (e) 类全部在 Task 8 自举冒烟时实证语料面(清点混合态残留——计划 Task 8 Step 3 输入);③ tagged/2L 相关 op 的盘点口径 = 表化边界在快值 64 位原子与 tag 字节原子(C2/G2),块序 = 清单 2 R3-R5 不动;④ IR_PHI/UOP_REF/UOP_DEREF/空号 40 = 非发射面项,Task 6「全 op 覆盖」清单须显式排除(写测试防回归)。 diff --git a/docs/superpowers/plans/2026-09-07-regalloc-backend.md b/docs/superpowers/plans/2026-09-07-regalloc-backend.md new file mode 100644 index 00000000..a8cfaa75 --- /dev/null +++ b/docs/superpowers/plans/2026-09-07-regalloc-backend.md @@ -0,0 +1,215 @@ +# 寄存器分配移后端实施计划 + +> **状态:全部 Task 完成(2026-09-07)**——Task 1-6 执行记录见文末;设计决策 +> 拍板(R1a / R2=D-1=Y / R3 / R5 / D-3)与受波及面处置见 specs/ +> 2026-09-07-regalloc-backend-design.md(状态:设计定稿 + 实施完成)。 +> 执行偏差:Task 2/3/4 因 corec 闭包互锁(数据面摘除 ↔ 落盘废止 ↔ 通道迁移 +> 同批编译)合并为单一切换提交——纯搬移提交先行(可独立评审),切换提交内 +> 按原 Task 顺序自查(见执行记录)。 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 寄存器分配(数据面 + CAG 分配 + 判定)从 corec(opt.cr)移至 corearch——编码层资源决策归位后端;.ccr opt_meta REG_ASSIGN 不再跨进程传输;「纯机械映射」表述取消。 + +**Architecture:** 按层拆 opt.cr(R1a):语义优化(CSE 等)留 corec;编码决策(compute_live_ranges/compute_entries/共存/alloc_registers/verify)迁 corearch(新模块)。分配结果不再入 .ccr(R2)——corearch load 后自算(输入 = v6 ENT 段内存态 + IR)。判定 verify 随分配移 corearch O2 自检(R3)。corec `-O` 门语义调整(R5:O1=CSE 留 corec;O2=分配在 corearch)。 + +**Tech Stack:** Core 自举编译器(src/compiler/opt.cr 拆分、src/arch/linux/ld/ 新 regalloc 模块、ccr_io.cr)、Python bootstrap。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2)。提交 `jj commit -m`;不动 bookmark +- 构建 `nice -n 19 python3 build_selfhost_native.py` 每次改动重建;测试 `bash -c 'ulimit -c 0; ...'` +- **行为等价硬约束**:迁移期/完成后 O2 产物行为与现状一致(分配决策等价——后端自算与 corec 算同结果;O2 自举 byte-identical 语义:同版本确定性——跨版本产物可不同(格式简化),测试判据 = 行为/exit + 自举可运行) +- 语义层零改动(corec 前端/IR 语义不变——只 opt 层拆分) +- 设计定稿:`specs/2026-09-07-regalloc-backend-design.md`(R1a/R2/R3 拍板 + R4/R5 注) +- 现状关键:分配真相源 = g_opt_meta OPT_KEY_REG_ASSIGN(.ccr SYM opt_meta 段传输);后端 g2_slot 负编码消费;判定 regalloc_verify_all 在 main.cr O2 路径;多字 tagged 识别已在 corearch(instr.cr) +- 测试:回归 compile/ccr_v6/region_cfg/live_ranges/hit_table/mw1-6/backend_bootstrap + corec check + +--- + +### Task 1: opt.cr 按层切分盘点(语义 vs 编码归属清单) + +**Files:** 分析产出(无代码改动——切分清单文档) +- 读 opt.cr 全 1939 行,分类所有函数:语义优化(AST/IR 优化——留 corec)/ 编码决策(活区间/条目/共存/分配/判定——迁 corearch)/ 共享工具(grow/buffer——随迁或复制) +- 产出切分清单(函数 → 归属 + 依赖图——迁入 corearch 的函数依赖哪些 corec 侧符号/全局——依赖也要迁) +- 交付:清单文档(.superpowers 或 plan 附注),Task 2+ 按清单执行 + +### Task 2: corearch 分配模块迁入(行为等价双跑验证) + +**Files:** 新建 src/arch/linux/ld/regalloc.cr(数据面+分配+判定迁入)+ corearch 闭包接线 + opt.cr 侧停用(O2 门不再调 alloc_registers) +- 迁移顺序:数据面(compute_live_ranges/entries/共存)→ 分配(alloc_registers + meta)→ 判定(verify)——依赖全局表(g_ir_*)corearch load 后已有 +- 触发点:corearch load 后、emit 前(O2 时)——替代 corec 传输 +- 验证:O2 行为等价(后端自算 vs 原 corec 算——迁移期间可双跑对照 exit/产物行为)——后端分配结果 = corec 原结果(逐变量 reg 对照——过渡期测试) +- test_mw_task5 哨兵(REG_ASSIGN 元数据通道)——corearch 侧同进程消费(tag 表直接可读) + +### Task 3: .ccr opt_meta 出格式(R2) + +**Files:** ccr_io.cr(save/load SYM opt_meta 段移除或空)+ test_ccr_v6/region_cfg walker 更新 +- REG_ASSIGN 不再落盘(corearch 自算)——opt_meta 段处理(全出?仅 REG_ASSIGN 出?——若 opt_meta 仅此用途则段整体移除——盘点确认) +- ENT home 字段处置(分配回填 = corearch 内存态——落盘恒 -1?或字段保留——决策执行时按设计定稿) +- 格式版本(v6 内部再改 or version bump?——同仓同步 v6-only——内部改段即可) + +### Task 4: 判定迁移(R3) + +**Files:** verify_regalloc_consistency 等(已随 Task 2 迁)——触发点移 corearch(emit 前 O2 自检——违反 = 编译错误);main.cr corec 侧判定触发移除 +- 测试:判定红注入用例在 corearch 路径仍红(--inject-* 通道移 corearch) + +### Task 5: 表述清除 + CLI 语义(R5) + +**Files:** opt.cr/g2_slot 注释「pure mechanical translation」清除(g2_slot 改述 = 后端槽解析 seam);corec `-O` 帮助/门语义(O1 留 corec CSE;O2 分配已在 corearch——corec -O 仅影响 CSE——CLI 文案);regalloc-consistency.cr 参考实现指针更新 + +### Task 6: 回归 + 文档收尾 + +- 全套回归(含 mw1-6 哨兵更新确认、backend_bootstrap、O2 自举可运行) +- spec 状态(R1-R5 拍板记录 + 实施完成)+ TODO 挂账(如有残余) + +--- + +## 风险(评审注意) + +- opt.cr 拆分依赖图遗漏(迁入函数引用未迁符号 → 编译错——Task 1 清单完备性关键) +- 行为等价验证(迁移过渡双跑——corec 分配停用瞬间 O2 行为依赖 corearch 正确) +- .ccr opt_meta 移除的 walker/测试联动面 +- 多字 tagged 分配同侧整合(Task 5 哨兵 → corearch 内断言) + +--- + +## Task 1 执行记录(2026-09-07):按层切分盘点 + +> 交付物 = 本清单。Task 2+ 按此执行;**D-1/D-2/D-3 三个拍板点须在 Task 2 动工前向用户拍板**(计划原 R1a/R2/R3 取向已获授权,本清单把它们落实为具体选择)。 + +### 文件实测 + +- `opt.cr` 实际 **1940 行**(计划头注 1939——差 1,以实测为准) +- corearch 闭包(build_selfhost_native.py ~170-199)**不含** opt.cr/dataflow.cr/ir_gen.cr/pass.cr——含 ast.cr/globals.cr/dyn_arr.cr/ccr_io.cr/hit/*/arch/linux/ld/* + corearch.cr。→ 迁入符号凡定义于 ir_gen/dataflow 者需一并搬移或确认替代(见下 get_ir_var_name) +- iri_*/irv_type/grow_opt_meta/ESZ_ENTRY/OFF_ENTRY_* 常量与访问器均在 dyn_arr.cr(双侧闭包含)✓;g_opt_meta/g_ir_entries/g_ir_func_entry_*/g_ir_live_ranges 等在 globals.cr(双侧)✓ + +### 归属清单 + +**A 语义优化——留 corec(opt.cr 不迁)** + +| 函数(行) | 性质/依赖 | 调用点 | +|---|---|---| +| ast_is_const_int(11)/ast_const_val(17)/ast_bool_as_int(21)/ast_fold_binary(26)/ast_optimize_body(65) | AST 常量折叠(AST 层,非编码决策) | optimize_all(死码,见下) | +| pass_cse(1819) | 线性流 CSE:NOP + 引用替换(iri_set_op 等);依赖 iri_*/g_ir_func_* | **main.cr:580(O≥1)** | +| optimize_all(1917) | 编排器(ptr_analysis/region_check/provenance/AST fold/pass_cse/alloc/pass_stack_share 的 O 门聚合) | **全仓无调用者 = 死码**(main.cr 566-588 直调各 pass)。拆分时须摘除其 alloc_registers()/pass_stack_share() 引用(否则 corec 闭包内符号悬空) | + +**B 数据面(存在区间/条目)——归属随拍板点 D-1(倾向迁 corearch 单宿主)** + +| 函数(行) | 性质/依赖 | 调用点 | +|---|---|---| +| grow_live_ranges(198)/live_range_slot(205)/live_first(215)/live_last(220)/compute_live_ranges(228) | 区间表 + 逐函数扫描;尾部调 compute_entries 整表重建 | main.cr:505/514/520(cir dump 通道)、dataflow.cr:392(lower_to_ccr 尾——**无条件,.ccr ENT 段唯一来源**)、alloc_registers:1316 首行 | +| grow_entries(319)/grow_func_entry_meta(326)/ent_*(341-347)/entry_start(350)/entry_count(355)/compute_entries(360) | 版本条目表(ESZ_ENTRY 24B/条) | 同上(compute_live_ranges 尾部循环调用) | +| 依赖 | g_ir_live_ranges/count/cap、g_ir_entries/count/cap、g_ir_func_entry_*/g_ir_func_var_*/g_ir_func_instr_*/g_ir_func_count、iri_*(g_ir_instrs)、buf_read_i32(ccr_io.cr)、alloc/_dyncpy/r64/w64/w32 | corearch 闭包均有(entries 表由 load_ccr 重建:ccr_io.cr:1114-1183,含盘→内存闭区间转换 + func 段界重建 + SYM first/last_ent 对照校验) | + +**C 判定族——迁 corearch(regalloc.cr)**(含注入测试钩子) + +| 函数(行) | 备注 | +|---|---| +| entries_coexist(544)/coexist_version_conflicts(560)/coexist_home_conflicts(591) | 共存(v6 Task 3;GC-1 越界守卫在 entries_coexist) | +| rl_rec_lt(694)/rl_merge_sort(704)/rl_print_loc(755)/rl_func_name(763)/rl_report_rule1(769)/rl_report_rule2(781)/rl_rule2_func(803)/verify_regalloc_consistency(872)/regalloc_verify_all(964) | 判定规则①②(规约 regalloc-consistency.corespec);LOC_HOME_BASE/RPT_MAX 文件内常量随迁 | +| meta_reg_for_var(670)/meta_reg_pair_off(1028)/meta_set_reg(1051)/meta_remove_var(1059)/meta_reg_assign_total(1101)/meta_append_reg_assign(1115) | g_opt_meta 读写侧辅助(镜像 instr.cr:33 get_reg_for_var 布局——迁后同进程,镜像收敛为直接消费) | +| try_inject_home_conflict(984)/inject_home_conflict(1013)/try_inject_reg_conflict(1136)/inject_reg_conflict(1163)/try_inject_read_gap(1179)/inject_read_gap(1229)/inject_coexist_oob(637) | 判定红注入(cir --inject-* 测试载体;真实构建路径不注入) | +| 调用点(现状 corec) | main.cr:540(check-regalloc verify)、main.cr:587(O2 build verify 拦截);dump_coexist_summary 经 main.cr:521 | +| 迁入依赖 | get_ir_var_name **定义在 ir_gen.cr:363(corearch 闭包无)**——打印路径(rl_report_*/dump)消费;处置:随迁搬至 dyn_arr.cr(双侧共享位;dump.cr:45/dataflow.cr:460 的 corec 调用不受影响);其余依赖同 B + g_opt_meta 表 | + +**D 分配——迁 corearch(regalloc.cr)** + +| 函数(行) | 备注 | +|---|---| +| label_pos_of(1303)/alloc_registers(1313) | CAG 上下文贪心(区头注释 1242-1302 含全部语义论证——随迁);阶段 1-5:hazard 门控/回边携带/候选窗口/First Fit/meta 落块 | +| 调用点(现状) | main.cr:532(check-regalloc 强制 O2)、**main.cr:583(O2 build)**、opt.cr optimize_all(死码,摘除) | +| 迁入依赖 | B 全部(alloc 首行 compute_live_ranges()——D-1 裁决它算不算第二次)、iri_*/irv_type(TI_DEX/TI_DYN/TI_UNIT 类型门)、IR_CONST/IR_REF/IR_I2F/IR_DYN_*/IR_JUMP/IR_BRANCH/IR_LABEL/IR_STORE 常量(ast.cr 双侧)、g_opt_meta 写侧 grow_opt_meta/store8(双侧)、g_opt_level(corearch CLI --opt-level 已接,corearch.cr:38-40;corec build 已透传:main.cr:644) | + +**E 栈共享——现状产物死路径,处置随拍板点 D-3** + +| 函数(行) | 备注 | +|---|---| +| pass_stack_share(1739) | 窗口共享填 g_stack_map;**g_stack_map 不落 .ccr**(ccr_io 无序列化),corearch.cr:7 启动置 "" → instr.cr:64-67(g2_slot)消费恒不命中 → 对 ELF 产物零效果(现状空跑) | +| 依赖 | iri_*/g_ir_func_*/g_stack_map(instr.cr 侧读取在 g2_slot:64——恒空) | + +**F 调试通道——联动随拍板点 D-2** + +| 函数(行) | 备注 | +|---|---| +| ir_op_kind_name(458)/dump_entries_summary(494)/dump_coexist_summary(620) | corec `cir --dump-entries/--dump-coexist` 载体(main.cr:504-523);test_live_ranges.py 消费 | +| 判定注入族(C 组) | corec `cir --check-regalloc/--inject-*` 载体(main.cr:529-546);test_live_ranges.py 消费(含 test_mw_task5 的 REG_ASSIGN 元断言读取通道?——mw_task5 走 corec build O2 产物行为 + cir 元断言两种) | +| 说明 | corearch 已有 debug 命令先例(--table/--dump-events);通道迁移 = corearch 加等价 flag 组 + 测试改道 | + +### 现状链路(迁移基线——全部经实证/读码确认) + +``` +corec build/ccr: + ir_gen_all(线性 g_ir_instrs 与 df 平行产出) + → O≥1: pass_cse(580) [作用于 g_ir_instrs] + → O≥2: alloc_registers(583) [内 compute_live_ranges+entries;写 g_opt_meta REG_ASSIGN] + regalloc_verify_all(587) → 违反 = 编译错误 + pass_stack_share(588) [写 g_stack_map——不落盘] + → lower_to_ccr(591) [自 df 重建 g_ir_instrs(= CSE 前形态);尾 compute_live_ranges+entries(重建流)] + → save_ccr: SYM opt_meta 子节(g_opt_meta 块,key 0=REG_ASSIGN 有产物;key 1/2=STACK_SHARE/CSE 预留无写入者)+ ENT 段(28B/条,home 现状恒 -1——alloc 不回填 home)+ REG/SYM func first/last_ent 对照 +corearch: + load_ccr(g_opt_meta/entries/func 边界重建 + 校验)→ init_backend_arrays(g_stack_map="") + → elf_gen → g2_slot(54):meta 查 g_opt_meta(g_opt_level≥1 门)→ E2_REG_SLOT_BASE+reg;否则栈槽 +``` + +### 实证结论(2026-09-07 探测) + +- 样本 `a:=7;b:=3;x:=a*b;y:=a*b;return x+y` 在 `corec ccr` 三档(O0/O1/O2): + - **NOD/SYM(除 opt_meta)/ENT/REG 四段逐字节相同**;STR 段 O0 比 O1 多 108B(未定位,疑字符串 intern 序差异——与分配/CSE 无关) + - SYM 段 O2 多 984B = opt_meta REG_ASSIGN 落盘 +- **推论:pass_cse 对 .ccr 产物零效果**(lower_to_ccr 自 df 重建丢弃其 NOP/替换;df 与线性流在 ir_gen 期同步后 CSE 不再回写 df)。含义: + - R5「O1=CSE 留 corec」如实表述为:CSE 运行于 corec 进程内 pre-lower 流,不承载于产物;O1 与 O0 产物的唯一差异面 = 无(现状) + - 现状 O2 分配窗口基于 CSE 后流(alloc 在 lower 前算)而产物按 CSE 前流发射——理论缝隙存在、实证无害(O2 全套绿);**迁移后 alloc 在 corearch 基于载入流(CSE 前)计算,与产物同流自洽**——反而消除缝隙。行为等价硬约束(exit/自举可运行判据)不受影响;分配决策与现状可不逐字节同(约束已允许跨版本产物不同) + +### 待拍板点(Task 2 动工前问用户) + +- **D-1 .ccr ENT 段去留**(R2 细化——决定数据面单/双宿主): + - 方案 X(ENT 保留传输):数据面生成留 corec(lower 尾不变、cir 通道不动);corearch 分配输入 = 载入 ENT + 需自算 live ranges(盘上无 var 级区间表)→ alloc 首行 compute_live_ranges 迁入即重算 → **数据面计算双宿主**(语义漂移风险,与「归位」目标相悖) + - 方案 Y(推荐):ENT 段恒空/出格式(loader 已支持段缺失 = 空 + pcnt=0 与 SYM first/last_ent=-1 对照——**格式结构零改动、无 version bump**);数据面计算单宿主 corearch(compute_live_ranges/entries/grow/访问器迁 regalloc.cr);corec lower 尾摘除调用;save 侧 ENT 计数 0;SYM/REG first/last_ent 落 -1;「发射不依赖 ENT」(ccr_io.cr:74 已注)✓ +- **D-2 调试通道联动**(随 D-1=Y):corec `cir` 的 dump-entries/dump-coexist/check-regalloc/inject-* 载体与 test_live_ranges.py 改道 corearch(新 flag 组:--dump-entries/--dump-coexist/--check-regalloc/--inject-*,corearch load .ccr 后内存态即数据面);corec 侧分支删除。D-1=X 则通道不动(数据面留 corec),仅判定注入随判定迁移面另行处置 +- **D-3 pass_stack_share 处置**:(a)随迁并启用(行为新变:帧缩小——现状产物无共享,启用 = 行为差异风险,需独立验证);(b)停用(推荐:产物零差异,调用摘除 + optimize_all 死码同步清理;恢复 = 未来 corearch 内以 alloc 同 seam 实现,挂账);(c)留 corec 空跑(现状,不归位) + +--- + +## Task 2-6 执行记录(2026-09-07) + +### 提交线(每提交可独立评审) + +1. `refactor: regalloc 编码决策层纯搬移入 corearch`——新建 `src/arch/linux/ld/regalloc.cr`(opt.cr 189-1737 保真搬移:数据面/共存/判定/注入/CAG alloc)+ corearch 闭包接线 + `get_ir_var_name` 移 dyn_arr.cr(corearch 闭包无 ir_gen.cr——双侧共享位);行为零变化基线(build 绿 + compile/ccr_v6 回归绿) +2. `feat: regalloc 移后端切换`(Task 2+3+4 合并——互锁说明见顶部状态注): + - corearch:load 后 **O2 自算 alloc_registers + regalloc_verify_all**(emit 前,违反 = 编译错误,成功静默);表模式跳过(M1 直线路径无 O2 组合验证);调试通道随迁同名 flag(--dump-entries/--dump-coexist/--check-regalloc/--inject-* 三态 dispatch + 新 --dump-regassign 逐对输出) + - corec:opt.cr 1940→312 行(迁走段删除;留 AST 折叠 + pass_cse + optimize_all 死码摘引用);main.cr O2 段与 `cir` 调试通道摘除;dataflow lower 尾 compute 摘除 + - .ccr 出格式(D-1=Y/R2):ENT 段 0 条、SYM func 与 REG first/last_ent 恒 -1、param_ents 恒 -1、opt_meta 子节 0 块——格式结构保留(loader 空表语义已支持:pcnt==0 ↔ -1 对照),零 version bump + - 测试联动:live_ranges 通道改道 corearch(断言零变化);mw_task5 元断言改 --dump-regassign;ccr_v6 重写恒空断言(conversion/param_ents 载体消亡删/改) +3. Task 5 表述清除(本批,见下)+ Task 6 文档收尾(spec/plan/TODO,本批) + +### Task 2 验证(行为等价,切换后全锚点) + +- O2 行为等价:cse_probe exit 42、fib 55、loop-carry 20、cond-def 14/24、else-def 22/22、else-control 30/31、region roundtrip 6、ccr_v6 roundtrip 15、backend_bootstrap stage 7——全部绿(corearch 自算分配路径) +- 双跑过渡验证:切换前 corec 落盘 meta 的旧 .ccr 由新 corearch 加载仍正常(载入 meta 首匹配遮蔽自算块 = 兼容旧产物);新 .ccr 无 meta → 自算块生效 +- 与 plan 原「逐 var reg 对照」的偏差:CSE 实证无产物效果(NOD O0/O1/O2 逐字节同)→ corec meta(算于 CSE 后流)与 corearch 自算(算于载入 pre-CSE 流)窗口可不同——计划硬约束(行为/exit + 自举可运行判据)为验证判据,未做逐对恒同断言;判定自洽性反而提升(分配/判定/产物同流) + +### Task 3 验证 + +- .ccr 载荷:ENT 段恒 4B(count 0)、opt_meta 恒 0 块、func/REG first/last -1(ccr_v6 8/8 恒空断言 + walker 全绿);loader 对空表与 -1 对照零改动通过 +- home 字段:随 ENT 落盘消亡;内存表 home 保留(corearch 自算态,M1 分配不回填——判定 home 组规则保留供未来回填) + +### Task 4 验证 + +- 判定红注入在 corearch 路径仍红:test_live_ranges check_regalloc_violations(rules 1/1/2)+ read_gap_nonfunc0(rule 2 on func 1)+ coexist OOB guard——全绿(13/13);check-regalloc watchdog(regalloc-assign pairs > 0)实证自算分配真实发生 +- corec 侧判定触发(main.cr O2 段 + cir --check-regalloc)已移除 + +### Task 5 表述清除(本次提交) + +- regalloc.cr 判定区头/镜像注记(「.ccr 传输 meta」「corec 不含后端文件」等 5 处)→ corearch 进程内自算自消费表述 +- instr.cr get_reg_for_var 头注(「saved in .ccr v3+」)→ 自算填充表述 +- globals.cr 存在区间/条目表注记(填充方 opt.cr)→ regalloc.cr + D-1=Y 注 +- regalloc-consistency.cr:参考实现指针 opt.cr → regalloc.cr;负编码数据模型表述清理;缺口注记更新(pass_stack_share 停用、param_ents 测试载体消亡、GC-1 已修) +- CLI 语义(R5):corec/corearch `-O` 帮助文案更新(O1 = CSE corec 进程内;O2 = 分配+判定 corearch,corec build 透传);「纯机械映射」表述 src 侧无残留(docs/project-book 后端哲学表述 → TODO 挂账校对) +- opt.cr 文件头注释(AST-level 单一表述)→ 剩件清单 + +### Task 6 收尾(本批) + +- [x] 全套回归绿(切换提交后全量:mw1-6 + compile + backend_bootstrap stage0-2 + ccr_v6 8/8 + region_cfg 22/22 + slice_bounds 7/7 + hit_table 11/11 + live_ranges 13/13 + bootstrap 三套 + selfhost 其余);Task 5 注释改动后 build 绿 + 关键子集复查(见提交记录) +- [x] spec 状态:R1-R5/D-3 拍板记录 + 实施完成(§6/§7 更新) +- [x] TODO 挂账(残余):见 TODO.md 新节——栈共享 corearch 恢复、R4 时间分布实测、v6 格式文档 ENT/opt_meta 恒空同步、project-book 后端哲学表述校对 diff --git a/docs/superpowers/plans/2026-09-09-lattice-ir-v7.md b/docs/superpowers/plans/2026-09-09-lattice-ir-v7.md new file mode 100644 index 00000000..91b3b376 --- /dev/null +++ b/docs/superpowers/plans/2026-09-09-lattice-ir-v7.md @@ -0,0 +1,184 @@ +# v7 格形态(真图载体)实施计划 + +> **状态:实施完成(2026-09-10 Task 3 收官)**——Task 1(v7 骨架:version 7 + NOD 36B 邻接 + EDG 段必落 + 幽灵边播种修复 + CIR_CACHE_VER 14)、Task 2(ENT 实记录 compute_entries_v7 + SYM/REG 条目回填 + loader 激活)、Task 3(测试族迁移收官:test_ccr_v6.py 合并退役 → test_ccr_v7.py 23/23 全绿(V7File walker 全量 + cache-hit 恢复路径回归 + ENT 负分支补面 R1/R2)+ 全量回归 + 自举重建冒烟 + 文档同步)全部落地;报告 = `.superpowers/sdd/v7-task-*-report.md`。字节状态以 v7 格式 spec 为准(§4 规则 4/§6 开放点 3 = home/flags 接受不拒绝裁决(R3)、开放点 5 = per-param/嵌套 REG 内容锁推迟(R4)回填);v6 spec = 历史基线(取代注记 + 差异① 实现语义为准);测试期望语义 = regalloc.cr compute_entries 实现(Task 0 表二差异①)。 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** .ccr 从 v6(线性 NOD + 恒空 ENT)切换到 v7 真图载体——NOD 36B 邻接 + EDG 段(数据/state 边必落)+ ENT 实记录(corec 产时重建),全链行为不变。 + +**Architecture:** 写侧数据源现成(corec 进程内 g_df_edges/g_df_var_producer——emit() 双写图与线性 IR 1:1,从未落盘);读侧 loader 的 ENT 实记录解析/校验代码已存在(恒空走不到,激活即可)。v7-only 同日切换(corec/corearch 双端同提交,管线中间产物零持久生态);行为判据 = 切换后全量回归绿 + backend_bootstrap stage 链 byte-identical + 自举重建(与 regalloc 移后端/MW M1 判据同款)。 + +**Tech Stack:** src/compiler/ccr_io.cr(双端共享读写)、dataflow.cr(图构建)、regalloc.cr(条目计算镜像基)、Python 测试(tests/selfhost/)。 + +**设计 spec:** `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md`(语义权威)+ `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(字节权威)。执行时 spec 为字段/接口的权威定义源。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2,git 被 hook 拦截)。每任务 `jj commit -m ''` +- 构建 `nice -n 19 python3 build_selfhost_native.py`(约 2-3 分钟,改 ccr_io 后必重建);测试 `nice -n 19` 前缀(铁律 #6) +- **行为等价硬约束**:v7 链(corec 产 v7 → corearch 读 v7 → 发射)与 v6 链行为一致——判据 = 同套测试全绿(测试 = 行为断言,与格式无关)+ backend_bootstrap stage 链 byte-identical + hit_table 24/24 保持绿(表路径挂起中但测试是行为回归网) +- 版本/形状守卫密度高(写侧 ccr_validate_i32_fields + 游标守卫;读侧段表规范序 + 逐段越界 + REG root span 上界 + ENT 双写对照)——v7 改动必须保持同等守卫强度,Python 侧严格段契约 walker = 免费格式回归网(迁移后重锁) +- 图数据落盘源 = g_df_edges(内存邻接 = 链表头插——出边序为创建逆序,落盘序任意但须确定;每条边 to_nod 前向 = 拓扑不变量) +- 每任务验证 = 构建 + 该任务判据 + 回归面(mw1-6/live_ranges/region_cfg/compile/backend_bootstrap/hit_table/slice_bounds 快子集——Task 3 全量) +- interp(corec run 路径)直食 DF 节点流,不经 .ccr——v7 零影响(回归验证即可) +- CSE 时序疑点(pass_cse 位于 emit 与 lower_to_ccr 之间,lower 从 g_df_nodes 全量重建——O1 下 CSE 是否被回滚)**非本计划修复项**(v6 链行为保持 = 判据);Task 0 实测注记即可——v7 落盘源统一为 df 镜像,与 CSE 无关 + +--- + +## Task 0: 盘点核对(v7 落盘数据源 + 守卫面)——计划锚点 + +**Files:** 分析产出(无代码改动) +- 核对 g_df_edges/g_df_var_producer 完备性:df_connect_srcs 按 op 的连边语义表(dataflow.cr:195-315)对照 ast.cr op 全集(0-51)——哪些 op 无数据边(LABEL/JUMP/PHI/ALLOC/编译期标记 = 0 出边正常);state 链覆盖(df_connect_state 纯函数判定表——find_func + fi_ispure);**产出 = EDG 预期内容清单**(每 op 类的出边类别/数量,供 Task 1 测试期望) +- CSE 时序 byte 级实测:`corec ccr -O0/-O1` 同源产物的 NOD 区 diff(pass_cse 是否被 lower_to_ccr 回滚)——注记(非修复项) +- regalloc.cr:180 `compute_entries` 与 v6 §4.1 规则逐条对照(定值点 = IR_STORE 的 s1 ∪ 其余 dest≥0;版本切分;收口 end=min(def-1,last_ref);参数/无定值 def=-1)——**Task 2 corec 侧 ENT builder 的镜像基**(corec 二进制不含 regalloc.cr——须独立实现同规则;对照产出 = 规则差异清单) +- 落盘序核对:NOD 落盘源 = lower_to_ccr 后的 g_ir_instrs = g_df_nodes 镜像(1:1)——EDG 节点 id 引用与 NOD 文件序一致的前提确认 +- 交付:清单并入本 plan 附注(文末「Task 0 盘点结果」节);差异 → Task 1/2 执行前修正 + +--- + +## Task 1: v7 骨架切换——version 7 + NOD 36B + EDG 段(写读同改) + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(CCR_VERSION 6→7(:87);CCR_SEG_COUNT 5→6(:88);段 tag 6=EDG 常量新增;save_ccr:NOD writer(:543-556)28B→36B + first_edge/edge_count、EDG 段 writer 新增、段表 5→6 项、calc_ccr_size(:306)/ccr_nod_seg_size(:292)扩展;load_ccr:NOD 36B 读(:1035-1059 区)、EDG 解析 + 拓扑校验、段表 tag 6 处理) +- Test: `tests/selfhost/test_ccr_v7.py`(新建——v7 walker:header/段表 6 段/EDG/36B NOD 解析)+ `tests/selfhost/test_ccr_v6.py`(机械更新:version 常量 6→7、reject 测试改「拒 version≠7」;ENT 空断言测试暂留——Task 2 翻转) + +**Interfaces:** +- Consumes: Task 0 清单(EDG 内容期望) +- Produces: v7 文件(写侧——EDG = g_df_edges 落盘:per-node 邻接连续布局,first_edge = 前节点 first_edge+count 累加;每边 {to_nod u32, kind u32} 8B);loader 读 v7(NOD 36B 跳 first_edge/count 后重建线性流——发射输入与 v6 逐字段一致;EDG 载入 g_v7_edges 缓冲 + 校验) +- 校验规则(字节 spec §4):每条边 to_nod > 所属节点(前向——数据/state 边 DAG 拓扑);edg_count == Σ edge_count;NOD/ENT/REG 引用界内;branch/jump 目标 = 操作数引用(可后向)非边——校验实现不把操作数当边 +- v6 文件读路径:删除(v7-only——loader 拒 version≠7;旧 v6 测试文件由 Python 侧改写,无转换工具) + +- [ ] **Step 1:** 写失败测试(test_ccr_v7.py):corec ccr 产文件 → v7 walker 断言(version=7、段表 6 段含 EDG、NOD 36B 记录、EDG 记录数与内容对照已知小程序的 Task 0 期望)——现 save 产 v6 → FAIL +- [ ] **Step 2:** 跑测试确认失败 +- [ ] **Step 3:** 写侧实现:CCR_VERSION=7、EDG 段写入(save_ccr 内:落盘前单遍扫 g_df_edges 按节点序收集出边——头插链表遍历 → 连续 EDG 数组 + first_edge/edge_count 回填 NOD)、段表/尺寸函数扩展 +- [ ] **Step 4:** 读侧实现:load_ccr 接受 version 7、NOD 36B 读、EDG 解析 + 拓扑校验、段表 tag 6 +- [ ] **Step 5:** 机械更新 test_ccr_v6.py(header/version 期望 6→7、V6File walker 段契约改 NOD 28B→36B + EDG 段走查 + 段表 5→6 项、reject 面改「拒 version≠7」——ENT 空断言与 roundtrip 用例暂留,Task 2 翻转/追加);test_ccr_v7.py 绿(两文件并存窗口,Task 3 合并) +- [ ] **Step 6:** 构建 + 回归快子集(compile/backend_bootstrap/hit_table 24/24——corec build 全链走 v7 文件) +- [ ] **Step 7:** 提交 `feat: v7 格式骨架——version 7 + NOD 36B 邻接 + EDG 段必落(写读同改,v6 读路径退役)` + +--- + +## Task 2: ENT 主干化——corec 产实记录 + loader 激活 + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(save:ENT writer(:558-587)恒 0 → 实记录(corec 侧条目计算 + 28B 盘布局 {var_id,version,def_nod,live_start,live_end 半开,home=-1,flags});calc_ccr_size/ccr_ent_seg_size 扩展;load:ENT 实记录路径激活(:1076-1145 已有解析/校验/函数块对照代码——恒空从未走到)) +- Modify: SYM func 记录写侧(:442-453 first_ent/last_ent 恒 -1 → 实回填;param_ents -1 → 参数条目 id)与 REG 写侧(:610-611 first_ent/last_ent 实回填)——与 ENT 同提交(loader 块对照校验消费) +- Test: `tests/selfhost/test_ccr_v7.py`(ENT 非空断言 + 字段语义 spot-check:已知小程序的条目数/版本切分/区间与 Python 独立期望对照——期望按 v6 §4.1 规则手算) + +**Interfaces:** +- Consumes: Task 0 的 regalloc compute_entries 规则对照清单(corec 侧独立实现——corec 二进制不含 regalloc.cr) +- Produces: `fn compute_entries_v7() -> int`(corec 侧:定值点收集(IR_STORE 的 s1 ∪ 其余 dest≥0,两处 carve-out:STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 排除)+ 版本切分 + 区间(图坐标 [def_k, def_{k+1}) 截断 [first_ref, last_ref+1))+ 参数/全局 def=-1 条目;写入 g_entry_count/ent 缓冲——28B 盘布局 version + live_end 半开转换);loader 校验(evr≥1/ev 命名空间/els 盘点完成(2026-09-09)。基源 = ccr_io.cr 全文件(写侧 save_ccr :345-636/读侧 load_ccr :658-1145)+ dataflow.cr(emit 双写 + df_connect_srcs/df_connect_state + g_df_edges)+ regalloc.cr(compute_entries :180-268)+ test_ccr_v6.py(V6File walker 严格段契约)。关键锚点已内联于各任务 Files/Interfaces 节(2026-09-09 勘探输出);Task 0 执行者按仓库惯例以代码复核为准。下列三表执行时回填: + +**表一:EDG 内容期望清单**(权威 = dataflow.cr `df_connect_srcs` :194-315 / `df_connect_state` :160-177 / `sg_pop` 终止边 :84-99 / `df_use_var` :179-190;op 全集 = ast.cr :538-588,0-51 无 40。Task 1 测试期望 = 本表 + 表一注记的幽灵边规则手算) + +| op 类别/区间 | op | 数据边(kind=0,本节点创建时自生产者连入,按操作数角色逐槽一条,平行边不合并) | state 边(kind=1,本节点创建时自 state 头连入 + 本节点成新头) | +|---|---|---|---| +| 字面量 | CONST(1) | 0(s1 = 标量值) | 无 | +| 算术/逻辑 | BINARY(2) | 2(s1、s2;s3 = op 子码非 var) | 无 | +| | UNARY(3) | 1(s1) | 无 | +| 浮点换算 | I2F(49) / F2I(50) | **0**(s1 实为 var 但 df_connect_srcs 未列 → GAP 族) | 无 | +| 指针/索引计算 | ADDR_INDEX(31) | **0**(s1=arr_var/s2=idx_var 实为 var 但未列 → GAP 族;ir_gen.cr:1129 真实发射) | 无 | +| 内存读 | LOAD(10) / LOAD_FIELD(11) / LOAD_INDEX(13) | 1(s1) | 无(读不连 state 链) | +| | LOAD_INDEX_VAR(15) | 2(s1、s2) | 无 | +| | DEREF(25) / LOAD_ENUM_TAG(23) | 1(s1) | 无 | +| | SLICE(24) | 3(s1、s2、s3) | 无 | +| 越界守卫 | BOUNDS_CHECK(30) | s1;type_kind≠0 时 +s2(动态界,ir_gen.cr:105-114 发射 tk=1)→ 1 或 2 | 无 | +| 内存写 | STORE(9) | 2(s1 目标、s2 值) | **入链** | +| | STORE_FIELD(12) | 2(s1、s2) | **入链** | +| | STORE_INDEX(14) | 2(s1=arr、s2=val;s3 = 常量下标) | **入链** | +| | STORE_INDEX_VAR(16) | 2(s1=arr、s2=idx)——**dest = 被存值源(值 var)不入边 → GAP**;s3 = 字面量 0 → 0-slot 幽灵(见注 A) | **入链** | +| | STORE_PTR(26) | 2(s1、s2) | **无 → GAP**(raw 指针内存写不连 state 链) | +| 分配/释放 | ALLOC(6)/ALLOC_STRUCT(7)/ALLOC_ARRAY(8) | 0 | 无 | +| | ARENA_NEW(32) | 名义 s1;**实际发射 s1 = 0 字面量**(ir_gen.cr:1663/1701/1761/2161/2275)→ 0-slot 幽灵边(见注 A),有效数据边 = 0 | 无 | +| | ARENA_RESET(33) | 1(s1 = arena_var 真 var) | **无 → GAP**(arena 复位不连 state 链) | +| 引用/解引用 | REF(18) | 1(s1) | 无 | +| 调用族 | CALL(4) | s2 条(args 自 s1 连续 s1..s1+s2−1 逐参一条;s2=0 → 0 条) | **视纯度**:find_func(s3)(s3 = 函数名 idx)< 0 或 fi_ispure==0 → 入链 | +| | CALL_EXTERN(45) | 1(仅 s2 = 首参)——**多参缺口 → GAP**(s3 = arg_count,args 2..n 不入边) | **无 → GAP**(extern 副作用不连 state 链) | +| | SPAWN(27) | s2 条(同 CALL 参数遍历) | 无 | +| | HOTPATCH_ROUTE(39) | 1(仅 s2 = 首参)——多参缺口同 CALL_EXTERN | 无 | +| | LAZY_THUNK(46) / LAZY_FORCE(47) | 1(s1) | 无 | +| 动态类型 | DYN_TAG(41)/DYN_VAL(42)/DYN_PACK(43)/DYN_DISPATCH(44) | 1(s1;DYN_PACK s2 = type_idx 字面量) | 无 | +| 控制流 | BRANCH(19) | 1(s1 条件 var;**s2/s3 = label id 非 var——v7 §4 边界声明「目标 = 操作数非边」同源佐证**) | 无 | +| | JUMP(20) | 0(s1 = label id) | 无 | +| | RETURN(5) | s1 ≥ 0 → 1,否则 0 | 无 | +| | LABEL(21) | 0 | 无(但可作 state 链头被后续边引用——出边出现在后续 state 节点创建时) | +| 流程/并发 | YIELD(28)(s1=value var)/ AWAIT(29)(dest、s1) | **0**(实为 var 但未列 → GAP 族) | 无 | +| 函数地址 | FNADDR(48) | 0(s1 = name idx int) | 无 | +| 编译期标记 | NO_BOUNDS_CHECK(35)/FAST(36)/UNROLL(37)/SECTION(38)/APPROX(51)/NOP(0) | 0 | 无 | +| 内联提示 | INLINE(34) | 1(s1) | 无 | +| 合并伪节点 | PHI(22) | 0 | 无(注:全仓 ir_gen/pass/interp/instr 无 IR_PHI 发射点——恒不出现) | +| 枚举构造 | MAKE_ENUM(17) | 0(s1 = variant name idx;字段走 STORE_FIELD) | 无 | +| 区域收尾(非 op——sg_pop 附加) | SG_LOOP(1)/SG_FOR(2) 收尾(dataflow.cr:84-99) | — | 区内最后 state 节点 → 区出口 label 节点 kind=1 终止边(条件:`g_last_state_node ≥ NSTART`,纯循环体无区内 state → 不产);随后 state 头 = 出口 label 节点 | + +注 A(**0-slot 幽灵边——本表最要命实测发现**):`g_df_var_producer` 只在节点 `dest ≥ 0` 时写入 nid;-1 播种循环(init_df dataflow.cr:28-34)执行时 `g_ir_var_count == 0`(main.cr:396-408 序:先清零后 init_df 再 ir_gen_globals),`grow_df_arrays`(dyn_arr.cr:516)新段靠 alloc 零页 → **从未被定值的 var(全局、参数、未定值临时)producer 槽 = 0 =「节点 0」** → `df_use_var`(`producer >= 0` 判)产出幽灵数据边 0→消费者;节点 0 = func0 的 arena_new(每函数首节点 = arena_new,ir_gen.cr:2275)→ **每编译恒出自环 (0,0)**。双程序实测(v7t0_probe / v7t0_cse 的 cir DOT):n0→n0 自环存在;参数读恒 = 幽灵边 0→读点。⇒ **与 v7 字节 spec §4 不变量 1「每条边 to_nod > 所属节点」直接冲突** —— Task 1 必先定夺其一:(a) dataflow.cr 修播种(grow 后新区写 -1 / init_df 延后到 globals 注册后)——改 .cir 视图但不动发射行为;(b) v7 写侧滤自环与 0→X 幽灵边(写侧净化,loader 校验面不动);(c) 放宽校验(自环合法)——不建议(图 DAG 语义掺水)。本计划判定线 = 任务内定夺,**不影响 Task 2/3**(ENT 规则无涉边集)。 + +**表二:compute_entries 规则对照**(权威 = regalloc.cr `compute_live_ranges` :48-111 + `compute_entries` :180-268;corearch 专属——build_selfhost_native.py 清单确认 regalloc.cr 仅在 corearch concat 列表;src/compiler/regalloc-consistency.cr = 双二进制均不编译的文档残留。Task 2 corec 侧 `compute_entries_v7` 的镜像对齐清单) + +| # | v6 §4.1 / 计划文字规则 | regalloc.cr 实现 | 差异 / Task 2 对齐项 | +|---|---|---|---| +| 1 | 定值点 = IR_STORE 的 s1 ∪ 其余 dest≥0(carve-out:STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 排除) | :206-212 逐条一致(STORE 先判 s1;三 op else-if 排除;其余 dest);追加约束:目标 var 须 ∈ [vs, vs+vc) 本函数 var 窗口 | 无差异——corec 侧同窗口约束;坐标 = NOD id 全局序(= 实现侧 `inst = ist + ii` 的全局坐标;corec 侧 ist 同源 = df func start,ii 序 = NOD 序) | +| 2 | 版本切分;收口 end = min(def−1, last_ref) | :218-224 收口上一版本 `pend = min(inst−1, last_global)`,last_global = var 级 last_ref(含定值自身——dest 列计入引用,恒 ≥ 次定值 → min 恒取 def−1,数学与 spec 截断式等价);末版 le 直接 = last_ref | 无差异(版本号不落盘:save 侧 vcnt 计数槽 :561-587 已实现,Task 2 激活) | +| 3 | 参数/全局条目 def_nod = -1,区间 = [函数首节点, last_ref+1) | :240-262 只对**函数窗口内从未定值但有引用**的 var 产 def=-1 条目,区间 = **[first_ref, last_ref] 闭区间**(live_start = 首引用指令非函数首节点) | **差异①(文档面)**:v6 §4.1 ③ 文字「区间 = [函数首节点…]」与实现不符——实测 pure_add 参数 a/b 条目 live 1..1(首引用 = BINARY @n1,函数首节点 = 0)。Task 2 镜像按实现(loader 只校验 els < ele ≤ instr_cnt、def≥0 时 ed == els——两语义都过,但「双写对照一致」要求两侧同规则);测试手算期望按实现语义 | +| 4 | 参数/全局 def_nod = -1 | 全局 var(SYM 前缀 0..G−1)**不在任何函数 var 窗口 → 恒无条目**(flags bit2「全局」永不落) | **差异②(文档面)**:v6 spec §3.2/§4.1 的「全局条目」面在实现中不存在——SYM globals 记录是全局的唯一存在面。Task 2 同实现(v7 字节 spec §3.5 flags bit2 语义保留 = 零实例声明) | +| 5 | 活区间(first/last_ref 扫描)为收口前提 | compute_live_ranges :48-111:逐函数逐指令扫 d/s1/s2 三列 ∈ 窗口 → [first_ref, last_ref](未用 = 双 -1) | **对齐项(实现前提)**:corec 二进制无 regalloc.cr——`compute_entries_v7` 须自含同规则引用扫描(或等价的 df 出边反扫——注意 df 边有幽灵缺陷表一注 A,**不得**以 df 边替代三列扫描) | +| 6 | 条目按函数升序成块;函数块界由 loader 按 var 窗口切 | :105-110 compute_live_ranges 尾部逐函数调 compute_entries(func_i==0 整表重置);块内序 = 定值指令序,尾部 def=-1 补丁按 var 序扫 | 无差异(loader 分块 :1114-1145:pv ∈ [fvs, fvs+fvc),fvc≤0 块断 → pcnt=0 对照 ffe==-1;与 SYM func first/last 双写对照已实现) | +| 7 | home/flags | 恒 home=-1、flags=0(:232/:255) | 无差异(v7 字节 spec:home 恒 -1 直通、无配方/参数/全局/驱逐位零实例) | + +**表三:落盘序核对**(权威 = ir_gen.cr `emit` :193-206 / main.cr ccr·build 路径 :527-583 / dataflow.cr `lower_to_ccr` :351-391 / cir_cache.cr 恢复 :242-415) + +| 核对面 | 结论 | +|---|---| +| NOD 落盘源 = lower_to_ccr 后的 g_ir_instrs? | ✓ save_ccr :542-556 逐条读 g_ir_instrs;main.cr :534 lower_to_ccr 先于 :557/:581 save(ccr/build 两路径同);lower_to_ccr = g_ir_instr_count 清零后自 g_df_nodes 0..count−1 逐字段重建(:356-371)+ func 边界自 df func start/count 复制(:375-383)——**NOD = g_df_nodes 镜像 1:1 同序** | +| emit 双写之外有无第三写点破坏 1:1? | 全线性流写点审计:emit(双写同参)、cir_cache 恢复(load_cir_cache :242-407——节点 :288-318/SG :321-356/边 :358-374/指令 :376-392 同快照双写,cache-hit 函数两表同参恢复,1:1 保持)、pass_cse(opt.cr :194-290 只 iri_set_op/iri_set_s1-3 改线性——lower 后回滚,见表后 CSE 注记)、lower_to_ccr。ir_gen 直改线性后补丁(arena size `iri_set_s1` ir_gen.cr:1711/:2287 等,另 :1674/:1772/:2165)只落线性侧、df 节点保持 emit 原值 → lower 后 NOD = emit 原值(既有行为,v7 同构,非本计划面) | +| EDG 节点 id 与 NOD 文件序一致的前提 | ✓ 节点 id 全局单调追加(init_df 每编译一次 main.cr:407,无逐函数清空);df func 切片 = start/count 视图(df_begin_func/df_end_func :395-410);lower 后指令 i ≡ df 节点 i ≡ NOD 文件序 i——EDG to_nod 引用与文件序一致前提成立;REG enter/exit 同坐标系(loader 回填 func 边界 :1017-1033 与 GC-3 上界校验 :1061-1074) | +| g_df_edges 节点 id 空间 = g_df_nodes(无跨函数泄漏?) | from/to 均在创建时点取节点 id,缓存恢复边同快照 → id 空间一致;**跨函数数据边存在两种形态**:(a) 表一注 A 幽灵边(node 0 → 任意函数消费者,实为跨函数);(b) 缓存快照边——缓存命中恢复 save 时点**全图**边(cir_cache.cr :167-179 存全量),源不变时与重建同态;**源变(前序函数指纹 miss 重建、后续函数仍命中)时快照边陈旧——既有 .cir 缓存设计潜在面,待核注记**。state 链无跨函数(df_begin_func 重置 g_last_state_node :401)。Task 1 测试建议:新路径/清 .core/cache 跑(避免缓存面混入期望) | +| 逐函数图清空机制 | 不存在——df 数组编译期全局追加;「每函数一图」只是切片视图。v7 写侧「按节点序收集出边」单遍扫 g_df_edges 即可(头插链表 → 落盘序任意但确定,节点出边连续布局由写侧回填 first_edge/count) | + +**CSE 时序 byte 级实测(注记——非修复项,Global Constraints 判定线确认)**:源 = 双函数含重复纯子表达式(x*x 双现 ×2、helper a*a 双现 + 循环内双现,NOD 解析证实 3 组 CSE-able 重复)。命令(每跑前 rm -rf .core 清缓存): +`nice -n 19 /home/DslsDZC/core/build/corec ccr /tmp/v7t0_cse_test.cr -o /tmp/v7t0_O0.ccr --opt-level 0` 与同源 `--opt-level 1` → **全文件 byte-identical**(cmp 一致;sha256 均 38cf7880d0db54ce…;NOD 段 = 72 节点 2020B 两版逐字节同)。代码路径佐证:main.cr:530-534 `pass_cse()`(opt.cr :194-290,只 NOP/改写线性 g_ir_instrs)位于 `lower_to_ccr()` 之前;lower 自 g_df_nodes 全量重建 → **O1 CSE 被整体回滚,NOD 落盘与 O0 恒同**(注:corec g_opt_level 默认 = 1,main.cr:203——默认跑即走此回滚路径,v6 现状如此)。结论:CSE 疑点坐实为「无效果 pass」,v7 落盘源 = df 镜像与其无涉,保持非修复项。 diff --git a/docs/superpowers/plans/2026-09-10-corearch-kernel-completion.md b/docs/superpowers/plans/2026-09-10-corearch-kernel-completion.md new file mode 100644 index 00000000..d81e7e20 --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-corearch-kernel-completion.md @@ -0,0 +1,281 @@ +# corearch 内核组件完备化实施计划(步骤 2.5) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +> **状态:实施完成(2026-09-10 Tasks 0-5 收官——落点:Task 0 盘点 8c3e6ad1 / Task 1 035ea289(+ 补正 72c40389)/ Task 2 99fc6af3 / Task 3 b4d8c74f / Task 4 b9685f5c / Task 5 = 收官提交)**——中立性 guard A+B 全绿(零残留)+ 全量回归绿 + 自举重建冒烟 + full-bootstrap guard(corec2/corec3 cmp 同 + N06=0)+ 文档同步(设计 spec 状态 → 已实施 + 执行注记 §7 / 蓝图步骤 2.5 完成注记 + §1.1 三组件完成态);执行注记 = 设计 spec `2026-09-10-corearch-kernel-completion-design.md` §7 + +**Goal:** 内核三组件按蓝图 §1.1 补完 + **全量中立化(2026-09-10 用户原则:内核零经典概念——寄存器/栈/调度/内存布局/指令任一概念不得出现)**——语义对象模型(对象面 API + 调度重建移实例)、A 通道中立化(compute 系改 NOD 对象面——内核零线性流引用)、B 通道中立化(登记表不透明位置 + LOC_HOME_BASE 移除)、注册契约完整(③④ 形式声明)——行为零变化。 + +**Architecture:** 现状(步骤 2 收官态)= ent_kernel.cr(判定引擎 + 数据面,双 concat)+ regalloc.cr(机器侧)+ 实例声明表。全量中立化审计(设计 spec §4.5)结论:全部经典残留 = 一根因族两通道——A 线性流坐标派生读取(4 函数 4 iri_* 使用点——compute 系/dump/rl_rule2)+ B 位置通道(meta_reg_for_var 读实例布局 + LOC_HOME_BASE 编码)。本计划 = 中立化波:写侧 populate_nod_objects(corec 镜像对称)→ compute 系改 NOD 对象面(同 index——F5)→ 登记表不透明化(实例双面同步责任在实例)→ 输出面中性化 + 配方查询补体 + ③④ 声明。判据 = 行为零变化(全回归 + byte-identical 面)+ 中立性静态 guard(A 组/B 组 token 红→绿)。 + +**Tech Stack:** ent_kernel.cr(内核)、regalloc.cr(实例机器侧)、ccr_io.cr(loader 收敛 + 写侧 populate)、corearch.cr(引导/实例侧调用)、build_selfhost_native.py、Python 测试(test_live_ranges.py/test_ccr_v7.py 判据锚 + test_ent_kernel_neutrality.py 中立性 guard)。 + +**设计 spec:** `docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md`(本计划执行依据——§1 对象模型/§2 中立化/§3 契约/§4 判据/§6 挂账)。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2,git 被 hook 拦截)。每任务 `jj commit -m ''` +- 构建 `nice -n 19 python3 build_selfhost_native.py`(约 2-3 分钟);测试 `nice -n 19` 前缀(铁律 #6);清 .core/cache 跑测试 +- **行为零变化硬约束**:全回归绿 + byte-identical 面(backend_bootstrap stage 链 + ccr_v7 产物)——重建移实例/登记表通道 = 纯搬移 + 通道等价,产物不变 +- **搬移/改动纪律**:函数体搬移零改动;登记表通道切换 = 语义等价改写(禁止顺手改判定逻辑);禁止新增接口抽象(现名即 API 裁决延续——新增面 = kern_loc_*/对象面访问器 = 设计 spec 明列者) +- **中立性目标(全量)**:ent_kernel.cr 零经典概念引用——实例符号(g_opt_meta/寄存器名/ELF 段名)+ 线性流(g_ir_instrs/iri_\*)+ 位置编码(LOC_HOME_BASE)——test_ent_kernel_neutrality.py 静态 guard 入回归面(A 组 Task 2 绿、B 组 Task 3 绿) +- corec 写侧不受影响(不经 loader);interp 不经 loader +- 判定③④ 不实现(用户裁决——needs_* 形式声明字段落契约) + +--- + +## Task 0: 盘点——对象面/移出面的依赖核对(计划锚点) + +**Files:** 分析产出(无代码改动——注记回填计划附注) +- 核对 load_ccr 内 NOD→g_ir_instrs 重建段的**完整依赖面**:重建段消费/写哪些全局(g_ir_instrs/g_ir_func_instr_*/g_label_*/g_v7 边缓冲/nod_edge_meta 暂存);GC-3 root span 上界校验消费什么(nod_count vs instr 流——确认移出后守卫仍成立);ENT 校验依赖线性流吗(evr/els/ele 界 = NOD 坐标——确认);loader 内其余段(STR/SYM/REG)与重建段的顺序依赖 +- **节点→区域映射(region_of_nod)定夺**:v7 文件无 g_df_node_region——loader 现是否重建(REG enter/exit 端点推导)?对象面 API 是否含 region_of_nod(含 = 重建推导; 不含 = 区域面仅 REG 行遍历)——**产出定夺注记** +- 登记表接入面核对:alloc_registers phase 5(写 g_opt_meta 点)+ 注入钩子 6+1(写点清单)+ regalloc_debug_dispatch 各 flag 路径(判定何时读登记表——compute 后/alloc 后时序)——登记 API 调用点清单 +- 判定读 meta 面清单:verify_regalloc_consistency/rl_rule2_func/meta_reg_for_var 的 g_opt_meta 读点全集——登记表切换的精确函数面 +- 中立性 guard 设计:ent_kernel.cr 现引用面扫描(g_opt_meta 引用点 = 待切清单; 其余实例符号 = 断言排除集) +- 交付:注记回填本 plan 附注「Task 0 盘点结果」节 + +--- + +## Task 1: 语义对象模型——对象面 API + 调度重建移实例 + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(load_ccr:NOD→g_ir_instrs 重建段移出——loader 产出 = 对象载入 + 守卫; 重建相关写点清理) +- Modify: `src/arch/linux/ld/regalloc.cr` 或新实例函数位(`build_linear_schedule()`——重建段函数体搬移——corearch concat 内实例侧; corearch.cr 发射前置调用) +- Modify: `src/arch/linux/ld/ent_kernel.cr`(对象面 API:nod 面访问器 + v7_edge 访问器 + 配方查询薄封装——见设计 spec §1.2; 若 Task 0 定夺 region_of_nod = 含 → 区域推导访问器) +- Modify: `src/compiler/corearch.cr`(load 后调 build_linear_schedule) +- Test: `tests/selfhost/test_ccr_v7.py`(23/23 保持——对象面新测试:EDG 配方可读断言——节点出边遍历 vs v7 Task 0 表一去幽灵后期望复用) + +**Interfaces:** +- Consumes: Task 0 依赖面清单 +- Produces: 对象面 API(nod_op/nod_dest/nod_s1-3/nod_tk/nod_edge_first/count/v7_edge_to/kind——ent_kernel 内核面);`build_linear_schedule()`(实例面——从对象重建 g_ir_instrs) +- 判据:ccr_v7 23/23 + backend_bootstrap stage 链 byte-identical(重建产物同——纯搬移)+ 回归快子集 +- loader 守卫强度不变(段表/拓扑/root span/ENT 双写——Task 0 确认的守卫面) + +- [ ] **Step 1:** 写失败测试:对象面 EDG 配方读断言(节点出边遍历 = 已知小程序期望——现无 API → FAIL) +- [ ] **Step 2:** 跑测试确认失败 +- [ ] **Step 3:** 对象面 API 实现(ent_kernel——nod/edge 访问器 + 配方查询; region_of_nod 按 Task 0 定夺) +- [ ] **Step 4:** 重建段移出:load_ccr 清理 + build_linear_schedule 落实例侧 + corearch 调用接线(load 后、发射前) +- [ ] **Step 5:** 测试绿(对象面新断言 + ccr_v7 23/23 + backend_bootstrap stage byte-identical)+ 回归快子集 +- [ ] **Step 6:** 提交 `feat: 内核完备 Task 1——语义对象模型(对象面 API + 调度重建移实例)` + +--- + +## Task 2: A 通道中立化——compute 系改 NOD 对象面 + 写侧 populate(全量审计 F1/F2/F3/F5) + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(corec 写侧 `populate_nod_objects()`——lower 后单遍 g_ir_instrs → g_v7_nod_sem 镜像(28B 语义字段,与 loader 解析对称——写侧载体裁决 2026-09-10); save_ccr 调 compute 前(:456 区)接入) +- Modify: `src/arch/linux/ld/ent_kernel.cr`(**compute_live_ranges/compute_entries/rl_rule2_func/dump_entries_summary 的 iri_\* 4 使用点(:99/:227-233/:677-680/:367)→ nod_\* 对象面**——同 index 机械替换(F5); 函数窗口源中性化(F3——现 g_ir_func_instr_\* 值 = NOD 坐标,以节点范围语义注记使用; rl_rule2_func 签名 ist/ic/vs/vc 措辞中性化——值不变); 头注自证矛盾消除(:819-823)) +- Test: `tests/selfhost/test_live_ranges.py`(13/13——dump 通道输出逐字节同,坐标值不变)+ `tests/selfhost/test_ccr_v7.py` 24/24(ENT 产物 byte-identical——populate 镜像 + compute 同值)+ 回归快子集 + +**Interfaces:** +- Consumes: Task 1 对象面(nod_\* 已建——corec 侧 populate 前 nod 缓冲为空,compute 时序 = populate 后) +- Produces: `populate_nod_objects()`(corec 写侧——ccr_io,corec concat 内) +- 判据: ENT 产物 byte-identical(写侧判据)+ dump 通道输出逐字节同 + O2 stage 链 byte-identical;核心判据 = 内核零 g_ir_instrs/iri_\* 代码引用(中立性 guard A 组 token 新增 g_ir_instrs——本任务转绿) + +- [ ] **Step 1:** 中立性 guard 文件先写(test_ent_kernel_neutrality.py——排除集 = g_opt_meta 族/LOC_HOME_BASE/OPT_\*/E2_\*/g_x86_\*/寄存器名/**g_ir_instrs**/iri_\*——现态红——早暴露纪律;A 组(g_ir_instrs/iri_\*)与 B 组(meta/LOC)分注,Task 2 绿 A 组、Task 3 绿 B 组) +- [ ] **Step 2:** 写失败测试:populate + 对象面 compute(corec 写侧产物 ENT byte-identical 判据先行基线) +- [ ] **Step 3:** `populate_nod_objects()` 实现(g_ir_instrs → g_v7_nod_sem——48B→28B 字段镜像,同 loader 解析字段序) +- [ ] **Step 4:** compute 系 4 函数 iri_\* → nod_\* 改写(同 index——F1/F2)+ 函数窗口语义注记(F3——值 = NOD 坐标) +- [ ] **Step 5:** 测试绿(A 组 guard 绿 + ENT byte-identical + dump 输出同 + stage 链)+ 回归快子集 +- [ ] **Step 6:** 提交 `feat: 内核完备 Task 2——A 通道中立化(compute 系改 NOD 对象面 + 写侧 populate,内核零线性流引用)` + +--- + +## Task 3: B 通道中立化——登记表 + meta 改写 + LOC_HOME_BASE 移除 + +**Files:** +- Modify: `src/arch/linux/ld/ent_kernel.cr`(位置登记表 {entry→loc} + kern_loc_assign/kern_loc_clear/kern_loc_of; verify 规则①② 读通道切换(meta_reg_for_var 改读登记表——函数体等价改写); **LOC_HOME_BASE 常量移除 + 引用面(:500/:590/:744)改写——位置 = 不透明整数,零域分类(§2.2 修正:home 面 = 条目字段独立互斥遍;合成若需保留 = 实例侧事务);无 kern_set_loc_domain/无域参数(废弃——2026-09-10 用户原则:内核无寄存器/域分类概念)**) +- Modify: `src/arch/linux/ld/regalloc.cr`(alloc_registers phase 5 + 注入钩子写 g_opt_meta 后调 kern 登记/清除——双面同步; **meta_reg_for_var 函数体等价改写为登记表查询——不删**(Task 0 ④ 修正注:regalloc.cr 注入探针 try_inject_read_gap :254/:261 调用它——须同步改实例侧私有扫描或经登记表等价读; emit 面 instr.cr get_reg_for_var = 独立实现不受影响)) +- Test: `tests/selfhost/test_live_ranges.py`(13/13——判定红/绿路径经登记表通道输出与直读时代逐字节同)+ test_ccr_v7.py 24/24 + +**Interfaces:** +- Consumes: Task 0 登记 API 调用点清单 + Task 1 对象面 + Task 2 A 通道 +- Produces: kern_loc_assign/clear/of; 登记表(内核数据——loc = 不透明整数) +- 判据:test_live_ranges 13/13(通道切换输出同——先基线后对照)+ O2 全链 byte-identical(--check-regalloc 绿/红路径经登记通道)+ 回归快子集;guard B 组转绿 +- 双份同步纪律:实例写点成对(g_opt_meta + 登记)——代码注记 + 注入钩子测试覆盖(红路径 = 双面一致注入) + +- [ ] **Step 1:** 写失败测试:登记表 API(kern_loc_assign——现无 → FAIL)——guard B 组红确认 +- [ ] **Step 2:** 跑测试确认失败 +- [ ] **Step 3:** 登记表 + kern API 实现(ent_kernel) +- [ ] **Step 4:** 判定读通道切换(verify/rl_rule2 读登记表——等价改写); LOC_HOME_BASE 移除(引用面改写——home 面 = 条目字段独立互斥遍或实例侧合成,内核零域概念) +- [ ] **Step 5:** 实例侧双面同步(alloc phase 5/注入钩子 → g_opt_meta + kern 登记成对; try_inject_read_gap :254/:261 同步——实例侧私有扫描或登记表等价读,注记理由) +- [ ] **Step 6:** 测试绿(live_ranges 13/13 通道输出同 + ccr_v7 24/24 + O2 byte-identical + guard B 组绿)+ 回归快子集 +- [ ] **Step 7:** 提交 `feat: 内核完备 Task 3——B 通道中立化(登记表通道 + LOC_HOME_BASE 移除,内核零位置编码/零实例布局读)` + +--- + +## Task 4: 输出面中性化 + 配方查询补体 + 注册契约完整化(③④ 声明) + +**Files:** +- Modify: `src/arch/linux/ld/ent_kernel.cr`(rl_print_loc :589-595 中性化(不透明 loc 直印——去 reg/home slot 分类措辞); rl_report_rule1/2 措辞("regalloc-consistency" 前缀 → 中性描述——保留 spec 文件名引用注记); **配方查询封装补体(857-860 悬空注——nod_inputs(n) 薄封装函数 = [first, first+count) 遍历——审计 API 缺口)**; ③④ 形式声明字段文档 + kern_verify_all 输出面注记) +- Modify: `src/compiler/corearch.cr`(g_instance_decl 行扩展: needs_eviction/needs_call_sites 字段 + 行数据(x86: {0, 0})——home_base/reg_domain 已从范围删除(§2.2 修正)) +- Test: `tests/selfhost/test_live_ranges.py`(13/13——flag 通道输出不变——措辞中性化不影响断言面确认; dump 文本断言如含 reg/home slot 字样 → 同步更新断言为中性措辞)+ 配方查询新断言(对象面遍历 = 现 dump_object_surface 内联逻辑的函数化) + +**Interfaces:** +- Consumes: Task 2/3(对象面 + 登记表——配方查询基于 nod_\* + v7_edge_\*) +- Produces: nod_inputs(n)(内核面——配方查询承诺兑现); 声明行 needs_eviction/needs_call_sites +- 判据:flag 通道输出不变(措辞中性化后文本断言同步——测试 = 行为锚,措辞 = 输出面) + test_live_ranges 13/13 + 回归快子集 + +- [ ] **Step 1:** rl_print_loc/rl_report 措辞中性化(loc 直印——测试断言同步) +- [ ] **Step 2:** nod_inputs 封装补体 + 配方查询测试(对象面遍历函数化) +- [ ] **Step 3:** 声明行 needs_eviction/needs_call_sites 扩展 + ③④ 文档注记(引擎不实现——设计 spec §3.2 引用)+ kern_verify_all 输出面注记 +- [ ] **Step 4:** test_live_ranges 13/13 + 回归快子集 +- [ ] **Step 5:** 提交 `feat: 内核完备 Task 4——输出面中性化 + 配方查询补体 + 注册契约完整化(③④ 声明)` + +--- + +## Task 5: 收官——中立性 guard 全绿 + 全量回归 + 自举 + 文档 + +**Files:** +- Test: 中立性 guard 全量绿确认(A 组 Task 2 绿 + B 组 Task 3 绿——红→绿记录核)+ 残留清零 +- Test: 全量回归(compile/backend_bootstrap/hit_table/region_cfg/mw1-6/slice_bounds/live_ranges/ccr_v7/bootstrap 三套 + full-bootstrap guard) +- Modify: `docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md`(状态 → 已实施 + 执行注记回填——含审计结论(§4.5)/写侧载体裁决/登记表双份纪律实证/③④ 声明落点/F4 挂账) +- Modify: `docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md`(步骤 2.5 完成注记 + §1.1 三组件完成态) +- Test: `nice -n 19 python3 build_selfhost_native.py` 重建 + 冒烟 + +**Interfaces:** +- Consumes: Task 2-4 全部 +- 验证: 中立性 guard 全绿(内核零实例符号/零线性流/零位置编码)+ 全量回归绿 + 自举重建冒烟 + 文档同步(设计 spec 状态/蓝图步骤 2.5 注记/progress 台账) + +- [ ] **Step 1:** 中立性 guard 全量绿确认(红→绿记录核——A/B 组分注)+ 残留清零(若有——回对应通道补漏) +- [ ] **Step 2:** 全量回归跑批 +- [ ] **Step 3:** 自举重建 + 冒烟 +- [ ] **Step 4:** 文档同步(设计 spec 状态回填含审计注记/蓝图步骤 2.5 注记) +- [ ] **Step 5:** 提交 `feat: 内核完备收官——中立性 guard 全绿 + 全量回归 + 自举 + 文档同步` +## 风险注记 + +- Task 1 重建移出 = 最大动面(loader 与发射之间契约变化)——判据 = backend_bootstrap stage byte-identical(重建产物纯搬移证明); loader 守卫面 Task 0 核对防漏 +- Task 2 通道切换 = 判定语义等价改写面——基线对照纪律(直读时代输出 vs 登记表通道输出逐字节同);双份同步漏点 = 注入红路径测试覆盖 +- 域参数面已整体废弃(2026-09-10 用户原则修正——内核零域分类概念; 原 kern_set_loc_domain/home_base/reg_domain 设计删除——见设计 spec §2.2 修正注) +- 中立性 guard 若在 Task 4 发现 ent_kernel 残留实例引用 → 回 Task 2 面补漏(guard 先写 = 早暴露) +- corec 写侧/interp 不经 loader——零影响(回归验证即可) + +--- + +## Task 0 盘点结果(已回填——2026-09-10,实施 Task 1-4 依此锚定) + +> 基源 = 设计 spec(权威)+ 步骤 2 收官态代码(ccr_io.cr 1372 行 / ent_kernel.cr 807 行 / +> regalloc.cr 795 行 / corearch.cr 492 行现态)。行号 = 盘点日锚点(后续改动若漂移以语义为准)。 + +### ① load_ccr 重建段依赖面(Task 1 移出裁决依据) + +- **重建段实际位置(纠正旧锚点)**:NOD→g_ir_instrs 线性重建 = ccr_io.cr **NOD 段解析 :1207-1236** + (盘 36B → 内存 48B:grow_ir_instrs(instr_cnt) :1211 + 逐记录 iri_set_op/dest/s1/s2/s3/tk :1228-1233 + + g_ir_instr_count 递增 :1234)。**不在先前锚 ~:1361-1393**(现文件 1372 行,EDG 校验尾 :1369—— + 旧锚点过时,重核如上)。消费 = data 缓冲 + seg_off3/seg_end3(段表局部),**不读任何 g_ir_* 内容**; + nod_edge_meta = load_ccr **局部**暂存(:1213 alloc 16B/节点 {first_edge, edge_count}——非全局); + g_v7_edges/g_v7_edge_count(全局,:1343-1344 写)属 EDG 段、不属重建段。 +- **守卫消费面(决定性事实——重建移出后全部成立)**:GC-3 root span 上界校验 :1244-1251 消费 + g_ir_func_instr_start/count(REG 段派生回填 :1186-1202)+ **NOD 段头计数 instr_cnt**(局部变量, + 非 g_ir_instrs/iri_*)→ 线性流零依赖。ENT 校验 :1255-1322(evr≥1/var 命名空间/els≥ele、 + **ele > instr_cnt 拒收 :1275、ed ≥ instr_cnt :1277、ed == els 定值恒等**——界全 = NOD 坐标 + + SYM var 命名空间;函数块界重建 :1291-1321 读 g_ir_entries 内容 + fn_meta(SYM first/last 双写对照)) + → 线性流零依赖。EDG 校验 :1335-1369 消费 instr_cnt + nod_edge_meta(NOD 邻接域 :1349-1352)→ + 线性流零依赖。**load_ccr 全程零 iri_* 读**。 +- **移出推论**:loader 仍须读 NOD 段计数(段界校验必需)与每记录邻接域 first_edge/edge_count + (36B 记录偏移 **+28/+32**——first_edge@+28/edge_count@+32,s1 为 8B 占 +8..+16;EDG 守卫消费)——28B 语义字段写线性流 = 唯一可搬体。现 NOD 语义字段 + 除 g_ir_instrs 外**无任何保留**(含邻接 = load_ccr 局部)→ Task 1 须裁决 NOD 对象留存形态 + (对象存储 or build_linear_schedule 重扫 data 缓冲——buf 存活至 corearch_main 尾,重扫式可行; + 文件段偏移现为 load_ccr 局部,重扫式需把 NOD 段界传出或段表重走)。 +- **顺序依赖**(现实现序 STR→SYM→REG→NOD→ENT→EDG;段按 offset 随机寻址):SYM funcs 产 fn_meta + 暂存 → REG 根行对照 :1174-1180;REG 根 span → 函数指令边界回填 → GC-3(需 NOD 计数); + NOD 计数 → ENT/EDG 界校验;SYM 声明区 → var 命名空间 → ENT var_id 界;ENT↔EDG 互不依赖。 + 重建段 loader 内**零下游消费者**——消费全在 load 返回后(见下)。 +- **load 后依赖面(接线点裁决)**:corearch_main 内 load_ccr :295 → init_backend_arrays :297 + (仅 g_x86_* 数组——与线性流无关)→ regalloc_debug_dispatch :305。dispatch 各分支直食线性流: + dump-entries :153 / inject-coexist-oob :158 / dump-coexist :163 的 compute_live_ranges(扫 + iri_dest/s1/s2)、dump-regassign :169 与 check-regalloc :191 的 alloc_registers、:199 + regalloc_verify_all;生产 O2 门 :308-314(alloc → verify);ELF 发射面(elf.cr/instr.cr/resolve.cr)。 + → **build_linear_schedule() 接线点 = load_ccr 成功(:296)后、dispatch :305 前**(紧邻 load)。 +- ~~另一 load_ccr 调用方 = src/arch/linux/ld/main.cr:56——**死代码**(自注记 2026-09-10 内核抽取 + Task 4:零 concat 引用,实际入口 = src/compiler/corearch.cr)——改签名/搬移无须同步。~~ + **(2026-09-10 Task 1 评审 I-1 驳斥——证据 = Task 1 报告 §3 偏差 ①:该调用方 = backend_bootstrap + stage 链活入口(project 模式 corec build src/arch/linux/ld → 目录 main.cr corearch_main → + load_ccr :56 → build_linear_schedule),Task 1 已接线——改签名/搬移须双入口同步)** + +### ② region_of_nod 定夺(设计 spec §6 挂账 ① 裁决 = **不含**) + +- v7 文件无 g_df_node_region;loader **不重建**节点→区域映射——REG 段只派生 nstart/ncount + (:1172-1173 = enter/exit 副本写回 g_sgs 行),不产映射数组。corec 构建侧映射 = 构建期快照 + (dataflow.cr df_create_node:110 写 g_df_node_region[nid] = g_cur_sg,-1 = 无)——不落盘。 +- **零消费方事实**:corearch concat 内 g_sgs 唯一消费者 = ccr_io.cr loader 自身;ent_kernel/ + regalloc/instr/elf/resolve 全部零 g_sgs 读(判定①② 不消费区域)。另注:设计 §1.2「现有 sg + 访问器」表述不确——corearch 侧无 sg_* 访问器函数(读写全为 r64/w64 直偏移)。 +- **推导可行性(后补注)**:REG 行 = 压栈序 + parent 链 + enter/exit 节点坐标(层流嵌套区间; + SG_FUNC 根 span = 函数指令范围,save :759-765/load GC-3 双侧校验过)。region_of_nod(n) = 含 n + 的最深行:每查询 O(函数 REG 行数) 扫描或 O(nodes) 栈扫预计算——数据无损、可后补。 +- **定夺注记**:对象面 API **不含 region_of_nod——区域面仅 REG 行遍历**。理由:零现消费方 + + 预计算表 = 无消费者内核数据(YAGNI)+ EDG 配方可读承诺不依赖区域。→ Task 1 不加区域推导 + 访问器;Task 4 文档同步时在 spec §1.2 表注 region_of_nod = 挂账已决(不含,按需后补)。 + +### ③ 登记 API 接入面(Task 2 调用点清单) + +- **alloc_registers phase 5**(regalloc.cr :733-792,逐函数尾):g_opt_meta 原语直写(store8 + 头/count/对 @+12,g_opt_meta_count = ei+1 :789;已分配 var = asg_lv/var_asg,全局 var id = + vs + lvp、reg = r64(var_asg,…))。→ **登记调用点 = phase 5 尾每函数**:对每个已分配 var,其 + 全部版本条目 e ∈ [entry_start(fi), entry_start(fi)+entry_count(fi))(ent_var(e) == vw)逐一 + kern_loc_assign(e, rn)(var 级分配 → 同 var 全条目同 loc——与现 meta_reg_for_var 投影语义 + 精确等价的前提,实施时保持全条目登记)。 +- **注入钩子写点(6+1 的实写点 = 3 个 try_*;顶层 inject_* = 纯遍历)**: + - try_inject_home_conflict :40-67:meta_remove_var(v1/v2) :56-57(配对 = 清两 var 全条目登记) + + home=7 直写条目表 :58-59(g_ir_entries = 内核数据面,无需登记——读侧由 ent_home + 域 + home_base 合成,与现 ent_home 直读等价)。 + - try_inject_reg_conflict :192-217:meta_set_reg(v1/v2, 3) :208-209(配对 = 登记两 var 全条目 → 3)。 + - try_inject_read_gap :235-283:条件 meta_remove_var(:267) + meta_set_reg(gv,3)(:275)(配对 = + 清共存 var 条目 + 登记 gv 条目)+ live_end 截断 :277(条目表直写,无需登记)。 + - inject_coexist_oob :22-29:零 meta 写——无配对。 +- **配对点取舍**:meta 写辅助函数体内嵌(meta_set_reg :107-111 / meta_remove_var :115-154 / + meta_append_reg_assign :171-186)覆盖全部注入路径 → 最小配对面 = **phase 5 写点 + meta_set_reg + + meta_remove_var 共 3 处**(phase 5 原语直写不经辅助函数,须单独配对)。 +- **flag 路径时序(判定读登记表时点)**:判定唯一读面 = regalloc_verify_all :798-807,调用点两处: + check-regalloc 分支(corearch.cr :199——保序 = 强制 O2 :190 → alloc :191(phase 5 登记完成)→ + 看门狗 meta_reg_assign_total :194(**直读 g_opt_meta,实例面 viewer,不经登记表**)→ 注入 + :196-198(配对保持同步)→ verify :199 读表)与生产 O2 门(:309 alloc → :312 verify)。 + 其余分支(dump-entries/dump-coexist/inject-coexist-oob)只跑 compute_live_ranges + 条目表 + dump——不触发判定、不需登记表;dump-regassign :167-187 直读 g_opt_meta(实例面)。verify 无 + 不经 alloc 的路径(生产门 = g_opt_level≥2 + needs_alloc 声明;check 分支强制 2)→ 登记表进程 + 起点空表 = 等价现状(meta 空 + home 全 -1 → 0 违反)。 + +### ④ 判定读 meta 面全集(通道切换精确函数面) + +- ent_kernel.cr 代码级 g_opt_meta 读点 = **唯一函数 meta_reg_for_var :504-525**(全文 12 处 + g_opt_meta 提及 = 12 行(**7 注释 + 5 代码读**——评审修正:11+5 计数矛盾)——循环界 g_opt_meta_count :507、块 key :509、 + data_len :511、对 var :515、对 reg :517)。布局:OPT_META_STRIDE = 64(ast.cr:642)、 + OPT_KEY_REG_ASSIGN = 0(ast.cr:641)@+0 / data_len @+4 / count @+8(跳过)/ 对 var@+12 reg@+16 + 8B 步进——与 loader opt_meta 段(ccr_io :1117-1140)及 emit 侧同布局。 +- **调用点(切换 = meta_reg_for_var 函数体等价改写为登记表查询,3 调用点不动)**:rl_rule2_func + :643(寄存器驻留过滤——var 级语义须保持:var 有任一已登记条目 = 驻留)、 + verify_regalloc_consistency 计数遍 :726 + 记录构建遍 :742(逐条目投影)。 +- **LOC_HOME_BASE 代码面**:常量 :500 + rl_print_loc :590(分类打印)+ verify home 编码 :744 + (home 组投影 = LOC_HOME_BASE + ent_home(e))→ 域参数化替换面(kern_set_loc_domain 接 + {home_base, reg_domain})。注:reg_domain 现无分组消费点(规则① 组别 = meta 投影 ∪ + ent_home 投影天然不相交:reg 0..15 vs home ≥ 10⁶)——reg_domain = 声明面参数 + 防相交护栏。 +- **emit 面独立实现(已核)**:instr.cr get_reg_for_var :34-53 = 与 meta_reg_for_var 字节级同构 + 的独立函数(g2_slot :55-73 消费;elf.cr:1309/resolve.cr:36-37 = get_reg_for_var/g2_slot 面)—— + 通道切换不动 emit 面。 +- **计划假设被实证反驳(修正注——Task 2 必须同步)**:plan Task 2 写「meta_reg_for_var 删——确认 + emit 面无引用(instr.cr get_reg_for_var = 独立实现——已核)」只核了 emit 面——但 + **regalloc.cr 注入探针 try_inject_read_gap :254/:261 调用 meta_reg_for_var**(实例侧读内核 + 函数)。删除/改写时须同步这两处(换实例侧直读 g_opt_meta 的私有扫描,或经登记表等价读—— + 前者贴实例面语义);corec concat 无引用(定义随 concat 编译,未用不报错)。 + +### ⑤ 中立性 guard 设计(Task 4 前置设计) + +- **ent_kernel.cr 实例味符号面(代码级全集)**:g_opt_meta/g_opt_meta_count + OPT_KEY_REG_ASSIGN/ + OPT_META_STRIDE(全经 meta_reg_for_var 体——Task 2 切换后清零)+ LOC_HOME_BASE(:500——Task 2 + 移除)。注释层实例提及(guard 剥注释后不拦,记录在案):x86/机器侧/regalloc.cr(头注 :4-30)、 + instr.cr g2_slot/get_reg_for_var(:478/:502)、rl_print_loc x86 寄存器枚举注(:593「3=rbx, + 12-15=r12-r15」)、alloc_registers() 前置条件注(:704)。 +- **结构性守卫(现成)**:ent_kernel.cr 双 concat 共享(corec concat 无 regalloc/instr/elf/ + hit/corearch.cr)→ 指向实例独有文件的符号 = corec 编译失败。文本 guard 真正防的 = + **共享声明文件里的实例符号**(g_opt_meta/opt 常量 = globals.cr/ast.cr,双 concat 都有 → + 结构守卫拦不住——正是静态 guard 必要性)。 +- **断言排除集(代码面 token 级)**:g_opt_meta、g_opt_meta_count、LOC_HOME_BASE、OPT_KEY_* / + OPT_META_STRIDE、E2_REG_SLOT_BASE/E2_*、g_x86_*、寄存器名 token(rbx/r12-r15 等实例枚举名)、 + ELF 面(ELF/elf_gen 等)——NOD/EDG/ENT/REG = 文件格式语义名**不拦**(§4.3 例外)。 + 实现注:必须先剥注释(// 与 /* */)再扫 identifier——rl_print_loc x86 注记在注释层,不剥则 + 误报;可选强化 = 引用标识符 ∩ 实例独有文件函数定义集 = ∅(结构守卫的文本镜像,成本低)。 +- **测试落点**:tests/selfhost/ 新文件 test_ent_kernel_neutrality.py(纯源码文本扫描,无 build + 依赖,与 selfhost 套件同风格入回归面)。**guard 落点 = Task 2 Step 1(评审 Minor 3 修正——与登记表 + API 失败测试同批前置写):现态 meta_reg_for_var 仍在 → 红; Task 2 切换后转绿**——Task 4 首写则首跑即绿、 + 红验证丢失(且坏剥注释/正则空转不可察)。 diff --git a/docs/superpowers/plans/2026-09-10-corearch-kernel-extraction.md b/docs/superpowers/plans/2026-09-10-corearch-kernel-extraction.md new file mode 100644 index 00000000..17c26f2a --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-corearch-kernel-extraction.md @@ -0,0 +1,124 @@ +# corearch 内核抽取实施计划(范式无关内核 + 实例定制——演进序步骤 2) + +> **状态:实施完成(2026-09-10)——Task 1-4 落点:Task 1 语义侧纯搬(b0d24b1b:ent_kernel.cr = 内核 32 函数逐字搬移 + LOC_HOME_BASE 单份随判定入内核;regalloc.cr 1560→844 纯机器侧;判据通道 sha256 逐字节同);Task 2 写侧单源化(edc70fbe + c138c44c:compute_entries_v7/ccr_grow_* 镜像删除、RPT_MAX/ir_op_kind_name 迁内核、ent_kernel 进双 concat + src/compiler import ent_kernel——full-bootstrap 面 N06/SIGSEGV 缺口闭合,全链 N06=0 + corec2/corec3 cmp=0);Task 3 注册契约最小面(6413dd80:g_instance_decl 两行实例声明表 + 表驱动 dispatch,评审独立 48/48 通道 base↔head 逐字节全同);Task 4 收官(本提交:全量回归绿——compile/backend_bootstrap 链/hit_table 24/24/region_cfg 22/22/mw1-6/slice_bounds 7/7/live_ranges 13/13/ccr_v7 23/23/bootstrap 三套 + full-bootstrap guard + 自举重建冒烟;蓝图步骤 2 完成注记;cir cache 指纹缺陷挂账 TODO 预存 Bug #5)** +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 从 corearch 的 regalloc.cr 抽出范式无关内核(语义判定引擎 + 条目/区间数据面 + 注册契约最小面),机器侧(CAG 分配/meta 写入)留 x86 实例——行为零变化(判定语义不动只搬家)。 + +**Architecture:** 纯代码搬移 + 单源化收敛 + 最小契约形式化。数据面(g_ir_live_ranges/g_ir_entries = globals.cr 共享声明)已 = 结构化语义对象;判定引擎(共存/规则①②)不读值内容、不含寄存器名/ABI 常量(硬编码点全在 alloc_registers 阶段 1/2/4 与 meta 写入 = 机器侧);判定→机器侧唯一读通道 = meta_reg_for_var(分配输出输入通道,随判定搬)。双转录第三处(ccr_io.cr compute_entries_v7 写侧镜像)借共享文件约束单源化(corec 与 corearch 共用 globals——内核文件进双 concat 后 corec 写侧直调内核 compute_entries,删镜像)。 + +**Tech Stack:** src/arch/linux/ld/regalloc.cr(1560 行——拆分源)、src/compiler/ccr_io.cr(loader/写侧)、src/compiler/globals.cr + dyn_arr.cr + ast.cr(共享声明)、build_selfhost_native.py(双 concat 清单)、Python 测试(test_live_ranges.py 13 判据锚 + test_ccr_v7.py 23/23 写侧判据)。 + +**设计 spec:** `docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md`(§2 裁决表——语义侧 → 内核/机器侧 → x86 实例;§3 演进序步骤 2 = 本计划执行依据)。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2,git 被 hook 拦截)。每任务 `jj commit -m ''` +- 构建 `nice -n 19 python3 build_selfhost_native.py`(约 2-3 分钟);测试 `nice -n 19` 前缀(铁律 #6);清 .core/cache 跑测试 +- **行为零变化硬约束**:判定语义不动只搬家——判据 = test_live_ranges.py 13/13(判定绿/红路径 + dump 通道输出不变)+ test_ccr_v7.py 23/23(写侧单源化后产物 byte-identical)+ 回归面(compile/backend_bootstrap/hit_table/region_cfg/mw1-6/slice_bounds/bootstrap 三套 + run.sh full-bootstrap corec 链——Task 收官全量;ent_kernel 双 concat 共享后 src/compiler 单源自编译单元 = 回归面成员:Task 2 评审发现该面 N06/SIGSEGV 缺口,src/compiler/_import.cr import ent_kernel + module.cr 跨树回退补齐) +- **搬移纪律**:逐函数搬移零改动(函数体 byte-identical 搬入新文件——除去重收敛点);禁止顺手改语义/加接口抽象(蓝图"判定语义不动只搬家"——资源域参数化/双向注册契约 = 后续步骤,非本计划) +- 共享文件约束:globals.cr/dyn_arr.cr/ast.cr/ccr_io.cr 进双二进制;regalloc.cr 只在 corearch concat;新内核文件若需 corec 侧(写侧单源化)→ 进双 concat +- 判定③④ 无实现(文档化 TODO——静态放置无驱逐事件/callee-saved 平凡满足)——本计划不补实现 +- src/arch/linux/ld/main.cr = 死代码遗留(零 concat 引用)——本计划不处理(删除待核挂账) + +--- + +## Task 1: 语义侧搬移——新内核文件 + 数据面/判定引擎纯搬 + +**Files:** +- Create: `src/arch/linux/ld/ent_kernel.cr`(内核——语义判定引擎 + 条目/区间数据面;文件头注:范式无关——不读值内容、无寄存器名/ABI 常量/编码知识) +- Modify: `src/arch/linux/ld/regalloc.cr`(语义侧函数删除 → 调内核;保留机器侧) +- Modify: `build_selfhost_native.py`(corearch concat 加 ent_kernel.cr) + +**Interfaces:** +- 搬入内核的函数(regalloc.cr 现位 → ent_kernel.cr,逐函数零语义改动): + - 数据面:grow_live_ranges (:18)/live_range_slot (:25)/live_first (:35)/live_last (:40)/compute_live_ranges (:48)/grow_entries (:139)/grow_func_entry_meta (:146)/ent_off/ent_var/ent_def/ent_live_start/ent_live_end/ent_home/ent_flags (:161-167)/entry_start (:170)/entry_count (:175)/compute_entries (:180-268) + - 共存:entries_coexist (:364)/coexist_version_conflicts (:380)/coexist_home_conflicts (:411) + - sweep 门禁:rl_rec_lt (:515)/rl_merge_sort (:525) + - 诊断:dump_entries_summary (:314)/dump_coexist_summary (:440)/rl_print_loc (:576)/rl_func_name (:584)——注意 rl_print_loc 内 x86 注释(:580 "3=rbx")随搬(注释层——本计划不清理,注记待后续) + - 常量:LOC_HOME_BASE (:486)(位置编码约定 = 判定与实例的共享 seam——**裁决修正(Task 1 实证):单份随判定搬内核**——concat 单编译单元下自举 NameResolver 拒重复顶层声明(name_resolver.py:73 无去重守卫 + symbol_table.py:28 NameError),且机器侧实证零引用) +- 保留 regalloc.cr(机器侧):meta_reg_for_var (:491——**裁决:随判定搬入内核**——它是判定输入通道(verify 读分配输出),纯读取;meta_set/append/remove/reg_assign_total (:849-936) 留实例侧(写入 = 实例动作);alloc_registers (:1136-1560) 留;注入钩子 (:805-1125) 留(注入 = 改分配结果 → 属实例侧测试通道);ir_op_kind_name (:278) 留(dump 诊断公共,或随搬——裁决:留 regalloc.cr,两文件同 concat 可互调) +- verify 判定面(规则①②合成)——**裁决:本任务搬 verify_regalloc_consistency (:693-781)/rl_rule2_func (:624)/rl_report_rule1/2 (:590/:602)/regalloc_verify_all (:785)**:它们读 meta_reg_for_var(已随判定搬内核)——搬移后 verify 全在内核;regalloc.cr 机器侧经 alloc_registers 尾/外部调用内核 verify(现调用点 corearch.cr:69/:163 不变——函数名不变即可) +- 判据:test_live_ranges.py 13/13 + 回归快子集(O2 check-regalloc 路径绿——corearch.cr 调用点签名不变) + +- [ ] **Step 1:** 建 ent_kernel.cr(文件头 + 搬移函数骨架——先搬数据面 grow/live/ent accessor + compute_live_ranges/compute_entries) +- [ ] **Step 2:** corearch concat 加 ent_kernel.cr;构建确认无符号冲突(函数跨文件调用 OK——concat 单编译单元) +- [ ] **Step 3:** 搬共存/sweep/诊断/dump 函数 +- [ ] **Step 4:** 搬 verify 判定面(meta_reg_for_var + rl_* + verify_*) +- [ ] **Step 5:** regalloc.cr 删已搬函数(保留机器侧 + 对外调用面);构建 + test_live_ranges 13/13(判据:dump-entries/coexist 输出与搬移前逐字节同——先跑基线再跑搬移后对照) +- [ ] **Step 6:** 回归快子集(compile/backend_bootstrap/hit_table/region_cfg) +- [ ] **Step 7:** 提交 `refactor: 内核抽取 Task 1——语义侧搬移 ent_kernel.cr(判定引擎 + 数据面纯搬,零语义改动)` + +--- + +## Task 2: 写侧单源化——compute_entries_v7 镜像消除 + +**Files:** +- Modify: `src/compiler/ccr_io.cr`(删 compute_entries_v7 :458-579 + ccr_grow_entries/ccr_grow_func_entry_meta :419-433 + 局部 lr 表机制——写侧直调内核 compute_entries/compute_live_ranges; save_ccr ENT 写调用点 :611 适配) +- Modify: `src/arch/linux/ld/regalloc.cr` + `src/arch/linux/ld/ent_kernel.cr`(**迁 RPT_MAX + ir_op_kind_name 自 regalloc.cr 入 ent_kernel.cr**——Task 1 评审 Important: 两声明机器侧零剩余使用(ir_op_kind_name 唯一调用方 = 内核 dump_entries_summary; RPT_MAX 唯一使用方 = 内核 rl_rule2_func/verify_regalloc_consistency),regalloc.cr 不在 corec concat → 内核进 corec concat 前不迁则 corec 构建解析失败; 迁移零风险) +- Modify: `build_selfhost_native.py`(**ent_kernel.cr 进 corec concat**——corec 二进制需要内核 compute_entries) +- Test: `tests/selfhost/test_ccr_v7.py`(23/23——ENT 实记录产物 byte-identical 判据) + +**Interfaces:** +- 内核 compute_entries/compute_live_ranges 现用全局 g_ir_live_ranges/g_ir_entries(globals.cr 共享声明——corec 进程内可用;corec 侧时序:save 前调用(main.cr ccr/build 路径 lower_to_ccr 后)——compute_live_ranges 扫 g_ir_instrs(lower 后 = df 镜像)同 corearch 语义) +- 差异消解(Task 0 勘探注记):①表载体(ccr_io 局部 lr → 内核全局 g_ir_live_ranges——corec 侧 grow/init 检查)②整表重置时序(compute_entries func_i==0 分支 vs compute_entries_v7 函数头清零——统一为内核语义)③grow 单实例(删 ccr_* 副本)④半开转换(写侧盘布局 +1 逻辑保留在 ccr_io 写点——内核算闭区间,写盘转换归 ccr_io) +- 判据:test_ccr_v7.py 23/23(含 ENT 手算期望/独立模型 replay——产物与 Task 2 原实现 byte-identical 即镜像消除无损)+ test_live_ranges 13/13(corearch 侧不动) + +- [ ] **Step 1:** RPT_MAX + ir_op_kind_name 迁入 ent_kernel.cr(自 regalloc.cr——Task 1 评审 Important 携带)→ corec concat 加 ent_kernel.cr;构建(corec 二进制含内核) +- [ ] **Step 2:** ccr_io 写侧适配:save ENT 前调内核 compute_live_ranges + 逐函数 compute_entries(func_i 循环——内核 compute_live_ranges 尾部已逐 func 调 compute_entries:直接调 compute_live_ranges 即可)→ 写盘(半开转换保留) +- [ ] **Step 3:** 删 compute_entries_v7/ccr_grow_*/局部 lr 机制(ccr_param_entry_id/ccr_ent_* 访问器如仍被 loader/写侧用则保留——按实际引用删) +- [ ] **Step 4:** test_ccr_v7.py 23/23 绿(产物 byte-identical——若差异:先比对 ENT 段,差异 = 单源化语义偏差,修内核调用序而非弯判据) +- [ ] **Step 5:** 回归快子集 +- [ ] **Step 6:** 提交 `refactor: 内核抽取 Task 2——写侧单源化(compute_entries_v7 镜像消除,双转录 R5 收敛)` + +--- + +## Task 3: 注册契约最小面——实例声明表 + 内核引导 + +**Files:** +- Modify: `src/compiler/corearch.cr`(分派形式化:三路分派(--table×opt 门/--opt-level/O2 verify/调试 flag)→ 实例声明表 + 表驱动 dispatch) +- Modify: `ent_kernel.cr`(内核入口函数族:kern_ 前缀导出——kern_compute_entries/kern_verify_all/kern_dump_* 封装现函数名;实例能力声明数据结构/注册函数最小面) +- Test: `tests/selfhost/test_live_ranges.py`(13/13——flag 通道输出不变) + +**Interfaces:** +- 实例声明表最小形态(Core 无高阶函数——数据表 + switch 分派): + - `g_instance_decl : {id i32, name str_idx, opt_min i32, opt_max i32, allow_table i32, allow_link i32, needs_alloc i32, needs_verify i32}` × n(表数据 = x86 实例声明 + 表模式路径声明——表模式 = 独立实例路径雏形(蓝图 §2 表裁决)) + - 引导:corearch_main 读 flag → 查表选实例(现三路分派逻辑收敛为表查询 + switch 调用) + - 内核入口:现有函数名加 kern_ 别名或直接以现名作为内核 API(裁决:现名即 API——加头注"内核接口面 = 本文件函数集",不加包装层——YAGNI) +- 行为判据:flag 测试通道输出逐字节不变(dump-entries/coexist/check-regalloc 全同)——纯形式化重构 +- **范围克制注**:本任务 = 最小面(声明表 + 引导收敛)——资源域代数参数化(判定读 g_opt_meta 的耦合)/双向契约(判定结果回填 home)= 蓝图后续步骤,本计划不做(Global Constraints 搬移纪律) + +- [ ] **Step 1:** 内核头注 + 接口面声明(kern_ API 面 = 现函数集,文档化——不动代码) +- [ ] **Step 2:** 实例声明表(corearch.cr 数据 + 查询函数) +- [ ] **Step 3:** 三路分派收敛为表驱动 dispatch(--check-regalloc 顺序 :58-75 保序: 强制 O2 → alloc → 看门狗 → 注入 → verify_all——行为不变) +- [ ] **Step 4:** test_live_ranges 13/13 + 回归快子集 +- [ ] **Step 5:** 提交 `feat: 内核抽取 Task 3——注册契约最小面(实例声明表 + 表驱动分派,行为不变)` + +--- + +## Task 4: 收官——全量回归 + 自举重建 + 文档/台账 + +**Files:** +- Test: 全量回归面(compile/backend_bootstrap/hit_table/region_cfg/mw1-6/slice_bounds/live_ranges/ccr_v7 + bootstrap 三套 + full-bootstrap stage:`./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0` 输出 N06-free 且 /tmp/corec2 冒烟绿——src/compiler 单源自编译单元含内核的守护断言) +- Modify: `docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md`(§2 裁决表执行状态注记 + 步骤 2 完成标记;新发现注记: 判定→meta 读通道随判定入内核/写入留实例——注册契约需双向的勘探发现) +- Modify: `src/arch/linux/ld/main.cr` 死代码注记(删除待核挂账——TODO 或删除 = 本任务裁决:**只注记不删**——非本计划范围确认) +- Test: `nice -n 19 python3 build_selfhost_native.py` 重建 + 冒烟(corec check/run 小样本 + backend_bootstrap stage 链) + +**Interfaces:** +- Consumes: Task 1-3 全部 +- 验证: 全量回归绿 + 自举重建冒烟;文档同步(蓝图步骤 2 完成、裁决表状态、双转录收敛注记 R5 消除);progress 台账 + +- [ ] **Step 1:** 全量回归跑批 +- [ ] **Step 2:** 自举重建 + 冒烟 +- [ ] **Step 3:** 文档同步(蓝图状态/裁决表/新发现注记——判定→meta 读通道方向、grow/accessor 收敛成果、rl_print_loc x86 注释待清挂账) +- [ ] **Step 4:** 提交 `docs: 内核抽取收官——蓝图步骤 2 完成 + 全量回归 + 文档同步` + +--- + +## 风险注记 + +- Task 1 为最大风险(搬移 30+ 函数跨文件)——判据 = dump/verify 输出与搬移前逐字节同(先基线后对照);逐函数零改动纪律防语义漂移 +- 双 concat 符号共享:ent_kernel.cr 进 corec concat(Task 2)时,ccr_io 内残留同名函数(ccr_grow_* 等已删)——删除完整性以构建 + 测试判 +- LOC_HOME_BASE = 单份随判定入内核(裁决修正——双份不可构建 + 机器侧零引用实证; 实例 seam 常量单源化 = 注册契约参数化步骤的天然内容——届时实例侧经内核导出访问) +- corec 侧 compute_live_ranges 首次运行时序(g_ir_live_ranges grow/init——corec 进程 init_df 无此表初始化? globals 声明即零态——compute 前自 grow——与 corearch 同路径,回归验证) +- 判定③④ 无实现不补(Global Constraints)——verify 覆盖 = 规则①②,test_live_ranges 现有绿/红路径即判据锚 diff --git a/docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md b/docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md new file mode 100644 index 00000000..e087352f --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md @@ -0,0 +1,663 @@ +# 语言面收窄 波 R1 实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 落实 `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md` 的波 R1 三项:定长数组「表示层概念」注记、宽度死条目移除 + 词法面对齐(`1_000` / 宽度后缀退役)、全局初始化修复(定长数组全局 SIGSEGV + 静默丢初始化)。 + +**Architecture:** 三类正交改动——(a) **注释层**(零行为,Task 1);(b) **词法/语法面收窄**(死 token kind 与死分支移除 + 两编译器对齐 + grammar 同步,Task 2/3);(c) **全局初始化修复**(前端把当前被静默丢弃/崩溃的全局初始化降级为运行期 IR 序列、解释器补「标量常量初始化」阶段,Task 4)。 + +**Tech Stack:** Core 自举栈 —— Python bootstrap(`bootstrap/corec/`,构建工具链)+ self-hosted 编译器(`src/compiler/`,用户面编译器)+ x86-64 ELF 后端(`src/arch/x86_64/` × `src/format/elf/` × `src/os/linux/`)+ 解释器(`src/compiler/interp.cr`)。 + +## Global Constraints + +- **jj only**:版本控制一律 `jj`(git 被 hook 机械拦截);提交 = `jj commit -m '...'`。禁止 `git` 命令。 +- **CPU 限速**:所有编译/测试命令必须 `nice -n 19` 前缀(CLAUDE.md 铁律 #6)。 +- **文件永久不允许还原**:任何回滚须用户明确许可(铁律 #3)。 +- **根因修复**:不得用绕过/变通掩盖问题(铁律 #1/#4)。 +- **勿重编号**:`src/compiler/ast.cr` 常量编号空间稳定——删除条目**留墓碑注释**,绝不重排后续编号(先例:`// T_FLOAT_TYPE 91 已删除…`)。 +- **行为零变化面**:Task 1(注释)、Task 2(死代码移除)必须输出等价——判据 = 同命令前后输出逐字节相同。 +- **行为变化面仅限两处**:Task 3(`_` 分隔符与宽度后缀**均由静默改为响亮报错**——用户裁决 2026-09-10:数字字面量不支持 `_`)、Task 4(原先 SIGSEGV / 静默 0 的全局初始化场景)。**其余一切行为不得变化**。 +- **自举守护**:`nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py` 必须 stage 链全 `[PASS]` + `corec2`/`corec3` `cmp` IDENTICAL + N06 = 0。 +- **run 门**:任何 `run`/`build` 输出中 `error[` 计数 = 0(不许「rc=0 就算过」)。 +- **提交粒度**:每任务一次提交,中文提交信息,前缀 `docs:` / `refactor:` / `fix:` / `test:`。 +- **diff 校验命令(Task 1 实现者实测教训,2026-09-10)**:本机 `jj diff` 默认 color-words 输出**新增行不带 `+` 前缀**,任何 `jj diff | grep '^+'` 形式的校验**恒真空通过、零校验力**;且本机 `grep` = ugrep,`'^+ *//'` 这类式样会直接报错(rc=2)。规范式样: + ```bash + jj diff --git | grep '^[+]' | grep -v -e '^[+][+][+]' -e '^[+][[:space:]]*//' -e '^[+][[:space:]]*$' # 期望空 + jj diff --git | grep -c '^[-]' # 期望 0(无删除行) + ``` + 并按需加**非真空控制组**(故意构造一条应命中的输入验证该命令真会报)。fish 环境另有 noclobber(`>` 覆盖已存在文件会静默失败)——重定向用 `>|` 或先删目标文件。 + +--- + +## 背景与判据(侦查已定,实现者不需要重新论证) + +### 定长数组全局崩溃 — root cause(2026-09-10 全链侦查结论) + +**类别 = 初始化缺失(storage/pointer 未生成),不是尺寸算错。** 证据链: + +1. `src/compiler/ir_gen.cr:2349`(`gtype : ., mut = TI_INT;`)+ `:2365`(`new_ir_var(name, gtype)`)—— 全局一律登记为 `TI_INT`,数组类型/长度不进 IR;`:2336`(`global_init_val` 尾部 `return 0`)对数组字面量静默返回 0(初值丢弃)。 +2. `src/os/linux/entry.cr:72-98` `_start` 常量初始化循环只写「`init_val != 0` 的标量」;数组 init_val = 0 → 不发射。全局槽 = BSS 零。 +3. `src/arch/x86_64/instr.cr:428-438` `e2_load_var` global 分支 = `lea r11,[rip+slot]; mov reg,[r11]`(取槽**内容**当基指针)→ 槽为 0 时后续 `[r10+idx*8]` 解引用 NULL → SIGSEGV。 +4. `src/compiler/interp.cr:67-93` `ir_interpret()` 只跑 main 的 node 区间,**无任何全局初始化阶段**(全文零引用 `g_ir_globals`)→ 解释器同因崩溃;且**可变标量全局也丢初始化**(`x : int, mut = 5` 读回 0)。 + +### 修复前行为基线(实现者须复现确认,作为 TDD 的「红」) + +| 用例(解释器 `./build/corec run`) | 现状 rc | 期望(修复后) | +|---|---|---| +| `g : [int;2] = [8,9]; fn main()->int{return g[1];}` | 139 | 9 | +| `g : [int;2]; fn main()->int{ g[1]=7; return g[1]; }` | 139 | 7 | +| `a : [int;2] = [8,9];\nb : [int;2] = a;\nfn main()->int{return b[1];}` | 139 | 9 | +| `x : int, mut = 5;\nfn main()->int{ x = x + 1; return x; }` | 1(x 读 0) | 6 | +| `x : int = 5; fn main()->int{return x;}`(常量折叠,回归) | 5 | 5 | +| `g : [int;4] = [1,2,3,4]; fn main()->int{return 5;}`(不访问) | 5 | 5 | +| ELF 路径:前三行任一 → `corec build` 后运行二进制 | 139 | 同上 | + +### 词法面基线(已核实) + +| 输入 | self-hosted(现状) | bootstrap(现状) | +|---|---|---| +| `1_000` | **静默 0**:lexer 把 `_÷` 当后缀消费 → `T_INT(值 0)`(`add_tok(T_INT, -1, …)`),残余 `000` 再成一枚 `T_INT(0)`;`x := 1_000` → x = 0(cir dump:`const int = 0` ×2);`return 1_000;` → `error[TF01]` | `1_000` → `INT_LIT '1000'` ✓ | +| `1_000.5` | 小数循环不吃 `_` → 断裂 | `FLOAT_LIT '1000.5'` ✓ | +| `0x1_0` | 16 ✓(十六进制路径早已吃 `_`) | 16 ✓ | +| `10f32` / `1.5f32` | 静默:后缀消费 + dex 路由 hack(`suffix == "f32"` → 走 T_DEX),宽度值从不落地(`W_F32` 分支不可达) | 后缀并入词素(`INT_LIT '10f32'`)→ parser `int()` 抛 `ValueError` | + +**裁决(2026-09-10 用户拍板,本批落实)**:数字字面量**不支持 `_` 分隔符**——两编译器**统一响亮报错**(现状双向皆错:self-hosted 静默 0、bootstrap 静默接受;grammar 原注记「以实现为准取不支持」方向正确但两实现均未落实);**宽度后缀(`f32`/`f64`/`i8..u8`…)退出语言面**,同样**响亮报错**(grammar 早已注明它是「apx CPU 位宽标注」= 机器形状,且全仓 `src/`+`tests/` 零使用)。 + +全仓数字下划线使用点(精确 grep:`(^|[^A-Za-z0-9_.])[0-9]+_[0-9]+`)**仅一处**:`tests/bootstrap/test_pipeline.py:329` `check_integer_literals`(bootstrap lexer 测试断言 `1_000`→1000)——随裁决改为断言报错。`src/` 与 `tests/` 其余为零,(自举构建不依赖该形态)。 + +### 关键既有机制(Task 4 复用,不新造) + +- `IR_ALLOC_ARRAY(8)`:ELF 侧 `src/arch/x86_64/instr.cr:939-957` → 调运行期 `alloc`;解释器侧 `src/compiler/interp.cr:176-186` → `alloc` + 显式清零。运行期 `alloc`(`src/runtime/rt.s:72-98`)**自带 `rep stosb` 清零** → 两路径语义一致(分配即零初始化)。 +- `IR_STORE(9)`:`interp.cr:187` —— `s1`(目标 var) ← `s2`(值 var);数组字面量 lowering = `src/compiler/ir_gen.cr:2107-2127`(`IR_ALLOC_ARRAY` + 逐元素 `IR_STORE_INDEX` + `irv_set_type(v, alloc_type(TYP_ARRAY, elem_ti, n))`)。 +- 全局 var 注册 = `ir_gen_globals()`(`ir_gen.cr:2390`),调用点 `src/compiler/main.cr:408`(**先于**逐函数生成 `main.cr:480 ir_gen_func(fi)` → 序言注入时 `g_ir_globals` 已就绪)。 +- 手工验证过的等价形态(可作 oracle):把初始化写进 main 体内——`fn main()->int{ g = [10,20,30,40]; return g[2]; }` → 现网 rc=30(两路径均正常)。本任务 = 把这段序列**自动化地**搬到序言。 + +--- + +## Task 1: 定长裁决注记(零行为改动) + +**Files:** +- Modify: `src/compiler/ast.cr:229`(`EXPR_ARRAY`)、`:316-317`(`TYP_ARRAY`)、`:321`(`TYP_SLICE`) +- Modify: `src/compiler/checker.cr:392-401`(`res_type_node` 的 `[T;N]`/`[T]` 分支)、`:2201`(数组字面量类型)、`:1812`(range → 数组类型) +- Modify: `src/compiler/parser.cr:44-58`(`parse_type` 的 `[T; N]` / `[T]` 分支) + +**Interfaces:** +- Consumes: 无(纯注释) +- Produces: 无(零行为);注记短语供后续 R2(接口化)检索:关键字 `表示层概念` + +- [x] **Step 1: 捕获基线输出** + +```bash +cd /home/DslsDZC/core +printf 'g : [int;2] = [8,9];\nfn main()->int{return g[1];}\n' > /tmp/r1t1_sample.cr +nice -n 19 ./build/corec check /tmp/r1t1_sample.cr > /tmp/r1t1_check_before.txt 2>&1 +nice -n 19 ./build/corec cir /tmp/r1t1_sample.cr > /tmp/r1t1_cir_before.txt 2>&1 +nice -n 19 ./build/corec build /tmp/r1t1_sample.cr --static -o /tmp/r1t1_bin_before > /tmp/r1t1_build_before.txt 2>&1 +``` +Expected: `check` 输出 `ok`(rc=0);`build` rc=0;`/tmp/r1t1_bin_before` 运行 rc=139(已知缺陷,Step 4 复跑时须一致)。 + +- [x] **Step 2: 逐处贴入注记** + +在下列每个位置各贴一段(紧邻被标注代码,同一文件内可共用一次完整注记 + 其余位置一行式引用): + +```core +// 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 的类型构造器身份已退役—— +// 语义归处 = product(N 元聚合)/ 序列接口 + 长度 where(N 长序列)/ F11 图(长度事实, +// 可表达依赖长度)。本语法保留为「内联容量存储」表示提示(映射参数层,与 hw-map 同层; +// 随实例选择生效或退化,非经典范式映射可忽略)。见 +// docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 +``` + +落点:`ast.cr:229`、`ast.cr:316`、`ast.cr:321`、`checker.cr:392`、`checker.cr:2201`、`checker.cr:1812`、`parser.cr:44`。 + +- [x] **Step 3: 校验「仅注释」** + +```bash +jj diff | grep '^+' | grep -v '^+++' | grep -v '^\+ *//' | grep -v '^\+$' +``` +Expected: **空输出**(新增行全部是注释或空行)。 + +- [x] **Step 4: 复跑 Step 1 命令并比对** + +```bash +nice -n 19 ./build/corec check /tmp/r1t1_sample.cr > /tmp/r1t1_check_after.txt 2>&1 +nice -n 19 ./build/corec cir /tmp/r1t1_sample.cr > /tmp/r1t1_cir_after.txt 2>&1 +diff /tmp/r1t1_check_before.txt /tmp/r1t1_check_after.txt && diff /tmp/r1t1_cir_before.txt /tmp/r1t1_cir_after.txt && echo IDENTICAL +``` +Expected: `IDENTICAL`(`build`/`bin` 不必重跑——注释不进二进制;如需,比对 `cmp /tmp/r1t1_bin_before <新产物>`)。 + +- [x] **Step 5: 提交** + +```bash +jj commit -m 'docs: 语言面收窄 R1 Task 1——定长数组「表示层概念」注记(ast/checker/parser 七处,零行为改动;语义归处 = product/序列+长度约束/F11,语法保留为内联容量存储表示提示)' +``` + +--- + +## Task 2: 宽度死条目移除 + parser 死分支清理 + +**Files:** +- Modify: `src/compiler/ast.cr:81-94`(`T_INT_I8`…`T_INT_I64`,`T_INT_U8`…`T_INT_U64`,`T_FLOAT_F32`,`T_FLOAT_F64` 定义,编号 77-86) +- Modify: `src/compiler/ast.cr:110-119`(`W_I8`…`W_U64`,`W_F32`,`W_F64` 宽度值常量) +- Modify: `src/compiler/parser.cr:392-403`、`:406-411`、`:897-908`(唯一引用面 = 死分支) + +**Interfaces:** +- Consumes: 无 +- Produces: 无(lexer 从不发射这些 token kind——`parser.cr` 的 `kn == T_INT_I8 …` 链与 `tok_k(t) >= T_INT_I8 && tok_k(t) <= T_INT_U64` 判定**永不可达**;`W_*` 仅被这些链引用) + +- [x] **Step 1: 引用面核实(必须只有 parser.cr 死分支)** + +```bash +nice -n 19 grep -rn "T_INT_I8\|T_INT_I16\|T_INT_I32\|T_INT_I64\|T_INT_U8\|T_INT_U16\|T_INT_U32\|T_INT_U64\|T_FLOAT_F32\|T_FLOAT_F64\|W_I8\|W_I16\|W_I32\|W_I64\|W_U8\|W_U16\|W_U32\|W_U64\|W_F32\|W_F64" src/ --include='*.cr' | grep -v '^src/compiler/ast.cr' +``` +Expected: 命中仅 `src/compiler/parser.cr` 第 392-411 / 897-908 行(若出现其它文件/行号 → **停下并上报**,不得继续)。 + +- [x] **Step 2: parser.cr 死分支移除** + +`parser.cr:392-403` 改为(两处同构,另一处在 `:897`): + +```core + if tok_k(t) == T_INT { + advance_tok(); + return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)); + } +``` + +`parser.cr:406-411` 改为: + +```core + if tok_k(t) == T_DEX { + advance_tok(); + // 节点字段(数值迁移 Task 4):a = binary64 位模式(apx 快路径字面量表示, + // 由 lexer 存入 token 的 lexeme 槽的数字串还原);int_val = 定点缩放整数 + // (精确表示,默认路径)。宽度后缀退役(2026-09-10 语言面收窄 §2):data 槽 + // 不再承载宽度标注,恒 0。 + bits : int = 0; + tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME); + if tl >= 0 { bits = str_to_f64_bits(istr_get(tl)); } + return alloc_node(EXPR_DEX, bits, 0, 0, tok_iv(t), TY_DEX, 0, tok_ln(t), tok_cl(t)); + } +``` + +- [x] **Step 3: ast.cr 死条目移除(留墓碑,勿重编号)** + +`ast.cr:81-94` 整段替换为: + +```core +// 77-86 原为宽度后缀 token kind(T_INT_I8..T_INT_U64 / T_FLOAT_F32 / T_FLOAT_F64)。 +// 2026-09-10 语言面收窄 §2 删除:lexer 从不发射这些 kind(宽度后缀已退役,改响亮报错), +// parser 侧引用同步移除。**勿重编号**——编号空间稳定(先例:T_FLOAT_TYPE 91 已删除)。 +``` + +`ast.cr:110-119`(`W_I8`…`W_F64`)整段替换为: + +```core +// W_I8..W_F64(1..10)原为「宽度标注」值域,仅供宽度后缀 token 分支使用。 +// 2026-09-10 语言面收窄 §2 随死分支一并删除。**勿重编号**。 +``` + +- [x] **Step 4: 引用面归零验证** + +```bash +nice -n 19 grep -rn "T_INT_I8\|T_INT_I16\|T_INT_I32\|T_INT_I64\|T_INT_U8\|T_INT_U16\|T_INT_U32\|T_INT_U64\|T_FLOAT_F32\|T_FLOAT_F64\|W_I8\|W_I16\|W_I32\|W_I64\|W_U8\|W_U16\|W_U32\|W_U64\|W_F32\|W_F64" src/ --include='*.cr' +nice -n 19 grep -n "T_INT_I64\|W_I64" bootstrap/ -r +``` +Expected: **过滤注释行后**零命中(**代码引用面**归零)——注意墓碑注释正文本身含这些标识符字样,故原始 grep 不会全空(Task 2 实现者实测指出此点,判据按「代码引用零命中」执行):`grep ... | grep -v '^[^:]*:[0-9]*: *//'` 或等价过滤后 rc=1/无输出。 + +- [x] **Step 5: 重建 + 冒烟 + 回归** + +```bash +nice -n 19 python3 build_selfhost_native.py # 期望 BUILD SUCCESS + [GUARD] manifest OK + 日志守卫零 error[/未定义 +nice -n 19 ./build/corec run 'fn main()->int{return 42;}' # 期望 rc=42、无 error[ +nice -n 19 python3 tests/selfhost/test_compile.py # 期望 All selfhost compile tests passed. +``` + +- [x] **Step 6: 提交** + +```bash +jj commit -m 'refactor: 语言面收窄 R1 Task 2——宽度死条目移除(ast.cr 77-86 token kind + W_* 值域,墓碑注释勿重编号)+ parser 两处死分支清理(lexer 从不发射,引用面归零)' +``` + +--- + +## Task 3: 词法面收窄(`_` 分隔符拒绝 + 宽度后缀退役——均响亮报错) + +**Files:** +- Modify: `src/compiler/lexer.cr`(数字扫描 `:418-470`:十进制/小数/`0x`/`0o`/`0b` 循环**均不接受** `_`;后缀段落改为响亮报错;dex 路由去掉 f32/f64 条件;删空分支) +- Modify: `bootstrap/corec/frontend/lexer.py:85-115`(`_` 与字母后缀 → `self.error(...)`) +- Modify: `tests/bootstrap/test_pipeline.py:327-340`(`check_integer_literals`:断言由「`1_000`→1000」改为「拒绝」) +- Modify: `grammar/tokens.ebnf:22-31`、`grammar/core.ebnf:58` +- Create: `tests/selfhost/test_lexer_parity.py` + +**Interfaces:** +- Consumes: Task 2 已删掉宽度 token kind(本任务不再产生它们) +- Produces: 词法契约——数字字面量**不接受** `_` 分隔符、**不接受**任何字母后缀,二者均**响亮报错**;合法形态(十进制/小数/`0x`/`0o`/`0b`/负号)行为不变 + +- [x] **Step 1: 写失败测试 `tests/selfhost/test_lexer_parity.py`** + +```python +#!/usr/bin/env python3 +"""词法对照测试:`_` 分隔符与宽度后缀在两编译器均被拒绝(2026-09-10 语言面收窄 §2)。""" +import sys, os, subprocess +sys.path.insert(0, 'bootstrap') +from corec.frontend.lexer import Lexer + +BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +COREC = os.path.join(BASE, 'build', 'corec') +BAD = ('1_000', '1_000.5', '0x1_0', '10f32', '1.5f64', '10u8') + +def selfhost(src): + r = subprocess.run(['nice', '-n', '19', COREC, 'run', 'fn main()->int{return %s;}' % src], + cwd=BASE, capture_output=True, text=True) + return r.returncode, r.stdout + r.stderr + +def test_bootstrap_rejects(): + for bad in BAD: + try: + Lexer(bad).tokenize() + assert False, 'bootstrap accepted %s' % bad + except SyntaxError: + pass + +def test_selfhost_rejects(): + # 断言必须命中**我们自己的诊断文本**——否则 `return 1_000` 既有的 TF01 会假绿。 + # 注意:lexer 诊断走 src/compiler/lexer.cr add_error,输出前缀是 `error: `, + # **不是** `error[XX]`(实测 `fn main()->int{return 0xZZ;}` → `error: invalid digit + # in integer literal`,rc=1)——故只断言消息标记 + 出现 `error`,不断言 `error[`。 + for bad, marker in (('1_000', 'not supported'), ('1_000.5', 'not supported'), + ('0x1_0', 'not supported'), ('10f32', 'retired'), + ('1.5f64', 'retired'), ('10u8', 'retired')): + rc, out = selfhost(bad) + assert 'error' in out and marker in out, (bad, rc, out) + +def test_legal_forms_unchanged(): + for src, want in (('0x1f', 31), ('0o17', 15), ('0b1010', 10), ('1000', 232)): + rc, out = selfhost(src) + assert 'error[' not in out, (src, out) + assert rc == want, (src, rc, want) + +if __name__ == '__main__': + for fn in (test_bootstrap_rejects, test_selfhost_rejects, test_legal_forms_unchanged): + fn(); print('PASS', fn.__name__) +``` + +- [x] **Step 2: 运行测试确认「红」** + +```bash +nice -n 19 python3 tests/selfhost/test_lexer_parity.py +``` +Expected: 两条 FAIL + 一条对照 PASS —— `test_bootstrap_rejects`(bootstrap 接受 `1_000`)/ `test_selfhost_rejects`(无我们的诊断文本,只有既有 TF01)/ `test_legal_forms_unchanged`(PASS,对照组)。记录实际输出到报告。 + +- [x] **Step 3: self-hosted lexer 改造(`src/compiler/lexer.cr`)** + +(a) 十进制数码循环(`:421-423`)与小数部分循环(`:445`)**保持只吃 digit**(不加 `_`);hex/oct/bin 三条循环(`:428-430`)各**删除** `|| hc == 95`、`|| oc == 95`、`|| bc == 95`(不再接受 `_`)。 + +(b) 后缀段落(`:452-468`)整段替换为: + +```core + // 数字词法收窄(2026-09-10 用户裁决,语言面收窄 §2):`_` 分隔符与宽度后缀 + // (f32/f64/i8..u64)**均不支持**——一律响亮报错且**不消费**残余字符 + // (按标识符继续 tokenize,使后续解析给出第二重信号,不静默、不猜)。 + // 修复前:`_` 被 is_alpha(95) 当后缀静默消费 → T_INT 取「无值」= 0 + // (`x := 1_000` 静默成 0 = 实质误编译);宽度后缀同样静默丢弃。 + sx := cur_char_at(_src, _pos, _slen); + if sx == 95 { + add_error("invalid character '_' in numeric literal (digit separators not supported)"); + } else if is_alpha(sx) != 0 { + add_error("invalid suffix on numeric literal (width suffixes retired 2026-09-10)"); + } + num_str := str_sub(_src, start, _pos - start); + if has_dot != 0 { + add_tok_int_lex(T_DEX, str_to_scaled(num_str), str_intern(num_str), start_line, start_col); + } else { + add_tok_int(T_INT, str_int_literal(num_str), start_line, start_col); + } +``` + +(c) `str_int_literal`(`:221`)、`str_to_scaled`、`str_to_f64_bits` 内部既有的 `_` 跳过逻辑**保留不动**(防御性;删除属无谓风险,且未来若恢复分隔符即插即用)。 + +- [x] **Step 4: bootstrap lexer 对齐(`bootstrap/corec/frontend/lexer.py:85-115`)** + +十进制分支: + +```python + else: + while self.current().isdigit(): + n += self.advance() + if self.current() == '_': + self.error("invalid character '_' in numeric literal (digit separators not supported)") + if self.current() == '.' and self.peek().isdigit(): + is_float = True + n += self.advance() + while self.current().isdigit(): + n += self.advance() + if self.current() == '_': + self.error("invalid character '_' in numeric literal (digit separators not supported)") + if self.current().isalpha(): + # 宽度后缀退役(2026-09-10 语言面收窄 §2)——与 self-hosted lexer 同款 + # 响亮报错;此前后缀被并入词素('10f32')→ parser int() 抛 ValueError。 + self.error("invalid suffix on numeric literal (width suffixes retired 2026-09-10)") +``` + +前缀进制分支(`:88-99`):digits 循环去掉 `or self.current() == '_'`,并在其后加同款 `_` 报错。`n = n.replace('_', '')`(`:117`)已无输入可清,保留无害。 + +- [x] **Step 5: bootstrap 既有测试同步(`tests/bootstrap/test_pipeline.py:327-340`)** + +```python +def check_integer_literals(): + tokens = Lexer('0x1f 0o17 0b1010').tokenize() + values = [int(t.lexeme) for t in tokens[:-1]] + if values != [31, 15, 10]: + print(f'[FAIL] integer literal forms: got {values}') + return False + for bad in ('1_000', '0x1_0', '10f32', '1_000.5'): + try: + Lexer(bad).tokenize() + except SyntaxError: + continue + print(f'[FAIL] rejected form was accepted: {bad}') + return False + try: + Lexer('0x').tokenize() + except SyntaxError: + pass + else: + print('[FAIL] empty prefixed integer was accepted') + return False + print('[PASS] integer literal forms: hex/octal/binary; separators/suffixes rejected') + return True +``` + +- [x] **Step 6: grammar 同步(`grammar/tokens.ebnf` / `grammar/core.ebnf`)** + +`INT_LIT` 行去掉 `[ INT_SUFFIX ]` 并删除 `INT_SUFFIX` 规则;`DEX_LIT` 行去掉 `[ 'f32' | 'f64' ]`。注释段改写为: + +```ebnf +(* 字面量 *) +(* 数字字面量不支持 '_' 分隔符、不支持任何宽度后缀(2026-09-10 语言面收窄 §2 用户裁决: + 两实现统一响亮报错——修复前 self-hosted 把 '_' 当后缀静默消费成取值 0、bootstrap + 静默接受;宽度后缀 = 机器形状,归映射层)。 *) +INT_LIT = DIGIT { DIGIT } ; +``` +(实现者按语法惯例给出等价 EBNF;核心约束 = 无 `_`、无任何后缀产生式。) +`grammar/core.ebnf:58` 的「f32/f64 后缀 = apx CPU 位宽标注」注释改为退役记述。 + +- [x] **Step 7: 重建 + 测试全绿** + +```bash +nice -n 19 python3 build_selfhost_native.py +nice -n 19 python3 tests/selfhost/test_lexer_parity.py # Expected: 三条全 PASS +nice -n 19 python3 tests/bootstrap/test_pipeline.py # Expected: 全 PASS(含改后的 check_integer_literals) +nice -n 19 ./build/corec run 'fn main()->int{x := 1_000; return x;}' # 期望含 'not supported'(lexer 诊断前缀 = `error: `,非 `error[`) +nice -n 19 ./build/corec run 'fn main()->int{return 1000;}' # 期望 rc=232、无任何 error +nice -n 19 python3 tests/selfhost/test_compile.py +``` + +- [x] **Step 8: 提交** + +```bash +jj commit -m 'fix: 语言面收窄 R1 Task 3——数字词法收窄(_ 分隔符与宽度后缀一律响亮报错:消灭 1_000 静默 0 误编译;bootstrap/self-hosted 双侧对齐;grammar + bootstrap 测试同步;新增词法对照测试)' +``` + +--- + +## Task 4: 全局初始化修复(定长数组全局 SIGSEGV + 静默丢初始化) + +**Files:** +- Modify: `src/compiler/ir_gen.cr`(新增 `global_needs_runtime_init` / `global_var_of` / `inject_global_inits`;`ir_gen_func:2222` 在 main 序言调用注入) +- Modify: `src/compiler/interp.cr:67-93`(`ir_interpret` 跑 main 前补「标量常量初始化」阶段) +- Create: `tests/selfhost/test_global_init.py` + +**Interfaces:** +- Consumes: `g_global_lets`(`{a=name_idx, b=type_node, c=value_node, int_val=apx 位模式|null}`);`g_ir_globals`(每 24B:`+0 name_idx`、`+8 ir_var`、`+16 init_val`,已由 `main.cr:408 ir_gen_globals()` 填充);`IR_ALLOC_ARRAY(8)`、`IR_STORE(9)`、`IR_STORE_INDEX`;`gen_expr(node)`、`new_ir_var`、`irv_set_type`、`emit` +- Produces: 全局初始化语义——(1) 聚合全局(`[T;N]`/`[T]`)无论有无初值都在运行期分配 + 写槽(存储持久,`alloc` 语义);(2) 非常量初值的全局在运行期求值并写槽;(3) 编译期标量常量初始化保持既有通道(ELF `_start` 常量循环),解释器补同语义阶段 + +- [x] **Step 1: 写失败测试 `tests/selfhost/test_global_init.py`** + +覆盖下表(解释器 `run` 与 ELF `build --static` + 运行二进制**双路径**;ELF 调用模式照抄 `tests/selfhost/test_native_memory.py:110-130`): + +| # | 源 | 期望 rc | +|---|---|---| +| 1 | `g : [int;2] = [8,9];\nfn main()->int{return g[1];}` | 9 | +| 2 | `g : [int;2];\nfn main()->int{ g[1]=7; return g[1]; }` | 7 | +| 3 | `a : [int;2] = [8,9];\nb : [int;2] = a;\nfn main()->int{return b[1];}` | 9 | +| 4 | `g : [int;4] = [1,2,3,4];\nfn main()->int{return 5;}` | 5 | +| 5 | `x : int, mut = 5;\nfn main()->int{ x = x + 1; return x; }` | 6 | +| 6 | `x : int = 5;\nfn main()->int{return x;}`(回归:常量折叠) | 5 | +| 7 | `g : [int;2] = [3,4];\nfn get()->int{return g[1];}\nfn main()->int{return get();}`(跨函数访问) | 4 | +| 8 | `a : [int;2] = [8,9];\nb : [int;2] = a;\nfn main()->int{return a[0]+b[1];}` | 17 | + +每例两条断言:**无 `error[`** + rc 精确值。 + +- [x] **Step 2: 运行确认「红」(两路径)** + +**前置(必须先做)——清增量缓存**:`inject_global_inits` 的注入点在 `ir_gen_func` 内(`main.cr:480` 调用),而**函数命中 cir 缓存时根本不走 `ir_gen_func`**(`main.cr:483` 的 cache-hit 分支)——叠加已知缺陷 TODO #5(缓存键不含编译器身份,跨重建不失效),**旧缓存会让本次修复看起来"没生效"**。故每次判据前: + +```bash +nice -n 19 ./build/corec clean-cache # 若子命令不可用:rm -rf .core/cache(先 ls 确认路径) +``` + +并在报告里记录该交互(缓存键/失效面 = TODO #5,不在本批修)。测试脚本自身不必清缓存,但**判据运行必须在清缓存后进行**。 + +```bash +nice -n 19 python3 tests/selfhost/test_global_init.py +``` +Expected: 例 1/2/3/7/8 双路径 FAIL(rc=139);例 5 FAIL(rc=1);例 4/6 PASS。记录原始输出(rc + 信号)到报告。 + +- [x] **Step 3: 捕获 ELF 产物基线(零变化判据)** + +```bash +nice -n 19 ./build/corec build tests/suite/<无运行期初始化全局的样本>.cr --static -o /tmp/r1t4_base_bin +sha256sum /tmp/r1t4_base_bin | tee /tmp/r1t4_base_sha +``` +(样本自选:`tests/suite/` 中不含文件级聚合/非常量全局的用例,例如 control-flow 类。) + +- [x] **Step 4: 前端修复(`src/compiler/ir_gen.cr`)** + +新增(建议置于 `global_init_val` 之后): + +```core +// 文件级 let 是否需要「运行期初始化」(2026-09-10 语言面收窄 §1.3 / R1 ②)。 +// 判据(与 global_init_val 互补——它只认编译期标量常量,其余静默归 0): +// ① 声明类型是聚合(语法节点 EXPR_ARRAY,含 `[T; N]` 与 `[T]`)→ 必须运行期分配存储; +// ② 初值存在且不是 int/bool/dex 字面量(含负号字面量)→ 必须运行期求值。 +fn global_needs_runtime_init(name_idx: int) -> int { + i : ., mut = g_global_let_count - 1; + loop { + if i < 0 { break; } + node := r64(g_global_lets, i * 8); + if ast_a(node) == name_idx { + tn := ast_b(node); + if tn >= 0 && ast_kind(tn) == EXPR_ARRAY { return 1; } + vn := ast_c(node); + if vn < 0 { return 0; } + vk := ast_kind(vn); + if vk == EXPR_INT || vk == EXPR_BOOL || vk == EXPR_DEX { return 0; } + if vk == EXPR_UNARY && ast_c(vn) == UOP_NEG { + inner := ast_a(vn); + ik := ast_kind(inner); + if ik == EXPR_INT || ik == EXPR_BOOL || ik == EXPR_DEX { return 0; } + } + return 1; + } + i = i - 1; + } + return 0; +} + +// 按 name_idx 查已注册的全局 IR var(未注册 = -1)。 +fn global_var_of(name_idx: int) -> int { + gi : ., mut = 0; + loop { + if gi >= g_ir_global_count { break; } + if r64(g_ir_globals, gi * 24) == name_idx { return r64(g_ir_globals, gi * 24 + 8); } + gi = gi + 1; + } + return -1; +} + +// 把需要运行期初始化的文件级 let 降级为 IR 序列(写进当前函数 = main 序言)。 +// 顺序 = g_global_lets 源序(跨全局依赖如 `b : [int;2] = a;` 因此正确)。 +fn inject_global_inits() { + i : ., mut = 0; + loop { + if i >= g_global_let_count { break; } + lnode := r64(g_global_lets, i * 8); + name_idx := ast_a(lnode); + if global_needs_runtime_init(name_idx) != 0 { + gv := global_var_of(name_idx); + if gv >= 0 { + vn := ast_c(lnode); + v : ., mut = -1; + if vn >= 0 { + v = gen_expr(vn); + v = force_if_thunk(v); + } else { + // 无初值聚合:整流分配(零初始化由 alloc 语义保证——rt.s:94-98 rep stosb; + // 解释器 IR_ALLOC_ARRAY 显式清零 interp.cr:197-207)。 + // 仅 `[T; N]`(N ≥ 1)注入;`[T]`(切片无长度,ast_int_val = 0)**不注入** + // ——保持 BSS 零 = 空切片/哑指针,空切片解引用属独立 null 陷阱类,不在本批。 + tn := ast_b(lnode); + cnt : ., mut = 0; + if tn >= 0 { cnt = ast_int_val(tn); } + if cnt > 0 { + v = new_ir_var("ginit", TI_UNIT); + emit(IR_ALLOC_ARRAY, v, cnt, 0, 0, 0); + irv_set_type(v, alloc_type(TYP_ARRAY, TI_INT, cnt)); + } + } + if v >= 0 { emit(IR_STORE, -1, gv, v, 0, 0); } + } + } + i = i + 1; + } +} +``` + +在 `ir_gen_func`(`ir_gen.cr:2222`)内、`emit(IR_ARENA_NEW, …)`(`:2269-2271`)之后、`gen_expr(body)` 之前插入: + +```core + // 程序启动期全局初始化(2026-09-10 R1 ②):只在 main 序言注入——run 与 build + // 两路径都从 main 进入(interp.cr:74-79 按名查找 main;ELF _start call main), + // 因此无需新 IR 函数/新 .ccr 段/新 _start 指令 = 无序列化与字节布局扰动。 + if str_eq(istr_get(name_idx), "main") != 0 { inject_global_inits(); } +``` + +- [x] **Step 5: 解释器修复(`src/compiler/interp.cr`)** + +`ir_interpret()`(`:67`)在「清零 value store」(`:86-93`)之后、预扫描 label 之前插入: + +```core + // 编译期标量常量全局初始化阶段——与 ELF _start 的常量初始化循环(os/linux/entry.cr:72-98) + // 同语义、同数据源(g_ir_globals +16 的 init_val)。此前解释器无此阶段, + // 可变标量全局读回 0(不可变标量因 find_global_const_node 折叠而侥幸正确)。 + gi2 : ., mut = 0; + loop { + if gi2 >= g_ir_global_count { break; } + iv2 := r64(g_ir_globals, gi2 * 24 + 16); + gv2 := r64(g_ir_globals, gi2 * 24 + 8); + if iv2 != 0 && gv2 >= 0 && gv2 < need { w64(g_ir_vals, gv2 * 8, iv2); } + gi2 = gi2 + 1; + } +``` + +- [x] **Step 6: 重建 + 测试全绿 + 零变化判据** + +```bash +nice -n 19 python3 build_selfhost_native.py +nice -n 19 python3 tests/selfhost/test_global_init.py # Expected: 全 PASS(双路径) +nice -n 19 ./build/corec build tests/suite/<同 Step 3 样本>.cr --static -o /tmp/r1t4_head_bin +cmp /tmp/r1t4_base_bin /tmp/r1t4_head_bin && echo BYTE-IDENTICAL +nice -n 19 python3 tests/selfhost/test_compile.py +nice -n 19 python3 tests/bootstrap/test_pipeline.py +``` +Expected: `BYTE-IDENTICAL`(无运行期初始化全局的程序 ELF 产物逐字节不变)。 + +- [x] **Step 7: 提交** + +```bash +jj commit -m 'fix: 语言面收窄 R1 Task 4——全局初始化修复(聚合/非常量全局降级为 main 序言 IR 序列:alloc+逐元素存+写槽;解释器补标量常量初始化阶段)——修 139 崩溃与静默 0;新增双路径测试' +``` + +--- + +## Task 5: 收官(回归 + 自举 + 文档 + 台账) + +**Files:** +- Modify: `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md`(状态行 + §3 波 R1 标记) +- Modify: `TODO.md`(宽度清理三项划销 + 定长裁决挂账处置 + 定长全局修复记录) +- Modify: `.superpowers/sdd/progress.md`(台账) +- Modify: `tests/selfhost/test_compile.py:20-38`(陈旧清单路径——见 Step 5) + +- [x] **Step 1: 全量回归** + +```bash +nice -n 19 python3 tests/selfhost/test_compile.py +nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py +nice -n 19 python3 tests/selfhost/test_hit_table.py +nice -n 19 python3 tests/selfhost/test_ccr_v7.py +nice -n 19 python3 tests/selfhost/test_live_ranges.py +nice -n 19 python3 tests/selfhost/test_slice_bounds.py +nice -n 19 python3 tests/selfhost/test_global_init.py +nice -n 19 python3 tests/selfhost/test_lexer_parity.py +nice -n 19 python3 tests/bootstrap/test_pipeline.py +nice -n 19 python3 tests/bootstrap/test_borrow.py +nice -n 19 python3 tests/bootstrap/test_generics.py +``` +Expected: 全绿。 + +- [x] **Step 2: 自举全链 + 守护** + +```bash +nice -n 19 python3 build_selfhost_native.py # BUILD SUCCESS + 两处 [GUARD] OK +# corec 前端三代链(corec2/corec3)**不在** test_backend_bootstrap 覆盖内——那套覆盖的是 +# corearch 的 stage1/2/3 byte-identical;corec 链按 src/ci/run.sh 的 full-bootstrap 段亲跑: +nice -n 19 ./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 +/tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 +cmp /tmp/corec2 /tmp/corec3 # 期望 IDENTICAL +nice -n 19 ./build/corec run 'fn main()->int{return 42;}' # rc=42 +``` +(口径修正来源 = Task 5 实现者实测:计划原写「由 test_backend_bootstrap 覆盖」不实,已按 run.sh 同款。) + +- [x] **Step 3: 文档同步** + +- spec 状态行 → 「**已实施(R1)**」+ §3 波 R1 三项逐条标记完成 + 记录三处行为变化(`1_000`、宽度后缀、全局初始化) +- `TODO.md`:宽度类型移出语言条目内三项(死条目移除 / `_f32/_f64` 后缀 / `1_000` 复核)划销并注记落点;定长裁决条目注记「类型身份退役 = R2 落实,R1 注记 + 全局路径已修」;新增「全局初始化机制」记录(main 序言注入 + 解释器常量阶段;未来若支持非 main 入口/库形态需迁移到独立 init 区) +- `TODO.md` **新登记**(R1 实测发现):**lexer 诊断前缀不一致**——lexer 走 `add_error`(`lexer.cr:38-44`)输出 `error: `(**无错误码**),checker/其余走 `diag.cr:134` 的 `error[XX]`;既有守卫与测试门(扫 `error[`)对 lexer 诊断**零覆盖**(实测 `return 0xZZ;` → `error: invalid digit in integer literal`,全无 `error[`)。建议统一格式或扩展守卫扫描面 +- `TODO.md` **新登记(Task 3 评审 Minor)**:① lexer 诊断**重复打印 4 遍**(多轮 tokenize 累积、`tokenize()` 不清零 `g_error_count`——既有行为,父版同款注释);② 边缘形态分歧(既有,超出本批判据集):`1._5`(SH 响亮报错 vs bootstrap 词法层 INT(1)+DOT+IDENT(_5),整管线下仍报错、无静默接受)、`1.`(既有分歧);③ `0x_` 诊断措辞优先级变化(现报分隔符消息,原报 invalid integer literal——两者皆错误,仅措辞) +- `TODO.md` **新登记(Task 4 评审发现)**: + - **Important(既有缺口,双路径分叉)**:解释器 callee 内联路径仍缺枚举族 opcode(如 `IR_MAKE_ENUM(17)`)——枚举值在 callee 内构造时 interp 与 ELF 结果分叉(复现件:`build/review_t4/p14_enum_in_callee.cr` interp 0 vs ELF 33;`p15_enum_split.cr` interp -11 vs ELF 33);本批 15 例未覆盖。建议按 Task 4 §6.2 同款「与主循环同语义同守卫」补齐 + - **Minor**:类型别名仅**一层**解析(`type A = [int;2]; type B = A; g : B;` 仍双路径 SIGSEGV;有初值形态工作)——`agg_elem_count_of` 需递归/迭代至底 + - **Minor(测试卫生,随本任务修)**:`tests/selfhost/test_global_init.py` 的 `clean_cache()` 返回码未检查(清缓存失败 + TODO #5 旧缓存 = 判据被静默污染——正是本项目大忌)→ 改为断言 rc=0;另删未用的 `import sys` + - **Minor(记录,不修)**:Task 4 报告 §6.5 称注入在 `g_cur_ret_ti = ret_ti` **之后**,实际在**之前**(`ir_gen.cr:2282`,= brief 指定位置)——报告措辞与代码不符(读码无歧义);`alloc_type(TYP_ARRAY, TI_INT, cnt)` 硬编码元素类型 TI_INT(无观测差异,元素尺寸恒 8) +- **Task 2 评审遗留(Minor)随本步清账**: + - M1:`parser.cr` dex 分支恢复被替换文本丢掉的**行尾注释** `// 词素串下标(-1 = 无)`(信息有损、零行为——纯注释恢复) + - M3:**文档面随迁**(Task 2 只动 `src/`)——`docs/pseudocode/compiler/parser-2.md:167-205,729-740`、`docs/pseudocode/compiler/ast.md:87-118,445-490`、`docs/numeric-migration-inventory.md:29,32,51,148,190,230-231,244` 中引用已删条目处逐条更新/标注(其中 `:148` 声称 `src/lsp/analysis.cr:895` 有 `(k >= T_INT_I8 && k <= T_FLOAT_F64)` 区间——**实测不存在**,该行本身即过时,按实测修正) + - Info(不处理,仅记录):`build/all.cr`、`build/bootstrap_self.cr`、`build/selfhost_compiler.cr` = 陈旧拼接产物(忽略项,不入树) + +- [x] **Step 4: 台账** + +`.superpowers/sdd/progress.md` 追加 R1 段(各任务提交哈希 + 判据结果)。 + +- [x] **Step 5: 测试清单陈旧路径核对(`tests/selfhost/test_compile.py:20-38`)** + +`concat_sources()` 的 `if os.path.exists(path)` 会**静默跳过**缺失文件——现清单含三条已搬迁路径(`src/compiler/backend/x86_64.cr`、`src/compiler/backend/x86_64/instr.cr`、`src/compiler/backend/resolve.cr` 均已不存在;波 1 三轴搬迁后为 `src/arch/x86_64/*.cr` + `src/format/elf/*.cr` + `src/os/linux/*.cr`)。 + +处理:把条目更新为三轴现状路径(对照 `build_selfhost_native.py` 的 `corearch_files`/`arch_x86_64_files`/`format_elf_files`/`os_linux_files`),并把 `exists` 静默跳过改为**存在性断言**(`build_selfhost_native.py:144-152 guard_manifest` 同款)。复跑 `test_compile.py` 必须仍全绿。 + +若补入后测试失败 → 说明原清单是刻意裁剪:改为在原处加**显式注释**说明裁剪意图(保 `exists` 语义)+ 在 `TODO.md` 登记本次发现——**不得**用还原/静默跳过掩盖。 + +- [x] **Step 6: 提交** + +```bash +jj commit -m 'docs: 语言面收窄 R1 收官——全量回归 + 自举守护全绿 + spec/TODO/台账同步 + test_compile 清单陈旧路径修正(存在性断言)' +``` + +--- + +## 自检记录(写完计划后复核) + +- **spec 覆盖**:§1(定长裁决)→ Task 1(注记)+ Task 4(全局路径修复);§1.3「checker 落实时点 = R2」→ 明确不在本批;§2(宽度三项)→ Task 2(死条目)+ Task 3(后缀/`1_000`);§3 波 R1 判据 → Task 5。**无遗漏,无越界**(表示提示机制留 R3、checker 类型判定重构留 R2)。 +- **占位符扫描**:无 TBD/TODO;所有代码块为可落地的具体代码;所有命令带期望输出。 +- **类型/命名一致性**:`global_needs_runtime_init` / `global_var_of` / `inject_global_inits` 三名在 Task 4 内自洽;`IR_ALLOC_ARRAY`=8、`IR_STORE`=9 与 `interp.cr:176-187` 一致;`emit(IR_STORE, d, s1, s2, …)` 参数序与 `interp.cr:187` 一致(`s1` 目标、`s2` 值)。 +- **风险注记**:Task 4 的序言注入只对「需要运行期初始化」的全局生效 → 其余程序 IR 不变(Step 3/6 的 byte-identical 判据即此保证);注入点选择 main 序言的理由(无新 IR 函数/无 .ccr 段变更/无 `_start` 字节扰动)已写明,若未来出现非 main 入口或库形态,需迁移到独立 init 区(已记入 TODO)。 diff --git a/docs/superpowers/plans/2026-09-10-r2-p0-type-engine.md b/docs/superpowers/plans/2026-09-10-r2-p0-type-engine.md new file mode 100644 index 00000000..f9400045 --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-r2-p0-type-engine.md @@ -0,0 +1,942 @@ +# R2 P0(建层)实施计划:类型项表 + 语义包含引擎 + 判定用例表 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 为 R2 建「类型项层 + 语义包含引擎」的地基:集合语义的类型项 DAG 表、NNF/DNF 规范化、包含/等价/不相交/可空/反例判定 API(三态 + 预算守卫)、数据驱动判定用例表与 `corec selftest-types` 自测通道——**checker 零改动、产物零变化**。 + +**Architecture:** 三个新文件分工:`type_terms.cr`(类型项表 + 构造/去重 + 规范化)、`type_engine.cr`(判定 API + 原子互斥公理 + μ memo + 预算守卫)、`type_selftest.cr`(用例表 + 自测驱动)。引擎自带原子类 id(`AK_*`)与 `g_types` 行解耦,natives 用 1:1 映射,结构/命名原子挂 `g_types` 行(`-1` = 类级)。**不改 checker / ir_gen / 后端 / 解释器**——P0 不改判据行为,只建能力。 + +**Tech Stack:** Core 自举栈(self-hosted 编译器 `src/compiler/`);扁平字节缓冲表(`string` + `w64/r64` + `grow_*` + `ESZ_*/OFF_*` 常量,先例 `g_types`/`g_ifaces`);CLI 通道(`cli_cmd` + `corec_main` 分派);Python 测试包装(`tests/selfhost/test_*.py` 风格,subprocess 调 `./build/corec`)。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(§0 裁决 / §1 语义基座 / §3 类型项层与引擎 / §4 步 1 建层 / §9 P0)。 + +## Global Constraints + +- **jj only**:版本控制一律 `jj`(git 被 hook 机械拦截);**提交必须路径限定**(多 agent 共用工作副本): + `jj commit <本次改动的精确文件...> -m '<中文,前缀 feat:/docs:/test:>'` +- **CPU 限速**:所有编译/测试命令必须 `nice -n 19` 前缀(铁律 #6)。 +- **文件永久不允许还原**;不得绕过/变通;根因修复。 +- **P0 零行为改动**:`checker.cr` / `ir_gen.cr` / `interp.cr` / 后端与内核文件**一行不动**;现有程序的编译产物 **byte-identical**(判据见 Task 5)。 +- **diff 校验规范式样**(本机 `jj diff` 默认 color-words 无 `+` 前缀,`grep '^+'` 恒真空通过;grep 实为 ugrep): + ```bash + jj diff --git | grep '^[+]' | grep -v -e '^[+][+][+]' # 逐行核新增 + jj diff --git | grep -c '^[-]' # 期望 0(除计划性替换) + ``` +- 环境陷阱:noclobber(`>` 覆盖已存在文件失败 → 用 `>|` 或先删);变量展开不分词。 +- **新文件必须注册三处**(否则构建静默缺件): + 1. `src/compiler/_import.cr`(project-mode 解析链) + 2. `build_selfhost_native.py` 的 **corec 清单**(`corec_files`,顺序 = 行为定义面:置于 `src/compiler/checker.cr` 之后、`ir_gen.cr` 之前) + 3. `tests/selfhost/test_compile.py` 的 `concat_sources()` 清单(同位置) + (`corelsp` / `corearch` 清单 P0 **不加**:引擎尚无消费者。) +- **判据铁律**:`corec selftest-types` 全 PASS + 末行计数断言 + 无 `FAIL` 行;全回归绿;自举 `corec2/corec3` `cmp` IDENTICAL + N06=0。 +- **附加验证纪律(Task 1 实证教训,2026-09-10)**:白盒/压力验证**必须限时**——禁止 `sleep` 式长等待(用短超时或直接看日志);同一缺陷最多迭代 **2 轮**(≥3 轮说明假设错了,改为最小化失败用例 + BLOCKED 上报);**计划代码不是免检的**(本次 `h - (h/cap)*cap` 负哈希取模越界即为计划缺陷,由压力测试抓出)。 + +--- + +## 文件结构(P0) + +| 文件 | 责任 | +|---|---| +| `src/compiler/type_terms.cr`(新建) | 类型项表 `g_type_terms` + 构造/去重(DAG)+ 访问器 + NNF + DNF 规范化 | +| `src/compiler/type_engine.cr`(新建) | 原子类 id/互斥公理 + 判定 API(`ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited`/`tt_witness`)+ memo + 预算守卫 | +| `src/compiler/type_selftest.cr`(新建) | 用例表(数据驱动)+ `type_selftest_run()` 驱动(打印 PASS/FAIL + 摘要,返回 rc) | +| `src/compiler/globals.cr`(修改) | 新增表全局声明(`g_type_terms` / `g_tt_*` / memo / budget / 引擎侧缓冲) | +| ~~`src/compiler/dyn_arr.cr`~~ | **不入本批**——扩容函数由引擎自持(`type_terms.cr`)。理由(Task 1 实测,D5):`dyn_arr.cr` 属**双 concat 共享面**(`build_selfhost_native.py` 的 `common_files`,corearch/corelsp 都编),而引擎文件只入 corec 清单 → 在共享文件里引用引擎符号 = corearch 解析期 `Undefined name: ESZ_TYPE_TERM / tt_reindex` 硬失败 | +| `src/compiler/main.cr`(修改) | `cli_cmd("selftest-types", ...)` + `corec_main` 分派 | +| `src/compiler/_import.cr`(修改) | 注册三个新模块 | +| `build_selfhost_native.py`(修改) | corec 清单加三个文件 | +| `tests/selfhost/test_compile.py`(修改) | concat 清单加三个文件 | +| `tests/selfhost/test_type_engine.py`(新建) | 子进程跑 `corec selftest-types`,断言 rc/摘要/零 FAIL | + +--- + +## Task 1: 类型项表 + 构造 API + 自测通道骨架 + +**Files:** +- Create: `src/compiler/type_terms.cr` +- Create: `src/compiler/type_selftest.cr` +- Modify: `src/compiler/globals.cr`(表全局声明)、`src/compiler/main.cr`(CLI)、`src/compiler/_import.cr`、`build_selfhost_native.py`、`tests/selfhost/test_compile.py` + (**不含 `dyn_arr.cr`**——扩容函数放 `type_terms.cr` 自持,理由见「文件结构」表注) +- Create: `tests/selfhost/test_type_engine.py` + +**Interfaces(本任务产出,后续任务依赖,签名不可改):** + +``` +// —— 标签常量(type_terms.cr)—— +TT_BOT=0 TT_TOP=1 TT_TOP_K=2 TT_UNION=3 TT_INTER=4 TT_NOT=5 +TT_MU=6 TT_ATOM=7 TT_VAR=8 TT_NIL=9 TT_CONS=10 +ESZ_TYPE_TERM=48 // {tag, a, b, c, d, hash} 各 8B +OFF_TT_TAG=0 OFF_TT_A=8 OFF_TT_B=16 OFF_TT_C=24 OFF_TT_D=32 OFF_TT_HASH=40 + +// —— 构造(全部返回类型项行号;DAG 去重)—— +fn tt_term(tag: int, a: int, b: int, c: int, d: int) -> int // 构造/复用 +fn tt_bot() -> int +fn tt_top() -> int +fn tt_top_k(atom_kind: int) -> int +fn tt_union(a: int, b: int) -> int +fn tt_inter(a: int, b: int) -> int +fn tt_not(a: int) -> int +fn tt_mu(var: int, body: int) -> int +fn tt_var(v: int) -> int +fn tt_atom(atom_kind: int, atom_ti: int, params: int) -> int // params = CONS 链或 -1 +fn tt_nil() -> int +fn tt_cons(head: int, tail: int) -> int + +// —— 访问器 —— +fn tt_tag(i: int) -> int +fn tt_a(i: int) -> int +fn tt_b(i: int) -> int +fn tt_c(i: int) -> int +fn tt_d(i: int) -> int +fn tt_count() -> int + +// —— 自测(type_selftest.cr)—— +fn type_selftest_run() -> int // 0 = 全过;1 = 有失败(打印每例 PASS/FAIL + 末行 "N/M type-engine cases passed") +``` + +- [ ] **Step 1: 写失败测试 `tests/selfhost/test_type_engine.py`** + +```python +#!/usr/bin/env python3 +"""R2 P0 判据:类型项引擎自测通道(corec selftest-types)。 + +引擎 = 集合语义的包含判定(spec docs/superpowers/specs/2026-09-10-type-interface-unification-design.md §3)。 +P0 只建层:checker 零改动、产物零变化;判据 = 用例表全 PASS + 零 FAIL + 计数行。 +""" + +import re +import subprocess +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 用例数下限:Task 1 = 建层,用例表 = 5 例(最小面:bot_sub_int/int_sub_str/int_sub_int/ +# dedup/notnot);Task 3/4 填满 spec §8 全类后**必须把此常量抬到 32**(Task 4 Step 4 明文要求)。 +MIN_CASES = 5 + + +def run_selftest(): + return subprocess.run( + ["nice", "-n", "19", str(COREC), "selftest-types"], + cwd=BASE, capture_output=True, text=True, + ) + + +def test_selftest_types_all_pass(): + r = run_selftest() + out = r.stdout + r.stderr + assert "FAIL" not in out, out + m = re.search(r"(\d+)/(\d+) type-engine cases passed", out) + assert m, out + passed, total = int(m.group(1)), int(m.group(2)) + assert passed == total and total >= MIN_CASES, (passed, total, out) + assert r.returncode == 0, (r.returncode, out) + + +if __name__ == "__main__": + test_selftest_types_all_pass() + print("PASS test_selftest_types_all_pass") +``` + +- [ ] **Step 2: 运行确认「红」** + +```bash +nice -n 19 python3 tests/selfhost/test_type_engine.py +``` +Expected: FAIL —— `selftest-types` 未注册(`corec` 打印未知子命令/无 `N/M ... passed` 行)。 + +- [ ] **Step 3: 表全局声明(`src/compiler/globals.cr`)** + +追加(与既有表声明同风格): + +```core +// R2 P0 类型项引擎:类型项 DAG 表 + 结构哈希索引 + 判定 memo + 预算守卫 +g_type_terms : string, mut; g_type_term_count : int, mut; g_type_term_cap : int, mut; +g_tt_index : string, mut; g_tt_index_cap : int, mut; g_tt_index_count : int, mut; +g_tt_nil : int, mut; g_tt_top : int, mut; +g_ty_memo_keys : string, mut; g_ty_memo_vals : string, mut; +g_ty_memo_count : int, mut; g_ty_memo_cap : int, mut; +g_ty_steps : int, mut; g_ty_budget_max : int, mut; g_ty_exhausted : int, mut; +g_ty_lits : string, mut; g_ty_lits_cap : int, mut; g_ty_lits_count : int, mut; +g_ty_uncovered : int, mut; // 未覆盖面命中位(如 AK_NAMED 具体行不展开)——P0 只登记不消费 +``` + +- [ ] **Step 4: 扩容函数(**落在 `src/compiler/type_terms.cr`,不放 `dyn_arr.cr`**)** + +> **位置修正(Task 1 实测 D5)**:照 `grow_types` 先例放 `dyn_arr.cr` 会让 **corearch 解析期硬失败**(`Undefined name: ESZ_TYPE_TERM / tt_reindex`)——`dyn_arr.cr` 是双 concat 共享面。引擎自持扩容与常量(Step 5 代码块内),共享面零改动。 + +追加(照 `grow_types` 先例): + +```core +fn grow_type_terms(needed: int) { + if needed < g_type_term_cap { return; } + nc : ., mut = g_type_term_cap * 2; + if nc < 256 { nc = 256; } + if nc < needed { nc = needed + 64; } + nb := alloc(nc * ESZ_TYPE_TERM); + _dyncpy(g_type_terms, g_type_term_cap * ESZ_TYPE_TERM, nb); + g_type_terms = nb; + g_type_term_cap = nc; +} + +fn grow_tt_index(needed: int) { + // 开放寻址索引:容量取 2 的幂、≥ 2×项数(重建式扩容,见 type_terms.cr 的 tt_reindex) + if needed < g_tt_index_cap { return; } + nc : ., mut = 1024; + loop { if nc >= needed { break; } nc = nc * 2; } + nb := alloc(nc * 8); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 8, -1); i = i + 1; } + g_tt_index = nb; + g_tt_index_cap = nc; + g_tt_index_count = 0; + tt_reindex(); +} +``` + +- [ ] **Step 5: 类型项表 + 构造 API(`src/compiler/type_terms.cr`)** + +> **Task 1 落地确认的三条实现约定(Task 2/3/4 必须沿用,勿回退)**: +> ① **键 = 五字段全比,哈希不入键**(哈希只负责槽位定位)——「存储哈希 == 重算哈希」只在 i64 回绕下成立,而 bootstrap 解释器为任意精度整数 ⇒ 该等式恒假、引擎静默退化为「只插不查」(Task 1 实证)。 +> ② **惰性 memo 用零初值 + ready 位**(`g_tt_top_ok`/`g_tt_nil_ok`)——全局 `= -1` 初值被 bootstrap 后端降级为 0(`bootstrap/corec/frontend/ir_gen.py:68-74`:`constant_value` 只对 `Literal` 赋值 → 其余 `.quad 0`;见 TODO #14)。 +> ③ **本语言无位异或算子** → 哈希用 FNV-1 加法折叠(照 `dyn_arr.cr` 的 `hash_bytes` 家族),不用 FNV-1a 的 `^`。 + +```core +// R2 P0:类型项表(集合语义的类型项 DAG) +// 语义见 spec §1:τ ::= ⊥ | ⊤ | ⊤ₖ | τ₁∪τ₂ | τ₁∩τ₂ | ¬τ | μX.τ | X | k(τ₁…τₙ) +// 条目 48B {tag, a, b, c, d, hash};同构项共享(DAG),索引 = 开放寻址哈希表 g_tt_index。 +// 本文件只做「表示 + 规范化」;判定在 type_engine.cr。 + +// 标签常量 +TT_BOT : int = 0; TT_TOP : int = 1; TT_TOP_K : int = 2; +TT_UNION : int = 3; TT_INTER : int = 4; TT_NOT : int = 5; +TT_MU : int = 6; TT_ATOM : int = 7; TT_VAR : int = 8; +TT_NIL : int = 9; TT_CONS : int = 10; +ESZ_TYPE_TERM : int = 48; +OFF_TT_TAG : int = 0; OFF_TT_A : int = 8; OFF_TT_B : int = 16; +OFF_TT_C : int = 24; OFF_TT_D : int = 32; OFF_TT_HASH : int = 40; + +fn tt_tag(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG); } +fn tt_a(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A); } +fn tt_b(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B); } +fn tt_c(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C); } +fn tt_d(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D); } +fn tt_count() -> int { return g_type_term_count; } + +fn tt_hash5(tag: int, a: int, b: int, c: int, d: int) -> int { + // FNV-1a 变体:逐槽 fold(确定性、与插入顺序无关——DAG 去重的键) + h : ., mut = 1469598103934665603; + slot : ., mut = 0; + loop { + if slot >= 5 { break; } + v : ., mut = tag; + if slot == 1 { v = a; } else if slot == 2 { v = b; } + else if slot == 3 { v = c; } else if slot == 4 { v = d; } + k : ., mut = 0; + loop { + if k >= 8 { break; } + byte := (v - (v / 256) * 256); + h = h ^ byte; + h = h * 1099511628211; + v = v / 256; + k = k + 1; + } + slot = slot + 1; + } + return h; +} + +fn tt_mod(h: int, cap: int) -> int { + // 非负取模(**必须用这个,不要写 h - (h/cap)*cap**): + // i64 除法向零截断——h 为负时上式得到负下标 → 探针越界 → 去重静默失效 + // (2026-09-10 P0 Task 1 压力测试实证:扩容后 dedup 全灭)。 + m : ., mut = h / 2; // 折半再去符号:避免 i64min 直接取负溢出 + if m < 0 { m = 0 - m; } + return m - (m / cap) * cap; +} + +fn tt_reindex() { + // 重建索引(扩容后调用):遍历现有项,重放开放寻址插入 + i : ., mut = 0; + loop { + if i >= g_type_term_count { break; } + h := r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH); + p : ., mut = tt_mod(h, g_tt_index_cap); + loop { + if r64(g_tt_index, p * 8) < 0 { break; } + p = p + 1; if p >= g_tt_index_cap { p = 0; } + } + w64(g_tt_index, p * 8, i); + g_tt_index_count = g_tt_index_count + 1; + i = i + 1; + } +} + +fn tt_same(i: int, j: int) -> int { + if tt_tag(i) != tt_tag(j) { return 0; } + if tt_a(i) != tt_a(j) { return 0; } + if tt_b(i) != tt_b(j) { return 0; } + if tt_c(i) != tt_c(j) { return 0; } + if tt_d(i) != tt_d(j) { return 0; } + return 1; +} + +fn tt_term(tag: int, a: int, b: int, c: int, d: int) -> int { + // 构造/复用:查哈希索引(开放寻址),未命中则追加 + if g_tt_index_cap <= 0 { grow_tt_index(2); } + h := tt_hash5(tag, a, b, c, d); + p : ., mut = tt_mod(h, g_tt_index_cap); // 非负取模(见 tt_mod 注释:负数取模是越界静默失效源) + loop { + idx := r64(g_tt_index, p * 8); + if idx < 0 { break; } + if r64(g_type_terms, idx * ESZ_TYPE_TERM + OFF_TT_HASH) == h { + if tt_tag(idx) == tag && tt_a(idx) == a && tt_b(idx) == b + && tt_c(idx) == c && tt_d(idx) == d { return idx; } + } + p = p + 1; if p >= g_tt_index_cap { p = 0; } + } + grow_type_terms(g_type_term_count + 1); + if (g_type_term_count + 1) * 2 >= g_tt_index_cap { grow_tt_index((g_type_term_count + 1) * 2); return tt_term(tag, a, b, c, d); } + i := g_type_term_count; + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG, tag); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A, a); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B, b); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C, c); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D, d); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH, h); + g_type_term_count = i + 1; + w64(g_tt_index, p * 8, i); + g_tt_index_count = g_tt_index_count + 1; + return i; +} + +fn tt_bot() -> int { return tt_term(TT_BOT, 0, 0, 0, 0); } +fn tt_top() -> int { if g_tt_top < 0 { g_tt_top = tt_term(TT_TOP, 0, 0, 0, 0); } return g_tt_top; } +fn tt_top_k(k: int) -> int { return tt_term(TT_TOP_K, k, 0, 0, 0); } +fn tt_union(a: int, b: int) -> int { + if a == b { return a; } + if tt_tag(a) == TT_BOT { return b; } + if tt_tag(b) == TT_BOT { return a; } + if tt_tag(a) == TT_TOP || tt_tag(b) == TT_TOP { return tt_top(); } + return tt_term(TT_UNION, a, b, 0, 0); +} +fn tt_inter(a: int, b: int) -> int { + if a == b { return a; } + if tt_tag(a) == TT_TOP { return b; } + if tt_tag(b) == TT_TOP { return a; } + if tt_tag(a) == TT_BOT || tt_tag(b) == TT_BOT { return tt_bot(); } + return tt_term(TT_INTER, a, b, 0, 0); +} +fn tt_not(a: int) -> int { + if tt_tag(a) == TT_BOT { return tt_top(); } + if tt_tag(a) == TT_TOP { return tt_bot(); } + if tt_tag(a) == TT_NOT { return tt_a(a); } // 双重否定免费 + return tt_term(TT_NOT, a, 0, 0, 0); +} +fn tt_mu(v: int, body: int) -> int { return tt_term(TT_MU, v, body, 0, 0); } +fn tt_var(v: int) -> int { return tt_term(TT_VAR, v, 0, 0, 0); } +fn tt_nil() -> int { if g_tt_nil < 0 { g_tt_nil = tt_term(TT_NIL, 0, 0, 0, 0); } return g_tt_nil; } +fn tt_cons(head: int, tail: int) -> int { return tt_term(TT_CONS, head, tail, 0, 0); } +fn tt_atom(ak: int, ti: int, params: int) -> int { return tt_term(TT_ATOM, ak, ti, params, 0); } +``` + +- [ ] **Step 6: 自测骨架(`src/compiler/type_selftest.cr`)** + +```core +// R2 P0:类型项引擎判定用例表(数据驱动)+ 自测驱动。 +// 判据:corec selftest-types → 每例一行 PASS/FAIL,末行 "N/M type-engine cases passed",rc=0 全过。 +// 说明:本文件只依赖 type_terms.cr / type_engine.cr 的公开 API(签名见计划 Task 1/Task 3)。 + +fn ts_check(name: string, got: int, want: int) -> int { + if got == want { + print("PASS "); println(name); + return 0; + } + print("FAIL "); print(name); + print(": got "); print(int_str(got)); + print(" want "); println(int_str(want)); + return 1; +} + +fn type_selftest_run() -> int { + fails : ., mut = 0; + total : ., mut = 0; + + // --- P0 最小用例(Task 3/4 扩充为 spec §8 全类:子类型/等价/不相交/可空/反例/穷尽性/递归/参数化/预算)--- + a_int := tt_atom(AK_INT, TI_INT, -1); + a_str := tt_atom(AK_STRING, TI_STR, -1); + + total = total + 1; fails = fails + ts_check("unit.bot_sub_int", ty_sub(tt_bot(), a_int), 1); + total = total + 1; fails = fails + ts_check("unit.int_sub_str", ty_sub(a_int, a_str), 0); + total = total + 1; fails = fails + ts_check("unit.int_sub_int", ty_sub(a_int, a_int), 1); + total = total + 1; fails = fails + ts_check("unit.dedup", (tt_union(a_int, a_str) == tt_union(a_int, a_str)), 1); + total = total + 1; fails = fails + ts_check("unit.notnot", (tt_not(tt_not(a_int)) == a_int), 1); + + print(int_str(total - fails)); print("/"); print(int_str(total)); println(" type-engine cases passed"); + if fails != 0 { return 1; } + return 0; +} +``` + +- [ ] **Step 7: `type_engine.cr` 最小桩(供 Step 6 编译通过;Task 3 补全判定)** + +```core +// R2 P0:类型项判定引擎(语义包含)。 +// 本文件在 Task 3 补全;Task 1 只放原子类常量 + ty_sub 桩(保证骨架可编译可跑)。 +AK_INT : int = 0; AK_DEX : int = 1; AK_STRING : int = 2; AK_BOOL : int = 3; +AK_UNIT : int = 4; AK_NEVER : int = 5; AK_CHAR : int = 6; AK_DYN : int = 7; +AK_PRODUCT : int = 8; AK_SUM : int = 9; AK_SEQUENCE : int = 10; +AK_REF : int = 11; AK_PTR : int = 12; AK_FN : int = 13; AK_NAMED : int = 14; + +fn ty_sub(a: int, b: int) -> int { // 桩:Task 3 替换为真判定(三态 1/0/-1) + if a == b { return 1; } + if tt_tag(b) == TT_TOP { return 1; } + if tt_tag(a) == TT_BOT { return 1; } + return 0; +} +``` + +- [ ] **Step 8: CLI 注册(`src/compiler/main.cr`)** + +在 `corec_main()` 的子命令注册段(`cli_cmd("clean-cache", ...)` 之后)追加: + +```core + cli_cmd("selftest-types", "Run type-engine self tests (R2 P0)"); +``` + +并在命令分派区(与 `cli_eq(cmd, "run")` 等同级)追加: + +```core + if cli_eq(cmd, "selftest-types") { return type_selftest_run(); } +``` + +- [ ] **Step 9: 清单注册(三处)** + +1. `src/compiler/_import.cr`:在 `import checker` 之后加 `import type_terms` / `import type_engine` / `import type_selftest` +2. `build_selfhost_native.py`:`corec_files` 列表在 `'src/compiler/checker.cr',` 之后插入三行 +3. `tests/selfhost/test_compile.py`:`concat_sources()` 的 `files` 列表同样位置插入三行 + +- [ ] **Step 10: 重建 + 跑测试** + +```bash +nice -n 19 python3 build_selfhost_native.py # BUILD SUCCESS + [GUARD] manifest OK + build log clean +nice -n 19 ./build/corec selftest-types # 期望 5/5 ... passed、rc=0 +nice -n 19 python3 tests/selfhost/test_type_engine.py # 期望 PASS +nice -n 19 ./build/corec run 'fn main()->int{return 42;}' # 期望 rc=42(既有行为不动) +``` +Expected: 全绿。 + +- [ ] **Step 11: 提交(路径限定)** + +```bash +jj commit src/compiler/type_terms.cr src/compiler/type_engine.cr src/compiler/type_selftest.cr \ + src/compiler/globals.cr src/compiler/main.cr src/compiler/_import.cr \ + build_selfhost_native.py tests/selfhost/test_compile.py tests/selfhost/test_type_engine.py \ + -m 'feat: R2 P0 Task 1——类型项表(DAG + 哈希去重)+ 构造 API + selftest-types 自测通道骨架(checker 零改动)' +``` +(**不含 `dyn_arr.cr`**——扩容自持于 `type_terms.cr`,见「文件结构」表注。) + +--- + +## Task 2: NNF + DNF 规范化 + +**Files:** +- Modify: `src/compiler/type_terms.cr` +- Modify: `src/compiler/type_selftest.cr`(加用例) + +**Interfaces:** +- Consumes: Task 1 的构造 API +- Produces: +``` +fn tt_nnf(i: int) -> int // 否定下推:¬(∪) → ∩(¬)、¬(∩) → ∪(¬)、¬¬ 消、¬ATOM/¬VAR/¬μ 保留为字面 +fn tt_norm(i: int) -> int // 规范化到 DNF:UNION of INTER-of-literals(literals = ATOM/¬ATOM/VAR/¬VAR/TOP_K) +fn tt_is_dnf(i: int) -> int // 自测辅助:检查项是否已是 DNF 形态 +``` + +- [ ] **Step 1: 加失败用例(`type_selftest.cr`)** + +```core + // --- 规范化(Task 2)--- + total = total + 1; fails = fails + ts_check("norm.demorgan", + ty_equiv(tt_not(tt_union(a_int, a_str)), tt_inter(tt_not(a_int), tt_not(a_str))), 1); + total = total + 1; fails = fails + ts_check("norm.dnf_shape", tt_is_dnf(tt_norm(tt_intersect_union_probe())), 1); + total = total + 1; fails = fails + ts_check("norm.union_inter_dist", + ty_equiv(tt_inter(tt_union(a_int, a_str), a_int), a_int), 1); +``` +(`tt_intersect_union_probe()` = 构造 `(int ∪ string) ∩ bool` 的探针函数,Task 2 一并加上——它测的是「∩ 分配到 ∪ 后 DNF 不丢项」。) + +- [ ] **Step 2: 运行确认「红」** + +```bash +nice -n 19 python3 build_selfhost_native.py && nice -n 19 ./build/corec selftest-types +``` +Expected: FAIL(`tt_norm`/`tt_is_dnf`/`tt_equiv` 未实现或桩返回 0)。 + +- [ ] **Step 3: 实现 NNF(`type_terms.cr`)** + +```core +// NNF:否定下推(De Morgan + 双重否定消除);μ 体内照推 +fn tt_nnf(i: int) -> int { + t := tt_tag(i); + if t == TT_BOT { return tt_top(); } + if t == TT_TOP { return tt_bot(); } + if t == TT_NOT { return tt_nnf_neg(tt_a(i)); } + if t == TT_UNION { return tt_union(tt_nnf(tt_a(i)), tt_nnf(tt_b(i))); } + if t == TT_INTER { return tt_inter(tt_nnf(tt_a(i)), tt_nnf(tt_b(i))); } + if t == TT_MU { return tt_mu(tt_a(i), tt_nnf(tt_b(i))); } + if t == TT_CONS { return tt_cons(tt_nnf(tt_a(i)), tt_nnf(tt_b(i))); } + if t == TT_ATOM { return tt_atom(tt_a(i), tt_b(i), tt_nnf_list(tt_c(i))); } + return i; // 字面:ATOM 参数已推 / VAR / NIL +} + +fn tt_nnf_neg(i: int) -> int { // 计算 ¬i 的 NNF(负位置) + t := tt_tag(i); + if t == TT_BOT { return tt_top(); } + if t == TT_TOP { return tt_bot(); } + if t == TT_NOT { return tt_nnf(tt_a(i)); } + if t == TT_UNION { return tt_inter(tt_nnf_neg(tt_a(i)), tt_nnf_neg(tt_b(i))); } + if t == TT_INTER { return tt_union(tt_nnf_neg(tt_a(i)), tt_nnf_neg(tt_b(i))); } + if t == TT_MU { return tt_mu(tt_a(i), tt_nnf_neg(tt_b(i))); } + return tt_not(i); // 字面取反:¬ATOM / ¬VAR +} +``` +(`tt_nnf_list`:对 CONS 链逐元素 `tt_nnf`——新加的小工具函数。) + +- [ ] **Step 4: 实现 DNF(`type_terms.cr`)** + +```core +// DNF:把 NNF 形态分配到「union of inter-of-literals」。 +// 入口先 tt_nnf;分配律在 INTER 节点上做(左右各自 DNF 后笛卡尔积)。 +// 简化:单元素 union/inter 不建节点;∩ 左/右为 union 时展开(分配律);结果项去重靠 tt_term 的 DAG。 +fn tt_norm(i: int) -> int { return tt_dnf(tt_nnf(i)); } + +fn tt_dnf(i: int) -> int { + t := tt_tag(i); + if t == TT_UNION { return tt_union(tt_dnf(tt_a(i)), tt_dnf(tt_b(i))); } + if t == TT_INTER { + l := tt_dnf(tt_a(i)); r := tt_dnf(tt_b(i)); + // 分配律**两支都展开**:(A∪B)∩C → (A∩C) ∪ (B∩C) + // (本计划初稿只展开一支 = 静默丢项;P0 Task 2 自测 norm.distributed 实证 got INTER want UNION) + if tt_tag(l) == TT_UNION { + return tt_union(tt_dnf(tt_inter(tt_a(l), r)), tt_dnf(tt_inter(tt_b(l), r))); + } + if tt_tag(r) == TT_UNION { + return tt_union(tt_dnf(tt_inter(l, tt_a(r))), tt_dnf(tt_inter(l, tt_b(r)))); + } + return tt_inter(l, r); // 已规范化(∩-链扁平) + } + if t == TT_MU { return tt_mu(tt_a(i), tt_dnf(tt_b(i))); } + return i; // 字面 +} + +// DNF 形态判定:UNION 的每个子项必须是「product 或 literal」(不得再嵌 UNION); +// INTER 链的叶子必须全是 literal;literal = ATOM / ¬ATOM / VAR / ¬VAR / TOP_K / ⊥ +fn tt_is_dnf(i: int) -> int { + t := tt_tag(i); + if t == TT_UNION { + if tt_is_dnf(tt_a(i)) == 0 { return 0; } + return tt_is_dnf(tt_b(i)); + } + // 非 UNION 位置:不许出现 UNION(上面已递归剥掉),检查 product/literal 形态 + if t == TT_INTER { return tt_is_product_chain(i); } + return tt_is_literal(i); +} + +fn tt_is_product_chain(i: int) -> int { + if tt_tag(i) == TT_INTER { + if tt_tag(tt_a(i)) == TT_UNION || tt_tag(tt_b(i)) == TT_UNION { return 0; } + if tt_is_product_chain(tt_a(i)) == 0 { return 0; } + return tt_is_product_chain(tt_b(i)); + } + return tt_is_literal(i); +} +``` +(`tt_is_literal` 已在 Task 3 Step 3 定义——**依赖顺序**:Task 2 落地时先补一个最小 `tt_is_literal`(语义同 Task 3 版本),Task 3 再替换为最终版;两版语义必须一致。) + +- [ ] **Step 5: 跑测试确认变绿** + +```bash +nice -n 19 python3 build_selfhost_native.py && nice -n 19 ./build/corec selftest-types +nice -n 19 python3 tests/selfhost/test_type_engine.py +``` +Expected: 全部 PASS(计数行 ≥ 8)。 + +- [ ] **Step 6: 提交** + +```bash +jj commit src/compiler/type_terms.cr src/compiler/type_selftest.cr -m 'feat: R2 P0 Task 2——NNF(否定下推)+ DNF 规范化(∪/∩ 分配律)+ 形态自测' +``` + +--- + +## Task 3: 判定引擎(三态 + 原子互斥 + μ memo + 预算守卫) + +**Files:** +- Modify: `src/compiler/type_engine.cr`(替换 Task 1 的桩) +- Modify: `src/compiler/type_selftest.cr`(加用例) + +**Interfaces:** +- Consumes: Task 1/2 的 API +- Produces(**API 稳定,Task 4/后续 P 期依赖**): +``` +// 三态:1 = 成立;0 = 不成立;-1 = 预算耗尽/不可判定(禁止当 0 用) +fn ty_sub(a: int, b: int) -> int +fn ty_equiv(a: int, b: int) -> int +fn ty_disjoint(a: int, b: int) -> int +fn ty_inhabited(a: int) -> int +fn tt_witness(a: int, b: int) -> int // A\B 的具体类型项(不可满足时 -1) +fn ty_budget_reset(limit: int) // 每次顶层判定前调用 +fn ty_exhausted() -> int // 上一次判定是否预算耗尽 +// —— 本任务一并实现的内部辅助(签名固定,供 Task 4 复用)—— +fn lit_collect(i: int) -> int // product/字面 → 字面数组(写入 g_ty_lits 侧缓冲),返回字面个数 +fn lit_at(k: int) -> int // 第 k 个字面项(g_ty_lits 读取) +fn lits_contradictory() -> int // 当前 g_ty_lits 内是否含 X 与 ¬X、或两个互斥原子类 → 1 +fn lit_set_covers(n: int, q: int) -> int // 前 n 个字面集合是否覆盖字面 q(lit_covers 扫描) +fn ak_disjoint(x: int, y: int) -> int +``` + +- [ ] **Step 1: 加失败用例(`type_selftest.cr`)** + +```core + // 深递归探针:μX₀. product<μX₁. product<… int>>(深度 256)——判定展开会耗尽小预算 + // (放在 type_selftest.cr 内,仅自测使用) + fn tt_mu_probe_deep() -> int { + acc : ., mut = tt_atom(AK_INT, TI_INT, -1); + d : ., mut = 0; + loop { + if d >= 256 { break; } + acc = tt_mu(0, tt_atom(AK_PRODUCT, -1, tt_cons(acc, tt_nil()))); + d = d + 1; + } + return acc; + } + + // --- 判定(Task 3)--- + u_is := tt_union(a_int, a_str); + total = total + 1; fails = fails + ts_check("sub.union_right", ty_sub(a_int, u_is), 1); + total = total + 1; fails = fails + ts_check("sub.union_left_neg", ty_sub(u_is, a_int), 0); + total = total + 1; fails = fails + ts_check("sub.inter", ty_sub(tt_inter(a_int, a_str), a_int), 1); + total = total + 1; fails = fails + ts_check("equiv.absorb", ty_equiv(tt_union(a_int, a_int), a_int), 1); + total = total + 1; fails = fails + ts_check("disjoint.atoms", ty_disjoint(a_int, a_str), 1); + total = total + 1; fails = fails + ts_check("disjoint.same", ty_disjoint(a_int, a_int), 0); + total = total + 1; fails = fails + ts_check("inh.atom", ty_inhabited(a_int), 1); + total = total + 1; fails = fails + ts_check("inh.contra", ty_inhabited(tt_inter(a_int, tt_not(a_int))), 0); + total = total + 1; fails = fails + ts_check("inh.mixed_atoms", + ty_inhabited(tt_inter(a_int, tt_atom(AK_SEQUENCE, -1, -1))), 0); + total = total + 1; fails = fails + ts_check("topk.sub", ty_sub(a_int, tt_top_k(AK_INT)), 1); + total = total + 1; fails = fails + ts_check("topk.neg", ty_sub(a_str, tt_top_k(AK_INT)), 0); + // 预算守卫(深递归爆炸项 + 极小预算 → 三态 -1,且不与"不成立"混淆) + ty_budget_reset(64); + total = total + 1; fails = fails + ts_check("budget.unknown", ty_sub(tt_mu_probe_deep(), a_int), -1); + total = total + 1; fails = fails + ts_check("budget.flag", ty_exhausted(), 1); + ty_budget_reset(200000); +``` + +- [ ] **Step 2: 运行确认「红」**(桩返回 0,断言 1/-1 的用例必挂) + +- [ ] **Step 3: 实现互斥公理 + 逐字面覆盖(`type_engine.cr`)** + +```core +// 原子类互斥(P0 公理表):不同原子类两两不相交;AK_DYN = ⊤(与所有类相交)。 +// 例外:AK_NAMED 的具体行可能展开为其它类——P0 不展开(登记为未覆盖面),按未知处理。 +fn ak_disjoint(x: int, y: int) -> int { + if x == y { return 0; } + if x == AK_DYN || y == AK_DYN { return 0; } + if x == AK_NEVER || y == AK_NEVER { return 1; } + return 1; +} + +fn tt_is_literal(i: int) -> int { + t := tt_tag(i); + if t == TT_ATOM || t == TT_VAR || t == TT_TOP_K || t == TT_BOT { return 1; } + if t == TT_NOT { + it := tt_tag(tt_a(i)); + if it == TT_ATOM || it == TT_VAR || it == TT_TOP_K { return 1; } + } + return 0; +} + +// 字面覆盖:literal p 是否蕴含 literal q(q 的语义包含 p 的) +fn lit_covers(p: int, q: int) -> int { + if p == q { return 1; } + pt := tt_tag(p); qt := tt_tag(q); + // ⊤ₖ 覆盖同类原子 + if qt == TT_TOP_K { + if pt == TT_ATOM { return ty_ak_of(p) == tt_a(q); } + if pt == TT_TOP_K { return tt_a(p) == tt_a(q); } + return 0; + } + if pt == TT_NOT && qt != TT_NOT { return 0; } // ¬A 不蕴含正字面(除 A=B 已排除) + return 0; // 其余一律不覆盖(⊥ 早退由调用方 lits_contradictory 处理) +} +``` +(`ty_ak_of(lit)`:ATOM → `tt_a(lit)`;`¬ATOM` → `tt_a(tt_a(lit))`。) + +- [ ] **Step 4: 实现判定的 product 覆盖 + μ 展开 + memo + 预算(`type_engine.cr`)** + +```core +// 顶层入口:每次重置步数/耗尽位,规范化两侧,逐 product 覆盖判定 +fn ty_sub(a: int, b: int) -> int { + if a == b { return 1; } + nav := tt_norm(a); nbv := tt_norm(b); + if nav == nbv { return 1; } + if tt_tag(nbv) == TT_TOP { return 1; } + if tt_tag(nav) == TT_BOT { return 1; } + // A = ⋃ products;每个 product 必须被 B 的某个 product 覆盖 + r := sub_union_covered(nav, nbv, 0); + if g_ty_exhausted != 0 { return -1; } + return r; +} + +// products 分解:DNF 形态下按 UNION 拆分,叶子 = product(∩-链)或字面 +fn prod_covered(p: int, b: int) -> int { + // b 拆 union:任一 product 覆盖即可 + if tt_tag(b) == TT_UNION { if prod_covered(p, tt_a(b)) == 1 { return 1; } return prod_covered(p, tt_b(b)); } + // p 与 b 均为 product/字面:收集双方字面集后做覆盖(b ⊆ p) + // —— p 是待检查的「更小集合」,b 是候选「更大集合」:需要 b 的每个字面被 p 的某字面蕴含 + plits := lit_collect(p); blits := lit_collect(b); + bi : ., mut = 0; + loop { + if bi >= blits_count { break; } + bq := blits[bi]; + // ⊥ 早退:p 的字面集内部矛盾 → 空集,覆盖一切 + if lits_contradictory(plits) != 0 { return 1; } + if !lit_set_covers(plits, bq) { return 0; } + bi = bi + 1; + } + return 1; +} +``` +(实现者按此语义实现:`lit_collect` 把 product 拆成字面数组(µ 展开点见下),`lit_set_covers` 用 `lit_covers` 扫描,`lits_contradictory` 检查 `ATOM(k1)` 与 `ATOM(k2)`(k1≠k2 且互斥)或 `X` 与 `¬X`。) + +```core +// μ 展开 + memo(假设-判定,最大不动点):遇到 MU 单步展开,并在假设集中登记 (子项对) +// 预算:每次 sub 调用 g_ty_steps += 1;超 g_ty_budget_max → g_ty_exhausted = 1,返回 -1 +fn sub_step(a: int, b: int) -> int { + g_ty_steps = g_ty_steps + 1; + if g_ty_steps > g_ty_budget_max { g_ty_exhausted = 1; return -1; } + mk := ty_memo_find(a, b); + if mk >= 0 { return r64(g_ty_memo_vals, mk * 8); } + ... // 1) a 为 MU → 展开(tt_subst(a 的 var, a 的 body))后递归 + // 2) b 为 MU → 展开后递归 + // 3) 结果写 memo(三态值) + return r; +} +``` + +- [ ] **Step 5: 实现 `ty_equiv` / `ty_disjoint` / `ty_inhabited` / 预算重置** + +```core +fn ty_equiv(a: int, b: int) -> int { + x := ty_sub(a, b); if x != 1 { return x; } + y := ty_sub(b, a); if y != 1 { return y; } + return 1; +} +fn ty_disjoint(a: int, b: int) -> int { + x := ty_inhabited(tt_inter(a, b)); + if x == -1 { return -1; } // 三态传播:未知不得降级为 0 + if x == 0 { return 1; } + return 0; +} +fn ty_inhabited(a: int) -> int { + n := tt_norm(a); + // DNF 下:存在一个 product,其字面集不矛盾且所有原子类两两相容 + return prod_inhabited_any(n); +} +fn ty_budget_reset(limit: int) { g_ty_steps = 0; g_ty_budget_max = limit; g_ty_exhausted = 0; } +fn ty_exhausted() -> int { return g_ty_exhausted; } +``` +(`prod_inhabited_any`:UNION 拆分 + 每个 product 做 `lits_contradictory` 检查;`AK_NAMED` 具体行 P0 不展开——**登记为未覆盖面**,遇到即 `g_ty_exhausted` 语义之外的 `unknown`:返回 -1 并置一个 `g_ty_uncovered` 位,自测里不出现。) + +- [ ] **Step 6: 跑测试(含预算用例)** + +```bash +nice -n 19 python3 build_selfhost_native.py && nice -n 19 ./build/corec selftest-types +nice -n 19 python3 tests/selfhost/test_type_engine.py +``` +Expected: 全 PASS(计数 ≥ 20,含 2 例预算三态)。 + +- [ ] **Step 7: 提交** + +```bash +jj commit src/compiler/type_engine.cr src/compiler/type_selftest.cr \ + -m 'feat: R2 P0 Task 3——语义包含判定(子类型/等价/不相交/可空 + 原子互斥公理 + μ 展开 memo + 三态预算守卫)' +``` + +--- + +## Task 4: 反例(witness)+ 穷尽性 + 递归/参数化用例补齐 + +**Files:** +- Modify: `src/compiler/type_engine.cr`(`tt_witness`、`ty_exhaustive`) +- Modify: `src/compiler/type_selftest.cr`(用例补齐到 spec §8 全类) + +**Interfaces:** +- Consumes: Task 3 的判定 API +- Produces: +``` +fn tt_witness(a: int, b: int) -> int // A\B 的类型项(规范化后);不可满足 → -1 +fn ty_exhaustive(domain: int, patterns: int) -> int // patterns = CONS 链;1 = 穷尽;0 = 有遗漏;-1 = 未知 +``` + +- [ ] **Step 1: 加失败用例(`type_selftest.cr`)** + +```core + // --- 反例 + 穷尽性(Task 4)--- + a_bool := tt_atom(AK_BOOL, TI_BOOL, -1); + w := tt_witness(a_int, a_str); + total = total + 1; fails = fails + ts_check("witness.nonempty", (w >= 0), 1); + total = total + 1; fails = fails + ts_check("witness.sub_of_a", ty_sub(w, a_int), 1); + total = total + 1; fails = fails + ts_check("witness.not_sub_of_b", ty_sub(w, a_str), 0); + total = total + 1; fails = fails + ts_check("witness.empty", tt_witness(a_int, a_int), -1); + + dom := tt_union(tt_union(a_int, a_str), a_bool); + pats := tt_cons(a_int, tt_cons(a_str, tt_nil())); + total = total + 1; fails = fails + ts_check("exhaust.incomplete", ty_exhaustive(dom, pats), 0); + miss := ty_exhaust_witness(dom, pats); + total = total + 1; fails = fails + ts_check("exhaust.witness_bool", ty_equiv(miss, a_bool), 1); + total = total + 1; fails = fails + ts_check("exhaust.complete", + ty_exhaustive(dom, tt_cons(a_int, tt_cons(a_str, tt_cons(a_bool, tt_nil())))), 1); + + // --- 递归 + 参数化(Task 4)--- + rec_abs := tt_mu(0, tt_union(a_int, tt_var(0))); // μX. int ∪ X ≡ int + total = total + 1; fails = fails + ts_check("rec.absorb", ty_equiv(rec_abs, a_int), 1); + rec_list := tt_mu(0, tt_union(a_int, tt_atom(AK_PRODUCT, -1, tt_cons(tt_var(0), tt_nil())))); + total = total + 1; fails = fails + ts_check("rec.inh", ty_inhabited(rec_list), 1); + total = total + 1; fails = fails + ts_check("rec.sub_self", ty_sub(rec_list, rec_list), 1); + seq_i := tt_atom(AK_SEQUENCE, -1, tt_cons(a_int, tt_nil())); + seq_s := tt_atom(AK_SEQUENCE, -1, tt_cons(a_str, tt_nil())); + total = total + 1; fails = fails + ts_check("param.same", ty_sub(seq_i, seq_i), 1); + total = total + 1; fails = fails + ts_check("param.invariant_p0", ty_sub(seq_i, seq_s), 0); // P0 参数不变;变型规则 P3 +``` + +- [ ] **Step 2: 运行确认「红」**(`tt_witness`/`ty_exhaustive` 未实现) + +- [ ] **Step 3: 实现 witness(`type_engine.cr`)** + +```core +// 反例 = A\B 的规范化项;不可满足(= ⊥)→ -1(无遗漏/无反例值) +fn tt_witness(a: int, b: int) -> int { + r := tt_norm(tt_inter(a, tt_not(b))); + if ty_inhabited(r) != 1 { return -1; } + if tt_tag(r) == TT_BOT { return -1; } + return r; +} + +// 穷尽性:¬(⋃patterns) ∩ domain 可满足 → 有遗漏;witness = 遗漏的具体类型项 +fn ty_exhaust_witness(domain: int, patterns: int) -> int { + acc : ., mut = tt_bot(); + p : ., mut = patterns; + loop { + if tt_tag(p) != TT_CONS { break; } + acc = tt_union(acc, tt_a(p)); + p = tt_b(p); + } + return tt_witness(domain, acc); // domain \ ⋃patterns +} +fn ty_exhaustive(domain: int, patterns: int) -> int { + w := ty_exhaust_witness(domain, patterns); + if g_ty_exhausted != 0 { return -1; } + if w < 0 { return 1; } // 补集空 = 穷尽 + return 0; +} +``` + +- [ ] **Step 4: 跑测试(用例表到 spec §8 全类)** + +**同时必办两件**(Task 1 评审遗留,2026-09-10): +1. 把 `tests/selfhost/test_type_engine.py` 的 `MIN_CASES` 从 `5` **抬到 `32`**(注释同步:P0 全类落地)。 +2. 用例表加入**索引扩容守门**(Task 1 实测:5 例 << 初始容量 1024 → 扩容路径在判据下不可达,只能靠一次性压力程序验证;长期守门须有常规用例): + 新增自测函数(`type_selftest.cr`):先把 `g_tt_index` 预置为 8 槽(直接 `grow_tt_index` 前先手动置 `g_tt_index_cap = 0` 再调 `grow_tt_index(2)`,或按实现时可用的最小触发路径),随后构造 ~40 个互异项逼出装填因子守卫扩容 + `tt_reindex` + 同轮重试,最后断言: + - `tt_count()` 等于期望项数(无重复插入、无丢项) + - 一组「扩容前构造的项」在扩容后**行号不变**(DAG 键跨重建稳定) + - 新项插入后 `tt_tag/tt_a` 读回正确 + 断言写成 `ts_check("grow.*", …)` 若干例(≥3)。 + +```bash +nice -n 19 python3 build_selfhost_native.py && nice -n 19 ./build/corec selftest-types +nice -n 19 python3 tests/selfhost/test_type_engine.py +``` +Expected: 全 PASS,`N/N ... passed` 且 N ≥ 32(子类型/等价/不相交/可空/反例/穷尽性/递归/参数化/预算全类齐 + 扩容守门)。 + +- [ ] **Step 5: 提交** + +```bash +jj commit src/compiler/type_engine.cr src/compiler/type_selftest.cr \ + -m 'feat: R2 P0 Task 4——反例(witness = A\B 具体类型项)+ 穷尽性(补集空性 + 遗漏 witness)+ 递归/参数化用例补齐(用例表覆盖 spec §8 全类)' +``` + +--- + +## Task 5: 收官(回归 + 自举 + 零产物变化 + 文档/台账) + +**Files:** +- Modify: `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(§9 P0 标记完成) +- Modify: `TODO.md`(登记 P0 落点 + 未覆盖面) +- Modify: `.superpowers/sdd/progress.md`(台账) + +- [ ] **Step 1: 全量回归** + +```bash +nice -n 19 ./build/corec clean-cache +nice -n 19 python3 tests/selfhost/test_type_engine.py +nice -n 19 python3 tests/selfhost/test_compile.py +nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py +nice -n 19 python3 tests/selfhost/test_hit_table.py +nice -n 19 python3 tests/selfhost/test_ccr_v7.py +nice -n 19 python3 tests/selfhost/test_live_ranges.py +nice -n 19 python3 tests/selfhost/test_slice_bounds.py +nice -n 19 python3 tests/selfhost/test_global_init.py +nice -n 19 python3 tests/selfhost/test_lexer_parity.py +nice -n 19 python3 tests/bootstrap/test_pipeline.py +nice -n 19 python3 tests/bootstrap/test_borrow.py +nice -n 19 python3 tests/bootstrap/test_generics.py +``` +Expected: 全绿(13 项)。 + +- [ ] **Step 2: 零产物变化判据(P0 的硬锚)** + +P0 前后各构建同一「无新能力程序」样本,产物必须逐字节相同: + +```bash +# 在**本批第一个任务提交之前**的基点捕获基线(若未捕获,用 jj 取父修订重建编译器的做法不可行—— +# 改以「同源两次构建」+ 既有套件全绿替代,并在报告显式记录该替代) +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/r2p0_head_bin +cmp /tmp/r1t4_base_bin /tmp/r2p0_head_bin && echo BYTE-IDENTICAL +``` +Expected: `BYTE-IDENTICAL`(`/tmp/r1t4_base_bin` = R1 批捕获的同一样本产物,sha256 `95084e7b…d475`;若该文件不在盘上 → 从 `tests/suite/ptr_arith.cr` 重建并在报告记录不可比对)。 + +- [ ] **Step 3: 自举全链** + +```bash +nice -n 19 python3 build_selfhost_native.py # BUILD SUCCESS + [GUARD] OK + 日志守卫 clean +nice -n 19 ./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 +/tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 +cmp /tmp/corec2 /tmp/corec3 && echo CMP_IDENTICAL +nice -n 19 ./build/corec run 'fn main()->int{return 42;}' # rc=42 +``` +Expected: `CMP_IDENTICAL` + rc=42 + 无 `error[`/N06。 + +- [ ] **Step 4: 文档与台账** + +- spec §9 表 P0 行标 ✅ 已完成 + 记录落点(提交哈希) +- `TODO.md` 新增条目:**P0 未覆盖面登记**(`AK_NAMED` 具体行不展开 → 判定返回 unknown;参数变型规则 = P3;用户接口/载体 = P2/P4) +- `.superpowers/sdd/progress.md` 追加 R2 P0 段 + +- [ ] **Step 5: 提交** + +```bash +jj commit docs/superpowers/specs/2026-09-10-type-interface-unification-design.md TODO.md \ + -m 'docs: R2 P0 收官——用例表全绿 + 全回归 + 零产物变化(byte-identical)+ 自举 cmp IDENTICAL;spec/TODO/台账同步(未覆盖面登记)' +``` + +--- + +## 自检记录(写完计划后复核) + +- **spec 覆盖**:spec §4 步 1(建层:类型项表 + 引擎 + 查询 API + 用例表;checker 不动)→ Task 1-4;§3.2 判定 API 五函数 → Task 3(三态)/Task 4(witness);§3.3 规范化/记忆化/位集合快路径/预算守卫 → Task 2(规范化)· Task 3(memo + 预算)· 位集合快路径**不在 P0**(属 P3 穷尽性快路径,已在本计划 Task 4 用判定实现穷尽性);§8 用例表分层 → Task 4;§9 P0 判据 → Task 5 +- **P0 边界守住**:checker/ir_gen/后端/解释器零改动(清单仅加三文件);查询 API(`iface_*`)= P2 的接线面,P0 只出判定 API(`ty_*`)——spec §3.2 与 §2.5 的分工在计划里明确(`iface_*` 属 P2) +- **占位符扫描**:无 TBD/TODO;Task 2 Step 4 的 `tt_is_dnf` 与 Task 3 Step 4 的 `prod_covered` 给了语义级骨架 + 明确要求(实现者补完辅助函数),非「稍后填」 +- **命名一致性**:`tt_*`(构造/访问/规范化)、`ty_*`(判定/预算)、`ak_*`/`AK_*`(原子类)、`ts_check`(自测)在四任务间签名一致;`AK_NAMED` 的两处引用(Task 3 Step 3 与 Step 5)语义一致 +- **风险注记**:Task 1 的开放寻址索引在扩容时用 `tt_reindex()` 重建(O(n)),实现者须保证 `grow_tt_index` 后 `tt_reindex` 被调用(计划代码已含);预算用例依赖 `g_ty_steps` 全局——多例连续跑时每例前须 `ty_budget_reset`(用例表已按此写) diff --git a/docs/superpowers/plans/2026-09-10-r2-p1-shadow-parity.md b/docs/superpowers/plans/2026-09-10-r2-p1-shadow-parity.md new file mode 100644 index 00000000..9d388763 --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-r2-p1-shadow-parity.md @@ -0,0 +1,366 @@ +# R2 P1(影子对拍)实施计划:旧判定 ∥ 新引擎 → 差异清单 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 P0 的类型项引擎以**影子模式**挂到检查器的类型判定上(旧判定照常生效),对真实语料并跑两套判定、产出**分类差异清单**——收紧面(旧接受→新拒绝 / 旧拒绝→新接受)在 P1 暴露,而不改任何行为。 + +**Architecture:** 三件新东西 + 一处包装:① 桥接层 `ty_shadow.cr`(checker 的 `ti` → 引擎类型项);② 影子判定核心(翻译 → `ty_equiv` → 分类:`AGREE` / `OLD_STRICTER`(旧拒新受)/ `OLD_LOOSER`(旧受新拒)/ `UNKNOWN`(引擎 -1,单列不计差异));③ 通道(CLI `--type-shadow` 开关 + 摘要行 + 转储文件);挂点 = `type_equal` 包装(原函数改名 `type_equal_core` 供内部递归,外部 8 个决策点经包装自动覆盖)。 + +**Tech Stack:** Core 自举栈;P0 引擎(`src/compiler/type_terms.cr` / `type_engine.cr` / `type_selftest.cr`);检查器 `src/compiler/checker.cr`;CLI `src/compiler/main.cr`。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §4 步 2(旁挂对拍)+ §8 判据 3(对拍层)+ §9 P1。 + +## Global Constraints + +- **jj only**;**提交必须路径限定**(多 agent 共用工作副本)。 +- 所有编译/测试命令 `nice -n 19` 前缀;改编译器源码后**必须重建**。 +- **P1 行为零变化(强判据)**:`--type-shadow` **关**(默认)= 产物与 P0 基线逐字节相同;**开** = 产物**同样**逐字节相同(影子只观察、不改判定、不写产物)。两态都要过。 +- **判据前清缓存**:`nice -n 19 ./build/corec clean-cache`(TODO #5),且 cwd = 仓库根。 +- 文件永久不允许还原;不得绕过;**引擎 UNKNOWN(-1)不得计入差异**(未覆盖面 ≠ 收紧面,单列统计)。 +- diff 校验式样:`jj diff --git | grep '^[+]' | grep -v -e '^[+][+][+]'`;环境陷阱:noclobber(用 `>|`)、变量不分词。 +- 新文件三处注册(`src/compiler/_import.cr` / `build_selfhost_native.py` 的 **corec 清单**(checker 段后)/ `tests/selfhost/test_compile.py`)。 + +--- + +## Task 1: 桥接层(checker `ti` → 引擎类型项)+ 单元自测 + +**Files:** +- Create: `src/compiler/ty_shadow.cr` +- Modify: `src/compiler/globals.cr`(影子侧表声明)、`src/compiler/type_selftest.cr`(加桥接用例)、`src/compiler/_import.cr`、`build_selfhost_native.py`、`tests/selfhost/test_compile.py` + +**Interfaces(产出;Task 2/3 依赖):** + +``` +// ti → 类型项(带 per-ti 缓存;-1 = 无法翻译/预算耗尽) +fn sh_term_of_ti(ti: int) -> int +// 桥接统计(自测断言用) +fn sh_map_hits() -> int // 缓存命中数 +fn sh_map_entries() -> int // 已翻译条目数 +``` + +**映射表(P0 引擎原子类 ↔ checker 类型 kind;`res_type_node`/`alloc_type` 现状见 spec §11)**: + +| checker | 引擎项 | +|---|---| +| `TYP_BASE` + `TY_INT/TY_DEX/TY_STRING/TY_BOOL/TY_UNIT/TY_NEVER/TY_CHAR` | `AK_INT/AK_DEX/AK_STRING/AK_BOOL/AK_UNIT/AK_NEVER/AK_CHAR`(`ti` 存 `b` 槽)——**⚠️ 必须按语义逐项分派,不得按数值直传**:`TI_BOOL=2/TI_STR=3` 与 `AK_STRING=2/AK_BOOL=3` **下标互换**(Task 1 实测:照抄数值直传会把 bool↔string 静默错标,且两侧同错自洽 → 差异清单全成假信号);`TY_DEX_S`(精确缩放 dex,占位哨兵行)同样归 `AK_DEX` | +| `TYP_DYN` | `AK_DYN` | +| `TYP_ARRAY`(data=元素, extra=N) | `AK_SEQUENCE`,参数链 `[elem]`(**N 不入身份**——R1 裁决) | +| `TYP_SLICE` | `AK_SEQUENCE`,参数链 `[elem]` | +| `TYP_PTR` / `TYP_REF` | `AK_PTR` / `AK_REF`,参数链 `[inner]` | +| `TYP_TUPLE`(**实读布局:`data` = 字段数、`extra` = `g_gen_apply_data` 起始下标**——checker.cr:118 与 :2041 两处消费点同证;计划初稿的「`g_tuple_*`」全仓不存在,Task 1 实测) | `AK_PRODUCT`,参数链 = 逐字段项 | +| `TYP_NAMED` | `AK_NAMED`(`ti` 存 `b` 槽;引擎不展开 → 多为 UNKNOWN,P1 预期) | +| `TYP_GENERIC_PARAM` / `TYP_GENERIC_APPLY` | `AK_NAMED`(同上,未知类) | + +- [ ] **Step 1: 加自测用例(`type_selftest.cr`;红)** + +```core + // --- P1 桥接(checker ti → 引擎项)--- + // 说明:调用方需 g_types 已初始化(selftest 里显式 init_types()) + t_int := sh_term_of_ti(TI_INT); + total = total + 1; fails = fails + ts_check("bridge.int_atom", tt_tag(t_int), TT_ATOM); + total = total + 1; fails = fails + ts_check("bridge.int_ak", tt_a(t_int), AK_INT); + t_arr := sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 3)); + total = total + 1; fails = fails + ts_check("bridge.arr_seq", tt_a(t_arr), AK_SEQUENCE); + t_arr3 := sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 4)); + total = total + 1; fails = fails + ts_check("bridge.len_not_identity", + ty_equiv(t_arr, t_arr3), 1); // N 不入身份(R1 裁决)——3 与 4 等价 + total = total + 1; fails = fails + ts_check("bridge.cache_hit", (sh_term_of_ti(TI_INT) == t_int), 1); +``` + +- [ ] **Step 2: 运行确认红**(`sh_term_of_ti` 未定义 → 构建失败/用例挂) + +- [ ] **Step 3: 实现 `ty_shadow.cr`(桥接层)** + +```core +// === ty_shadow.cr === +// R2 P1:影子对拍桥接层——把 checker 的类型表行号(ti)翻译成 P0 引擎的类型项。 +// 语义映射见计划 Task 1 表;命名/泛型类映射为 AK_NAMED(引擎不展开 → UNKNOWN, +// 这正是 P1 要暴露的「未覆盖面」,与「收紧面」分开统计)。 +// 缓存:g_shadow_map(16B/条:ti → term),线性探测开放寻址(与引擎表同式)。 + +fn sh_map_cap_init() { + if g_shadow_map_cap <= 0 { + nc : ., mut = 1024; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_shadow_map = nb; + g_shadow_map_cap = nc; + } +} + +// ⚠️ 骨架修正(Task 1 评审 M6:**直接照抄初稿骨架会回归缺陷 ②③**): +// · 探测前必须有**装填因子守卫**(否则 >cap 条目时探测永不落空 = 挂死) +// · 扩容 = 重建 + **重放既有条目**(`sh_map_rehash`) +// · 调用方**回写前必须重探**(递归翻译可能触发扩容/重建,写旧 slot 会写错槽) +fn sh_map_find(ti: int) -> int { // 命中返回槽位,未命中返回空槽位(不插入) + sh_map_cap_init(); + if (g_shadow_map_count + 1) * 2 >= g_shadow_map_cap { sh_map_rehash(); } // 守卫(探测前) + h : ., mut = ti % g_shadow_map_cap; + if h < 0 { h = 0 - h; } + p : ., mut = h; + loop { + k := r64(g_shadow_map, p * 16); + if k < 0 { return p; } + if k == ti { return p; } + p = p + 1; if p >= g_shadow_map_cap { p = 0; } + } +} + +// 扩容:容量翻倍(≥2 的幂)+ 逐条重放(保留既有映射) +fn sh_map_rehash() { + old_map := g_shadow_map; + old_cap := g_shadow_map_cap; + nc : ., mut = old_cap * 2; + if nc < 1024 { nc = 1024; } + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_shadow_map = nb; + g_shadow_map_cap = nc; + g_shadow_map_count = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + k := r64(old_map, j * 16); + if k >= 0 { + s := sh_map_slot_no_grow(k); // ⚠️ 重放必须用**无守卫**版(有守卫版会再触发 rehash → 递归) + w64(g_shadow_map, s * 16, k); + w64(g_shadow_map, s * 16 + 8, r64(old_map, j * 16 + 8)); + g_shadow_map_count = g_shadow_map_count + 1; + } + j = j + 1; + } +} + +// 无守卫探测(仅供 rehash 重放;外部一律走 sh_map_find) +fn sh_map_slot_no_grow(ti: int) -> int { + h : ., mut = ti % g_shadow_map_cap; + if h < 0 { h = 0 - h; } + p : ., mut = h; + loop { + k := r64(g_shadow_map, p * 16); + if k < 0 { return p; } + if k == ti { return p; } + p = p + 1; if p >= g_shadow_map_cap { p = 0; } + } +} + +fn sh_term_of_ti(ti: int) -> int { + if ti < 0 { return -1; } + // 预置常量:natives **按语义逐项分派**(⚠️ 数值不 1:1:TI_BOOL=2/TI_STR=3 vs + // AK_STRING=2/AK_BOOL=3 下标互换——直接 tt_atom(ti,ti,-1) 会静默错标,Task 1 实测) + if ti == TI_INT { return tt_atom(AK_INT, ti, -1); } + if ti == TI_DEX { return tt_atom(AK_DEX, ti, -1); } + if ti == TI_BOOL { return tt_atom(AK_BOOL, ti, -1); } + if ti == TI_STR { return tt_atom(AK_STRING, ti, -1); } + if ti == TI_UNIT { return tt_atom(AK_UNIT, ti, -1); } + if ti == TI_NEVER { return tt_atom(AK_NEVER, ti, -1); } + if ti == TI_CHAR { return tt_atom(AK_CHAR, ti, -1); } + if ti == TI_DYN { return tt_atom(AK_DYN, ti, -1); } // 注意:dyn 位图按 ⊤ 近似(过宽)→ Task 3 须单列 dyn 类差异 + if ti == TI_DEX_S { return tt_atom(AK_DEX, ti, -1); } // 精确 dex 与 dex 同语义域 + slot := sh_map_find(ti); + k := r64(g_shadow_map, slot * 16); + if k == ti { + g_shadow_hits = g_shadow_hits + 1; + return r64(g_shadow_map, slot * 16 + 8); + } + // 未命中:按 kind 翻译(递归子项) + term : ., mut = -1; + k1 := get_type_kind(ti); + d1 := get_type_data(ti); + if k1 == TYP_ARRAY || k1 == TYP_SLICE { + el := sh_term_of_ti(d1); + if el >= 0 { term = tt_atom(AK_SEQUENCE, -1, tt_cons(el, tt_nil())); } + } else if k1 == TYP_PTR { + in1 := sh_term_of_ti(d1); + if in1 >= 0 { term = tt_atom(AK_PTR, -1, tt_cons(in1, tt_nil())); } + } else if k1 == TYP_REF { + in2 := sh_term_of_ti(d1); + if in2 >= 0 { term = tt_atom(AK_REF, -1, tt_cons(in2, tt_nil())); } + } else if k1 == TYP_TUPLE { + // 逐字段翻译(字段访问器与 g_tuple_* 布局见 checker.cr;实现时以实际访问器为准) + term = sh_tuple_to_product(ti); + } else if k1 == TYP_NAMED || k1 == TYP_GENERIC_PARAM || k1 == TYP_GENERIC_APPLY { + term = tt_atom(AK_NAMED, ti, -1); // 不展开 → UNKNOWN(P1 预期) + } else { + term = tt_atom(AK_NAMED, ti, -1); // 未知 kind 保守归入命名类 + } + if term < 0 { return -1; } + // ⚠️ 缓存表必须:① 装填因子守卫(表满时开放寻址探测永不落空 = 挂死——P0 C2 同族, + // Task 1 实测:固定 1024 容量在编译器自身语料(数千 ti)下必挂);② 重建时重放既有 + // 条目;③ **回写前重探**(递归翻译可能触发扩容/重建,直接写旧 slot 会写错槽)。 + slot2 := sh_map_find(ti); + w64(g_shadow_map, slot2 * 16, ti); + w64(g_shadow_map, slot2 * 16 + 8, term); + g_shadow_entries = g_shadow_entries + 1; + return term; +} +``` +(`sh_tuple_to_product`:按 checker 侧 tuple 字段访问器逐字段 `sh_term_of_ti` 后串成 CONS 链——实现者按实际访问器落,签名 `fn sh_tuple_to_product(ti: int) -> int`。) + +- [ ] **Step 4: 表全局声明(`globals.cr`)+ 三处注册** + +```core +// R2 P1 影子对拍:ti → 类型项 缓存 + 统计 +g_shadow_map : string, mut; g_shadow_map_cap : int, mut; +g_shadow_map_count : int, mut; // 已占用槽数(装填因子守卫用) +g_shadow_hits : int, mut; g_shadow_entries : int, mut; +// 影子判定(Task 2):开关 + 站点 + 分类计数 + 环形缓冲 +g_shadow_on : int, mut; g_shadow_site : int, mut; +g_shadow_total : int, mut; g_shadow_agree : int, mut; +g_shadow_old_stricter : int, mut; g_shadow_old_looser : int, mut; +g_shadow_unknown : int, mut; g_shadow_buf : string, mut; +g_shadow_buf_count : int, mut; g_shadow_buf_cap : int, mut; +``` + +- [ ] **Step 5: 跑绿 + 提交** + +```bash +nice -n 19 python3 build_selfhost_native.py && nice -n 19 ./build/corec selftest-types # 期望 49+N 全 PASS +``` +提交(路径限定):`feat: R2 P1 Task 1——影子桥接层(checker ti → 引擎类型项,含 N 不入身份断言)` + +--- + +## Task 2: 影子判定核心 + `type_equal` 包装 + 通道 + +**Files:** +- Modify: `src/compiler/ty_shadow.cr`(判定与分类)、`src/compiler/checker.cr`(`type_equal` 改名 + 包装 + 站点标注)、`src/compiler/globals.cr`、`src/compiler/main.cr`(CLI) + +**Interfaces:** + +> **Task 1 评审落地注记(M1/M4,Task 2 一并办)**: +> ① **native 分派改用 `get_type_data` 显式比对 `TY_*` 码**(而非 `TI_*` 行号)——现状「`init_types` 行号序恰与 `TY_*` 码序一致」属**隐式依赖**(当前真、不保证),显式化后不随表布局变化而坏。 +> ② `ty_shadow.cr` 头注**点明「`AK_*` 与 `TI_*` 下标不 1:1(bool/str 互换)」**这一反直觉事实,防后人照数值直传(Task 1 已留守门用例 `bridge.str_ak`/`bridge.bool_ak`)。 + +``` +// 影子判定:翻译两侧 → ty_equiv → 分类计数(old = 旧判定的结果) +fn sh_compare(t1: int, t2: int, old_ok: int) -> void +fn sh_site_begin(site: int) -> void // 8 个外部决策点各设一个站点 id +fn sh_report() -> int // 打印摘要行;返回 0 +fn sh_dump_write(path_ni: int) -> int // 转储差异条目(-1 = 未启用) +// 分类计数访问器(自测/摘要用) +fn sh_count_agree() -> int fn sh_count_old_stricter() -> int +fn sh_count_old_looser() -> int fn sh_count_unknown() -> int +``` + +- [ ] **Step 1: 包装(`checker.cr`)**:`fn type_equal(t1: int, t2: int) -> bool` 改名为 `type_equal_core`(**内部 8 处递归调用一并改名**),新增: + +```core +// R2 P1 影子对拍包装:旧判定照常返回;影子判定只观察(--type-shadow 开时) +fn type_equal(t1: int, t2: int) -> bool { + r := type_equal_core(t1, t2); + if g_shadow_on != 0 { sh_compare(t1, t2, r ? 1 : 0); } + return r; +} +``` +(`r ? 1 : 0`:**本语言无三元运算符**——用 `ok : ., mut = 0; if r { ok = 1; }` 显式写。) + +- [ ] **Step 2: 站点标注**:在 8 个外部调用点(`checker.cr:711/947/959/973/1212/1405/1796/2127`)调用前 `sh_site_begin()`。 + **实读修正(Task 2 落地,取代初稿标签)**:8 个外部点**无一是 match 路径**;站点 4 = 泛型匹配(pattern 侧恒 `TI_UNIT` → 难产差异);站点 6 = `EXPR_BINARY+OP_ASSIGN` 遗留路径(**当前不可达**——parser 已把 `=` 统一降为 `EXPR_ASSIGN`,挂点保留但永不产样本,去留待 P2 裁决);站点 8 = `EXPR_ASSIGN` 节点(**非「索引」**)。站点 ↔ 语义以 `ty_shadow.cr` 头注(实读表)为准。 + +- [ ] **Step 3: 分类核心(`ty_shadow.cr`)** + +```core +fn sh_compare(t1: int, t2: int, old_ok: int) -> void { + g_shadow_total = g_shadow_total + 1; + a := sh_term_of_ti(t1); + b := sh_term_of_ti(t2); + if a < 0 || b < 0 { g_shadow_unknown = g_shadow_unknown + 1; return; } + ty_budget_reset(200000); + e := ty_equiv(a, b); + ty_budget_reset(200000); // 影子运行不污染后续 + if e == -1 { g_shadow_unknown = g_shadow_unknown + 1; sh_record(0, t1, t2, old_ok); return; } + // 注意语义:旧 true ⇔ 引擎 1 + if e == old_ok { g_shadow_agree = g_shadow_agree + 1; return; } + if old_ok == 0 && e == 1 { g_shadow_old_stricter = g_shadow_old_stricter + 1; } // 旧拒新受 + else { g_shadow_old_looser = g_shadow_old_looser + 1; } // 旧受新拒 = 收紧面 + sh_record(1, t1, t2, old_ok); +} +``` +`sh_record(flag, t1, t2, old)`:环形缓冲(前 256 条差异/未知)——每槽 `{site, t1, t2, old_ok, kind}`;满则只计数。 + +- [ ] **Step 4: 通道(`main.cr`)** + +```core + cli_flag_bool("type-shadow", "", "R2 P1: shadow type decisions with the engine (observation only)"); + cli_flag("type-shadow-dump", "", "R2 P1: dump shadow diff entries to file"); +``` +分派处:解析两旗标 → `g_shadow_on = 1` / dump 路径;编译结束前 `sh_report()`(摘要行 `[type-shadow] decisions=… agree=… old_stricter=… old_looser=… unknown=…`)+ 若给了 dump → `sh_dump_write(...)`。 + +- [ ] **Step 5: 判据(两态零变化 + 摘要可得)** + +```bash +nice -n 19 ./build/corec clean-cache +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/p1_off # 默认关 +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static --type-shadow -o /tmp/p1_on +cmp /tmp/r1t4_base_bin /tmp/p1_off && echo OFF-IDENTICAL +cmp /tmp/r1t4_base_bin /tmp/p1_on && echo ON-IDENTICAL # 影子不得改产物 +nice -n 19 ./build/corec check src/compiler/checker.cr --type-shadow | tail -3 # 摘要行出现 +``` + +- [ ] **Step 6: 提交**:`feat: R2 P1 Task 2——影子判定挂点(type_equal 包装 + 8 站点)+ 分类计数 + CLI 通道(两态产物逐字节不变)` + +--- + +## Task 3: 语料运行 + 差异清单归档 + +**Files:** +- Create: `docs/superpowers/specs/2026-09-10-type-shadow-findings.md` + +- [ ] **Step 0(前置,评审硬性要求):未知桶拆两因(**先改代码再采数**)**——`sh_compare` 里把「任一侧翻译失败(`sh_term_of_ti` 返回 -1)」与「引擎三态负值(-1/-2,含预算耗尽)」分记两个 kind(如 `kind=3` = 桥接 -1 / `kind=0` = 引擎负值)。理由(Task 2 评审):dump 现存条已混记,**若只做文档不拆代码,Task 3 的归因不可恢复**。 + +- [ ] **Step 1: 三档语料跑影子(各自 clean-cache + cwd=仓库根)** + +```bash +# ① 套件样本 +for f in tests/suite/*.cr; do nice -n 19 ./build/corec check "$f" --type-shadow; done >| /tmp/p1_suite.log 2>&1 +# ② 编译器自身(最大真实语料) +nice -n 19 ./build/corec check src/compiler/main.cr --type-shadow >| /tmp/p1_corec.log 2>&1 +# ③ 后端/内核单元 +nice -n 19 ./build/corec check src/compiler/ccr_io.cr --type-shadow >> /tmp/p1_corec.log 2>&1 +``` +(`check` 路径若不带影子通道穿透,改用 `build … --type-shadow` 并丢弃产物;实现者按实际可用路径落,报告写明。) + +- [ ] **Step 2: 汇总差异**:从日志抽 `[type-shadow]` 摘要行;若有 dump 文件则按 `kind` 分类统计 Top 差异(`old_looser` 优先——那是真正的收紧面)。 + **未知桶须拆两因**(Task 2 落地时同记 `kind=0`):① **任一侧无法翻译**(桥接返回 -1,如未支持的 kind);② **引擎三态 -1**(AK_NAMED 未展开 / 预算耗尽 / 未覆盖面)。Task 3 归因前**必须先把这两类分开统计**(否则「设计预期的 AK_NAMED 未知」会与「桥接缺口」混为一谈)。 + **dyn 类必须单列**:`TYP_DYN`(dyn 位图)按计划映射为 `AK_DYN`(=⊤)属**潜在**过宽近似(Task 1 评审:全仓仅 row 7 一处且今日被快路径截获、无位图 dyn 构造点)→ 凡两侧任一带 dyn 条目的差异一律归入「近似噪声」类,**不得计入收紧面/宽松面**,报告中单列计数与样本。 + **`&T` vs `&mut T` 必须单列**:桥接把 `TYP_REF` 译为 `AK_REF[inner]`(**丢 mut**)而旧 `type_equal` 比较 mut → 该对会**稳定产出**差异;归「已知 by-design(引用可变性 = P3「条目化 + 变型」面)」,**不得计入收紧面**(同款:PTR 的 `address_space` 旧亦不比 → 无差异,无需处理)。 + +- [ ] **Step 3: 写 findings 文档**(`docs/superpowers/specs/2026-09-10-type-shadow-findings.md`):逐语料的计数表 + 差异样本(site/t1/t2/旧/新)+ 初步归因(结构性 vs 未覆盖面 vs 真收紧)+ 后续裁决建议(哪些进 P2 的替换清单、哪些需补引擎规则)。 + +- [ ] **Step 4: 提交**:`docs: R2 P1 Task 3——影子对拍差异清单(三档语料计数 + 分类样本 + 归因与裁决建议)` + +--- + +## Task 4: 收官(回归 + 自举 + 文档/台账) + +- [ ] **Step 1: 全量回归**(同 P0 Task 5 的 13 项清单 + `selftest-types`) +- [ ] **Step 2: 两态零变化复验**(Task 2 Step 5 的命令,含 `check` 与 `build` 两条路径) +- [ ] **Step 3: 自举链**(`corec2`→`corec3` `cmp` IDENTICAL + N06=0 + 冒烟 rc=42) +- [ ] **Step 4: 文档**:spec §9 P1 行标 ✅ + 落点;TODO 登记(影子模式开关的默认值与产物影响、差异清单的后续裁决归属);台账 +- [ ] **Step 4c: Task 3 评审遗留(4 Minor + 5 登记,随本步一并办)**: + - **M1 文档精度**:`type-shadow-findings.md` TL;DR「71 个文件」→「67 有效(71 候选 − 4 排除)」;§3.1「不变式在全部 71 行成立」→「67 条摘要行」。**注意:工作副本里已有 2 行未提交修正(`jj diff` 可见)——先核其正确性,与其余修正一并提交**。 + - **M2**:report §1.1 / findings §1.1 / 源码注释的「前 6 字段前缀不变」→「**前 5 组 key=value**」(评审从 `28fed09` 的 `sh_report` 逐字比对)。 + - **M3(注释与关态开销)**:`ty_shadow.cr` 注释称「影子关时 wrapper 不调本函数」**不成立**——8 站点无条件调 `sh_site_begin`(全仓仅 `type_equal` 内一处 `g_shadow_on` 判断)→ 关态每次多一次调用 + 一次性 64B alloc + RMW 自增。**产物仍逐字节相同(已实测)**,但注释理由错误且关态开销未测。→ 二选一并记录:① 改注释如实描述;② 给 `sh_site_begin` 加 `if g_shadow_on == 0 { return; }` 早退(推荐 ②,兼消除关态开销)。 + - **M4**:dump 的 kind 名 `unknown`→`unknown_engine`(brief Step 0 契约变更,仓内无消费者)→ 在 findings 里注明读 dump 者须知。 + - **登记 TODO(5 条)**:① **F1 = P2 硬前置**(同名 named 多行未规范化 → 换判定即从真变未判定;P2 第一项 + 守门用例);② **F2 = P2 裁决**(数组长度 N 旧比引擎不比:现状 `error[TF01]`,替换后静默通过);③ **F3**(调用位点实参类型不匹配无诊断,`unify_types` 返回值被丢弃 `checker.cr:1053`);④ **F4**(泛型函数后续形参声明类型在推断中不生效);⑤ 仓库卫生(`tests/suite/test_control_flow.cr` / `test_generics.cr` **0 字节**;`src/compiler/elf.cr` 陈旧 parse 不过;`linker.cr` 空文件) + - **登记 harness 缺口(新,评审发现)**:`.claude` 的 `block-git.py` 只拒「以 `git` 开头」的命令 → 复合/管道命令可绕过(铁律 #2「机械拦截」有洞)。**建议单开修复**(按 shell 分隔符分词后做词边界匹配);本轮只登记不改 harness。 + +- [ ] **Step 4b: 挂账清零**(按 Task 1/Task 2 评审实际状态更新): + - ~~① `build_selfhost_native.py:309` 注释更正~~ ——**已被 Task 2 提交完成,核销即可**(Task 2 评审 M2 提示勿重复劳动) + - ② Task 1 评审「>1024 条目第二次重建无实测」——补一条守门用例或如实登记 + - ③ **既有缺陷登记(最小复现已由 Task 2 评审更正)**:**函数体内嵌套 `fn` 声明 → 编译 rc=139**(min4/min6 同族;mini6 **无** `@inline`——原报告措辞有误);崩点在 parse→checker 之间(日志止于 `[3/5] parse...`);**两版编译器均复现**(非影子层引入)→ TODO #16 + - ④ **Task 2 评审 M3**:`src/compiler/type_terms.cr:73` 头注「corearch/corelsp 不受影响」半句陈旧(corelsp 自 Task 2 起必须链接引擎层)→ 更正 + - ⑤ Task 2 评审 M5:站点 4 因 `res_call_type` 无 `EXPR_ARRAY`/tuple 分支将**恒 agree** → Task 3 findings 中不得据「站点 4 零差异」判该面收敛(写入 findings 文档的限制说明) +- [ ] **Step 5: 提交**(路径限定) + +--- + +## 自检记录 + +- **spec 覆盖**:§4 步 2(旁挂对拍)→ Task 1-2;§8 判据 3(对拍层:零差异门才允许替换)→ Task 3(清单产出,替换门属 P2);§9 P1 行 → Task 4。 +- **占位符扫描**:无 TBD;`sh_tuple_to_product` 明确要求实现者按实际 tuple 访问器落并给签名(非「稍后填」)。 +- **命名一致性**:`sh_*`(影子)与 `tt_*`/`ty_*`(引擎)分工清晰;`type_equal_core` 改名后内部递归与外部调用边界在 Task 2 Step 1 写明。 +- **风险注记**:① 影子必须**不改变** `ty_budget_reset`/memo 的语义外溢 → `sh_compare` 前后各重置一次预算;② `check` 路径能否穿透旗标未验证 → Task 3 Step 1 给了退路(`build` + 丢产物)并要求报告写明;③ 差异量可能很大 → 环形缓冲上限 256 条 + 计数不封顶。 diff --git a/docs/superpowers/plans/2026-09-10-r2-p2-replace.md b/docs/superpowers/plans/2026-09-10-r2-p2-replace.md new file mode 100644 index 00000000..8aa404a7 --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-r2-p2-replace.md @@ -0,0 +1,209 @@ +# R2 P2(逐点替换 · 批 a)实施计划:F1 表格去重 + F2 长度约束 + 判定替换 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `type_equal` 的判定从「结构判等」替换为「P0 引擎语义判定」,并按 P1 findings 的两个前置(F1 同名 named 表格去重、F2 数组长度转长度约束)保证替换**不留静默缺口**。 + +**Architecture:** 三步:① **F1**(用户裁决 = 类型表建表去重):`TYP_NAMED` 同名归一行(唯一分配点 `alloc_type` 加名字去重侧表),消除 P1 观察到的 9 条 unknown 根因;② **F2**(用户裁决 = 落常量档长度约束):`[T;N]` 的 N 从「类型身份」迁为**显式长度约束检查**(保持现状拒绝语义,不静默通过);③ **替换**:`type_equal` 的判定改走引擎(旧实现保留为 `type_equal_legacy` 供本轮对拍复跑,P5 收尾删除)。 + +**Tech Stack:** Core 自举栈;P0 引擎(`type_terms.cr`/`type_engine.cr`);P1 桥接与影子通道(`ty_shadow.cr`:`sh_term_of_ti` / `--type-shadow` / 分类计数);检查器 `checker.cr`。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §4 步 3(逐点替换)+ §5.1(定长退役:常量档长度约束)+ §9 P2。**P1 交接**:`docs/superpowers/specs/2026-09-10-type-shadow-findings.md`(F1 硬前置 / F2 待裁决 / 站点覆盖须同报 / unknown 政策)。 + +**范围**:本批 = **P2a**(F1 + F2 + `type_equal` 替换 + 对拍复跑 + 收紧清单)。**P2b**(本批不做、另立计划):`infer_expr` 公理区 → `iface_ops` 查表(本质条目表 + 操作许可 + 字面量定型)、`res_type_node` 双份 TY→TI 映射合一。 + +## Global Constraints + +- **jj only(含只读与复合命令一律不用 `git`)**;**提交必须路径限定**;所有命令 `nice -n 19`;判据前 `clean-cache` 且 cwd = 仓库根。 +- **行为零变化面(P2a 的硬判据)**:`type_equal` 语义替换后,**ELF 产物必须与基线逐字节相同**(`tests/suite/ptr_arith.cr` vs `/tmp/r1t4_base_bin`,sha256 `95084e7b…d475`);影子开/关两态亦逐字节相同。 +- **`.ccr` 字节可能变化**(类型行去重 → 索引重编号):**允许但必须实测并报告**,且 `test_ccr_v7` 结构性判据必须全绿;若 ELF 也变 → **停下上报**(说明类型信息泄进了编码层,属越界)。 +- **替换门(P1 交接,硬性)**:报告差异数时**必须同时报站点覆盖**(P1 实测站点 1/2/4/6 零命中);「差异 = 0」+「站点覆盖不全」≠ 通过。 +- **unknown 政策**:引擎返回 -1(未知)时**不得静默当 0/1**——替换后的判定语义必须与替换前在**全部语料**上一致(差异清单归零),任何残留 unknown 必须逐条登记。 +- 本语言无三元运算符;取模须非负;键比较不得依赖 i64 回绕;noclobber(用 `>|`)。 +- 文件永久不允许还原;不得绕过。 + +--- + +## Task 1(F1):类型表同名 `TYP_NAMED` 去重 + +**Files:** +- Modify: `src/compiler/checker.cr`(新增 `alloc_named_type`;8 个 `TYP_NAMED` 分配点改调它;去重侧表) +- Modify: `src/compiler/globals.cr`(侧表声明) +- Modify: `src/compiler/type_selftest.cr`(守门用例) + +**Interfaces:** +``` +// 同名 named 归一行:命中返回既有行;未命中分配 + 登记(侧表 g_named_dedup:name_idx → ti) +fn alloc_named_type(name_idx: int) -> int +fn named_dedup_rows(name_idx: int) -> int // 自测用:该名字当前占用的行数(恒 0/1) +``` + +**依据(侦查已核)**:`alloc_type` 是唯一分配点(`checker.cr:9-17`);`TYP_NAMED` 的 8 个分配点全部是 `alloc_type(TYP_NAMED, name_idx, 0)`(`:406/554/598/613/622/920/2191/2206`);**12 个读取点全读 `get_type_data(ti)`(名字而非行号)** → 去重不改语义。 + +- [ ] **Step 1: 守门用例(`type_selftest.cr`;红)** + +```core + // --- F1:同名 named 归一行(P2a)--- + n1 := alloc_named_type(str_intern("DedupProbe")); + n2 := alloc_named_type(str_intern("DedupProbe")); + total = total + 1; fails = fails + ts_check("f1.same_name_one_row", (n1 == n2), 1); + total = total + 1; fails = fails + ts_check("f1.row_count", named_dedup_rows(str_intern("DedupProbe")), 1); + n3 := alloc_named_type(str_intern("DedupProbe2")); + total = total + 1; fails = fails + ts_check("f1.diff_name_diff_row", (n3 != n1), 1); + // 引擎侧:同名两行归一后,桥接的同一性成立(P1 的 9 unknown 根因消除) + total = total + 1; fails = fails + ts_check("f1.bridge_same_term", + (sh_term_of_ti(n1) == sh_term_of_ti(n2)), 1); +``` + +- [ ] **Step 2: 运行确认红**(`alloc_named_type` 未定义 → 构建失败/用例挂) + +- [ ] **Step 3: 实现(`checker.cr` + `globals.cr`)** + +```core +// globals.cr +g_named_dedup : string, mut; g_named_dedup_cap : int, mut; g_named_dedup_count : int, mut; + +// checker.cr:侧表 = 开放寻址(16B/条 {name_idx, ti}),与影子侧表同式(含装填守卫 + 重建重放 + 回写前重探) +fn alloc_named_type(name_idx: int) -> int { + slot := named_dedup_slot(name_idx); // 含守卫/重放,见下 + if r64(g_named_dedup, slot * 16) == name_idx { return r64(g_named_dedup, slot * 16 + 8); } + ti := alloc_type(TYP_NAMED, name_idx, 0); + w64(g_named_dedup, slot * 16, name_idx); + w64(g_named_dedup, slot * 16 + 8, ti); + g_named_dedup_count = g_named_dedup_count + 1; + return ti; +} + +fn named_dedup_rows(name_idx: int) -> int { // 自测用:该名字在 g_types 里的行数 + n : ., mut = 0; + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + if get_type_kind(i) == TYP_NAMED && get_type_data(i) == name_idx { n = n + 1; } + i = i + 1; + } + return n; +} +``` +(`named_dedup_slot` = 开放寻址探测,**必须有装填因子守卫 + 重建重放 + 回写前重探**——照 `ty_shadow.cr` 的 `sh_map_*` 先例,勿重蹈 P0/P1 的表满死循环。) + +- [ ] **Step 4: 8 个分配点改调**:`checker.cr:406/554/598/613/622/920/2191/2206` 的 `alloc_type(TYP_NAMED, name_idx, 0)` → `alloc_named_type(name_idx)`。 + +- [ ] **Step 5: 判据** + +```bash +nice -n 19 python3 build_selfhost_native.py +nice -n 19 ./build/corec selftest-types # 期望 **77/77**(72 + 4 计划用例 + 1 条装填守卫/重放用例——实现者实测补面,已声明) +nice -n 19 ./build/corec clean-cache +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/p2a_t1_bin +cmp /tmp/r1t4_base_bin /tmp/p2a_t1_bin && echo ELF-IDENTICAL # 必须相同 +# .ccr 是否变化:实测并记录(允许变,须报告) +nice -n 19 python3 tests/selfhost/test_ccr_v7.py # 结构判据须全绿 +nice -n 19 python3 tests/selfhost/test_compile.py +``` +- 若 `ELF-IDENTICAL` 不成立 → **停下上报**(类型信息泄进编码层)。 + +- [ ] **Step 6: 提交**:`feat: R2 P2a Task 1(F1)——同名 TYP_NAMED 建表去重(唯一分配点 + 侧表守卫/重放/重探;12 读取点读名字故语义不变)` + +--- + +## Task 2(F2):数组长度转「常量档长度约束」 + +**Files:** +- Modify: `src/compiler/checker.cr`(`type_equal` 的 `TYP_ARRAY` 分支去掉 N 比较;赋值/比较路径加常量档长度检查) +- Modify: `src/compiler/type_selftest.cr`(用例) + +**Interfaces:** +``` +// 常量档长度约束:两侧皆 `[T;N]` 且 N 皆字面量 → N 必须相等(否则诊断); +// 引擎侧 `[T;N]` 与 `[T;M]` 等价(N 不入身份),故本检查**不**经 type_equal +fn array_len_constraint_ok(ti_a: int, ti_b: int) -> int // 1 = 满足;0 = 违反 +``` + +- [ ] **Step 1: 用例(红)**:同长通过 / 异长拒绝(现状语义保持) + +```core + total = total + 1; fails = fails + ts_check("f2.same_len_ok", + array_len_constraint_ok(alloc_type(TYP_ARRAY, TI_INT, 3), alloc_type(TYP_ARRAY, TI_INT, 3)), 1); + total = total + 1; fails = fails + ts_check("f2.diff_len_reject", + array_len_constraint_ok(alloc_type(TYP_ARRAY, TI_INT, 4), alloc_type(TYP_ARRAY, TI_INT, 3)), 0); + // 引擎侧:N 不入身份(spec §5.1) + total = total + 1; fails = fails + ts_check("f2.engine_len_agnostic", + ty_equiv(sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 4)), sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 3))), 1); +``` + +- [ ] **Step 2: 实现**:`type_equal` 的 `TYP_ARRAY` 分支(`:110-119`)去掉 `extra` 比较;赋值/参数/返回等**判定点**在 `type_equal` 之外补 `array_len_constraint_ok`(或等价的显式检查),使 `[int;4]` → `[int;3]` 仍报错(诊断措辞改为「长度约束不满足」),且 `error[TF01]` 门语义不变。 + +- [ ] **Step 3: 判据**:`selftest-types` 全 PASS;现有 `error[TF01]` 用例语义不变(`tests/suite/test_control_flow.cr` 等);全回归 + ELF 逐字节 + `.ccr` 实测报告。 + +- [ ] **Step 4: 提交**:`feat: R2 P2a Task 2(F2)——数组长度 N 迁为常量档长度约束(身份去 N,拒绝语义保持:异长仍报错、不静默通过)` + +--- + +## Task 3:判定替换(`type_equal` → 引擎) + +**Files:** +- Modify: `src/compiler/checker.cr`(`type_equal` 实现替换;旧实现保留为 `type_equal_legacy`) +- Modify: `src/compiler/ty_shadow.cr`(影子对照物切到 legacy) + +**Interfaces:** +``` +// 替换后:快路径(同一 ti → true)+ 引擎语义判定(ty_equiv);-1(未知)按 unknown 政策处理 +fn type_equal(t1: int, t2: int) -> bool +fn type_equal_legacy(t1: int, t2: int) -> bool // 替换前的结构判等(本轮对拍对照物,P5 删) +``` + +- [ ] **Step 1: 替换实现** + +```core +fn type_equal(t1: int, t2: int) -> bool { + if t1 == t2 { return true; } // 同一行快路径 + a := sh_term_of_ti(t1); + b := sh_term_of_ti(t2); + if a < 0 || b < 0 { return type_equal_legacy(t1, t2); } // 桥接不了 → 回落 legacy(登记) + ty_budget_reset(200000); + e := ty_equiv(a, b); + ty_budget_reset(200000); + if e == 1 { return true; } + if e == 0 { return false; } + // e == -1(未知):**不静默**——回落 legacy 并计数(unknown 政策),报告里单列 + g_replace_unknown = g_replace_unknown + 1; + return type_equal_legacy(t1, t2); +} +``` +(`g_replace_unknown` 计数器 + 摘要行扩展,供报告。) + +- [ ] **Step 2: 对拍复跑(替换门)**:影子通道的对照物改为 `type_equal_legacy`,重跑 P1 三档语料 → **差异清单必须归零**(agree = 全部;unknown 须为 0 或逐条登记);**报告必须同时报站点覆盖**。 + **Task 1 交接的三条必测项**(实现者 concern): + - ① **同名字泛型应用跨站点探针**(C-1):F1 去重把 `TYP_GENERIC_APPLY` 分支从「比基型**行号**」放宽为「同名 ⇒ 继续比实参」= 真实语义变化,现有语料未显形 → **必须造跨站点同名字泛型应用样例**专测;**且必须含负控**(同名**不同实参**不得因去重被判等——评审 Task 1 裁决)。 + - ④ **管线内断言**(C-4):编译 `at_test_struct` 后按名查侧表命中 ti **必须等于** `res_type_node` 的返回值(补「8 生产点全走侧表」的行为级证据,替代弱断言 `f1.row_count`)。 + - ② **自指语料**(C-5):编译器自身(ti 数千)的 `.ccr` 行号位移——最可能暴露去重副作用的语料,**必须实测报告**; + - ③ **unknown 计数**(替换后回落 legacy 的次数)须与站点覆盖同报。 + +- [ ] **Step 3: 判据**:全回归 14 套件 + `selftest-types` + ELF 逐字节 + 自举 `cmp` IDENTICAL + N06=0 + 冒烟 42。 +- [ ] **Step 3b: N 面验收必须以行为探针承担(Task 2 评审 Important,硬性)**:**F2 之后影子对 N 面已「失明」**——影子的「旧」参照物 `type_equal_core` 自身也已 N-free ⇒ `old_stricter` 必为 0(同义反复,无诊断力;P1 探针 D 的 `old_stricter=1` 才有)。故本步的「对拍归零」**不得**作为 N 面证据;N 面验收 = **行为探针**(异长拒绝 / 同长通过 / **嵌套位**:`[[int;3];2]`、`G<[int;3]>`、`*[int;3]`、`(int,[int;3])`)。 +- [ ] **Step 3c: N 不得回身份(Task 2 评审裁决,硬性)**:替换后确认 `type_equal` 路径**不再**包含 N 比较;长度面一律走 `array_len_constraint_ok`(否则等于回退 F2)。 + +- [ ] **Step 4: 提交**:`feat: R2 P2a Task 3——type_equal 判定替换(快路径 + 引擎语义判定;未知回落 legacy 并计数,不静默)+ 对拍复跑差异归零 + 站点覆盖同报` + +--- + +## Task 4:收官(回归 + 自举 + 收紧清单 + 文档) + +- [ ] **Step 1: 全量回归**(14 套件 + `selftest-types`) +- [ ] **Step 2: 零变化复验**:ELF 逐字节 + 影子开/关两态逐字节 + `.ccr` 变化实测报告 +- [ ] **Step 3: 自举链**:`corec2`→`corec3` `cmp` IDENTICAL + N06=0 + 冒烟 rc=42 +- [ ] **Step 4: 收紧清单**:本批「旧接受 → 新拒绝」逐条记录(**预期为空**——P1 实测 old_looser = 0;若非空,逐条登记并说明) +- [ ] **Step 5: 文档**:spec §9 P2 行标(a 部分 ✅ + P2b 待办);TODO 更新(F1/F2 划销;`type_equal_legacy` 删除归 P5);findings 文档追加「P2a 后复跑」一节 +- [ ] **Step 5c: Task 2 评审遗留(并入本步)**:① **自指语料 `.ccr` 冷缓存对比**(C-5① 收口——Task 2 未直接采样;不得只看替代证据);② **M2 用例**:补 REF/SLICE 下钻位 + `type_compat_strict` 的 0/−1 措辞分派用例(把「站点接线」纳入自测面)。 +- [ ] **Step 5b: 新登记(Task 1 评审发现)**:**`.ccr` 输出与 cir 缓存态相关**——同一二进制二次运行产出不同 `.ccr` 字节(评审实测 `53ebcf12…`→`5054e26d…`;冷缓存下 pre/post 行为一致)。归入 TODO #5 家族(缓存键缺编译器/环境身份)并单列一行:**判据比较 `.ccr` 前必须 clean-cache**(并入本计划的 Global Constraints 精神) +- [ ] **Step 6: 提交**(路径限定) + +--- + +## 自检记录 + +- **spec 覆盖**:§4 步 3 的 `type_equal → ty_equiv` → Task 3;§5.1 定长退役的常量档 → Task 2;§9 P2 行 → Task 1-4;§4 步 3 的「`infer_expr` 公理区 → `iface_ops`」「`res_type_node` 两表合一」→ **明标 P2b 另立**(本计划不做,已在范围节写明)。 +- **占位符扫描**:无 TBD;`named_dedup_slot` 明确要求照 `sh_map_*` 先例(含守卫/重放/重探),非「稍后填」。 +- **命名一致性**:`alloc_named_type` / `named_dedup_rows` / `array_len_constraint_ok` / `type_equal_legacy` / `g_replace_unknown` 在任务间一致。 +- **风险注记**:① F1 去重使类型行数减少 → `.ccr` 索引重编号(**允许但须实测报告**;ELF 必须不变,否则停下上报);② 替换后 unknown 回落 legacy = 过渡策略,须计数并在 P5 前清零;③ 站点覆盖须与差异数同报(P1 交接硬性要求)。 diff --git a/docs/superpowers/plans/2026-09-10-x86-instance-wave1.md b/docs/superpowers/plans/2026-09-10-x86-instance-wave1.md new file mode 100644 index 00000000..67f6602b --- /dev/null +++ b/docs/superpowers/plans/2026-09-10-x86-instance-wave1.md @@ -0,0 +1,135 @@ +# x86 实例化 波 1(结构波)实施计划 + +> **状态:实施完成(2026-09-10 波 1 结构波 Task 1-7 收官)——落点:Task 0 盘点 327c2cf01af3(设计定稿 6694f358f376)/ Task 1 三轴搬迁 + 组合根 29d177ac49a5(+ 补正 c927525baf35、docs 58e6be3a1c18)/ Task 2 entry.cr 整搬 b23f008000cf(+ docs 8dcfaeca52b2)/ Task 3 frame.cr 抽取 b0ba8c1c9b01 / Task 4 tag2l.cr 整搬 d65fc1b8f3c3(+ docs c6b6cbfe9a75)/ Task 5 callseq.cr 抽取 3ab39f6eaa16(+ docs 525a872a0ba7)/ Task 6 syscall.cr 抽取 94d1cac15359(+ docs d2f3e0968d8c)/ Task 7 = 收官提交**。判据全绿:**行为零变化**(每任务 stage 链 byte-identical + 收官全量回归逐套计数)+ 自举重建冒烟 + full-bootstrap guard(corec2/corec3 cmp 同 + N06=0)+ syscall4 套件持久覆盖(`tests/suite/syscall4_test.cr`)——逐套计数与未做项见设计 spec `2026-09-10-x86-instance-design.md` §8 执行注记。 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 三轴目录重组(架构 × 格式 × OS)+ 序列算法文件化 + 表数据迁架构轴 + 构建清单分段守卫——**行为零变化**(纯搬移/文件化,stage 链 byte-identical)。 + +**Architecture:** 序列算法现全部内联在 elf_gen(elf.cr 982-1830,849 行)与 emit_instr(instr.cr 986-2183,1198 行)两个巨型函数里——波 1 = 零抽取搬迁(Task 1)优先,随后逐类文件化(entry 整搬 → frame 抽取 → tag2l 整搬 → callseq 抽取 → syscall 抽取),**每任务独立提交 + stage 链 byte-identical 判据**(H1:抽取改变 cp/pos/buf 作用域——绝不合并多个抽取点)。组合根裁决 = `src/targets/x86_64-linux/`(target triple = 三轴组合的正式名)。 + +**Tech Stack:** elf.cr/instr.cr(拆分源)、module.cr 回退链、build_selfhost_native.py 清单、tests/selfhost/(判据套件)、tools/pseudocode_extract.py。 + +**设计 spec:** `docs/superpowers/specs/2026-09-10-x86-instance-design.md`(§2 三轴目录/§3 清单分段/§5 波 1/§6 挂账)。 + +## Global Constraints + +- 版本控制 `jj`(铁律 #2);每任务 `jj commit -m ''` +- 构建 `nice -n 19 python3 build_selfhost_native.py`;测试 `nice -n 19` 前缀(铁律 #6);清 .core/cache +- **行为零变化硬约束**:每任务判据 = `test_backend_bootstrap.py` 11/11(stage 链 run 内 byte-identical)+ 相关套件(hit_table 24/24 表路径保持 / ccr_v7 24/24 / live_ranges 13/13 / compile);收官全量 +- **抽取纪律(H1)**:每抽一段 = 独立提交 + stage 链验证;抽段时 cp/pos/buf 三参数显式传递;**帧公式双源(H2)必须单入口**——`sizes.cr:4-10` 头注明令「NEVER hardcode byte counts」;`test_mw_task1.py` 的 sub rsp 立即数断言 = 帧专项判据 +- 归属裁决(H3):`g2_tag_off`/`mw_frame_size` 归 `frame.cr`;`tag2l.cr` 依赖 frame(同轴内);mw 族 e2_mw_* 纯编码归 tag2l;e2_* 通用编码原语(640-985)**留 instr.cr** +- stage0 硬编码点 `test_backend_bootstrap.py:13` 必须先改后搬(H6) +- TODO #6(双入口 flag 分歧)**不与波 1 混合**——结构波只搬移 + 同步改「两入口行为同构」注记措辞(H7);flag 收敛留波 2 + +--- + +## Task 0: 盘点(锚点已备——本计划附注) + +**Files:** 分析产出(勘探已完成,锚点并入本计划文末「盘点结果」节) +- 交付:文末三张表(切分映射/搬迁影响面/路径改点清单)——Task 1-6 依此执行 + +--- + +## Task 1: 三轴搬迁 + 组合根 + 清单分段守卫(零抽取) + +**Files:** +- Move: `src/arch/linux/ld/{elf.cr,resolve.cr,ld.cr}` → `src/format/elf/`;`{instr.cr,sizes.cr,regalloc.cr}` → `src/arch/x86_64/`;`core-x86.toml`(自 `src/arch/hit/`)→ `src/arch/x86_64/`;`{main.cr,_import.cr,Core.toml}` → `src/targets/x86_64-linux/`(组合根裁决——target triple 命名) +- Modify: `src/compiler/module.cr`(回退链 +4:`src/format/elf`、`src/arch/x86_64`、`src/os/linux`、`src/targets/x86_64-linux`——锚点 §2.3;置放序在既有条目前后保持既有命中不漂) +- Modify: `build_selfhost_native.py`(corearch 清单 6 行路径改 + **分段重构**:`common_files`/`kernel_files`/`arch_x86_64_files`/`format_elf_files`/`os_linux_files`/hit 段/`x86_linux_target_files`——组合序确定;**守卫**:清单文件存在性断言 + 构建日志 `error[` 计数非零 = 失败门(TODO #6 建议③)) +- Modify: `tests/selfhost/test_backend_bootstrap.py:13`(BACKEND_SOURCE → 组合根——先改后搬)+ `tests/selfhost/test_hit_table.py:28`(TABLE → src/arch/x86_64/core-x86.toml)+ `tools/pseudocode_extract.py:6-7`(ROOTS 替换 ld → 三轴目录) +- Modify: 注释同步(`instr.cr:2189` 表路径注、`src/arch/hit/hit.cr:2`、`lower_to_core.cr:44`、`src/stdlib/toml.cr:163`、`ld/main.cr:11-15` 两入口注记措辞(H7)) +- Test: 全回归快子集 + stage 链 + +**Interfaces:** +- Consumes: 盘点锚点(文末表) +- Produces: 三轴目录骨架 + 组合根(stage0 = `corec build src/targets/x86_64-linux`)+ 分段清单 +- 判据: stage 链 byte-identical + hit_table 24/24 + compile + 抽检(文件内容零改动——R 重命名) + +- [ ] **Step 1:** 先改 stage0 硬编码点(test_backend_bootstrap:13 → 组合根)+ module.cr 回退链 +4 +- [ ] **Step 2:** 文件搬迁(按依赖序——§5.1 第 0 步:format/arch/targets 并行)——`src/os/linux/` 空目录占位(Task 2+ 使用) +- [ ] **Step 3:** 清单分段重构 + 守卫(存在性 + error 计数门)+ hit 表路径改点 +- [ ] **Step 4:** 构建 + stage 链 + hit_table + 回归快子集(判据:全绿) +- [ ] **Step 5:** 提交 `refactor: x86 实例化波 1 Task 1——三轴目录搬迁 + 组合根(src/targets/x86_64-linux)+ 清单分段守卫(零抽取)` + +--- + +## Task 2: entry.cr 整搬(低风险) + +**Files:** +- Move: `elf.cr` `emit_start`(858-959)+ `emit_start_size`(961-979)→ `src/os/linux/entry.cr`(整函数——外部耦合 = gv_argc/gv_argv/gv_current_arena 索引全局,随迁暴露) +- Modify: `elf.cr`(调用点 1188 → 跨轴调用,回退链已备) + +**Interfaces:** +- Consumes: Task 1 骨架 +- 判据: stage 链 byte-identical + 全回归快子集 +- [ ] **Step 1:** 整函数搬迁 + 调用点接线;**Step 2:** 构建 + 判据;**Step 3:** 提交 `refactor: x86 实例化波 1 Task 2——emit_start 整搬 src/os/linux/entry.cr` + +--- + +## Task 3: frame.cr 抽取(H2 帧公式单源——波 1 最大风险之一) + +**Files:** +- Create: `src/arch/x86_64/frame.cr`(导出三入口:`pf_frame_size(vc)`(= mw_frame_size + dry-run 核算合流)/`pf_prologue`(= Phase3 1228-1247 序言段)/`pf_epilogue`(= 1372-1392 尾声段,含 opt≥1 逆序 pop);形参落槽段(1248-1319)与 ret patch/sub rsp 回填段(1352-1370)随 prologue/epilogue 语义归并——**具体切界以实现为准,原则 = 帧相关段全入 frame.cr**) +- Modify: `elf.cr`(Phase 2 dry-run 1078-1113 与 Phase 3 1197-1442 双调用点各接 `pf_frame_size`/`pf_prologue`/`pf_epilogue`——**双源合流**;`g2_tag_off`/`mw_frame_size` 自 instr.cr 迁入 frame.cr(H3)) +- Modify: `instr.cr`(g2_tag_off/mw_frame_size 迁出——mw 族消费点引用调整) +- 判据: stage 链 byte-identical + `test_mw_task1.py`(sub rsp 立即数专项——帧公式错即红)+ 全回归快子集 +- [ ] **Step 1:** pf_frame_size 单源合流(dry-run + Phase3 两处改调——最险一步单独验证);**Step 2:** pf_prologue/pf_epilogue 抽取;**Step 3:** g2_tag_off/mw_frame_size 迁入;**Step 4:** 构建 + stage 链 + mw 套件;**Step 5:** 提交 `refactor: x86 实例化波 1 Task 3——frame.cr 抽取(帧公式双源合流单入口)` + +--- + +## Task 4: tag2l.cr 整搬(mw 族纯编码) + +**Files:** +- Move: `instr.cr` mw 族(144-637,除 g2_tag_off/mw_frame_size 已入 frame)→ `src/arch/x86_64/tag2l.cr`(e2_mw_jo/slow_block/t8/b8/ld8/st8/tag_clr/tag_cpy/oc_new/oc_check/ld2/sext/opnd_block + mw_setup_tags/mw_int_arith_jo_needed——**整函数搬**) +- 注: elf.cr 慢块/2L 块发射循环(1394-1441)随 frame 尾声(Task 3 已并)——本任务只搬纯编码 +- 判据: stage 链 byte-identical + mw1-6 全套 + 回归快子集 +- [ ] **Step 1:** mw 族搬迁(逐函数,两文件同轴)+ 消费点接线;**Step 2:** 构建 + mw 套件 + stage 链;**Step 3:** 提交 `refactor: x86 实例化波 1 Task 4——tag2l.cr 整搬(mw 族纯编码)` + +--- + +## Task 5: callseq.cr 抽取(H1 最大风险——emit_instr 内联段落) + +**Files:** +- Create: `src/os/linux/callseq.cr`(抽 5 函数:`cs_args_dispatch`(寄存器参数分派——IR_CALL 1201-1220 + EXTERN 1443-1453 + SPAWN 1488-1506 三处同源合流)/`cs_stack_args`(栈参右到左压——1221-1245 + 1507-1531)/`cs_stack_cleanup`(1414-1424 + 1538-1549)/`cs_ret_value`(IR_RETURN 值序列 1578-1611,含 tag 读路径)/`cs_call_direct`(通用调用 1389-1408)) +- Modify: `instr.cr`(emit_instr 四处 `if op ==` 块改调——IR_CALL/IR_CALL_EXTERN/IR_SPAWN/IR_RETURN) +- 判据: stage 链 byte-identical + 调用密集程序(fib 等——backend_bootstrap 冒烟 + compile)+ 回归快子集 +- [ ] **Step 1:** cs_args_dispatch 三处同源抽取(最险——单独验证);**Step 2:** cs_stack_args/cs_stack_cleanup/cs_call_direct;**Step 3:** cs_ret_value;**Step 4:** 构建 + stage 链 + 冒烟;**Step 5:** 提交 `refactor: x86 实例化波 1 Task 5——callseq.cr 抽取(参数/栈/返回序列五函数,SysV 三处同源合流)` + +--- + +## Task 6: syscall.cr 抽取 + +**Files:** +- Create: `src/os/linux/syscall.cr`(`sys_syscall3_stub`/`sys_syscall4_stub`(instr.cr 1247-1268——r10 第四参注记随迁);builtin 名扫描段(elf.cr 990-1008)中 syscall 条目相关面——**扫描段整体可延后参数化波**(设计 §1.4——本任务只抽发射两函数,扫描段保留原位加注) +- Modify: `instr.cr`(builtin 链改调) +- 判据: stage 链 byte-identical + 回归快子集 +- [ ] **Step 1:** 两函数抽取 + 接线;**Step 2:** 构建 + 判据;**Step 3:** 提交 `refactor: x86 实例化波 1 Task 6——syscall.cr 抽取(syscall3/4 内置体)` + +--- + +## Task 7: 收官——全量回归 + 自举 + 文档 + +**Files:** +- Test: 全量回归(compile/backend_bootstrap/hit_table/region_cfg/mw1-6/slice_bounds/live_ranges/ccr_v7/bootstrap 三套 + full-bootstrap guard + 中立性 guard) +- Modify: 设计 spec(`2026-09-10-x86-instance-design.md` 状态 → 波 1 已实施 + 执行注记)+ 计划状态行 + TODO #6 注记同步(波 1 已搬未收敛) +- Test: `nice -n 19 python3 build_selfhost_native.py` 重建 + 冒烟 +- 判据: 全绿 + 文档同步 +- [ ] **Step 1:** 全量回归;**Step 2:** 重建 + 冒烟;**Step 3:** 文档同步;**Step 4:** 提交 `refactor: x86 实例化波 1 收官——全量回归 + 自举 + 文档同步` + +--- + +## 风险注记 + +- H1 巨型函数切分(Task 3/5):每段独立提交 + stage 链;cp/pos/buf 显式参数化 +- H2 帧公式双源(Task 3):pf_frame_size 单源合流 = 首步单独验证;mw_task1 sub rsp 立即数断言 = 专项判据 +- H5 跨轴缝 6 处(elf → arch):回退链是唯一通道——Task 1 Step 1 先落 +- H6 stage0 硬编码先改后搬 +- H7 TODO #6 不与波 1 混合(仅注记措辞同步) +- 文件内容零改动判据:搬迁任务(Task 1/2/4/6)应 R 重命名(内容 verbatim);抽取任务(Task 3/5)内容进新文件 + 调用点替换——diff 可读性 = 评审锚 + +--- + +## 盘点结果(勘探已备——2026-09-10) + +> 基源 = 勘探锚点清单(.superpowers/sdd 勘探产出,权威 = 设计 spec §2)。关键事实:**elf.cr 1830 行 = elf_gen 单巨函数 982-1830(849 行)+ 10 段体 helper;instr.cr 2588 行 = emit_instr 单巨函数 986-2183(1198 行)+ HIT 编码器 2185-2588 + e2_* 原语 640-985 + mw 族 144-637**——全部序列算法内联,无现成阶段函数。三表(切分映射/搬迁影响面/路径改点)见勘探清单(Task 1-6 的 Files 节已内联全部行锚)。 diff --git a/docs/superpowers/plans/2026-09-11-effect-purity-fix.md b/docs/superpowers/plans/2026-09-11-effect-purity-fix.md new file mode 100644 index 00000000..420e1ecf --- /dev/null +++ b/docs/superpowers/plans/2026-09-11-effect-purity-fix.md @@ -0,0 +1,100 @@ +# 效应/纯度事实修正 实施计划(P0 插队批) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把「函数纯度」从**未实现的乐观常量**改为**真计算**,使「调用」这类效应重新进入 state 链——消除「自动记忆化会缓存掉可观测效应」这条 P0 缺陷。 + +**Architecture:** 三步:① **真纯度计算**(保守起点 + 调用图迭代至不动点;递归/SCC 保守判不纯;不可解析调用不纯)替换 `checker.cr:1107/1142` 的两个乐观写入点,并给 `monomorph` 的新 `FuncInfo` 补该字段;② **state 链分类表补全**(`IR_CALL_EXTERN` + 间接调用/并发效应 opcode 入链;可解析调用按真纯度决定);③ **判据重定**(`.ccr` 的 EDG/NOD 期望重锁 + 边集语义断言;**ELF 逐字节不变**因 lazy 判定**解耦**而得以保留)。 + +**Tech Stack:** Core 自举栈;`src/compiler/checker.cr`(`collect_decls`/`find_func`)、`src/compiler/monomorph.cr`(实例创建)、`src/compiler/dataflow.cr`(`df_connect_state`)、`src/compiler/ir_gen.cr`(lazy 判定——**本批不动**)、`src/compiler/ccr_io.cr`(EDG/NOD 序列化)、`tests/selfhost/test_ccr_v7.py`(期望表)。 + +**侦查底座(2026-09-11 purity-recon,结论已实测)**: +- `fi_ispure` **仅 2 个写入点**(`checker.cr:1107/1142`)且均为常量 1(注释 "optimistic");读取点 **仅 2 处**(`dataflow.cr:171` state 链 / `ir_gen.cr:1590` lazy)。 +- 今日四态:普通函数=1(不入链)|extern=1 **且 `IR_CALL_EXTERN` 不在分类表**(双重缺失)|泛型实例=**0**(`monomorph.cr` 未写该字段 + `rt.s` zero-init ⇒ 读到 0)→ **意外入链**|不可解析 builtin(`find_func<0`)→ 保守入链 ✓。 +- 实测:`fn effect(){st=1;}` 被 main 调两次 ⇒ main 的 state 链**只有 store 边,两个 CALL 均不在**(`dataflow.cr:401` 每函数重置 ⇒ 跨函数效应序完全丢失)。 +- 影响面:state 边消费者 = `ccr_io`(EDG 段 + NOD 邻接字段)、`cir_cache`、`dump.cr`、`ent_kernel.v7_edge_kind`;**发射/解释器/regalloc 零消费** ⇒ **只改纯度(不动 lazy)时 ELF 逐字节不变**。 +- 权威语义(`plans/2026-08-08-region-cfg.md:484`):**非纯调用才进链;`find_func` 不可得(外部函数)保守进链**。 + +## Global Constraints + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**;命令 `nice -n 19`;判据前 `clean-cache`;cwd = 仓库根。 +- **本批不许动 lazy**:`ir_gen.cr:1590` 的 lazy 判定与 `IR_LAZY_THUNK/FORCE` 发射**保持原样**(其 use_count 时序缺陷已登记 TODO)——这是保住「ELF 逐字节不变」判据的前提。 +- **硬判据**:① 运行语义零变化(解释器 + ELF 两侧输出一致);② **ELF 逐字节不变**(`tests/suite/ptr_arith.cr` vs `/tmp/r1t4_base_bin`,sha `95084e7b…d475`)——若变,**停下上报**(说明纯度改动泄进了发射面);③ `.ccr` 的 EDG/NOD **预期会变** → 期望表重锁 + 新增**边集语义断言**(取代"字节相同");④ 全回归绿 + 自举 `corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42。 +- **判据重定(本批确立,写入 TODO)**:凡涉及 state 边/纯度的改动,判据 = 「语义零变化 + 边集语义断言 + 自举稳定(连续两次编译产物一致)」;旧的「.ccr 逐字节同旧版」不再适用。 +- 本语言无三元运算符;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`)。 + +--- + +## Task 1:真纯度计算(根因修复) + +**Files:** +- Modify: `src/compiler/checker.cr`(删除两处乐观写入;新增 `compute_purity`/`compute_all_purity`;在 `check_all()` 尾部调用) +- Modify: `src/compiler/monomorph.cr`(新实例继承源函数纯度) +- Modify: `src/compiler/globals.cr`(如需侧表:调用图邻接/迭代标记) + +**Interfaces:** +``` +// 保守纯度判定(不动点迭代;返回 1 = 可证纯,0 = 不纯/未知) +fn compute_purity(fi: int) -> int +fn compute_all_purity() // check_all() 尾部调用:全部函数一次算完 +// 自测辅助(供 type_selftest / 新测试断言) +fn fi_ispure_of(name: str_ni: int) -> int +``` + +**语义(按 `plans/2026-08-08-region-cfg.md:484`)**:纯 ⟺ ① 函数体无 store 族(`IR_STORE/STORE_FIELD/STORE_INDEX/STORE_INDEX_VAR`)、无 `unsafe`/volatile;② 无 IO/FFI/extern 效应(`IR_CALL_EXTERN` 出现即不纯);③ **传递闭包**内被调者全纯(`find_func(s3)` 解析;不可解析 ⇒ 不纯);④ 递归/SCC ⇒ 保守判不纯。 + +- [ ] **Step 1: 失败用例(红)**:新增自测(`type_selftest.cr` 或独立 `tests/selfhost/test_purity.py`) + - 正控:`fn pure_add(a:int,b:int)->int{return a+b;}` ⇒ `fi_ispure == 1` + - 负控①(store):`fn effect(){st:.,mut=0; st=1;}` ⇒ `0` + - 负控②(传递):`fn call_effect(){effect();}` ⇒ `0` + - 负控③(extern):含 `IR_CALL_EXTERN` 的函数 ⇒ `0` + - 负控④(不可解析):调用 runtime builtin 的函数 ⇒ `0` + - 负控⑤(递归):自递归函数 ⇒ `0`(保守) + - 负控⑥(泛型实例):`fn id[T](x:T)->T{return x;}` 的实例 ⇒ 与源函数同值(**今日恒 0**——本任务须修正为"继承源纯度") +- [ ] **Step 2: 确认真红**(现值为常量 1 ⇒ 负控①-⑥ 全挂) +- [ ] **Step 3: 实现** + - 写侧:`checker.cr:1107`/`:1142` 两处 `fi_set_ispure(...,1)` → 删除(改由 `compute_all_purity()` 统一写)。 + - `compute_all_purity()`:扫描每个函数的 IR 体(`g_ir_func_instr_start/instr_count`)取 opcode 集;对 `IR_CALL` 用 `find_func(s3)` 建调用边;`IR_CALL_EXTERN`/不可解析/递归 SCC ⇒ 不纯;从"全不纯"起点迭代**升为纯**至不动点(保守单调)。 + - **时点**:`check_all()` 尾部(全部声明与 IR 体就绪、`df_connect_state` 之前——注意 `collect_decls` 期不可用)。 + - `monomorph.cr:414-428`:`fi_set_ispure(new_fi, fi_ispure(func_ni))`。 +- [ ] **Step 4: 判据**:`selftest-types` + 新用例全绿;**ELF 逐字节不变**;`.ccr` 变化实测(预期变);回归抽样;自举 `cmp` IDENTICAL。 +- [ ] **Step 5: 提交**:`feat: 效应纯度修正 Task 1——真纯度计算(保守起点 + 调用图不动点 + SCC 保守)替换乐观常量;monomorph 继承源纯度` + +## Task 2:state 链分类表补全 + +**Files:** +- Modify: `src/compiler/dataflow.cr:160-177`(`df_connect_state` 分类表) + +- [ ] **Step 1: 补 `IR_CALL_EXTERN` 分支**(s1 = 符号名 ni;不可解析 ⇒ **恒入链**) +- [ ] **Step 2: 补其它效应 opcode**(侦查第五类):`IR_SPAWN`、`IR_YIELD`、`IR_HOTPATCH_ROUTE`、`IR_DYN_DISPATCH`(间接调用 = 效应,保守入链)——逐个核其载荷字段并加断言 +- [ ] **Step 3: 可解析调用**:维持 `fi_ispure(cfi)==0 ⇒ 入链`(Task 1 后该判据才有真实语义) +- [ ] **Step 4: 判据**:**实测复现侦查的验收例**——`fn effect(){st=1;}` 被 main 调两次 ⇒ main 的 state 链**必须出现两个 CALL**;`pure_add` 调用**不出现**;ELF 逐字节不变;`.ccr` 变化实测;回归绿 +- [ ] **Step 5: 提交**:`feat: 效应纯度修正 Task 2——state 链分类表补全(IR_CALL_EXTERN + spawn/yield/hotpatch/dyn_dispatch;外部与间接调用保守入链)` + +## Task 3:判据重定与期望重锁 + +**Files:** +- Modify: `tests/selfhost/test_ccr_v7.py`(`EXPECT_DATA/EXPECT_STATE` 重锁;新增边集语义断言) +- Modify: `TODO.md`(判据重定条目:ELD/EDG 类改动的判据 = 语义零变化 + 边集断言 + 自举稳定) + +- [ ] **Step 1: 重锁期望表**(`test_ccr_v7.py:84-104` 的 EXPECT_DATA(41)/EXPECT_STATE(13);fixture 含 `pure_add` 调用 ⇒ Task 1 后应保持"无该调用边") +- [ ] **Step 2: 新增边集语义断言(取代"字节同旧")**:① 可证纯调用 ⇒ **无** kind=1 入边;② 效应调用(store/extern/不可解析/间接) ⇒ **必有** kind=1 入边;③ 每函数链独立(跨函数不连)——三条各自带正/负控 +- [ ] **Step 3: 判据重定入 TODO**(含:产物变更清单 `.ccr` NOD/EDG/`--dump-objects`/`.cir` 缓存;「自举稳定」替代「与旧版逐字节同」) +- [ ] **Step 4: 提交**:`test: 效应纯度修正 Task 3——判据重定(边集语义断言取代 .ccr 字节同旧)+ test_ccr_v7 期望重锁 + TODO 判据条目` + +## Task 4:收官 + +- [ ] Step 1 全量回归(已跑集 + 全仓 38 套件) +- [ ] Step 2 **ELF 逐字节复验** + `.ccr` 变更实测报告 + **自举稳定**(连续两次编译产物一致) +- [ ] Step 3 自举链(`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42) +- [ ] Step 4 文档:TODO #25 家族更新(fi_ispure 修毕;**lazy use_count 时序缺陷保持独立**;`IR_DYN_DISPATCH` 间接调用覆盖);spec(S-C/S-E/S-B5)里"纯度事实不可靠"的措辞改为"已于 修复",切片门禁相应恢复 +- [ ] Step 5 提交(路径限定) + +--- + +## 自检记录 + +- **根因 vs 止血**:Task 1 为根因(按 `plans/2026-08-08-region-cfg.md:484` 实现);Task 2 的表补全属同一根因面(分类表漏 opcode);**未采用**"只补 extern"的窄止血(不解决 print/chan_send 等可解析效应丢失)。 +- **lazy 解耦(关键决策)**:本批**不动** `ir_gen.cr:1590` 的 lazy 判定 ⇒ 保 ELF 逐字节不变(否则每个 call 的 11 字节 thunk 会消失、ELF 与自举链全变)。lazy 自身的 use_count 时序缺陷**保持已登记状态**,留独立批。 +- **占位符扫描**:无 TBD;`compute_purity` 的算法要点(保守起点/不动点/SCC/不可解析)已给足。 +- **风险**:① `find_func` O(F²)(大项目下变慢——可先测编译耗时,超阈值再换哈希);② 递归 SCC 保守判不纯 = 宁可多入链(语义安全方向);③ `.ccr` 字节变化属**预期**,判据已按 §7 重定。 diff --git a/docs/superpowers/plans/2026-09-11-r2-p2b-iface-ops.md b/docs/superpowers/plans/2026-09-11-r2-p2b-iface-ops.md new file mode 100644 index 00000000..2b533fa4 --- /dev/null +++ b/docs/superpowers/plans/2026-09-11-r2-p2b-iface-ops.md @@ -0,0 +1,446 @@ +# R2 P2(逐点替换 · 批 b)实施计划:`infer_expr` 公理区 → `iface_ops` 查表接线 + `res_type_node` 双份 TY→TI 映射合一 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `infer_expr` 的**内联类型公理区**(决定「操作是否许可 / 结果是什么类型 / 字面量定型」的硬编码特判)替换为**本质条目表 + 操作许可位集**的查表接线(spec §2),并把 `res_type_node`/`res_call_type` 的**双份 TY→TI 映射**合成单表(spec §4 步 3);同时交付 P2 的查询 API `iface_*`(spec §2.5)——它是 P3/P4/P5 与下游 S-A/S-B/S-C/S-D 切片共同等待的「统一入口」。 + +**Architecture:** 三块,逐块独立可验收: +1. **注册表(新建 `iface_registry.cr`)**——13 条本质条目(8 原生 + product/sequence/ref/ptr/named)+ 操作许可位集 + 字面量定型码 + `iface_*` 查询 API。表是**静态数据**(不新增 `g_types` 行 ⇒ 不动 `.ccr` 类型段)。 +2. **接线(`checker.cr` 三个面)**——① 字面量定型 5 处;② 操作许可(二元 11 条 / 一元 4 条 / 条件 3 条);③ 容器面(索引 5 条 / 字段 4 条 / 转换 1 条 / dyn)。 +3. **两表合一**——`res_type_node` 与 `res_call_type` 的基型分支合为 `ty_code_to_ti(ty)`;**顺带**把同族 6 处 TY→TI 内联链与「checker 行号 → 原子类」映射(现散在桥接层 `sh_base_ak`/`sh_native_ak`)也收敛到同一处,避免 P2b 自己制造新重复。 + +**接线口径 = 保语义(零行为变化)**:本批**只换寻址方式,不换判定结果**——表的每一格都是从现状代码**逐格转录**而来,且每格注上现状 `file:line`。任何「旧接受 → 新拒绝/新放宽」都**不在本批**(spec §4 裁决 6 的收紧处置属 P3 能力落地批,见 Global Constraints 第 4 条)。 + +**Tech Stack:** Core 自举栈;P0 引擎(`type_terms.cr`/`type_engine.cr` 的 `AK_*` + `tt_atom` + 判定 API);P1/P2a 桥接与影子通道(`ty_shadow.cr`:`sh_native_ak`/`sh_base_ak`/`sh_map_*`/`--type-shadow`/分类计数);P2a 判定替换面(`type_equal_engine`/`type_equal_legacy`/`type_compat_strict`/`array_len_constraint_ok`);检查器 `checker.cr`;自测通道 `type_selftest.cr`(`corec selftest-types`)。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §2(双轴注册表:本质轴字段表 / §2.4 衔接 / §2.5 查询 API)+ §4 步 3(逐点替换:`infer_expr` 公理区 → `iface_ops`;`res_type_node` 两表合一)+ §9 P2 行 (b) 部分。方向设计:`docs/superpowers/specs/2026-08-30-type-system-direction-design.md` 定案 2/3(int/dex/string/bool/dyn = **原生接口条目**(规则内建、用户不可实现);一张 interface 注册表;检查器删类型特判、统一查表验证;**发射层原语映射不动**)。 +**P2a 交接**:`docs/superpowers/specs/2026-09-10-type-shadow-findings.md` §11(P2a 后复跑:26989/26989 agree、站点 1/2/4/6 零命中、N 面须行为探针)+ TODO #24(P5 继承项 / 未覆盖面)。 + +**范围**:本批 = **P2b**(注册表建层 + 公理区三面接线 + 双份 TY→TI 合一 + 收官)。 +- **不做**(明记,避免范围蔓延):① 收紧任何现状宽松面(= P3);② 横切轴(序列/可索引/可迭代接口)与用户轴(`impl`/`interface` 语义重定义、mangling 废弃)= P3(spec §2.2/§2.3);③ `iface_size/iface_align`(尺寸/对齐 = 映射层/hw-map 领域,spec §2.1 明标「非语义」,且 `ir_gen.cr:279/462-463/514-515` 的 `type_size`/`type_align` 是发射面消费点);④ `iface_satisfies`(三类统一满足判定——**零调用者**,P3 落地 impl 语义时才有对象);⑤ 发射层:`ir_gen.cr` 的算子降级/字符串比较降级(`ir_gen.cr:904` 起)与 `type_size/type_align` **一律不动**(方向设计定案 3:`+` → addq 是机器知识,归 hw-map 编码层);⑥ `AK_SUM`/`AK_FN` 条目(checker 侧无对应原子:enum = `TYP_NAMED`,见侦查 §4.1)。 + +--- + +## Global Constraints + +- **jj only(含只读与复合命令一律不用 `git`)**;**提交必须路径限定**(多 agent 并行下全员含协调者);所有命令 `nice -n 19`;判据前 `clean-cache` 且 **cwd = 仓库根**。 +- **行为零变化面(P2b 的硬判据)**:接线前后 **ELF 产物逐字节相同**(`tests/suite/ptr_arith.cr` vs 基线,sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`);影子开/关两态亦逐字节相同。 +- **`.ccr` 面**:注册表是**静态数据表,不 alloc `g_types` 行、不改既有行号**(先例:`init_builtins` 的 `g_rt_builtin_*` 旁表不占类型行)⇒ `.ccr` **预期逐字节不变**(强于 P2a 的「允许变须实测」)。**必须实测并报告**(冷缓存);若变 → **停下上报**(说明类型信息泄进了编码层/行号被扰动)。 +- **保语义(本批的语义口径,硬性)**:表的每一格 = 现状代码的**逐格转录**,格注必须给出对应现状 `file:line`。现状的**宽松面**(侦查 §2 清单:比较不校验操作数、算术门「任一侧数值即可」、一元透传、索引类型不校验、字段落空静默、转换无校验)**原样保留为表的「全许可」格**——它们由本批**登记为 P3 收紧面**,不得在接线时顺手收紧/放宽(spec §4 裁决 6:收紧 = 改源码 + 逐处记录 + 语义争议停下上报)。 +- **AK/TI 下标不 1:1(血泪,硬性)**:`AK_STRING=2` 而 `TI_STR=3`、`AK_BOOL=3` 而 `TI_BOOL=2`(`ty_shadow.cr:18-26`;P1 事故:照下标直传会把 bool↔string 静默错标且两侧同错自洽)。**注册表内任何「原子类 ↔ 类型行」的对应都必须按语义逐项分派,禁止数值直传**;守卫 = `bridge.str_ak`/`bridge.bool_ak`(`type_selftest.cr:208-209`)+ 本批新增逐条目用例。 +- **表全局放 `globals.cr`**(bootstrap 名字解析对**变量**按声明序、跨文件前向引用不成立——`globals.cr:290-292` 的 `g_purity_inst` 先例);**函数**的跨文件前向引用成立(既有事实:`checker.cr` 调后置文件 `ty_shadow.cr`/`type_engine.cr` 的函数)。 +- **本语言无三元运算符、无移位运算符**;取模须非负;位集构造照既有乘 2 循环(`checker.cr:1830/1841/1853-1854` 的 `dyn_set_type` 惯例);键比较不得依赖 i64 回绕;noclobber(用 `>|`);比较 `.ccr` 前必须 `clean-cache`(TODO #5 家族:冷/热缓存态分歧)。 +- **文件永久不允许还原;不得绕过。** +- **CI 挂点**:新测试须挂 `src/ci/run.sh` 的 `selfhost-tests`(该 job 是「已跑集」的唯一真源;不挂 = 不进判据)。 + +--- + +## 侦查底座(2026-09-11 实测;行号 = 本计划起草时点工作副本,**非 spec 时点**) + +> **行号口径警告**:spec §11 的行号指修订 `a9d991908910`(`checker.cr` **2483 行**,已用 `jj file show` 核对)。工作副本现为 **3376 行**(P2a/P0/#25/#28/#29/纯度批等已落),**偏移逐段不同**(+521 于 `infer_expr` 头部、+648 于 `EXPR_AS`)。本计划**一律用实测行号**,需要跨文档对读处另注 spec 时点行号。 + +### 0. 交付物现状:`iface_*` **不存在**(硬前置) + +`grep -rn "iface_kind_of\|iface_ops\|iface_satisfies\|iface_size\|iface_of_term" src/` = **零命中**(仅 `docs/` 出现;提及该 API 的 spec:设计 spec §2.5、`2026-09-11-semantic-safety-roadmap.md:122`、`2026-09-11-policy-injection-design.md:365`、`2026-09-11-policy-information-flow-design.md:316`、`2026-09-11-semantic-safety-obligations-design.md:497`)。⇒ **P2b 的首个交付 = 该 API 本身**(Task 1),不是「已有 API 的接线」。spec §9 P0 行已预先登记此项归属:「查询 API `iface_*` 属 P2 接线面,P0 只出判定 API」。 + +### 1. 公理区全量枚举(`infer_expr` = `checker.cr:1888-3061`,1174 行) + +口径:**一条 = 一处决定「操作是否许可」或「结果是什么类型」的硬编码判定**。家族 × 条数 = **66 条**(下表逐条;= 5+8+1+1+2+4+3+5+5+5+1+4+4+15+3)。量度代理(同口径的可复核计数):`return TI_<非 unit>` **49** 处 · `== TI_*`/`!= TI_*` **28** 处 · `get_type_kind(...)` 调用 **20 点/21 次** · `check_error` **36** 处 · `TYP_*` 提及面 PTR 9 / GENERIC_APPLY 6 / ARRAY 5 / TUPLE 3 / REF 3 / NAMED 3 / GENERIC_PARAM 3 / SLICE 2。 + +| # | 家族 | 现状行号 | 条数 | 内容(现状语义) | +|---|---|---|---|---| +| 1 | **字面量定型** | `:1892` `:1894` `:1895` `:1896` `:1897` | 5 | `EXPR_INT→TI_INT`/`EXPR_DEX→TI_DEX`/`EXPR_STRING→TI_STR`/`EXPR_BOOL→TI_BOOL`/`EXPR_CHAR→TI_CHAR`(spec 时点 `:1371-1376`) | +| 2 | **二元算术/指针/拼接** | `:1935`(组门)`:1937`(串拼接)`:1939-1941`(`*T±int→*T`)`:1942-1944`(`int+*T→*T`)`:1946-1948`(`*T−*T→int`)`:1950-1952`(合法性门)`:1953-1954`(dex 支配→DEX 否则 INT)`:1965`(兜底 INT) | 8 | 见侦查 §2 的宽松面 ①② | +| 3 | **比较** | `:1956-1958` | 1 | 6 个比较 op **恒返回 `TI_BOOL`、不校验操作数** | +| 4 | **逻辑** | `:1959-1964` | 1 | 两侧各须 `bool\|int`(**每侧独立**)→ `TI_BOOL` | +| 5 | **赋值** | `:1922-1932`(`OP_ASSIGN`)`:2638-2660`(`EXPR_ASSIGN`) | 2 | 站点 6(无生产点,TODO #17)/ 站点 8 | +| 6 | **一元** | `:1970-1972`(NEG/NOT 透传)`:1973-1993`(`UOP_REF`→`TYP_PTR`)`:1994-2006`(`UOP_DEREF` 三种分支)`:2007`(兜底透传) | 4 | `&x` 产 `TYP_PTR` 而类型位 `&T` 产 `TYP_REF` = **双表示并存**(spec §11 同记) | +| 7 | **条件/区间** | `:2265`(if 须 bool\|int)`:2376`(while 须 bool)`:2402-2403`(range 两端须 int) | 3 | | +| 8 | **索引** | `:2586-2603`(range→`TYP_SLICE` + F11 界)`:2605-2615`(ARRAY→元素 + F2 越界)`:2616-2618`(SLICE→元素)`:2619-2633`(STR→`TI_INT` + 字面量越界)`:2634`(`EC_TK_INDEX` 拒绝) | 5 | `idx_ti`(`:2583`)**推断后从未被使用** ⇒ 索引**类型**不校验 | +| 9 | **字段** | `:2513-2517`(REF 自动解引用)`:2519-2521`(APPLY 解包基型)`:2522-2563`(NAMED→struct 字段表 + 泛型实参代换 `:2538-2555`)`:2566-2577`(TUPLE `.N`)`:2578`(**落空 → `TI_UNIT`,无诊断**) | 5 | | +| 10 | **方法** | `:2022-2062`(module 限定调用)`:2064-2082`(dyn 方法)`:2083-2101`(NAMED→方法表 `g_methods`)`:2103-2146`(GENERIC_PARAM→约束 iface + **mangled 字符串**)`:2147-2160`(实参推断 + 返回) | 5 | mangling 废弃 = P3(spec §2.3) | +| 11 | **转换** | `:2892-2908` | 1 | `as` 仅处理 `TYP_PTR` 的 asp 传递(`:2897-2906`),其余**无合法性校验** | +| 12 | **dyn 面** | `:1825-1834`(写集)`:1836-1845`(读集)`:1861-1884`(方法校验)`:2066-2082` `:2643-2651` `:2473-2477`(三消费点) | 4 | 位图 **64 上限**(`:1829`/`:1840`/`:1868` 的 `ti < 64` 钳位;spec §3.3 登记「须解除或显式登记」) | +| 13 | **内建/外部调用定型** | `:2173-2177`(`syscall3/4`→INT,按名字)`:2195-2203`(SO 返回码→TI_*)`:2218-2225`(`g_rt_builtin_ret_types` 表)`:2238`(未知外部→`TI_INT`) | 4 | `:2195-2203` 是**第三份 TY 式→TI 映射**(`ret_code2 % 100`,`:2196`) | +| 14 | **`@builtin` 定型** | `:2946-3057` | 15 | `str_eq(name, …)` 15 处:`sizeOf`/`addr`/`alignOf`/`fields`/`hasField`/`field`/`typeInfo`/`raw_int`/`comptime`/`inline`/`no_bounds_check`/`fast`/`unroll`/`section`/`hotpatch` + 兜底报错 | +| 15 | **其它结构特判** | `:2359-2361`(await 数组→元素)`:2871-2888`(try:**按名字字符串**判 `Option`/`Result`)`:2341-2343`(go range→`TYP_ARRAY`) | 3 | `:2879` 的 `base_name == "Option"` 是名字特判(spec §5.4「不再依赖内建 Option 枚举名」= P3) | + +### 2. 现状「宽松面」清单(**保真/收紧分界**;本批逐格转录,P3 才动) + +逐条实测(读码 + 推演,**未**逐条跑探针——见 §6.2 待补),这些是接线时**最容易被"顺手修正"**的格子: + +1. **比较不校验操作数**(`:1956-1958`):`(1, "a") == …` / 结构体 `==` / 数组 `==` 一律收敛为 `TI_BOOL` 无诊断。 +2. **算术合法性门是「任一侧」语义**(`:1950`):`lt != INT && lt != DEX && rt != INT && rt != DEX` ⇒ **两侧皆非数值才报错**。故 `1 + [int;3]`(lt=int)**静默为 INT**;`"a" * 2`(rt=int)**静默为 INT**;`"a" * "b"` 才报 `EC_TB_ADD`。 +3. **串拼接优先且单向**(`:1937`):`op == OP_ADD && (lt == TI_STR || rt == TI_STR) → TI_STR` ⇒ `1 + "a"` 得 string。 +4. **一元透传**(`:1970-1972`):`-x`/`!x` 直接返回操作数类型,**不校验**(`-"a"` → string)。 +5. **解引用兜底透传**(`:2004-2005`):非 REF/PTR/GPARAM 的操作数**原样返回**。 +6. **索引类型不校验**(`:2583` 的 `idx_ti` 无消费者)。 +7. **字段落空静默**(`:2578`):非 NAMED/TUPLE 的 `.f` → `TI_UNIT`,**无诊断**。 +8. **转换无校验**(`:2904-2907`):除 PTR 的 asp 外,`as` 是恒等透传。 +9. **dyn = ⊤ 近似**(`:2066`/`:2643`/`:2473` + 桥接 `ty_shadow.cr:212-216`):位图是收窄而 `AK_DYN` 在引擎 = ⊤。 + +**这份清单即「收紧清单的预备队」**:本批报告须逐条给出「现状格 → 表中格」,并声明「均按现状落格,无一条被收紧」。 + +### 3. 双份 TY→TI 映射 + 同族(P2b 第二交付物) + +**spec 所指两表**(已用 `jj file show -r a9d991908910` 核对 spec 时点行号 → 现址): + +| 表 | spec 时点 | 现址 | 内容差异 | +|---|---|---|---| +| `res_type_node` 基型分支 | `:367-380` | **`checker.cr:732-744`** | 8 行:`TY_INT/DEX/BOOL/STRING/UNIT/NEVER/CHAR` + `tv == TI_DYN` | +| `res_call_type` 基型分支 | `:886-893` | **`checker.cr:1382-1392`** | 7 行:**无 `TY_NEVER`**;`TY_CHAR` 位置不同;同样有 `tv == TI_DYN` | + +- **两表唯一的语义差 = `TY_NEVER` 单元格**(`:740` 有 / `:1388-1391` 缺,缺则落 `TI_UNIT`)。`never` **是**可解析的类型位(`parser.cr:94` `else if lex == "never" { … TY_NEVER … }`)⇒ 该差异**并非文法不可达**,合一必须显式处置(Task 6 Step 2 的探针)。 +- `tv == TI_DYN`(`:742`/`:1390`)比的是 **TY 值域**而常量取自 **TI 命名**(值 7)——这正是 spec §2.4 记的**纯数字撞车** `TY_GENERIC_PARAM=7 == TI_DYN=7` 的现场;下行字面量节点不受影响(parser 从不产 `type_val=7`),但合一表时**必须原样保留该格语义**(枚举 §2.4 的「按命名空间分家」= P4/P5 面,本批不改)。 +- **同族另有 6 处 TY→TI 内联链**(不在 spec 两表口径内,但同属「同一映射抄了多份」的病;Task 6 一并合并,逐处给出等价判据): + `:1055-1061`(hotpatch 返回)|`:1080-1085`(hotpatch 首版返回)|`:1133-1140`(extern 返回)|`:1671-1675`(`check_func` 形参;**缺 UNIT/NEVER**,落 `TI_UNIT`)|`:1724-1730`(`check_func` 返回)|`:2129-2134`(iface 返回;**`iface_ret2` 与 `TY_*` 比较而值域是 TI_*,靠 `TY_INT==TI_INT==0 … TY_CHAR==TI_CHAR==6` 的数值撞车成立**——合一时须显式化)。 + 另有 2 处不同性质、**不入合并**仅登记:`:241-248`(`init_types` 位置公理,8 行顺序分配)与 `:859-865`(`get_type_name` **反向** TY→名字)。 +- 实测计数:`checker.cr` 内含 `TY_*` 常量的代码行 **61** 行(含注释共 65 次提及)。 +- **spec 数字勘误(诚实声明)**:spec §11 记「`== TI_*` 比较 **72 处**」——本计划四种口径均**未复现**(spec 时点修订实测:`checker.cr` 含 TI_ 判等的行 19 / `== TI_` 10 / 与 `ir_gen.cr` 合计 75 行;工作副本 `checker.cr` 19 行、`ir_gen.cr` 56 行)。本计划一律引用上述自测口径,**不沿用 72**。 + +### 4. 可贴合的既有结构(新表必须照抄的形态) + +1. **checker 侧原子宇宙 = 13 类**(不是引擎的 14):`TYP_BASE`→8 原生(经 `TY_*` 码)、`TYP_NAMED`/**`TYP_GENERIC_PARAM`**/**`TYP_GENERIC_APPLY`**→`AK_NAMED`、`TYP_ARRAY`/`TYP_SLICE`→`AK_SEQUENCE`、`TYP_REF`→`AK_REF`、`TYP_PTR`→`AK_PTR`、`TYP_TUPLE`→`AK_PRODUCT`、`TYP_DYN`→`AK_DYN`。**无 `AK_SUM`/`AK_FN` 对应**(enum 类型 = `TYP_NAMED` 行,`checker.cr:982`;checker 无函数类型行)。 +2. **「行号 → 原子类」的既有唯一实现 = 桥接层** `sh_native_ak`(`ty_shadow.cr:67-71`)+ `sh_base_ak`(`:75-88`)——**这就是 `iface_kind_of` 的原型**(含 `TY_DEX_S→AK_DEX`、`TY_GENERIC_PARAM→AK_NAMED` 两条已裁决格)。⇒ Task 2 的合一不是新建,是**单源化**(否则 P2b 会亲手造出第二份映射 = 本批要治的病)。 +3. **「名字 → 结果类型」的表驱动先例** = `init_builtins`/`bi_add`(`checker.cr:261-303`,`g_rt_builtin_names` + `g_rt_builtin_ret_types` 两条平行 i64 缓冲)——本质条目表的形态参照(**扁平 i64 缓冲 + 偏移常量**,非对象树)。 +4. **类型行 24B/条**(`g_types`,`checker.cr:9-16`);引擎原子项 48B/条(`ESZ_TYPE_TERM`,`type_terms.cr:22-24`);桥接缓存 16B/条(`g_shadow_map`)。新条目表取 **40B/条 × 5 字段**(下 Task 1)。 +5. **iface 既有表(P3 面,本批不动)**:`g_ifaces` 1432B/条 ×16 方法×88B(`dyn_arr.cr:143-149`)+ `g_impl_for` 16B(`:830-833`);查询 `find_iface`(`checker.cr:844`)/`type_has_method`(`:873`,**名字拼接**)/`check_iface`(`:881`)/`check_impl_for`(`:1746`)。 + +### 5. 判据底座(既有,复用不另发明) + +| 判据 | 现值/命令 | 来源 | +|---|---|---| +| `selftest-types` | **95/95 PASS**(本计划实测,rc=0) | `nice -n 19 ./build/corec selftest-types`;下限守卫 `tests/selfhost/test_type_engine.py:20` `MIN_CASES = 32` | +| ELF canary | sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(**本计划未复测**,取自近期提交链反复实测值) | `clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/x` → `sha256sum` | +| 影子对拍 | **71 文件 / 26989 判定 / agree 26989 / `old_stricter` 0 / `old_looser` 0 / `unknown` 0 / `replace_*` 0**(**本计划未复跑**,取自 findings §11 表) | `--type-shadow` + `grep '^\[type-shadow\] '` | +| `.ccr` 结构 | `tests/selfhost/test_ccr_v7.py` 27 例 | 见 `src/ci/run.sh:72` | +| 自举链 | `src/ci/run.sh full-bootstrap`(corec→corec2→corec3 `cmp` IDENTICAL + 两段 `error[N06]=0` + `--help` rc=1) | `src/ci/run.sh:90-101` | +| 冒烟 | `corec run 'fn main()->int{return 42;}'` rc=42 | 见 spec §4「每步判据」行 | +| 套件 | 38(7 bootstrap + 31 selfhost,TODO #27 收官实测)+ `tests/suite` 20 语料 | `src/ci/run.sh` | +| **判据重定(TODO #26)不适用** | 本批**不触** state 边/纯度(`dataflow.cr`/`purity_op_effect`/`ccr_io` 的 EDG/NOD 零改动)⇒ **不套用**边集断言;仍适用「ELF 逐字节 + `.ccr` 实测 + 自举稳定」 | TODO #26 适用面=state 链/纯度类改动 | + +### 6. 待实现者实测确认的两项(本计划**未**测,不得当已知) + +1. **`never` 在「调用位点类型节点」的可达性**(Task 6 Step 2 探针):`fn f[T](a: T, b: never)` 与嵌套形 `[never;3]` / `*never` / `G[never]` 是否让 `res_call_type` 走到 `TY_NEVER` 格。可达 ⇒ 合一必须保留该调用点现状(`TI_UNIT`)并登记;不可达 ⇒ 合一可直接统一语义(仍须登记)。 +2. **现状宽松面的行为实证**(§2 九条):本计划对第 2/6/7 条只做了读码推演(`1 + [int;3]` → INT、`arr[true]` 无诊断、`p.f` 落空静默)——**未跑探针**。Task 4/5 的红态用例须先跑出这些现状事实(**探针先行**,防「以为保语义其实改了」)。 + +--- + +## Task 1:建层——本质条目表 + `iface_*` 查询 API + 用例表(零消费者,零行为变化) + +**Files:** +- 新建:`src/compiler/iface_registry.cr`(表 + 访问器 + API;**只含函数与常量**——表全局须放 `globals.cr`) +- Modify: `src/compiler/globals.cr`(`g_iface_entries` / `g_iface_entry_count` / `g_iface_registry_ok`) +- Modify: `src/compiler/type_selftest.cr`(`iface.*` 用例段) +- Modify: `src/compiler/_import.cr`(`import iface_registry`,插在 `type_engine` 之后 `ty_shadow` 之前) +- Modify: `build_selfhost_native.py`(**corec 清单** `:307-315` 区、**corelsp 清单** `:392-394` 区——插在 `type_engine.cr` 之后、`ty_shadow.cr` **之前**) +- Modify: `tests/selfhost/test_compile.py`(`concat_sources()` 清单 `:25-27` 区同步;清单漂移 = 该测试 resolver 报 `Undefined name`) + +**Interfaces:** +```core +// ── 条目布局(40B/条 × 5 字段;与 g_types(24B)/ESZ_TYPE_TERM(48B) 同族:扁平 i64 缓冲)── +// {ak, ti_row, name_ni, lit_code, ops} +// ak = 原子类(AK_*;type_engine.cr:19-22) +// ti_row = 该原子的**规范 checker 类型行**(8 原生 = TI_INT..TI_DYN 常量;结构/命名 = -1) +// name_ni = 名字 ni(str_intern;自测/诊断显示用) +// lit_code = **字面量定型**:AST 字面量 kind(EXPR_INT/EXPR_DEX/EXPR_STRING/EXPR_BOOL/EXPR_CHAR) +// → 本条目(查表入口 = `iface_lit_ti`/`iface_lit_ak` 的**字典**,本字段为其中一项);-1 = 无字面量 +// ops = 操作许可位集(bit(OP_*)/bit(UOP_*+20)/bit(IP_*);见 Task 4 的位下标约定) +ESZ_IFACE_ENTRY : int = 40; +OFF_IE_AK : int = 0; OFF_IE_TI : int = 8; OFF_IE_NAME : int = 16; +OFF_IE_LIT : int = 24; OFF_IE_OPS : int = 32; + +fn iface_registry_init() // 幂等;由 init_types() 调用(尾部,原生 9 行 alloc 之后;表内容 = 常量,不缓存行号) +fn iface_count() -> int // 条目数(恒 13;自测断言用) +fn iface_entry(ak: int) -> int // 原子类 → 条目行号(-1 = 无此原子) +fn iface_ops(ak: int) -> int // 操作许可位集(**spec §2.5 的签名**;-1/未知原子 → 全 0 = 无许可) +fn iface_permits(ak: int, op: int) -> int // 1/0:位测试(op 已含 UOP/IP 偏置) +fn iface_lit_ti(lit_kind: int) -> int // 字面量 AST kind → TI_*(-1 = 非字面量 kind) +fn iface_lit_ak(lit_kind: int) -> int // 同上 → AK_*(交叉断言/引擎侧用) +fn iface_kind_of(ti: int) -> int // checker 行号 → AK_*(-1 = 越界/负)——Task 2 起为**唯一**实现 +fn iface_by_ty_code(ty: int) -> int // TY_* 码 → AK_*(= 现 sh_base_ak 的语义;-1 = 无对应) +fn iface_ti_of(ak: int) -> int // 原子类 → 规范行(8 原生;其余 -1) +fn iface_of_term(t: int) -> int // 类型项 → AK_*(单一原子;非单一 → -1;⊤ₖ → 其 k) +``` + +**表数据(13 条,静态;每格注现状 `file:line`)**:8 原生条目 + `AK_PRODUCT`/`AK_SEQUENCE`/`AK_REF`/`AK_PTR`/`AK_NAMED`。`ti_row` 取 `TI_*` **常量**(不是位置推演——P1 M1 教训:勿依赖 init_types 行号序);`AK↔TI` 的对应**逐项写死**(`AK_INT↔TI_INT`、`AK_DEX↔TI_DEX`、**`AK_STRING↔TI_STR`**、**`AK_BOOL↔TI_BOOL`**、`AK_UNIT↔TI_UNIT`、`AK_NEVER↔TI_NEVER`、`AK_CHAR↔TI_CHAR`、`AK_DYN↔TI_DYN`——bool/string 必错位书写以示下标不 1:1)。 + +- [ ] **Step 1: 守门用例(`type_selftest.cr`;红)** + +```core + // --- P2b Task 1:本质条目表 + iface_* 查询 API --- + total = total + 1; fails = fails + ts_check("iface.count", iface_count(), 13); + // 逐原生条目:ak/ti 对 + AK↔TI 下标**不 1:1** 的显式守卫(bool/string 互换) + total = total + 1; fails = fails + ts_check("iface.int_ti", iface_ti_of(AK_INT), TI_INT); + total = total + 1; fails = fails + ts_check("iface.str_ti", iface_ti_of(AK_STRING), TI_STR); + total = total + 1; fails = fails + ts_check("iface.bool_ti", iface_ti_of(AK_BOOL), TI_BOOL); + total = total + 1; fails = fails + ts_check("iface.dex_ti", iface_ti_of(AK_DEX), TI_DEX); + total = total + 1; fails = fails + ts_check("iface.dyn_ti", iface_ti_of(AK_DYN), TI_DYN); + // 行号 → 原子类:8 原生经**类型表本体**(kind == TYP_BASE && data == TY_*)判,不按行号猜 + total = total + 1; fails = fails + ts_check("iface.kind_int", iface_kind_of(TI_INT), AK_INT); + total = total + 1; fails = fails + ts_check("iface.kind_str", iface_kind_of(TI_STR), AK_STRING); + total = total + 1; fails = fails + ts_check("iface.kind_bool", iface_kind_of(TI_BOOL), AK_BOOL); + total = total + 1; fails = fails + ts_check("iface.kind_dyn", iface_kind_of(TI_DYN), AK_DYN); + total = total + 1; fails = fails + ts_check("iface.kind_oob", iface_kind_of(g_type_count + 7), -1); + total = total + 1; fails = fails + ts_check("iface.kind_neg", iface_kind_of(-1), -1); + // 字面量定型(**当前实现 = :1892-1897 的内联 if 链**,本表为唯一真源后仍须逐 kind 对拍) + total = total + 1; fails = fails + ts_check("iface.lit_int", iface_lit_ti(EXPR_INT), TI_INT); + total = total + 1; fails = fails + ts_check("iface.lit_dex", iface_lit_ti(EXPR_DEX), TI_DEX); + total = total + 1; fails = fails + ts_check("iface.lit_str", iface_lit_ti(EXPR_STRING), TI_STR); + total = total + 1; fails = fails + ts_check("iface.lit_bool", iface_lit_ti(EXPR_BOOL), TI_BOOL); + total = total + 1; fails = fails + ts_check("iface.lit_char", iface_lit_ti(EXPR_CHAR), TI_CHAR); + total = total + 1; fails = fails + ts_check("iface.lit_none", iface_lit_ti(EXPR_IDENT), -1); + // 未知原子 → 空许可集(不得给「看起来有许可」的位) + total = total + 1; fails = fails + ts_check("iface.ops_unknown", iface_ops(-1), 0); + total = total + 1; fails = fails + ts_check("iface.ops_unknown_ak", iface_ops(9999), 0); +``` + +- [ ] **Step 2: 运行确认红**(`iface_*` 未定义 → 构建失败 / 用例挂;`nice -n 19 python3 build_selfhost_native.py` 期望解析期报错) + +- [ ] **Step 3: 实现**(`iface_registry.cr` + `globals.cr` + 清单一/二/三处) + - 表用**两条平行 i64 缓冲**(照 `bi_add` 先例)还是单 40B 缓冲?**取单缓冲**(40B/条,`alloc(13 * 40)`,`w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_*)`)——理由是「13 条 × 5 字段」有 4 个异构字段,平行表会产生四份偏移表(`bi_add` 式只有「名字/返回型」两列才划算)。 + - `iface_kind_of(ti)`:`k := get_type_kind(ti)`;`k == TYP_BASE` → `iface_by_ty_code(get_type_data(ti))`;`k == TYP_DYN` → `AK_DYN`;`TYP_NAMED|TYP_GENERIC_PARAM|TYP_GENERIC_APPLY` → `AK_NAMED`;`TYP_ARRAY|TYP_SLICE` → `AK_SEQUENCE`;`TYP_REF` → `AK_REF`;`TYP_PTR` → `AK_PTR`;`TYP_TUPLE` → `AK_PRODUCT`;`k < 0` → `-1`。**本步先在本文件内实现 `iface_by_ty_code`(= `sh_base_ak` 的语义,含 `TY_DEX_S→AK_DEX`、`TY_GENERIC_PARAM→AK_NAMED` 两条已裁决格);Task 2 再把桥接层改成委托它。** + - `iface_lit_ti/lit_ak`:按条目表的 `lit_code` 反向扫(13 条线性扫,AST kind 面只有 5 个命中项)。 + - `iface_of_term(t)`:`tt_tag(t) == TT_ATOM` → `tt_a(t)`;`tt_tag(t) == TT_TOP_K` → `tt_a(t)`;其余(union/inter/not/bot/top/mu/var/nil/cons)→ `-1`。 + - `iface_registry_init()`:由 `init_types()` 调用(**尾部**,`:253` 原生 9 行 alloc 之后——表要读 `get_type_kind`);幂等(`g_iface_registry_ok`);照 `named_dedup_reset()`(`checker.cr:233`)/`sh_map_reset()`(`:237`)的重置先例挂同处。 + - `ops` 位集本 Task 先落**空集**(全 0),Task 4/5 逐格填——**空集期间不得有消费者**(本 Task 零消费者)。 + +- [ ] **Step 4: 判据** +```bash +nice -n 19 python3 build_selfhost_native.py +nice -n 19 ./build/corec selftest-types # 现值 95/95(本计划实测)→ 本 Task 加 20 例 ⇒ 期望 115/115(实现者实测补面,计数以实测为准) +nice -n 19 ./build/corec clean-cache +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/p2b_t1_bin +sha256sum /tmp/p2b_t1_bin # 必须 == 95084e7b…d475 +# .ccr 面(预期逐字节不变,须实测报告): +nice -n 19 ./build/corec clean-cache && nice -n 19 ./build/corec ccr tests/suite/ptr_arith.cr -o /tmp/t1.ccr +# 与基线比 sha 并记录(基线 = 本批**第一个提交前**用同一构造路径 + clean-cache 取;若未取, +# 则以「同源两次构建产出一致」替代并在报告**显式记录该替代**——照 P0 计划先例) +nice -n 19 python3 tests/selfhost/test_ccr_v7.py # 结构判据全绿 +``` + - `iface.count != 13` 或 `iface_*.ti` 对不上 → **停下上报**(AK/TI 下标事故的现场)。 +- [ ] **Step 5: 提交**(路径限定):`feat: R2 P2b Task 1——本质条目表 + iface_* 查询 API(13 条原子条目:8 原生 + product/sequence/ref/ptr/named;AK↔TI 逐项分派不按下标直传)+ selftest 用例;零消费者零行为变化` + +--- + +## Task 2:`iface_kind_of` 单源化——桥接分派(`sh_native_ak`/`sh_base_ak`)与注册表合一 + +**为什么在本批做**:`iface_kind_of` 与 `sh_*_ak` 是**同一个函数的两份实现**(侦查 §4.2)。不合一 = P2b 亲手制造「同一映射两份」,正是本批要治的病(先例:P2a 的 F1 就是「同名类型多行」的同族病)。 + +**Files:** Modify: `src/compiler/ty_shadow.cr`(`:67-88` 三函数改为委托)、`src/compiler/type_selftest.cr`(对拍用例) + +**Interfaces:** +```core +// ty_shadow.cr 保留原签名(影子证据链的可比性依赖调用点不变),实现改委托: +fn sh_native_ak(ti: int) -> int { + if ti < 0 { return -1; } + if get_type_kind(ti) != TYP_BASE { return -1; } // **原门保留**(见下) + return iface_by_ty_code(get_type_data(ti)); +} +fn sh_base_ak(ty: int) -> int { return iface_by_ty_code(ty); } +``` +(**`sh_native_ak` 不可直接换成 `iface_kind_of`**:原实现要求 `get_type_kind(ti) == TYP_BASE` 才回 AK,否则 -1(`ty_shadow.cr:67-71`);而 `iface_kind_of` 对 `TYP_DYN` 行回 `AK_DYN`、对结构行回 `AK_SEQUENCE` 等。原 `sh_native_ak(TI_DYN)` 回 **-1**,`TI_DYN` 由 `sh_term_of_ti` 的 `TYP_DYN` 分支另行处理(`:212-216`)⇒ 两者只在 `TYP_BASE` 行上重合,委托必须**显式保留该门**。) + +- [ ] **Step 1: 对拍用例(红)——**对照物**先行**:旧实现**改名保留**(`sh_base_ak_legacy`/`sh_native_ak_legacy`,照 P2a 的 `type_equal_legacy` 双用先例;**P5 删**),委托版与 legacy 版**全表逐项对拍**——不是抽样:`ty ∈ 全部 TY 码`(含 `TY_DEX_S` 与 `TY_GENERIC_PARAM` 两条灰格 + 未映射码)× `ti ∈ [0, g_type_count)`(含 `TI_DYN` 行与全部结构/命名行)。 +```core + // --- P2b Task 2:iface_kind_of 与桥接分派单源化(**全表对拍**,非抽样)--- + // 判据 = 对每个 ti 码:委托版 == legacy 版(legacy = 改动前的字面拷贝;P5 删) + ty_codes := alloc(16 * 8); + w64(ty_codes, 0, TY_INT); w64(ty_codes, 8, TY_DEX); w64(ty_codes, 16, TY_BOOL); + w64(ty_codes, 24, TY_STRING); w64(ty_codes, 32, TY_UNIT); w64(ty_codes, 40, TY_NEVER); + w64(ty_codes, 48, TY_CHAR); w64(ty_codes, 56, TY_GENERIC_PARAM); w64(ty_codes, 64, TY_DEX_S); + w64(ty_codes, 72, 999); // 未映射码 + ty_bad : ., mut = 0; + tc_i : ., mut = 0; + loop { + if tc_i >= 10 { break; } + if iface_by_ty_code(r64(ty_codes, tc_i * 8)) != sh_base_ak_legacy(r64(ty_codes, tc_i * 8)) { ty_bad = ty_bad + 1; } + tc_i = tc_i + 1; + } + total = total + 1; fails = fails + ts_check("iface.by_ty_code_all_codes", ty_bad, 0); + // 行号面全表:构造 8 原生 + struct/数组/指针/元组/泛型应用行后逐 ti 比 + ti_bad : ., mut = 0; + ti_i : ., mut = 0; + loop { + if ti_i >= g_type_count { break; } + if sh_native_ak(ti_i) != sh_native_ak_legacy(ti_i) { ti_bad = ti_bad + 1; } + ti_i = ti_i + 1; + } + total = total + 1; fails = fails + ts_check("iface.native_ak_all_rows", ti_bad, 0); + // 两条灰格的**显式**断言(不止"两版相等"——防两版一起错) + total = total + 1; fails = fails + ts_check("iface.kind_dyn_vs_bridge", + (iface_kind_of(TI_DYN) == AK_DYN), 1); // 注册表:DYN 行 → AK_DYN + total = total + 1; fails = fails + ts_check("iface.bridge_dyn_branch", + (sh_native_ak(TI_DYN) == -1), 1); // 桥接:DYN 行**不经原生快路径**(委托后须保持) + // TY_GENERIC_PARAM(=7,与 TI_DYN 同值)与 TY_DEX_S(=8)两条灰格 + total = total + 1; fails = fails + ts_check("iface.gparam_ak", + iface_by_ty_code(TY_GENERIC_PARAM), AK_NAMED); + total = total + 1; fails = fails + ts_check("iface.dex_s_ak", + iface_by_ty_code(TY_DEX_S), AK_DEX); + total = total + 1; fails = fails + ts_check("iface.unknown_ty_code", + iface_by_ty_code(999), -1); + // 语义分派守卫(P1 血泪:AK/TI 下标不 1:1)——沿用 bridge.str_ak / bridge.bool_ak + total = total + 1; fails = fails + ts_check("iface.dispatch_no_index_shortcut", + (iface_ti_of(AK_STRING) != AK_STRING), 1); // 若哪天有人"按下标直传",本行必红 +``` +- [ ] **Step 2: 实现**:① 原体改名为 `sh_base_ak_legacy`/`sh_native_ak_legacy`(**保留**,供对拍 + 报告引用;P5 删,登记入 TODO #24 的 P5 继承项);② `sh_base_ak`/`sh_native_ak` 改为委托 `iface_by_ty_code`(**显式保留** `ti < 0` 与 `get_type_kind(ti) != TYP_BASE → -1` 两个门);③ 跑 Step 1 用例,`*_all_codes`/`*_all_rows` 必须为 0 mismatches。 +- [ ] **Step 3: 判据**:`selftest-types` 全绿 + **影子语料复跑数字与基线逐项相同**(71 文件 / 26989 / agree 26989 / 0 / 0 / 0;**必须同报站点直方图**——P1 交接硬性要求)+ ELF/`.ccr` 逐字节 + `test_lsp.py`(桥接缓存重置面,P2a 评审 Critical 的守卫)。 +- [ ] **Step 4: 提交**:`refactor: R2 P2b Task 2——iface_kind_of 与桥接分派(sh_native_ak/sh_base_ak)单源化(逐 case 对拍含 DYN/GENERIC_PARAM/DEX_S 三条灰格;影子语料数字逐项不变)` + +--- + +## Task 3:字面量定型查表接线(5 处) + +**Files:** Modify: `src/compiler/checker.cr`(`:1892-1897`)、`src/compiler/type_selftest.cr` + +**Interfaces:** 复用 Task 1 的 `iface_lit_ti(lit_kind)`;**接线后 `infer_expr` 头部 5 行变 5 行查表调用**(不得合并成一次「先取 kind 再查」的循环——5 个 AST kind 的判断顺序与短路行为必须逐字保持)。 + +- [ ] **Step 1: 交叉断言用例(红)**:字面量节点在 parser 侧**本已带 `type_val`**(`parser.cr:399` `EXPR_INT→TY_INT`、`:410` `EXPR_DEX→TY_DEX`、`:414/:1032` `EXPR_STRING→TY_STRING`、`:416-417/:1040-1044` `EXPR_BOOL→TY_BOOL`、`:556/:1036` `EXPR_CHAR→TY_CHAR`)⇒ 可加一条**跨交付物交叉断言**:`iface_lit_ti(K) == ty_code_to_ti()` 对 5 个 kind 全成立(Task 6 的 `ty_code_to_ti` 落地后启用;本 Task 先断前 5 个 `iface.lit_*` 用例不变)。 +- [ ] **Step 2: 接线**:`:1892` `if ast_kind(node) == EXPR_INT { return iface_lit_ti(EXPR_INT); }` … 逐行同构替换(**不**引入中间变量、**不**改判断顺序)。 +- [ ] **Step 3: 判据**:`selftest-types` 全绿 + ELF 逐字节 + `.ccr` 实测 + 全回归。 +- [ ] **Step 4: 提交**:`refactor: R2 P2b Task 3——字面量定型查表(infer_expr:1892-1897 内联 if 链 → iface_lit_ti;5 个 kind 逐项对拍,短路顺序逐字保持)` + +--- + +## Task 4:操作许可接线(二元 11 条 / 一元 4 条 / 条件 3 条) + +**Files:** Modify: `src/compiler/checker.cr`(`:1918-1966`、`:1968-2008`、`:2265`、`:2376`、`:2402-2403`)、`src/compiler/iface_registry.cr`(填 `ops` 位集)、`src/compiler/type_selftest.cr` + +**位下标约定(写死,跨 Task 一致):** +```core +// 许可位下标 = **复用 ast.cr 既有操作码**(少一层映射 = 少一处漂移源;P1 的 AK/TI 事故即映射层自造): +// OP_* 1..19 → 直接用其值(OP_ADD=1 … OP_PTR_DIFF=19;OP_AND=12/OP_OR=13 同理, +// 故**不另设** IP_LOGIC——逻辑族的谓词就写 iface_permits(kind, OP_AND)) +// UOP_* 1..4 → 经 +20 偏置(UOP_NEG→21 … UOP_DEREF→24) +// 新族 25.. → IP_INDEX=25 / IP_INDEX_RANGE=26 / IP_FIELD=27 / IP_METHOD=28 / IP_AS=29 +// / IP_COND=30(真值性,`if` 现状收 bool|int) +// / IP_COND_BOOL=31(严格 bool,`while` 现状**只收 bool**——两条规则现状不同, +// **不得合并为一个「更统一」的位**:合并 = 收紧 `if` 或放宽 `while`) +IP_UOP_BIAS : int = 20; +IP_INDEX : int = 25; IP_INDEX_RANGE : int = 26; IP_FIELD : int = 27; +IP_METHOD : int = 28; IP_AS : int = 29; IP_COND : int = 30; IP_COND_BOOL : int = 31; +// 位构造照 dyn_set_type 的乘 2 循环(本语言无移位运算符): +fn iface_bit(n: int) -> int { b : ., mut = 1; k : ., mut = n; loop { if k <= 0 { break; } b = b * 2; k = k - 1; } return b; } +``` + +**站点侧的三种「门形状」(现状各异,**必须逐字保留**;表只出「原子类 × op」的单侧许可):** +```core +// ① ANY(算术族 :=1950):至少一侧许可即通过 —— if iface_permits(k(lt),op) == 0 && iface_permits(k(rt),op) == 0 { 报错 } +// ② ALL(逻辑族 :=1960):每侧都须许可 —— if iface_permits(k(lt),OP_AND) == 0 || iface_permits(k(rt),OP_AND) == 0 { 报错 } +// ③ ONE(条件族 :=2265 / :2376):单操作数 —— if iface_permits(k(c),IP_COND)==0 { 报错 }(while 用 IP_COND_BOOL) +``` + +**表填空(逐格 = 现状语义;**这是本 Task 的核心产出物**,每格注现状行号)** + +**先立一条结构事实(否则表会填错)**:现状的「算术许可」是**两层**——① `:1937/1939/1942/1946` 的**早退规则**(串拼接、指针算术、指针差:命中即 `return`,**不会走到门**);② `:1950` 的**门**(「两侧皆非数值才报错」)。故: + +- **门集合只含 `INT`/`DEX` 两员**——这**不是**省事,而是与 `:1950` 一字等价所要求的最小集。反例(**若把 `PTR` 或 `STRING` 也放进门的 ADD 格,就会放宽**): + `*T + *T` 现状 = 报错(`:1939/1942` 都不命中、`:1950` 两侧非数值)——若 `PTR@ADD=1`,ANY 门下 `permits(PTR,ADD)=1` ⇒ **不再报错**(静默降为 `TI_INT`)= 放宽; + `"a" - "b"` 同理(`:1937` 只管 `OP_ADD`)。 +- **串拼接(`+` on string)的「许可」在现状**落在①(`:1937`)**而非门里**——这与 spec §2.1 的示例(「`+` 对 int/dex/string 合法」)**不冲突**:spec 说的是语义层的操作许可,本批的落地形态把它拆成「门集合 + 早退规则」两处,两处合起来 = spec 的语义(**报告须逐条给出该对应关系**,见 Task 7 Step 5 的事实表)。 +- 故门的**许可格**只有 `AK_INT`/`AK_DEX` 的 `ADD..MOD = 1`,其余原子一律 `0`;早退规则(串拼接/指针算术/指针差)**留代码**(它们同时决定**结果类型**,属结果规则)。 + +**许可格(门 + 非算术族;每格注现状行号):** + +| 原子类 | 许可格(现状依据) | +|---|---| +| `AK_INT` | 门:`ADD..MOD`(`:1950`)+ `AND/OR`(`:1960` 允许 bool\|int)+ `EQ..GE`(`:1956` 全许可)+ `NEG/NOT`(`:1970`)+ `REF`(`:1973`)+ `DEREF`(`:1994-2005` 兜底透传)+ `AS`(`:2904` 无校验)+ `IP_COND`(`:2265` 收 int);**`IP_COND_BOOL` 否**(`:2376` 只收 bool);`IP_INDEX*` 否(非容器) | +| `AK_DEX` | 门:`ADD..MOD`(`:1950`)+ `EQ..GE`/`NEG/NOT`/`REF`/`DEREF`/`AS`(全许可面同上);**逻辑/条件格 = 0**——`AND/OR` = 0(`:1960` 只收 bool\|int)、`IP_COND` = 0、`IP_COND_BOOL` = 0(`:2265`/`:2376`);实测拒:`1.5 && true` / `1.5 \|\| false` / `if 1.5` → `error[TC01]`;`IP_INDEX*` 否(非容器) | +| `AK_BOOL` | 门:`ADD..MOD = 0`(`:1950` 门不含 bool)+ `AND/OR` + `EQ..GE` + `NEG(NOT)`/`REF`/`DEREF`/`AS` + `IP_COND`/**`IP_COND_BOOL`** | +| `AK_STRING` | 门:**全 0**(拼接走 `:1937` 早退)+ `EQ..GE` + `NEG/NOT`/`REF`/`DEREF`/`AS` + `IP_INDEX`(`:2619` 串下标→int);`IP_COND*` 否(`:2265` 只收 bool/int) | +| `AK_UNIT`/`AK_NEVER`/`AK_CHAR`/`AK_DYN` | 门全 0 + `EQ..GE` + `NEG/NOT`/`REF`/`DEREF`/`AS`;**`IP_COND`/`IP_COND_BOOL` 否**(`:2265`/`:2376` 只收 bool\|int / bool——**不得因「全许可面」顺手表上**);`AK_DYN` 额外 `IP_METHOD`(`:2066` dyn 方法校验路径) | +| `AK_PRODUCT`(元组) | 门全 0(`:1956` 比较**无校验** ⇒ 比较面全许可,登记 P3 收紧面)+ `IP_FIELD`(`:2566` `.N`)+ `REF`/`DEREF`/`AS` | +| `AK_SEQUENCE` | 门全 0 + `IP_INDEX`(`:2605-2618`)+ `IP_INDEX_RANGE`(`:2586`)+ 比较/一元/转换面 | +| `AK_REF`/`AK_PTR` | 门全 0(**指针算术由 `:1939-1948` 早退承担,见上「结构事实」**)+ `DEREF`(`:1996-2001`)+ 比较全许可面 + `AS`(`:2897`) | +| `AK_NAMED` | 门全 0 + `IP_FIELD`(`:2522` 字段)+ `IP_METHOD`(`:2088` 方法表)+ 比较全许可面 + `NEG/NOT`/`REF`/`DEREF`/`AS` | + +> **勘误(P2b 阶段评审 Important #1-A 回填;实测见 `.superpowers/sdd/p2b-review` 结论)**:上表原为 `AK_INT`/`AK_DEX` **合并一行**,把 `AND/OR`(`:1960` 允许 bool\|int)与 `IP_COND`(`:2265` 收 int)记在合并行上——**错**,这两格属 `AK_INT` **独有**;`AK_DEX` 的 `AND/OR` = 0、`IP_COND` = 0、`IP_COND_BOOL` = 0(`1.5 && true` / `1.5 || false` / `if 1.5` 实测 `error[TC01]`)。已拆为两行;交付真值 = `src/compiler/iface_registry.cr:114`(`AK_INT = o_base + o_arith + o_logic + o_cond`)与 `:116`(`AK_DEX = o_base + o_arith`;in-code 裁决注记 `:98-100`)。 + +(**「比较/一元/转换面全许可」的口径**:`:1956` 比较不校验 ⇒ 6 个比较位对**全部 13 类**置 1;`:1970` 一元透传、`:1994-2005` 解引用兜底、`:2904` 转换无校验 ⇒ `NEG/NOT`/`DEREF`/`AS` 对**全部 13 类**置 1;`UOP_REF`:`:1973` 对任意操作数产 `TYP_PTR` ⇒ 亦全许可。**这四组「全 1 列」是现状宽松面的集中体现**,报告须单列其 P3 收紧建议。) + +- [ ] **Step 1: 红态(现状行为探针先行,`tests/selfhost/test_iface_ops.py` 新建)**——先跑出 §2 九条宽松面的**现状事实**(本计划未测,须由实现者实测确证),再接线: + - 正控(许可):`fn main()->int{ x:=1+2; return x; }` rc=0;`"a"+"b"` rc=0;`p+1`/`1+p`(`*int`)rc=0;`s[0]`(string)rc=0;`t.0`(元组)rc=0。 + - 负控(拒绝,码不变):`[int;3] + [int;3]` → `error[TB01]` rc=1;`"a" * "b"` → `error[TB01]` rc=1;`"a" - "b"` → `error[TB01]` rc=1(**门不含 string**);**`*T + *T` → `error[TB01]` rc=1(本批最易放宽的一条——见上「结构事实」)**;`!x` 中 `x: [int;3]`—— **不适用**(一元现状透传,须断 rc=0 且类型 = 数组,登记为 P3 面);`if [int;3] {}` → `error[TC01]` rc=1;`while 1.5 {}` → `error[TC01]` rc=1。 + - 登记面(现状宽松,**断言"现状不动"**):`1 + [int;3]` rc=0;`"a" * 2` rc=0;`arr[true]` rc=0(索引类型不校验);`p.f`(p 非 struct)rc=0。 +- [ ] **Step 2: 接线(逐条同构替换,三种门形状照上表)**: + - `:1950`(ANY)`if lt != TI_INT && lt != TI_DEX && rt != TI_INT && rt != TI_DEX` → `if iface_permits(iface_kind_of(lt), op) == 0 && iface_permits(iface_kind_of(rt), op) == 0`(**`&&` 形状与现状一字不差**——现状「两侧皆无许可才报错」)。 + - `:1960`(ALL)`(lt != BOOL && lt != INT) || (rt != BOOL && rt != INT)` → `iface_permits(k(lt), OP_AND) == 0 || iface_permits(k(rt), OP_AND) == 0`(`op` 手头就是 `OP_AND`/`OP_OR`)。 + - `:2265`(ONE,`IP_COND` = bool\|int)与 `:2376`(ONE,`IP_COND_BOOL` = 仅 bool)分别取各自位——**两条规则现状不同,不得合并**。 +- [ ] **Step 3: 判据**:`selftest-types` 全绿(含新增 `iface.ops.*` 逐格用例 ≥10 例)+ `test_iface_ops.py` 正/负/登记三类全绿 + ELF 逐字节 + `.ccr` 实测 + 全回归 + 影子语料数字不变。 +- [ ] **Step 4: 提交**:`refactor: R2 P2b Task 4——操作许可查表接线(二元/一元/条件;许可位 = ast.cr 既有操作码复用;ANY/ALL 语义与现状逐字等价,宽松面原样保留并登记)` + +--- + +## Task 5:容器面接线(索引 / 字段 / 转换 / dyn 消费点) + +**Files:** Modify: `src/compiler/checker.cr`(`:2581-2636`、`:2509-2579`、`:2892-2908`、`:2066-2082`、`:2643-2651`、`:2473-2477`)、`src/compiler/iface_registry.cr`、`tests/selfhost/test_iface_ops.py` + +- [ ] **Step 1: 红态**:索引容器许可三正(数组/切片/串)+ 非容器负控(`1[0]` → `error[TK01]` 现状已有,`:2634`)+ 落空分支**登记**(`p.f` 落空 rc=0;`x as T` 任意组合 rc=0;`dyn` 方法不存在 → `error[N08]`(`EC_N_METHOD=2008`)`checker.cr:1878`)。 +- [ ] **Step 2: 接线**(**与 Task 4 的关键区别**:索引面的三个 kind 分支**做的是不同的事**——数组带 F2 越界检查、切片返元素、串返 `TI_INT`——**它们都是结果规则,必须原地保留**;表在这里只承担「谁**可以**进这个面」的**拒绝判定**): + - **不得**把 `if arr_kind == TYP_ARRAY`(`:2605`)/`if arr_kind == TYP_SLICE`(`:2616`)/`if arr_ti == TI_STR`(`:2619`)换成 `iface_permits(...)`——那会把三个分支合并成一条(结果类型与检查全丢)。改动只落在一处:`:2634` 的兜底拒绝(`check_error(EC_TK_INDEX, ...)`)之前加/改为 `if iface_permits(iface_kind_of(arr_ti), IP_INDEX) == 0 { <原 TK01 报错,码与文案逐字不变> }`,**门体即原兜底调用;`return TI_INT` 保留**(勘误——原句「其后保留原兜底(双保险)」不成立:`check_error` 无同码同位去重,门后叠加第二次裸调用会在拒绝路径上报**两条 TK01** = 可观测行为变化;表的拒绝集与兜底逐行相等,措辞路径不变。勘误来源 = P2b 阶段评审 Important #1-B 回填,实测见 `.superpowers/sdd/p2b-review` 结论;交付真值 = `src/compiler/checker.cr:2678-2681`)。 + - 同理 CHECK:`IP_INDEX_RANGE` 的拒绝分支(`:2602` 的 `return TI_UNIT`)**保留现状**(range 索引非数组时今日静默返 `TI_UNIT` 无诊断——登记面,不改)。 + - 字段/转换/dyn:**落空分支一律保留现状**(`:2578` 返 `TI_UNIT` 无诊断、`:2907` 恒等透传、`:2071` `validate_dyn_method`)——本 Task 只把它们的**许可判据**改成查表(`IP_FIELD`/`IP_METHOD`/`IP_AS`),**诊断面零改动**;落空分支的「无诊断」事实写入报告的事实表(P3 收紧面的输入)。 + - **可空的接线强度(如实登记)**:索引/字段/转换三面的表格在现状下**几乎不改变判定**(拒绝集与兜底同集)——其价值 = 把「拒绝集」从散落的分支变成表里可审计的一格(P3 收紧的旋钮位置)。报告须如实说明哪些格子**当前不可达/无行为差异**,不得把它记成「已生效的接线」。 +- [ ] **Step 3: 判据**:同 Task 4 + 现有 `test_agg_checks.py`(TS01-04/TK02)、`test_slice_bounds.py`、`test_agg_slots.py` 全绿。 +- [ ] **Step 4: 提交**:`refactor: R2 P2b Task 5——容器面许可查表接线(索引/字段/转换/dyn;结果规则与诊断面零改动,落空分支现状登记)` + +--- + +## Task 6:`res_type_node` 双份 TY→TI 映射合一(+ 同族 6 处内联链) + +**Files:** Modify: `src/compiler/checker.cr`(`:732-744`、`:1382-1392` + 同族 `:1055-1061`/`:1080-1085`/`:1133-1140`/`:1671-1675`/`:1724-1730`/`:2129-2134`)、`src/compiler/iface_registry.cr`、`src/compiler/type_selftest.cr` + +**Interfaces:** +```core +// TY_*(基类型码)→ TI_*(类型表行常量);**-1 = 无对应**(调用方自行决定回落,不再表内兜底) +fn ty_code_to_ti(ty: int) -> int +// 语义:TY_INT→TI_INT / TY_DEX→TI_DEX / TY_BOOL→TI_BOOL / TY_STRING→TI_STR / TY_UNIT→TI_UNIT / +// TY_NEVER→TI_NEVER / TY_CHAR→TI_CHAR / TI_DYN(值 7,**原样保留现状写法与注释**)→TI_DYN / +// TY_DEX_S→TI_DEX_S(占位行,仅 init_types 面用;不属两表并集)/ 其余 → -1 +// 调用点现状等价改写(**两处回落必须显式写出,不得藏在表里**): +// res_type_node : ti := ty_code_to_ti(tv); if ti < 0 { ti = TI_UNIT; } return ti; +// res_call_type : ti := ty_code_to_ti(tv); if ti < 0 || tv == TY_NEVER { ti = TI_UNIT; } return ti; ← 见 Step 2 探针裁决 +``` + +- [ ] **Step 1: 用例(红)**:逐码对拍 `ty_code_to_ti(TY_INT..TY_CHAR)` = 对应 TI;`TY_DEX_S`/未知码 → -1(**表内不兜底**);`res_type_node`/`res_call_type` 的**基型节点**在合一前后同值(用 `alloc_node(0,…,TY_X)` 逐码构造节点比返回)。 +- [ ] **Step 2: `never` 单元格探针(**本计划未测**,须实测才可裁决)**: + ```core + // 探针 A:调用位点的 never 形参 + fn f[T](a: T, b: never) -> int { return 1; } + // 探针 B/C/D:嵌套位 [never;3] / *never / G[never]——经 res_call_type 递归 + ``` + - 结果 **不可达** ⇒ 合一后 `res_call_type` 直接用 `if ti < 0 { ti = TI_UNIT; }`(与另表同形),差异消失,报告登记「不可达」。 + - 结果 **可达** ⇒ **保留现状**:`res_call_type` 处显式 `|| tv == TY_NEVER`(一行 + 注记 + 探针文件),并把「`never` 形参在调用位点被当作 unit」登记为**待裁决项**(交 P3;语义争议按 spec §4 裁决 6 **停下上报**)。 + - **判据 = 探针实测输出**,不得以「大概到不了」代替。 +- [ ] **Step 3: 同族 6 处合并**:`:1055-1061`/`:1080-1085`/`:1133-1140`/`:1671-1675`/`:1724-1730`/`:2129-2134` 逐处改调 `ty_code_to_ti`,**逐处保留各自的尾部回落**(`TI_UNIT`;`:1724-1730` 含 `TY_NEVER`;`:2129-2134` 的 `TY_*` 撞车比较改为 `ty_code_to_ti(iface_ret2)` 显式化——**行为等价**:`TY_INT==TI_INT==0 … TY_CHAR==TI_CHAR==6`)。 +- [ ] **Step 4: 判据**:`selftest-types` 全绿(新 ≥6 例)+ ELF 逐字节 + `.ccr` 实测(**预期不变**:合一只改 Rust-式分支形状,不 alloc 行)+ 全回归 + 自举链。 +- [ ] **Step 5: 提交**:`refactor: R2 P2b Task 6——res_type_node/res_call_type 双份 TY→TI 映射合一(单表 ty_code_to_ti + 各调用点显式回落;同族 6 处内联链一并收敛;never 单元格探针实测裁决)` + +--- + +## Task 7:收官(回归 + 自举 + 收紧清单 + 文档) + +- [ ] **Step 1: 全量回归**:38 套件(7 bootstrap + 31 selfhost)+ `tests/suite` 20 语料 + `tests/selfhost/test_iface_ops.py`(新)。 +- [ ] **Step 2: 零变化复验**:ELF 逐字节(`ptr_arith`,clean-cache)+ 影子开/关两态逐字节 + `.ccr` **实测变更报告**(预期零变化;变则逐段说明)+ 影子语料复跑(数字 + 站点直方图同报)。 +- [ ] **Step 3: 自举链**:`src/ci/run.sh full-bootstrap`(`corec2/corec3` `cmp` IDENTICAL + 两段 `error[N06]=0` + `--help` rc=1)+ 冒烟 `run 'fn main()->int{return 42;}'` rc=42。 +- [ ] **Step 4: 收紧清单**:本批「旧接受 → 新拒绝」逐条记录——**预期为空**(保语义口径);若非空 → 逐条登记文件/用例/旧判定/新判定/处置,并说明为何属于「接线必然而非收紧」。 +- [ ] **Step 5: 事实表**:侦查 §2 九条宽松面 → 表格的**逐条落格记录**(现状行号 / 表中格 / 是否有诊断 / P3 收紧建议),落 `docs/superpowers/specs/2026-09-10-type-shadow-findings.md` 新增一节「P2b 后:公理区事实表」,供 P3 直接消费。 +- [ ] **Step 6: 文档/TODO**:spec §9 P2 行标 (b) 部分 ✅(含落点/提交链/未覆盖面);TODO #24 的「P2b 待办」划销、新增「P2b 落地」条目(落点 + 未覆盖面登记:`iface_satisfies`/横切轴/用户轴/尺寸对齐未做、`AK_SUM`/`AK_FN` 无 checker 对应、dyn 64 上限、P3 收紧面清单);`src/ci/run.sh` 挂 `test_iface_ops.py`。 +- [ ] **Step 7: 提交**(路径限定)。 + +--- + +## 自检记录 + +- **根因 vs 止血**:本批**不是止血**——它是 spec §2「一张注册表、检查器统一查表」的**第一段落地面**(此前只有 P0 的判定 API,无查询 API、无条目表)。**采用的路线 = 保语义接线**(表逐格转录现状),**明确不做**「顺手收紧」(= P3):理由是 P2a 已确立「替换门 = 语料差异归零 + 站点覆盖同报」的纪律,而收紧面尚无全语料证据(P1 的 9 条差异 100% 是 unknown,**零条**是收紧面)。**未采用**的替代方案:① 一步到位「表 + 收紧」(会把语言面收紧混进接线批,违反裁决 6 的「逐处记录 + 争议停下」);② 只建 API 不接线(P2b 交付物缺一半,且下游切片等的是「统一入口」)。 +- **两个交付物的耦合(设计说明)**:`iface_lit_ti`(字面量定型)与 `ty_code_to_ti`(TY→TI 单表)在本语言里**同源**——字面量节点本就携带 `type_val = TY_*`(`parser.cr:399/410/414/416/556`)。本批选**kind 主路**(保语义:`infer_expr` 现状按 AST kind 判,不读 `type_val`),并把 `type_val` 路作为**交叉断言**(Task 3 Step 1)而非主路——因为并非所有字面量铸点都写 type_val(`monomorph.cr:228-231` 克隆**保留** `tv`,但其它路径未审计),主路换 kind 会在未来铸点漏写时静默错型。 +- **占位符扫描**:无 TBD;两处**明标未测**(侦查 §6:`never` 可达性 + 宽松面行为实证)已各自绑定「Step 1 红态探针」而非「稍后填」。表的空白格一律有明确取值(0/1),无「待定」。 +- **关键结构发现(本计划最大单点风险,已写入 Task 4)**:现状的「算术许可」是**两层**(`:1937/1939/1942/1946` 早退规则 + `:1950` 门),故**门的许可集只能含 `INT`/`DEX`**——把 `PTR`/`STRING` 填进 `ADD` 格会让 `*T + *T`、`"a" - "b"` 从「报错」变「静默」(ANY 门语义下 `permits(PTR,ADD)=1` 足以放行)。spec §2.1 说「`+` 对 int/dex/string 合法」与此不冲突:该语义在现状由「门 + 早退规则」两处合起来给出,本批的落地形态**照现状拆**,并把它列入报告的事实表。**若实现者按直觉把 string/ptr 填进门,判据(负控 `*T + *T` / `"a" - "b"` rc=1)必红。** +- **命名一致性**:`iface_registry.cr` / `iface_count` / `iface_entry` / `iface_ops` / `iface_permits` / `iface_lit_ti` / `iface_lit_ak` / `iface_kind_of` / `iface_ti_of` / `iface_of_term` / `iface_by_ty_code` / `ty_code_to_ti` / `iface_bit` / `ESZ_IFACE_ENTRY` / `OFF_IE_*` / `IP_*` / `IP_UOP_BIAS` 在任务间一致;`sh_native_ak`/`sh_base_ak` 保留原名(影子链路可比性)。 +- **spec 覆盖**:§2.1 三字段(操作许可 / 字面量定型 / 尺寸对齐「非语义」)→ Task 1/3/4/5 + 范围节第 ③ 条;§2.5 五个 API → Task 1(`iface_satisfies` 明标 P3,零调用者);§2.4 编号撞车 → **登记**(Task 6 Step 3 显式化 `:2129-2134` 一处,其余归 P4/P5,不在本批改命名空间);§4 步 3 的两个点 → Task 4/5 与 Task 6;§9 P2 (b) → 全批。 +- **风险 / 退路**: + 1. **最大风险 = 接线时「顺手收紧」**(侦查 §2 的九格都是「看起来像 bug」的现状)⇒ 后果:自举源码或语料出现新诊断(rc 变/ELF 变),且收紧面**无全语料证据**。缓解 = 逐格转录 + 格注行号 + 三类探针(正控/负控/**登记面**)+ 收紧清单预期空。 + 2. **AK/TI 下标事故复发**(P1 已发生过一次,且本批要新建「原子类 ↔ 类型行」双向映射)⇒ 缓解 = `bridge.str_ak`/`bridge.bool_ak` 既有守卫 + `iface.dispatch_no_index_shortcut` + 逐条目 `iface.*_ti` 用例。 + 3. **桥接层改动动摇影子证据链**(P1/P2a 的数字是跨批可比性基线)⇒ 缓解 = Task 2 单列 + **全表逐行对拍** + 语料数字逐项相同 + 退路 = 若评审否决该合并,**登记为 P5 项**(不静默、不删除重复)。 + 4. **`.ccr` 意外变化**(若某处接线扰动类型行号或误 alloc 行)⇒ 缓解 = 表**不 alloc 类型行**、判据要求冷缓存实测 `.ccr`;变则**停下上报**。 + 5. **新文件的清单/顺序**(bootstrap 单遍解析;三份清单 + `_import.cr` 四处同步点)⇒ 缓解 = `guard_manifest` + `test_compile.py` 的存在性断言(清单漂移 = resolver 报 `Undefined name`)。 diff --git a/docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md b/docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md new file mode 100644 index 00000000..868531d8 --- /dev/null +++ b/docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md @@ -0,0 +1,611 @@ +# R2 P3(能力落地)实施计划:定长退役 + 序列接口/长度约束 + 穷尽性/联合/可选 + 泛型约束 + impl 契约 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 P0/P1/P2a 建成的「类型项层 + 语义包含引擎 + 判定接线」从**判定替换**推进到**能力落地**——五项目标能力(定长退役、序列接口/长度约束、穷尽性/联合/可选、泛型约束、impl 契约)各自在引擎/桥接/checker 三层接通,每项带正负控、边界例与不变量双钉。 + +**Architecture:** 三条主线:① **引擎展开层**(Task 0:named/struct/enum/generic-apply → 结构项;**仅**服务满足判定与域查询,等价判定保持原子名义)——它是穷尽性/联合/可选/泛型约束/impl 四项的共同前提,也是 P2a 登记的「P5 引擎命名展开」前移;② **不依赖 P2b 的能力**(Task 1 定长/变型、Task 3 穷尽性、Task 4 联合/可选、Task 5 的非接口半边)= **P3a**;③ **依赖 P2b 注册表的接口面**(Task 2 横切接口、Task 6 impl 契约、Task 5 的 `T: I` 满足半边)= **P3b**(P2b 计划落地后接,接口契约见下节)。 + +**Tech Stack:** Core 自举栈。`src/compiler/type_terms.cr`(类型项 DAG)· `type_engine.cr`(三态判定)· `ty_shadow.cr`(ti→term 桥接 + 10 站点 + 计数)· `checker.cr`(判定点/公理区/泛型/接口)· `monomorph.cr`(实例化)· `ir_gen.cr`(表示层读 N 的两处)。判据通道:`corec selftest-types`、`--type-shadow`、`tests/selfhost/test_type_engine.py`。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` §5(能力落点:§5.1 定长/序列/长度约束、§5.2 泛型、§5.3 impl、§5.4 穷尽性/联合/可选)+ §1/§2(语义基座/双轴注册表)+ §9 P3 行;方向定案 `docs/superpowers/specs/2026-08-30-type-system-direction-design.md` 条目 8;定长裁决 `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md` §1 + TODO.md `fixed-array-retire`(TODO.md:446)。**前置状态(已实测,非本计划目标)**:P0 引擎 + **95 例**(`selftest-types` 95/95);P1 影子(开/关两态产物逐字节同);P2a 判定替换(`type_equal` → `ty_equiv`)+ 语料对拍归零。 + +## Global Constraints + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**;所有命令 `nice -n 19`;判据前 `clean-cache`、cwd = 仓库根(比较任何 `.ccr`/缓存态产物前必须 clean-cache——TODO #5 末条/#26)。 +- **ELF canary 本计划全任务预期不变**:`clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/…` → sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(即 `/tmp/r1t4_base_bin`)。**任一任务 ELF 变 → 停下上报**(类型层改动泄进发射面)。⚠ canary 语料(`tests/suite/ptr_arith.cr`:一个数组字面量 + 取址/解引用,**无切片/联合/match**)对类型层改动**不敏感**——它是「发射面零泄漏」证据,**不得**当新语义正确性证据;新语义一律由**行为探针**承担(各任务判据步单列)。 +- **`.ccr` 预期与实测**:Task 0/1/2/3 预期不变(类型表行序不动);**Task 4(退役内建 Option 注册)/ Task 5(实例键类型项化)预期会变**(命名行编号 / SYM 中 mangled 实例名)→ **允许但必须实测报告**;判据按 TODO #26 = **结构性断言**(`tests/selfhost/test_ccr_v7.py` 全绿)+ 语义零变化 + 自举稳定(`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42),不以「与旧版逐字节同」为准。ELF 同时变 → 停下上报。 +- **三态纪律(P0/P1/P2a 继承)**:引擎 -1(预算耗尽/未覆盖面)**不得**当 0/1 用;判定路径回落(`type_equal_legacy`)必须计数(`g_replace_unknown`/`g_replace_bridge`)并单列。新引擎查询自带预算前后隔离(照 `type_equal_engine`,checker.cr:500-502)。 +- **影子对拍门(每任务复跑)**(**R2 P5 Task 5 起作废**:影子通道已整体下线 ⇒ 改由 **冻结基线同源对拍 + 行为探针 + 突变控制** 承担,见 `TODO.md` #48 与 findings §15;本行余下语义——「零差异 ≠ 面收敛」与站点覆盖口径——**继续有效**,只是证据载体从对拍换为行为探针):基线 = 71 候选 / **67 有效文件 / 26,989 判定全 agree / `old_stricter=0` / `old_looser=0` / `unknown=0` / `replace_*=0`**(findings §11)。**必须与站点覆盖同报**(站点 1/2/4/6 语料零命中、3 仅 2 次)——「零差异」≠「面收敛」;新能力面(match/切片/泛型约束/impl)语料零命中 ⇒ 由定向探针承担。 +- **收紧处置(spec §0 裁决 6)**:新判定若「旧接受 → 新拒绝」,逐条记录(文件/用例/旧判定/新判定/处置);涉及自举源码先跑全语料清单再改;**语义争议停下上报**。 +- **新硬错误先报告后开门**:任何把新检查升为 rc=1 门的步骤(穷尽性、长度方向、可选面)必须先在**全语料**(`src/compiler/_import.cr` 自指 + `tests/suite` + `tests/selfhost` 内联源 + `src/stdlib` + `examples`)report-only 跑一遍,清单审查后再入 `run_frontend` 硬错误名单(main.cr:146-158;照 #29 先例)。语料排除项见 TODO #22(`src/compiler/elf.cr` 陈旧 2 parse error;`tests/suite/test_control_flow.cr`/`test_generics.cr` 0 字节)。 +- **不得回退既有收纳**:TS01-04/TK02 硬错误门(#29)、F2 常量档拒绝语义与「N 不回身份」(#19/#24)、F5 槽位契约(#25/#28)、效应/纯度判据(#26/#27)本计划一律不动;冲突 → 停下上报。 +- 本语言无三元运算符;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);自测用例**只增不减**(`test_type_engine.py:MIN_CASES=32` 为下限,逐任务下限见各任务判据)。 + +## 侦查底座(2026-09-11 实读 + 定向实测,file:line = 当前工作树) + +**引擎/桥接(能力落点现状)** +- 三态 API:`ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited`/`tt_witness`/`ty_exhaustive`(type_engine.cr:390/405/479/414/448/471);`tt_norm`(type_terms.cr:326);预算 `ty_budget_reset`(type_engine.cr:301)。 +- P0 显式未覆盖面(type_engine.cr:10-13):① 参数化原子**仅同形判等**(变型 = P3);② `AK_NAMED` **不展开**;③ 空递归保守 -1。变型现状证据 = 同类参数不同形 → `-1`(type_engine.cr:175-182)+ `tt_list_same` 结构相等(:187-193)。 +- 桥接(ty_shadow.cr:194-246):`TYP_ARRAY`/`TYP_SLICE` **同归** `AK_SEQUENCE(elem)`(:217-220,固定性与 N 均不入项);`TYP_REF` 的 mut 标记**不入参数链**(:224-227,注记自认「引擎无只读/可写区分 = P3 条目化面」);`TYP_NAMED`/`TYP_GENERIC_PARAM`/`TYP_GENERIC_APPLY` → `AK_NAMED`(:230-231);`TYP_TUPLE` → `AK_PRODUCT`(:178-191)。桥接缓存随 `init_types()` 失效(:98-102)。 + +**判定点/长度约束(P2a 现状)** +- `type_equal_engine`(checker.cr:492-508)+ 包装(:515-523);`type_equal_legacy`(:419-477,对照物 + 回落,P5 删);10 站点表(ty_shadow.cr:252-269)。 +- `array_len_constraint_ok`(checker.cr:346-386)= **常量档**、下钻 6 位(数组/指针/引用/切片/元组字段/泛型实参);**异 kind → 返回 1(无约束)**;`type_compat_strict`(:394-398)= 身份 ∧ 长度;判定点 10 处(:1088/1455/1468/1483/1732/1927/2320/2654/2780/2845)。 +- **实测(2026-09-11,探针 `/tmp/r2p3_probe/`,cwd=探针目录,`build/corec` = 20:39 构建)**: + - pA `fn g() -> [int;3] { b:[int;4]=[1,2,3,4]; return b[0..2]; }` → **rc=0、零诊断**(切片值当 `[int;3]` 用 = **静默放宽**;读码:legacy 数组分支要求两侧同为 `TYP_ARRAY`(:432-434),跨 kind 判 false ⇒ 此为 P2a 替换后的**现行语义洞**。P2a 前为 `error[TF01]`——此「前值」为读码结论,**未实测**)。 + - pB `fn h() -> [int] { c:[int;3]=[1,2,3]; return c; }` → rc=0(符合 spec §5.1 `[T;N] <: [T]`)。 + - pC 异长(`[int;4]` 在返回位给 `[int;3]`)→ rc=1 `error[TF01]: Array length constraint not satisfied`(常量档拒绝仍在)。 + - pD `fn f(x: int?) -> int` + `v := Some(3)`(无用户 `enum Option`)→ rc=1 `error[N01]: Undefined enum constructor 'Some'`。 + +**定长退役「未办面」枚举(读码逐条,非猜)** +1. **array/slice 判等过度合并**:桥接同译 `AK_SEQUENCE(elem)`(ty_shadow.cr:217-220)+ `array_len_constraint_ok` 异 kind 返回 1(checker.cr:349)⇒ 方向不设防(实测 pA)。 +2. **变型规则缺位**:参数链不携带「固定长/视图」「只读/可写」维度(ty_shadow.cr:220/225-227)⇒ `[T;N] <: [T]`、`[T;N] ≢ [T]`、`&T` vs `&mut T` 均无法表达。 +3. **长度档位**:常量档已有(:346-386);**符号档/动态档未实现**(:343-344 自注「P3 面」)。本次裁定:动态档 = 既有运行期检查(`g_ir_slice_lens` 侧表 + `IR_BOUNDS_CHECK`,R1 已落、TODO F11 划销)+ checker 字面量界检查(:2586-2602);**符号档 = VC 义务,本计划不实现,显式登记**。 +4. **N 读取面边界**:`get_type_extra(TYP_ARRAY)` 仍被读 5 处——常量档(:351)、F11 字面量切片界(:2589)、F2 字面量索引界(:2609)、表示层 `ir_gen.cr:120-131 arr_len_lit_of` / `:476-483 type_size`(+`type_align`)。退役后的**不变式** = 「N 只许被常量档检查与表示层读,身份/子类型路径零 N」——Task 1 为它加守门用例。 +5. 自举源码 10 处 `[T; N]` 零改造(R1 裁决)——Task 1 判据复验。 + +**穷尽性/联合/可选(现状)** +- match **零检查**:`EC_TM_*` 仅定义(ast.cr:443-451),`checker.cr` 零 raise;`EXPR_MATCH` 只推臂体(:2407-2451),模式绑定变量**恒 `TI_INT`**(:2431/:2439);臂类型一致性、模式 vs scrutinee、穷尽性、冗余臂全无。模式形态(parser.cr:1020-1040):通配 / 字面量(int/string/char/bool)/ 绑定 ident / 枚举 / struct 模式。 +- enum:变体构造器注册为「返回枚举 named 类型的函数」(checker.cr:977-993);`EXPR_ENUM_CONSTRUCTOR` 只解析名字(:2489-2507,载荷类型不校验);表访问器 dyn_arr.cr:393-399;`MAX_ENUM_VARIANTS=16`(ast.cr:117)。 +- 可选:`T?` 降级为 `Option[T]`(parser.cr:139-143);`Option` 仅在用户未声明时被注册为**一个 named 类型**(checker.cr:962-975);`Some`/`None` 走 `EXPR_ENUM_CONSTRUCTOR`(parser.cr:418-431)⇒ 无用户 `enum Option` 时 `Some(3)` 报 N01(实测 pD);`EXPR_TRY` 按**基名字符串** `"Option"`/`"Result"` 解包(checker.cr:2871-2888)。 +- 语料:`src/compiler/**` 与 `tests/suite/**` **零 `match` 表达式**、零 `enum` 声明(全仓 grep 仅注记命中)⇒ 穷尽性收紧的**自举风险 ≈ 0**;enum/match 仅出现在 `tests/selfhost/test_interp_float.py:34-35`、`test_interp_parity.py:54-74`、`test_native_memory.py:68`、`test_lsp.py:137-145` 内联 fixture(均为全变体覆盖的 3 变体枚举)。 + +**泛型约束(现状)** +- 约束语法仅**函数**泛型有:`parse_generics_into` 收 `T: I`(parser.cr:1184-1188)→ `save_func_gen_constrs`(:1210-1222,索引 `fi * MAX_GENERICS + gi`);**结构/枚举/接口泛型把约束写进 dummy 缓冲后丢弃**(parser.cr:1533/:1575/:1628)⇒ `struct S[T: I]` 静默无约束。 +- 调用点检查 = 名字拼接 + `check_iface`(checker.cr:1562-1598 → :1588);泛型体内经约束的方法调用同样走名拼接(:2103-2144)。 +- monomorph:实例键 = **逗号分隔类型名字符串**,由 `ir_gen` 从**调用实参**的 `irv_type` 生成(ir_gen.cr:1497-1513),**非原生类型一律兜底 `"int"`**(:1511;读码 ⇒ 不同类型实参可折叠到同一实例,**未实测**);去重 `istr_eq`(monomorph.cr:443-456);替换 = 名字文本替换(:64-89);`MAX_GENERICS=4`(ast.cr:115)。 + +**impl/interface(现状)** +- 表:`g_ifaces` 1432B/条(16 方法 × 88B、方法参数 ≤8;dyn_arr.cr:143-149);签名存**裸 `TY_*`**(parser.cr:1671/1685 `unpack_type`)⇒ 签名**无法表达命名/泛型类型**。`g_impl_for` 16B 存两名字 ni(parser.cr:1763-1767)。 +- 判定:`find_iface`(checker.cr:844-852)/`type_has_method`(:873-879,名字拼接 `Type.method`)/`check_iface`(:881-904)/`check_impl_for`(:1746-1807,param/ret 裸 TY 比较)。 +- **ir_gen 零消费、.ccr 零序列化**(spec §11 已核);覆盖 = `tests/selfhost/test_impl.py` 5 check + 2 native 例,**全为固有方法**(`impl Vec { … }`),`interface`/`impl I for T` 零命中(实读)。 + +**产物面(判据依据)** +- ELF 无符号表(`src/format/elf/elf.cr` 全文件 `symtab` 零命中)⇒ 函数名/mangled 名**不入 ELF**;`.ccr` 函数记录含 `name_idx`、NOD/var 含类型行号(ccr_io.cr:39-47/129-130)⇒ 命名行编号或实例名变化 → `.ccr` 字节变(允许、须报告)。 + +## 与 P2b 的交接面(阻塞 / 非阻塞) + +| P3 项 | 阻塞于 P2b? | 说明 | +|---|---|---| +| 定长退役(身份方向/变型/常量档收口) | **否** | 落点 = 桥接(ty_shadow.cr:217-227)+ 引擎变型表 + `array_len_constraint_ok`;P2b 的双表合一不碰这三处 | +| 穷尽性(match 真判定) | **否** | 引擎 `ty_exhaustive` 已有(type_engine.cr:471);缺的是枚举域项(Task 0)与 checker 接线 | +| 联合/可选(enum = sum、`T?` = T ∪ null) | **否** | 同上;退役 `Option` 注册在 checker.cr:962-975,与 `res_type_node` 无交集 | +| 泛型约束 | **半阻塞** | `T: I` 的**满足判定**须走 `iface_satisfies`(spec §2.3/§2.5)= P2b 注册表入口;**非阻塞半边** = 结构/枚举泛型约束保留(parser.cr:1533/1575)+ monomorph 实例键类型项化 | +| 序列接口 / 横切接口 | **是** | 横切轴条目(`sequence<⊤>`、只读/可写两形状)属注册表;P3 增量 = 形状条目内容 + 消费点(索引/切片/迭代)接线 | +| impl 契约(用户轴) | **是** | 条目升级(形状项引用 + 满足关系)+ `g_ifaces` 布局 + mangling 退役归 P2b;P3 增量 = 签名类型项化 + 覆盖集 | + +**交接契约(要求 P2b 落点提供;若落地形态与此不符 → 停下上报后重定 Task 2/6)**:① `iface_satisfies(t_ti: int, iface_ni: int) -> int`(三态 1/0/-1,不得静默当 0);② 本质条目留可扩展字段——`操作许可`(P2b 落)与**变型规则**(P3 落;引擎侧表可先在 Task 1 落,条目字段后续挂引用);③ `res_type_node` 单一化后 `TYP_ARRAY`/`TYP_SLICE`/`TYP_NAMED` 构造语义不得变化(Task 0/1 依赖)。 + +> **P2b 已落地(2026-09-11,提交链 `7f520dce`→`30545da7`)——本条契约按 P2b 实际交付面重定,实施前必读附录 A;⚠ 其后的 ① 已被 P3b Task 0 关闭(2026-09-12,见附录 B.1 首注)**:① **`iface_satisfies` 未交付**(P2b 范围明标「零调用者,P3 落地 impl 语义时才有对象」,见附录 A.3-①)⇒ Task 2/6 与 Task 5 的 `T: I` 满足半边**开工前先补条目 + 满足判定**;② 条目表已落 5 字段 `{ak, ti_row, name_ni, lit_code, ops}`(40B/条,无变型规则字段;`name_ni` 现为 `-1`——.ccr STR 段硬判据,见 A.3-②)⇒ 变型规则无处挂,Task 1 按「引擎侧表先行」口径落地(本计划原文已备此路);③ 已满足:`res_type_node`/`res_call_type` 经单表 `ty_code_to_ti`,`TYP_ARRAY`/`TYP_SLICE`/`TYP_NAMED` 三构造语义零变化(P2b 全判据 = 零行为变化)。 + +--- + +## Task 0(P3a,前置):引擎展开层——named/struct/enum/generic-apply → 结构项 + +**Files:** Modify `src/compiler/ty_shadow.cr`(新增展开查询,**不改**既有分支语义)· `src/compiler/checker.cr`(暴露 名字→struct/enum 行 查询)· `src/compiler/type_engine.cr`(满足判定入口占位)· `src/compiler/type_selftest.cr` + +**Interfaces:** +``` +// 展开查询(只读、per-ti 缓存 + 预算;-1 = 不可展开/未覆盖面——不得静默判否) +fn sh_struct_term(ti: int) -> int // struct named → AK_PRODUCT(字段项链, 声明序) +fn sh_enum_domain_term(ti: int) -> int // enum named → AK_SUM 变体项之 union(= 域) +fn sh_variant_term(ti: int, name_ni: int) -> int // 变体名 → 变体项(穷尽性模式项用) +fn sh_iface_shape_term(iface_ni: int) -> int // 接口 → 形状项(方法集 product of fn;P2b 条目就绪前返回 -1) +``` +**语义边界(实施须遵守)**:展开**只服务** ① 满足判定 `T <: 形状` ② 穷尽性域/模式项。**等价/包含判定(`type_equal` 路径)保持原子名义,不展开**——否则同形不同名类型被判等价 = 语义漂移,且推翻 P2a 对拍归零基线。一层深度不足 → 按引擎既有规则 -1 上抛。 + +- [ ] **Step 1: 用例(红)6 例**(`type_selftest.cr`):struct → product 字段序;enum → sum 变体数;`Box[int]` 域展开含实参代入;**名义不漂移**(两个同形不同名 struct:`ty_equiv` 仍 **-1**、`type_equal` 仍 false);行号越界 → -1;展开缓存命中计数。**为何 6 例**:四个正向形态 + 一条名义不变量双钉 + 一条缺口上抛。 +- [ ] **Step 2: 实现 + 缓存**(照 `sh_map_*` 先例:装填守卫 / 重建重放 / 回写重探;`find_struct`/`find_enum`/`find_iface`(checker.cr:824-852)为名字入口)。 +- [ ] **Step 3: 判据**:`selftest-types` **95 → ≥101**;影子对拍复跑(**预期零变化**——展开未接入 `type_equal` 路径;`unknown`/`replace_*` 若变 → 逐条归因登记);ELF canary IDENTICAL(clean-cache);`test_compile`/`test_purity`/`test_ccr_v7` 绿。 +- [ ] **Step 4: 提交**:`feat: R2 P3 Task 0——引擎展开层(struct→product / enum→sum / generic-apply 代入;仅满足判定与域查询用,等价判定保持原子名义)` + +## Task 1(P3a):定长退役收口——array/slice 方向 + 变型 + 长度档位 + +**Files:** Modify `src/compiler/ty_shadow.cr`(`AK_SEQUENCE`/`AK_REF` 参数链携带固定性与 mut)· `src/compiler/type_engine.cr`(变型表 + `tt_list_same` 按变型比较)· `src/compiler/checker.cr`(`array_len_constraint_ok` 方向规则;判定点不变)· `src/compiler/type_selftest.cr` + +**Interfaces:** +``` +fn ty_variance_of(ak: int, slot: int) -> int // 0 = 不变 / 1 = 协变(P3 首版落引擎;条目字段后挂引用) +fn sh_seq_term(elem: int, fixed_len: int) -> int // fixed_len = N(数组)或 -1(视图/切片);N 仍不入等价身份,仅入「固定性」位 +fn array_len_constraint_ok(a: int, b: int) -> int // 签名不变;补方向:视图→固定 且无法证 len==N ⇒ 0 +``` +**语义(spec §5.1)**:`[T;N] <: [T]`(更强长度约束 = 子类型);`[T] ⊄ [T;N]`(除非证 len==N);只读序列视图**协变**、可写视图**不变**;`&T`/`&mut T` 进参数链(mut 标记成为判定维度,替代 ty_shadow.cr:224-227 的「不入链」)。 + +- [ ] **Step 1: 用例(红)8 例**:方向 4(`[int;3] <: [int]` = 1 / `[int] <: [int;3]` = 0 / `[int;3]` vs `[int;4]`:等价判 1(N 不入身份)+ 常量档拒 0 / slice vs slice 同元素 = 1);变型 2(只读协变通过、可写不变拒绝);**N 读取面守门 1**(身份路径零 N:`type_equal` 对异长同结构仍 true,且 `array_len_constraint_ok` 是唯一拒绝来源);**动态档登记 1**(切片运行期长度不可证 ⇒ 视图→固定拒绝,不给 -1 当 0)。**为何 8 例**:每条方向/变型规则正负控成对 + N 边界双钉 + 三态纪律。 +- [ ] **Step 2: 实现**(变型表首版仅含 `AK_SEQUENCE`/`AK_REF`/`AK_PTR` 三构造子,其余默认不变;`tt_list_same` 改为按 `ty_variance_of` 递归,**不得**改变同形时的既有结果——同形判定仍是 1)。 +- [ ] **Step 3: 行为探针(本任务主判据)**:pA 必须由 rc=0 翻为 **rc=1 + 专属措辞**(收紧清单第 1 条);pB/pC 语义保持(pB rc=0、pC rc=1 TF01);新增 pE(`&mut T` 形参接 `&T` 实参拒绝 / 反向拒绝);嵌套位沿用 f2.* 探针形态(`[[int;3];2]`、`G<[int;3]>`、`*[int;3]`、`(int,[int;3])`)。 +- [ ] **Step 4: 判据**:`selftest-types` 101 → **≥109**;影子对拍复跑(预期零差异——N 面影子已失明,**不得**以对拍充当 N 面证据,findings §11);全语料 report-only(收紧面清单)→ 自举源码 10 处 `[T;N]` 复验零改造;ELF canary IDENTICAL;全回归 + 自举链。 +- [ ] **Step 5: 提交**:`feat: R2 P3 Task 1——定长退役收口(array<:slice 方向规则 + 只读协变/可写不变变型表 + N 读取面守门;修 slice→数组 静默放宽)` + +## Task 2(P3b,P2b 后置):横切接口——序列/可索引/可迭代/product 形状判定接线 + +**Files:** Modify `src/compiler/checker.cr`(索引/切片/字段/迭代路径:`get_type_kind == TYP_ARRAY/TYP_SLICE` 直比 → `iface_satisfies`)· `src/compiler/ty_shadow.cr`(形状项构造)· `src/compiler/type_selftest.cr` + +**依赖**:P2b 注册表的 `iface_satisfies` 与横切轴条目(交接契约 ①)。若 P2b 只落本质轴操作许可(lead 描述的窄版本),本任务 Step 1 先补横切轴条目再接线,并在报告登记范围扩大。 + +- [x] **Step 1: 形状条目细化(spec §2.2 的「待细化」)**:`序列接口` ⟺ `sequence<⊤>`;**只读/可写两形状**(只读 = 协变、可写 = 不变,与 Task 1 变型表同源);`可索引`/`可迭代`/`product` 三条目首版(形状项 = 对应 AK 的 ⊤ 实例化)。——**已交付(2026-09-12)**:六条形状项 = `iface_registry.cr` 横切形状段(`sh_shape_seq` / `sh_shape_seq_ro` / `sh_shape_seq_rw` / `sh_shape_indexable` / `sh_shape_iterable` / `sh_shape_product`)。**两处按实测重定**:① 「`sequence<⊤>`」按 Task 0 §1.3 勘误落 **`⊤ₖ` 类别形**(不变槽遇 ⊤ 落 -1);② 只读形状 = 序列本体 ∪ **只读视图**(`&[⊤ₖ(SEQ)]`,协变槽判定)——「只读 = 协变」在**视图面**兑现,可写形状 = 本体面 + 拒绝只读视图,可写**视图**形状不可表达(-1,登记)。**名字面**:生产路径不注册(注册名 = 驻留 ni,初始化路径 `str_intern` 会动 `.ccr` STR 段 = A.3-② 硬约束)⇒ 消费者走无名字入口 `iface_satisfies_term`;命名消费语法待裁决。 +- [x] **Step 2: 用例(红)6 例**:数组/切片满足序列接口(正);int 不满足(负);只读形状接受数组与切片、可写形状拒绝只读视图;`可索引` 对字符串(TI_STR 索引已支持,checker.cr:2619-2633)正控;接口形状项不可展开 → -1(不得当 0)。——**已交付**:15 例(`x2.*`,两路咬合:形状判定 + 两条**全类型行枚举**等价;含非同一比较断言防快路径冒充判定、零 `str_intern` 守门、注册/复位泄漏守门)。 +- [x] **Step 3: 消费点接线**:索引(checker.cr:2581-2636)/切片(:2586-2602)的 `get_type_kind` 直比改为「形状满足 + 元素项取出」;**行为保持**(既有接受/拒绝集合不变,仅来源换位——如出现差异按收紧清单登记)。——**已交付**:索引兜底门 → 可索引形状(-1 回落许可表);range 分支 → 序列形状 ∧ 固定性位(取自序列项 b 槽,`sh_seq_fixed_len_of_ti`)。结果三分支原地保留(A.2 #16-18)。**差异 = 0**(收紧/放宽皆 0;12 例老/新两二进制逐例同 + 全语料 check 面零差异)。 +- [x] **Step 4: 判据**:`selftest-types` ≥109 → **≥115**;影子对拍复跑(站点 8/5/7 主流量:**判定数应不变**,agree 全绿);行为探针:数组/切片/字符串索引全部正例 + 负例(`int` 索引拒绝);ELF canary IDENTICAL;全回归。——**已交付(按 P3a/P3b-T0 后基线重定阈值)**:`selftest-types` 304 → **319/319**;影子对拍全计数 0(判定数变化逐条归因 = 新源码行自身站点);新套件 12 例挂 `run.sh`;ELF canary `95084e7b…d475` + `.ccr` 逐字节同;全回归 + 自举链 + 突变控制。 +- [x] **Step 5: 提交**:`feat: R2 P3 Task 2——横切接口接线(序列/可索引/可迭代/product 形状满足判定取代 TYP_* 直比;只读协变/可写不变两形状)` + +## Task 3(P3a):穷尽性真判定——match 补集空性 + 反例值 + +**Files:** Modify `src/compiler/checker.cr`(`EXPR_MATCH` 分支:模式项收集 + `ty_exhaustive` 判定 + 诊断)· `src/compiler/ty_shadow.cr`(变体项)· `src/compiler/type_selftest.cr` · New `tests/selfhost/test_match_exhaust.py` + +**语义(spec §5.4)**:穷尽性 = 补集空性 ⇒ `ty_exhaustive(domain, patterns)`;反例给**具体值**(未被覆盖的变体名)。**域限定**:仅枚举 scrutinee 判穷尽(域 = 变体集);int/string/bool/char 字面量模式与通配的域不判穷尽(**显式登记**:非枚举域不判,不得假装穷尽或假装不穷尽);`-1`(未知)**不得**升为「不穷尽」硬错误。 + +- [ ] **Step 1: 模式项收集**:臂模式 → 项(枚举模式 = `sh_variant_term`;绑定 ident = 通配但**不绑定值域**;通配 = ⊤);scrutinee 类型 → 域项(`sh_enum_domain_term`,Task 0)。 +- [ ] **Step 2: 用例(红)8 例**(引擎层,`type_selftest.cr`):全变体覆盖 = 1 / 缺一变体 = 0 + witness = 该变体 / 通配吸收 = 1 / 单变体枚举 = 1(无臂 = 0)/ 域不可展开(`AK_NAMED` 不展开)= -1(不得当 0)/ 空枚举域 = 0(无值可覆盖,登记语义)/ 重复臂 → 补集已空(`EC_TM_REDUNDANT` 同源判据)/ payload 变体与 tag 变体混合。**「非枚举域不判穷尽」是 checker 侧登记项**(不调引擎),验证在 Step 4 行为集,不占引擎用例。 +- [ ] **Step 3: 硬错误门(report-only 先行)**:`EC_TM_EXHAUST`(9003)入 `run_frontend` 硬名单前,全语料 report-only;清单若空 → 开门(main.cr:146-158 追加一行)。**冗余臂(9004)同期评估**(同族判据,可同批)。 +- [ ] **Step 4: 行为覆盖集**:新增 `tests/selfhost/test_match_exhaust.py`(≥8 例:穷尽过 / 缺臂拒 + 码 + **无产物** / 通配过 / 嵌套枚举 / payload 变体 / 空枚举边界 / 非枚举域不报穷尽 / 泛型枚举实例),挂钩 `src/ci/run.sh` selfhost-tests。 +- [ ] **Step 5: 判据**:`selftest-types` ≥115 → **≥123**;镜像回归:`test_interp_float.py`(3 变体全列)、`test_interp_parity.py`、`test_native_memory.py` 全绿(**这些是唯一既有 match 语料**);ELF canary IDENTICAL;全回归 + 自举链。 +- [ ] **Step 6: 提交**:`feat: R2 P3 Task 3——match 穷尽性真判定(补集空性 + 具体变体反例;仅枚举域,非枚举显式登记)+ TM03 硬门 + 覆盖集` + +## Task 4(P3a):联合/可选——enum = sum 项、`T?` = T ∪ null、退役内建 Option 注册 + +**Files:** Modify `src/compiler/checker.cr`(:962-975 退役;`EXPR_TRY` :2871-2888 去名字依赖)· `src/compiler/parser.cr`(`T?` 目标形态)· `src/compiler/ty_shadow.cr`(null 项)· `src/compiler/type_selftest.cr` · New `tests/selfhost/test_optional.py` + +**决策点(实施前须定,语义争议 → 停下上报,spec 裁决 6)**:`null` 的类型承载。spec §5.4 写「`T?` = `T ∪ null`」,但 §1.2 原生八员**无 `null`**。候选:**(a) 新增原生原子 `AK_NULL`(原生九员,值域单点)**——推荐(`None` 值、`T ∪ null` 直接成立、与 AK_UNIT 不相交);(b) `null` 归 `AK_UNIT`(值域混淆,不推荐);(c) 仅保留枚举形态(= 不做此项,与 spec 相悖)。**裁定前不实施本任务 Step 2+**。 + +- [ ] **Step 1: 用例(红)6 例**:`T?` 的项 = `T ∪ null`(`ty_equiv`)/ `None` 满足 `T?`(子类型)/ `Some(1)` 满足 `int?` / `null` 与 `int` 不相交(`ty_disjoint`)/ 联合在 match 域上的穷尽性(`T?` 两分支:有值 + None)/ 退役后 `Option` 名字不再被 bultin 注册(行数对照)。 +- [ ] **Step 2: 实现**:`Some`/`None` 在无用户 `enum Option` 时仍可解析(parser.cr:418-431 的名字路径接 `T?` 的联合项);`EXPR_TRY` 改按**类型项结构**(`AK_SUM`/联合含 null)解包,废掉基名字符串 `"Option"`/`"Result"`(checker.cr:2879 的 `base_name ==` 判断)。 +- [ ] **Step 3: 判据**:`selftest-types` ≥123 → **≥129**;`.ccr` **预期变**(退役一个命名行 → 行号位移)→ 实测报告 + `test_ccr_v7` 结构性断言全绿 + 冷缓存对比;ELF canary IDENTICAL(若变 → 停下上报);`test_optional.py` ≥8 例(含解释器/ELF 双路径 rc);全回归。 +- [ ] **Step 4: 提交**:`feat: R2 P3 Task 4——可选 = 联合(T? = T ∪ null;退役内建 Option 注册与 EXPR_TRY 名字解包)` + +## Task 5(P3a 主 + P3b 半):泛型约束——保留/实例化判定/monomorph 类型项化 + +**Files:** Modify `src/compiler/parser.cr`(:1533/:1575 结构/枚举泛型约束不再丢)· `src/compiler/checker.cr`(声明处约束登记 + `ty_sub(实参, 约束)` 实例化判定)· `src/compiler/monomorph.cr` + `src/compiler/ir_gen.cr:1497-1513`(实例键类型项化)· `src/compiler/type_selftest.cr` + +- [ ] **Step 1: F4 同族复核(TODO #21)**:`infer_gen_call` 的形参链导航(`ast_data(pn)`)在后续形参上取到 `gparam(T)` 而非声明类型——本任务先修此(否则实例化判定拿不到正确的实参类型);用例正/负控各一。 +- [ ] **Step 2: 结构/枚举泛型约束落地**:`parse_generics_into` 的约束不再写 dummy(parser.cr:1533/1575),登记到结构/枚举侧表(照 `g_generic_constr` 先例,索引空间分家);声明处检查 = 实例化点(`Box[T: I]` 的 `Box[P]`)。 +- [ ] **Step 3: 实例化判定引擎化**:`T: I` 检查 = `ty_sub(实参项, 约束项)`(约束为**类型项**;本质轴形状用引擎、用户接口形状待 P2b 的 `iface_satisfies` ⇒ **本步本质轴部分 P3a 可做,用户接口部分 P3b**);失败给**非空反例**(`tt_witness`),诊断含反例值;`-1` 不得当 0。 +- [ ] **Step 4: monomorph 类型项化**:实例键由「调用实参名串(非原生 → `"int"` 兜底)」改为「**类型项结构哈希**(含命名/泛型应用实参真身份)」;`gen_build_subst` 名字替换升级为类型项替换(或按项哈希生成规范名)。**产物面**:实例 mangled 名若变 → SYM/`.ccr` 变(允许、实测报告;ELF 无 symtab ⇒ 预期不变);去重正确性探针:两个**不同结构体类型**实参调用同一泛型函数 → 必须是两个实例(今天可能折叠——**未实测**,本步实测并登记)。 +- [ ] **Step 5: 判据**:`selftest-types` ≥129 → **≥137**;`MAX_GENERICS=4`(ast.cr:115)解除或显式登记(二选一,记录理由);行为探针:约束满足/违反(含反例文本)/结构泛型约束/双类型实参实例分离;影子对拍复跑 + 全回归 + ELF canary IDENTICAL + `.ccr` 实测报告 + 自举链。 +- [ ] **Step 6: 提交**:`feat: R2 P3 Task 5——泛型约束(结构/枚举约束不再丢 + 实例化 ty_sub 判定 + 反例;monomorph 实例键类型项化)+ F4 形参链修复` + +## Task 6(P3b,P2b 后置):impl 契约——签名类型项化 + 满足判定接引擎 + mangling 退役 + 覆盖集 + +**Files:** Modify `src/compiler/parser.cr`(:1619-1721 `interface` 方法签名:裸 `TY_*` → 类型节点/ti;≥16 方法 / ≥8 参数上限的处理)· `src/compiler/checker.cr`(:844-904/:1746-1807 + 方法解析 :2103-2144 名字拼接 → 条目查表)· `src/compiler/ty_shadow.cr`(接口形状项)· New `tests/selfhost/test_impl_iface.py` + +**依赖**:P2b 注册表(条目 + 满足关系 + `iface_satisfies`)。**mangling 废弃**(spec §2.3/§5.3,无兼容窗口 = 裁决 5)。 + +- [x] **Step 1: 签名类型项化**:`interface I { fn m(a: T) -> U; }` 的 T/U 从裸 `TY_*` 改为类型节点解析(`res_type_node`)——**现状只能写基类型**(parser.cr:1671/1685);方法上限 16 / 参数上限 8(dyn_arr.cr:143-149)**解除或显式登记**。——**已交付(2026-09-12)**:方法条目新增类型**节点**槽(`OFF_IFM_PARAM_NODES` / `OFF_IFM_RET_NODE` / `OFF_IFM_SELF_MODE`;`ESZ_IFMETHOD` 88→168、`ESZ_IFACEINFO` 1432→2712——**不入 `.ccr` 序列化**,实测逐字节同)+ parser 写点;**裸码槽保留**(S6 站点返回型映射值域 = 映射层编码,P2b Task 6 已钉);参数裸码槽自本批起**零读者**(登记)。**上限显式登记保留**(`MAX_IFACE_METHODS=16` / `MAX_IFACE_METHOD_PARAMS=8` 单源常量;超限 = **硬错 rc=1** 非静默截断,新套件两例钉死;解除面 = 方法表迁侧表,按 B.4-7 口径「先加护栏、再评估解除」)。 +- [x] **Step 2: 覆盖集(spec §5.3 要求新建)**:`test_impl_iface.py` ≥10 例——正向(满足)/ 反向(缺方法、参数数不符、参数类型不符、返回类型不符)/ 多接口 / 与泛型约束交互(`fn f[T: I]`)/ 错误例(未定义接口)/ 固有方法不受影响(`test_impl.py` 7 例保持绿)。——**已交付**:**20 例**(正向 **双站点**同证(实例化点注解 + 函数调用点)7 + 泛型体内方法调用运行面 2 + 反向签名五面 7 + 错误例 1 + 登记面 1 + 上限硬错 2)。**双站点是硬要求**(Task 0 M4 教训实测):只走实例化点的正例在突变下不咬合(本轮一度空转,已修)。解释器面登记:泛型体内方法调用 `corec run` 解析不支持(旧二进制同 rc=1)⇒ 该 2 例走 check + ELF。 +- [x] **Step 3: 满足判定接引擎**:`check_iface`/`check_impl_for` 改走 `iface_satisfies`(形状项包含判定);方法解析(`type_has_method` 名字拼接,checker.cr:873-879)改「类型项 + 条目」查表;泛型约束的方法调用路径(:2103-2144)同步。——**已交付(按实测重定两处)**:① 形状项 `sh_iface_shape_term` = 方法集(product of fn,b 槽 = 方法名)**已建**;判定 = **逐成员包含**(方法名表查询 + 接收者模式显式比较 + 签名 fn 项经引擎结构比较原语 `tt_list_same`)。**整形状 `ty_sub` 路由不可用**(实测两条:引擎对 AK_NAMED 不展开 ⇒ 命名行实参恒 -1;product 比较是结构相等非子集包含 ⇒ 带额外方法的实现方误拒)——登记为未覆盖面。**逐成员签名比较不得用 `ty_sub`**:引擎把不变槽的「确定不同」上抛 **-1** ⇒ 签名不符退化「不判」= 静默通过面复活(突变 M3b 实证 8 自测 + 3 行为例红);签名项为规范形(N 不入项 / 泛型应用展开 / 命名行按名)⇒ `tt_list_same`(0/1 全域)即正确判据。② `check_iface` = 与 `iface_user_satisfies` **同一核心**(`iface_user_satisfies_ii`,同源由构造保证);`check_impl_for` 逐参/返回比较**同改签名项**(诊断码/措辞保留);泛型约束方法调用路径 → **实例化时查表解析**(`CALL_FLAG_IFACE_METHOD` + monomorph 克隆分支)——**旧态产物运行 rc=139**(合成 "T.m" 串 + 文本替换 ⇒ 调用目标悬空),本批修。 +- [x] **Step 4: 判据**:`selftest-types` ≥137 → **≥141**;`test_impl.py` 7/7 不回归 + 新套件全绿 + `src/ci/run.sh` 挂钩;影子对拍复跑;全回归;ELF canary IDENTICAL;`.ccr` 实测报告(接口目前零序列化 ⇒ 预期不变)。——**已交付(阈值按 P3a/P3b 后基线重定)**:`selftest-types` 319 → **333/333**(+14 `ifc.*`);新套件 **20/20** 挂 `run.sh`;`test_impl` 7/7 + 六套件全绿(`test_iface_satisfies` 按台账改 1 例后 17/17);ELF canary `95084e7b…d475` IDENTICAL + `.ccr` 逐字节同(88943B/130974B——接口零序列化 + 零新 `str_intern`);影子 30628/30628(全计数 0,站点同报);同源对拍 72 档零差异;五 CI job;更宽 selfhost 45/45 + `test_backend_bootstrap` rc=0;突变控制 4 条(M4 = 行为级独有牙,登记)。 +- [x] **Step 5: 提交**:`feat: R2 P3 Task 6——impl 契约(接口签名类型项化 + iface_satisfies 接引擎 + mangling 退役)+ 覆盖集`(提交见 §11 / 本批报告) + +## Task 7:收官(回归 + 判据 + 收紧清单 + 文档) + +- [ ] **Step 1: 全量回归**(`src/ci/run.sh` selfhost + bootstrap 全档) +- [ ] **Step 2: 零变化复验**:ELF canary IDENTICAL(clean-cache,开/关两态)+ `.ccr` 变更面实测报告(逐任务列)+ 影子对拍终态(差异/站点覆盖/回落计数同报) +- [ ] **Step 3: 自举链**:`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 `run 'fn main()->int{return 42;}'` = 42 +- [ ] **Step 4: 收紧清单汇总**:本批「旧接受 → 新拒绝」逐条(预期 ≥1 条:pA 切片→数组;标「P2a 引入的静默放宽在本批关闭」) +- [ ] **Step 5: 文档**:spec §9 P3 行标 ✅ + 未覆盖面更新(符号档登记);TODO:`fixed-array-retire`(TODO.md:446)未办句划销、#19 的「P3 面遗留」收口、#26 判据继承、新增登记(非枚举域不判穷尽 / 空递归保守 / MAX_* 上限处置);findings 文档追加「P3 后复跑」节 +- [x] **Step 1: 全量回归**(`src/ci/run.sh` selfhost + bootstrap 全档)——收官实测:五 job(selfhost-tests / bootstrap-tests / suite / check / full-bootstrap)rc 与逐档计数见收官报告 §1;全量套件枚举 **42 selfhost + 7 bootstrap** 逐档记录 rc;`check` job rc=1 = **既有 red 划界**(2 条 TF01 误报,非本批引入);枚举**暴露 1 处新增 red**(`test_backend_bootstrap.py` — T5 漏改 project-mode 清单 ⇒ 33×N06 静默,**已修**,见**附录 B.6**)⇒ 终态 **49/49 rc=0** +- [x] **Step 2: 零变化复验**:ELF canary IDENTICAL(clean-cache)`95084e7b…d475` ✓;`.ccr` 变更面逐任务列(T0/T1/T3 不变;T4 = STR 段整串退役 −10B;T5 = `generics_test` +370B 反折叠而 `ptr_arith` 不变)✓;影子对拍终态 `decisions=30128 agree=30128`(T5 的 30338 → 收官 30128,−210 = 附录 B.6 修复所致,归因见 B.3)全计数 0、站点覆盖同报 ✓ +- [x] **Step 3: 自举链**:`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42 ✓(收官复验,见报告 §2) +- [x] **Step 4: 收紧清单汇总**:**18 条**(T0 0 · T1 6 · T3 6 · T4 3 · T5 3)+ 诊断面新增 1(TM04 软);放宽 **8 条**(T4 7 + T5 1)+ 实例名忠实化 6(T5)——统一台账 = **附录 B.2** +- [x] **Step 5: 文档**:spec §9 P3 行标 ✅(**P3a**)+ 符号档登记;TODO = #34(P3a 落地)+ `fixed-array-retire` 未办句划销 + #19「P3 面遗留」收口 + #26 判据继承 + 新登记(非枚举域不判穷尽 / 空递归保守 / MAX_* 上限处置);findings §13「P3a 后复跑」 +- [x] **Step 6: 提交**(路径限定) + +## Task 7b:P3b 收官(回归 + 判据 + 统一台账 + P3 总状态 + P4 交接;2026-09-12) + +> P3b = Task 0(契约回补 `9aa1786c`)/ Task 2(横切接口 `44100ba4`)/ Task 6(impl 契约 `e838f18b`)三件;本 Task = 三份任务报告(`.superpowers/sdd/p3b-task{0,2,6}-report.md`)之上的**阶段收官**——全量回归 + 判据复验 + 台账统一 + P4 交接包(附录 C)。判据口径继承:**本轮实跑 vs 继承基线逐条标注**(收官报告 §1/§2)。 + +- [x] **Step 1: 全量回归**——五 CI job(selfhost-tests / bootstrap-tests / suite / full-bootstrap / check)rc + 逐档计数;全枚举 selfhost 45 档 + bootstrap 7 档逐档 rc;`check` job rc=1 = **既有 red 划界**(恰 2 条 TF01 误报 `lits_copy`/`ty_memo_slot_no_grow`,非本批引入)。✅ 实测见收官报告 §1。【**2026-09-13 更新**:两条已修(TODO #40)⇒ `check src/compiler` rc=0,该划界口径作废(行保留为当期实跑记录)。】 +- [x] **Step 2: 判据复验**——`clean-cache` → ELF canary `95084e7b…d475` IDENTICAL;`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42 + `corec3 --help` rc=1(既有约定);`.ccr` 面(`ptr_arith`/`generics_test`)。✅ 实测见收官报告 §2。 +- [x] **Step 3: 统一台账**(P3b 三件聚合;下表 = 收紧 **10** / 放宽 **0** / 崩溃修复 **1**;全语料零命中,逐任务同源对拍零差异) +- [x] **Step 4: P3 总状态 + 未开工清单**(见本节末) +- [x] **Step 5: P4 交接包**(**附录 C**:载体盘点 + 加段四面 + 翻转钉 + 待裁决面) +- [x] **Step 6: 文档 + 提交**(路径限定)——TODO #39(本批落地)+ spec §9 P3 行 P3b ✅ + 本计划状态表/附录 C;本报告不入提交(未跟踪面,同前例)。 + +### P3b 统一收紧台账(T0 6 · T2 0 · T6 4 = **收紧 10 / 放宽 0 / 崩溃修复 1**) + +**T0(`9aa1786c`;旧 = P3a 收官 `aa54d26a` 构建,6 例逐例实测)** + +| # | 用例(`test_iface_satisfies.py`) | 旧 → 新 | 处置 | +|---|---|---|---| +| T0-1 | `inst_missing_method_rejected` | check rc=0 零诊断 → rc=1 + `error[TG02]` | 结构/枚举实例化点 `T: I` 缺失方法 | +| T0-2 | `inst_count_mismatch_rejected` | rc=0 零诊断 → rc=1 + TG02 | 参数计数不符 | +| T0-3 | `inst_ret_code_mismatch_rejected` | rc=0 零诊断 → rc=1 + TG02 | 返回码不符(函数调用点本就拒 = 同措辞) | +| T0-4 | `inst_enum_missing_method_rejected` | rc=0 零诊断 → rc=1 + TG02 | 枚举实例化点 | +| T0-5 | `inst_multi_method_partial_rejected` | rc=0 零诊断 → rc=1 + TG02 | 多方法接口半实现 | +| T0-6 | `inst_second_iface_missing_rejected` | rc=0 零诊断 → rc=1 + TG02 | 两接口隔离 | +| (重钉) | `test_generic_constr.py::struct_iface_constr_unjudged` → `..._violated` | 同台账 | 同一边界的旧「不判」钉 | + +**T2(`44100ba4`)**:**收紧 0 · 放宽 0**——换位批(行为保持);证据 = 12 例行为探针旧/新两二进制逐例同 + 全语料 72 档 check 日志 `diff -r` 空 + 影子旧×新同值。台账条目 = **空**。 + +**T6(`e838f18b`;旧 = Task 2 基线 `/tmp/p3b2_pre_mut_corec`,逐例实测;新套件 20 例:旧 14/20 → 新 20/20)** + +| # | 用例(`test_impl_iface.py`) | 旧 → 新 | 处置 | +|---|---|---|---| +| T6-1 | `neg_named_param_type_mismatch` | check rc=0 零诊断 → rc=1 + TG02 | 参数类型入判定(旧态裸码塌缩不可比) | +| T6-2 | `neg_named_ret_vs_native` | rc=0 零诊断 → rc=1 + TG02 | 返回型按类型项(取代编码相等) | +| T6-3 | `neg_apply_arg_mismatch` | rc=0 零诊断 → rc=1 + TG02 | 泛型应用实参(规范形展开后可比) | +| T6-4 | `neg_receiver_mode_mismatch` | rc=0 零诊断 → rc=1 + TG02 | 接收者模式(旧态两侧皆码 0) | +| (重钉) | `test_iface_satisfies.py::inst_encoding_limit_named_ret_pinned` → `..._rejected` | 同台账 | 同返回型面的既有钉(T0 登记「解锁 = T6 Step 1」) | +| (**崩溃修复**,非收紧) | `pos_generic_body_call_inherent` / `_impl_for` | build rc=0 + 产物运行 **rc=139** → 运行 rc=7 / rc=9 | mangling 退役收益:泛型体内方法调用实例体调用目标悬空(旧态合成 `"T.m"` 串) | + +**语料扫面证据(逐任务;站点覆盖与差异数同报)** + +| 任务 | 同源对拍(旧二进制 × 新源 vs 新二进制 × 新源) | 影子判定数 | 新能力面语料命中 | +|---|---|---|---| +| T0 | check/shadow/dump 三面 `diff -r` **空** | 30271 / 30271,全计数 0 | 接口/`T: I` 零命中 | +| T2 | check 面 `diff -r` **空**(rc 分布 33×0 / 39×1 两侧同) | 30400 / 30400;**旧×新同值** | 形状面零命中(行为保持批) | +| T6 | check 面 `diff -r` **空**;shadow dump 空 | 30628 / 30628;**旧×新同值** | `interface`/`impl I for T`/`T: I` 生产用法零命中 | + +> 影子判定数逐任务增量(30128 → 30271 → 30400 → 30628)**均为新增源码自身的站点**(旧二进制跑同一新源得完全相同的数)——「零差异」≠「面收敛」的声明继承 P3a(B.3);P3b 新能力面正确性由各任务的定向探针(T0 17 例 / T2 12 例 / T6 20 例)+ 用例表(+45 `isat.*`/`x2.*`/`ifc.*`)+ 突变控制(4+3+4 条)承担。 + +### P3 阶段总状态(P3a + P3b) + +- **交付面**:五项目标能力**全部接通**——定长退役(T1)· 序列接口/长度约束(T2)· 穷尽性/联合/可选(T3/T4)· 泛型约束(T5 + T0b 的 `T: I` 半边)· impl 契约(T6)。`selftest-types` **95 → 333**;新套件 **6 个**(P3a 3 + P3b 3)= `test_match_exhaust` 16 · `test_optional` 12 · `test_generic_constr` 15 · `test_iface_satisfies` 17 · `test_xcut_iface` 12 · `test_impl_iface` 20(全挂 `run.sh`)。 +- **收紧/放宽总账**:P3a 收紧 **18** / 放宽 **8** / 诊断面新增 1(TM04 软)/ 实例名忠实化 6 / 挂死修复 2 端;P3b 收紧 **10** / 放宽 **0** / 崩溃修复 1 ⇒ **P3 合计:收紧 28 · 放宽 8 · 软诊断面 +1 · 挂死与崩溃修复 3 端**(全语料零命中;自举源码零改造)。 +- **判据面**:ELF canary `95084e7b…d475` **全阶段 IDENTICAL**(类型层改动零泄漏发射面);`.ccr` 变更面 = T4 `ptr_arith` STR 段 −10B + T5 `generics_test` +370B(反折叠),P3b 三件**零变更**;自举链 `corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42 全程稳定。 + +**未开工 / 未实现清单(显式;P3 未覆盖面终态)** + +1. **iterable 面接线**:`EXPR_FOR` 现状**零类型检查**(`checker.cr:2810-2821`——`infer_expr(iter)` 返回值被丢弃、循环变量恒 `TI_INT`;T2 登记项④)⇒ 接线 = 新判定 + 可能的新硬门,须 report-only 专批。 +2. **形状命名消费语法**(`T: 可索引`)+ **`.ccr` STR 段增长裁决**(A.3-② 后续;两者同一裁决的两面,见附录 C.6-①)。 +3. **整形状 `ty_sub` 路由**(T6 实测不可用:引擎 `AK_NAMED` 不展开 ⇒ 命名行恒 -1;product 比较 = 结构相等非子集包含)⇒ 形状项作**签名载体**由逐成员判定消费(`tt_list_same`)。 +4. **上限族保留**:`MAX_GENERICS=4`(ast.cr:115)· `MAX_IFACE_METHODS=16` / `MAX_IFACE_METHOD_PARAMS=8`(dyn_arr.cr:162/167,硬错护栏已立 = 非静默截断)· `MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES=16`(TODO #35)——「先加护栏、再评估解除」。 +5. **TODO #38 名义 vs 结构裁决**(用户轴口径;维护者裁——采纳会翻转 5+ 既有钉,见 C.6-②)。 +6. **运行期可选表示统一**(B.7 一等条目:`Some` 臂双路径 SIGSEGV rc=139,check/build rc=0 零诊断)——P3b 未动;修法与 P4 的 TYPE 段同批裁(C.6-⑤)。 +7. **TODO #32**(`EXPR_LET` 无任何兼容检查;实测 `x: [int;4] = [1,2,3]` / `y: int = x`(x 为 `int?`)rc=0)。 +8. **TODO #35**(前端枚举表写入侧无护栏:≥17 变体越界写;check 静默 rc=0 / build rc=1 在读回闸)。 +9. **其余未覆盖面(登记,非漏放)**:可写视图形状不可表达(不变槽遇 ⊤ 落 -1)· 索引侧「须 int」位缺失(A.3-⑧)· dyn 与字段面未接线(A.2 #23/#28)· 符号档长度约束 = **VC 义务,不实现** · `Box[S]` 命名实参 vs 原生约束 = -1 不判(B.4-3)· 接口泛型形参 `interface I[T]` 被 parser 丢弃 · 变参签名不可表达 · `save_func_gen_constrs` 稀疏零初值(B.4-1,未触发)· bootstrap `&&`/`||` 不短路根因(B.4-12)· 空递归保守 -1 · 非枚举域不判穷尽 · 空枚举域 = 空洞穷尽 · `never` 语义裁决(B.4-2)· 性能面 `(ak → entry)` 快表无必要(实测)。 + +## 实施状态总表(2026-09-12,P3b 收官更新) + +> **P3a = Task 0/1/3/4/5 已交付**(提交链 `61d3a8b4`→`ef61f002`→`e9818d62`→`c8c7731b`→`9c0a83ec` + 收官提交);**P3b = Task 0/2/6 已交付**(提交链 `9aa1786c`→`44100ba4`→`e838f18b` + Task 7b 收官提交)⇒ **P3 全阶段(P3a + P3b)收官**。P3b 开工期阻塞/接线/登记面 = **附录 B**;P4 开工必读 = **附录 C**(载体交接包)。 + +| 任务 | 状态 | 提交 | 判据要点 | 报告 | +|---|---|---|---|---| +| Task 0 引擎展开层 | ✅ 已交付 | `61d3a8b4` | 纯新增 502/0;用例 212→**229**;零行为变化(72 档三面 diff 空) | `.superpowers/sdd/p3-task0-report.md` | +| Task 1 定长退役收口 | ✅ 已交付 | `ef61f002` | 用例 229→**249**;收紧 **6** / 放宽 0;修「切片→固定长」静默放宽 | `.superpowers/sdd/p3-task1-report.md` | +| Task 0b(P3b)iface_satisfies 契约回补 | ✅ 已交付 | `9aa1786c` | 三态契约 + 轴 A>C>B 分派 + 实例化点 `T: I` 真判定;收紧 **6** / 放宽 0(全语料零命中);ELF/`.ccr` 逐字节同 | `.superpowers/sdd/p3b-task0-report.md` | +| Task 2 横切接口 | ✅ 已交付(P3b) | `44100ba4` | 用例 304→**319**;六形状项(`⊤ₖ` 形 + 只读/可写分野)+ 索引/切片消费点换位;**收紧 0 / 放宽 0**(行为保持:12 例老新两二进制同、全语料零差异);ELF/`.ccr` 逐字节同 | `.superpowers/sdd/p3b-task2-report.md` | +| Task 3 穷尽性真判定 | ✅ 已交付 | `e9818d62` | 用例 249→**263**;收紧 **6** + 软 1(TM04);TM03 硬门;新套件 16 例 | `.superpowers/sdd/p3-task3-report.md` | +| Task 4 联合/可选 | ✅ 已交付 | `c8c7731b` | 用例 263→**279**;收紧 **3** / 放宽 **7**(含挂死修复 2 端);新套件 12 例 | `.superpowers/sdd/p3-task4-report.md` | +| Task 5 泛型约束(P3a 半边) | ✅ 已交付 | `9c0a83ec` | 用例 279→**288**;收紧 **3** / 放宽 1 / 实例名忠实化 6;新套件 14 例;`T: I` 半边 = P3b | `.superpowers/sdd/p3-task5-report.md` | +| Task 6 impl 契约 | ✅ 已交付(P3b) | `e838f18b` | 用例 319→**333**;签名四维(参数/返回/应用实参/接收者模式)+ 形状项逐成员判定 + mangling 退役(**零 str_intern**)+ 泛型体内方法调用**崩溃修复**(旧 rc=139 → 正确值);收紧 4 条 / 放宽 0;ELF/`.ccr` 逐字节同 | `.superpowers/sdd/p3b-task6-report.md` | +| Task 7 收官(P3a) | ✅ 已交付 | 收官提交 | 全量回归(49/49)+ canary + 台账汇总 + 交接包(附录 B);**发现并修复 T5 的 project-mode 清单漏改回归**(附录 B.6) | `.superpowers/sdd/p3-task7-report.md` | +| Task 7b 收官(P3b) | ✅ 已交付 | 见收官报告 §7 | 全量回归(五 CI job + 46 selfhost + 7 bootstrap 逐档)+ 判据复验(canary/corec2·3/N06/冒烟)+ **统一台账(收紧 10 / 放宽 0)** + P3 总状态 + **P4 交接包(附录 C)** | `.superpowers/sdd/p3b-task7-report.md` | + +## 自检记录 + +- **spec 覆盖**:§5.1 定长/序列/长度约束 → Task 0/1/2;§5.2 泛型 → Task 5;§5.3 impl → Task 6;§5.4 穷尽性/联合/可选 → Task 3/4;§9 P3 行五项目标能力逐项有落点;`TODO.md:446` fixed-array-retire 的「未办 = 类型身份退役本身」由 Task 1 收口(枚举了 5 条读码事实,非猜)。 +- **占位符扫描**:无 TBD;两处**决策点显式登记**(Task 4 的 `null` 原子承载、Task 5 的 MAX_GENERICS 解除或登记)——均为"实施前须定",非「稍后填」。 +- **命名一致性**:`sh_struct_term`/`sh_enum_domain_term`/`sh_variant_term`/`sh_iface_shape_term`/`ty_variance_of`/`sh_seq_term`/`iface_satisfies` 在任务间一致;`iface_satisfies` 为 P2b 交接契约名。 +- **数字口径**:95 = 实测(`grep -c "fails = fails + ts_check("` = 95);26,989/67 文件 = findings §11 实测;pA-pD 四探针 = 本计划实测(2026-09-11);其余「≥N 例」为计划下限(可增不可减)。 +- **风险面**:① **展开层裁定**(Task 0 的「仅满足判定展开、等价不展开」边界)——裁错 → 判定全面漂移 + 推翻 P2a 基线(缓解:影子对拍逐任务归零 + 名义不变量用例);② **pA 类静默放宽**(已实测在库:切片→数组)——本批收紧即修,须走收紧清单 + report-only;③ 穷尽性硬错误影响用户 rc(缓解:语料零 match + report-only 先行);④ monomorph 实例键变更(`.ccr`/SYM 变化 + 折叠缺陷修复可能改变既有实例数——实测报告);⑤ **P2b 依赖项阻塞**(Task 2/6 + Task 5 半)——分批交付(P3a 先行、P3b 待 P2b)。 + +--- + +## 附录 A:R2 P2b 交接包(2026-09-11 收官实测;P3 实施前必读) + +**来源**:R2 P2b Task 1~6 报告(`.superpowers/sdd/p2b-task{1..6}-report.md`)+ 收官复验(提交链 `7f520dce`→`31143cd0`→`6d2790cd`→`a99ee825`→`7145eea5`→`30545da7` + 本附录所在收官提交)。P2b 的硬口径 = **保语义接线(零行为变化)**:同源双编译器对拍逐任务空(旧二进制 × 新源 == 新二进制 × 新源:21 档语料 + 自源 + 影子 + `.ccr` 四面)。⇒ 本附录的每一条「不接线」= **在该口径下不可能接线**,理由均为实测(探针/突变/全行枚举),不是判断。 + +**行号口径**:本附录行号 = P2b 末提交 `30545da7` 实测(`checker.cr` 3422 行);括注「←T4/T5 报告号」= 该任务报告的**接线前**行号(两报告间有 +10~+21 增行位移,勿混;定位请按内容)。 + +### A.1 P2b 实际接线面(P3 的「已就位」清单) + +| 面 | 站点(P2b 末行号) | 接线形态 | 任务 | +|---|---|---|---| +| 字面量定型 5 处 | `checker.cr:1906/1908/1909/1910/1911` | 内联 `if` → `iface_lit_ti(EXPR_*)`(短路顺序逐字保持;`EXPR_NONE` 转发行夹在中间未动) | T3 | +| 算术门(ANY) | `checker.cr:1969` | `iface_permits(iface_kind_of(lt), op) == 0 && …(rt)…`;表 ADD..MOD **只含 int/dex**(早退规则 `:1951/:1953/:1956/:1960` 留代码 = 结果规则) | T4 | +| 逻辑门(ALL) | `checker.cr:1984` | `… OP_AND … \|\| …`;表 AND/OR **恰 {int, bool}**(dex 为 0——`1.5 && true` 探针实测拒) | T4 | +| `if` 条件门(ONE) | `checker.cr:2298` | `IP_COND`(bool\|int) | T4 | +| `while` 条件门(ONE) | `checker.cr:2411` | `IP_COND_BOOL`(仅 bool;**不得与上合并**) | T4 | +| 索引兜底拒绝 | `checker.cr:2678` | 原 `EC_TK_INDEX` 调用被 `iface_permits(…, IP_INDEX) == 0` 包住;门体即原调用(**不是**「门 + 原调用」两条——`check_error` 无去重,T5 §3 裁决);可达集 = 表拒绝集(逐行枚举 `idx.gate_deny_covers_fallback` 钉死) | T5 | +| TY→TI 单表(8 站点) | `checker.cr:742`(res_type_node)/`:1397`(res_call_type)/`:1063`+`:1089`(hotpatch 两处)/`:1146`(extern)/`:1682`(形参)/`:1737`(返回)/`:2159`(iface 返回)——值全部经 `iface_registry.cr:259-270` 的 `ty_code_to_ti` | 单表给**值** + 站点给**域**(逐站显式排除项);域差异是**载荷**(T6 §6.2 突变 M2 实证:一刀切写法会静默改判定) | T6 | + +### A.2 未接线站点台账(31 条 = T4 的 14 + T5 的 17) + +> 「不接线」的三种实测理由分类:**结果规则**(该分支同时决定结果类型,非许可判定)、**无拒绝路径**(现状恒真,换成门 = 空转)、**谓词非类级位**(类级位表达不了逐行/逐名判定)。 + +#### A.2.1 二元 / 一元 / 条件面(T4:18 站点 − 4 接线 = 14) + +| # | 站点(P2b 末行号 ← 接线前) | 现状语义 | 不接线的实测理由 | P3 动作 | +|---|---|---|---|---| +| 1 | `:1949` ←`:1944` 组派发 `op ∈ ADD..MOD` | 选出算术族 | **结构判定**(不是许可):决定走哪一族;换表需先有「族」概念 | 横切轴(Task 2) | +| 2 | `:1951` ←`:1946` 串拼接 | `OP_ADD ∧ (lt/rt==STR) → STR` | **结果规则 + 早退**:表 ADD 格含 STRING 会在 ANY 门下误命中(如 `1 + 2` 一侧为串即成)⇒ 需**另设拼接位** = 计划位表外的新设计 | 拼接位独立建模 | +| 3 | `:1953` ←`:1948` `*T ± int → *T` | 结果 = 指针行 | 结果规则;PTR 进门 = 放宽(`*T + *T` 现状 TB01,负控实测) | — | +| 4 | `:1956` ←`:1951` `int + *T → *T` | 结果 = 指针行 | 同上 | — | +| 5 | `:1960` ←`:1955` `*T − *T → int` | 结果 = int | 结果规则(且只认 `OP_SUB`) | — | +| 6 | `:1972` ←`:1962` dex 支配 | 任一侧 DEX → DEX | 结果规则(门只管报不报错) | — | +| 7 | `:1973` ←`:1963` 兜底 | `return TI_INT` | 结果规则 | — | +| 8 | `:1975-1977` ←`:1965-1967` 比较 6 op | 恒 `TI_BOOL`、**不校验操作数** | **无拒绝路径**(登记面探针零拒绝) | 可比性判据后逐类收紧 | +| 9 | `:1936-1946` ←`:1931-1941` 赋值(`EXPR_BINARY`+`OP_ASSIGN`) | `type_compat_strict(lt, rt)` | **P2a 判定替换面**(引擎 + legacy 回落),非 ops 位集对象;`OP_ASSIGN` 位在表中不使用 | 随 P2a 面(P5 清偿) | +| 10 | `:1994-1996` ←`:1979-1981` NEG/NOT | 直接返回操作数类型 | **无拒绝路径**(`-"a"` rc=0,探针实测) | 数值性判据(NEG 只收数值、NOT 只收 bool/int) | +| 11 | `:1997-2017` ←`:1982-2002` `UOP_REF` | 任意操作数 → `alloc_type(TYP_PTR, …)` | 结果规则(构造类型行);全类许可 | — | +| 12 | `:2018-2029` ←`:2003-2014` `UOP_DEREF` | REF/PTR→内层;GPARAM→自身;**兜底透传** | 三分支皆结果规则;兜底 = **无拒绝路径**(`*x`(x: int)rc=0) | 兜底分支改拒绝 | +| 13 | `:2030` ←`:2016` 一元尾兜底 | 透传操作数 | 无诊断(未识别一元 op) | — | +| 14 | `:2437-2438` ←`:2411-2412` range 两端 | `st == TI_INT && et == TI_INT`(**只收 int**) | **无对应位定义**:最接近的 `IP_COND` 含 bool ⇒ 用之 = 放宽(`"a".."b"` 现状 TB01,实测);新造端点位 = 计划位表外设计 | 端点位(新位) | + +#### A.2.2 容器面(T5:18 候选 − 1 接线 = 17) + +| # | 面 | 站点(P2b 末行号 ← 接线前) | 现状语义 | 不接线的实测理由 | P3 动作 | +|---|---|---|---|---|---| +| 15 | 索引 | `:2621-2638` ←`:2611-2628` range 索引 | 数组→`TYP_SLICE`;否则 `return TI_UNIT` **静默** | `IP_INDEX_RANGE` 拒绝分支**无诊断** ⇒ 门恒真(探针 I14-I16:串/切片/int range 全 rc=0) | 落空加诊断(或定串 range 语义——语义裁决) | +| 16 | 索引 | `:2640-2650` ←`:2630-2640` `TYP_ARRAY`→元素 + **F2 越界** | 结果规则 + 既有诊断 | 计划明令原地保留(三分支做**不同的事**:F2 检查/元素/`TI_INT`) | — | +| 17 | 索引 | `:2651-2653` ←`:2641-2643` `TYP_SLICE`→元素 | 结果规则 | 同上 | — | +| 18 | 索引 | `:2654-2668` ←`:2644-2658` `TI_STR`→`TI_INT` + 字面量越界 | 结果规则 + 既有诊断 | 同上 | — | +| 19 | 字段 | `:2548-2552` ←`:2538-2542` `TYP_REF` 自动解引用 | 结果规则(落 `actual_ti`) | 无拒绝路径;改查表 = 改结果规则 | — | +| 20 | 字段 | `:2553-2556` ←`:2543-2546` `TYP_GENERIC_APPLY` 解包基型 | 结果规则 | 同上 | — | +| 21 | 字段 | `:2557-2599` ←`:2547-2589` `TYP_NAMED`→struct 字段表(含泛型代换) | 结果规则(名字匹配 + 返回字段行) | 谓词是**字段名匹配**(`si_field_name == field_ni`),不是类级位 | 字段名×类型二维判定 | +| 22 | 字段 | `:2600-2611` ←`:2590-2602` `TYP_TUPLE`→`.N` | 结果规则 | 同上 | — | +| 23 | 字段 | `:2613` ←`:2603` **落空** `return TI_UNIT` | **无诊断静默** | `IP_FIELD` 拒绝集与「能走到的行集」**同集** ⇒ 恒真门(探针 F3-F12:越界元组位/未知字段/方法当字段/ptr·串·数组/int·dex·bool·dyn 操作数 **12/12 rc=0**) | 落空加诊断;此时 `IP_FIELD` 才成为可推翻判定 | +| 24 | 转换 | `:2943-2952` ←`:2922-2931` PTR 目标 asp 传递 | 结果规则(构造 `*T`) | 结果规则,非许可判定 | — | +| 25 | 转换 | `:2953` ←`:2932` 其余恒等透传 | `return target_ti` | **无拒绝路径**(探针 A1-A10:`"a" as int`/`arr as int`/`1 as S` 等 **10/10 rc=0**) | 转换合法性矩阵(源→目标允许表) | +| 26 | dyn | `:1834-1843` ←`:1829-1838` 写集 | 位图并集 | 无拒绝路径;**64 上限钳位**(spec §3.3) | 解除 64 上限 | +| 27 | dyn | `:1845-1854` ←`:1840-1849` 读集 | 位测试 | 只读,无判定面 | — | +| 28 | dyn | `:1870-1893` ←`:1865-1888` `validate_dyn_method` | 「dyn 集里**每个**具体行都须有该方法」 | **谓词非类级位**:`type_has_method(get_type_name(ti), method_ni)` = 逐具体行名拼接方法表成员判定;**int/string 候选行今日也发 N08**(探针 D1/D4)⇒ 类级门会**抑制既有诊断 = 放宽**;反向(给 int 开位)抑制不了 NAMED 行判定 | (方法名 × 类型行) 二维判定 | +| 29 | dyn | `:2090-2106` ←`:2085-2101` dyn 方法分派 | 结果规则(早退 `TI_UNIT`) | 谓词 = **dyn 行同一性**(非许可);`IP_METHOD` 在 `AK_NAMED` 也为 1 ⇒ 换成 `permits != 0` 会把 struct 方法调用误吞进 dyn 早退分支(返回 unit)= 大面积行为变化 | — | +| 30 | dyn | `:2508-2511` ←`:2498-2502` `EXPR_LET` 建 dyn 集 | 结果规则 | 无拒绝路径 | — | +| 31 | dyn | `:2689-2697` ←`:2674` `EXPR_ASSIGN` 建 dyn 集 | 结果规则 | 同上 | — | + +### A.3 P3 裁决点登记(实施前须定 / 须先补) + +① **`iface_satisfies` 未交付**:P2b 范围明标「零调用者,P3 落地 impl 语义时才有对象」(spec §2.5)。⇒ 本计划 Task 2/6 与 Task 5 的 `T: I` 半边**开工前先补条目 + 满足判定**(三态 1/0/-1,不得静默当 0);已就位底座 = `iface_of_term`(类型项 → AK,单原子/-1)、`iface_kind_of`(行 → AK)、条目表 13 类。 +② **`name_ni` 列 = `-1`**(T1 登记偏差):`.ccr` 的 STR 段 = 编译期 `g_strs` **全量**(`ccr_io.cr:537`)⇒ 在 `iface_registry_init`(`init_types` 尾部、每编译一次)里 `str_intern("product")` 之类会把 13 个新串追加进 `g_strs` ⇒ `.ccr` 逐字节变(控制组实测:未用标识符 `zzz_probe_unused_ident` 确实进 STR 段)。⇒ **首个消费者(P3 诊断/显示)落地时裁决**「是否接受 .ccr STR 段增长」;此前在本表初始化路径加 `str_intern` = 打破 P2b 硬判据。 +③ **`TY_DEX_S`(码 8)不入 `ty_code_to_ti`**(T6 §6.1):两处基型分支现状对码 8 落 `TI_UNIT`;入表 = 若该码可达即行为变化(selftest `t6.R_dex_s_unit`/`t6.C_dex_s_unit` 把现状钉红)⇒ 需 8 个站点各加排除,净收益负。实测:`TY_DEX_S` 仅现于 `init_types` 位置公理(`checker.cr:253`)+ 定义 + 注释;parser 只产 7 个类型名码。 +④ **`never` 单元格:调用位点 = unit,类型位点 = never**(T6 §5,插桩实测**可达**):`res_call_type` 侧显式保留 `|| tv == TY_NEVER`(`checker.cr:1394-1398`);`fn g[T](a: T) -> never` 的调用结果按 unit 参与检查(`@raw_int` → TF07 实测;32 档语料 + 自源命中 0 次——真实源码无 `never` 类型位)⇒ **语义裁决**(never 形参/返回在调用位点应如何参与),P2b 按 spec §4 裁决 6 停下上报、未顺手改。 +⑤ **「全许可列」= 11 个 op 位 × 13 类 = 143 格**(比较 6 + `NEG`/`NOT`/`REF`/`DEREF` + `AS`):现状**无拒绝路径**(T4/T5 探针 32+44 例零拒绝)⇒ 表位已置 1、无门可换(本批不接线)。每位收紧建议见 findings §12。⚠ 任务简报记「20 个全许可列站点」——**本报告无法复现该计数**:实测口径 = 11 位(比较 6 / 一元 4 / 转换 1)、未接线站点合计 **31**(A.2 逐条)。 +⑥ **字段面 / 转换面:无任何拒绝路径**:`EXPR_FIELD` 全形 rc=0 零诊断(落空 `return TI_UNIT`,`checker.cr:2613`)、`EXPR_AS` 除 PTR 结果规则外恒等透传(`checker.cr:2953`)⇒ `IP_FIELD`/`IP_AS` 现为**恒真门**;P3 加诊断后才是可推翻判定(A.2 #23/#25)。 +⑦ **dyn 面:谓词非类级位**(A.2 #28/#29);另有 **dyn 位图 64 上限**(`union_bitmaps` 与 `validate_dyn_method` 的 `ti >= 64` 钳位;spec §3.3 已登记)。 +⑧ **索引侧「须 int」位缺失**:`idx_ti`(`checker.cr:2618`)推断后**无消费者** ⇒ `a[true]` rc=0。`IP_INDEX` 是**容器侧**位(ANY 语义),不能兼职索引侧(须 ALL 语义的「indexee」位)⇒ 新位 + 新门属 P3 设计面。 +⑨ **枚举域无条目**:注册表 13 类**无 `AK_SUM`/`AK_FN`**(checker 侧 enum 类型行 = `TYP_NAMED`;`iface_kind_of` 对全部 NAMED 行回 `AK_NAMED`)⇒ 本计划 Task 3/4(穷尽性/联合)经 `sh_enum_domain_term` 走桥接展开,**不要**在注册表查「sum 原子」;若要在条目层表达 sum,须先扩 `iface_kind_of` 的命名行分派(需要行→声明的查询,即 Task 0 Step 2 的 `find_enum`)。 +⑩ **性能面(如实登记)**:单次判定 = `iface_kind_of` + `iface_entry`(13 条线性扫)+ 位构造循环;三处登记(T3/T4/T5 报告)均判「无编译耗时回归证据」。P3 若把判定面扩到热路径(索引/迭代/字段),先看编译耗时再考虑 `(ak → entry)` 快表。 + +### A.4 表 / 注册表结构事实(P3 扩展前必知) + +- **条目布局** 40B/条 × 5 字段 `{ak, ti_row, name_ni, lit_code, ops}`(`iface_registry.cr:44-48`);`ti_row` 仅 8 原生有值,**结构/命名类 = -1 = 类级**(类级/行级不得混同——selftest 有专例);`lit_code` = AST 字面量 kind 的字典;`ops` = 许可位集。 +- **位下标**:`OP_*` 直用(1..19)、`UOP_*` **+20 偏置**、`IP_INDEX=25`/`IP_INDEX_RANGE=26`/`IP_FIELD=27`/`IP_METHOD=28`/`IP_AS=29`/`IP_COND=30`/`IP_COND_BOOL=31`。常量在 **`globals.cr:254-271`**(**必须在 globals.cr**:变量/常量跨文件按声明序可见,`checker.cr` 在 `iface_registry.cr` 之前;函数不受限)。`iface_bit` = 乘 2 循环(本语言无移位);`iface_permits(op<0 || op>62)` → 0(不回绕;≥63 会把 i64 推负 = 全位命中)。 +- **三态纪律**:`iface_ops(未知/-1)` = 0、`iface_kind_of(负/越界)` = -1、`iface_lit_ti(非字面量 kind)` = -1;`iface_of_term` 仅 `TT_ATOM`/`TT_TOP_K` 回 AK,其余 -1。 +- **初始化**:挂 `init_types()` 尾部(`checker.cr:257` 区)——幂等 + **不缓存行号**(长驻 corelsp 每请求 `init_types`)⇒ P3 新字段照此;**不得**在初始化路径 `str_intern`(见 A.3-②)。 +- **桥接单源化**:`sh_base_ak`/`sh_native_ak` 委托 `iface_by_ty_code`(唯一实现;`ty_shadow.cr:72-89`);`sh_native_ak` **保留** `get_type_kind != TYP_BASE → -1` 门(`sh_native_ak(TI_DYN) == -1`——DYN 行经 `sh_term_of_ti` 的另一分支;**不得**直接换成 `iface_kind_of`);`sh_*_ak_legacy` 为对拍对照物(P5 删,TODO #24-④)。 +- **单表 `ty_code_to_ti`**(`iface_registry.cr:259-270`):8 码逐项 + 域外 -1;**码 7 格 = 现状数值撞车原样保留**(`TY_GENERIC_PARAM=7` 与 `TI_DYN=7`;`parser.cr:98` 对 `dyn` 正产码 7 ⇒ 可达、N08 判据依赖)——归 P4/P5 命名空间分家,**P3 不得改**。 +- **语义分派守卫**(P1 血泪:AK/TI 下标不 1:1):AK↔TI 一律按语义逐项写死(`AK_STRING↔TI_STR`、`AK_BOOL↔TI_BOOL` 错位书写);守卫用例 `iface.dispatch_no_index_shortcut`——**任何新映射不得按数值直传**。 + +### A.5 事实表指针 + +侦查 §2 九条现状宽松面 → 表中格 → 现状诊断 → P3 收紧建议:**findings 文档 §12「P2b 后:公理区事实表」**(本附录所在提交同批新增);P3 各任务的「旧接受 → 新拒绝」登记表照其口径逐条填。 + +--- + +## 附录 B:R2 P3a 收官交接包(2026-09-12;**P3b 开工期档案——已交付,见 Task 7b + 附录 C**) + +**范围与来源**:P3a = Task 0/1/3/4/5 的**非接口半边**,提交链 `61d3a8b4`→`ef61f002`→`e9818d62`→`c8c7731b`→`9c0a83ec` + 本附录所在收官提交。**P3b 三件全部未开工**(写作时点):Task 2(横切接口)/ Task 6(impl 契约)/ Task 5 的 `T: I` 满足半边。本附录 = 五份任务报告(`.superpowers/sdd/p3-task{0,1,3,4,5}-report.md`)**逐条聚合**(不重新推导)+ 收官复验;数字出处 = 各报告 §2/§3,复验出处 = 收官报告(`.superpowers/sdd/p3-task7-report.md`)。 + +> **2026-09-12 更新(P3b 收官后)**:P3b 三件**已全部交付**(`9aa1786c`→`44100ba4`→`e838f18b`)——本附录保留为**开工期档案**(阻塞/接线点/登记面);交付面/统一台账/P4 交接 = **Task 7b + 附录 C**,逐件详版 = TODO #36/#37/#38 + 各任务报告。 + +### B.1 P3b 阻塞项与接线点(~~唯一阻塞 = `iface_satisfies` 未交付~~ ⇒ **2026-09-12 P3b Task 0 已解除**,状态见本节末「Task 0 交付后」) + +> **Task 0 交付后(2026-09-12;报告 = `.superpowers/sdd/p3b-task0-report.md`,提交见其 §11)——逐条对照下表**: +> - **① `iface_satisfies(t_ti, iface_ni)` 已交付**(`type_engine.cr`,三态 + 每查询预算隔离)= **原生/横切/用户三类同入口**,轴分派 **A 横切形状名(表 `iface_shape_*` 已就位,本批空表)→ C 用户接口 → B 本质轴**(轴优先序 A > C > B;同名撞车面:形状优先、接口 vs 原生名**保持 P3a 现状 = 接口优先**)。 +> - **Task 5 的 `T: I` 半边 = 已接线并真判定(实例化点)**:新消费者 `gen_inst_constr_satisfied`(结构/枚举实例化点直取真值 ⇒ 可证违反 = `error[TG02]`「does not satisfy interface 'I'」,与既有函数调用点**同措辞**;软诊断同门同码)。函数调用点 = **1 提前返回 + 0/-1 回落既有 `check_iface`**(措辞/去重/rc 逐字未动)——**0 → 新措辞的切换仍归 Task 6 Step 3**。谓词 = 与 `check_iface` 同源(方法名在位 + 参数计数 + 返回码),域 = 命名行;**非命名/dyn/泛型形参 ⇒ -1 不判**;判定路径零 `str_intern`(`g_methods` 查名,.ccr STR 段守)。 +> - **② Task 2**:形状表的**注册面已就位**(`iface_shape_register/lookup/count/reset` + `iface_satisfies_term` 原语,全在 `iface_registry.cr`)⇒ Step 1 只剩**条目细化**(`sequence`/只读/可写/可索引/可迭代/product 各自形状项——注意 `AK_SEQUENCE` 元素槽**不变**,`<: 序列接口` 的形状项须用 `⊤ₖ` 形而非 `sequence<⊤>` 参数链;本批 selftest `isat.axis_a_shape` 即该形);Step 3 消费点换位未动(本批未接线,行为零变化)。 +> - **②b Task 2 交付后(2026-09-12;报告 = `.superpowers/sdd/p3b-task2-report.md`)**:**Step 1 条目细化 + Step 3 消费点换位已交付**——六条形状项(`sh_shape_seq` / `sh_shape_seq_ro` / `sh_shape_seq_rw` / `sh_shape_indexable` / `sh_shape_iterable` / `sh_shape_product`,`iface_registry.cr` 横切形状段);索引兜底门 → 可索引形状、range 分支 → 序列形状 ∧ 固定性位(序列项 b 槽)。**行为保持**(收紧 0 / 放宽 0):12 例行为探针老/新两二进制逐例同 + 全语料 72 档 check 面**零差异**(rc + 诊断逐字节)+ ELF/`.ccr` 逐字节同。**两处按实测重定**:只读形状 = 序列本体 ∪ **只读视图**(协变槽判定;「只读 = 协变」在视图面兑现,T1 §6-④ 在形状面闭合),可写形状 = 本体面(**可写视图形状不可表达** = 不变槽遇 ⊤ 落 -1,登记 + 用例钉死);**名字面不注册**(驻留 ni 约束 ⇒ 命名消费语法待裁决,消费者走 `iface_satisfies_term`)。`可迭代` 面**未接线**(`EXPR_FOR` 现状零类型检查,登记)。 +> - **③ Task 6**:**已交付(2026-09-12;报告 = `.superpowers/sdd/p3b-task6-report.md`)**——Step 1 签名类型项化(方法条目类型**节点**槽)+ `sh_iface_shape_term` 建项(方法集 = product of fn)+ 逐成员包含判定 + mangling 退役(`g_methods` 表查询,零 `str_intern`)+ 泛型体内方法调用实例化解析(**旧 rc=139 → 正确值**)。整形状 `ty_sub` 路由经实测**不可用**(AK_NAMED 不展开 ⇒ -1;product 比较是结构相等非包含)⇒ 形状项作为签名载体由逐成员判定消费;逐成员比较用引擎结构比较原语 `tt_list_same`(`ty_sub` 的 -1 面会让签名不符退化为「不判」——突变 M3b 实证)。两处裁决(报告 §7):**用户轴保持结构口径**(不采名义)+ **形状名字面仍不注册**。 +> - **收紧台账(本批新增)**:结构/枚举实例化点的接口约束违反 **rc=0 零诊断 → check rc=1 + TG02**(6 例,全语料 72 档零命中;`test_generic_constr.py::struct_iface_constr_unjudged` 按台账改为 `..._violated`)。**未覆盖面**:接口签名槽 = 映射层编码(非原生类型塌缩为码 0、`self` 槽两侧均 0)⇒ 逐参数类型不参与、返回码语义 = 编码相等(钉红用例 `inst_encoding_limit_named_ret_pinned`;解锁 = Task 6 Step 1)。**判据**:`selftest-types` **304/304**(+16 `isat.*`)+ 新套件 `test_iface_satisfies.py` 17 例(挂 CI)+ 四套件绿 + ELF canary 逐字节同 + 同源对拍零差异 + 影子全计数 0。 + +| # | 件 | 阻塞形态(P3a 末实态) | P3a 侧已就位的接线点 | +|---|---|---|---| +| 1 | Task 5 的 `T: I` 半边 | 用户接口约束**一律 -1 不判**(结构/枚举实例化点零诊断);函数调用点**回落**既有 `check_iface` 名拼接路径(逐字未动,`iface_ops`/`test_impl` 零回归) | `checker.cr` `gen_constr_satisfied` 首分支(`find_iface(c_ni) >= 0 ⇒ -1`)——`iface_satisfies` 交付后**改这一处**即全覆盖;`sh_iface_shape_term` = Task 0 占位(恒 -1) | +| 2 | Task 2 横切接口 | 形状条目(`sequence<⊤>` / 只读 / 可写 / 可索引 / 可迭代 / product)不存在;索引/切片/迭代的 `get_type_kind` 直比未换位 | `ty_variance_of`(T1:只读协变/可写不变已在引擎侧表)+ `sh_seq_term`(固定性位)+ `sh_struct_term`/`sh_enum_domain_term`(T0 展开层,一层深度) | +| 3 | Task 6 impl 契约 | 接口签名仍裸 `TY_*`(`g_ifaces` 布局未动 ⇒ 签名**无法表达**命名/泛型类型);`check_iface`/`check_impl_for` 未接引擎;mangling 未退役;方法 16/参数 8 上限未处置 | `sh_iface_shape_term`(占位)+ `iface_registry.cr` 条目表(5 字段,**无变型规则字段**——T1 口径 = 变型表留引擎侧,条目字段后挂引用) | + +**开工顺序建议**:先补 `iface_satisfies`(三态 1/0/-1,不得静默当 0;零调用者 → 首个消费者)→ Task 6 Step 1 签名类型项化(**形状项构造的前提**)→ Task 2 形状条目 + 消费点换位 → Task 5 半边接线(`gen_constr_satisfied` 一处 + `sh_iface_shape_term`)。**继承的硬口径**:新硬错误先全语料 report-only(Global Constraints 第 6 条);`.ccr` STR 段约束(A.3-②:初始化路径**不得** `str_intern`);三态纪律(-1 不当 0/1)。**新裁决面**:P3a 的 `AK_NULL` 不进 `iface_registry`(`iface.count == 13` 为判据硬值)——若 Task 2 形状面要覆盖 `T?`/`null`,须先裁「条目表扩列 vs 引擎侧另表」。**另见 B.7 一等条目**(`T?` 运行期表示未统一 ⇒ `Some` 臂双路径 SIGSEGV rc=0+139——非本三件的开工阻塞,但 P3b/P4 必修)。 + +### B.2 P3a 收紧/放宽统一台账(逐条 = 旧 → 新 + 处置;**汇总:收紧 18 · 放宽 8 · 诊断面新增 1 · 实例名忠实化 6**) + +**收紧(「旧接受 → 新拒绝/新诊断」)——T0 = 0 条(纯新增 502/0,三面零差异)** + +| 任务-# | 用例 | 旧 → 新 | 处置/依据 | +|---|---|---|---| +| T1-1 | pA `return b[0..2]`(切片当 `[int;3]` 用,视图→固定) | rc=0 → **rc=1** `TF01` | **计划 Step 3 明令翻转**:P2a 引入的静默放宽在本批关闭 | +| T1-2 | pF `struct S { a: [int;3] }` ← 切片值(字段位) | rc=0 → **rc=1** `TS03` | 同一规则的自然外延(站点 9) | +| T1-3 | pIf1 `if c { arr } else { sl }`(合并结果位) | rc=0 → **rc=1** `TC02` | 结果类型 = then 侧 ⇒ else 须不弱于它(参序归一) | +| T1-4 | pN3 `return [s, s]`(嵌套 `[[int;3];2]` ← 切片元素) | rc=0 → **rc=1** `TF01` | 嵌套位同律(6 个递归位全覆盖) | +| T1-5 | pE1 `&mut int` 实参 → `&int` 形参 | rc=0 → **rc=1** `TF01` | mut 入参数链 = 判定维度(等式语义,非 Rust 单向) | +| T1-6 | pE2 `&int` → `&mut int`(反向) | rc=0 → **rc=1** `TF01` | 反向同拒(计划 pE 原文) | +| T3-1 | p1 缺臂 match(`{Red, Green}` 缺 Blue) | rc=0 + 产物 → **rc=1 + 无产物** | `TM03` 硬门(report-only 先行,清单空后开门) | +| T3-2 | p1b 缺臂且运行值 = 缺失变体 | rc=0 + 产物 / 运行 rc=100(静默 0)→ **rc=1 + 无产物** | 静默误编译面的直接证据 | +| T3-3 | p4 payload 变体缺臂(Third 缺) | rc=0 + 产物(静默 0)→ **rc=1** | 变体身份粒度同律 | +| T3-4 | p8b 无臂 `match c { }` | build rc=0 + 产物 → **rc=1** | 未覆盖面 = 全域 | +| T3-5 | p17 枚举形参位缺臂 | rc=0 + 产物 → **rc=1** | 非构造器来源同律 | +| T3-6 | p20b 用户 `enum Option[T]` + `int?` 缺 `None` 臂 | rc=0 → **rc=1** | 声明枚举即入域判定(T4 后与 `T?` 行身份分家) | +| T4-T1 | p8 `fn f(x: Option[int])`(无用户声明 Option) | rc=0 零诊断 → **rc=1** `N09`(**软**:build rc=0) | 退役内建 Option 注册的直接后果(spec §5.4 明令) | +| T4-T2 | p9 用户 `enum Option[T]`:`-> int?` ← `Option[int]` 值 | **rc=124(30s 挂死)** → rc=1 `TF01` | 旧态本身是挂死 ⇒ 净效果 = 明确拒绝(零语料命中) | +| T4-T3 | p13 用户 `enum Result[T,E]` 的 `v?` 解包 | rc=0(旧按**名字**解包)→ rc=1 `TF01` | 计划 Step 2 明令废「基名字符串」;结构解包只认 `TYP_OPTIONAL` | +| T5-T1 | p04 `struct Box[T: int]` + `Box[string]` | chk=0(约束被丢弃)→ **chk=1** `TG02` + 反例 `string ∩ ¬int` | 结构泛型约束保留 + 实例化点判定(零语料命中) | +| T5-T2 | p05 `enum EBox[T: int]` + `EBox[string]` | chk=0 → **chk=1** `TG02` | 枚举侧同源(同一 `gen_inst_constr_check`) | +| T5-T3 | p02 `fn f[T: int]` 以 `f("s")` 调用 | chk=1(**仅** TF01)→ chk=1(**+ TG02**) | 码集合扩张,**rc 不变 ⇒ 非硬门变化**(旧态约束被 `check_iface` 路径吞掉) | + +**诊断面新增(rc 不变)**:T3-p11 重复臂 → `error[TM04] Redundant match arm`(**软面**,评估后不升硬门;升门须重跑全语料 report-only)。 + +**放宽(「旧拒绝/不可用 → 新接受」)——T0/T1/T3 = 0 条** + +| 任务-# | 用例 | 旧 → 新 | 说明 | +|---|---|---|---| +| T4-L1 | p1 `fn g() -> int? { return 5; }` | rc=1 `TF01` → **rc=0** | `T ⊆ T?`(判定点可选目标注入) | +| T4-L2 | p2 `-> int? { return None; }` | rc=1 `N01` → **rc=0** | `null ⊆ T?` + `None` 可解析 | +| T4-L3 | p3 `-> int? { return Some(1); }` | rc=1 `N01` → **rc=0** | `Some` 可解析;行 = `int?` | +| T4-L4 | p4 `x: int? = 5; x = 3;` | rc=1 `TA01` → **rc=0** | 赋值位注入 | +| T4-L5 | p7 调用位 `fn f(v: int)` ← `int?` 实参 | rc=1 `N01` → rc=0 | **不是语义放宽**(构造器可解析而已);调用位点仍**无检查**(#20/#32 姊妹面) | +| T4-L6 | p12 `match x { Some(v) => v, None => 0 }` | rc=1 `N01` → **rc=0** | 两臂覆盖 = 穷尽(可选域接入 TM03 判定) | +| T4-L7 | p14 用户 `enum Option[T]` + `x: int? = Some(5)` | **rc=124(挂死)** → **rc=0** | 挂死根因(`Some(x)` 实参链契约)修复 | +| T5-L1 | p09 `take(1, 2)`(F4 假拒) | chk=1 `TF01`(假拒:T 未绑定)→ **chk=0** | F4 修复的正确性正控(实例名 `take[int,int]` → `take[int]`) | + +**实例名忠实化(T5,rc 全同)**:S1 `f[unit]`(折叠!)→ `f[A]`+`f[B]`(**反折叠**,计划 Step 4 要求实证的折叠缺陷)· S2 `id[unit]` → `id[S]` · S3 嵌套实例 `inner[A]`+`outer[A]`(克隆期绑定重映射)· S4 `take[int,string]` → `take[string]`(按形参声明序) · S5 `f[unit]` → `f[S]` · S6 元组实参 = **未覆盖面**(复合实参无语法形态 ⇒ 回落旧名串,同值)。 + +**挂死修复面(2 端)**:T4-T2(30s 超时 → 明确拒绝)· T4-L7(挂死 → rc=0)——同一根因(`Some(x)` 实参链契约 + `sym_*` 越界读)的两个出口。 + +### B.3 语料全扫证据(逐任务;**站点覆盖与差异数同报**——P1 交接硬性要求) + +语料 = 72 档(`tests/suite` 32 + 自源 2 + 后端/kernel 15 + stdlib 19 + examples 4),逐档 `clean-cache`,cwd = 仓库根;对拍形态 = **旧二进制 × 新源 vs 新二进制 × 新源**(同源双编译器)。 + +| 任务 | 对拍结果 | 影子判定数 | 新能力面语料命中 | +|---|---|---|---| +| T0 | check/shadow/dump 三面 `diff -r` **空** | 29,090 / agree 29,090(全计数 0) | 展开层零命中(本批零生产消费者) | +| T1 | 三面 `diff -r` **空** | 29,267 / agree 29,267(全计数 0) | array↔slice 方向/变型/mut/嵌套固定性零命中 | +| T3 | check 面**仅 2 处行号位移**(既有 TF01 误报随新增源码行漂移;诊断集合/条数/rc 全同);shadow/dump 空 | 29,442 / agree 29,442(全计数 0) | 语料**零 match、零 enum 声明** | +| T4 | check 面逐档**诊断码集合与 rc 同**(3 档自源行号位移);shadow dump 与 T3 逐字节同 | 29,687 / agree 29,687(全计数 0) | 语料零 `T?`、零 `Some`/`None` | +| T5 | 同源对拍 **0 档差异**(rc + 诊断码 + 逐条消息含行号全等) | **30,338** / agree 30,338(全计数 0);dump 与 T4 逐字节同 | 语料零泛型约束语法 | +| 收官 | 终态复跑(本轮实测):check/shadow/dump 三面 `diff -r` **空**;rc 分布 33×rc=0 / 39×rc=1(两二进制同) | **30,128 / agree 30,128**(全计数 0);站点直方图 `assign-node=22785 · fn-body-ret=5338 · if-branch=1971 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2` | — | + +> **判定数 30,338(T5)→ 30,128(收官)的 −210 已精确归因**:唯一来源 = 附录 B.6 的修复(删 target project 的 `import monomorph`)⇒ monomorph.cr 退出 target `main.cr`/`_import.cr` 两档的导入闭包——逐档切换实测 3577→3472 与 3542→3437(各 −105,合计恰 −210);语义面零变化由「同源对拍空 + 两档 N06 33→0」双证。 + +**站点覆盖(五任务口径一致)**:`assign-node` / `fn-body-ret` / `if-branch` 为主流量;`struct-field-type=28` · `array-elem-type=4` · `generic-apply-base=2`;**站点 1(hotpatch-ret)/ 2(generic-arg)/ 4(unify-fallback)/ 6(assign-binary)语料零命中**。⇒ P3a 全部新能力面**语料零命中**,其上表「零差异」**不是**新能力正确性证据——正确性由各任务的**行为探针**(T1 19 例 / T3 22 例 / T4 14 例 / T5 16 例)+ 用例表(288 例)+ 新套件(16+12+14 例)+ 突变控制承担。 + +### B.4 逐条登记面(出处 → 现状 → 影响 → 建议;P3b 或后批消费) + +1. **`save_func_gen_constrs`(函数侧)稀疏零初值同族风险**(T5 §7-②):struct/enum 侧已修(空槽预填 -1),**函数侧未改**——其唯一读者 `infer_gen_call` 以 `iface_ni >= 0` 判有效,同一「空槽读成 0 = 合法名字 ni(首个驻留串)= 凭空约束」风险在库;改动会触既有 `test_iface_ops` 基线 ⇒ 建议**单开**(若 P3b 动 `infer_gen_call` 的约束循环,**先修此处**)。 +2. **`never` 不满足原生约束**(T5 §7-③):引擎 `AK_NEVER` 是与 `AK_INT` 互斥的**原子**(非 ⊥)⇒ `gen_constr_satisfied(int, never) == 0`;同源面 = `never ⊆ T?` 不给(T4 §7-⑩)、`never` 调用位点 = unit / 类型位点 = never(附录 A.3-④)。**语义裁决项**,P3b 不动引擎即保持现状。 +3. **命名实参 vs 原生约束 = -1 不判**(T5 §7-③):`struct Box[T: int]` + `Box[S]`(S = struct)零诊断——根因 = 引擎 `AK_NAMED` 不展开(P0 未覆盖面②;T0 的展开层**故意**只服务满足判定/域查询);`Box[string]` 这类可判的照拒。⇒ 若 P3b/P4 要闭合,须裁「满足判定侧启用展开」(**不得**动等价面)。 +4. **MAX_GENERICS = 4 未解除**(T5 §0-②,显式登记):>4 现状 = parse 期硬报(`P01`/`TA08`,非静默截断);解除面 = struct/enum 表 4 槽布局 + `ESZ_*` + `parse_generics_into` + `ccr_io` 零写槽;语料最大 2 ⇒ 零收益零容忍成本,归单开批。 +5. **`EXPR_LET` 无任何兼容检查**(T1 §6-① → TODO #32):`x: [int;3] = s;` / `x: [int;4] = [1,2,3];` / `x: int? = 5; y: int = x;`(运行 rc=5)全部 rc=0——**本站不是既有 10 判定点之一**,追加即「新判定点 + 新硬错误门」⇒ 须 report-only 专批;与 #20(调用位点无诊断)互为姊妹面(T4-L5 同源)。 +6. **运行期表示未统一 = 一等缺口**(T4 §7-③;**升级条目 = B.7**):`Some(1)`/`None` 走 `IR_MAKE_ENUM` 对象 + tag,`T?` 槽里的**裸值**仍是裸值 ⇒ 同一 `T?` 变量可持两种表示。**实测(2026-09-12 复核,P3a 收官二进制,cwd = 仓库根)**:`x : int? = 5; match x { Some(v) => { return v; } None => { return 0; } }` —— `check`/`build` 均 **rc=0 零诊断**,而产物运行与 `corec run`(解释器)**双路径 SIGSEGV rc=139**;含 `Some(...)` 臂的其余形态同崩(`Some(v)` + 通配 `_`、match 作表达式取值 `y := match x { … }` 均双路径 139);**纯通配臂**(`match x { _ => { return 7; } }`,无 `Some` 绑定)实测**不崩**(双路径 rc=7 = 通配臂照常执行)。⇒ 旧文「走 `None` 臂」的准确内核 = 「裸值不被识别为 `Some`(静默走非 Some 分支)」,**但凡存在 `Some` 臂即由静默升为崩溃**(载荷解包把裸值当枚举对象解引用)。**非 P3a 引入**:pre-P3a 工具链(`/tmp/p3t0_base_bin` 的 corec+corearch)同程序同 rc(check 0 / build 0 / 运行 139)。需 ir_gen/后端 + 表示决策(P3b/P4;复现、边界、修法归属详版 = **B.7**)。 +7. **枚举变体数/载荷数 >16 越界写**(T4 §7-⑥,既有;**已登记为 TODO #35**):parser 写 `OFF_EI_VARIANTS + vc*OFF_EV_SIZE` 与载荷列 `OFF_EV_TYPES + tc*8` **无上限闸**——`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES`(皆 16)**只在 `.ccr` 读回侧**(`ccr_io.cr:1121`/`:1148`)检查,写入侧(parser)零引用。**「静默」只限 check 面**(前端不读回 ⇒ rc=0 零诊断);**build 面非静默** = corearch 读回命中该闸 ⇒ rc=1(`error: .ccr enum variant count exceeds max` + `error: invalid .ccr file`,无产物)——但**写入已发生在读回闸之前**(护栏在读回侧 ≠ 写入侧),第 17 变体槽越 `ESZ_ENUMINFO` 224B(常量/写点/实测细节见 #35)。修法照 TODO #8 先例(parser 硬错 + 访问器护栏,P020 式)⇒ **先 report-only**。**同族 MAX_* 处置总口径**(本批新登记):MAX_ENUM_VARIANTS/MAX_VARIANT_TYPES(本条 = #35)、MAX_GENERICS(上条 4)三者均属「表布局定长槽 + 无写入闸」族,统一按「先加护栏、再评估解除」办。 +8. **不可判面清点**(T3 §6-④ + T4 §7-⑩):match 面四条(非枚举域 / 不可映射模式(字面量·struct 模式·未声明名·异枚举限定名)/ 引用 scrutinee / `T?` 无声明枚举——T4 后 `T?` 已入判定,仅「无声明枚举」的旧口径失效);可选域模式名**只接裸名**(`Option.Some` ⇒ 不可映射 ⇒ 不判);`TYP_NULL` 的 `EXPR_TRY` **不解包**(保守);`type_compat_sym`(hotpatch 站)**无**可选注入;`iface_*` 对 `TYP_OPTIONAL`/`TYP_NULL` 行回 **-1**(门全拒 = 保守面)。 +9. **空枚举域 = 空洞穷尽**(T3 §6-③,计划原文「= 0」的偏差登记):域 = ⊥ ⇒ 反例集恒空 ⇒ 判穷尽 = **1**(不报错是正确行为);源码面 `enum E { }` 可写(`EC_P_ENUM_EMPTY`(P009) 有定义未见 raise——既有事实)。 +10. **空递归保守**(P0 引擎未覆盖面③,P3a 未动):`μX.X` 按深度守卫回 -1;P3a 新增面(变型/展开/联合)均在预算窗口内(`ty_budget_reset(200000)` 前后隔离,照 `type_equal_engine`)。 +11. **符号档长度约束未实现**(计划 §侦查 3 / #19 的 P3 面遗留 → 本批收口口径):**符号档 = VC 义务,本计划不实现,显式登记**;动态档 = 既有运行期检查(`g_ir_slice_lens` + `IR_BOUNDS_CHECK`)+ checker 字面量界检查(R1 已落)。 +12. **bootstrap `&&`/`||` 不短路 = 根因仍开放**(T0 §5-③;T4 §7-④ 只关了「`g_syms` 未分配」触发链:`sym_*` 四读取器加范围闸):短路是**前端**行为(自托管 `ir_gen` 分支化,D11 实证),而 **Python bootstrap 的 `ir_gen.gen_binary` 两侧无条件求值** ⇒ 由它构建的编译器二进制自身按「两侧均求值」执行;生产路径 `g_syms` 必已分配 ⇒ 无外部表现,但任何 `guard && 表读(idx)` 形态在「表未分配」通道可崩。修法 = bootstrap `ir_gen` 分支化(或全线护栏化该形态)。 +13. **`EXPR_ENUMPAT` 名字槽 = `ast_a`**(T3 §6-②,实测踩坑):任何新消费 `EXPR_ENUMPAT` 的代码必须用 `ast_a`(`ast_int_val` = `EXPR_IDENT` 的约定,两套并存);**selftest 夹具与 helper 同错自洽**曾致 report-only 假绿——行为探针(真 parser 输入)是唯一抓手。 +14. **witness 项含空析取支**(T0 §5-② / T3 §6-①):缺变体时 `ty_exhaustive` 的 witness = `(A∩¬A)∪(B∩¬A)`(`tt_norm` 不净化);实测 `ty_equiv(witness, 缺失变体) = -1`(未覆盖面)、`ty_sub(w,B)=1`、`ty_inhabited=1`。⇒ **反例命名走覆盖位**(T3 实现);若后续净化 `tt_norm` 或改 `ty_exhaustive`,须同步 `t3.counterexample_named_variant` 的四个断言。 +15. **引擎侧无「链元素皆类型项」不变量**(T1 §6-②):不变槽「确定不等价 ⇒ 0」的加强**依赖**该不变量(展开项/桥接项/令牌混用同一链空间);本批以「非同形 = -1」保守(令牌已原子化消除混读风险)。 +16. **只读序列视图第二层协变未做**(T1 §3.3-6/§6-④):`&[int] ⊄ &[int∪str]`(序列构造子自身不变);spec §5.1 的「只读序列视图协变」在本批以「只读 ref 元素协变」兑现——若 Task 2 形状面要闭合,须引入只读序列形状。 +17. **复合实参实例键兜底**(T5 §7-③):元组/嵌套应用无语法形态 ⇒ `inst_type_node_of_ti` 单槽根契约不成立 ⇒ 键走行号兜底 `#` + 替换回落名字路径;`gen_remap_binds` 的「全具体」前置不满足时**整段放弃**(回落旧名串,不发明半解实例)。`ir_gen.cr` 的 `find_or_create_mono_func` = **死码**(零调用者)且其 `int_val` 旧语义已改(绑定段起始 +1)——若复活**必须先改**(T5 §7-④)。 +18. **`monomorph.cr` 已迁出 corearch 清单**(T5 §0-③):理由 = 本批类型项化需 checker/parser 层符号,corearch 侧对它零代码引用(逐符号核对);唯一产物面影响 = corearch 二进制不含该段死码(**corearch 二进制 sha 不是本计划判据**——T0 §5-④ 同款说明;ELF canary + 全回归 + 自举链为安全面证据)。 +19. **性能面**(T1 §6-⑤ / A.3-⑩ 口径):P3a 未做基准测量;新增判定路径均在预算窗口内且主流走节点同一性快路径(`p == q`);若 P3b 把判定面扩到热路径(索引/迭代/字段),先看编译耗时再考虑 `(ak → entry)` 快表。 + +### B.5 交 P5 的继承项(并入 TODO #24/#30 清单;**P3a 后状态更新**) + +- **`type_equal_legacy` 删除归 P5**(双用:对拍对照物 + unknown/桥接回落实现)——**状态更新**:P3a 未减少其调用面(T0 的展开层按设计**只**服务满足判定/域查询,等价面仍原子名义);⇒ #24 的「unknown 清零前提 = 引擎命名展开」**不会被 T0 满足**(那是等价面的展开,属**裁决**而非欠账):P5 须先裁「等价面是否接受命名展开(会推翻 P2a 对拍基线)」或「接受 legacy 长期化」。 +- **`sh_native_ak_legacy`/`sh_base_ak_legacy` 删除归 P5**(仅 `type_selftest.cr` 三例引用,删时同步调整)。 +- **站点 6(assign-binary)挂点去留**随 P5 站点面清理(P3a 未动站点集合)。 +- **影子层(`--type-shadow` / `g_shadow_map` / `replace_*` 计数)与 legacy 同族**,P5 一并下线;P3a 全程复用该通道做对拍(其站点覆盖口径**不得**当新能力证据——见 B.3 声明)。 +- **`tt_display`/`tt_display_at`(T5 新增反例文本)零 `str_intern`**:若 P5 允许 `.ccr` STR 段增长(A.3-② 的后续裁决),此处是首批可选改造点(当前为守判据而用逐字比较)。 +- **MAX_* 族统一处置**(见 B.4-7)与 **`iface_satisfies` 交付后的条目表扩列裁决**(见 B.1 末)——两者均属 P4/P5 载体面(.ccr TYPE/IFACE 段)的输入。 + +### B.6 收官发现并修复的回归:project-mode 清单漏改(Task 7;**后续批同类面必查**) + +**现象**:`tests/selfhost/test_backend_bootstrap.py`(stage0 = `corec build src/targets/x86_64-linux -o … --static -O 0`)rc=1——`[FAIL] stage0 builds corearch stage1: exit 0 but build log carries 33 error[ diagnostic line(s)`,全部为 `error[N06]: Undefined function`(`get_type_kind`/`get_type_data`/`get_type_extra`/`alloc_node`/`alloc_type`)。 + +**根因**:Task 5 把 `monomorph.cr` 从 corearch 的 **concat 面**清单(`build_selfhost_native.py` `backend_support_files`)迁出,但**漏改 project-mode 面清单**——`src/targets/x86_64-linux/_import.cr` 的 `import monomorph` 仍在。Task 5 给 monomorph.cr 的类型项化引入 checker/parser 层符号(46 处),而 project-mode corearch 单元集不含该层 ⇒ 33×N06 **静默未定义**(rc=0 + 产物照出;stage 链互测不可见,正是该门要拦的类)。 + +**为何 P3a 期间不可见**:唯一守卫 = `test_backend_bootstrap.py` 的 project-mode `error[` 门,而该套件**未挂 CI**(TODO #31:`selfhost-tests` 挂 18/42)⇒ 5 个任务的回归面都没跑到;收官全量枚举(42+7 逐档)首次暴露。**同类面**:`build_selfhost_native.py` 与 `_import.cr`(project)是**双注册**关系(文件头注记自陈),任何「清单增减」改动必须**两面同改**。 + +**修复**:删 `src/targets/x86_64-linux/_import.cr` 的 `import monomorph`(证据与 concat 面同一:monomorph 在 corearch 链接集**零代码引用**——全部定义符只出现在 globals.cr 注释里;文件头注记落案)。**复验**:该套件 rc=0(stage1/2/3 逐字节同 + smoke/O2 smoke 全绿)· project-mode 构建日志 `error[` **33 → 0** · ELF canary `95084e7b…d475` 不变 · 三二进制(corec/corearch/corelsp)sha 不变(该文件不入任何 concat 清单)· 全量枚举终态 **49/49 rc=0**。 + +**给后续批(P3b 尤其 Task 6 的清单改动)**:① 动 `build_selfhost_native.py` 的任何清单 = 必须同步核 `src/targets/x86_64-linux/_import.cr` 与 `src/compiler/_import.cr`(两面);② 每任务回归面**必含** `test_backend_bootstrap.py`(或先把它挂进 CI——#31)。 + +### B.7 一等条目(**P4 必修**——P3b 未动;非 P3a 引入):`T?` 运行期表示未统一 ⇒ `Some` 臂双路径 SIGSEGV + +> **✅ 已闭合(R2 P4 Task 5,2026-09-13;裁决 5「解包侧判表示」)**:表示位(IR 层,0 = 裸值 / 1 = 装箱,运行期值)+ 两解包点(`match` 的 Some/None 臂、`?`)分派;跨函数面(返回/形参)走隐藏全局信道(纯 IR);未覆盖面(结构体字段/数组元素/全局槽/match 结果/惰性 thunk)回落既有装箱假定——**裸值形态响亮失败双路径同,装箱形态照常正确**。实测:本节的四形态 + 跨函数面全绿双路径(本节下表 rc=139 行全部消失);`?` 解包装箱值(修复前 ELF/解释器双路径各错各的)同步闭合。落地/判据/台账见 `plans/2026-09-12-r2-p4-carrier.md` Task 5 + `.superpowers/sdd/p4-task5-report.md`。 + + +**缺口**(T4 §7-③ 原登记,B.4-6 升级):`Some(1)`/`None` 走 `IR_MAKE_ENUM` 对象 + tag,而 `T?` 槽里的**裸值**仍是裸值 ⇒ 同一 `T?` 变量可持两种表示;计划未列 ir_gen/后端,P3a 只登记未修。**升级理由 = 实测后果不是「走 `None` 臂」而是崩溃**(2026-09-12 复核)。 + +**复现(最小 8 行)**: + +``` +fn main() -> int { + x : int? = 5; + match x { + Some(v) => { return v; } + None => { return 0; } + } + return 0; +} +``` + +**实测 rc(P3a 收官二进制 `./build/corec`,2026-09-12;cwd = 仓库根——`/tmp` cwd 会触发 import 解析假失败,勿用)**: + +| 面 | rc | 观察 | +|---|---|---| +| `check` | 0 | **零诊断**(编译器完全不拦) | +| `build --static` | 0 | 产物照出 | +| 产物运行 | **139** | SIGSEGV | +| `corec run`(解释器) | **139** | 同崩(非 ELF 特有) | + +**边界(防误诊;本轮四形态逐个实测)**:`Some(v)`+`None` 臂 = 双路径 139(上表);`Some(v)`+通配 `_` 混合臂 = 双路径 139;`y := match x { Some(v) => v, None => 0 };`(match 作表达式取值)= 双路径 139;**纯通配臂**(`match x { _ => { return 7; } }`,无 `Some` 绑定)= 双路径 **rc=7、不崩**(通配臂照常执行)。⇒ 崩溃面 = **`Some` 臂的载荷解包**(把裸值当枚举对象解引用);「裸值入 `T?`」本身是静默的,是 `Some` 臂把它变成崩溃。 + +**非 P3a 引入(预存证据)**:pre-P3a 工具链 `/tmp/p3t0_base_bin`(P3a Task 0 起点,corec+corearch)同程序:`check` rc=0 · `build` rc=0 · 运行 **rc=139**——与终态同 rc ⇒ 无回归;T4 报告 §7-③ 与 `tests/selfhost/test_optional.py` 头注均已如实登记(「表示统一不在本任务 Files 面内」)。 + +**修法归属(P3b/P4)**:运行期表示统一,二选一——①**构造/赋值侧装箱**(`T?` 槽一律存带 tag 的表示,裸值入口补 `IR_MAKE_ENUM`);②**解包侧判表示**(`Some` 臂按 tag/表示判别后取载荷,裸值路径直取)。落点 = `ir_gen.cr` + 后端(`T?` 的赋值/形参/返回/字段等流);**与 P4 的 .ccr TYPE 段载体一并裁**(B.1 末「条目表扩列 vs 引擎侧另表」同批)。**判据面** = `tests/selfhost/test_optional.py` 扩例(`x : int? = 5` 的 Some/混合/通配臂 + 产物运行 + 解释器双路)——现套件只钉「Some 对象走 Some 臂 / None 走 None 臂 / 裸值入返回位 / 裸值配通配臂」,**未覆盖「裸值 + `Some` 臂」**,这正是该缺口存活至今的直接原因。 + +--- + +## 附录 C:R2 P4 交接包——载体(`.ccr` TYPE/IFACE 段 + DFNode.TK 升格 + corearch 读回) + +**范围与来源**:P4 目标 = 统一设计 spec `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` **§6(载体)**:TYPE 段 = 类型项表序列化 + 原子条目扩展;IFACE 段 = 条目(原生/横切/用户)+ 满足关系(impl 边);DFNode.TK 升格;corearch 读回重建类型项表(§6.3)⇒ 兑现 §6.4「类型/接口信息随载体出前端,验证工具直接消费(不必重跑前端)」。本附录 = P3b 三份任务报告(`.superpowers/sdd/p3b-task{0,2,6}-report.md`)+ Task 7b 收官实测**逐条聚合**,供 P4 实施者开工即用。**P4 无独立计划**(`docs/superpowers/plans/` 全目录实查无 P4 计划;本附录即其起点文档)。 + +### C.1 P4 四件 ↔ 现状实测(逐条) + +| P4 件 | spec | 现状(2026-09-12 实测) | +|---|---|---| +| `.ccr` **TYPE 段** | §6.1 | **不存在**:段表仅 6 段(STR/SYM/NOD/ENT/REG/EDG),类型本体不落盘——类型只以 u32 索引出现在 SYM(func/struct/enum 记录)/NOD(`tk`)/EDG 里(spec §6.1 原文即此判断,本轮复核成立);类型项 DAG(`g_type_terms` 48B/条 `{tag,a,b,c,d,hash}` + 开放寻址索引 `g_tt_index`,`globals.cr:264-265`)**全在进程内存** | +| `.ccr` **IFACE 段** | §6.1 | **零序列化**(硬证据:`ccr_io.cr` 全文件 `iface` / `impl_for` / `g_methods` 命中数 = **0**,本轮实测):`g_ifaces`(`ESZ_IFACEINFO=2712`)· `g_iface_shape_*`(形状表)· `g_impl_for`(16B/条)· `g_methods`(24B/条)· 本质条目表(13 条静态常量,可随二进制重建)全部只在 corec 进程内 | +| **DFNode.TK 升格** | §6.2 | `OFF_DF_TK`(`dyn_arr.cr:106`;`ESZ_DFNODE=64`)今日 = **粗类型码槽**(spec 原文「基类型常量槽」):`emit()`(`ir_gen.cr:190-206`)把同一 `type_kind` 直写 IR instr(`iri_set_tk`)与 DF 节点(`df_create_node` 第六参);非零写入样本 = `TI_INT`/`TI_DEX`/`1` 等(带 `TI_*`/`TY_*` 类型的 `emit` 调用 63 处,grep -c 实测);`.ccr` NOD 36B 的 `tk` 字段(`ccr_io.cr:113`)即镜像;读回 = `df_rebuild_ir`(`dataflow.cr:448`)+ `cir_cache.cr:511` | +| **corearch 读回** | §6.3 | 消费面已备范式:段表寻址 → **语义对象缓冲** `g_v7_nod_sem`(`ccr_io.cr:447-461`,内核完备 Task 1 的「对象留存形态」)⇒ P4 加段 + 读回映射即可,**不必**动 NOD 主链与线性调度重建(`build_linear_schedule`) | + +### C.2 今日载体盘点(内存态;P4 序列化候选集) + +| 载体 | 布局 / 单元素 | 生命周期 | 入 `.ccr`? | corearch 侧重建性 ⇒ P4 动作 | +|---|---|---|---|---| +| 类型项 DAG | `g_type_terms` 48B + `g_tt_index`(`type_terms.cr`;构造去重、append-only、哈希稳定) | `init_types` 建;跨编译稳定 | 否 | **不可重建**(前端语义)⇒ **TYPE 段第一内容**(spec §6.1:DAG tag/a..d + 原子扩展) | +| 本质条目表 | `g_iface_entries` 13 条 × 40B `{ak, ti_row, name_ni, lit_code, ops}`(`iface_registry.cr`) | 编译期常量 | 否 | 可重建(常量随二进制)⇒ 可选入段(IFACE 段「原生条目」面) | +| 横切形状表 | `g_iface_shape_{names,terms}` + count(`globals.cr:55-56`) | 进程级;`reset_frontend_state` 复位 | 否 | **不可重建**(生产路径**零注册**——名字面为空,消费者走无名字入口 `iface_satisfies_term`;见 C.6-①) | +| 用户接口表 | `g_ifaces`:`ESZ_IFACEINFO=2712`(24 + 16×`ESZ_IFMETHOD=168`);方法条目 = 名字(0)/参数计数(8)/返回裸码(16)/参数裸码×8(24..87)/**参数节点×8(88..151)/返回节点(152)/接收者模式(160)** | parser 建;reset 清零 | 否 | **不可重建** ⇒ **IFACE 段第一内容**;节点槽 = 类型节点行号(TYPE 段就位后可跨端解析) | +| impl 边 | `g_impl_for` 16B `{trait_ni, type_ni}`(`parser.cr:1866-1869`) | 同上 | 否 | **不可重建**(名义口径的边;结构口径下 = 声明元数据——见 C.6-②) | +| 方法表 | `g_methods` 24B `{type_ni, method_ni, mangled_ni}`(`parser.cr:1855-1857`) | 同上 | 否 | **不可重建**(查表身份:`type_has_method`/`check_iface` 全走它) | +| 签名规范项 | `sh_sig_term_*` / `sh_iface_shape_term` / `sh_shape_member_at`(`ty_shadow.cr`,唯一构造点) | 派生 | 否 | **可重建**(从 IFACE/TYPE 段载入后重算——P4 优先,不必入段) | +| 类型行表 | `g_types`(行号空间;SYM 里已以 u32 索引引用) | `init_types` 建 | 否 | 部分可重建(原生 8 行常量;命名/应用行随 SYM + TYPE 段) | +| 桥接/影子缓存 | `g_shadow_map` / `sh_map_*` / `sh_unf_map_*` | `init_types` 失效 | 否 | 可重建(缓存,P5 下线面) | +| SYM 段 | 函数/全局/结构/枚举(已有) | — | **是** | 唯一既有类型邻接载体(结构字段类型、函数形参/返回 = u32 类型索引) | + +### C.3 `.ccr` 段表机制(加段 = 四面同改 + 版本裁决) + +**字节格式权威** = `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(⚠ 统一设计 spec §6.1 关联行写的是 `2026-09-07-lattice-ir-v7-format.md`——**该文件名不存在**,实测目录实查;引用按 `2026-09-09` 版)。要点(逐条对照 `ccr_io.cr`): + +- **布局**:Header 16B(magic `"CCR1"` + version=7 + `seg_count` + reserved)+ 段表 `seg_count × 12B` `{tag, offset, size}`(offset 相对文件头)+ 段体连续。tag 常量 `1=STR 2=SYM 3=NOD 4=ENT 5=REG 6=EDG`(`CCR_SEG_COUNT=6`,`ccr_io.cr:108`);**`7+` 预留**(format spec §2 原文:驱逐标注段/证书段)⇒ TYPE/IFACE 取 7/8 不与预留冲突。 +- **加段四同改**(缺一即 loader/写侧不一致):① `CCR_SEG_COUNT`(`ccr_io.cr:108`)+ calc 尺寸(`:342` `16 + CCR_SEG_COUNT*12`);② **save 写侧**(`:468-530`,段表规范序 + 段体连续);③ **load 读侧**(`:846+`)——`tg < 1 || tg > 6` 闸(`:899`)· `tg != ri + 1` 规范序闸 · `soff != cursor` 连续闸 · 逐段 `have{1..6}` 去重标量(**硬编码标量族** ⇒ 加段须扩 `seg_off7/8` 等);④ **测试面** `tests/selfhost/test_ccr_v7.py`(结构性断言;载体落地后「逐字节同」判据作废——按 TODO #26 换结构断言)。 +- **版本裁决**:v7-only、无转换工具(format spec §4 规则 4 + v6→v7 先例)。加段是否 bump version(7→8)由 P4 裁:若沿用 7,则 loader 的「v7 = 恰六段规范序」语义必须显式放宽(`tg > 6` ⇒ `tg > 8`)并在 format spec 同步。 +- **corearch 读回范式**:loader 现有「段 → 内存态/语义对象」链路(STR→g_strs;SYM→globals/funcs 声明区;REG→派生边界;NOD→`g_v7_nod_sem`;ENT→内存 24B 表并去 version;EDG→`g_v7_edges` 邻接校验)可直接照搬:TYPE 段载入 → 类型项 DAG 重建(节点表 + 去重索引重放);IFACE 段载入 → `g_ifaces`/`g_impl_for`/`g_methods`/形状表重建。**判据 = 重建后引擎判定在 corearch 侧可跑**(spec §6.3 的 `atom_of` 承接面)。 + +### C.4 载体落地会翻的钉(pinned tests;逐条 = 触发条件 → 处置) + +| 钉 | 位置 | 触发条件 | 处置 | +|---|---|---|---| +| 零 `str_intern` 三守门 | `type_selftest.cr` `x2.no_str_intern` · `ifc.mangling_zero_str_intern` · `isat.no_str_intern_on_missing` | 形状**名字面**生产注册(`T: 可索引`)或任何初始化路径新驻留串(A.3-② 硬约束) | 按 C.6-① 裁决后重定(delta 守门 → 登记式增长) | +| `.ccr` 逐字节同记录值 | P3b 各报告 §4b:`ptr_arith` 88943B `54e3856b…` · `generics_test` 130974B `97be2f9e…`;`test_ccr_v7.py` 结构块断言 | TYPE/IFACE 段入序列化(尺寸/哈希必变) | 记录值作废 → 结构断言(TODO #26 口径);**ELF canary `95084e7b…d475` 不得变**(变 = 发射面泄漏,停下上报) | +| 形状注册面 | `x2.axis_a_registered` / `x2.shape_no_leak_after_reset`(注册面 = 测试名) | 名字面生产注册落地 | 注册面改生产名后重定 | +| 签名编码面 | `isat.axis_c_ret_code`(返回码语义 = 编码相等)· `inst_encoding_limit_named_ret_rejected` · `ifc.sig_*` | 段内类型项与进程内项同构后,签名比较可直接读项 | 编码面例外可删(改走类型项)——**先证同构** | +| 本质条目数硬值 | `type_selftest.cr:2362` `iface_count() == 13`(配套 `iface_entry(AK_NULL) == -1`) | 条目表扩列(AK_NULL / sum / fn 类) | 按 C.6-③ 裁决后重定 | +| 影子站点面 | `--type-shadow` 站点集/计数(`g_shadow_site_counts`) | TK 升格若改 `type_equal` 站点面 | 站点面属 P5 清理(B.5);P4 不动 | +| 清单双注册面 | `build_selfhost_native.py` ↔ `src/targets/x86_64-linux/_import.cr` ↔ `src/compiler/_import.cr` | 任何清单/段表改动 | 两面同改(B.6);`test_backend_bootstrap.py` 必跑(rc=0 + `error[` = 0) | + +### C.5 开工必守(P3 继承硬口径) + +1. **判据 = 结构性断言 + 行为面**(TODO #26):载体批**不再**以「`.ccr` 逐字节同」为判据;改用 `test_ccr_v7.py` 结构断言 + 语义零变化 + 自举稳定(`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42)+ **ELF canary IDENTICAL**。 +2. **清单双注册面**(B.6):动任何清单 ⇒ 同步核 project-mode 与 concat 面两处。 +3. **三态纪律**:载入失败/段缺失 ⇒ 拒绝(loader 既有 `-1` 范式),**不得**静默当空表/当 0。 +4. **新硬错误先 report-only**(Global Constraints 第 6 条)。 +5. **一构建一编译串行 + `nice -n 19` + `clean-cache` 先于任何 `.ccr`/缓存态比较**(TODO #5 末条/#26);cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +6. **发射面零泄漏先证**:任何 tk 升格步骤(迁移期双槽)须先证 ELF canary `95084e7b…d475` 不变;变 ⇒ 停下上报。 + +### C.6 待裁决面(P4 开工前须定 / 须先裁;逐条 = 现状 + 选项 + 影响) + +1. **形状命名消费语法(`T: 可索引`)+ `.ccr` STR 段是否接受增长**(A.3-② 的后续裁决;**同一裁决的两面**):现状 = 生产路径零注册(形状表 0 条目;消费者走无名字入口 `iface_satisfies_term`,T2/T6 均记为「命名消费语法待裁决」);命名化需驻留名 ⇒ STR 段变 ⇒ 本包 C.4 全部「逐字节同」记录值失效。 +2. **用户轴名义 vs 结构**(**TODO #38**;T6 裁决①的翻转 = 维护者裁决项):名义 ⇒ `g_impl_for` 边查询直接判满足(表已在,零新数据面)+ 翻转 5+ 既有钉(`iface_constr_legacy_pass` 等);结构 ⇒ IFACE 段「满足关系」仍按方法集重算,`g_impl_for` 降为声明元数据。 +3. **条目表扩列 vs 引擎侧另表**(B.1 末;`iface.count == 13` 为钉死硬值):若 TYPE/IFACE 段要覆盖 `AK_NULL`(T4 原生第九员)/`AK_SUM`/`AK_FN` 面,须先裁「本 13 条表扩列」还是「段内另设引擎原子表」。 +4. **整形状 `ty_sub` 路由不可用**(T6 §1.2c 两条实测:引擎 `AK_NAMED` 不展开 ⇒ 命名行恒 -1;product 比较 = 结构相等非子集包含 ⇒ 实现方带额外方法被误拒):若 P4 想「形状项 = 段的直接消费单位」,须先修引擎(**满足判定侧**启用展开;**不得**动等价面)或维持逐成员判定(现状)。 +5. **运行期可选表示统一**(B.7 一等条目:裸值 + `Some` 臂 ⇒ 产物/解释器双路径 rc=139,check/build rc=0 零诊断):二选一(①构造/赋值侧装箱 ②解包侧判表示),落点 `ir_gen.cr` + 后端;spec 明示与 TYPE 段**一并裁**。 +6. **MAX_\* 统一处置**(B.4-7):`MAX_GENERICS=4` · `MAX_IFACE_METHODS=16` / `MAX_IFACE_METHOD_PARAMS=8`(T6 已立硬错护栏)· `MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES=16`(TODO #35 写入侧护栏未修)——「先加护栏、再评估解除」;解除面 = 表迁侧表 = 段布局定长槽的同一决策。 +7. **`never` 语义**(B.4-2:引擎 `AK_NEVER` 与 `AK_INT` 互斥非 ⊥ ⇒ `never` 不满足原生约束/不给 `never ⊆ T?`)与**符号档长度约束**(B.4-11 = VC 义务)——段内条目语义须表态(是否给 `never` 独立原子槽)。 +8. **DFNode.TK 升格的发射面判据**:tk 消费者 = 后端发射器(`iri_set_tk` 读点族)+ `.ccr` NOD `tk` ⇒ 升格必须保持发射行为(ELF canary 不变)或显式重定判据;spec §6.2「迁移期双槽、收尾单槽」即此缓冲。 diff --git a/docs/superpowers/plans/2026-09-12-r2-p4-carrier.md b/docs/superpowers/plans/2026-09-12-r2-p4-carrier.md new file mode 100644 index 00000000..277acf0c --- /dev/null +++ b/docs/superpowers/plans/2026-09-12-r2-p4-carrier.md @@ -0,0 +1,404 @@ +# R2 P4(载体)实施计划:`.ccr` TYPE/IFACE 段 + DFNode.TK 升格 + corearch 读回 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 兑现统一设计 spec §6(载体)与 §6.4(语义保鲜兑现点)——类型/接口信息**随载体出前端**:`.ccr` 新增 **TYPE 段**(类型行表 + 类型项 DAG)与 **IFACE 段**(原生条目 + 横切形状 + 用户接口签名项 + impl 边 + 方法表);corearch 载入两段**重建**类型项表与接口表,使引擎判定在 corearch 侧可跑(§6.3 的 `atom_of` 承接面);DFNode.TK 进入**迁移期双槽**(原子码原样 + 类型项引用),发射面零扰动;B.7 一等缺口(`T?` 裸值 + `Some` 臂双路径 rc=139)按裁决 5 于本批(解包侧判表示)闭合。 + +**Architecture:** 五条主线:① **链接面纯化**(Task 0:把 `type_engine.cr` 的 iface 簇与 `iface_registry.cr` 的 `iface_kind_of` 移出,corearch 才能链接引擎核——**前置且纯搬运**);② **段机制**(Task 1:tag/版本/加段四面/loader 闸 + 结构断言重定;两段先空壳落盘);③ **TYPE 段**(Task 2:确定性装填 + 行表/DAG 序列化 + corearch 读回 + 判定原语跨进程同值);④ **IFACE 段**(Task 3:扩列 + 形状命名化 + 签名项化 + 读回对拍);⑤ **图与表示面**(Task 4 TK 双槽 + Task 5 可选表示解包侧 + Task 6 MAX_* 护栏);Task 7 收官。 + +**Tech Stack:** Core 自举栈。`src/compiler/ccr_io.cr`(段表/写侧/读侧)· `type_terms.cr`(项 DAG + 哈希 + 开放寻址索引 + 扩容)· `type_engine.cr`(判定核 1-612 行 + iface 簇 613-772 行)· `iface_registry.cr`(条目表/形状表/查询 API)· `ty_shadow.cr`(桥接/展开/签名项)· `checker.cr`(类型行表 + `init_types`)· `dataflow.cr`/`ir_gen.cr`/`interp.cr`/`provenance_verify.cr`(DFNode.TK 消费面)· `cir_cache.cr`(`.cir` 快照)· `corearch.cr` + `src/lattice/ent_kernel.cr`(读回通道)。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` **§6.1**(TYPE/IFACE 段内容)、**§6.2**(DFNode.TK 升格 = 迁移期双槽 → 收尾单槽)、**§6.3**(corearch 读回)、**§6.4**(语义保鲜兑现点);字节格式权威 = `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(⚠ 统一 spec §6.1 关联行写的 `2026-09-07-lattice-ir-v7-format.md` **文件不存在**——实测目录实查,引用一律按 2026-09-09 版;P3 计划附录 C.3 已勘误)。**P4 起点文档 = P3 计划附录 C**(`docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md:541-609`)。 + +**前置状态(已实测,非本计划目标)**:P3 全阶段(P3a + P3b)收官于 HEAD `55280aa5`;`selftest-types` **333/333**;ELF canary `95084e7b…d475` 全阶段 IDENTICAL;自举链 `corec2==corec3` + N06=0 + 冒烟 42 稳定;`ccr_io.cr` 全文件 `iface`/`impl_for`/`g_methods` 命中数 = **0**(附录 C.1 实测,本轮复核成立);类型项 DAG **全在进程内存**(`globals.cr:265-273`)。 + +--- + +## P4 实施状态(收官登记,2026-09-13;本表 = 各任务勾选框的权威替代) + +**P4 载体批 = 8 提交**(T0/T1/T2/T3/T4/T5/T5 补/T6)+ **附带件 1 提交**(TF01 收口,非任务号)+ 计划定稿 `3f8885ec`;逐件详版 = `.superpowers/sdd/p4-task{0..6}-report.md` + `p4-task5-collision-note.md` + `p4-task5-verify-independent.md`。 + +| Task | 内容 | 提交 | 状态 / 实施偏差(本批实测登记) | +|---|---|---|---| +| T0 | 引擎核纯化拆分(iface 簇 + `iface_kind_of` → `iface_axis.cr`) | `7c822003` | ✅ 零行为变化。清单面**实测四处**(计划列 2 处):`src/compiler/_import.cr` 必改 + `tests/selfhost/test_compile.py`(计划未列,全枚举捕获) | +| T1 | 段机制 TYPE=7/IFACE=8 + 版本 7→8(两段空壳) | `97febdbf` | ✅ 重定面**实测五处**(计划列 4 处):+`test_region_cfg.py` | +| T2 | TYPE 段内容 + 确定性装填 D13 + corearch 读回 | `3d604e11` | ✅ D13 项面承诺成立;**冷/热预测半成立**(行表 15→12——见附录 D-1 勘误) | +| — | TF01 收口(`check src/compiler` 归零) | `6822102a` | ✅ 附带件:T2 §9-② 收口路径 (a) 兑现;`check` job 判据自 T3 起改按 rc=0 | +| T3 | IFACE 段内容(扩列 16 + 形状命名化 + 签名项化 + impl 边/方法表) | `8f4af99b` | ✅ STR 段增长按裁决 1 接受;内存槽扩不入盘/不入快照 | +| T4 | DFNode.TK 迁移期双槽(允许清单制) | `387abf2e` | ✅ **计划 D15/Step 4 被实测推翻**:`.cir` 快照不落项槽 + `CIR_CACHE_VER` 保持 17(装载侧 emit 等价时点重派生) | +| T5 | 可选运行期表示——解包侧判表示(裁决 5) | `2be26cc5` | ✅ B.7 一等条目闭合;后四条根因 = 计划未列面(逐条实测) | +| T5 补 | `IR_LOAD_ENUM_TAG` tag 读取源补全局行分派 | `cf368259` | ✅ 预存静默双路径分歧闭合(碰撞窗独立验证档证实必要性) | +| T6 | `MAX_*` 写入侧护栏(TODO #35;P022/P023) | `cc015ae4` | ✅ 三上限**不解除**(评估 = 报告 §3);同族第三处(结构体字段)RED 步发现 | +| T7 | 收官(回归 + 判据 + 统一台账 + 文档 + P5 交接) | 本提交 | ✅ 见下 Task 7 与**附录 D(P5 交接包)** | + +**收官判据(T7 本轮实跑,2026-09-13)**:五 CI job 全 rc=0 · 全枚举 **55/55 rc=0**(48 selfhost + 7 bootstrap)· `selftest-types` **355/355** · ELF canary `95084e7b…d475` **IDENTICAL** · `corec2==corec3` `cmp` IDENTICAL(双 2792078B `4919023b…`)+ N06=0 + 冒烟 42(corec/corec2)+ `corec3 --help` rc=1 · 同源对拍(pre-P4 二进制 `9a583215…` × 当前源 vs 当前二进制 × 当前源):影子摘要两侧同值(`decisions=32620 agree=32620`,全计数 0,站点覆盖同报;两侧 dumps 目录**均空 = 无差异条目 ⇒ `sh_dump_write` 早退**,勿引「dumps diff 空」——findings §14)——**同源对拍两侧一致**;check/影子**日志面**唯一差异 = TF01 收口(pre-P4 二进制在 5 处语料报 `ty_memo_slot_no_grow` 误报、3 处 rc 1→0;逐行分类见 T7 报告)——P4 内**有意修复**面,非语义回归 · `.ccr` 记录值见附录 D-1(两套口径:`ccr` = 90823B/134701B;`build --static` = 90966B/134844B)。 + +--- + +## 裁决(定案,实施时不得再议) + +**维护者裁定(2026-09-12,八条;P3 计划附录 C.6 的八项待裁决面逐条收口,均按推荐项)** + +| # | 议题 | 裁定 | +|---|---|---| +| 1 | 形状命名化 + `.ccr` STR 段增长 | **形状名入生产注册面;接受 STR 段增长**;判据按 TODO #26 换结构断言(`tests/selfhost/test_ccr_v7.py` 全绿 + 语义零变化 + 自举稳定,不以「与旧版逐字节同」为准) | +| 2 | 用户轴名义 vs 结构(TODO #38) | **维持结构性**(方法集包含);`g_impl_for` 降为**声明元数据**(入段、不参与判定) | +| 3 | 本质条目表 | **扩列**(本表增行:`AK_NULL` / `AK_SUM` / `AK_FN`)⇒ 硬值 13 随之重定;`name_ni` 列按裁决 1 一同填充 | +| 4 | 整形状 `ty_sub` 路由 | **不启用**;维持逐成员判定(`iface_user_satisfies_ii`,`type_engine.cr:738`),形状项 = **签名载体** | +| 5 | 可选运行期表示 | **解包侧判表示**,与 TYPE 段同批(本计划 Task 5) | +| 6 | `MAX_*` | **先补护栏、再评估解除**(本计划 Task 6;`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES` 写入侧护栏 = TODO #35) | +| 7 | `never` | **给独立原子槽**(保留 `AK_NEVER` 独立原子语义,段内原子条目面显式落槽;不作 ⊥ 折叠) | +| 8 | DFNode.TK 升格 | **维持 ELF canary 不变**;变即停下上报(本计划 Task 4 的硬判据) | + +**本计划裁定(D9–D18;每条 = 决策 + 理由,实施时按此执行)** + +| # | 决策 | 理由 | +|---|---|---| +| D9 | **段 tag:`TYPE=7` / `IFACE=8`**;format spec §2 的「`7+` 预留」两段(驱逐标注段/证书段)**顺移 9/10**(spec 同步改一行) | 预留两段在代码中**零实现**(`ccr_io.cr` 无对应常量;驱逐机制按实例设计 §3.2 声明「不实现」,证书按 v6 spec §185「不落本文件」)⇒ 仅与注释冲突;取 7/8 保住 loader 的规范序闸 `tg == ri+1`(`ccr_io.cr:900`)与段体连续闸(`:901`)两不变式,加段成本 = 闸界 6→8 一处。**替代方案(若维护者坚持保留 7/8 给预留段)= TYPE=9/IFACE=10**,代价 = 规范序闸必须改形(`tg == ri+1` 不再成立,须换「允许集 + 单调序」双闸),本计划不采 | +| D10 | **版本 bump `CCR_VERSION` 7 → 8**(`ccr_io.cr:107`) | 若留 7,则「v7 = 恰六段规范序」语义必须放宽为「6 或 8 段」,**旧 6 段文件会被新 corearch 静默接受**(TYPE/IFACE 缺席 = 空表)——正是三态纪律要消灭的静默类(C.5-3)。bump 后旧文件在版本闸(`ccr_io.cr:875`)整类拒收,v6→v7 同日切换先例同款。**文件名/测试名不改**(`test_ccr_v7.py` 保留,引用面 40+ 处,改名收益为零;文档注明「v8 = v7 段表架构的加法扩展」) | +| D11 | **TYPE/IFACE 两段必备**(loader `have7/have8 == 0 → 拒绝`,并入 `ccr_io.cr:919` 的必备集) | 可选段 = 两种 `.ccr` 在野,任何消费者都要处理缺席 = 静默降级面;本阶段承诺即「信息恒随载体」(§6.4)。与 ENT 的可选先例(v5 兼容语义)不同源 | +| D12 | **TYPE 段 = 两小节**:①类型行表(`kind/data/extra`,24B/条)②类型项 DAG(`tag/a..d`,40B/条,**哈希不落盘**,加载时 `tt_hash5` 重算) | §6.1「DAG:tag/a..d」原文即五字段;哈希是五字段的纯函数(`type_terms.cr:35`)⇒ 落盘 = 冗余 + 第二真源。行表即 §6.1 的「原子条目扩展」(`kind/data/extra` + 用户类型名 = `TYP_NAMED` 行的 `data`(STR 索引)+ 语义域标注**不入盘** = 由 `kind/data` 经 `iface_by_ty_code`(`iface_registry.cr:234`)派生,理由 = 同一映射不得两份(P2b 刚治过的病) | +| D13 | **确定性装填**:`save_ccr` 前先做「项层规范化重建」——项表清零 + 索引失效 + 引擎 memo/预算复位 + 桥接/展开缓存复位 → 形状重注册 → 按**行序**逐行装填 → 按**接口声明序**装填签名项 → 再序列化 | 项 DAG 是**惰性**填充的(`sh_term_of_ti` 只在判定时建项)⇒ 直接落盘 = 段内容随「判定历史」变(**冷/热 `.cir` 缓存命中 ⇒ 判定次数不同 ⇒ 段不同** = 已登记的冷/热分歧类,TODO #5 末条)。重建后段内容 = 类型表/接口表的**纯函数**。前置核查:`save_ccr` 的两处调用点(`main.cr:612` / `:636`)之后不得有类型项消费者(逐站点核;`sh_finish`(`main.cr:214`)只读 `g_shadow_*` 计数,不在复位面内) | +| D14 | **IFACE 段 = 五小节**:①原生条目②横切形状(名 ni + 项)③用户接口(方法签名**存类型项索引**)④impl 边⑤方法表。**签名裸码(`TY_*`)不入段** | §6.1 明列 IFACE =「条目(原生/横切/用户)+ 满足关系」;裸码是前端**编码层**(P3b 已钉其唯一活消费者 = S6 映射面,`dyn_arr.cr:148-154`),对跨端消费零信息(非原生类型一律塌缩);且「存 AST 节点行」对 corearch 无意义(无 AST)⇒ 必须在 save 侧解析成项 | +| D15 | **DFNode.TK 升格 = 内存双槽**:`OFF_DF_TK` 语义/字节**零变化** + 新增 `OFF_DF_TK_TERM`(类型项引用;-1 = 无);`.ccr` NOD 36B 记录**不变**;~~`.cir` 快照**入项槽**并 `CIR_CACHE_VER` 17→18~~ **【实施修正(实测反证):快照不落项槽、`CIR_CACHE_VER` 保持 17——装载侧在 emit 等价时点重派生。原「快照必须入槽否则冷/暖分歧」前提被 `--dump-tk-terms` 实测推翻:入槽版本暖路径 `bad_term=329`(项索引 = 进程内表索引,全数悬空);不落盘 + 重派生后冷/暖 1298 行逐行同、`bad_term=0`。理由另含第二真源纪律。见报告 §4 + plan Step 4】** | §6.2 的「迁移期双槽」;NOD 盘记录的 `tk` 是**混用码**(基类型行 + 小旗标,如 `IR_BOUNDS_CHECK` 的 `ti=1` 动态上限),不是行号 ⇒ 无法从 `tk` 反推项 ⇒ 盘面项槽归 P5 单槽化(届时另定小节/扩记录)。~~快照必须入槽,否则 cache-hit 恢复路径与冷路径分歧(`cir_cache.cr:246-253`/`:426` 对称写读)~~ | +| D16 | **链接面纯化**:`type_engine.cr` 的 iface 簇(`:613-772`)与 `iface_registry.cr` 的 `iface_kind_of`(`:279-293`)**移出**到 corec-only 文件;corearch 清单 += `type_terms.cr`/`type_engine.cr`/`iface_registry.cr` | 实测:`type_engine.cr` 1-612 行**零** checker/桥接/注册表代码引用(非注释行 grep = 0),iface 簇是唯一耦合段(`find_iface`/`find_func`/`decl_name_of_ti`/`gen_constr_type_ti`/`sh_*`);`iface_kind_of` 读 `get_type_kind`(`checker.cr:314`)。不拆 = corearch 构建 33×N06 式静默未定义(B.6 同族) | +| D17 | **形状名 = 6 个 ASCII 名**:`sequence` / `sequence_ro` / `sequence_rw` / `indexable` / `iterable` / `product` | 与 spec §2.2 的构造子英文名同形、与 `AK_*` 命名同族、避免非 ASCII 进 STR 段。若维护者改中文名(如 `可索引`)⇒ 改的是常量与守门用例名,段布局零影响。**命名消费语法(`T: 可索引`)不在本批**(P3 未开工清单项,另裁) | +| D18 | **段内容构造与序列化解耦**:装填/序列化(引用桥接/checker 层)落 **corec-only 新文件**(`src/compiler/ccr_types.cr`),预生成段体缓冲到 globals;`save_ccr`(`ccr_io.cr`,**corearch 也链接**)只按段表写缓冲 | `ccr_io.cr` 同时在 `backend_support_files`(`build_selfhost_native.py:103-111`)与 corec 清单里 ⇒ 其中任何对前端层符号的引用 = corearch 构建 N06 静默未定义(B.6 monomorph 教训)。解耦后该文件保持「共享层符号」纯度(可用 test_backend_bootstrap 的 `error[`=0 门持续守) | + +--- + +## Global Constraints + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**(多 agent 在场:一律 `jj commit -m … `);所有命令 `nice -n 19`;一构建一编译**串行**;cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +- **比较任何 `.ccr`/缓存态产物前必须 `clean-cache`**(`./build/corec clean-cache`,`main.cr:396`;相对 cwd)。冷/热缓存态 `.ccr` 分歧未修(TODO #5 末条),本批**新增**判定依据:TYPE 段内容由 D13 保证为**纯函数** ⇒ 冷/热两态 `ptr_arith` 的 TYPE 段应逐字节同(新增守门用例)。**【T7 收官勘误(2026-09-13,两条,实测):**(i) **「`.ccr` 冷/热两态逐字节同」对非可选程序本就不可达**(独立验证档实测:`ptr_arith` 冷 `90966B`/`592afa31…` ≠ 热 `90769B`/`16996f1c…`,两态各自**确定性**可重复;pre-P4 二进制同病 `89086B` vs `88954B` ⇒ **预存**,TODO #5 末条家族)⇒ 该判据只对「**可选面零足迹**」程序成立(可选程序关 `.cir` 缓存 ⇒ 冷≡热按构造;T7 实测 `2bfbbe2b…` 冷热同);**非可选程序豁免**(判据改为:冷/热各自确定性 + 与同源基线编译器逐字节同)。**「TYPE 段」面同款半成立**(T2 §8-③):项表/probe 冷/热逐位同(D13 项面承诺成立),但**行表** 15 → 12(热路径跳过 `ir_gen` 而不重建 `alloc_type` 行)⇒ 用例⑮改断言实际成立的不变量(前缀关系 + 项表同值 + 引用域不出冷态表)。**(ii) `.ccr` 记录值有两条命令口径**:`corec ccr F -o O`(T3/T4 记录值:`ptr_arith` 90823B/`generics_test` 134701B)与 `corec build F -o O --static`(产 `O.ccr`;**`--static` 前置 `rt.cr`(`main.cr:423-425`)⇒ +143B**;T5/T6 记录值:90966B/134844B、`ec8413ec…`)——两者各自确定性可重复,引用时必须带口径(T7 复测逐一复现)。】 +- **ELF canary 本计划全任务预期不变**:`clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/…` → sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`。**任一任务 ELF 变 → 停下上报**(类型/载体层改动泄进发射面)。⚠ 该语料对类型层不敏感(附录 C.5-6 口径继承)——它是「发射面零泄漏」证据,**不得**当新语义正确性证据;新语义一律由行为探针承担。 +- **`.ccr` 判据重定(TODO #26,本批自 T1 起生效)**:**不再以「与旧版逐字节同」为准**;改为 `test_ccr_v7.py` 结构性断言全绿 + 语义零变化 + 自举稳定(`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 42)。旧记录值(P3b 报告 §4b:`ptr_arith` 88943B `54e3856b…` / `generics_test` 130974B `97be2f9e…`)自 T1 起**作废**,替换为:新尺寸/哈希(冷缓存实测报告记录)+ 结构性断言。 +- **三态纪律(P0 起继承)**:引擎 -1(预算耗尽/未覆盖面)不得当 0/1 用;**载入失败/段缺失/行不可译 ⇒ 拒绝(不得静默当空表/当 0)**(C.5-3);save 侧装填遇不可译行/签名项 ⇒ **save 拒绝(rc=1 + 诊断)**,且入硬门前先跑全语料 report-only(下条)。 +- **新硬错误先 report-only**:任何把新检查升为 rc=1 门的步骤(装填失败拒绝、`MAX_*` 写入侧护栏、IFACE 段签名项不可建拒绝)必须先在**全语料**(`src/compiler/_import.cr` 自指 + `tests/suite` + `tests/selfhost` 内联源 + `src/stdlib` + `examples`)report-only 跑一遍,清单审查后再开门(`main.cr:146-165`;照 #29/TM03 先例)。语料排除项见 TODO #22。 +- **清单双注册面(B.6,硬性)**:任何清单/段表改动必须**三面同核**——`build_selfhost_native.py`(`corec_files`/`corearch_files`/`corelsp_files`)↔ `src/targets/x86_64-linux/_import.cr`(project-mode)↔ `src/compiler/_import.cr`;每任务回归面**必含** `tests/selfhost/test_backend_bootstrap.py`(rc=0 + 构建日志 `error[` = 0)。 +- **影子通道 = 必要不充分**(T7b concern ④ 继承):`--type-shadow` 只覆盖 `type_equal` 站点面(本站点集 = P5 清理面,`ty_shadow.cr:252-269` 口径),**载体面(段/读回/TK/表示)零覆盖** ⇒ 载体正确性由**结构性断言 + corearch 读回对拍 + 定向探针**承担,不得以影子归零充当证据;每任务仍复跑影子对拍(**站点覆盖与差异数同报**)(**R2 P5 Task 5 起作废**:影子通道已整体下线 ⇒ 本行的「每任务复跑影子对拍」改由 **冻结基线同源对拍 + 行为探针 + 突变控制** 承担,见 `TODO.md` #48 与 `docs/superpowers/specs/2026-09-10-type-shadow-findings.md` §15;本行余下语义——「影子对**载体面**零覆盖 ⇒ 不得以影子归零充当载体证据」——**继续有效**)。 +- **`.cir` 缓存面**:`CIR_CACHE_VER` 手工 bump 是格式/语义代变更的既定配方(`cir_cache.cr:14-31` 的 v14/v15/v16/v17 先例);DF 记录布局/语义一变即**必须 bump**(v16 教训:旧快照会把坏状态原样复活成活产物)。快照对称写读 + 冷/暖两路径产物一致(`test_cache_identity.py` + `test_ccr_v7.py::test_v7_cache_hit_restore_path`)为本批守门。 +- **不得回退既有收纳**:TS01-04/TK02 硬门(#29)、TM03/TG02 门(#31/#33)、F2 常量档拒绝语义与「N 不回身份」、F5 槽位契约(#25/#28)、效应/纯度判据(#26/#27)、`iface_satisfies` 轴序 A>C>B、逐成员判定——本计划一律不动;冲突 → 停下上报。 +- 本语言无三元运算符;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);自测用例**只增不减**(`test_type_engine.py:MIN_CASES` 与 `selftest-types` 逐任务下限见各任务判据)。 +- **发射/解释器零泄漏**:`--dump-objects`(`corearch.cr:352`)与 `cir_cache` 快照在 TK 升格任务中的输出面**必须显式声明预期**(前者不变;后者随 bump 只影响缓存命中率,不影响产物)。 + +--- + +## 侦查底座(2026-09-12 实读,file:line = 当前工作树 HEAD `55280aa5`;未标注者为本轮实测) + +**段表机制(ccr_io.cr)** +- `CCR_MAGIC=827474755`(`:106`)· `CCR_VERSION=7`(`:107`)· `CCR_SEG_COUNT=6`(`:108`,注「规范序 tag 1..6;预留 7+ 不占空间」)· NOD 盘记录 36B(`:113`)· EDG 8B(`:118`)· REG 24B(`:125`)。 +- 尺寸:`ccr_str_seg_size`(`:261`)· `ccr_sym_seg_size`(`:272`)· `ccr_nod_seg_size`(`:322`)· `ccr_edg_seg_size`(`:326`)· `ccr_ent_seg_size`(`:330`)· `ccr_reg_seg_size`(`:334`)· `calc_ccr_size`(`:341`,`16 + CCR_SEG_COUNT*12` 起)。 +- 写侧:`save_ccr`(`:470`)——`ccr_validate_i32_fields`(`:216`)形状守卫 → `populate_nod_objects`(`:452`)+ `compute_live_ranges`(`:489`)→ EDG 收集(`:495`)→ 尺寸/缓冲(`:503-505`)→ Header(`:508-511`)→ **段表 6×12B 硬编码 6 行**(`:529-534`)→ 段体 STR/SYM/NOD/ENT/REG/EDG(`:537+`)。STR 段 = 编译期 `g_strs` **全量**(`:537`)。 +- 读侧:`load_ccr`(`:864`)——`fsize<16` 拒(`:865`)· magic(`:871`)· **版本闸 `ver != CCR_VERSION`**(`:875`)· `seg_cnt > (fsize-16)/12` 拒(`:880`)· 逐段三闸:`tg < 1 || tg > 6`(`:899`)· `tg != ri+1`(`:900`,规范序)· `soff != cursor`(`:901`,段体连续)· 越界(`:902`)· 去重标量 `have1..have6` + `seg_off1..6`/`seg_end1..6`(`:885-909`,**硬编码标量族**)· 必备集 `have1/2/3/5/6`(`:919`,ENT 可缺 `:920`)· 状态重置(`:923-935`)· 逐段装载(`:937+`;NOD → `g_v7_nod_sem` 对象面,`ent_kernel.cr:1010-1032`)。 +- 枚举闸(读回侧):`ccr_io.cr:1121`(`vc > MAX_ENUM_VARIANTS`)/`:1148`(`tc > MAX_VARIANT_TYPES`)——**写入侧零引用**(TODO #35)。 + +**类型行表与类型项 DAG** +- 行表:`alloc_type(kind,data,extra)`(`checker.cr:9-17`,24B/条)· 访问器 `get_type_kind/data/extra`(`checker.cr:314/319/324`)· `init_types()`(`checker.cr:231`)建原生 9 行(`:246-258`)并以 `iface_registry_init()`(`:262`)收尾 · 同名 `TYP_NAMED` 去重侧表 `g_named_dedup`(`checker.cr:125-140`)。 +- 项 DAG:`g_type_terms`(48B/条 `{tag,a,b,c,d,hash}`,`type_terms.cr:22-24`)· 访问器 `tt_tag/tt_a..tt_d`(`:27-31`)· `tt_count`(`:32`)· `tt_hash5`(`:35`)· `tt_mod`(`:62`)· `grow_type_terms`(`:77`)· `grow_tt_index`(`:88`,容量 ≥ 2×项数)· `tt_reindex`(`:108`,重放插入)· `tt_term`(`:136`,装填守卫 + 键 = 五字段全比)· 构造 API(`:181-222`)· `tt_norm`/NNF/DNF(`:229-330`)。 +- 项层全局(`globals.cr:265-273`):`g_type_terms`/`g_type_term_count/cap`、`g_tt_index`/`g_tt_index_cap/count`、`g_tt_nil(_ok)`/`g_tt_top(_ok)`、`g_ty_memo_*`、`g_ty_steps`/`g_ty_budget_max`/`g_ty_exhausted`、`g_ty_lits_*`、`g_ty_uncovered`。 + +**引擎(type_engine.cr)** +- 纯核(1-612 行,**零** checker/桥接/注册表代码引用):`AK_*`(`:18-35`,含 `AK_NULL=15`)· `tt_list_same`(`:208`)· `tt_type_elem_same`(`:228`)· `tt_list_variance_at/_variance`(`:288`/`:309`)· `ty_budget_reset`(`:428`)· `ty_exhausted`/`ty_uncovered`(`:441/442`)· `ty_sub`/`ty_equiv`/`ty_exhaustive`/`ty_inhabited`/`tt_witness`/`ty_disjoint`(`:390/405/471/414/448/606`)。 +- iface 簇(613-772 行,**唯一耦合段**):`IFACE_SAT_BUDGET`(`:632`)· `iface_satisfies`(`:634`,轴 A 形状名 → C 用户接口 → B 本质轴)· `iface_satisfies_term`(`:661`)· `iface_satisfies_ti`(`:673`)· `iface_find_method`(`:693`,唯一实现,mangling 已退役)· `iface_user_satisfies`/`_ii`(`:725`/`:738`,逐成员判定)。 +- 依赖(簇外引用):`find_iface`(`checker.cr:940`)· `find_func`(checker)· `decl_name_of_ti`(`checker.cr:954`)· `gen_constr_type_ti`(checker)· `sh_term_of_ti`/`sh_iface_shape_term`/`sh_shape_member_at`/`sh_iface_self_mode`/`sh_func_self_mode`/`sh_func_sig_term`(`ty_shadow.cr`)· `iface_shape_lookup`(`iface_registry.cr:355`)。 + +**接口/形状面** +- 本质条目表:`ESZ_IFACE_ENTRY=40`(`iface_registry.cr:57`)· `IFACE_ENTRY_COUNT=13`(`:61`)· 建表 `iface_registry_init`(`:85`)· 查询 `iface_count`(`:164`)/`iface_entry`(`:167`)/`iface_ops`(`:179`)/`iface_permits`(`:187`)/`iface_lit_*`(`:202-227`)· `iface_by_ty_code`(`:234`)· `ty_code_to_ti`(`:259`)· **`iface_kind_of`(`:279`,读 `get_type_kind`/`get_type_data` = checker 耦合点)**· `iface_ti_of`(`:296`)· 形状表 `iface_shape_grow/register/lookup/count/reset`(`:319-370`)· `iface_of_term`(`:375`)· 六形状 `sh_shape_seq[_ro/_rw]`/`_indexable`/`_iterable`/`_product`(`:402-433`)。 +- 用户接口表:`ESZ_IFACEINFO=2712`(`dyn_arr.cr:143`)· `OFF_IF_NAME/METHOD_COUNT/GENERIC_COUNT/METHODS`(`:144-145`)· `ESZ_IFMETHOD=168` + `OFF_IFM_*`(`:155-161`)· 上限 `MAX_IFACE_METHOD_PARAMS=8`(`:162`)/`MAX_IFACE_METHODS=16`(`:167`)· parser 写点(`parser.cr:1701+`,方法签名节点 `:1762-1766`/返回节点 `:1778+`)· `g_impl_for` 写点(`parser.cr:1866-1869`)· `g_methods` 写点(`parser.cr:1855-1857`)。 +- 全局(`globals.cr`):`g_ifaces`/`g_iface_count`(`:36`)· `g_impl_for`(`:37`)· `g_methods`/`g_method_count`(`:31`)· `g_iface_entries`/`g_iface_entry_count`/`g_iface_registry_ok`(`:282`)· `g_iface_shape_names/terms/count/cap`(`:55-56`)· `IP_*` 位下标(`:298-300`)。 +- 桥接(ty_shadow.cr):`sh_native_ak`(`:79`)· `sh_base_ak`(`:94`)· 桥接缓存 `sh_map_*`(`:122-201`,`sh_map_reset` `:130`)· `sh_seq_term`(`:219`)· `sh_ref_mut_marker`(`:231`)· `sh_name_token`(`:244`)· `sh_null_term`(`:269`)· `sh_term_of_ti`(`:274`,per-ti 缓存)· 展开层 `sh_struct_term`(`:787`)/`sh_variant_term`(`:828`)/`sh_enum_domain_term`(`:841`)/展开缓存 `sh_unf_*`(`:671-738`)· 签名项 `sh_sig_term_of_ti`(`:892`,规范形)/`sh_sig_term_of_node`(`:964`)/`sh_iface_sig_param_term`(`:974`)/`sh_iface_sig_ret_term`(`:984`)/`sh_iface_self_mode`(`:990`)/`sh_func_sig_*`(`:997-1085`)· `sh_iface_shape_term`(`:1086`)/`sh_shape_member_at`(`:1119`)。 + +**DFNode.TK 面(全消费点清点,9 处)** +- 定义/布局:`ESZ_DFNODE=64`,`OFF_DF_TK=40`(`dyn_arr.cr:103-107`)· 建点 `df_create_node`(`dataflow.cr:92`,tk 写 `:102`)· 唯一调用点 `emit`(`ir_gen.cr:193-206`,tk 直传 `:202/:205`)。 +- 读者:`df_rebuild_ir`(`dataflow.cr:448`,tk → `iri_set_tk`)· `ir_gen.cr:129`(`arr_len_lit_of` 的 `TI_STR` 判定)· `interp.cr:256`/`:546`(解释器两处)· `provenance_verify.cr:57`(宽度)· `cir_cache.cr:251`(快照写)/`:426`(快照读)· `ccr_io.cr` NOD 段(盘 36B 的 `tk` 字段)。 +- `.cir` 快照:`CIR_CACHE_VER=17`(`cir_cache.cr:31`,历史 v14-v17 逐版注记 `:14-31`);头部 = magic/ver/**编译器身份 cid**/fp/sig/name(`:220-226`),身份闸 `:367-369`;DF 记录 8 槽对称写读(`:246-253` / `:426`)。 + +**构建/测试面** +- 清单:`build_selfhost_native.py` `common_files`(`:35-45`:runtime + stdlib(io/fmt/cli/toml) + ast/globals/dyn_arr)· `backend_support_files`(`:103-111`:ccr_io/hotpatch/arena/goroutine/chan/sched/corearch)· `corec_files`(`:298+`,引擎三件 `:313-323`;monomorph 迁入注 `:334`)· `corearch_files = common + hit_engine + kernel + backend_support`(`:366-368`)· `corelsp_files`(`:383-418`,含引擎三件 + 影子层,**无 ccr_io**)。 +- 项目模式双注册:`src/targets/x86_64-linux/_import.cr`(B.6 事发面)+ `src/compiler/_import.cr`。 +- 测试:`tests/selfhost/test_ccr_v7.py`(**27 例**;`SEG_TAGS=[1..6]` `:83`·`HEADER_TABLE=88` `:88`·`seg_count == 6` 断言 `:129`·版本拒收 `:1088`)· `tests/selfhost/test_backend_bootstrap.py`(#31 未挂 CI,**本批每任务必跑**)· `tests/selfhost/test_cache_identity.py` · `tests/selfhost/test_type_engine.py`(判据下限)· `src/ci/run.sh` selfhost 挂点(`:68-90`)。 +- 自测守门(本批会翻的钉):`isat.no_str_intern_on_missing`(`type_selftest.cr:420`)· `x2.no_str_intern`(`:619`)· `ifc.mangling_zero_str_intern`(`:866`)· `x2.axis_a_registered`(`:599`)/`x2.shape_no_leak_after_reset`(`:609`)· `iface.count`(`:1362`)· `iface_entry(AK_NULL) == -1 && iface_count() == 13`(`:2362`)· `isat.axis_c_ret_code`(`:368`)· `tests/selfhost/test_iface_satisfies.py:318`(`inst_encoding_limit_named_ret_rejected`)。 +- corearch 通道:`load_ccr`(`corearch.cr:339`)→ `init_backend_arrays`(`:341`)→ `build_linear_schedule`(`:347`)→ `--dump-objects`(`:352`,`dump_object_surface` `:232`,逐节点打印 `nod_*` 对象面)。 + +--- + +## Task 0(前置,纯搬运):引擎核纯化拆分——corearch 链接面开路 + +**Files:** New `src/compiler/iface_axis.cr`(corec-only:iface 簇 + `iface_kind_of`)· Modify `src/compiler/type_engine.cr`(`:613-772` 整段移出,保留纯核)· `src/compiler/iface_registry.cr`(`:279-293` 移出)· `build_selfhost_native.py`(`corec_files`/`corelsp_files` 增新文件;**corearch 清单本任务不动**)· `src/compiler/_import.cr` + `src/targets/x86_64-linux/_import.cr`(双注册面同核)· `src/compiler/type_selftest.cr`(若有符号位置断言) + +**Interfaces:** +``` +// iface_axis.cr(corec-only;内容 = 原 type_engine.cr:613-772 逐字搬迁 + iface_kind_of 逐字搬迁) +fn iface_satisfies(t_ti: int, iface_ni: int) -> int // 轴 A>C>B(签名/语义零变化) +fn iface_satisfies_term(t_ti: int, shape: int) -> int +fn iface_satisfies_ti(t_ti: int, cti: int) -> int +fn iface_find_method(type_ni: int, method_ni: int) -> int +fn iface_user_satisfies(t_ti: int, iface_ni: int) -> int +fn iface_user_satisfies_ii(tname_ni: int, ii: int) -> int +fn iface_kind_of(ti: int) -> int // 自 iface_registry.cr 移入(读 get_type_kind/get_type_data) + +// 纯化后仍留在 type_engine.cr / iface_registry.cr 的面(corearch 链接目标) +// type_engine.cr(1-612 行):ty_sub/ty_equiv/ty_disjoint/ty_inhabited/tt_witness/ty_exhaustive/ +// tt_norm 族/tt_list_same/变型表/预算守卫/tt_list_variance* +// iface_registry.cr(除 iface_kind_of 外全部):条目表 + 形状表 + iface_of_term + ty_code_to_ti + iface_by_ty_code +``` + +- [ ] **Step 1: 红态(链接面实证,**不改仓库文件**)**:以 `/tmp` 下一次性脚本读 `build_selfhost_native.py` 的清单,**在内存中**把 `type_terms.cr`/`type_engine.cr`/`iface_registry.cr` 追加进 corearch 集后 concat 编译(等价于临时清单变更,但不落任何仓库改动——铁律 3「文件永久不允许还原」下**不得**走「改了再还原」路径)→ **期望红**:构建日志 `error[` 非零(N06 未定义:`find_iface`/`find_func`/`decl_name_of_ti`/`gen_constr_type_ti`/`sh_term_of_ti`/`sh_iface_shape_term`/`get_type_kind` 族)。**记录清单与计数**(= 拆分覆盖面,入报告)。 +- [ ] **Step 2: 拆分(纯搬运)**:新建 `src/compiler/iface_axis.cr`,把 `type_engine.cr:613-772`(`IFACE_SAT_BUDGET` + 7 函数,含段头注)**逐字**移入;把 `iface_registry.cr:279-293`(`iface_kind_of` + 头注)**逐字**移入。原位留一行指路注释(`// iface 簇已移至 iface_axis.cr(R2 P4 Task 0,链接面纯化)`)。 +- [ ] **Step 3: 清单三面同改**:`build_selfhost_native.py` 的 `corec_files` **与** `corelsp_files` 在 `iface_registry.cr` 之后插 `src/compiler/iface_axis.cr`(corearch 清单**本任务仍不加**——加面归 Task 2/Task 3,按需分步);核 `src/compiler/_import.cr` 与 `src/targets/x86_64-linux/_import.cr`(project-mode 面:本任务不引入新依赖,若两文件均未列新文件则**不动**,在报告登记「本任务投射面零变化」)。 +- [ ] **Step 4: 判据(零行为变化)**:`selftest-types` **333/333 不变**(用例数零增减);`test_ccr_v7.py` 27/27;ELF canary IDENTICAL(clean-cache);`.ccr` 逐字节同(`ptr_arith` 88943B `54e3856b…` / `generics_test` 130974B `97be2f9e…`,冷缓存);影子对拍复跑(`decisions` 同值、全计数 0、站点覆盖同报);全量回归(五 CI job + `test_backend_bootstrap.py` rc=0 + 构建日志 `error[` = 0)+ 自举链(`corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42)+ 三二进制(corec/corearch/corelsp)构建 rc=0。 +- [x] **Step 5: 提交**:`refactor: R2 P4 Task 0——引擎核纯化拆分(iface 簇 → iface_axis.cr;iface_kind_of 移出注册表)为 corearch 链接面开路;零行为变化` + +## Task 1:段机制——TYPE=7 / IFACE=8 + 版本 7→8 + 加段四面(两段空壳) + +**Files:** Modify `src/compiler/ccr_io.cr`(常量/尺寸/写侧/读侧/闸族)· `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(§1 版本表 / §2 tag 行 + 布局图 / §3 新增 §3.7 TYPE、§3.8 IFACE 定义 / §4 校验规则)· `tests/selfhost/test_ccr_v7.py`(结构性断言重定)· New `tests/selfhost/test_ccr_types.py`(两段结构性断言新套件)· `src/ci/run.sh`(挂钩) + +**Interfaces:** +``` +CCR_VERSION : int = 8; // ccr_io.cr:107(7 → 8) +CCR_SEG_COUNT : int = 8; // ccr_io.cr:108(6 → 8;tag 7=TYPE 8=IFACE) +CCR_SEG_TYPE : int = 7; CCR_SEG_IFACE : int = 8; // 新增常量(数值权威 = 段序) + +fn ccr_type_seg_size() -> int // 4 + (段体缓冲长度),与写侧同一来源 +fn ccr_iface_seg_size() -> int +// 段体缓冲(D18 解耦:内容构造在 corec-only ccr_types.cr;本文件只搬运) +g_ccr_type_seg : string, mut; g_ccr_type_seg_len : int, mut; +g_ccr_iface_seg : string, mut; g_ccr_iface_seg_len : int, mut; +``` + +- [ ] **Step 1: 用例(红)**:`tests/selfhost/test_ccr_types.py` 新增 ≥6 例(本步先立「机制面」四条,内容面随 Task 2/3 增长):① 段表 = 8 段、tag 序 1..8、offset 连续、`end == fsize`;② `seg_count` 字段 = 8、version = 8;③ loader 拒收 **6 段**(把 T1 前的旧 8 段文件截成 6 段 / 或 `seg_cnt` 改成 6)与 **缺 TYPE 段**、**缺 IFACE 段**(byte mutation 删段/改 tag);④ 空壳两段(count = 0)时 corec `build` + corearch `--elf` 全链 rc=0、产物行为正确。**为何这几条**:加段四面的每条不变式各一正控 + 缺失拒绝(三态纪律)+ 端到端不回归。 +- [ ] **Step 2: 实现(写侧)**:`:107/:108` 常量;`:341` `calc_ccr_size` 加两段;段表 6 行 → 8 行(`:529-534`,offset 链延长);段体按规范序写两段缓冲(**本任务先写空壳:count=0**,不引入内容构造);`ccr_validate_i32_fields`(`:216`)如需覆盖新段字段则同步。 +- [ ] **Step 3: 实现(读侧)**:`:885-890` 标量族扩 `seg_off7/8`、`seg_end7/8`、`have7/8`;`:899` 闸界 `6 → 8`;`:900/:901` 规范序/连续闸**形状不变**(D9 的直接收益);`:919` 必备集 += `have7/have8`;`:923-935` 状态重置面确认(本任务无新内存表);`:937+` 两段解析(空壳:读 count 并**校验 == 0**,非 0 = 拒绝——内容面落地前不接受外部内容,防半成品被消费)。 +- [ ] **Step 4: 测试面重定**:`test_ccr_v7.py`:`SEG_TAGS`(`:83`)→ `[1..8]`;`HEADER_TABLE`(`:88`)→ `16 + 8*12 = 112`;`V7File` 的 `seg_count == 6` 断言(`:129`)→ 8;`test_v7_loader_rejects_version_ne_7`(`:1088`)坏版本三元组 `(6,5,8)` → `(6,5,7)`(8 已是合法版本);其余用例(ENT/REG/EDG 走查、负分支)走 `segs[tag]` 寻址,**预期零改动**——逐例实跑确认并登记改动面。**文件头 docstring 的字节真相表(`:22-38`)同步**(6→8 段、version 7→8)。 +- [ ] **Step 5: spec 同步(D9/D10)**:`2026-09-09-lattice-ir-v7-format.md`:§1 表 `version 6→7` 行改为「7(magic 不变)」→ 追加 v8 行(新段集合 = 8 段);§2 tag 行改 `1=STR 2=SYM 3=NOD 4=ENT 5=REG 6=EDG 7=TYPE 8=IFACE`(`9+` 预留:驱逐标注段/证书段——**顺移**);布局图把 header 的 `version u32 = 7` 改 8、段体列补两段;§3 新增 TYPE/IFACE 两小节(本任务先落**空壳定义** + 「内容定义见 Task 2/3」指针);§4 校验规则补 `seg_cnt == 8` 与两段必备。 +- [ ] **Step 6: 判据**:新套件 ≥6 例全绿 + `test_ccr_v7.py` 全绿(**改动面逐例登记**:改了哪几例、为何);ELF canary IDENTICAL;**`.ccr` 新记录值**(冷缓存 `ptr_arith`/`generics_test` 尺寸 + sha256,作废旧值并留档);冷/热两态 `.ccr` 逐字节同(T1 起新增守门);全量回归 + `test_backend_bootstrap.py`(`error[`=0)+ 自举链 + 影子对拍复跑(**预期零变化**:本任务不触判定路径)。 +- [ ] **Step 7: 提交**:`feat: R2 P4 Task 1——.ccr 段机制:TYPE(7)/IFACE(8) 加段 + 版本 7→8 + loader 三闸与必备集扩面;两段空壳落盘 + 结构断言套件` + +## Task 2:TYPE 段内容——类型行表 + 类型项 DAG + 确定性装填 + corearch 读回 + +**Files:** New `src/compiler/ccr_types.cr`(**corec-only**:D13 装填 + 段体构造 + 自测 helper)· Modify `src/compiler/ccr_io.cr`(读侧两小节解析 → 内存重建;写侧搬运缓冲)· `src/compiler/type_terms.cr`(如需 `tt_reset`/`tt_hash5` 暴露;**corearch 链接**)· `build_selfhost_native.py`(`corearch_files` += `type_terms.cr`;`corec_files` += `ccr_types.cr`)· 双注册面两处 · `src/lattice/ent_kernel.cr` 或 `src/compiler/corearch.cr`(读回断言/dump 通道)· New `tests/selfhost/test_ccr_types.py`(内容面用例) + +**Interfaces:** +``` +// ── TYPE(7) 段字节(D12;小端、offset 相对段首)── +// [row_count u32] +// [row_count × 24B {kind i32, data i32, extra i32}] ← g_types 0..g_type_count-1 原样 +// [term_count u32] +// [term_count × 40B {tag i32, a i32, b i32, c i32, d i32}] ← 项 DAG(索引 = 行号;哈希不落盘) +// 加载侧不变量(逐条校验,违规 = 拒绝): +// · 子项引用(UNION/INTER 的 a,b;NOT 的 a;MU 的 b;CONS 的 a,b;ATOM 的 c 及 c 链元素)必须 < 自身行号 +// ——**ATOM 的 a(ak)与 b(ti 行号/固定性位/token 值)是标注,不得按「< 行号」校验** +// (b = 数千行号会被误拒;见 sh_ref_mut_marker/sh_name_token/sh_seq_term 三构造点的 b 语义) +// · tag ∈ 0..10(TT_*);a..d ≥ -1;越界/拓扑违规 ⇒ 拒绝 + +// ── corec 侧装填与构造(ccr_types.cr)── +fn ccr_type_populate() -> int // D13:规范化重建 + 行序装填 + 签名装填 + 形状重注册;-1 = 有不可译行(save 拒绝) +fn ccr_type_seg_build() -> int // 生成 g_ccr_type_seg/g_ccr_type_seg_len;-1 = 失败 +fn ccr_type_selftest_dump() -> int // 自测/对拍通道:逐行打印(测试解析用) + +// ── corearch 侧读回(load_ccr 内)── +// 段 → g_types 行(kind/data/extra)+ g_type_terms 行(tag/a..d + 重算 hash)+ g_tt_index 重建(tt_reindex) +// 失败 = load_ccr 返回 -1(不得返回 0 且留空表) +``` + +- [ ] **Step 1: 用例(红)≥8 例**:① 段结构(两小节计数/长度自洽、`end == fsize`);② 行表逐行 == corec 侧 `g_types`(对拍 dump);③ 项表重建后:同 tag/a..d 的项**命中同一节点**(去重重建正确——对 `[int;3]`/`[int;4]`/`int?`/元组等已知形态各一);④ **确定性**:同源两次编译(**冷缓存 / 热缓存两态**)TYPE 段逐字节同(D13 的直接守门);⑤ 拓扑违规 mutation(子项引用 ≥ 自身行号)→ loader 拒绝;⑥ 标注槽不被误拒:构造 `b = 大值行号` 的 ATOM 项,loader 接受(**防「假校验」实现**);⑦ 段缺失/截断 → 拒绝;⑧ `--dump-types`(corearch)与 corec 侧 dump 逐行一致(跨进程同构)。 +- [ ] **Step 2: 实现(corec 装填 D13)**:`ccr_type_populate()`:① 项层复位(`g_type_term_count = 0`、`g_tt_index_cap = 0`(惰性重建)、`g_tt_top_ok/g_tt_nil_ok = 0`、`g_ty_memo_ok = 0`、预算计数复位)② 桥接/展开缓存复位(`sh_map_reset()` / `sh_unf_map_reset()`)③ 形状**重注册**(`iface_shape_builtin_init()`——Task 3 落地;本任务先以现注册面为准并登记)④ 行序 0..`g_type_count-1` 调 `sh_term_of_ti`(返回 -1 ⇒ **save 拒绝 + 诊断**)⑤ 接口签名项装填(本任务先空转,Task 3 填充)。**前置核查(写进报告)**:`save_ccr` 两处调用点(`main.cr:612`/`:636`)之后无类型项消费者(`sh_finish` 只读 `g_shadow_*`)。 +- [ ] **Step 3: 实现(序列化 + loader)**:`ccr_type_seg_build()` 按 D12 布局写缓冲;`save_ccr` 加段搬运(D18);`load_ccr` 解析 → 重建(含 `tt_hash5` 重算 + `tt_reindex` 重放;`grow_type_terms`/`grow_tt_index` 前置计数)。 +- [ ] **Step 4: corearch 面**:manifest `corearch_files` += `type_terms.cr`(在 `ccr_io.cr` **之前**——`ccr_io` 读侧要调 `tt_hash5`/`tt_reindex`;清单顺序 = 常量可见性约束,`iface_registry.cr:18-23` 先例)+ 双注册面两处同步;新增 `--dump-types` 测试通道(`corearch.cr`,照 `--dump-objects` `:352` 先例)。 +- [ ] **Step 5: 判据**:新套件全绿;`selftest-types` **≥333 → ≥336**(新增项层/装填用例);ELF canary IDENTICAL;`.ccr` 记录值更新(TYPE 段入盘 ⇒ 尺寸变大,实测报告);**冷/热两态 `.ccr` 同**(D13 守门);`test_backend_bootstrap.py` rc=0 + `error[` = 0;全量回归 + 自举链;影子对拍复跑(本任务不触判定路径 ⇒ 预期零变化,站点覆盖同报);**尺寸实测报告**(`g_type_count`/`tt_count` 对三档语料 = `tests/suite/ptr_arith.cr` · 自源 `src/compiler/_import.cr` · 大盘,落报告,勿以估算代之)。 +- [ ] **Step 6: 提交**:`feat: R2 P4 Task 2——TYPE 段(类型行表 + 类型项 DAG 序列化 + 确定性装填 D13 + corearch 读回重建);corearch 链接 type_terms.cr` + +## Task 3:IFACE 段内容——扩列 + 形状命名化 + 签名项化 + impl 边/方法表 + 读回 + +**Files:** New/Modify `src/compiler/ccr_types.cr`(IFACE 段构造 + 装填)· Modify `src/compiler/iface_registry.cr`(扩列:3 新行 + `name_ni` 填充 + `iface_shape_builtin_init`)· `src/compiler/ccr_io.cr`(写/读)· `src/compiler/type_selftest.cr`(硬值重定 + 零 str_intern 守门重定)· `tests/selfhost/test_ccr_types.py` · `tests/selfhost/test_iface_satisfies.py`(编码面钉评估)· `src/ci/run.sh` + +**Interfaces:** +``` +// ── IFACE(8) 段字节(D14;五小节,各带 count u32)── +// ① [native_count u32] [× {ak i32, ti_row i32, name_ni i32, lit_code i32, ops i64}] = 24B/条(IFACE_ENTRY_COUNT 条) +// ② [shape_count u32] [× {name_ni i32, term i32}] = 8B/条 +// ③ [iface_count u32] [× {name_ni i32, method_count i32, generic_count i32, pad i32} +// + method_count × {name_ni i32, param_count i32, self_mode i32, +// ret_term i32, param_terms[8] i32, param_codes[8] i32}] +// 方法记录 = {name_ni, param_count, self_mode, ret_term} 4×4B + param_codes[8] 32B +// + param_terms[8] 32B = 80B(ret_term/param_terms = 类型项索引,-1 = 不可建) +// ④ [impl_count u32] [× {trait_ni i32, type_ni i32}] = 8B/条(g_impl_for) +// ⑤ [method_count u32] [× {type_ni i32, method_ni i32, mangled_ni i32}] = 12B/条(g_methods) +// 校验:name_ni/type_ni/method_ni 必须 < STR 段串数;term 索引必须 < TYPE 段项数(跨段引用域检查) + +// ── 扩列(裁决 3 + 7)── +IFACE_ENTRY_COUNT : int = 16; // 13 → 16:+AK_NULL(原生第九员,裁决 7 独立槽)+AK_SUM +AK_FN +// 新行列值:{ak, ti_row = -1(无规范行), name_ni = 注册名, lit_code = -1, ops = 0} +// ops = 0 的解释:null/sum/fn 无「操作许可」面(算术/逻辑/条件门对其全拒 = 现状行为,P2b 起 iface_permits(-1/未登记)=0 同值) +// ⇒ 本三行为**纯信息面**:只改「可查询性」,不改任何判定(iface_kind_of 的行分派见 Step 3 边界) + +// ── 形状命名化(裁决 1 + D17)── +fn iface_shape_builtin_init() -> int // 六形状按 D17 名注册(zero-arg from its own idempotent path);init_types 尾部调用 +fn sh_shape_*() -> int // 六个构造点不变(iface_registry.cr:402-433) +``` + +- [x] **Step 1: 用例(红)≥10 例**:① 六形状在生产路径**名字可查**(`iface_shape_lookup(ni) >= 0`,逐名);② 形状项与 `sh_shape_*()` 构造点同项(节点同一);③ 注册面**泄漏守门**(`reset_frontend_state` 后 count=0 → 再 `init_types` 恢复 6,`x2.shape_no_leak_after_reset` 口径延续);④ 扩列后 `iface_count() == 16` 且三新行 `iface_ops == 0`、`iface_entry(AK_NULL) >= 0`;⑤ **判定面零变化**:扩列前后 `iface_kind_of` 对全类型行枚举**逐行同值**(`TYP_NULL`/`TYP_OPTIONAL` 行仍回 -1 = 保守面不变);⑥ IFACE 段结构自洽(五小节计数/长度/`end == fsize`);⑦ 签名项化:接口方法签名**入段后逐项可读**(`ret_term`/`param_terms` 与 corec 侧 `sh_iface_sig_*` 同项);⑧ 跨段引用域违规 mutation → 拒绝(`term` 索引越界 / `name_ni` ≥ STR 串数);⑨ 读回对拍:corearch `--dump-ifaces` 与 corec 侧逐行一致;⑩ 签名不可建(`dyn` 位图行入签名)⇒ save 拒绝 + 诊断(**先 report-only**)。 +- [x] **Step 2: 扩列实现**:`iface_registry.cr` 条目表加 3 行(`iface_put(13..15, …)`)+ `IFACE_ENTRY_COUNT 16`;`name_ni` 列按裁决 1 填注册名——16 行逐名 = 原生 8(`int/dex/string/bool/unit/never/char/dyn`)+ 结构/命名 5(`product/sequence/ref/ptr/named`)+ 新行 3(`null/sum/fn`);`iface_registry_init` 的 `str_intern` 面 = 本次裁决允许的**唯一**初始化期驻留(判据按 TODO #26)。 +- [x] **Step 3: 行分派边界(硬性)**:`iface_kind_of`(Task 0 后位于 `iface_axis.cr`)**零改动**——`TYP_NULL`/`TYP_OPTIONAL`/`TYP_DYN` 等行的分派与 P2b 钉死的域**逐格保持**(扩列只加「可查询性」不加「行→类」新映射);`ty_code_to_ti` 零改动(`iface_registry.cr:259-269`,码 7 撞车格原样)。 +- [x] **Step 4: 形状命名化**:`iface_shape_builtin_init()` 在 `init_types()` 尾部(`checker.cr:262` 之后)调用;`iface_shape_register` 保持同名覆盖幂等;D13 的规范化重建路径中**重跑**该初始化(order:reset → 形状重注册 → 行装填 → 签名装填)。 +- [x] **Step 5: IFACE 段实现(写/读)**:写侧按 D14 五小节(③ 的签名项来自 `sh_iface_sig_param_term`/`sh_iface_sig_ret_term`,`param_codes` 为**信息面**保留裸码——`dyn_arr.cr:148-154` 的裸码槽语义不变;裸码不可建/塌缩照现状);读侧重建 `g_iface_entries`(并置 `g_iface_registry_ok = 1`——**段 = corearch 侧真源**,防 `iface_ensure()` 用常量表覆盖)、`g_iface_shape_*`、`g_ifaces`(`ESZ_IFACEINFO` 布局 + `OFF_IFM_PARAM_NODES/RET_NODE` 槽:corearch 侧**无 AST 节点** ⇒ 节点槽写 -1、项槽走新字段——**布局决策**:新增 `OFF_IFM_RET_TERM` / `OFF_IFM_PARAM_TERMS` 槽(`ESZ_IFMETHOD` 168 → 168 + 8×8 + 8 = 240)**或**复用节点槽(corearch 侧节点槽恒 -1,不可复用——签名项在 corec 侧由节点派生、在 corearch 侧由段给出,二者不同域)。**取新增槽**(`ESZ_IFMETHOD` 扩至 240、`ESZ_IFACEINFO` 2712 → 16×240 + 24 = 3864。)**影响面须实测声明**:内存布局扩槽**不入盘**(盘面 = D14 紧凑形);`g_ifaces` **不入** `.cir` 快照(快照面 = var 声明/DF 节点/SG/边/指令/串常量)⇒ **本布局变更不触发 `CIR_CACHE_VER`**——实施时以「快照字段清单逐项核对 + 冷/暖产物一致」复证并写入报告)。`g_impl_for`/`g_methods` 直填。 +- [x] **Step 6: 守门重定(裁决 1 的落地面)**:`x2.no_str_intern`(`type_selftest.cr:619`)/`isat.no_str_intern_on_missing`(`:420`)/`ifc.mangling_zero_str_intern`(`:866`)→ 重定为「**判定/查询路径**零新增驻留 + 初始化/注册面固定增量(逐名登记)」;`x2.axis_a_registered`(`:599`)/`x2.shape_no_leak_after_reset`(`:609`)→ 生产注册面重钉;`iface.count`(`:1362`)与 `:2362` 复合断言 → 13 → **16**(逐行理由入注释)。 +- [x] **Step 7: 签名编码面(C.4)**:`isat.axis_c_ret_code`(`type_selftest.cr:368`)与 `inst_encoding_limit_named_ret_rejected`(`test_iface_satisfies.py:318`)——**先证同构**(段内项与进程内项同节点/同判定)后才允许改走类型项;本任务只落证据,**不动用例**(改动归 P5 收尾面,登记)。 +- [x] **Step 8: 判据**:新套件 ≥10 例全绿;扩列/命名化的「旧接受→新拒绝/放宽」清单(**预期空**:纯信息面 + 名字注册)逐条报告;`selftest-types` ≥336 → **≥344**;ELF canary IDENTICAL;`.ccr` 记录值更新 + **STR 段增长实测**(新增驻留串数与字节数,逐名列举);冷/热两态同;全量回归 + `test_backend_bootstrap.py` + 自举链;影子对拍复跑(**预期零差异**,站点覆盖同报);扩列三行的判定面零变化由 ⑤ 的全类型行枚举承担。 +- [x] **Step 9: 提交**:`feat: R2 P4 Task 3——IFACE 段(原生条目扩列 13→16 + 形状命名化 + 接口签名项化 + impl 边/方法表序列化 + corearch 读回);STR 段增长按裁决 1 接受` + +## Task 4:DFNode.TK 升格——迁移期双槽 + `.cir` 快照版本 + canary 证明(裁决 8) + +**Files:** Modify `src/compiler/dyn_arr.cr`(`ESZ_DFNODE` + `OFF_DF_TK_TERM`)· `src/compiler/dataflow.cr`(`df_create_node` 增参 + 存储)· `src/compiler/ir_gen.cr`(`emit` 派生项 + 传参)· `src/compiler/ty_shadow.cr`(`sh_tk_term_of_code(opcode, tk)`:允许清单制派生,其余 -1)· `src/compiler/cir_cache.cr`(快照对称写读 + `CIR_CACHE_VER` 17→18)· `src/compiler/corearch.cr`/`src/lattice/ent_kernel.cr`(如需 dump 项槽)· `tests/selfhost/test_ccr_types.py` / `test_ccr_v7.py` / `test_cache_identity.py` + +**Interfaces:** +``` +ESZ_DFNODE : int = 64 → 72; OFF_DF_TK_TERM : int = 64 // 双槽:tk 原样 + 项引用(-1 = 无项) +fn df_create_node(opcode, dest, src1, src2, src3, type_kind, tk_term: int) -> int // 唯一调用点 = emit +fn sh_tk_term_of_code(opcode: int, tk: int) -> int +// 允许清单制(见 Step 2):仅当 op ∈ 允许清单 ∧ 0 ≤ tk < g_type_count ⇒ sh_term_of_ti(tk) +// (项层**复用既有缓存/建项语义**,不新增第二套建项逻辑);否则 -1 +CIR_CACHE_VER : int = 18; // cir_cache.cr:31(v18 注记:DF 记录 +8B 项槽,布局变更) +``` + +- [x] **Step 1: 用例(红)**【实施记录:6 例入 `test_ccr_types.py`(㉖..㉛)+ 8 例入 `selftest-types`(`t4.tk_*`)】:① `emit` 后逐节点检查:`tk` 与升格前**逐字节同**、`tk_term` 在「tk 是合法类型行」时 = `sh_term_of_ti(tk)`、其余 -1(三类正控 + 三类负控:`IR_BOUNDS_CHECK` 的 `ti=1` 旗标语义、`tk=0` 的合法行 vs `tk` 为越界值);② `.cir` 冷/暖两路径:命中恢复后 `tk_term` 与冷路径**同值**(快照对称写读)+ `test_v7_cache_hit_restore_path` 口径;③ `.ccr` NOD 段**36B/条不变**(记录尺寸断言 + 旧记录值复核);④ `--dump-objects` 输出**逐字节同**(tk 列不变、不新增列);⑤ ~~`CIR_CACHE_VER` 变 ⇒ 旧 `.cir` 条目整类失效~~**【实施修正:不 bump——见 Step 4】**。**为何这几条**:升格的三条硬约束(发射面零泄漏 / 快照对称 / 缓存失效正确)各一正控,另加双槽语义正负控。 +- [x] **Step 2: 允许清单推导(先于实现;产物 = op 表入报告)**:`tk` 现为**混用槽**——同一位置既放类型行也放旗标/忽略值(实测样本:`IR_BOUNDS_CHECK` 的 `ti=1` = 动态上限旗标(`ir_gen.cr:105/:114/:140`)、比较族 `IR_BINARY` 传 `0` = 忽略)。⇒ 允许清单 = 「后端按 `ti` 分派发射的 op」(`instr.cr:460` 分发器逐分支审计)×「该 op 的 `tk` 由调用点传**类型行**」;**旗标/忽略值 op 一律 -1**(不得给 `IR_BOUNDS_CHECK` 凭空安上 `dex` 项)。清单与排除项逐 op 列报告(不猜)。**【实施实测:`instr.cr` 全文件 `ti` 消费者仅 5 处 = `IR_CONST(ti==TI_STR)` / `IR_BINARY(ti==TI_DEX)` / `IR_DEREF`+`IR_STORE_PTR`(`e2_ptr_bounds_check` 的 access_width)/ `IR_BOUNDS_CHECK(ti!=0)`;216 个 `emit(` 调用点逐 op 静态审计 + 全语料 NOD 的 (op,tk) 直方图(自源 146540 行 / `ptr_arith` 1298 行 / `dex_test`)⇒ ①×② 交集 = 恰 **{IR_CONST, IR_BINARY}**;排除面逐 op 见报告 §2(`IR_ALLOC`/`IR_CALL`/`IR_I2F`/`IR_F2I`/`IR_LOAD` 传类型行但后端不按 ti 分派 ⇒ 暂不入清单,P5 再裁)】** +- [x] **Step 3: 实现(emit 期填槽)**:`dyn_arr.cr` 布局 + `df_create_node` 增参(**唯一调用点 = `emit`**,`ir_gen.cr:193-206`,签名同步);`emit` 内 `sh_tk_term_of_code(opcode, type_kind)` 派生后传参;`dataflow.cr` **仅存储**(不引用桥接符号——该文件虽不在 `corearch_files`(`build_selfhost_native.py:366-368` = common + hit_engine + kernel + backend_support),仍按「共享文件零前端依赖」纪律留余量,注记落文件)。**时点选择 = emit 期(非「save 前派生式」)**:快照在 IR 生成期逐函数写(`cir_cache` 路径),若填槽晚于快照 ⇒ 冷/暖恢复路径项槽分歧(D13 同类陷阱);emit 期填 ⇒ 由构造保证一致。代价 = 每 `emit` 一次桥接查询(首次建项后为缓存命中),**须报编译耗时抽样**(照 A.3-⑩ 口径)。 +- [x] **Step 4: 快照面**【**实施修正(实测反证,见 D15 注):不落盘第 9 槽、`CIR_CACHE_VER` 保持 17**】:原设计(写点加第 9 槽 + 读点对称 + 17→18)以「不入槽则冷/暖分歧」为前提,**实测方向相反**——`--dump-tk-terms` 实测 `ptr_arith`:冷路径 `terms=13 bad_term=0`、暖路径 `terms=10` **`bad_term=329`**(恢复来的项引用**全部越出**暖进程项表)。根因 = 项表是**进程内**内容寻址表、emit 期按需追加 ⇒ 快照里的索引只在写侧进程成立;「纯函数值与其入参并存于同一记录」另违第二真源纪律。修正 = 盘记录仍 8×8B=64B(`.cir` 布局零变化 ⇒ 无 bump),装载侧在 **emit 的等价时点**(逐函数逐节点序)用 `sh_tk_term_of_code(opcode, tk)` 重派生 ⇒ 项表追加序与冷路径一致,冷/暖 (op,tk,term,tag,a,b,c) 逐行相同(㉙ 用例锁住;实测两态 `terms=13 bad_term=0` 且 1298 行全等)。**旧条目无需失效**:格式未变 + 身份字段(v17)已覆盖跨二进制面。 +- [x] **Step 5: 判据(裁决 8 的硬判据)**【实测全绿:canary `95084e7b…d475` **IDENTICAL**(clean-cache 冷态)· `ptr_arith`/`generics_test` 八段**逐字节同**(升格前后,含 NOD tk 列)· `--dump-objects` 逐字节同 · 五 CI job rc=0 · 全枚举 54/54 · 自举链 `cmp` IDENTICAL(双 2754918B `55237ec1…`)+ N06=0 + 冒烟 42 · 影子对拍 diff 空 · `selftest-types` **355/355**(347 + 8)】:**ELF canary IDENTICAL**(clean-cache;变即停下上报);`.ccr` **逐字节同**(NOD 36B 不变;TYPE/IFACE 段不受影响)——冷缓存实测;`--dump-objects` 逐字节同;解释器路径(`test_interp_parity.py`/`test_optional.py`)全绿;`test_cache_identity.py` + 冷/暖 `test_v7_cache_hit_restore_path` 绿;全量回归 + 自举链;影子对拍复跑(本任务不触判定路径);`selftest-types` ≥344(+升格面用例)。 +- [x] **Step 6: 登记面(写进报告,勿静默)**:NOD 盘面项槽归 **P5 单槽化**(理由 = `tk` 混用码不可反推项,见 D15);`.csr`/TagNode 图消费面(§6.4 的验证消费)本批**无调用者**——项槽的价值由 ①/② 的**内容断言**兑现(不假装有外部消费者)。**【追加登记:`.cir` 快照不载项槽(Step 4 修正);P5 单槽化时项引用须换稳定形态(内容寻址/落盘项 ID)或同款「装载侧重派生」,**不得**直接落进程内索引(本任务实测的 bad_term=329 即该形态的红证)】**。 +- [x] **Step 7: 提交**:`feat: R2 P4 Task 4——DFNode.TK 迁移期双槽(tk 零变化 + 项引用槽,允许清单制)+ .cir 快照 CIR_CACHE_VER 17→18;发射面零泄漏`**【实施:`.cir` 快照保持 17(不落槽、装载侧重派生——实测反证见 Step 4);提交信息按实际改写】** + +## Task 5:可选运行期表示——解包侧判表示(裁决 5,与 TYPE 段同批) + +**Files(实施记录,2026-09-13):** `src/compiler/ir_gen.cr`(表示位侧表 + 写点置位 + 两解包点分派 + 信道)· `src/compiler/globals.cr`(侧表/信道全局 + `oe_bare/oe_boxed`)· `src/arch/x86_64/instr.cr`(**全局行分派补两处**:`IR_CONST` 直写 + `IR_LOAD_ENUM_TAG` tag 源——第三/第六根因,见下)· `src/compiler/main.cr`(文件路径 `optrep_begin` + 可选面关 .cir 缓存)· `tests/selfhost/test_optional.py`(12 → **34 例**)· `src/ci/run.sh`(计数/说明同步) + +**实施记录(Step 1–5 全部落地;三根因逐条实测见报告)** + +- **载体裁定 = (a) 表示位侧表**(推荐项)。实现形态 = **IR 层**(非后端帧旁路字节):每个可选槽(局部/形参/调用结果)配一个**普通 int IR 变量**当表示位(0 = 裸值 / 1 = 装箱),写点置位(静态已知 → 常量;源已配位 → 拷贝),两解包点(`match` 的 Some/None 臂、`?`)按位分派。**纯 IR ⇒ 无新 opcode、无 ABI/帧布局改动 ⇒ ELF 后端与解释器天然同源**(对照 `g_x86_mw_tag_off` 先例:后端旁路 tag 区需两后端各写一份,本任务不需要)。 +- **跨函数面 = 隐藏全局信道**(本计划允许的 (a) 扩展面,纯 IR):返回点写 / 调用点读(返回信道单元)+ 调用点写 / 被调序言读(每形参位一个单元,被调序言读点先于任何可改写信道的调用)。**读点必须紧跟真实调用 ⇒ 返回可选者的惰性 thunk 关闭**(thunk 把调用推迟到 force 点,读点悬空——实测踩过)。 +- **门(零足迹)**:AST 无 `EXPR_OPTIONAL` 且无 `Some`/`None` ⇒ 整面不启用(不注册隐藏全局、不发任何表示指令)⇒ **非可选程序发射面逐字节不变**(`ptr_arith`/`generics_test` 八段 + ELF canary IDENTICAL 实测)。 +- **未覆盖面(硬性登记,实测三态)**:结构体字段/数组元素/全局槽/match 结果/可选的惰性 thunk ⇒ 不配表示位 ⇒ 解包回落**既有**装箱假定:裸值形态**响亮失败**(rc=139 双路径**同**——非静默分歧)、装箱形态照常正确(`test_optional.py` 两例分别钉)。 +- **第三根因(实测挖出,非计划面;同族两处 + 一处后续实测补)**:ELF 后端逐 op 的**全局行分派**缺口——(i) `IR_CONST` 无全局行分支(`g2_slot` 对全局返回帧外伪偏移 ⇒ 写栈垃圾 = 静默丢写;解释器同 IR 直写 `g_ir_vals` ⇒ **两路径分歧**)——本任务首次出现「IR_CONST 落全局行」的发射点(形参信道单元),逐分支修之(与 IR_LOAD/IR_STORE 的全局分派同款);(ii) **`IR_LOAD_ENUM_TAG` 的 tag 读取源同缺**(实测:全局可选槽 `g : int? = Some(5)` + `Some` 臂 = ELF 0 / 解释器 5 = **静默双路径分歧**,**基线同病**)——同款修(源走 `e2_load_var`),目的槽的全局行仍零发射点(注释登记)。**第四根因**(`g2_slot` 对全局返回帧外伪偏移 ⇒ 写栈垃圾 = 静默丢写;解释器同 IR 直写 `g_ir_vals` ⇒ **两路径分歧**)——本任务首次出现「IR_CONST 落全局行」的发射点(信道单元),逐分支修之(与 IR_LOAD/IR_STORE 的全局分派同款)。**第四根因**:Python bootstrap 的 `gen_let_decl` 只对**裸字面量**记 `constant_value`(`-1` = UnaryOp ⇒ 丢初值 ⇒ `.quad 0`)⇒ **自举链二进制的负值文件级常量静默读 0**(同类既存面 = `cir_cache.cr` 的 `CIR_CACHE_MAGIC`)——本任务改走 `oe_bare()/oe_boxed()` 函数。**第五面**:表示位侧表是编译期进程内状态、`.cir` 快照不载 ⇒ 命中恢复路径与冷路径**产物分歧** ⇒ 可选面启用即**关 .cir 缓存**(正确性优先;非可选程序照常缓存)。 + +**缺口事实(附录 B.7,已实测,复述以钉住红态)**:`x : int? = 5; match x { Some(v) => { return v; } None => { return 0; } }` → `check` rc=0 零诊断 / `build` rc=0 / **产物运行 rc=139** / `corec run` rc=139;纯通配臂不崩(rc=7)。根因 = `Some(1)`/`None` 走 `IR_MAKE_ENUM` 对象 + tag,而 `T?` 槽里的裸值仍是裸值 ⇒ **同一 `T?` 变量可持两种表示**;`Some` 臂的载荷解包(`IR_LOAD_FIELD`,`ir_gen.cr:1885`,偏移 `fi+1` = tag 之后)把裸值当枚举对象解引用。 + +- [x] **Step 1: 判别载体裁定(实施前须定,写入报告)**【实施:取推荐 (a);承载形态改为 IR 层表示位(理由见上「实施记录」)】:候选 = **(a) 表示位侧表**(照 int 多字 tag 区先例 `g_x86_mw_tag_off`(`globals.cr:186`)+ `tag2l.cr`:`T?` 槽配 1 字节表示位,赋值/装箱侧置位、解包侧读位分派)|(b) 值自描述(= 一律装箱,与裁决 5 相悖,否)|(c) 静态面收窄(仅可静态证表示处直取,其余**保守拒绝**)。**推荐 (a)**:与既有 tagged-slot 机制同源、解包侧即可闭合、不改变已装箱值的表示。**未覆盖面(硬性登记)**:跨函数面(形参/返回/字段/聚合元素)的表示位传递若不可行 ⇒ 该面必须**保持响亮失败或保守拒绝**,**不得**退化为静默错值(本任务的红态对比必须逐形态实测:正确值 / 明确拒绝 / rc=139 三态,禁「静默 0」)。 +- [x] **Step 2: 用例(红)**【实施:复跑确认四形态红态(L1/L5/L6/L8 双路径 139)后扩至 32 例】:`test_optional.py` 扩 ≥6 例覆盖 B.7 的四形态(裸值 + `Some` 臂 / `Some` + 通配混合 / match 作表达式 / 纯通配)× 双路径(ELF 产物 + `corec run`);**当前 4 例红**(rc=139)为本步的失败证据(复跑确认,勿以 B.7 记录代跑)。 +- [x] **Step 3: 实现**(按 Step 1 裁定)【实施:两解包点(`match` 六处臂条件/载荷绑定 + `?`)+ 写点(LET/赋值)+ 两信道(返回/形参);构造侧 `IR_MAKE_ENUM` 逐字未动】:`Some` 臂解包路径改为「读表示位 → 裸值分支直取 / 对象分支按 `fi+1` 取载荷」;`None` 臂同源;`Some(x)`/`None` 构造侧保持 `IR_MAKE_ENUM`(不动);如涉 `?` 解包(`EXPR_TRY`)则同规则。 +- [x] **Step 4: 判据**【实测:`test_optional.py` 32/32 · ELF canary `95084e7b…d475` **IDENTICAL**(非可选程序零足迹 ⇒ 未触发允许面变化)· `.ccr` 冷/热同 + 可选程序关缓存 · 全枚举 54/54 · 自举链不变量 · `test_interp_parity` 绿;未覆盖面清单见下】;**ELF canary**:若实现触及 x86 发射(tag 区/新指令)⇒ canary **预期变**,属**允许面**但**必须报告**(与裁决 8 不相干——裁决 8 只约束 TK 升格);`.ccr` 变化实测报告;全量回归 + 自举链 + `test_interp_parity.py` 绿;report-only 面(未覆盖形态清单)先行。 +- [x] **Step 5: 提交**:`2be26cc5`(`fix: R2 P4 Task 5——运行期可选表示统一(裁决 5「解包侧判表示」):B.7 裸值 + Some 臂双路径 SIGSEGV(rc=139)闭合`)+ 补提交 `cf368259`(`IR_LOAD_ENUM_TAG` tag 读取源补全局行分派——实测发现的预存静默双路径分歧) + +## Task 6:`MAX_*` 护栏与解除评估(裁决 6) + +**Files(实施记录,2026-09-13):** `src/compiler/ast.cr`(`EC_P_ENUM_LIMIT`=P022 / `EC_P_STRUCT_LIMIT`=P023)· `src/compiler/parser.cr`(枚举变体/载荷 + **结构体字段**写入侧护栏)· `src/compiler/dyn_arr.cr`(受护访问器,照 `fi_param_type`/`fi_set_param_type` 先例 = TODO #8 收口形态)· New `tests/selfhost/test_enum_limit.py`(13 例)· `src/ci/run.sh` · `docs/error-codes.md`(P022/P023 行 + 统计表)· `TODO.md`(#35 划销)· `src/compiler/main.cr` **零改动**(见 Step 2 实测) + +**实施记录(Step 1–4 全部落地;详版 = `.superpowers/sdd/p4-task6-report.md`)** + +- **Step 1 红态(首次构造危害形态——TODO #35「未做」项收口)**:三档探针 × {`check`, `build`} 逐值实测(二进制 = 修复前 `6afbd333`,另在 /tmp 沙箱用**同源重建的修复前二进制**复证)。**危害不是崩溃而是「表污染 + 判定静默」**:① 17 变体声明 `check` **rc=0 零诊断**;② 该枚举上 **match 穷尽性判定被静默禁用**(第 17 变体名槽 = `variant_count` 同址,count 后写胜出 ⇒ 名→项查表失败 ⇒ 域不可展开 ⇒ -1「不判」)⇒ **真·非穷尽 match 也 rc=0**(对照 16 变体必报 TM03);③ 第 17 变体**不可构造**(假诊断「Undefined enum constructor」);④ **同族第三处 = `MAX_STRUCT_FIELDS`**(17 字段结构体 `check` rc=0 静默,RED 步发现,一并修);⑤ 越界写落在 `grow_*` 扩容余量内 ⇒ 无 SIGSEGV 可观测。`build` 面修复前 rc=1 仅因**读回侧**闸(`ccr_io.cr` 的 `vc > MAX_ENUM_VARIANTS`)——写入早已发生(TODO 原话「护栏在读回侧 ≠ 写入侧」实测成立)。 +- **Step 2 护栏**:parser 三处写入点**跳过超限槽**(计数保持真值,照 P020 的 `add_func(pc)` 先例)+ 末尾**单条定位诊断**(`error[P022]` 变体/载荷、`error[P023]` 字段,消息含真实数码与上限);`dyn_arr.cr` **唯一受护写点**(`ei_set_variant_*`/`si_set_field_*`)+ 读护栏(越界回哨兵 -1/0,不越读)。**实测修正**:拒绝由 **parse 阶段诊断闸**(`main.cr` `[3/5] parse...` 后 `g_diag_count > 0 ⇒ rc=1`,与 P019/P020/P021 同款)承担 ⇒ **hard 名单条目对本族冗余**(删条目零行为差异,实测)⇒ 不加(`main.cr` 零改动);拒绝时机 = **前端**(回归用例断言输出不含 `lower to ccr`/`save .ccr`/`generate ELF`)。report-only 全语料(自源 `check src/compiler` + `tests/suite` 25 档 + `examples`)**零命中**(另附静态扫描:语料最大结构体 9 字段 / 无 >12 变体枚举)。 +- **Step 3 解除评估(写在报告,不实施)**:逐项结论见报告 §解除评估——**三项上限(16)本批均不解除**:解除面 = 槽区(记录布局)迁侧表,而记录布局与 `.ccr` SYM/段布局、`OFF_*` 常量族、`.ccr` 读回闸、`sh_*` 展开层同源 ⇒ 一次改动同时触 4 面 + 需 `CCR_VERSION`/`CIR_CACHE_VER` 级联 bump;收益 = 语言面容量(当前语料最大使用 = 9 字段/0 枚举超限,即**零在野需求**);风险 = 定长槽删除后 `OFF_*` 常量与内存布局镜像(`ast.cr` 的 `StructInfo/EnumInfo` 镜像结构体)同步失配 = B.6 式静默未定义类。`MAX_GENERICS=4` 属**另一类**(写入侧有界,超限 = parse 期硬报,无静默接受面)⇒ 无需护栏,解除同属表布局面(P3 报告已登记)。`MAX_IFACE_METHODS/PARAMS` 已有 P13/P11 硬错护栏(本轮复测:17 方法/9 参数逐例 rc=1 + 定位诊断)。 +- **Step 4 判据(实测)**:新套件 **13/13**(16 边界三面正控 + 17 变体/载荷/字段 × check+build 拒绝 + 无产物 + 阶段断言);`check src/compiler` **rc=0**;`test_backend_bootstrap` rc=0 + `error[` = 0;`selftest-types` **355/355**;**ELF canary `95084e7b…d475` IDENTICAL**;**`.ccr` 与修复前编译器逐字节同**(`ptr_arith` `592afa31…` 90966B / `generics_test` `ec8413ec…` 134844B——同源双编译器对拍 `6afbd333` × `5d2b15ad`,比 TODO #26 的「结构断言」判据更强);五 CI job 全 rc=0 + 全枚举 **55/55** + 自举链(`corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1);**突变控制**(删 3 条诊断 ⇒ 8 例红 + 静默面复活;复位 ⇒ 二进制逐字节复原 `5d2b15ad`)。 + +- [x] **Step 1: 红态复现(TODO #35 记录复核)**:`enum E { V0..V16 }`(17 变体)→ 现状 `check` rc=0 零诊断(前端静默越界写:槽起点 `OFF_EI_VARIANT_COUNT` = 4360,越 `ESZ_ENUMINFO` 224B)/ `build` rc=1(读回闸 `ccr_io.cr:1121`)。≥17 载荷类型同族(`:1148`)。**逐值实测并记录**。 +- [x] **Step 2: 护栏**:写入侧硬错(照 P020 形态:`error[P0xx]` + rc=1,**非静默截断**);访问器加范围闸(唯一写点受护)。`report-only` 先行跑全语料,清单空后开门。 +- [x] **Step 3: 解除评估(写在报告,不实施)**:`MAX_GENERICS=4`(`ast.cr:115`)· `MAX_IFACE_METHODS=16`/`MAX_IFACE_METHOD_PARAMS=8`(`dyn_arr.cr:162/167`,已有硬错护栏)· `MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES=16`(`ast.cr:117-118`)——逐项给出「解除面 = 表迁侧表(= 段布局去定长槽)的成本 / 收益 / 风险」,结论入 TODO 与报告。 +- [x] **Step 4: 判据**:新用例(17 变体拒绝 + 16 变体通过 + 17 载荷拒绝)+ 全量回归 + `test_backend_bootstrap.py` + ELF canary IDENTICAL + `.ccr` 零变化(拒绝路径不产生产物)。 +- [x] **Step 5: 提交**:`fix: R2 P4 Task 6——枚举/结构体写入侧 MAX_* 护栏(TODO #35:变体/载荷/字段;P022/P023 定位硬错,#8 同族形态)+ MAX_* 解除评估登记` + +## Task 7:收官(回归 + 判据 + 统一台账 + 文档) + +- [x] **Step 1: 全量回归**(**T7 实跑,2026-09-13;cwd = 仓库根、`nice -n 19`、一构建一编译串行、比较前 `clean-cache`;无并发构建者**):五 CI job(selfhost-tests / bootstrap-tests / suite / check / full-bootstrap)**全 rc=0**;全枚举 **55/55 rc=0**(**48 selfhost + 7 bootstrap**,含 #31 未挂 CI 档 `test_backend_bootstrap` rc=0 + 11 条 `[PASS]` + 构建日志 `error[`=0);`check` job **rc=0**(TF01 收口后口径,原「既有 red 划界」句**作废**;`check src/compiler` = `ok` 零诊断);构建 rc=0 三面 GUARD clean(corec 45 / corearch 32 / target 3 / corelsp 31)+ 重建 sha 复原(`build/corec` = `5d2b15ad…` 与 T6 记录值逐字节同 = 构建确定 + 提交树 == 被测二进制度);逐档计数见 `.superpowers/sdd/p4-task7-report.md` §1。 +- [x] **Step 2: 判据复验**(**T7 实跑**;碰撞窗未背书的五项**全部由本实例重跑**):`clean-cache` → ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` **IDENTICAL**(28822B,Task 5 允许面**未被触发**);`corec2==corec3` `cmp` **IDENTICAL**(双 2792078B `4919023b3e92b661219f4624859512d53abb58e3dbfe874a51ae8a5b297f9034`)+ 构建日志 **N06=0**(`grep -c N06` = 0)+ 冒烟 42(corec **42** / corec2 **42**)+ `/tmp/corec3 --help` **rc=1**;`.ccr` 面 = **附录 D-1 两套口径 + 冷/热勘误表**(冷态可重复 · 可选程序冷≡热 `2bfbbe2b…` · 非可选冷≠热 = 预存面豁免)+ 段结构断言(version 8 / seg_count 8 / tag 序 1..8 / 连续 `end == fsize`);影子终态 = `decisions=32620 agree=32620` + 全计数 0(`old_stricter`/`old_looser`/`unknown*`/`replace_*`)+ 站点覆盖 `assign-node=24721 · fn-body-ret=5828 · if-branch=2037 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`(其余 0;Σ = 32620 = decisions)——**且声明:影子通道对载体面(段/读回/TK/表示)零覆盖**,载体正确性证据 = 结构断言 + corearch 读回对拍 + 定向探针。**dumps 通道注(勿当强证据)**:两侧 `dumps/` 目录**均无文件** = 无差异条目 ⇒ `sh_dump_write`(`ty_shadow.cr:651`)早退不落盘 ⇒ 本处只引摘要与站点覆盖(findings §14)。 +- [x] **Step 3: 统一台账**(T7 汇总,逐条出处 = 各任务报告):**收紧 10 类 · 放宽 2 条(均为「T1 空壳闸退役」性质,非语义放宽)· 崩溃修复 8 形态 + 1 静默错值类 · 全语料命中 0**——逐件 = T0 0/0 · T1 收紧 3(缺 TYPE 段 / 缺 IFACE 段 / 版本≠8 整类拒收)· T2 收紧 2 类(save 侧装填不可译行拒绝 + loader TYPE 结构不变量)、放宽 1(空壳闸退役)· T3 收紧 2 类(签名不可建 save 拒绝 + loader IFACE 五小节/跨段引用域)、放宽 1(空壳闸退役 + 名字面注册)· T4 0/0 · T5 0/0 + 修复 8 形态(B.7 双路径 rc=139 → 正确值)+ 1 静默错值类(`?` 解包装箱值:旧 ELF=8 / interp=200 各错各的 → 7/7)· T6 收紧 3(P022 变体/载荷 + P023 字段);「命名化/扩列的判定面零变化」证据 = 全类型行枚举用例(`ct2.kind_of_no_new_mapping` 等,含在 `selftest-types` 355/355 内)+ 影子全计数 0;**同源对拍(pre-P4 `9a583215…` × 当前源 vs 当前二进制 × 当前源)**:影子 dumps 空 + 摘要同值;check/影子**日志面差异 = 逐行分类后 100% 为 TF01 收口类**(5 处 `ty_memo_slot_no_grow` 误报 + 3 处 rc 1→0,P4 内有意修复,非语义回归)。 +- [x] **Step 4: 文档**:统一设计 spec **§9 P4 行标 ✅**(含未覆盖面与继承项指针);format spec 复核一致性(§5 测试族计数刷新为当前值 + 判据口径注 · §1/§2/§3.7/§3.8/§4 与实现一致,无改动);TODO **#44 = P4 收官条目**(8 提交链 + 统一台账 + 判据 + P5 继承项)+ #35 已在 T6 划销 + #26 判据继承注(本计划 Global Constraints 勘误回填)+ P5 继承项(**附录 D**);findings 文档追加 **§14「P4 后复跑」**;CLAUDE.md 的 `.ccr` 描述同步 **v8/8 段**(§Key Conventions 行 + 命令注释 + bootstrap 目录注三处)。 +- [x] **Step 5: 提交**(路径限定;P4 载体批共 **8 提交** + 附带 TF01 一件,详见状态表)。 + +--- + +## 翻转钉清单(P3 附录 C.4 逐条 → 本批处置) + +| 钉 | 位置 | 触发任务 | 处置 | +|---|---|---|---| +| `.ccr` 整体段数/表长/版本断言 | `tests/selfhost/test_ccr_v7.py:83/:88/:129` | T1 | 8 段 / `HEADER_TABLE=112` / `seg_count==8` | +| 版本拒收三元组 | `test_ccr_v7.py:1088-1118` | T1 | `(6,5,8)` → `(6,5,7)` | +| `.ccr` 逐字节同记录值 | P3b 报告 §4b(`ptr_arith` 88943B `54e3856b…`;`generics_test` 130974B `97be2f9e…`) | T1 起 | **作废** → 新冷缓存记录值 + 结构性断言(TODO #26) | +| 零 `str_intern` 三守门 | `type_selftest.cr:420/:619/:866` | T3 | 重定:判定/查询路径零新增 + 初始化/注册面**固定增量逐名登记** | +| 形状注册面两例 | `type_selftest.cr:599/:609` | T3 | 生产名注册后重钉(泄漏守门口径不变) | +| 签名编码面 | `type_selftest.cr:368` + `test_iface_satisfies.py:318` | T3(**仅落证据**) | 段内项与进程内项同构证据入报告;用例改动归 P5 | +| 本质条目数硬值 | `type_selftest.cr:1362/:2362` | T3 | 13 → 16(三新行逐条理由) | +| 影子站点面 | `--type-shadow` 站点集/计数 | 本批不动 | P5 清理(B.5);每任务复跑同报 | +| 清单双注册面 | `build_selfhost_native.py` ↔ `_import.cr`×2 | T0/T2/T3 | 三面同核 + `test_backend_bootstrap.py` 必跑(`error[`=0) | +| `.cir` 快照布局 | `cir_cache.cr:31` | T4 | **保持 17**(实测推翻计划原判「17→18」:快照**不落项槽**、装载侧 emit 等价时点重派生 ⇒ 格式未变 = 不 bump;`test_cache_identity.py:VER_EXPECTED` 亦未 flip)+ 冷/暖 (op,tk,term) 逐行同 | + +## 自检记录 + +- **根因 vs 止血**:① **段内容随判定历史漂移**是载体落地的真根因(惰性项 DAG 直接落盘 ⇒ 冷/热门槛差异 = 已登记的静默类)——D13 用「确定性装填 / 段内容 = 表纯函数」根治,非采样规避;② **corearch 链接面**(D16/D18)是按 B.6 的实际事故形态(清单泄漏 ⇒ N06 静默未定义)**前置**处理,非事后补门;③ Task 5 按裁决 5 走解包侧,**未**采用「先补通配臂特例」式止血(那会保留「同一变量两种表示」的根因);④ Task 6 是 TODO #35 的写入侧护栏(读回闸前置 ≠ 写入侧护栏),非「把闸往前挪一行」。 +- **占位符扫描**:无 TBD/TODO-to-fill;三处**实施前须定的登记项**明写并给了推荐与边界——Task 5 的判别载体(推荐 (a) + 未覆盖面硬约束)、Task 3 的 `ESZ_IFMETHOD` 扩槽(新增项槽,理由 = corearch 无 AST)、Task 2 的尺寸实测(**数字留待实测,不预填**)。**未知数不猜**:TYPE/IFACE 段体的实际尺寸、STR 增长字节数、`g_type_count`/`tt_count` 量级、Task 5 的 canary 是否变——全部列为实测报告项。 +- **数字口径**:333(`selftest-types`,`src/ci/run.sh:71` 注释与 P3b 收官实测)· 13/`ESZ_IFACEINFO=2712`/`ESZ_IFMETHOD=168`/`ESZ_DFNODE=64`/`OFF_DF_TK=40`/`CIR_CACHE_VER=17`/`CCR_SEG_COUNT=6`/`CCR_VERSION=7`/88943B/130974B/`95084e7b…d475` = 实读或既有记录值;「≥N 例」= 计划下限(可增不可减)。16(扩列后条目数)与 240/3864(内存槽扩)为**本计划决策值**,已注明。 +- **风险面**:① **`.ccr` 尺寸增长**(行表 24B + 项 DAG 40B,量级未实测)——缓解:Task 2 强制尺寸实测报告;若超预算(如自源 `.ccr` 翻倍)⇒ 停下上报(可退:项 DAG 只落「被引用闭包」= 需先裁剪再落盘,属本计划的 D12 替代面);② **D13 的复位副作用**(save 前清项层)——前置核查 `save_ccr` 后无消费者已列步骤,若核查发现新消费者 ⇒ 停下重定(改为「序列化后重放」);③ **corearch 二进制变大**(+type_terms/type_engine/iface_registry 纯段)——非判据面(B.4-18 先例:corearch 二进制 sha 不是计划判据),但需在报告登记;④ **STR 段增长**按裁决 1 接受后,凡「`.ccr` 逐字节同」类型的既有判据全部失效——本计划以结构性断言全覆盖,**遗留:其他批次的旧记录值需一处汇总划销**(Task 7 Step 4 落 TODO);⑤ **Task 5 未覆盖形态**(跨函数表示位)若只能「保守拒绝」⇒ 收紧清单 + 报告;**绝不允许**以静默错值收口;⑥ **影子通道零覆盖载体面**(本计划 Global Constraints 明写)——防止后续批次把「影子归零」当载体证据复用。 +- **与既有裁决的一致性复核**:裁决 2(结构口径)⇒ IFACE 段 ④ 入段但不进判定(与 `type_engine.cr:641-651` 现状一致);裁决 4 ⇒ Task 3 的形状项仅作签名载体,**不**接 `iface_satisfies_term` 整形状路由(防线保留在 `type_engine.cr:645-650` 的注释与用例);裁决 7 ⇒ `AK_NULL` 独立槽(`type_engine.cr:34-35` 语义零改动);裁决 8 ⇒ Task 4 的硬判据与「停下上报」条件逐字落纸。 + +--- + +## 附录 D:P5 交接包(T7 收官产出,2026-09-13) + +**放置声明(P5 开工者从这里读)**:P5 尚无独立计划(实查 `docs/superpowers/plans/` 无 P5 文件);立计划时应以本附录为起点。指针链 = 统一设计 spec §9 的 P4 行 → 本计划 Task 7 → 本附录;TODO #44 同留指路行。**本附录只做交接登记,不重复推导**——每条均给出证据出处(报告/代码位置),P5 实施时逐条重验。 + +### D-1 `.ccr` 记录值与两套命令口径(收官实测,2026-09-13;先读) + +P4 全批的 `.ccr` 记录值存在**两套口径**,引用时必须带口径(T7 实测:两套各自确定性可重复,差 = 恰 143B): + +| 口径 | `ptr_arith` | `generics_test` | 出处 | +|---|---|---|---| +| `corec ccr F -o O` | **90823B** `fb4a3b59103f4e0c…` | **134701B** `cafb42781f8a4f9b…` | T3/T4 记录值;T7 复测逐一复现 | +| `corec build F -o O --static`(读 `O.ccr`) | **90966B** `592afa31d66cc75a…` | **134844B** `ec8413ec55007f51…` | T5/T6 记录值;T7 复测逐一复现 | + +**根因(T7 查明,消除「±143B 漂移」疑云)**:`--static` 在 `main.cr:423-425` 前置 `rt.cr`(「so `*` functions inline」)⇒ 4 全局 + 1 串进 `.ccr` = +143B。两套值互不矛盾(T5 报告曾把该差归因「期间 `arena_globals` 导入引入」,**归因有误、数值成立**——T7 以两口径同树对拍复现,见 `p4-task7-report.md` §2)。 + +**冷/热两态(判据勘误的落纸面)**: + +| 程序 | 冷 | 热 | 判定 | +|---|---|---|---| +| `ptr_arith`(非可选;`build --static`) | 90966B `592afa31…` | 90769B `16996f1c…` | **冷≠热,两态各自可重复(确定性)——预存面**:pre-P4 二进制同病(89086B `b0b10406…` vs 88954B `1ae48560…`)⇒ 非 P4 引入,属 TODO #5 末条("`.ccr` 输出与 cir 缓存态相关",`at_test_struct.cr` 先例)⇒ **非可选程序豁免该判据** | +| `ptr_arith`(同上;`ccr` 口径) | 90823B `fb4a3b59…` | 90626B `05531c0b…` | 同款(冷≠热;--static 侧实测差因 = 热态少 125 条 IR 指令);**两口径差恒 143B** ⇒ 口径一致性交叉验证 | +| 可选程序(关 `.cir` 缓存) | 97604B `2bfbbe2b…` | 97604B `2bfbbe2b…` | **冷≡热按构造成立**(`g_optrep_on ⇒ cache_enabled=0`)——判据只对此类成立 | +| 自源 `src/compiler/_import.cr`(冷,`ccr` 口径) | **10661888B `76c76bb1…`** | —(自源热态 = 预存暖态 SIGSEGV 面,D-3-9-(ii)) | T7 新记录值;源随 P4 变更 ⇒ **不与 T2/T3/T4 值直比**(同 T4 §7-⑧ 口径) | + +⇒ 判据替换为(本批确立):**① 冷态可重复 + 热态可重复;② 与同源基线编译器逐字节同(同口径);③ 可选程序冷/热 IDENTICAL;④ 「TYPE 段」面 = 项表/probe 冷热同(行表随缓存态 15→12,T2 §8-③ 已登记——D13 项面承诺成立、行面不成立)**。 + +### D-2 P5 任务面(计划行:单槽化 + 大步删旧路径 + 文档/台账) + +1. **DFNode.TK 单槽化**(迁移期双槽 → 收尾单槽):`OFF_DF_TK`(混用码)与 `OFF_DF_TK_TERM`(项引用)合一——**前置 = D-3-2 的稳定引用形态先定**;盘面(NOD 36B 的 `tk`)与 `.cir` 快照的项槽归属同批裁(本批明确**不假装**有外部消费者:`.csr`/TagNode 消费面零调用者)。 +2. **大步删旧路径**(路线 3):`type_equal_legacy` 删除 + unknown 清零 + 桥接缓存退出生产路径 + 影子挂点清理(D-3-3~6)。 +3. **文档/台账**:spec/format spec 的 P5 行、findings §14、TODO 划销链。 + +### D-3 已登记继承项(逐条:内容 / 现状证据 / P5 动作 / 前置裁决) + +| # | 项 | 现状与证据 | P5 动作 / 前置 | +|---|---|---|---| +| 1 | **NOD 盘面项槽** | NOD 盘记录 `tk` 是**混用码**(基类型行 + 旗标/宽度码,如 `IR_BOUNDS_CHECK` 的 `ti=1`),**不可反推项** ⇒ 盘面项槽 P4 未落 | 单槽化时另定 NOD 小节/扩记录;与 D-3-2 同批 | +| 2 | **项引用的稳定形态**(T4 concern,最高优先) | `bad_term=329` 红证:项表是**进程内**内容寻址表、emit 期按需追加 ⇒ 落进程内索引必悬空(快照入槽版实测) | 三选一**先裁**:内容寻址 / 落盘项 ID / 装载侧同款重派生(现用第三条);**不得**直接落进程内索引 | +| 3 | **`type_equal_legacy` 删除** | P2a 起降级为「对拍对照物 + 回落实现」双用(`checker.cr` 包装 + 影子层) | 删除前提 = D-3-4 unknown 清零路径裁定 + 影子层下线 | +| 4 | **unknown 清零** | 前提 = 引擎命名展开(`AK_NAMED`/`TYP_GENERIC_APPLY` 不展开 ⇒ -1 ⇒ 回落 legacy);P3a 展开层按裁决**只服务满足判定/域查询** ⇒ 该前提**不会被自动满足**(findings §13 尾) | P5 须裁:**(a)** 接受命名展开(推翻 P2a 对拍基线,等价判定换真值)|**(b)** legacy 长期化(承认 -1 面)——二选一,不得默认 | +| 5 | **桥接缓存退出生产路径** | P2b 继承项:`sh_*_ak_legacy` 等(TODO #24/#30) | 与 D-3-4 同批;退出后影子对照物也随之下线 | +| 6 | **站点 6 去留** | 影子站点集 = `ty_shadow.cr:252-269`(站点 1/2/4/6 语料零命中、3=2 次、5/9/10 有命中) | 随 D-3-3~5 的站点面清理一并裁(零命中 ≠ 可删:须先补站点评级说明) | +| 7 | **对象布局表示位**(T5 concern) | 未覆盖面 = 结构体字段/数组元素/全局槽/match 结果(裸值形态**响亮失败** rc=139 双路径同;装箱形态正确)——修复面 = packed 位图(结构体尺寸变更)/ element stride(数组) | 与 D-3-8(`MAX_*` 定长槽)**同批**(同触 `dyn_arr.cr` 布局 + `ast.cr` 镜像);判据 = 未覆盖面**不得**退化为静默错值 | +| 8 | **`MAX_*` 解除**(T6 裁定) | 三上限(`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES`/`MAX_STRUCT_FIELDS`,皆 16)**只**约束进程内定长记录;盘面变长不受限。**解除 = 同一个决策**(记录布局 → 侧表),与 D-3-7 同源 | 若要解除:前置 ①读回闸同步 ②`ast.cr` 镜像归位(`StructInfo.field_*` 镜像 64 vs 布局 16 **已不一致**)③覆盖位表示(`sh_match_bit = 2^vi` 走 int,>63 变体溢出 ⇒ 需位图/侧表)④冷/热 `.ccr` 与 ELF canary 判据;`MAX_GENERICS=4` 属**另一类**(写入侧有界、parse 期硬报 ⇒ 无静默面);`MAX_IFACE_METHODS/PARAMS` 已有 P13/P11 硬错护栏。护栏本身 = 解除时的安全网(漏改只会得定位硬错) | +| 9 | **`.cir` 缓存两席面** | (i) 可选程序**关缓存**(表示位侧表是进程内状态、快照不载;收回须给快照加表示位面或装载侧重派生);(ii) **预存暖态 SIGSEGV**:`corec build src/compiler/main.cr -O 0` 暖态 rc=139(`load_cir_cache` → `str_len` ← `load64`,gdb 栈;**基线二进制同病**) | (ii) 归 `.cir` 缓存恢复面(TODO #5 家族)**另单必修**(可选面的规避只覆盖可选程序);(i) 与 D-3-7 同批 | +| 10 | **载体消费面** | ① corearch 侧 IFACE 段**无判定消费者**(`--dump-types`/`--dump-ifaces` = 唯一读回通道)——§6.3 `atom_of` 承接面未接;② `.csr`/TagNode 图消费面零调用者(§6.4);③ 命名消费语法(`T: 可索引`)未开工;④ 形状可写视图不可表达(`ref(mut=1, ⊤ₖ(SEQ))` ⇒ -1);⑤ `g_impl_for` 入段但**不入判定**(裁决 2) | P5/P6 按 spec §6.3/§6.4 接消费者;接前**不得**把「段内数据存在」当「语义已兑现」 | +| 11 | **签名编码面收口** | `isat.axis_c_ret_code`(`type_selftest.cr:368`)与 `test_iface_satisfies.py:318` 的 `inst_encoding_limit_named_ret_rejected`:P4 只落「段内项与进程内项同节点/同判定」证据,**未改用例** | 改走类型项的收口归 P5(改动时同批更新 spec §2.3 的编码面陈述) | +| 12 | **陈旧工具/副本**(非本批引入) | `tools/module_to_ccr.py` 仍写 version=1 的远古 `.ccr`(零 in-repo 调用者,v5 起即失效);`pkg/` 内陈旧 `ccr_io.cr` 副本(非构建输入);`specs/2026-09-11-explain-predict-incremental-design.md:371` 称 `CIR_CACHE_VER=14`(陈旧) | 随文档清理批处理(登记不修) | +| 13 | **纪律继承**(不得稀释) | 三态纪律(-1 不当 0/1;缺失/不可译 ⇒ 拒绝)· 新硬错先 report-only 全语料 · 清单三面同核 + `test_backend_bootstrap`(`error[`=0)· 判据口径按 TODO #26(结构性断言,非「与旧版逐字节同」)· **影子通道对载体面零覆盖**(不得当载体正确性证据)· 比较前 `clean-cache`(D-1 口径) | 写进 P5 计划的 Global Constraints 逐字继承 | +| 14 | **未随 P4 收口的 TODO 指针**(P4 收官登记) | #32(`EXPR_LET` 站点无任何兼容检查,`checker.cr`)**未随 P4 收口**;#35(枚举写侧无护栏)**已由 T6 划销**(回归 = `test_enum_limit.py` 13 例);#43 = T5 落地条目(P5 若收回可选程序缓存先读其「登记面」);TODO #5 末条 = 冷/热 `.ccr` 分歧(P4 已量化 + 判据重定,见 D-1) | P5 计划把 #32 排入(或另立专批);#43/#5 只作背景读,不重复登记 | + +### D-4 P5 开工前置检查单(逐项可核) + +- [ ] 读 D-1 两套口径 + 冷/热豁免表;P5 新增 `.ccr` 记录值必须带口径与缓存态 +- [ ] D-3-2(项引用稳定形态)**先裁**再动单槽化(否则复现 `bad_term=329` 类) +- [ ] D-3-4(unknown 清零 (a)/(b))**先裁**再动 `type_equal_legacy` 删除 +- [ ] D-3-7 与 D-3-8 合并评估(同触布局面) +- [ ] D-3-9-(ii) 暖态 SIGSEGV 是否随 P5 先修(`.cir` 缓存恢复面,独立可做;复现 = `corec build src/compiler/main.cr -O 0` 连跑两次,冷 rc=0 / 暖 rc=139——**复现时注意 #5 家族的 clean-cache 口径**) +- [ ] D-3-14(#32)排入 P5 任务面 +- [ ] 全部判据沿用:五 CI job + 全枚举 + ELF canary `95084e7b…d475` + `corec2==corec3` cmp + N06=0 + 冒烟 42 + 同源对拍(check/shadow) diff --git a/docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md b/docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md new file mode 100644 index 00000000..6aac4fdd --- /dev/null +++ b/docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md @@ -0,0 +1,495 @@ +# R2 P5(收尾)实施计划:DFNode.TK 单槽化 + 大步删旧路径(路线 3)+ 文档/台账 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 兑现统一设计 spec §9 的 P5 行(**收尾**)+ §3.4「收尾:单槽 = 类型项引用 + `atom_of(term)` 取原子(后端改动面 ≈ 20 处比较点,收尾批一并改)」:① **单槽化**——把 DFNode 的**类型面**从「迁移期双槽(`OFF_DF_TK` 混用码 + `OFF_DF_TK_TERM` 项引用)」收拢为**项引用唯一真源 + 派生码**,并把混用码里的**非类型辅码**(旗标/宽度)显式分离(`OFF_DF_AUX`);② **大步删旧路径(路线 3)**——删 `type_equal_legacy` 与全部回落面、删桥接层 legacy 副本、删站点 6 与不可达死分支、**影子判定对拍层下线**,并把「判定面回归网」切换为**冻结基线同源对拍 + 行为探针 + 突变控制**;③ **文档/台账**——spec 行标 / findings 终态 / TODO 划销链 / **P6 交接包(附录 E)**。**边界纪律(本批为收尾批)**:任何「新能力」(盘面项索引、corearch `atom_of` 读回承接面、对象布局表示位、`MAX_*` 解除、可选程序收回 `.cir` 缓存、命名消费语法、iterable 接线)**一律转 P6**,本批只做**删除 / 收口 / 零行为变化的单源化**。 + +**Architecture:** 六条主线:① **前置侦查与基线冻结**(Task 0:全 op `tk` 语义分类表 + 站点评级表 + -1 面清点 + 冻结基线二进制;**源码零改动**);② **预存缺陷**(Task 1:`.cir` 暖态 SIGSEGV 根因修复——它是单槽化 `.cir` 面的地基,且属「响亮失败」级预存面);③ **单槽化**(Task 2:槽语义对调 + 辅码分离 + `atom_of` 契约 + **派生码不变量**「新派生值 ≡ 旧 `tk`」);④ **unknown 清零**(Task 3:命名面判定化——**身份链 + 可判定面加强**,不是结构性展开);⑤ **删除批**(Task 4 删 legacy + 回落 + 副本 + 死分支;Task 5 影子层下线 + 回归网切换);⑥ **独立收口与收官**(Task 6:TODO #32 `EXPR_LET` 判定点;Task 7:收官)。 + +**Tech Stack:** Core 自举栈。`src/compiler/checker.cr`(`type_equal_legacy` :504 / `type_equal_engine` / `type_equal` 包装 / `type_compat_strict` :442 / `EXPR_LET` 分支 :3079)· `src/compiler/ty_shadow.cr`(桥接 `sh_term_of_ti` :275 / `sh_tk_term_of_code` :377 / 影子挂点与站点表 :472-538 / 展开层 :678 / `sh_*_ak_legacy` :101/:109)· `src/compiler/type_engine.cr`(判定核 `lit_implies` :165 / 变型表 :264 / `tt_list_variance_at` :291)· `src/compiler/dyn_arr.cr`(DFNode 布局 :103-121)· `src/compiler/dataflow.cr`(`df_create_node` / `df_rebuild_ir` :455)· `src/compiler/ir_gen.cr`(`emit` / :129 / :338)· `src/compiler/cir_cache.cr`(`:48` `CIR_CACHE_VER=17` / 装载重派生 :448-453)· `src/arch/x86_64/instr.cr` :460 · `regalloc.cr` :457 · `lower_to_core.cr` :250/:574 · `dump.cr` :125 · `ccr_io.cr`(NOD 盘记录 :143 / :511 / :768)。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` **§3.4**(迁移期双槽 → 收尾单槽)、**§4 步 4**(收尾 = 删旧分支/旧映射/双槽,单源化)、**§6.2**(DFNode.TK 升格落地记录 + 「收尾(单槽化)归 P5」)、**§9 P5 行**;风险表 **R5**(双槽期漂移:**term 为准,atom 由 `atom_of(term)` 派生**——本批的单源化口径即此句)。**P5 起点文档 = P4 计划附录 D**(`docs/superpowers/plans/2026-09-12-r2-p4-carrier.md:345-405`;D-1 口径表 / D-2 任务面 / D-3 十四项 / D-4 检查单)。 + +**前置状态(已实测,非本计划目标)**:P4 全阶段收官于 `9bcb7083`(工作副本 `8d1885df`,`jj status` 干净、无空描述改动);`selftest-types` **355/355**(`src/ci/run.sh:71`);ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475` 全批 IDENTICAL;自举链 `corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42;全枚举 **55/55**(48 selfhost + 7 bootstrap);`CIR_CACHE_VER=17`(`cir_cache.cr:48`,P4 T4 实测后**保持不 bump**);`ESZ_DFNODE=72`(9 槽,`dyn_arr.cr:119-121`);DFNode 项槽(`OFF_DF_TK_TERM`)**无生产消费者**(P4 T4 §7-②)。 + +--- + +## 实施状态总表(R2 P5,收官时点 2026-09-14;提交链 12 提交) + +| 任务 | 内容 | 提交 | 状态 | +|---|---|---|---| +| T0 | 前置侦查 + 冻结基线(源码零改动) | `2b9b5463` | ✅ | +| T1 | `.cir` 暖态 SIGSEGV 根因修复(单一复用读缓冲;载入失败 ⇒ miss) | `11913f8a` | ✅ | +| T2 | DFNode.TK 单槽化(α):类型面单源 + 辅码分离 + `atom_of` 契约 | `7f6c38cb` | ✅ | +| T3 | unknown 清零——命名面判定化(身份链 + `te_named_pair`;不接结构性展开) | `461d96f3` + 收口注 `5a333e0c` | ✅ | +| T3b | 不变槽元素三态 `te_elem_cmp`(T3 停条件② 残留面收口) | `faca94e0` | ✅ | +| T4 | 删 `type_equal_legacy` + 全部回落面 + 桥接 legacy 副本 + 死分支/站点 6(引擎唯一权威 + 残留 -1 政策 P-A/ICE04) | `e6db38b8` | ✅ | +| T5 | 影子层整体下线 + 回归网切换(冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律) | `b6bf33cb` + 收口注 `d65ff9ad` + `66316751` | ✅ | +| T6 | `EXPR_LET`/全局声明位点兼容判定(TODO #32 收口;TA02 硬错) | `04c20487` | ✅ | +| T7 | 收官:全量回归 + 判据复验 + 统一台账 + 文档 + P6 交接包(附录 E) | 本提交 | ✅ | + +**未随批收口(显式转 P6,逐条 = 附录 E)**:β 盘面项索引 + corearch `atom_of` 读回 · 对象布局表示位 · `MAX_*` 解除(合批)· 可选程序收回 `.cir` 缓存 · `.csr`/TagNode 消费面 · 命名消费语法/iterable/形状可写视图 · T3b 揭开的 ¬ 面过判 · T6 的 `-> never` 调用面 · #20/F3 调用位点 · 冻结基线复现配方 · ICE04/P-A 签核 · 非硬名单诊断不阻 `build` · `string == int` 混比 · 单条目 >1GiB 分片 · 设计先例引用面。 + +--- + +## 收官总述:类型接口化工作流的终态(一页;维护者先读) + +**单一权威(现在谁说最后一句)** + +| 面 | 权威 | 位置 | +|---|---|---| +| 类型判定 | **语义子类型引擎**(`ty_equiv`/`ty_sub` 三态)——历史 `type_equal_legacy` 结构判等**已删**(T4),回落计数**已删**,「引擎 -1」不再静默:⇒ **ICE04 硬错**(P-A,入 `main.cr` 硬名单 ⇒ `build` 拒绝落盘) | `checker.cr::type_equal` → `type_engine.cr` | +| DFNode 类型面 | **类型项引用唯一真源 + 派生码**:`OFF_DF_TK`(40) = 类型项引用(-1 = 无)、`OFF_DF_AUX`(64) = 辅码;`iri_tk`/盘面一律 `sh_dfn_code_of_slots` **派生**;分类表 = 唯一分类真源 | `dyn_arr.cr` · `ty_shadow.cr` | +| 声明位点兼容 | `check_let_annot_compat`(局部 `EXPR_LET` + 全局 `check_global_let` 共用)⇒ **TA02 硬错** | `checker.cr` | +| 盘面 | `.ccr` v8 八段;NOD `tk` = **派生码**(进程内项引用**禁止**跨进程 —— D20);`CIR_CACHE_VER=17` | `ccr_io.cr` · `cir_cache.cr` | +| 回归网(T5 起) | ① 冻结基线同源对拍(pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源,72 档 `check` rc + 日志逐档 diff)② 行为探针(套件 + 30 档探针语料)③ 突变控制 ④ 三态纪律(未知/不可译 ⇒ 拒绝,绝不静默当 0/1) | `/tmp/p3t0_run.sh` + `tests/selfhost/*.py` + `main.cr` 硬名单 | + +**退役的仪器(逐件 + 死亡证据 = 统一台账)**:`type_equal_legacy`(含 7 自调)+ 两处回落 + `g_replace_*` 计数(T4;证据 = 全语料清零判据 0)· 影子对拍通道 = 12 函数 + `sh_finish` + 17 个 `g_shadow_*` + 2 个 CLI 旗标 + checker 9 处挂点 + hits 存-复原 hack(T5;证据 = `sh_compare` 零调用点 ⇒ 全语料 `decisions=0`)· 站点 6(`EXPR_BINARY+OP_ASSIGN`)与 `ir_gen` 同分支(T4;证据 = parser 三面不可达 + 语料 0 命中)· 桥接 legacy 副本 `sh_*_ak_legacy`(T4;证据 = 零生产调用)· 桥接缓存**保留**(生产面,改名 `g_term_map`)。 + +**判据(收官终值,全部本实例实跑)**:五 CI job rc=0 · 全枚举 **58/58**(51 selfhost + 7 bootstrap)· `selftest-types` **404/404** · `check src/compiler` rc=0 · ELF canary `95084e7b…d475`(28822B)IDENTICAL · `corec2==corec3` `cmp` IDENTICAL(`5559beef…`)+ N06=0 + 冒烟 42 + `corec3 --help` rc=1 · 冻结基线同源对拍 **72 档零差异** · 探针全集 **30 档零差异** · 突变控制逐条转红 + 精确回滚 · `.ccr` 两套口径(`ptr_arith` 90823B/90966B · `generics_test` 135341B/135484B,冷态)· `--dump-objects`/`.cir` DOT 逐字节同 · 构建确定性 ×2(`corec f737e26b…`)。 + +**留 P6**:附录 E 逐条(现状/出处/影响面/建议/前置)。 + +--- + +## 裁决 + +**待维护者签核(四项;开工前须落纸。未签核时本计划 Task 步骤按「推荐」写死;改裁定 ⇒ 对应任务重定后重审)** + +| # | 议题 | 推荐 | 备选与代价 | +|---|---|---|---| +| 裁-1 | **单槽化范围** | **α 本批 + β 推迟 P6**(α = 内存面单源化 + 派生码;β = 盘面项索引 + corearch `atom_of` 读回) | 备选:**β 并入本批**。代价 = `.ccr` NOD 记录扩面 + `CCR_VERSION` 8→9 级联 + corearch 发射路径改造 + 新记录布局台账;收益 = §6.4 图节点级类型链盘面自洽。**判据**:β 是**新能力**而非收尾(无在野消费者,P4 T4 §7-② 已登记),与「本批只做删除/收口」的边界纪律冲突 | +| 裁-2 | **unknown 清零路线**(D-3-4 的 (a)/(b) 二选一) | **(a) 命名面判定化**(Task 3;**身份链 + 可判定面加强**,**不接**结构性展开层) | 备选:(b) legacy 长期化 ⇒ **本批 Task 4/5 取消**、P5 不再是「删旧收尾」、影子层保留;另注:**结构性展开**(P3a 展开层直接接等价面)**不采**——P3a 已实测反证(同形不同名 struct 被判等价 = 语义漂移,且推翻 P2a 对拍归零基线,`ty_shadow.cr:678-689`) | +| 裁-3 | **影子层去向** | **整体下线**(Task 5;对照物随 legacy 一并消失,保留 = 无对照物的自欺) | 备选:保留为**调试通道**(需自造第二判据 ⇒ 等于重造 legacy;不采) | +| 裁-4 | **对象布局表示位 + `MAX_*` 定长槽**(D-3-7 / D-3-8) | **P5 不做,合并为一个 P6 批次**(前置四条见附录 E);P5 只做「未覆盖面**不得退化为静默错值**」的守门复验 | 备选:本批合并做。代价 = 记录布局(`dyn_arr.cr` 定长槽)+ `ast.cr` 镜像归位 + `.ccr` SYM/段布局 + `OFF_*` 常量族 + 读回闸 + `sh_*` 展开层 **6 面同改** + `CCR_VERSION`/`CIR_CACHE_VER` 级联 bump(T6 解除评估 §3 已实测);在野需求 = 0(语料最大结构体 9 字段 / 无超限枚举) | + +**本计划裁定(D19–D28,实施时按此执行)** + +| # | 决策 | 理由 | +|---|---|---| +| D19 | **单槽化形态(α)= 槽语义对调 + 辅码显式分离**:`OFF_DF_TK`(40)语义改为**类型项引用**(-1 = 无项;类型面唯一真源);`OFF_DF_TK_TERM`(64)**改名 `OFF_DF_AUX`** 承接原混用码的**非类型部分**(旗标/宽度;0 = 无);`ESZ_DFNODE` **保持 72B**(两槽对调语义,不增不减) | ① 现状 `tk` 是**混用槽**(`IR_BOUNDS_CHECK` 的 `1` = 动态上限旗标、`IR_DEREF`/`IR_STORE_PTR` 的 `8` = 访问宽度)⇒「单槽」在物理上不可能承载非类型信息(spec §3.4 的「单槽」= **类型面**单源,**非**「字段减一」);② 不改字节数 ⇒ `.ccr`/`.cir` 布局与后端零扰动面最大化;③ 槽改名(`TK_TERM` → `AUX`)正是消灭「`tk` 被当类型行读」的陷阱类(P4 T4 §2 实测:宽度码 `8` 数值上恰是 `TI_DEX_S` 行、旗标 `1` 恰是 `TI_DEX` 行) | +| D20 | **项引用稳定形态裁定(D-3-2,单槽化前置)**:① **本批不引入任何跨进程项引用**——凡跨边界者(`.ccr` NOD 盘记录、`.cir` 快照)**一律派生码**(即:码 = 类型行/辅码,由类型表与分类表唯一决定);② **未来形态规范(写入格式 spec 注释)**:需要跨边界时唯一许可形态 = **TYPE 段文件内项索引**(D13 确定性装填 ⇒ 文件内索引是稳定引用,corearch 已能重建该项表);③ **禁令**:进程内项表索引**不得**跨进程/跨复位边界(`bad_term=329` 红证,`p4-task4-report.md` §4) | ① 稳定引用问题的根因是「进程内表索引被当稳定 ID」,而本仓已有根治先例 = 装载侧重派生(`cir_cache.cr:448-453`);② 任何「纯函数值与其入参并存」= 第二真源(D15 修正的附因);③ 把形态**规范**与**实现**分开落纸:规范立即生效(约束未来批次),实现(β)转裁定裁-1 | +| D21 | **`atom_of` 契约**:`sh_atom_of_term(term)`:`TT_ATOM ∧ b ≥ 0 ⇒ b`(类型行);其余(union/product/μ/空链/负)⇒ **-1 = 非单一原子**(**不得**近似、不得回 0) | 桥接层单原子项的 `b` 槽**就是**类型行(`tt_atom(nak, ti, -1)`,`ty_shadow.cr:281` 区域);非原子项(`T?` 的 union、元组/结构 product)在**现有后端分派点**上语义等价于「不是 TI_STR / 不是 TI_DEX」——实施时逐点核(Task 2 Step 4) | +| D22 | **两条硬不变量**(单槽化):① **派生码不变量**:`iri_tk` 的新派生值 ≡ 旧值(**全语料逐节点对拍 + (op,tk) 直方图 diff = 空**);② **互斥不变量**:辅码 ≠ 0 ⇒ 项 = -1;项 ≥ 0 ⇒ 辅码 = 0(分类表逐 op 唯一归位) | ① 把「零行为变化」变成**可判定**(而非「看起来没变」);② 互斥不变量是防「一个 op 同时占两槽」的半成品消费面 | +| D23 | **类型行不可译 ⇒ save 拒绝**(照 D13 先例):`emit` 期类型面 op 的 `tk` 若 `sh_term_of_ti` 回 -1(行越界/桥接缺口)⇒ **建项失败**,`save_ccr` 拒绝(rc=1 + 诊断),**不得**留空项静默落盘 | 三态纪律(-1 不得当 0/1);D13 已确立「段内容 = 表纯函数,不可译即拒绝」的形态,节点项面同源 | +| D24 | **删除顺序(硬性)**:**清零(Task 3,判据 = 全语料 + 探针语料的回落计数 = 0)→ 删 legacy(Task 4)→ 影子层下线(Task 5)**;Task 4 与 Task 5 **必须连续提交**(Task 4 删掉对照物后影子判定对拍**停摆**——中间态不跑影子判据,且不得发布) | 影子通道是删除批的**安全网**(找不到差异 ⇒ 才敢删),删在清零之前 = 盲删;对照物与影子层是**同一根绳**(影子 `ok` 的唯一来源 = legacy,`checker.cr:612`) | +| D25 | **站点处置**:**站点 6 删除**(不可达:`tok2op`(`parser.cr:170-184`)不产 `OP_ASSIGN`;`T_EQ` → `EXPR_ASSIGN`(`:227`);`+=` 族显式构造 `EXPR_ASSIGN` 包裹的 `EXPR_BINARY` 且 op ∈ {ADD,SUB,MUL,DIV}(`:216-224`)⇒ `EXPR_BINARY+OP_ASSIGN` 分支全仓不可达);站点 1–5/7–10 **判定点全部保留**(只去 `sh_site_begin` 挂点),站点 3(2 次命中)同此;**站点 id 不重编号**(6 退役为 0 槽,注记落表) | D-3-6「零命中 ≠ 可删」:删前先出**评级表**(Task 0 Step 4);站点下线 **≠** 判定点下线——判定调用一律保留(本任务是「删对拍挂点」,不是「删检查」) | +| D26 | **删件 → 替代证据(安全网替代,逐件落纸)**:① 影子判定对拍(in-process 旧/新对照)→ **冻结基线二进制的同源对拍**(72 档 × `check`,pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源)+ **行为探针集**(Task 3 产物,**行为断言形态**,不依赖 legacy 存活)+ **突变控制**;② legacy 回落(引擎 -1 的兜底)→ **清零判据**(回落计数 = 0)+ **残留 -1 的显式政策**(硬错/登记,**绝不静默当 0/1**,三态纪律);③ 桥接调试计数(`g_shadow_hits/entries` 在生产路径,`ty_shadow.cr:48`)→ 删除(含 `sh_tk_term_of_code` 的 hits 存-复原 hack) | 「网」不能只删不换;每件删除必须在**同一提交**里指明「过去谁覆盖它 / 现在谁覆盖它」,且新网必须**在删除前已验证可用**(Task 3 的探针必须在影子存活期跑出 0 差异,删除后再跑同一批) | +| D27 | **不收回/不新建**:可选程序 `.cir` 缓存**不收回**(随 D-3-9-(i) ⇒ P6,与 D-3-7 同批);`--type-shadow`/`--type-shadow-dump`/`--verify-named-dedup` 三个隐藏通道**随影子层下线一并删除**(`main.cr:236-238`/`:244`/`:182`);`tt_display` 零 `str_intern` 改造**不做**(登记 P6) | 收尾批的「删」与「不新增」边界;隐藏通道是调试/对拍结构进生产路径的同族(TODO #24 同族清偿口径) | +| D28 | **判据引用口径逐字继承**(P4 D-1/Global Constraints):`.ccr` **两套命令口径**必须带标(`corec ccr` vs `corec build --static`,差恒 143B);**冷/热**:非可选程序冷≠热为**预存**(各自确定性可重复,豁免「冷≡热」判据)、可选面零足迹程序冷≡热成立;TYPE 段「纯函数」承诺**项面成立、行面不成立**(热路径行表 12/冷 15,前缀关系);影子对拍**站点覆盖与差异数同报**;dumps 目录空 = `sh_dump_write` 早退 ⇒ **空洞证据不得引用**;比较任何 `.ccr`/缓存态产物前 `clean-cache` | 判据面已在 P4 收口;P5 不得回退到「与旧版逐字节同」的旧口径(TODO #26),也不得复引 P4 作废值 | + +--- + +## Global Constraints + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**(多 agent 在场:一律 `jj commit -m … `);所有命令 `nice -n 19`;一构建一编译**串行**;cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +- **基线纪律**:每任务开工先取**同任务起点**基线(binary sha + 语料计数),**不得沿用 P4 计数**(P4 期间源码多次变更,`decisions`/`.ccr` 记录值跨提交不可比——T4 §7-⑧ 口径)。 +- **ELF canary(硬闸,全批)**:`clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/…` → sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`。**任一任务变即停下上报**。⚠ 该语料对类型层不敏感——它是「发射面零泄漏」证据,**不得**当新语义正确性证据(P3 附录 C.5-6 口径继承)。 +- **三态纪律(不得稀释)**:引擎 -1(预算耗尽/未覆盖面)不得当 0/1 用;载入失败/段缺失/行不可译/项不可建 ⇒ **拒绝**(不得静默当空表当 0);save 侧装填遇不可译行/签名项 ⇒ **save 拒绝**。 +- **新硬错先 report-only**:本批新增的任何 rc=1 门(含 Task 6 的 `EXPR_LET` 判定、Task 3 若引入的残留 -1 政策)必须先在**全语料**(`src/compiler/_import.cr` 自指 + `tests/suite` + `tests/selfhost` 内联源 + `src/stdlib` + `examples`)report-only 跑一遍,清单审查后才开门(硬门 = `main.cr` parse 阶段 `g_diag_count > 0` 硬名单闸,本轮实读 ≈:149-170;照 #29/TM03 先例)。 +- **清单双注册面(B.6,硬性)**:任何清单/段表改动必须**三面同核**——`build_selfhost_native.py`(`corec_files`/`corearch_files`/`corelsp_files`)↔ `src/targets/x86_64-linux/_import.cr`(project-mode)↔ `src/compiler/_import.cr`;每任务回归面**必含** `tests/selfhost/test_backend_bootstrap.py`(rc=0 + 构建日志 `error[` = 0)。 +- **回归网(本批切换;D26)**:Task 5 之前 = 影子对拍每任务复跑(**站点覆盖与差异数同报**;载体面零覆盖声明继承);Task 5 之后 = **冻结基线同源对拍**(Task 0 冻结的 pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源,72 档 `check` 日志 + rc 逐档 diff)+ 行为探针全集中跑 + 突变控制。**载体正确性证据**(段/读回/TK/表示)**一律** = 结构性断言 + corearch 读回对拍 + 定向探针(影子通道对其零覆盖,继承声明)。 +- **`.cir` 缓存面**:`CIR_CACHE_VER` 手工 bump 是格式/语义代变更的既定配方;**DF 记录布局/语义一变即必须 bump**(v16 教训)。本批 D19 下**布局零变化 ⇒ 不 bump**(实施时以「盘记录逐字段对照 + `test_cache_identity.py:VER_EXPECTED` 不动」复证并登记)。 +- **不得回退既有收纳**:TS01-04/TK02 硬门(#29)、TM03/TG02 门(#31/#33)、F2 常量档拒绝语义与「N 不回身份」、F5 槽位契约(#25/#28)、效应/纯度判据(#26/#27)、`iface_satisfies` 轴序 A>C>B、逐成员判定、P4 全批(段机制/确定性装填/允许清单制/可选表示)——本计划一律不动;冲突 → 停下上报。 +- **本语言无三元运算符**;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);自测用例**只增不减**;判据「≥N 例」= 下限(可增不可减)。 +- **多 agent 纪律**:不并发构建;提交路径限定;本批为只读侦查 + 计划,实施时逐任务串行。 + +--- + +## 侦查底座(2026-09-13 实读;file:line = 当前工作树(父提交 `9bcb7083` = P4 收官,侦查时工作副本为空、无未描述改动;工作副本 id 随编辑变动,不作为锚点);未标注者为本轮实读实核) + +**判定面(删除对象)** +- `type_equal_legacy`:定义 `checker.cr:504`(降级注记 :495-503);**内部递归自调 7 处**(:518 数组 / :528 元组 / :534 引用 / :537 指针 / :540 切片 / :547 可选 / :560 泛型应用);**回落调用 2 处**(:590 桥接缺口→`g_replace_bridge`;:599 引擎 -1→`g_replace_unknown`);**影子对照 1 处**(:612 `if type_equal_legacy(t1, t2) { ok = 1; }`)。判定入口链 = `type_equal`(:604-615)→ `type_equal_engine`(:573-601)→ `ty_equiv`。 +- `g_replace_unknown`/`g_replace_bridge`:声明 `globals.cr:421`;复位 `checker.cr:246`;摘要打印 `ty_shadow.cr:628/:630`。 +- 钉住回落语义的自测例 **5 处**(删 legacy 时必须重钉):`t3.unknown_fallback_legacy`(`type_selftest.cr:1814`)· `t3.bridge_fallback_legacy`(:1821)· `unf.nominal_equiv_atomic_unknown`(:2487,断言 `ty_equiv(命名项, 命名项) == -1`)· `unf.nominal_type_equal_false`(:2492,断言回落 +1)· `t1.n_face_gate_nested`(:2691,断言**不**回落)。 +- 桥接层 legacy 副本:`sh_native_ak_legacy`(`ty_shadow.cr:101`)/`sh_base_ak_legacy`(:109);零生产调用,仅 `type_selftest.cr:1957/:1966/:1977` 三例引用。 +- 影子层:站点表 `ty_shadow.cr:472-483`(10 站点;**站点 6** = `checker.cr:2454-2463` 的 `EXPR_BINARY+OP_ASSIGN`,:479 注「当前 parser 已无生产点 = 遗留路径,无样本」——**本轮独立复核成立**:`tok2op`(`parser.cr:170-184`)零 `OP_ASSIGN`、`T_EQ` → `EXPR_ASSIGN`(:227)、`+=` 族(:216-224)显式包裹且 op ∈ {ADD,SUB,MUL,DIV};对侧 `ir_gen.cr:927-929` 同分支);`sh_site_begin`(:504,关态早退 + 10 槽直方图);环缓冲 `sh_record`(:519 起)/`sh_compare`(:539)/`sh_site_name`(:574)/`sh_kind_name`(:588)/`sh_report`(:607)/`sh_dump_write`(:651);`sh_finish`/`sh_dump_write` 调用点(`main.cr:212-216`);CLI:`--type-shadow`(`main.cr:236`)、`--type-shadow-dump`(:237)、`g_shadow_on`(:244)、`--verify-named-dedup`(:182/:238)。 +- **桥接缓存(生产面,**不删**)**:`g_shadow_map`(16B/条 {ti, term})+ `g_shadow_entries/hits`(`ty_shadow.cr:56-62`);重置钩子 `sh_map_reset`(:130-133)经 `init_types()` 调用(`checker.cr:240`,P2a 评审 Critical 修复)+ `sh_unf_map_reset`(:243)+ `tt_layer_reset`(`type_terms.cr:144`,`ccr_types.cr:57-59` 亦调)。**登记**:计数在生产路径(`ty_shadow.cr:48` 注「影子关时也读写」)⇒ `sh_tk_term_of_code` 须存-复原 `g_shadow_hits`(:383-386 区域)——D26-③ 删除对象。 +- 引擎命名面(清零对象):`lit_implies` 两处 `AK_NAMED ⇒ g_ty_uncovered=1; return -1`(`type_engine.cr:192` 否定面 / :198 正原子面);`ak_disjoint` 对 NAMED **不断言互斥**(:76-78,P0 终审 Important B 实证);变型表 `ty_variance_of`(:264-277)表外构造子一律**不变**(含 AK_NAMED);不变槽 mismatch ⇒ `g_ty_uncovered=1; return -1`(`tt_list_variance_at` :305-307);`tt_type_elem_same`(:231)+ 其登记注(:219-230:**「确定不等价 ⇒ 0」加强须先有「链元素皆类型项」不变量**)。桥接命名构造点**两处**:`sh_term_of_ti`(`ty_shadow.cr:328`/`:330`)+ `sh_sig_term_of_ti`(:951)——**两处必须同步**(双构造点契约,照域/模式项先例)。 +- 展开层(**本批不动**):边界声明 + 反证 `ty_shadow.cr:678-700`(只在满足判定/域查询;等价面一律不经;「把展开项接进等价判定」= 同形不同名 struct 判等 + 推翻 P2a 归零基线);实现 `sh_struct_term`(:833)/`sh_variant_term`(:874)/`sh_enum_domain_term`(:887)。**注意**:`sh_unf_map_reset` 等缓存不得 `str_intern` 面新增(留给 P6)。 + +**DFNode / 载体面(单槽化对象)** +- 布局:`ESZ_DFNODE=72`、`OFF_DF_TK=40`(混用码)、`OFF_DF_TK_TERM=64`(项引用;P4 新增)——`dyn_arr.cr:103-121`(含三条契约注:① `OFF_DF_TK` 语义/字节零变化;② 项槽只在 **emit 期**派生(时点不得后移);③ 唯一写点 = `df_create_node` + `.cir` 装载重派生)。 +- 写点:`df_create_node`(定义 `dataflow.cr:98`;tk 写 :108、项槽写 :109);**唯一调用点 = `emit`**(定义 `ir_gen.cr:329`,调用 :345——本轮 grep 全仓仅此一处调用);派生函数 `sh_tk_term_of_code`(`ty_shadow.cr:377`,**允许清单 = {IR_CONST, IR_BINARY}**,另有 4 处排除说明含「IR_ALLOC/IR_CALL/IR_I2F/IR_F2I/IR_LOAD 传类型行但后端不按 ti 分派 ⇒ 暂不入清单,**留待 P5 单槽化时按需再裁**」;hack = 存-复原 `g_shadow_hits`,:384-386)。 +- 派生链:`df_rebuild_ir`(`dataflow.cr:448-455`,tk → `iri_set_tk`)· `ir_gen.cr:338`(`iri_set_tk(idx, type_kind)`)。 +- 消费点全清点:`instr.cr:460`(`ti := iri_tk`,全后端 5 分派点:`IR_CONST`==TI_STR / `IR_BINARY`==TI_DEX / `IR_DEREF`+`IR_STORE_PTR` 的 access_width / `IR_BOUNDS_CHECK` != 0)· `regalloc.cr:457`(dex 危险面)· `lower_to_core.cr:250/:574`(HIT 降级)· `dump.cr:125`(文本)· `ir_gen.cr:129`(`arr_len_lit_of` 的 `TI_STR` 判定,读 **DF 节点** tk)· `interp.cr:256/:546`(读 DF 节点 tk)· `provenance_verify.cr:57`(读 DF 节点 tk 当**宽度**)· 盘面 `ccr_io.cr:511`(→ `OFF_NS_TK`)/:768(→ NOD 记录)· 快照 `cir_cache.cr:323`(写 `iri_tk`)/:540(读)/:448-453(DF 节点装载 + 项槽重派生)。 +- 盘记录:`ESZ_NOD_DISK=36`(`ccr_io.cr:143`);`.ccr` 八段结构断言套件 `tests/selfhost/test_ccr_types.py`(34 例);快照版本 `CIR_CACHE_VER=17`(`cir_cache.cr:48`,:28-46 为「为何不 bump」的长注)。 +- 冷/暖面:DF 节点装载 loop(`cir_cache.cr:431-453`)+ IRI 装载 loop(:530-545)**两路对称**;快照入场闸 = 版本 `:383` / 编译器身份 `:389-391` / 指纹 `:396`;P4 ㉙ 用例锁「冷/暖 (op,tk,term) 逐行同」。 + +**TODO / 未收口面** +- `#32`(`EXPR_LET` 无检查):`checker.cr:3079-3097` 的 `EXPR_LET` 分支——`val_ti := infer_expr(val_c)` 后 `ti := val_ti; if type_node >= 0 { ti = res_type_node(type_node); }`(:3096-3097)**无任何兼容检查**;可复用判定 = `type_compat_strict`(:442)+ `diag_type_incompatible`(:486)。姊妹面 = #20(F3 调用位点无诊断:`unify_types`(定义 :1905)的返回值在 :2017 被丢弃——**TODO #32 记录的旧锚 `:1053` 已随行漂移**,以本次实读 :2017 为准)。**全局声明面同形**(`checker.cr:1614-1620` 全局 let 注册亦无比对——#32 的修法须显式划界是否覆盖)。 +- `#35` **已由 P4 T6 划销**(`test_enum_limit.py` 13 例);`#43` = P4 T5 落地(其「登记面」= 可选程序关缓存 + 暖态 SIGSEGV,本计划 Task 1/D27 处理);`#44` = P4 收官(本计划 Task 7 接续)。 +- 判据面:`src/ci/run.sh:68-90`(selfhost-tests 挂点,含 `test_ccr_types`/`test_optional`/`test_enum_limit` 等);72 档同源对拍 runner `/tmp/p3t0_run.sh`(**本轮实查存在**,2026-09-12 版)。 + +--- + +## Task 0(前置,纯侦查 + 基线冻结;**源码零改动**) + +**Files:** 无源码改动。产物 = 报告 `.superpowers/sdd/p5-task0-report.md`(工作区,不入提交)+ 冻结基线二进制 `/tmp/p5t0/base/{corec,corearch,corelsp}` + 直方图/清点原始输出留档(`/tmp/p5t0/`)。 + +**Interfaces(产物四表,全部落报告;数字一律实测,未实测标「待实测」):** +- **表 A:全 op `tk` 语义分类表**(单槽化 Step 1 的输入)——逐 op:`{类型行 / 旗标 / 宽度 / 无}` + 后端消费者(`instr.cr:460` 的 5 分派点 / `regalloc.cr:457` / `lower_to_core.cr:250·574` / `dump.cr:125`)+ 是否入项槽 + 依据(P4 T4 §2 方法扩面:`instr.cr` 分支逐条审计 + `ir_gen.cr` 全 `emit(` 调用点逐 op 静态审计 + 全语料 NOD (op,tk) 直方图)。**必须显式再裁 P4 排除项**:`IR_ALLOC`/`IR_CALL`/`IR_I2F`/`IR_F2I`/`IR_LOAD`/`IR_LOAD_ENUM_TAG`(P4 T4 §2 注明「留待 P5」)。 +- **表 B:站点评级表**(D-3-6 / D25)——站点 1..10 逐条 `{语义 / 可达性证明 / 语料命中 / 判定点保留? / 挂点去留}`;站点 6 附不可达三面证明(`parser.cr:170-184` / `:227` / `:216-224`)。 +- **表 C:P5 起点基线**——冻结二进制三 sha + 冒烟;72 档 × {`check`, `shadow`} 的 `decisions`/`agree`/全计数/站点覆盖/rc 分布(**以此为新基线**,P4 的 32620 等值不得直接沿用)。 +- **表 D:-1 面清点(report-only)**——探针语料逐形态记录的 `ty_equiv`/`ty_sub` 负值计数 + `g_ty_uncovered`/`g_ty_exhausted` 归因(Task 3 的输入清单)。 + +- [x] **Step 1: 冻结基线**:`nice -n 19 python3 build_selfhost_native.py`(串行、无其他构建)→ 记录三二进制 sha + 冒烟 42;**再构建一次**取「构建确定性」(两次逐字节同);拷贝到 `/tmp/p5t0/base/`。判据:两次构建 sha 同 + 冒烟 42 + `/tmp/p5t0/base/corec` 可独立跑 `check`。 +- [x] **Step 2: 语料基线与 runner**:核 `/tmp/p3t0_run.sh` 可用(不存在则按 T7 口径重建:72 档 = `tests/suite` + 自源 + 后端/内核单元 + `src/stdlib` + `examples`,逐档 `clean-cache`);复跑 {`check`, `shadow`} → 表 C。 +- [x] **Step 3: 表 A**(全 op `tk` 语义分类表;含排除项再裁)——**这是 Task 2 的判据输入,必须逐 op 有依据行,不得留空**。 +- [x] **Step 4: 表 B**(站点评级表;站点 6 不可达证明)。 +- [x] **Step 5: 表 D**(-1 面清点):构造/汇总**命名面 + 泛型应用面 + 边界形态**探针(同/异名命名、两次实例化的同实参泛型应用、递归命名 `struct Node { next: *Node }`、`*Named`/`[Named;N]`/`Named?`/元组含命名、别名一层、泛型形参位、union/μ 面、预算耗尽面),逐条记录引擎负值 + 归因(在第二次 `ty_budget_reset` 前读 `ty_uncovered`/`ty_exhausted`——P1 Step 0 口径)。 +- [x] **Step 6: D-4 开工检查单逐项核**(附录 D 六项)并落报告:口径表 / D-3-2 先裁 / D-3-4 先裁 / D-3-7+8 合并评估 / D-3-9-(ii) 排期 / #32 排入 / 判据沿用清单。 +- [x] **Step 7: 报告 + 提交**(**仅报告不入源码**;提交 = 计划文件本身 + 报告路径登记若维护者要求)——本任务默认**不提交源码**(`jj status` 应干净)。 + +**判据**:四表齐 + 检查单逐项有结论 + 源码零改动(`jj status` 干净)+ 报告内数字全部可追溯(命令 + 原始输出片段)。 + +### Task 0 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task0-report.md`) + +**冻结基线**(本批下游任务的比较基准):`build_selfhost_native.py` ×2 逐字节同;`corec 5d2b15ad…` · `corearch 493dc490…` · `corelsp 18b94bd9…`(= P4 记录值);ELF canary `95084e7b…d475`(28822B)IDENTICAL;`corec2==corec3` cmp IDENTICAL(双 `4919023b…`)+ N06=0 + 冒烟 42 + `--help` rc=1;全枚举 **55/55**;`check src/compiler` rc=0;`selftest-types` **355/355**;`.ccr` 两套口径冷态四条与附录 D-1 逐条同(90823/134701/90966/134844)。**72 档语料基线**(runner `/tmp/p3t0_run.sh`,sha `12dc571f…`):`check` rc 分布 **34×0/38×1**;shadow 70 档 `decisions=32620 agree=32620`、全计数 0、站点覆盖 `assign-node=24721 · fn-body-ret=5828 · if-branch=2037 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`(其余 4 站 0;站点 6 = 0 命中佐证不可达)。产物 `/tmp/p5t0/{base/,parity/base_{check,shadow},tkhist/,probes/,negprobes/,enum_chain.out,corec2,corec3}`。 + +**四表结论(摘要)**:**表 A** 51 opcode 逐行分类(类型行 9 · 旗标 1 · 宽度 2 · 无 38 · 填充伪值 1);后端 `ti` 消费者机器审计 = 恰 5 分支;现行允许清单量化复核(790828 节点)`term≥0` 恰 = {CONST,BINARY} 且清单外带项 = 0。**表 B** 10 站点评级(5 站有命中、5 站零命中)+ 站点 6 不可达三面证明(parser `tok2op`/`T_EQ`/`+=` 族 + 语料 0 命中)。**表 D** 9 探针:异名同形命名 + 同实参泛型应用两次实例化为实测引擎 -1(均 `unknown_engine_uncovered` + `replace_unknown` 回落),其余形态 0 负值;`ty_sub` 负值无计数通道(读码代替,证据级登记);预算耗尽面用户语料不可达。 + +**新增开工前未决项(T2 前必须落纸;报告 §6)**:① **F1** `ir_gen.cr:1758` 的 5 参 `emit` 调用 ⇒ `IR_HOTPATCH_ROUTE` 的 tk = r9 残留(实测 0/4 两态;`bootstrap/backend/x86_64_stack_asm.py:222-226` 只为实参写寄存器)——建议 T2 同提交补显式第 6 实参并把派生码 4→0 登记为已裁决偏差;② **F2** `atom_of` 对**复合行**不可逆(实测 `ptr_ref_first` BINARY tk=10 = 指针行 ⇒ `tt_atom(AK_PTR,-1,chain)` 的 b=-1 ⇒ 派生 ≠ 旧值)——T2 须在「扩 atom_of 携行 / 复合行走 aux / 分类表限原子行」三条中先裁(D22-① 在这三类 op 上不可满足);③ op22/41/0 零发射点(死 opcode)、op35/36 仅 `@x()` 括号形态可达(语句形态静默无 IR)——登记不修。 + +## Task 1:`.cir` 暖态 SIGSEGV 根因修复(预存缺陷;D-3-9-(ii),独立可做) + +**Files:** Modify `src/compiler/cir_cache.cr`(根因处)· 可能 `src/compiler/main.cr`(cache 使能面)/ `src/stdlib/fmt.cr`(`str_len` 消费面——**只在根因确证涉及时**)· New/Modify `tests/selfhost/test_cache_identity.py`(或新套件 `test_cir_warm_path.py`)。 + +**背景(登记值,实施时**先复现**,不得引用为已证)**:`corec build src/compiler/main.cr -O 0` **冷 rc=0 / 暖 rc=139**;gdb 栈 = `load_cir_cache`(`cir_cache.cr:373`)→ `str_len`(`fmt.cr:4`)← `load64`;**基线二进制同病**(P4 T5 §7-5 实测,非 P4 引入)。P4 T5 的「可选面关 `.cir` 缓存」只**顺带规避可选程序**,非本缺陷的修法。 + +- [x] **Step 1: 红态复现(本任务自跑)**:`clean-cache` → 冷运行 rc=0 → **第二次**运行 rc=139(复现记录 = 命令 + rc + 是否稳定可重复);gdb 栈复现并落报告;用 `/tmp/p5t0/base/corec`(Task 0 冻结)复证「预存、非本批引入」。⚠ 复现时注意 TODO #5 家族的 clean-cache 口径(跨重建必须 clean,相对 cwd)。 +- [x] **Step 2: 归因(写读对称表)**:把快照的写侧(`cir_cache.cr` 写点)与读侧(:430-560 两个装载 loop + 入场闸 版本 `:383` / 身份 `:389-391` / 指纹 `:396`)逐字段列成**对称表**(字段 / 写偏移 / 读偏移 / 长度来源),核 `pos` 推进与长度域;**定位根因**(候选:长度字段读偏/越界、0 长与空串混淆、函数计数与实际记录数不一致、身份闸后的残余旧条目),**不得**先加守卫吞异常(若最终只能加守卫,必须在报告里说明根因不可达的实测证据 + 停下上报)。 +- [x] **Step 3: 用例(红→绿)≥6 例**:① 冷/暖两次运行 rc 均 0;② 冷/暖产物(ELF + `.ccr`,带口径)逐字节同;③ 多函数/多串/0 串/大串/含可选程序(关缓存面不受影响)各一;④ 既有 `test_cache_identity.py` 全绿不回归;⑤ 自源暖态(本缺陷的红态语料)转绿。 +- [x] **Step 4: 判据**:新套件全绿;**ELF canary IDENTICAL**;五 CI job rc=0;全枚举 55/55;`corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42;`.ccr` 记录值(两套口径)冷态复测;`test_backend_bootstrap.py` rc=0 + `error[`=0;自源 `check src/compiler` rc=0。 +- [x] **Step 5: 提交**:`fix: R2 P5 Task 1——.cir 暖态 SIGSEGV 根因修复(...根因一句话...);冷/暖两态产物一致回归` + +**停条件**:若归因指向格式缺陷(需 `CIR_CACHE_VER` bump)或更深的缓存设计 ⇒ **停下上报**(bump 是允许面但须单独裁决 + 记失效面);若暖态 SIGSEGV 与「快照面语义」纠缠到无法在本任务内隔离 ⇒ 上报转独立批(不硬塞进 P5)。 + +### Task 1 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task1-report.md`;证据 `/tmp/p5t1b/`) + +**根因(实测,非引用)**:装载侧**逐函数** `read_file(path)` = `alloc(fsize+1)`,而 bump 分配器(`rt.s alloc`)**不回收** ⇒ 946 个快照读缓冲**累计 1.1674 GiB > 1 GiB 堆**(`rt.s heap_start: .space 1024*1024*1024`;单条目 max 3,987,879B = 3.80MiB,因快照含**全程序边表** ⇒ 尺寸 O(程序) 而非 O(函数))⇒ `alloc` 耗尽返回 0(`.Lalloc_oom`)⇒ `load_cir_cache` 首行 `str_len(0)` → `load64(0,-8)` = **暖态 SIGSEGV**(gdb 三跑同栈 `load64(rdi=0,rsi=-8) ← str_len ← load_cir_cache`;冷态不读快照 ⇒ rc=0)。**停条件核**:写读对称表逐字段核(字段/写偏移/读偏移/长度来源 + `pos` 推进)⇒ **无长度域/格式缺陷 ⇒ 不 bump `CIR_CACHE_VER`**(保持 17)。 + +**修法(根因级,非加钳位)**:`cir_cache.cr::cir_read_snapshot`(新增 30 行)= **单一复用读缓冲** `g_cir_read_buf/g_cir_read_cap`(峰值 = 最大单条目;读侧自此与写侧既有 `g_cir_write_buf/g_cir_write_cap` **同构**——读侧原是唯一逐调用新分配方);`w64(buf,-8,fsize+1)` 保住 `str_len` 口径 ⇒ **消费面(r64/str_sub 四点)零改动**;载入失败(open 失败 / `fsize<=0` / 短读 / 缓冲不可得=read EFAULT / 长度<56)⇒ `-1` = **cache miss**(三态纪律:载入失败 ⇒ 拒绝,miss = 无害重建,绝不静默当空表)。刻意**不写** `buf == 0` 形态空判(该 string-int 混比在 bootstrap 侧 `x86_64_stack_asm.py:164` 编成 `str_eq(buf,0)` ⇒ `str_len(0)` 自崩溃;**建议另单**,登记于报告 §6-①)。 + +**判据(全部本实例实跑)**:新套件 **19/19**(`COREC_WARM_SELF=1` ⇒ **21/21**;RED 对照:冻结基线 × 同套件 = **15 PASS/1 FAIL**(D3 rc=-11))· `build_selfhost_native.py` rc=0(`corec` `8edb967f…` 两次构建逐字节同;`corearch`/`corelsp` = T0 记录值)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · `selftest-types` **355/355** · 全枚举 **56/56**(48+1 selfhost + 7 bootstrap;+1 = 本套件)· ELF canary `95084e7b…d475` **IDENTICAL** · **暖≡冷(自源)**:冷 + 暖×2 三跑 ELF 同 sha `13230542f79da591c4ea…`(`cmp` IDENTICAL)、`.ccr` **八段冷==暖1==暖2**、946 条目戳三跑全等(**真命中**)、冻结基线冷产物亦同 sha(零发射面漂移)· 自举链 `corec2==corec3` `cmp` **IDENTICAL**(双 `13230542…`)+ N06=**0** + 冒烟 **42** + `--help` rc=1 · **突变控制**(M1:`load_cir_cache` 回灌修复前读路径 ⇒ 套件 **15 PASS/1 FAIL** 转红;精确回滚 ⇒ 源 sha `1220742f…` 复原 + 三二进制**逐字节回原值**)· 语料既有失败面无新增。 + +**登记(不静默)**:① `string == int` 混比双路径分歧(bootstrap `str_eq` 分派;本修复绕开未修,建议另单)· ② `g_cir_read_cap` 于 alloc 失败时仍前进 = **良性确定性**(此后恒 EFAULT ⇒ miss;与写侧同形)· ③ 单条目 > 1GiB 的「格式分片」议题(当前 3.99MB,非本任务面)· ④ `.cir` 其余口径逐字未动(VER=17 / 身份闸 / 指纹闸 / 项槽重派生 / save 侧)。**交叉引用**:TODO #43 登记面 ⓪、#44 交接包 D-3-9-(ii) **本提交闭合**。 + +## Task 2:单槽化(α)——内存面单源化 + 辅码分离 + `atom_of` 契约 + +**Files:** Modify `src/compiler/dyn_arr.cr`(槽语义/偏移改名 + 布局注)· `src/compiler/dataflow.cr`(`df_create_node` 签名 + 存储 + `df_rebuild_ir` 派生)· `src/compiler/ir_gen.cr`(`emit` 分流;:129 消费点)· `src/compiler/ty_shadow.cr`(分类表驱动 + `sh_atom_of_term`;`sh_tk_term_of_code` 改造)· `src/compiler/cir_cache.cr`(装载侧重派生;**布局零变化**)· `src/compiler/dump.cr`(`--dump-tk-terms` 扩展)· `src/compiler/type_selftest.cr`(`t4.tk_*` 重钉 + 新例)· `tests/selfhost/test_ccr_types.py`(T4 节扩展)· New `tests/selfhost/test_single_slot.py`(或并入既有套件)。 + +**Interfaces:** +``` +ESZ_DFNODE : int = 72; // 字节数不变(D19) +OFF_DF_TK : int = 40; // ★语义变更:类型项引用(g_type_terms 行号;-1 = 无项)= 类型面唯一真源 +OFF_DF_AUX : int = 64; // 原 OFF_DF_TK_TERM 槽位改名:非类型辅码(旗标/宽度;0 = 无) + +fn sh_tk_face_of_code(opcode: int, tk: int) -> int // 分类表(Task 0 表 A 的代码化):0 = 类型行 / 1 = 辅码 / 2 = 无 +fn sh_tk_term_of_code(opcode: int, tk: int) -> int // 类型行 ⇒ sh_term_of_ti(tk);辅码/无 ⇒ -1(分类表驱动,取代 {CONST,BINARY} 硬清单) +fn sh_atom_of_term(term: int) -> int // D21:TT_ATOM ∧ b ≥ 0 ⇒ b;否则 -1 + +fn df_create_node(opcode, dest, src1, src2, src3, tk_term: int, tk_aux: int) -> int +// 存储:OFF_DF_TK ← tk_term;OFF_DF_AUX ← tk_aux(互斥不变量 D22-②) +``` +- [x] **Step 1: 用例(红)≥10 例**:① **类型面**:分类表为「类型行」的每类 op 各一正控(项槽 = `sh_term_of_ti(tk)` 同项);② **辅码面**:`IR_BOUNDS_CHECK`(旗标 1)/`IR_DEREF`/`IR_STORE_PTR`(宽度 8/4/2/1)三负控——项槽 **-1** ∧ 辅码槽 = 原码;③ **数值陷阱负控**(P4 同款):旗标 `1` 与宽度 `8` **不得**被当类型行建项(`TI_DEX`/`TI_DEX_S` 行);④ **无类型面**:`tk` 恒 0 的 op ⇒ 两槽皆空;⑤ **派生码不变量**:全语料 `iri_tk` 逐节点 == 旧值(对拍脚本 + (op,tk) 直方图 diff 空);⑥ **互斥不变量**:任一节点两槽不同时非空;⑦ **不可译拒绝**(D23):构造行越界/桥接缺口的类型面 op ⇒ `save_ccr` 拒绝 rc=1 + 诊断 + 无产物;⑧ **冷/暖**:项槽与辅码槽两态同值同索引;⑨ **`.ccr`/`--dump-objects` 逐字节同**(发射面零泄漏);⑩ **版本不动**:`CIR_CACHE_VER == 17` ∧ `test_cache_identity.py:VER_EXPECTED` 不动。 +- [x] **Step 2: 写点分流**:`emit` 按 `sh_tk_face_of_code(opcode, type_kind)` 分流:类型行 ⇒ `tk_term = sh_term_of_ti(tk)`、`tk_aux = 0`;辅码 ⇒ `tk_term = -1`、`tk_aux = tk`;无 ⇒ 皆空。`df_create_node` 签名同步(**唯一调用点 = `emit`**,逐处核);`dataflow.cr` 保持「仅存储、零前端符号引用」纪律(注记落文件头)。 +- [x] **Step 3: 派生面**:`df_rebuild_ir` 由 `(tk_term, tk_aux)` 派生 `iri_tk`:`tk_aux != 0 ⇒ tk_aux`;否则 `atom_of(tk_term)`;否则 0。**逐节点对拍旧值**(Step 1-⑤)。`.cir` 装载侧(`cir_cache.cr:448-453`)保持「重派生项槽」并把辅码槽按盘上码重派生(盘面仍 8 槽;DF 节点装载 loop 内两槽皆**重派生**,不得读盘项槽——P4 时点纪律)。 +- [x] **Step 4: 消费点核(逐点登记,后端预期零改动)**:`ir_gen.cr:129`(DF 节点 tk 的 `TI_STR` 判定)改走 `atom_of`(或按分类表保留派生码——**二者择一并落纸**);`interp.cr:256/:546`、`provenance_verify.cr:57` 同款逐点核(**读类型面 vs 读辅码面**必须分类正确——`provenance_verify.cr:57` 读的是**宽度** ⇒ 应改读 `AUX`);`instr.cr:460`/`regalloc.cr:457`/`lower_to_core.cr:250·574`/`dump.cr:125` **保持读 `iri_tk`**(派生码不变 ⇒ 零改动,逐点核并登记「读的是派生码,不是槽」)。 +- [x] **Step 5: 判据(零行为变化,硬闸)**:**ELF canary IDENTICAL**(变即停下上报)· `.ccr` 冷态逐字节同(同源基线,带口径)· `--dump-objects` 逐字节同 · **全语料 NOD/IRI (op,tk) 直方图 diff 空 + 逐节点对拍空** · `selftest-types` ≥355 → ≥365 · `test_ccr_types.py` ≥34 → ≥40 · 五 CI job rc=0 · 全枚举 55/55 · 自举链(cmp IDENTICAL + N06=0 + 冒烟 42)· `test_backend_bootstrap.py` rc=0 + `error[`=0 · 影子对拍复跑(下线前;**预期零变化**,站点覆盖同报)· **突变控制 ≥3 条**(分类表某格改坏 ⇒ 用例 + 直方图转红;辅码槽写成项 ⇒ 互斥断言红;`atom_of` 近似 ⇒ 直方图转红)。 +- [x] **Step 6: 提交**:`refactor: R2 P5 Task 2——DFNode.TK 单槽化(类型面单源:OFF_DF_TK=项引用 + OFF_DF_AUX=辅码;分类表驱动派生;零行为变化)` + +**风险与停条件**:分类表任一行证据不足 ⇒ **停下补测**(不得猜);若发现某 op 的类型面**既非类型行又非辅码**(第三类)⇒ 上报(D19 需扩第三槽或另裁);`.cir` 面若因派生顺序产生冷/暖分歧 ⇒ 停下(P4 ㉙ 用例应立刻转红)。 + +### Task 2 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task2-report.md`;证据 `/tmp/p5t2/`) + +**形态(D19 落地)**:`OFF_DF_TK`(40) = **类型项引用**(类型面唯一真源)、`OFF_DF_TK_TERM`(64) 更名 **`OFF_DF_AUX`** = 辅码(0 = 无);`ESZ_DFNODE` 保持 72B。**分类表代码化**(`ty_shadow.cr`:`sh_tk_is_type_face`/`sh_tk_row_state`/`sh_atom_of_term`/`sh_tk_face_of_code`/`sh_tk_split`(+`_load`)/`sh_dfn_code_of_slots`):类型行面 = `{CONST, BINARY}` ∪ P4 六排除项再裁的 `{ALLOC, CALL, LOAD, I2F, F2I}`(P4 的「后端不按 ti 分派」= 发射面判据,不适用于语义类型面);辅码面 = `BOUNDS_CHECK/`DEREF`/`STORE_PTR`/`SPAWN`/`HOTPATCH_ROUTE`;其余无面。写点 = `df_create_node`(仅存储;BOUNDS_CHECK 的旗标依赖改读辅码);派生点 = `emit`/`lower_to_ccr`/`cir_cache` 装载侧共用 `sh_dfn_code_of_slots`。 + +**F1(裁决:修调用点)**:`ir_gen.cr:1758` 补显式第 6 实参 `0`(op39 属无面 ⇒ 显式「无类型」);已裁决偏差 = 含 hotpatch 语料该节点码 **4→0**(全语料 3 节点,热补丁测试档;该值本是 r9 残留垃圾、零消费者)——`hotpatch_test.cr` 的 `.ccr` 相应差 3 处(逐字节定位见报告 §3/§5-①)。 + +**F2(裁决:分类表只覆盖原子行 + 复合行走辅码)**:复合行(ptr/ref/array/slice/optional/null/tuple 行——项不可逆)⇒ `term = -1 ∧ aux = 原行码`(Task 0 候选 (iii)+(ii))⇒ **派生码逐字节 ≡ 旧混用码**(D22-① 全语料成立,含复合行)。**证明**(协调者要求的审计 + 探针/负控):5 个 `ti` 消费者谓词的值敏感面 = `{TI_STR=3, TI_DEX=1, 宽度, 旗标}`,复合行码(实测仅 `ptr_ref_first` BINARY tk=10 一例 + 夹具 tk=13)**永不落入**该面;且本设计下消费者读到的是**旧值本身**(非裁决备选的 -1)⇒ 不变按构造成立(突变 M1 反证该格可抓)。 + +**D23 闸(新硬错)**:类型面行的可译性 —— **仅 emit 路径**计数(装载侧不计数:暖进程类型表可更小,见下「判据重定」);`ccr_seg_prepare_save` 入口拒绝落盘(rc=1 + 计数诊断);**全语料 0 命中**(94 档 `face_fail=0`,report-only 先行);牙齿 = 突变 M4。 + +**判据重定(2 处,实测驱动)**:① 冷/暖比对由「索引全等」改为「**派生码面(op,tk,aux)逐行同 + 项内容(tag/a/b/c)等价**」——暖态类型表/项表可小于冷态(缓存命中跳过 `ir_gen` ⇒ 其 `alloc_type` 新行不存在;P4 窄清单掩盖、P5 扩面暴露;根治 = β 盘面项索引,P6);② `--dump-tk-terms` 头部新增 `rows=`(进程类型表行数)与 `aux_nonzero=`/`face_fail=` 观测列,供上述精确形态断言。 + +**判据(全部本实例实跑)**:`selftest-types` **367/367**(355+12)· `test_ccr_types` **40/40**(34+6)· `test_cir_warm_path` 19/19 · `test_cache_identity` 5/5(`VER_EXPECTED=17` 未动)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· `.ccr` 两套口径冷态四条与起点基线**逐条同** · `--dump-objects` 逐字节同(2427 行)· **派生码不变量**:94 档直方图逐节点 diff **= 仅 F1 的 3 节点**(非自源 338,207 节点余者 0)· 全枚举 **56/56** · 五 CI job 全 rc=0 · 自举链 `corec2==corec3` cmp IDENTICAL(2821374B)+ N06=0 + 冒烟 42 · 72 档 `check` 同源对拍(rc 同、日志 diff 仅 2 档自源行号位移)· 影子对拍 `decisions=agree=32767`、全分类计数 0(非自源 67 档逐字节同)· **突变控制 4 条**逐条转红(复合行入项槽 / 两槽同置 / 派生忽略辅码 / D23 越界行)。 + +**执行偏离(诚实登记)**:① 首轮 `hits` 复原只包住 `sh_term_of_ti`(分类查询漏网)⇒ P4 计数纪律用例转红 ⇒ 已按根因修正;② 首轮 D23 对装载侧也计数 ⇒ 暖态误拒(用例抓红)⇒ 收口为 emit-only;③ 一次自造并发(基线收尾期跑套件)⇒ 两条假红,已识别并全部串行重跑。 + +## Task 3:unknown 清零——命名面判定化(身份链 + 可判定面加强) + +**Files:** Modify `src/compiler/ty_shadow.cr`(`sh_term_of_ti` 命名分支 :328/:330 + `sh_sig_term_of_ti` :951 **两处同步**;新增身份链构造)· `src/compiler/type_engine.cr`(AK_NAMED 可判定面规则 + 不变槽加强的**适用域**与守卫)· `src/compiler/type_selftest.cr`(`unf.nominal_*` 两例重钉 + 新例)· `src/compiler/globals.cr`(如需新归因计数)· New `tests/selfhost/test_named_face.py`(**行为断言形态**——它必须能在 shadow 下线后独立存活)。 + +**Interfaces:** +``` +// ── 桥接侧:命名面身份链(取代空链 + b=行号 当身份)── +// TYP_NAMED / TYP_GENERIC_PARAM : c = [sh_name_token(name_ni)] +// TYP_GENERIC_APPLY : c = [sh_name_token(base_ni), 实参项…] +// (b 槽 = 行号仍是**标注**,不参与判定——引擎只比 a/c,见 type_engine.cr:281 注) +fn sh_named_identity_term(ti: int) -> int // -1 = 不可构造(⇒ 清零不成立,上抛) +// ── 引擎侧:AK_NAMED 可判定面(**不得**用结构性展开)── +// 两条规则(适用域见下):① 同链(逐元素同一性)⇒ 1;② 链异且**两侧链元素皆为类型项/身份令牌** ⇒ 0 +// 边界:union/μ/未范化面**不适用**(保持三态 -1,登记)——加强须先满足「链元素皆类型项」不变量 +``` +- [x] **Step 1: 用例(红)**:命名面探针逐条断言 **`ty_equiv` ∈ {0,1}(非 -1)** 且**与 legacy 同值**:同/异名命名、两次实例化同实参的泛型应用(行不同 ⇒ 必须判等)、异实参泛型应用(判不等)、递归命名(`Node` vs `Node`;`*Node` vs `*Node`)、`[Named;N]`/`Named?`/元组含命名、别名一层、泛型形参位(`T` vs `T` / `T` vs `int`)、`AK_NAMED` vs 非命名(product/tuple/原生)。**同时用例必须落「负控」**:union/μ 未范化面**仍** -1(不得被加强误判为 0)。 +- [x] **Step 2: 清零清点(report-only,**删除前置**)**:全语料 + 探针语料跑 {`check`, `shadow`} → `g_replace_unknown` = **0** ∧ `g_replace_bridge` = **0**(逐档表;同时报站点覆盖)。**判据 = 0;非 0 ⇒ 停下上报**(逐条裁决:收紧/放宽/未覆盖面)。 +- [x] **Step 3: 实现**:① 桥接:两处命名构造点改为身份链(`sh_name_token` 复用,`ty_shadow.cr:244`);泛型应用实参项走 `sh_term_of_ti`(**不展开**,只用既有身份语义)。② 引擎:`lit_implies` 的 AK_NAMED 守卫(:192/:198)改为「链比较(同一性)→ 1 / 链异且链元素皆为类型项或身份令牌 → 0」;`ak_disjoint` 的 NAMED 行**不动**(仍不断言互斥)。③ **适用域守卫**:加强**只**用于「链元素集合 ⊆ {类型项, 身份令牌}」的原子(实现为一个显式判定函数 + 注释),其余面保持 -1(含 `tt_list_variance_at` 的不变槽面——**该面本任务不动**,除非探针证明残留 -1 来自该面;动则须逐面出「链元素皆类型项」证据)。 +- [x] **Step 4: 判据**:① 全语料 + 探针影子对拍 `old_stricter`/`old_looser`/`unknown*`/`replace_*` **全 0** + 站点覆盖同报;② 探针行为断言全绿;③ `selftest-types` ≥365 → ≥380(`unf.nominal_*` 两例重钉为「引擎自决 0/1 + 无回落」);④ **ELF canary IDENTICAL**;⑤ `.ccr` 冷态逐字节同(同源基线);⑥ 五 CI job + 全枚举 + 自举链 + `test_backend_bootstrap`;⑦ **突变控制 ≥2 条**(身份链去一个元素 ⇒ 探针红;规则放宽(链异也判 1)⇒ 探针红)。 +- [x] **Step 5: 提交**:`feat: R2 P5 Task 3——unknown 清零(命名面判定化:身份链 + 可判定面加强;两构造点同步;全语料回落计数归零)` + +**停条件(D-3-4 的实际边界)**:① 探针暴露**无法在不引入漏报的前提下判定**的形态 ⇒ **停下上报**(该形态 = 裁-2 备选 (b) 的真实边界;**不得**为了清零而放宽/近似);② 若清零要求动 `tt_list_variance_at` 的不变槽面 ⇒ 单独裁决(并出「链元素皆类型项」不变量证明 + 全语料反证);③ 若 `sh_sig_term_of_ti`(:951)与 `sh_term_of_ti` 的身份链无法保持逐位同构 ⇒ 停下(双构造点契约)。 + +### Task 3 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task3-report.md`;证据 `/tmp/p5t3/`) + +**形态**:桥接 `ty_shadow.cr` 新增 `sh_named_identity_term`(NAMED/PARAM = `tt_atom(AK_NAMED, 本行, [名字令牌])`)+ `sh_apply_identity_term`/`sh_apply_args_chain`(APPLY = `tt_atom(AK_NAMED, **基型行**, [基名令牌, 实参项…])`——基型行 = **规范形**:两处实例化同型 ⇒ **同节点**;与 `sh_sig_term_of_ti` **两点逐位同构**,停条件③**不触发**);`sh_term_of_ti` 三分支改写、`sh_sig_term_of_ti` 两分支同步。引擎 `type_engine.cr`:`lit_implies` 正原子分支 AK_NAMED 守卫 → `te_named_pair`(`te_is_name_token`/`te_chain_cmp`/`te_token_cmp`/`te_arg_cmp`/`te_is_arg_term` 五原语):**同类 ⇒ 链比较**(令牌位按节点同一性 / 实参位按引擎 `ty_equiv` 三态直传;空链 ⇒ -1);**混类 ⇒ 0**(命名侧带链 ∧ 异类侧非 NEVER/DYN),域外(空链/位置错位/实参位 μ 变元/NEVER·DYN)⇒ **-1 + uncovered**。**不接展开层**(`unf.nominal_structural_equal` 展开项结构相等 1 与桥接项 0 双断并存)。**零新全局计数**(清零证据 = 影子摘要 + 自测直断)。 + +**判据(全部本实例实跑)**:`selftest-types` **386/386**(367+19 新例;3 处重钉:`t3.named_face_decided_no_fallback`(原 `t3.unknown_fallback_legacy`)/`unf.nominal_equiv_atomic_decided`(原 `unf.nominal_equiv_atomic_unknown`)/`unf.nominal_type_equal_false`(零回落))· 新套件 `test_named_face.py` **18/18**(受 10 + 拒 8,**不依赖影子通道**,挂 `run.sh` selfhost-tests)· 语料 shadow **`decisions=agree=32917`、`old_stricter/old_looser/unknown*/replace_*` 全 0**(站点覆盖同报;非自源 65 档与 T0 基线**逐字节同**)· **探针可判定域 18 档 `unknown_engine=replace_unknown=0`**(RED = 1~2/档;rc/诊断逐条不变)· **ELF canary IDENTICAL**(28822B)· `.ccr` 两套口径:`ptr_arith` **逐字节同**、`generics_test` **+640B**(TYPE 段项表 52→68 行 = 身份链新增令牌/CONS 项;**发射面零泄漏**:同语料 ELF 逐字节同 + `--dump-objects` 同 + `.cir` DOT 同 ⇒ 判据⑤按「无语料命名行档逐字节同 + delta 归因」重定)· check 语料 34×0/38×1 逐档同 · **D26 同源对拍零差异**(rc + 日志)· 全枚举 **57/57** · 五 CI job 全 rc=0 · 自举链 `corec2==corec3` cmp IDENTICAL(`ba8d98b8…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · 构建确定性 ×2 逐字节同 · **突变控制 3 条**(M1 令牌塌缩:自测 8 FAIL + 套件 2 例转绿;M2 链异判 1:7 + 4 例;M3 域外守卫去掉:自测 1 FAIL)逐条转红 + 源/二进制精确回滚。 + +**收紧面(登记,§7)**:混类判定从 -1 变 0 ⇒ 约束站点(`gen_constr_satisfied`/`gen_inst_constr_satisfied`)「命名实参 × 原生约束」由**不判**变 **TG02**(`check` rc=1;`build` rc 不变——TG02 非硬名单)。**全语料 0 命中**(rc/日志与基线逐条同 + D26 零差异);唯一命中 = `test_generic_constr.py` 1 例登记面(已重钉 `struct_inst_named_arg_rejected`)。**提请复核**(回退面 = 混类分支回 -1)。 + +**停条件②(触发;按纪律未单方动)**:参数链**不变槽**元素非同形(`[NA;2]` vs `[NB;2]`、`*NA` vs `*NB`、元组含异名)仍 -1 + 回落(**可判定**——legacy 判 0,行为面早已正确拒绝)⇒ 无法判定残留在 §3 逐条证明 + 套件 `residual.*` 两例钉行为不变 + 建议修法(不变槽元素比较改三态 `te_elem_cmp`)**未落地**——**协调者裁定(2026-09-13)= 默认案 ①**:本任务只做命名面清零,该面**另立一步**(紧跟本任务派发),三件证据(适用域证明 / 全语料对拍 / 突变控制 + 负控)见报告 §6.1 交接条目。 + +**登记**:`¬` 面(桥接零 TT_NOT ⇒ 不可达)保持 -1;链**不区分 kind**(NAMED vs PARAM 同名 ⇒ 判等,legacy 按 kind 判否——可达性未证实);APPLY 项 b 槽取基型行 ⇒ DF 载体面该行由 face 0 降 face 1(**派生码不变**,语料 APPLY 行不入 tk ⇒ 零可观测变化);**预存发现另单建议**:非硬名单诊断(TA01/TG02)不阻 `build`(`main.cr:149-170` 硬名单闸;冻结基线同)。 + +### Task 3b:不变槽元素三态 `te_elem_cmp`(停条件② 残留面收口;2026-09-13 派发;报告 `.superpowers/sdd/p5-task3b-report.md`;证据 `/tmp/p5t3b/`) + +**形态**:`src/compiler/type_engine.cr` 新增 `te_elem_cmp` 三态——① 两侧皆**身份令牌**(`te_is_name_token`:AK_UNIT ∧ b ≥ 0)⇒ **节点同一性**(不同节点 = 确定不同 ⇒ **0**);② 两侧皆**类型项**(`te_is_arg_term`)⇒ 引擎 `ty_equiv` 三态直传;③ 其余(μ 变元/¬/裸值——**既非令牌也非类型项**)⇒ **-1 + uncovered**。`tt_list_variance_at` 不变槽分支由「非同形 ⇒ -1 + uncovered」改为「同形(`tt_type_elem_same`)⇒ 1;否则 `te_elem_cmp`:0 ⇒ 0、-1 ⇒ 上抛」。文件头注 ①(P0 登记的「不变槽非同形」未覆盖面)随之收口。 + +**判据(全部本实例实跑)**:残留探针 n13/n17/n18 由 `unknown_engine=1 replace_unknown=1` → **0/0**(rc/诊断逐条不变;冻结基线与改后二进制同诊断)· 语料 72 档 check rc **34×0/38×1 逐档同** + shadow **`decisions=agree=32922`**、全桶(`old_*/unknown*/replace_*`)**0**(非自源 65 档逐字节同;5 个自源档 `decisions` +1/档 = 自源码增长,与 T0→T2 的 +147 同因)· `selftest-types` 386 → **401/401**(−1 例迁出 + 13 例 t3b 段 + 3 例 witness 合取式拆项)· `test_named_face.py` **18/18**(含 `residual.*` 两例行为不变)· **ELF canary IDENTICAL** · `.ccr` 四件(`ptr_arith`/`generics_test` × `ccr`/`build --static`)与 T3 后状态**逐字节同**(本步**零新项行**——`te_elem_cmp` 不构造项)· 五 CI job + 全枚举 57/57 + 自举链(`corec2==corec3` + N06=0 + 冒烟 42)+ 构建确定性 ×2。 + +**三件证据(协调者裁定)**:① **适用域证明** = 逐构造点**排序表**(令牌位:AK_REF 槽 0 / AK_SUM 槽 0,1 / AK_NAMED 槽 0〔不经本函数,走 `te_named_pair`〕;类型项位:SEQUENCE/PTR/PRODUCT/FN 全槽 + AK_REF 槽 1 + NAMED 实参位)⇒ 同 (ak, slot) **不存在排序混排**;域外面(μ 变元/¬/⊤ₖ)**保持 -1**(牙齿 = 突变 M3 + 自测 `t3b.elem_out_of_domain_stays_minus1`)。**AK_UNIT 形态二义**(原生 unit 项 = `tt_atom(AK_UNIT, 行号, -1)` 与令牌同形)**登记**:令牌先判是**失效方向**要求(若先判类型项 ⇒ token × unit 走 `ty_equiv` 判**等** = 假等 = P3-T1 令牌碰撞类);可达性 = 无构造点在同一槽混排两排序(负控 §③)。② **全语料对拍** = 上(**零翻转**)。③ **突变控制 4 条**:M1 令牌面中和(4 FAIL)/ M2 类型项面中和(7 FAIL + **残留探针复红 1/1**)/ M3 域外守卫去掉(3 FAIL)/ **M4 分派次序倒置**(3 FAIL,**复现 P3-T1 令牌碰撞 `unf.enum_variant_identity_scoped` 红** = 负控有牙)——逐条转红 + 源 sha/二进制 sha **精确回滚**(回滚后 `build/corec` 逐字节复原)。 + +**翻转与登记(收紧面台账)**:① `param.unknown_p0` → `param.elem_class_diff_zero`(`[int]` vs `[str]` 元素位)由 **-1 变 0**——语义加强(异原生类元素确定不同);与 spec §2.2 的 `sequence<⊤>` 正向面**无关**(后者走 `⊤ₖ` 分支),legacy 同结论(替换门一致);② `t3.counterexample_named_variant` 的 `ty_equiv(w, B)` 由 **-1 变 0**:空析取支里的**异变体令牌**比较由 -1(旧)变 0 ⇒ 撤掉原先掩盖 `sub_cover(B, B∩¬R) = 0` 的 -1 ⇒ **揭开预存过判**(¬ 面同类别名规则把 `B ⊆ ¬R` 判 0,值集语义应 1)——影响面实测 = 仅 witness 项(生产 `ty_exhaustive` 走 `tt_norm`+`ty_inhabited`、`gen_constr_witness_str` 走 `tt_display`;全语料零翻转)⇒ **登记为后续步**(¬ 面「异身份原子 ⇒ 不交」加强),本步不修、不近似。 +**停条件② 状态**:**已关闭**;D24 的「回落计数归零 → 删 legacy」前置对本面成立(残留探针 0/0 = 硬证据)。 + +## Task 4:删 `type_equal_legacy` 与全部回落面(+ 桥接 legacy 副本 + 死分支/站点 6) + +**前置(硬性)**:Task 3 Step 2 的清零判据成立(全语料 + 探针回落计数 0)——**否则不得开工**。本任务与 Task 5 **必须连续提交**(本任务删掉对照物后影子判定对拍停摆)。 + +**Files:** Modify `src/compiler/checker.cr`(删 :504-597 全函数 + 7 处递归自调 + 回落两处 :590/:599 + 包装对照 :612;`type_equal_engine` 语义改为「引擎唯一权威」)· `src/compiler/globals.cr`(删 `g_replace_unknown`/`g_replace_bridge` :421 + 复位 :246)· `src/compiler/ty_shadow.cr`(删 `sh_native_ak_legacy` :101 / `sh_base_ak_legacy` :109 + 摘要两行 :628/:630)· `src/compiler/type_selftest.cr`(5 处回落钉重钉/删::1814/:1821/:2487/:2492/:2691;桥接副本 3 例 :1957/:1966/:1977 改走生产入口)· `src/compiler/ir_gen.cr`(:927-929 死分支)· `tests/selfhost/test_named_dedup.py`(若引用 legacy 语义的注释/断言——:7 注)· `src/ci/run.sh`(计数注释同步)。 + +- [x] **Step 1: 残留 -1 政策落纸(**开工前置**)**:清零后引擎仍可能回 -1 的**入口**只剩(a)预算耗尽、(b)未覆盖面(μ/空递归/dyn 位图等,Task 0 表 D 的清点结果)。政策二选一(**写进本任务 Step 1 报告,需维护者签核**):**P-A**「-1 ⇒ 编译期硬错(新码/新措辞 + 报告显示未覆盖面 + 反例)」——需先 report-only 全语料(预期 0 命中,由 Task 3 Step 2 的 0 计数支撑);**P-B**「-1 ⇒ 保守 false + 登记计数(保留一个最小计数器)」——**注意**:P-B 不是 legacy 长期化(无 legacy 参与),但它把「未知」静默降为 false ⇒ **与三态纪律冲突,不推荐**。**推荐 P-A**。 +- [x] **Step 2: 删除(逐件)**:`type_equal_legacy` 函数体 + 7 自调 + 2 回落 + `g_replace_*` + 包装层 `ok` 计算(:610-614 区域);`type_equal_engine` 改为「快路径 + 桥接 + 三态(1/0/P-A 硬错)」;**同提交**把影子 `sh_compare` 的调用点移除(`ty_shadow.cr` 的判定观察面停摆——注记「对照物已删,余部随 Task 5 下线」)。 +- [x] **Step 3: 桥接 legacy 副本删除 + 自测三例改走生产入口**(`iface_by_ty_code`/`sh_native_ak`——P2b 已单源化,副本仅对拍用)。 +- [x] **Step 4: 死分支与站点 6**(D25):删 `checker.cr:2454-2463` 的 `EXPR_BINARY+OP_ASSIGN` 分支(含 `sh_site_begin(6)`)+ `ir_gen.cr:927-929` 对应分支;站点表 :472-483 的 6 行改注「退役(不可达,P5 D25)」;**站点 id 不重编号**。**若实施中发现任一分支可达(构造面反证)⇒ 停下上报**。 +- [x] **Step 5: 判据**:`selftest-types` ≥380(5 处重钉后净变化逐例登记)· 五 CI job + 全枚举 55/55 + canary + `.ccr` 冷态 + 自举链 + `test_backend_bootstrap` · **冻结基线同源对拍(72 档 `check` 逐档 rc + 日志 diff)** · **探针全集复跑**(Task 3 的行为断言,此时代谢 legacy 已删)· 突变控制(重钉后的探针在「-1 政策被改回静默 false」时转红)。 +- [x] **Step 6: 提交**:`refactor: R2 P5 Task 4——删 type_equal_legacy 与全部回落面(引擎唯一权威 + 残留 -1 政策 P-A);桥接 legacy 副本与 EXPR_BINARY+OP_ASSIGN 死分支/站点 6 一并删除` + +### Task 4 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task4-report.md`;证据 `/tmp/p5t4/`) + +**Step 1(残留 -1 政策 = P-A,落纸待签核)**:清零后引擎的 -1 出口只剩(a)桥接缺口(行译不成项)、(b)未覆盖面/预算耗尽(Task 0 表 D)。**实施 P-A**(计划推荐案):新码 **`EC_ICE_TY_INDET`(ICE04)**「type judgment indeterminate」+ 反例(两侧项文本 `tt_display`,成因两措辞「bridge gap」/「(uncovered face|budget exhausted)」)+ **入 `main.cr` 硬名单**(build 亦拒绝落盘)。report-only 依据 = 全语料 72 档零命中(本任务 Step 0 复现:`decisions=agree=32988`、全桶 0);判定点无 AST 位置 ⇒ `--> 0:0`(定位由调用点自身诊断承担)。**⚠ 政策选择按计划 Step 1「需维护者签核」——回退 = 删 `main.cr` 硬名单一行(诊断保留)或整条回 `P-B`。** + +**清零判据复现(删除前置,本任务 Step 0 自跑;二进制 = T3b 的 `f0862d70…`)**:72 档 `check` **34×0/38×1**;shadow **`decisions=agree=32988`**、`old_stricter=old_looser=unknown=unknown_bridge=unknown_engine=unknown_engine_uncovered=unknown_engine_budget=replace_bridge=replace_unknown=0`;站点覆盖 `assign-node=24998 · fn-body-ret=5905 · if-branch=2051 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`(`assign-binary=0` = 站点 6 不可达的语料佐证)。 + +**删除清单(逐件 + 死亡证据)**:`type_equal_legacy`(前镜像 `checker.cr:497-573` = 注记 10 行 + 函数 67 行,含 **7 自调**)· 回落两处(:590 桥接 / :599 引擎 -1)· `g_replace_unknown`/`g_replace_bridge`(`globals.cr` 声明 + `checker.cr` 复位 + `ty_shadow.cr` 摘要两字段)· `sh_native_ak_legacy`/`sh_base_ak_legacy`(`ty_shadow.cr`)· **站点 6 分支**(`checker.cr:2459-2470` = 12 行,含 `sh_site_begin(6)`)+ `ir_gen.cr:932-996` 对应发射分支(65 行)· `type_equal` 包装层的对照计算(改纯转发)+ `sh_compare` 调用点移除(D24 停摆;函数体留 T5)。**死亡证据** = 清零判据(全语料 `replace_*=0`)+ 站点 6 三面不可达(`tok2op` 零 `OP_ASSIGN` / `T_EQ`→`EXPR_ASSIGN` / `+=` 族显式包裹 op∈{ADD,SUB,MUL,DIV})+ 语料 `assign-binary=0`;自测 3 例(桥接副本全表对拍)改钉**冻结期望表** `ts_t4_ak_expected`/`ts_t4_native_ak_expected`(不再需要生产文件里的第二份实现)。 + +**判据(本实例实跑)**:`build_selfhost_native.py` rc=0(`corec` **`eae13428…`**,构建日志 `error[`=0)· `test_backend_bootstrap` rc=0(CI bootstrap-tests job)· `check src/compiler` rc=0 · `selftest-types` **404/404**(401 + 新增 `ts_t4_run` 3 例;5 处计数钉重钉为「零 ICE04 诊断」形态)· 五 CI job 全 rc=0 · 全枚举 **57/57** · **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· `.ccr` 两口径四条与 T3b 后**逐字节同**(`ptr_arith` 90823B `fb4a3b59…`/90966B `592afa31…`;`generics_test` 135341B `ddec1ce6…`/135484B `cd2af565…`)· `--dump-objects` 逐字节同(静态/非静态两口径)· `.cir` DOT 逐字节同(165915B)· 语料 `check` 34×0/38×1 + 日志仅 2 档自源**行号均匀位移 −38**(纯源行位移,内容逐字同)· **冻结基线同源对拍(pre-P5 二进制 × 当前源 vs 当前二进制 × 当前源):72 档 rc + 日志逐字节零差异** · **探针全集复跑**(T3/T3b/T0 三套探针语料)pre-T4 vs post-T4 **rc + 日志全同**(残留 -1 面全已由 T3/T3b 收口 ⇒ 无 ICE04 命中)· 自举链 `corec2==corec3` `cmp` **IDENTICAL**(`36e8f3fd…`)+ N06=**0/0** + 冒烟 **42** + `--help` rc=1 · **突变控制 3 条**:M1(P-A 报告面去掉 ⇒ 3 例转红 `t3.bridge_gap_ice04_hard_error`/`t4.ice04_bridge_gap_hard_error`/`t4.ice04_engine_uncovered_hard_error`)· M2(两因措辞合并 ⇒ 1 例转红)· M3(强制触发 ⇒ `check` rc=1 打印 `error[ICE04]: type judgment indeterminate: string vs int (uncovered face)` + `build` rc=1 **无产物** = 硬名单接线端到端证明),三条均**精确回滚**(源 sha + 二进制 sha 复原 `eae13428…`)。 + +**中间态口径(D24,逐条)**:① 影子判定对拍**停摆**(`sh_compare` 无调用点 ⇒ 摘要 `decisions=0/agree=0`、差异桶恒 0;站点直方图仍随挂点计数——**不作为判据**;余部随 Task 5 下线);② 判定面回归网自本任务起 = **冻结基线同源对拍 + 行为探针 + 突变控制**;③ 本任务与 Task 5 **连续提交**、中间态不发布。 + +## Task 5:影子层下线 + 回归网切换(**必须紧随 Task 4 提交**) + +**Files:** Modify `src/compiler/ty_shadow.cr`(删 `sh_compare`/`sh_site_begin`/ring/`sh_report`/`sh_dump_write`/`sh_kind_name`/站点表与相关 globals 引用;**保留** `sh_term_of_ti`/`sh_map_*`(生产桥接)并剥离调试计数)· `src/compiler/globals.cr`(删 `g_shadow_*` 判定面全局;桥接缓存 globals 去计数)· `src/compiler/main.cr`(删 `--type-shadow` :236 / `--type-shadow-dump` :237 / `g_shadow_on` :244 / `sh_finish`/`sh_dump_write` 调用 :212-216;**删 `--verify-named-dedup` :182/:238**(TODO #24 同族清偿))· `src/compiler/checker.cr`(10 处 `sh_site_begin` 挂点删除——**判定调用一律保留**)· `src/compiler/ty_shadow.cr` 的 `sh_tk_term_of_code`(删 hits 存-复原 hack)· `src/compiler/type_selftest.cr`(站点/摘要相关例重钉或删)· `tests/selfhost/*.py`(引用 shadow 的用例与 runner)· `src/ci/run.sh`(注释/挂点同步)· `build_selfhost_native.py`(若清单有 `ty_shadow` 专用面——**corelsp 清单含影子层**,删面须三面同核)。 + +- [x] **Step 1: 站点挂点删除(判定点保留证明)**:逐站点删 `sh_site_begin(id)`;判据之一 = **删挂点前后的 `check` 面同源对拍逐档空**(证明「站点下线 ≠ 判定点下线」)。 +- [x] **Step 2: 影子通道面删除**(ring/摘要/转储/`--type-shadow*`/`--verify-named-dedup`/`g_shadow_on`/kind 空间/`sh_kind_name`);**桥接缓存保留**但**剥离调试计数**(`g_shadow_hits/entries` 及 `sh_map_reset` 中的计数复位;`sh_tk_term_of_code` 的存取-复原删除);`g_shadow_map` **改名**为 `g_term_map`(+ 引用全改)——若实施时引用面 > 30 处或触及 corearch 清单面 ⇒ 改名转为 P6 登记项(**择一落纸**)。 +- [x] **Step 3: 回归网切换(D26-①)**:同提交落 `回归网声明`(报告 + TODO)——「判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律;影子通道已下线,任何后续批次不得引用其计数/摘要/站点直方图」;把 P4 Global Constraints 的「每任务复跑影子对拍」行**在 TODO/计划继承注里改写**(避免后续批次引用已死的通道)。 +- [x] **Step 4: 判据**:五 CI job + 全枚举 55/55 + `selftest-types` ≥380 + canary IDENTICAL + `.ccr` 冷态逐字节同 + `--dump-objects` 逐字节同 + 自举链 + `test_backend_bootstrap`(**三面同核**,corelsp 面必核)+ **冻结基线同源对拍(72 档 `check`)+ 探针全集** + 突变控制(在派生面故意注入错值 ⇒ 探针/对拍转红;证明网仍在)。 +- [x] **Step 5: 提交**:`refactor: R2 P5 Task 5——影子层下线(判定面对拍网退役)+ 回归网切换(冻结基线同源对拍 + 行为探针 + 突变控制);桥接层剥调试计数` + +**停条件**:corelsp 清单/长驻进程面因删面出现行为变化 ⇒ 停下上报(`reset_frontend_state` 链路上的缓存重置钩子**不得**随计数一起被误删——`sh_map_reset`/`sh_unf_map_reset`/`tt_layer_reset` 的调用链保留)。 + +### Task 5 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task5-report.md`;证据 `/tmp/p5t5/`) + +**Gate(下线前置,本实例实测)**:`sh_compare` **零调用点**(T4 已移除)+ 起点二进制 × 72 档 shadow:**70/70 档 `decisions=0 agree=0`、全差异桶 0**;消费者仅 `sh_finish`(main.cr)← 两个默认关的 CLI 旗标;无测试驱动 `--type-shadow` ⇒ **无活依赖**。 + +**删除面**:12 函数(`sh_site_begin`/`sh_record`/`sh_compare`/`sh_report`/`sh_dump_write`/`sh_site_name`/`sh_kind_name`/`sh_count_*`×4/`sh_map_hits`)+ `sh_finish`(main.cr,含 2 调用点)+ **17 个 `g_shadow_*` 全局** + `SHADOW_RING_CAP` + 2 CLI 旗标 + checker **9 处挂点** + hits 存-复原 hack(3 处)+ `sh_map_reset` 的 hits 复位;站点头注区改「已删除」+ 判定点保留说明。**改名**(择一条件:引用 29 ≤ 30 且不触 corearch 清单):`g_shadow_map`/`_cap`/`_entries` → `g_term_map`/`_cap`/`_entries`;`SHADOW_MAP_INIT_CAP` → `TERM_MAP_INIT_CAP`。**保留**:桥接层全族(生产)、展开层全族(生产)、`--verify-named-dedup`(**偏离 D27,见报告 §7-1**:非影子面 + 有活消费者 `test_named_dedup.py`,删它违「只增不减」)、`--verify-evp-nodes`。 + +**回归网切换(D26-①)**:声明落 `src/ci/run.sh`(selfhost-tests 自述块)+ `ty_shadow.cr` 文件头 + `globals.cr` + TODO(#24 残部 + 本批条目)——**判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律(ICE04)**;`test_named_dedup.py` **新挂进 CI** selfhost-tests(判定面网组件,原仅在全枚举内)。 + +**判据(全部本实例实跑)**:`build_selfhost_native.py` rc=0(`build/corec` **`2342fd03…`**,6+ 次构建同 sha;`error[` 真实诊断 = 0)· `test_backend_bootstrap` rc=0(`error[`=0)· `check src/compiler` rc=0 · `selftest-types` **404/404**(两例**重钉**:hits 计数断言 → 查询纯度 / 缓存命中同项;不增减)· 五 CI job 全 rc=0 · 全枚举 **57/57** · **ELF canary `95084e7b…d475` IDENTICAL** · `.ccr` 四件(`ptr_arith` `fb4a3b59…`/`592afa31…` · `generics_test` `ddec1ce6…`/`cd2af565…`)与 T4 **逐字节同** · `--dump-objects`(两口径 3521 行)`cmp` IDENTICAL · `.cir` DOT(165915B)IDENTICAL · **冻结基线同源对拍 72 档 rc + 日志零差异**(`diff -rq` 空)· **探针全集 30 档 vs T4 记录零差异** · 自举链 `corec2==corec3` `cmp` IDENTICAL(`75da3610…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · **突变控制 2 条**:M1(`sh_dfn_code_of_slots` 去辅码 ⇒ 自测 400/404 + test_ccr_types 35/40 + `.ccr` sha 变)· M2(`type_equal_engine` 的 `e==0` 判真 ⇒ `test_named_face` 10/18 + 自测 397/404 + **语料同源对拍 12/12 档 DIFF(3 档 rc 1→0)**)——两条均**精确回滚**(源 sha + 二进制 sha 复原 `2342fd03…`)。 + +**停条件核**:`sh_map_reset`/`sh_unf_map_reset`/`tt_layer_reset` 调用链保留(corelsp 面 = `test_lsp.py` 在枚举 57/57 内 rc=0);未触发上报条件。 + +## Task 6:TODO #32——`EXPR_LET` 判定点(独立;report-only 先行) + +**Files:** Modify `src/compiler/checker.cr`(`EXPR_LET` 分支 :3079-3097 加兼容检查;**划界是否含全局声明面 :1614-1620**)· New `tests/selfhost/test_let_check.py` · `docs/error-codes.md`(若新增码)· `src/ci/run.sh`。 + +- [x] **Step 1: 红态复现(#32 记录复核,本任务自跑)**:`x: [int;3] = s;`(切片)/`x: [int;4] = [1,2,3];`(异长常量档)/`x: int? = 5; y: int = x;`(可选流进窄槽)逐条实测现状 rc(登记:check rc=0 等)——**旧二进制复现**(Task 0 冻结基线),确认是预存而非本批引入。 +- [x] **Step 2: report-only 全语料**:加检查(`type_compat_strict(源 = val_ti, 目标 = res_type_node(type_node))` + `diag_type_incompatible`,码/措辞沿用 TF01/TA01/TC02 体系**或新码——先裁决**)→ 全语料 report-only 出命中清单(**预期命中**:泛型函数体内 `T` 注解、`[T;N]` 表示提示位、可选注入面——逐条审查后入硬名单;`x := <值>` 与 `x : .` auto 形态**不受影响**)。 +- [x] **Step 3: 开门 + 用例 ≥8 例**(三红态转 rc=1 + 无产物 + 正控:合法注解/auto/泛型体内 `T`/`[T;N]` 不误报)。 +- [x] **Step 4: 判据**:新套件全绿 + **`check src/compiler` rc=0**(自源零命中)+ 五 CI job + 全枚举 + canary IDENTICAL + `.ccr` 冷态 + 自举链 + 冻结基线同源对拍(**本任务预期出现 diff——收紧面**,逐档裁决入台账)+ 突变控制。 +- [x] **Step 5: 提交**:`fix: R2 P5 Task 6——EXPR_LET 站点判定点(TODO #32:值/注解兼容检查 + 定位诊断;report-only 全语料零/清单裁决)` + +### Task 6 实施记录(2026-09-13;报告 `.superpowers/sdd/p5-task6-report.md`;证据 `/tmp/p5t6/`) + +**形态**:`checker.cr::check_let_annot_compat`(新判定函数)= 豁免面单点 + `type_compat_strict` + `diag_type_incompatible`,**两调用点共用**:① `EXPR_LET` 分支(局部);② `check_global_let`(**全局初始化器——显式划界覆盖**,注册趟 `:1614-1620` 同形缺口)。参序(源 = `val_ti`,目标 = 注解行)照 P3 T1 §3.1。**码 = TA02**(`EC_TA_DECL`;error-codes.md 既有槽位,修复前定义零 raise)→ 复用 + 入 `main.cr` 硬名单(`build` 亦拒绝)。豁免四条:无注解/auto/无初值 · 注解 `dyn` · 值 `TI_NEVER`(底部 + **错误标记 = 级联抑制**)· 注解泛型形参(照返回位点既有策略;套件同策略钉住)。 + +**裁决两处**(计划 Step 2/3 的「先裁决」项):① **码** = TA02 复用(非新码;TA02 是 error-codes.md 为此面预留的槽位,且 `EC_TA_DECL` 早已定义 ⇒ 零清单扩张);② **硬 vs 软** = **硬**(入硬名单)——依据 = 计划 Step 3 的「三红态转 rc=1 + **无产物**」+ 硬名单既有判据(「判定继续 = 产出静默错产物」:符号取注解行、后端按注解行发射,与 TS01-04/TK02 同族);report-only 零命中(自源 + 72 档)⇒ 开门零风险。 + +**report-only 命中清单(全语料,实跑)**:72 档 runner `check` **rc 34×0/38×1 与 pre-P5 基线逐档同 + 诊断正文归一化逐条同 + 零 TA02**;`check src/compiler` rc=0;**58 套件全枚举**(tests/selfhost **内联源** + tests/bootstrap)⇒ **命中恰 1 例**:`tests/selfhost/test_named_face.py::named.in_container_same` 的 `p : *NA = 0;`(int 字面量 0 入指针注解)。**裁决 = 修语料**(非放松检查):`*T ← int` 与 `*T ← null` 在**赋值位点**早已 TA01 拒绝(探针 t1,冻结基线与本任务二进制同值)⇒ 声明位点拒绝与既有语义一致;改为 `p : *NA, mut;`(无初值声明,指针身份面语义不变)⇒ 该套件 18/18、全枚举 58/58。**计划预期的三类命中(泛型体 `T` 注解 / `[T;N]` / 可选注入面)实测零命中**——均为豁免面或正控(探针 q3/q4/q5/p10/p12)。 + +**判据(全部本实例实跑,二进制 `f737e26b…`)**:新套件 `test_let_check.py` **24/24**(拒绝 11:四形态 + 结构双向 + 全局 + 批量 + 丢弃名 + 级联抑制 2;正控 13:精确/无注解/auto/泛型 `T` 与 `[T;N]` 与 `[T]`/可选注入/可选 None/dyn/无初值/切片注解/定长精确/命名与 string;挂 `src/ci/run.sh`)· `selftest-types` **404/404**(不增减)· `check src/compiler` rc=0 · `test_backend_bootstrap` rc=0 + `error[`=0 · **五 CI job 全 rc=0**(selfhost-tests 首跑暴露上述 1 例命中 ⇒ 修语料后复绿)· **全枚举 58/58**(57 + 本套件)· **ELF canary `95084e7b…d475` IDENTICAL**(28822B)· **`.ccr` 两口径四条与 T5 记录逐字节同**(`ptr_arith` 90823B `fb4a3b59…` / 90966B `592afa31…`;`generics_test` 135341B `ddec1ce6…` / 135484B `cd2af565…`)· **零发射面泄漏**(收紧面 = 纯诊断面)· 自举链 `corec2==corec3` cmp IDENTICAL(2854910B `5559beef…`)+ N06=0/0 + 冒烟 42 + `--help` rc=1 · 构建确定性 ×2(`f737e26b…` 两连建同)· **冻结基线同源对拍 72 档 rc + 诊断正文零差异**(`text_diffs=0 rc_diffs=0`)· **突变控制 4 条**(M1 局部调用点移除 → 15/24;M2 豁免④移除 → 23/24(`let_generic_param_annotation_not_judged` 转红);M3 硬名单条目移除 → 20/24「静默面复活」;M4 全局面调用点移除 → 23/24 单点归因)逐条转红 + **精确回滚**(源 sha `fff7a8ba…`/`739f7f5f…` + 三二进制 sha 复原 `f737e26b…`/`544e9201…`/`4251108b…`)。 + +**登记(未覆盖面/不收口)**:① `-> never` 函数的**调用**被推断为 unit(非 never)⇒ `x : int = boom()` 新增 TA02 且 `return boom()` 今天即 TF01——**同一既有 `never` 类型化缺口**在本站点多暴一例,非新类(全语料零 `-> never`);② 计划预期的三类命中面实测不存在(豁免面覆盖)——不改判;③ **#20(F3 调用位点无诊断)不并入**:本任务只落**声明位点**(计划 Task 6 的文件面仅 checker/测试/error-codes/run.sh),调用位点(实参/返回)另单。 + +**停条件**:report-only 命中面涉及**语义裁决**(如泛型体内 `T` 注解是否应拒)⇒ 停下上报;若命中面过大(需改自举源码)⇒ 转独立批(本批只落「清单 + 裁决点」)。 + +## Task 7:收官(回归 + 判据 + 统一台账 + 文档 + P6 交接) + +- [x] **Step 1: 全量回归**(cwd = 仓库根、`nice -n 19`、串行、比较前 `clean-cache`):五 CI job 全 rc=0;全枚举 55/55(+新增套件);`check src/compiler` rc=0;`selftest-types` 终值(≥380);构建确定性(两次构建 sha 同)。 +- [x] **Step 2: 判据复验**:`clean-cache` → ELF canary IDENTICAL(28822B);`corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42 + `corec3 --help` rc=1;`.ccr` 两套口径(冷态、带标)+ 冷/热豁免表复测(可选程序冷≡热);**冻结基线同源对拍 72 档**(逐档 rc + 日志 diff,diff 逐条分类)+ 探针全集 + 突变控制快照。 +- [x] **Step 3: 统一台账**:逐任务列 {删除件 / 替代证据 / 收紧 / 放宽 / 修复 / 未覆盖面 / 实施偏差}——**删除件必须与 Task 4/5 的实际 diff 逐条对应**(不得用「计划写的」代替「实际删的」);全语料命中数与基线逐条比。 +- [x] **Step 4: 文档**:统一 design spec **§9 P5 行标 ✅**(含未覆盖面与继承项)+ §3.4/§6.2/§6.3 状态同步(单槽化落地形态、β 与 `atom_of` 承接面指向 P6);format spec(`.ccr` 版本/记录不变 ⇒ 只加「D20 稳定引用形态规范」注释 + P5 段说明);findings **§15 已由 T5 落盘**(影子层最后一次基线)+ 本任务加 **§16 终态复核(文件自冻结)**;TODO:#44 家族接 P5 收官条目(**#49**)、划销 `type_equal_legacy`/unknown/站点 6 三项、登记 P6 条目(附录 E 逐条);CLAUDE.md 本批无文件/结构变化(不在提交面)。 +- [x] **Step 5: 提交**(路径限定;逐任务提交已在各 Task 内)。 +- [x] **Step 6: 附录 E 落盘**(P6 交接包;见下)。 + +**判据**:五 CI job + 全枚举 + canary + 自举链 + 同源对拍 + 探针 + 台账逐条可溯源 + 文档指针链闭合(spec §9 P5 行 → 本计划 Task 7 → 附录 E)。 + +### Task 7 实施记录(2026-09-14;报告 `.superpowers/sdd/p5-task7-report.md`;证据 `/tmp/p5t7/`) + +**全量回归(本实例实跑,无并发构建窗口;`nice -n 19` 串行)**:构建 ×2 **逐字节同**(`corec f737e26bf42f…` · `corearch 544e9201af10…` · `corelsp 4251108b04cf…`;构建日志逐字节同 + 三面 `error[`=0/`GUARD clean`×3)⇒ 提交树 == 被测二进制度(且与中断实例的两次构建交叉同值 ⇒ 确定性 ×4)· **五 CI job 全 rc=0**(check/bootstrap-tests/suite/selfhost-tests/full-bootstrap)· `check src/compiler` rc=0 · `selftest-types` **404/404** · **全枚举 58/58 rc=0**(51 selfhost + 7 bootstrap;逐档日志 `/tmp/p5t7/t7_enum/`)· 自举链 `corec2==corec3` = `5559beef2abf…`(2854910B,与 T6 记录同值)+ N06=0 + 冒烟 **42**(corec 与 corec3 双通道)。 + +**判据复验(本实例实跑)**:ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(**28822B**)IDENTICAL · `.ccr` 两套口径冷态四条 = `ptr_arith` **90823B `fb4a3b59…`** / **90966B `592afa31…`**、`generics_test` **135341B `ddec1ce6…`** / **135484B `cd2af565…`**(与 T5/T6 记录逐字节同;差恒 143B)· `--dump-objects` 两口径 **3521 行** `cmp` IDENTICAL(vs T5 记录)· `.cir` DOT `165915B b1bdf480…` IDENTICAL · **冻结基线同源对拍 72 档**(pre-P5 `5d2b15ad…` × 当前源 vs `f737e26b…` × 当前源):`diff -rq` **零输出**、rc 分布两侧同为 **34×0/38×1** · **行为探针全集 30 档** `diff -rq` **零差异**(vs T5 记录)· **冷/热豁免表复测(三类缓存态,本实例实测)**:① **缓存启用档**(无泛型、非可选)——`ptr_arith`(16 条目)/`chan_test`(46 条目)条目戳三跑全等 = **真命中**:ELF 冷 ≡ 暖(canary 值 / `422421f3…`),`.ccr` **冷 ≠ 暖**(`ptr_arith` 90966 → 90769B;`chan_test` 231044 → 230936B)但**各自确定性**(暖1 ≡ 暖2)⇒ D-1 勘误的「非可选冷≠热为预存」**复现**(冷/暖段级差异由 ㉙ 契约钉住:**派生码面逐行同 + 项内容等价**,`test_ccr_types` 40/40 含此例);② **含泛型函数档**(`generics_test`)`entries=0` = **缓存整体关闭**(`main.cr` 既有策略:任一泛型函数 ⇒ `cache_enabled=0`)⇒ 冷 ≡ 暖(ELF `f1f78683…` + `.ccr` 135484B 同);③ **可选零足迹档**(`Some`/`match` 语料)`entries=0`(表示面关缓存)⇒ 冷 ≡ 暖(ELF + `.ccr` 同;运行 rc=1 = 载荷值 1,正确)。 + +**突变控制(本实例实跑;仓库外实拷贝 `/tmp/p5t7/mut`,仓库源码零改动)**:**M1 派生面**(`sh_dfn_code_of_slots` 忽略辅码)⇒ `selftest-types` **400/404** + `test_ccr_types` **35/40** + `ptr_arith.ccr` sha ≠ 记录值 `fb4a3b59…`(`2f279f5c…`);**M2 判定面**(`type_equal_engine` 的 `e==0` 判真)⇒ `test_named_face` **10/18** + `selftest-types` **397/404** + **语料对拍子集 12/12 档 DIFF**(TA01/TF01 消失面、3 档 rc 1→0 = 拒绝静默消失,正是影子对拍过去要抓的类);回滚 = 源 sha 复原;仓库 `jj status` 干净(本轮全程未触碰)。**网的两条腿(派生面 + 判定面)都活着。** **harness 教训(本轮踩到并登记)**:`test_named_face.py` 用 `Path(__file__).resolve()` 定位仓库根 ⇒ 突变 harness 里 `tests/` 用**软链**时套件会解析回仓库二进制(假绿 18/18);**改为实拷贝 `tests/`** 后得真值 10/18。后续批的突变 harness 必须实拷贝(`test_ccr_types.py` 用 `abspath` 无此坑)。 + +**衔接说明(前实例残留)**:中断实例在 `/tmp/p5t7/` 留有草稿脚本与部分输出(build ×2 + 五 CI job 全绿,止于枚举前)——本实例**全部判据重跑**(构建/CI/枚举/判据/对拍/探针/突变),旧输出存档 `/tmp/p5t7/prev_run/` 仅作旁证(其 build1/build2 sha 与本实例逐字节同)。**冻结基线 `/tmp/p5t0/base/` 当前仍在位** ⇒ 对拍腿本轮成立;跨会话复现配方 = 附录 E-13。 + +--- + +## 统一台账(T0–T6 汇总;R2 P5 Task 7 收口) + +> 口径:每件给**死亡证据**(为何敢动)与**替代证据**(现在谁覆盖);删除件与 T4/T5 的实际 diff 逐条对应(前镜像清单 = `p5-task4-report.md` §3 / `p5-task5-report.md` §2)。全语料命中数一律与各任务起点基线逐条比。 + +### 1. 删除 / 退役(7 件 = 6 删除 + 1 改名族) + +| # | 件 | 任务 | 前镜像规模 | 死亡证据 | 替代证据 | +|---|---|---|---|---|---| +| 1 | `type_equal_legacy`(含 **7 处递归自调**)+ 两处回落(桥接缺口 / 引擎 -1) | T4 | `checker.cr` 注记 10 行 + 函数 67 行 | 清零判据成立:全语料 `replace_unknown=replace_bridge=0`(T3/T3b;72 档 + 探针);引擎与 legacy 逐点同值 | ① 冻结基线同源对拍 ② 行为探针(`test_named_face` 18 例 + 30 档探针语料)③ 突变控制 M2 ④ **ICE04 硬错**(残留 -1 不再静默) | +| 2 | `g_replace_unknown` / `g_replace_bridge`(计数 + 复位 + 影子摘要两字段) | T4 | `globals.cr` 声明 + `checker.cr` 复位 + `ty_shadow.cr` 摘要 | 计数无对象(回落分支恒不进入) | ICE04 诊断(成因位读于第二次 `ty_budget_reset` 前) | +| 3 | `sh_native_ak_legacy` / `sh_base_ak_legacy`(桥接 legacy 副本) | T4 | `ty_shadow.cr` 两函数 | 零生产调用(仅自测 3 例引用) | 自测改钉**冻结期望表** `ts_t4_ak_expected`/`ts_t4_native_ak_expected`(对生产入口 `iface_by_ty_code`/`sh_native_ak`) | +| 4 | 站点 6 分支(`EXPR_BINARY+OP_ASSIGN`)+ `ir_gen` 对应发射分支 | T4 | `checker.cr` 12 行(含挂点)+ `ir_gen.cr` 65 行 | **三面不可达**:`tok2op` 零 `OP_ASSIGN` / `T_EQ`→`EXPR_ASSIGN` / `+=` 族显式包裹且 op∈{ADD,SUB,MUL,DIV};语料 `assign-binary=0`(32988 次判定) | 真实赋值站点 8(`EXPR_ASSIGN`,语料 24998 次命中) | +| 5 | 影子层:12 函数(`sh_compare`/`sh_site_begin`/`sh_record`/`sh_report`/`sh_dump_write`/`sh_site_name`/`sh_kind_name`/`sh_count_*`×4/`sh_map_hits`)+ `sh_finish` + **17 个 `g_shadow_*`** + `SHADOW_RING_CAP` + **2 个 CLI 旗标** + **checker 9 处挂点** | T5 | `ty_shadow.cr` 函数 76→64;`main.cr` −2 调用点 | **Gate**:`sh_compare` 零调用点(T4 已移除对照物)⇒ 全语料 `decisions=0 agree=0`(**结构性为空**,非「恰好 0 差异」);无测试驱动 `--type-shadow` | ① 冻结基线同源对拍 ② 行为探针 ③ 突变控制 ④ 三态纪律(D26-① 声明落 `src/ci/run.sh`/`ty_shadow.cr` 文件头/TODO #48) | +| 6 | 桥接调试计数 `g_shadow_hits`(含 `sh_tk_split_impl`/`sh_tk_term_of_code`/`sh_map_reset` 的存-复原 hack) | T5 | 3 处 hack + 1 计数 | 计数消费者已删;装填守卫输入 = 真实占用(`g_term_map_entries` 保留) | 查询纯度 / 缓存命中同项的**行为断言**(`t4.tk_query_purity_and_cache_hit` + `bridge.cache_hit_same_term_no_new_entry` 重钉) | +| 7 | 改名族:`g_shadow_map`/`_cap`/`_entries` → `g_term_map`/`_cap`/`_entries`;`SHADOW_MAP_INIT_CAP` → `TERM_MAP_INIT_CAP` | T5 | 引用 29 ≤ 30(计划择一条件内)且不触 corearch 清单 | —(桥接缓存 = **生产面,保留**) | 语义等价改名,零行为变化(全量判据同批) | + +**显式保留(不删,理由)**:桥接层全族(`sh_term_of_ti` 等 = 判定路径无条件经它译项,生产面)· 展开层全族(满足判定/域查询,P3 边界)· `--verify-named-dedup` / `named_dedup_verify`(非影子面 + 有活消费者 `test_named_dedup.py`,**偏离计划 D27 一项**,回退面 = 一行)· `--verify-evp-nodes`(同理)· `sh_unf_*` 展开层计数(自测读数)。 + +### 2. 收紧(5 类;report-only 逐批先行) + +| # | 形态(旧 → 新) | 任务 | 全语料命中 | 处置 / 牙齿 | +|---|---|---|---|---| +| 1 | 类型面行不可译(桥接缺口/行越界)⇒ 静默落盘 → **`ccr_seg_prepare_save` 拒绝**(rc=1 + 诊断) | T2(D23) | 0(94 档 `face_fail=0`) | **仅 emit 路径**计数(装载侧不计数 ⇒ 缓存命中绝不误拒);牙齿 = 突变 M4(越界行 ⇒ rc=1 + 无产物) | +| 2 | 命名 × 原生混类判定 `-1`(不判)→ **0**(确定不同)⇒ 约束站点 TG02 | T3 | 0(72 档 rc/日志逐条同);1 例套件内联面重钉(`struct_inst_named_arg_rejected`) | 语义 = 正确(legacy 同结论);`build` rc 不变(TG02 非硬名单);回退面 = 混类分支回 -1(n10/n20/n21 复红) | +| 3 | 不变槽元素非同形 `-1` → 三态 `te_elem_cmp`(令牌按节点同一性 / 类型项按 `ty_equiv` / 域外 -1) | T3b | 0(72 档 check rc 逐档同 + shadow 零翻转) | 残留探针 n13/n17/n18 `1/1 → 0/0`;牙齿 = 突变 M2(残留复红 1/1) | +| 4 | 引擎 -1(桥接缺口 / 未覆盖面 / 预算耗尽)⇒ 回落 legacy → **ICE04 硬错**(入 `main.cr` 硬名单 ⇒ `build` 拒绝落盘) | T4(P-A) | 0(72 档 + 探针全集) | **⚠ 待维护者签核**;回退面 = 删硬名单一行(诊断保留)或整条回 P-B | +| 5 | `x : T = <异型值>`(局部/全局)静默 rc=0 → **TA02 硬错**(build rc=1 + 无产物) | T6(#32) | 0(自源 + 72 档 + 58 套件);1 例套件内联面修正(`p : *NA = 0` → 无初值声明) | 豁免四条(无注解/auto · dyn · 值 NEVER · 注解泛型形参);牙齿 = 突变 M1–M4 单点归因 | + +### 3. 放宽(**0**) + +本批无任何「旧拒绝 → 新接受」类变更。两处面变化不属放宽:T1 的 `.cir` 载入失败 ⇒ **miss**(三态纪律:载入失败本就不该当空表)· T5 的「站点下线 ≠ 判定点下线」(9 个判定点全保留,删的只是挂点)。 + +### 4. 修复(崩溃 / 静默错值 4 件) + +| # | 形态(前 → 后) | 任务 | 根因 + 判据 | +|---|---|---|---| +| 1 | `.cir` 暖态 **SIGSEGV rc=139** → rc=0 且暖 ≡ 冷 | T1 | 装载侧逐函数 `read_file`(`alloc(fsize+1)`,bump 不回收)⇒ 946 条目累计 **1.1674GiB > 1GiB 堆** ⇒ `alloc` 返回 0 ⇒ `str_len(0)` 解引用;修 = 单一复用读缓冲(与写侧同构)+ 载入失败 ⇒ miss;判据 = `test_cir_warm_path` 19 例(RED 对照 15 PASS/1 FAIL)+ 自源暖态转绿 | +| 2 | `IR_HOTPATCH_ROUTE` 的 tk = **r9 残留**(UB 烘进分类表)→ 显式第 6 实参 0 | T2(F1) | bootstrap 只为实际实参写寄存器;含 hotpatch 语料 3 节点码 **4→0**(已裁决偏差,零消费者) | 自测 `p5t2.hotpatch_route_no_face` + ㉞ | +| 3 | 声明位点**静默错产物**(符号取注解行、后端按注解行发射)→ TA02 | T6 | `EXPR_LET` 与 `check_global_let` 无兼容检查(bootstrap 参考实现本有该检查 ⇒ 回归);`EC_TA_DECL` 此前定义零 raise | 套件 24/24 + 突变 4 条 | +| 4 | `EXPR_BINARY+OP_ASSIGN` 死分支(12 + 65 行)→ 删 | T4(D25) | 三面不可达(parser 三证)+ 语料 0 命中 | 站点 8 活判定点保留;`check` 同源对拍零差异 | + +### 5. 未覆盖面 / 登记(转 P6;逐条 = 附录 E) + +β 盘面项索引 + `atom_of` 读回 · 暖态项表可小于冷态 · 对象布局表示位 · `MAX_*` 解除 · 可选程序收回 `.cir` 缓存 · `.csr`/TagNode · 命名消费语法/iterable/形状可写视图 · ¬ 面过判 · `-> never` 调用面 · #20/F3 调用位点 · 冻结基线复现配方 · ICE04 签核 · 非硬名单诊断不阻 `build` · `string == int` 混比 · 单条目 >1GiB 分片 · 设计先例引用面。 + +### 6. 实施偏差(计划 ↔ 实测;逐条) + +| # | 计划文本 | 实测 | 处置 | +|---|---|---|---| +| 1 | P4 六排除项(ALLOC/CALL/LOAD/I2F/F2I)「P4 排除」 | P4 排除理由 = **发射面**判据(后端不按 `ti` 分派),不适用于**语义**类型面 | T2 再裁入类型行面(`IR_LOAD_ENUM_TAG` 保持无面) | +| 2 | F2 三候选(扩 `atom_of` / 复合行走 aux / 分类表限原子行)未裁 | 实测复合行不可逆(`ptr_ref_first` BINARY tk=10) | 裁 (iii)+(ii):分类表只覆盖原子行 + 复合行原码进辅码槽保真 | +| 3 | 冷/暖判据「索引全等」 | 暖态类型/项表可**小于**冷态(缓存命中跳过 `ir_gen` 的 `alloc_type`) | 判据重定 = 派生码面逐行同 + 项内容等价(`--dump-tk-terms` 增 `rows=`/`aux_nonzero=`/`face_fail=`) | +| 4 | T3 接口未明写 APPLY 项 b 槽取值 | 取**基型行**(规范形)⇒ 两实例化同型同节点;双构造点逐位同构(停条件③不触发) | 落纸 + 自测 `t3n.apply_two_rows_same_args` | +| 5 | `.ccr` 判据「冷态逐字节同」 | 含命名行语料必然 +项行(`generics_test` +640B = TYPE 项表 52→68 行) | 判据重定 = 无语料命名行档逐字节同 + delta 归因(发射面零泄漏三证) | +| 6 | T5 计划 D27「`--verify-named-dedup` 一并删」 | 非影子面 + 有活消费者(`test_named_dedup.py`;删它 = 杀死既有回归网 + 违只增不减) | **未删**(偏离登记,回退面一行);`--verify-evp-nodes` 同理 | +| 7 | T6 预期命中面(泛型 `T` 注解 / `[T;N]` / 可选注入) | 实测三类均 rc=0(豁免面/正控)⇒ 零命中;真命中 1 例在 tests/selfhost **内联源**(72 档 runner 不含内联源 ⇒ 首跑 `selfhost-tests` 才暴露) | 修语料(三条独立证据);「58 套件全枚举」并入 report-only 语料面 | +| 8 | 冻结基线「不入库」的复现腿 | `/tmp/p5t0/base/` 本轮仍在位(对拍腿成立);跨会话不可复现 | 转 P6:重建配方 + sha 白名单(维护者已裁定「重建,不落二进制」) | + +--- + +## 删件 → 替代证据(安全网替代表,D26;实施时逐条勾) + +| 删件 | 过去谁覆盖 | 现在谁覆盖 | 验证时点 | +|---|---|---|---| +| `type_equal_legacy`(含 7 自调 + 2 回落) | 影子对拍(旧/新逐点对账)+ 引擎 -1 兜底 | ①清零判据(全语料 + 探针回落计数 0);②冻结基线同源对拍;③探针行为断言;④三态纪律(残留 -1 = P-A 硬错) | Task 3 Step 2(删前)→ Task 4 Step 5(删后) | +| `g_replace_*` 计数 | 回落面的「不静默」证据 | 清零后无回落 ⇒ 计数无对象;-1 面由 P-A 硬错 + 报告承担 | Task 4 | +| `sh_native_ak_legacy`/`sh_base_ak_legacy` | 全表对拍(仅自测 3 例) | 生产入口 vs 表(`iface_by_ty_code`/`sh_native_ak`)断言 | Task 4 Step 3 | +| 站点 6(`EXPR_BINARY+OP_ASSIGN` 分支) | 影子站点 6 挂点 | 不可达构造面证明(parser 三面)+ 真实赋值站点 8(`EXPR_ASSIGN`) | Task 0 表 B(删前)→ Task 4 Step 4 | +| 影子层(ring/摘要/转储/flags/站点直方图/`--verify-named-dedup`) | 判定面回归网 | 冻结基线同源对拍 + 探针全集中跑 + 突变控制 + 全枚举 + canary | Task 3(探针在影子存活期先跑 0 差异)→ Task 5 Step 4 | +| 桥接调试计数(`g_shadow_hits/entries`,`ty_shadow.cr:48`) | `sh_tk_term_of_code` 的存-复原补偿 | 计数删除 ⇒ 补偿 hack 一并删除;桥接缓存容量/装填守卫改由真实占用驱动 | Task 5 Step 2 | + +> **T7 收口注**:本表六行**全部兑现**(逐条实际 diff 与替代证据 = 「统一台账」§1 七行;「验证时点」列的删前证据 = 各任务 report-only 清零判据:T3/T3b 全语料 + 探针 `replace_*=0`)。 + +--- + +## 翻转钉清单(逐条:现钉 → 本批处置) + +| 钉 | 位置 | 触发任务 | 处置 | +|---|---|---|---| +| `unf.nominal_equiv_atomic_unknown`(断言 `ty_equiv(命名,命名) == -1`) | `type_selftest.cr:2487` | Task 3 | 重钉为「引擎自决 0/1(同值 legacy)」+ 无回落 | +| `unf.nominal_type_equal_false`(断言回落 +1) | `type_selftest.cr:2492` | Task 3 | 重钉为「无回落(计数不存在)」 | +| `t3.unknown_fallback_legacy` / `t3.bridge_fallback_legacy` | `type_selftest.cr:1814/:1821` | Task 4 | 删除(回落面消亡)或改断言 P-A 硬错 | +| `t1.n_face_gate_nested`(断言**不**回落) | `type_selftest.cr:2691` | Task 4 | 重钉为「零回落计数」(语义不变) | +| `param.unknown_p0`(断言不变槽 `[int]` vs `[str]` ⇒ -1) | `type_selftest.cr:1681` | Task 3b | 重钉 `param.elem_class_diff_zero`(**0** = 确定不同;uncovered 面触发改域外元素——见 plan Task 3b 台账) | +| `t3.counterexample_named_variant` 的 `ty_equiv(w, b) == -1` 合取项 | `type_selftest.cr:3106` | Task 3b | 拆项重钉:`ty_sub(w,b)=1`/可空/覆盖位命名保留;反向与等价改钉 **0**(`*_over_claim` 命名,预存 ¬ 面过判被揭开——登记后续步) | +| `t3n.invariant_slot_residual_negative`(断言残留 -1) | `type_selftest.cr:1081` | Task 3b | 删除(断言迁 `t3b.inv_slot_seq_named_diff_zero`:**0** + 零未覆盖面) | +| 桥接副本三例 | `type_selftest.cr:1957/:1966/:1977` | Task 4 | 改走生产入口(`iface_by_ty_code`/`sh_native_ak`) | +| 站点表 6 行 + `sh_site_begin(6)` | `ty_shadow.cr:479` / `checker.cr:2460` | Task 4/5 | 退役注记 + 挂点删除 | +| 影子 flags/摘要行 | `main.cr:236-237/:244` / `ty_shadow.cr:628-630` | Task 5 | 删除;回归网声明落 TODO | +| `selftest-types` 计数注释 | `src/ci/run.sh:71` | 逐任务 | 355 → 各任务实测值(只增不减) | +| `test_ccr_types.py` 计数注释 | `src/ci/run.sh` 挂点行 | Task 2 | 34 → 实测值 | +| `test_cache_identity.py:VER_EXPECTED` | `tests/selfhost/test_cache_identity.py` | Task 1/2 | **不变**(17;布局零变化——若 Task 1 归因须 bump 则此为允许面并单独记录) | +| 站点/计数相关 LSP 用例 | `tests/selfhost/test_named_dedup.py` 等 | Task 5 | 逐例核:判定面断言保留,影子面断言删除 | + +--- + +## 自检记录 + +- **根因 vs 止血**:① Task 1 是**根因修复**(暖态 SIGSEGV 在 `.cir` 恢复面,见 `cir_cache.cr` 装载链),**未**采用「给长度加钳位」式吞异常;② Task 3 用「命名面**判定化**」而非「结构性展开」——后者 P3a 已实测反证(同形不同名 struct 判等 + 推翻 P2a 对拍基线,`ty_shadow.cr:678-689`),前者按 R5 的口径「term 为准、atom 派生」直达单源;③ Task 2 的单槽化 = **类型面单源 + 辅码显式分离**(D19),**不**走「把辅码也硬塞进项」的近似(那会复现 P4 T4 §2 的数值陷阱:旗标 1 = `TI_DEX` 行、宽度 8 = `TI_DEX_S` 行);④ 删除批**先证后删**(D24:清零判据 → 删 legacy → 影子下线),并给逐件替代证据(删件→替代表)——不是「删了再补网」。 +- **与既有裁决的一致性复核**:spec **R5**(双槽期漂移 ⇒ term 为准 + atom 派生)= D19/D21 的实现口径;spec **§4 步 4**(删旧分支/旧映射/双槽)= 本批 Task 2/4/5;P3a **展开层边界**(只服务满足判定/域查询)= Task 3 **不得**越界(D23 适用域守卫);T6 判定(`MAX_*` 解除 = 记录布局迁侧表,与对象布局同源)= 裁-4(P6 合批);裁决 2(`g_impl_for` 入段不入判定)与裁决 7(`AK_NULL` 独立槽)本批零触碰;**三态纪律**在 Task 3 的「不得断言不等价」与 Task 4 的 P-A 政策两侧同时受约束。 +- **占位符扫描**:无 TBD/TODO-to-fill;三处**实施前须定的登记项**明写并给推荐与边界——裁-1/裁-2/裁-4;Task 4 Step 1 的「残留 -1 政策 P-A/P-B」给了推荐(P-A)与拒绝理由(P-B 违三态纪律);Task 5 Step 2 的 `g_shadow_map` 改名给了「>30 处引用则转 P6」的择一条件。**未知数不猜**:全 op 分类表的实际分类结果(Task 0 表 A 实测)、暖态 SIGSEGV 的实际根因(Task 1 实测)、命名面是否存在不可判定形态(Task 3 探针实测)、清零后的实际残留 -1 入口(Task 0 表 D 实测)——全部列为实测报告项,**不预填数字**。 +- **数字口径(全部实读或既有记录值)**:`selftest-types` 355(`src/ci/run.sh:71`);`test_ccr_types` 34;全枚举 55/55(48 selfhost + 7 bootstrap);canary `95084e7b…d475`(28822B);`CIR_CACHE_VER=17`(`cir_cache.cr:48`);`ESZ_DFNODE=72`/`OFF_DF_TK=40`/`OFF_DF_TK_TERM=64`(`dyn_arr.cr:119-121`);`ESZ_NOD_DISK=36`(`ccr_io.cr:143`);`.ccr` 两套口径(`ccr` 90823B/134701B · `build --static` 90966B/134844B,附录 D-1);站点覆盖(T7 值,**仅作 P4 时点记录**,P5 以 Task 0 表 C 为准);「≥N 例」一律为**计划下限**(可增不可减)。 +- **风险面**:① **单槽化的分类表错一格 = 静默错码**(后端按 `ti` 分派)——缓解:Task 0 表 A 逐 op 有依据 + 派生码不变量逐节点对拍 + 直方图 diff + 突变控制;② **命名面判定化引入漏报**(把「未知」误判为「不等价」)——缓解:加强的适用域守卫 + 探针负控(union/μ 仍 -1)+ 影子清零判据(0 差异)+ 突变控制;③ **删除批的网切换空窗**——缓解:D24 顺序 + 探针必须在影子存活期与删除后**两跑** + 冻结基线同源对拍;④ **Task 1 牵出版本级联**(`.cir` bump)——缓解:停条件 + 单列裁决;⑤ **多 agent 并发**——路径限定提交 + 一构建一编译串行(P4 已有碰撞教训);⑥ **`g_shadow_map` 改名面过大**——择一条件转 P6(避免收尾批出现大面积 diff 无收益)。 +- **本计划的自我限制(诚实登记)**:本计划**未跑任何构建/测试**(纯只读侦查);所有「现状值」均为**实读源码/文档**或**既有报告记录值**(出处已标);凡需实测的数字(分类表结果、SIGSEGV 根因、清零后的残留面)一律留给实施任务的 RED 步,**不以估算充当**。 + +--- + +## 附录 E:P6 交接包(P5 收官产出;逐条 = 内容 / 现状证据 / 前置 / 判据) + +1. **盘面项索引 + corearch `atom_of` 读回承接面**(裁-1 的 β;spec §6.3/§6.4):`.ccr` NOD 记录承载 TYPE 段文件内项索引(D20-② 的许可形态)+ 辅码字段 ⇒ 记录 36B → 40B + `CCR_VERSION` 8→9 级联;corearch 由项索引取项 → `atom_of` → 发射码(判据 = ELF canary IDENTICAL + `.ccr` 结构断言 + `--dump-objects` 逐字节同 + 版本拒收面)。前置 = P5 Task 2 的 `atom_of` 契约 + 分类表先行落地。 +2. **对象布局表示位**(T5 未覆盖面;D-3-7):结构体字段 / 数组元素 / 全局槽 / match 结果的可选表示位(packed 位图 / element stride)——现状 = 裸值形态**响亮失败**(rc=139 双路径同)、装箱形态正确(`test_optional.py` 两例钉住)。前置 = 与 3 合并(同触 `dyn_arr.cr` 布局 + `ast.cr` 镜像)。 +3. **`MAX_*` 定长槽解除**(T6 裁定;D-3-8):`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES`/`MAX_STRUCT_FIELDS`(皆 16)解除 = 「记录布局迁侧表」;前置四条(读回闸同步 / `ast.cr` 镜像归位(`StructInfo.field_*` 镜像 64 vs 布局 16 **已不一致**)/ 覆盖位表示(`sh_match_bit = 2^vi` 走 int,>63 变体溢出)/ 冷热 `.ccr` 与 ELF canary 判据);`MAX_GENERICS=4` 属另一类(写入侧有界、parse 期硬报)。**现有的 P022/P023 护栏 = 解除时的安全网**(漏改只得定位硬错)。 +4. **可选程序收回 `.cir` 缓存**(D-3-9-(i)):表示位侧表是编译期进程内状态 ⇒ 收回须给快照加表示位面**或**装载侧重派生(同 Task 2 的派生纪律);现状 = `main.cr` 文件路径 `g_optrep_on ⇒ cache_enabled=0`(代价 = 编译耗时)。 +5. **`.csr`/TagNode 图消费面**(spec §6.4;D-3-10-②):零调用者——接消费者前**不得**把「段内数据存在」当「语义已兑现」。 +6. **命名消费语法 `T: 可索引`**(P3 未开工;D-3-10-③)+ **iterable 接线**(`EXPR_FOR` 零类型检查)+ **形状可写视图**(`ref(mut=1, ⊤ₖ(SEQ))` ⇒ -1)。 +7. **`tt_display` 零 `str_intern` 改造**(P4 继承;含 `.ccr` STR 段增长面)· **`g_shadow_map` → `g_term_map` 改名**(若 Task 5 择一条件触发)· **`test_type_engine.py:MIN_CASES` 由下限收紧到实测值**(P4 T4 §7-⑥ 登记)。 +8. **陈旧工具/副本**(D-3-12,P4 登记不修):`tools/module_to_ccr.py`(写 version=1 远古 `.ccr`,零 in-repo 调用者)· `pkg/` 内陈旧 `ccr_io.cr` 副本 · `docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md:371`(称 `CIR_CACHE_VER=14`,陈旧)。 +9. **纪律继承(不得稀释)**:三态纪律 · 新硬错先 report-only · 清单三面同核 + `test_backend_bootstrap`(`error[`=0)· 判据口径按 TODO #26(结构性断言)· **影子通道已下线 ⇒ 判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制**(P5 Task 5 起)· 比较前 `clean-cache`(D-1 口径)。 + +**P5 收官追加(T7;每项 = 现状 / 出处 / 影响面 / 建议 / 前置)** + +10. **`¬` 面过判(T3b 揭开的预存缺陷)** — **现状**:`sub_cover` 的 product 规则撞 `lit_implies` 的 ¬ 面规则——`lp ⊆ ¬x` **同类原子** ⇒ `ak_disjoint` 回 0 ⇒ 判「不含」;值集语义对**异身份**同形原子(变体令牌 / 命名链)应为 1。自测以 `t3.witness_reverse_over_claim`/`t3.witness_equiv_over_claim`/`t3.product_neg_over_claim` 三例钉住过度判定 + 最小复现。**出处**:`p5-task3b-report.md` §7-1(§5-② 逐步推导)。**影响面**:仅 witness 项的 `ty_sub` 消费者(生产 `ty_exhaustive` 走 `tt_norm`+`ty_inhabited`、`gen_constr_witness_str` 只 `tt_display`)⇒ **全语料零翻转**,生产不可观测。**建议**:加强 ¬ 面「异身份原子 ⇒ 不交」,落地后三例应转 1 并重钉。**前置**:「链元素皆类型项/身份令牌」适用域证明(T3b §3.1 排序表可复用)+ 突变控制负控(M4 令牌碰撞反例)。 + +11. **`-> never` 调用返回型缺口(T6 登记,预存)** — **现状**:`fn boom() -> never` 的**调用**被推断为 unit(非 never)⇒ `x : int = boom()` 触发 TA02、`return boom()` 今天即 TF01——同一缺口在本站点多暴一例。全语料 `-> never` = 0。**出处**:`p5-task6-report.md` §6-①。**影响面**:调用面类型推断(返回型映射);当前无语料命中。**建议**:调用返回型补 `TI_NEVER`(返回位点/声明位点/实参位点同源受益)。**前置**:与 12(#20)同批——同一调用推断面(`infer_gen_call`/`unify_types`)。 + +12. **#20 / F3:调用位点实参不匹配无诊断** — **现状**:`unify_types` 返回值被丢弃(实参位);返回位点部分静默;T6 只落**声明位点**(显式划界)。**出处**:TODO #20 · findings §6 F3 · `p5-task6-report.md` §6-③。**影响面**:调用面静默接受错型实参(符号取声明、后端按声明发射 = 静默错产物同族)。**建议**:把 `unify_types` 的 -1/0 值与诊断接线(码体系照 TA01/TA02),入硬名单前先 report-only。**前置**:调用面**错误标记**语义(级联抑制,照 T6 豁免③ 先例)+ 与 11 同批。 + +13. **冻结基线可复现性(T5 §7-4;维护者已裁定「重建,不落二进制」)** — **现状**:同源对拍腿依赖 `/tmp/p5t0/base/{corec,corearch,corelsp}`(= P4 收官二进制 `5d2b15ad…`/`493dc490…`/`18b94bd9…`);`/tmp` 清理即不可复现(本轮仍在位 ⇒ T7 对拍腿成立)。**出处**:`p5-task5-report.md` §7-4 · 本计划 Global Constraints(冻结基线腿)。**影响面**:每批收尾的判定面主判据之一(跨会话失效)。**建议**:把**重建配方 + sha 白名单**落进仓库(如 `tools/baseline/REBUILD.md`:`jj` 检出 pinned revision `9bcb7083`(P4 收官)→ `python3 build_selfhost_native.py` → 三 sha 对白名单)。**前置**:维护者确认配方位置 + 白名单维护纪律(基线换代时更新)。 + +14. **ICE04/P-A 政策签核 + 非硬名单诊断不阻 `build`(政策对偶面)** — **现状**:(a)T4 按计划推荐案落 P-A(ICE04 入 `main.cr` 硬名单 ⇒ `build` 拒绝落盘),**签核未取**;(b)反向面:TA01/TG02 等**非**硬名单诊断不阻 `build`(硬名单闸只收 R002/TK05/TK06/TS*/TK02/TM03/ICE04/TA02;冻结基线同)。**出处**:`p5-task4-report.md` §7-7 · `p5-task3-report.md` §8-4 · `p5-task6-report.md` §6-④。**影响面**:`main.cr` 硬名单闸一处(≈:149-170);「诊断了但产物照出」类静默面。**建议**:一次政策复核同时裁——(a)ICE04 是否保留硬名单(回退面 = 一行);(b)非硬名单诊断是否收紧(至少 `build` 面汇总告警);(c)新硬错入闸判据成文化(既有判据 =「判定继续 ⇒ 产出静默错产物」)。**前置**:维护者裁决。 + +15. **`string == int` 混比双路径分歧 + 单条目 >1GiB 分片(T1 登记)** — **现状**:`s == 0` 在 Python bootstrap 侧编成 `str_eq`(`str_len(0)` 自崩)、自举侧按 int 比较 ⇒ 两路径语义分歧;T1 以 read-EFAULT 判据绕开(语言面未修)。另:`.cir` 快照单条目 > 1GiB 无分片机制(当前最大 3.99MB)。**出处**:`p5-task1-report.md` §6-①/§6-③。**影响面**:语言面比较语义(用户可写形态);缓存格式分片面(远期)。**建议**:语言面裁决「string 与 int 可否比较」(编译期拒绝 or 单侧转换);分片作独立议题登记。**前置**:混比语义定案;分片须有 >1GiB 真实用例(当前零)。 + +16. **设计先例引用面(登记,非功能缺口)** — **现状**:`specs/2026-09-11-policy-injection-design.md:275` 与 `specs/2026-09-11-explain-predict-incremental-design.md:84/127/181/447` 以 `--type-shadow` 为**先例**(「审计通道默认关、两态产物逐字节同」)——通道已随 T5 下线。**出处**:grep 全仓(`--type-shadow` 的**非历史**引用面 = 这两份设计稿 + findings/TODO 的已更新说明)。**影响面**:未来批设计稿的先例引用(不影响当前判据)。**建议**:两 spec 下次修订时把先例载体改写为 `--verify-*` 家族(`--verify-named-dedup`/`--verify-evp-nodes`)+ 行为探针口径(原则不变)。**前置**:无(文档面顺手项)。 diff --git a/docs/superpowers/plans/2026-09-14-r2-p6-tail.md b/docs/superpowers/plans/2026-09-14-r2-p6-tail.md new file mode 100644 index 00000000..bb0bd5b8 --- /dev/null +++ b/docs/superpowers/plans/2026-09-14-r2-p6-tail.md @@ -0,0 +1,465 @@ +# R2 P6(尾批)实施计划:附录 E 十六项分诊收口——回归网可复现化 + β 盘面项索引 + 判定缺口 + 文档/登记 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 兑现统一设计 spec §9 的**尾批**(P5 已 ✅,本批 = 类型接口化工作流的**最后一批**):把 P5 交接包(附录 E 16 条)**逐条分诊并处置**——① **立即实施**(7 条:E-1 β / E-7 残项 / E-8 陈旧件 / E-10 ¬ 面过判 / E-11 `never` 调用面 / E-13 冻结基线复现 / E-16 先例引用);② **待维护者裁定**(7 条:E-2/E-3/E-4 容量批归属 · E-6 能力面范围 · E-12 调用位点诊断 · E-14 政策复核 · E-15 混比语义);③ **维持登记**(E-5 `.csr`/TagNode + E-15 分片半项);E-9 纪律继承非任务,逐字落本计划 Global Constraints。**边界纪律(尾批)**:不做「新能力」(盘面项索引 β 除外——它是 P5 裁-1 已裁定的 **P6 承接面**,属**已裁定的**交付);凡本批新引入的收紧一律 **report-only 先行**;凡待裁项**不得**先行实施。 + +**Architecture:** 七条主线:① **前置侦查与基线重建**(Task 0:**从 pinned revision `9bcb7083` 重建冻结基线**并核 sha 白名单 + 语料/探针清点 + 锚点实核;源码零改动);② **回归网可复现化**(Task 1 = E-13:重建配方 + sha 白名单 + 语料 runner + 探针语料**入仓**——当前探针源已在 `/tmp` 内**部分丢失**,见「前置状态」);③ **判定面缺陷收口**(Task 2 = E-10 ¬ 面过判;Task 4 = E-11 `never` 调用返回型 + E-12 调用位点诊断(挂裁-P6-2));④ **载体尾项**(Task 3 = E-1 β:`.ccr` NOD 盘面项索引 + `CCR_VERSION` 8→9 + corearch `atom_of` 读回);⑤ **文档/卫生**(Task 5 = E-7/E-8/E-16);⑥ **收官**(Task 6)。 + +**Tech Stack:** Core 自举栈。`src/compiler/ccr_io.cr`(`CCR_VERSION=8` :135 · `ESZ_NOD_DISK=36` :143 · NOD 写 :768 / 读 :1384-1389 / `OFF_NS_TK` :511 · 段界检 :1367/:1373)· `src/compiler/ccr_types.cr`(`ccr_type_populate` :54 / `ccr_iface_populate` :94 / `ccr_seg_prepare_save` :300)· `src/compiler/ty_shadow.cr`(分类表 :424-523 · `sh_term_of_ti` :321 · `sh_sig_term_of_ti` :885 · `tt_display_at` :539)· `src/compiler/type_engine.cr`(`lit_implies` / `ak_disjoint` / `sub_cover` / `te_elem_cmp`)· `src/compiler/checker.cr`(`unify_types` 定义 :1891 / 丢弃点 :2000 · `EXPR_FOR` :2929 · 返回位点 `body_ti != TI_NEVER` :2234)· `src/compiler/main.cr`(硬名单 :149-176 · `cache_enabled`/`g_optrep_on` :477-491)· `src/compiler/dyn_arr.cr`(`ESZ_DFNODE=72` :119 · `OFF_DF_TK=40` :122 · `OFF_DF_AUX=64` :124 · 受护访问器 :481-548)· `src/compiler/ast.cr`(`MAX_*` :115-118 · 镜像 `[.;64]` :143-145 · 错误码 :407-428)· `src/ci/run.sh`(五 job;selfhost-tests :68-109)· `tests/selfhost/test_type_engine.py`(`MIN_CASES=32` :20)。 + +**Spec:** `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` **§6.3**(corearch 读回:**接线 = β 批次(P6)**,前置已齐)、**§6.2/§3.4**(D20 稳定引用形态规范)、**§6.4**(语义保鲜兑现点)、**§9 P5 行**(✅ 终态与未覆盖面指针)、**§10**(YAGNI:不做项与未覆盖面登记机制)。**P6 起点文档 = P5 计划附录 E**(`docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md`:1–16 条,逐条 = 现状/出处/影响面/建议/前置)+ P5 统一台账(同计划「统一台账」节)。 + +**前置状态(本实例实核/实读;记录值一律带出处)**: + +- **工作树**:head = `5b93c9fe`(P5 Task 7 收官,纯文档 5 文件);工作副本 `15da9397` **空**、`jj status` 干净(本实例 `jj status` 实核)。**源码零改动**(P5 T7 之后无提交)。 +- **P5 判据终值**(P5 T7 记录,`p5-task7-report.md` §0/§2):五 CI job rc=0 · 全枚举 **58/58**(51 selfhost + 7 bootstrap)· `selftest-types` **404/404** · `check src/compiler` rc=0 · ELF canary `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(28822B)· 自举链 `corec2==corec3` `5559beef…`(2854910B)+ N06=0 + 冒烟 42 + `--help` rc=1 · `.ccr` 两口径冷态四条(`ptr_arith` 90823B `fb4a3b59…` / 90966B `592afa31…`;`generics_test` 135341B `ddec1ce6…` / 135484B `cd2af565…`;差恒 143B)· `--dump-objects` 3521 行 · `.cir` DOT 165915B `b1bdf480…` · 构建确定性 ×2(`corec f737e26b…` / `corearch 544e9201…` / `corelsp 4251108b…`)。 +- **冻结基线(pre-P5)**:**本实例实核仍在位且 sha 与白名单逐条同**——`/tmp/p5t0/base/corec` = `5d2b15ad746018619b01143cce400e6f3489ae72a962e12283a7afd3dcdb41e7` · `corearch` = `493dc490dfaed7774b46b74ccf1cfeca9fe5f61620e34229afb8991b1628a2a7` · `corelsp` = `18b94bd955fa204accf0753095a28f6499871566254d846333a7496c99109d49`(= P4 收官 `9bcb7083` 产物,`jj log -r 9bcb7083` 实核存在);runner `/tmp/p3t0_run.sh` sha = `12dc571f…`(实核)。基线 rc 分布 **34×0/38×1**(P5 T0/T4/T5/T7 一致)。 +- **⚠ 探针语料已开始腐坏(本实例实核,Task 1 的硬证据)**:`/tmp/p5t3b/probes/` 内**零 `.cr` 源**(仅存 `n13/n17/n18*` 的 `.log`/`.dump`——**T3b 残留探针源已丢**);`/tmp/p5t3/probes` 18 个 `.cr` + `/tmp/p5t0/probes` 11 个 `.cr` ⇒ **全量 find 实得 29 个 `.cr`**(P5 T5/T7 记载「30 档」,**数字须在 Task 0 重清点,不得沿用 30**);`/tmp/p5t4/run_probes.sh` 仍按 `*.cr` 通配三目录 ⇒ **对 `/tmp/p5t3b/probes` 静默空跑**。⇒ E-13 的诉求不止于冻结**二进制**,「探针语料」这条腿**已在退化**。 +- **runner 影子位已死**:`/tmp/p3t0_run.sh` 的 `shadow` 模式仍传 `--type-shadow`/`--type-shadow-dump`——两旗标已随 P5 T5 删除 ⇒ **该模式不可用**;`check` 模式(无旗标)**仍可用**(本实例实读脚本确认)。入仓版必须剥掉影子分支。 +- **已知未收口面**(附录 E 之外,本批须一并看护):`main.cr:149-176` 硬名单闸(ICE04 `:166` / TA02 `:174` 已入;**非硬名单诊断不阻 `build`**);`g_optrep_on ⇒ cache_enabled=0`(`main.cr:488-490`);`EXPR_FOR` 零类型检查(`checker.cr:2929-2940` 恒绑 `TI_INT`)。 + +--- + +## 附录 E 十六项分诊表(**本计划第一产物**;逐条 = 分诊 / 依据 / 处置) + +> 分诊口径:**(a) 立即实施** = 形态已定、无需新裁定(实施者按本仓既有纪律自行落地);**(b) 待裁** = 须维护者答复(逐条点名问句,见「裁决门」);**(c) 维持登记** = 本批不做,理由写死。**不得**用本表把待裁项偷渡成实施项。 + +| # | 项 | 现状一句话(出处) | 分诊 | 处置 | +|---|---|---|---|---| +| E-1 | β:盘面项索引 + corearch `atom_of` 读回 | `.ccr` NOD 只有**派生码**(`ccr_io.cr:143`/`:511`),项不可跨进程解析;corearch 无 TYPE 段消费者(spec §6.3);暖态项表可小于冷态(P5 T2 §5-④ 实测 `rows` 16→13) | **(a)** | **Task 3**(裁-1 已裁 P6;形态 = D20-② 许可形态。**两处先裁项**见 Task 3 Step 1) | +| E-2 | 对象布局表示位 | 结构体字段 / 数组元素 / 全局槽 / match 结果无表示位 ⇒ 裸值形态 **rc=139 双路径同**(响亮)、装箱形态正确(`test_optional.py` 两例钉) | **(b)** 裁-P6-1 | 与 E-3 同批(裁-4);**新能力面**(非尾批范围) | +| E-3 | `MAX_*` 定长槽解除 | 三上限 16(`ast.cr:116-118`)只约束进程内定长记录;解除 = 记录布局迁侧表;**四条前置未满足**(读回闸 / `ast.cr:143-145` 镜像 64 vs 布局 16**已不一致** / `sh_match_bit = 2^vi` >63 变体溢出 / 冷热 `.ccr` 与 canary 判据) | **(b)** 裁-P6-1 | 同上;护栏 P022/P023 已在位(#35 划销) | +| E-4 | 可选程序收回 `.cir` 缓存 | `main.cr:488-490`:`g_optrep_on != 0 ⇒ cache_enabled = 0`(表示位侧表 = 进程内状态、快照不载) | **(b)** 裁-P6-1 | 依赖 E-2(同批) | +| E-5 | `.csr`/TagNode 图消费面 | **零调用者**(P4 T4 §7-②;`grep csr/TagNode --include='*.cr' src/` = 0 命中,本实例实核) | **(c)** | 消费者属**验证切片轮**(spec §10「完整证明搜索归验证切片轮」);「段内数据存在 ≠ 语义已兑现」口径保持 | +| E-6 | 命名消费语法 / iterable 接线 / 形状可写视图 | `T: 可索引` 未开工 · `EXPR_FOR` **零类型检查**(`checker.cr:2929-2940`:`infer_expr(iter)` 后恒 `def_sym(var_ni, SYM_LOCAL, TI_INT, -1)`)· 可写视图实测 `ref(mut=1, ⊤ₖ(SEQ))` ⇒ -1 | **(b)** 裁-P6-4 | 能力面(P3 未开工面遗留);含一处**静默接受面**(`EXPR_FOR` 元素类型恒 int) | +| E-7 | `tt_display` 零 `str_intern` + 改名残项 + `MIN_CASES` | `tt_display_at` **已**只读零驻留(`ty_shadow.cr:535-536` 头注 + P5 T4 §4-9 归因实测);`g_shadow_map → g_term_map` **P5 T5 已执行**;`test_type_engine.py:20` `MIN_CASES = 32` = 下限(实测 404) | **(a)**(仅 `MIN_CASES`)+ **前两项「现状复核」** | **Task 5**;`tt_display` 项的**原始改造意图无法确定**(见「未决项」U-1) | +| E-8 | 陈旧工具/副本 | `tools/module_to_ccr.py`(12784B;`wu32(1)` = **version 1** 远古 `.ccr`,本实例实读 `:219-220`;**零 in-repo 调用者**——本实例全仓 grep 仅命中自身 docstring/CHANGELOG/计划文档)· `pkg/` 陈旧 `ccr_io.cr` 副本(`.gitignore` 已覆盖 `pkg/` ⇒ **非入库面**)· `specs/2026-09-11-explain-predict-incremental-design.md:371` 的 `.cir` 缓存版本陈旧(旧记 `CIR_CACHE_VER`=14,实为 **17**) | **(a)** | **Task 5** ✅(2026-09-14 落地) | +| E-9 | 纪律继承 | 三态 · 新硬错先 report-only · 清单三面同核 + `test_backend_bootstrap` · 判据口径按 TODO #26 · 影子通道已死 · `clean-cache` | **非任务** | 逐字落本计划 **Global Constraints** | +| E-10 | ¬ 面过判(T3b 揭开) | `sub_cover` product 规则撞 `lit_implies` ¬ 面规则:`lp ⊆ ¬x` **同类原子** ⇒ `ak_disjoint` 回 0 ⇒ 判「不含」;值集语义对**异身份**同形原子应为 1——**已收口面 = `AK_SUM` 变体令牌**(链 = `[枚举名令牌, 变体名令牌]`);**「命名链」半项不实施**(`AK_NAMED` 早退 -1 属 P0 终审 Important B 在册政策,翻转为政策变更;裁定 2026-09-14 = 维持 -1,待裁登记另起)。**仅 witness 项可观测**(生产 `ty_exhaustive` 走 `tt_norm`+`ty_inhabited`;全语料零翻转) | **(a)** | **Task 2 ✅**(三例 `*_over_claim` 已转 1 重钉 + 邻域负控 11 例) | +| E-11 | `-> never` 调用返回型 | `fn boom() -> never` 的**调用**被推断为 unit ⇒ `x : int = boom()` 误发 TA02、`return boom()` 误发 TF01(同一缺口两站点)。根因 = 声明注册域守卫 `checker.cr:1491`(NEVER→unit 钳位)。**收口面(P6 T4a,2026-09-14)** = 直调 + 方法调用两站点(非泛型 `fn`);**同族未修(裁④ + 新登记)** = 泛型调用(`res_call_type:1829` + 自测 `t6.never_cell_diff`)· extern(注册域 `:1574`)· 模块限定(`g_mod_func_tis`)· **`EXPR_ASSIGN` 无 never 豁免(`:3314-3336`,既有缺口,入 TODO)**。口径注:「全语料 `-> never` = 0」限 `.cr` 编译语料(72 档 + 自源 + stdlib + examples);**58 套件内联源有 6 处**(`test_iface_ops.py`) | **(a)** | **Task 4a ✅**;**发射面零变化**(canary IDENTICAL 实测) | +| E-12 | #20/F3 调用位点无诊断 | `unify_types` 返回值被丢(`checker.cr:2000`,泛型形参位,`infer_gen_call` 内;**非泛型调用位点不是「待定位」而是「不存在」**:已知 SYM_FN 的非泛型调用分支**连实参推断都不走**(对照模块/方法/SO/外部未知/枚举构造器五处均有走查)⇒ E-12 实施 = 在该分支**新增**判定点);`EC_TF_ARG_TYPE`(TF07,`ast.cr:423`)当前**仅 1 处 raise**(`checker.cr:3683`,且是 `@raw_int` 内建语法位置,**非调用位点**) | **(b)** 裁-P6-2 | **Task 4b(未实施,挂裁)**;**入闸判据 = 裁-P6-2(c)**;report-only 先行 | +| E-13 | 冻结基线可复现性 | `/tmp/p5t0/base/` = /tmp 产物(**本轮仍在位、sha 实核相符**);`src/ci/run.sh:73-76` 自述「本 job 内**无法**复现——冻结二进制不入库」;维护者已裁定「**重建,不落二进制**」 | **(a)** | **Task 0**(重建 + 核白名单)+ **Task 1**(配方/runner/探针入仓) | +| E-14 | ICE04/P-A 签核 + 非硬名单诊断 | ICE04(`main.cr:166`)已按 P-A 入硬名单、**签核未取**;反向面:TA01/TG01/TG02 等**非**硬名单诊断**不阻 `build`**(`EC_TG_ARG_COUNT`/`EC_TG_BOUND` = `ast.cr:488-489`;硬名单闸实读 = R002/TK05/TK06/TS01-04/TK02/TM03/ICE04/TA02,`main.cr:152-174`) | **(b)** 裁-P6-2 | 三问同裁 + 两处**冻结面**(`*T ← 0/None`;`--verify-named-dedup` 保留=偏离 D27) | +| E-15 | `string == int` 混比 + >1GiB 分片 | bootstrap 侧对「任一侧为 string 型 var」的比较一律分派 `str_eq`(`x86_64_stack_asm.py:164`)⇒ 自举侧按 int 比较 = **双路径语义分歧**;T1 以 read-EFAULT 判据绕开。分片:单条目 >1GiB 无机制(当前最大 3.99MB,**零用例**) | 混比 **(b)** 裁-P6-3 / 分片 **(c)** | 混比 = 语言面语义定案;分片 = 无真实用例,**登记** | +| E-16 | 设计先例引用面 | `specs/2026-09-11-policy-injection-design.md:275` 与 `specs/2026-09-11-explain-predict-incremental-design.md:84/127/181/447` 以 **`--type-shadow`** 为先例——该通道已随 P5 T5 删除 | **(a)** | **Task 5**(文档顺手项,零风险) | + +**分诊计数**:立即实施 **7**(E-1 · E-7 · E-8 · E-10 · E-11 · E-13 · E-16)/待裁 **7**(E-2 · E-3 · E-4 · E-6 · E-12 · E-14 · E-15 混比)/维持登记 **1 + 半**(E-5 · E-15 分片)/非任务 **1**(E-9)。 + +--- + +## 裁决门(**开工前置**;逐条 = 问句 / 影响项 / 推荐 / 未取裁时的行为) + +> 纪律:**未取裁的项一律不实施**(对应任务只到「report-only / 登记」为止),且**不得**以「推荐案」冒充裁定——P5 T4 的 P-A 是**已实施待签核**(回退面 = 一行),本批不复制该形态。 + +| # | 问句(逐条照抄给维护者) | 影响项 | 本计划推荐 | 未取裁时 | +|---|---|---|---|---| +| **裁-P6-1** | 容量批(对象布局表示位 E-2 + `MAX_*` 解除 E-3 + 可选程序收回 `.cir` 缓存 E-4)——**仍在 P6 内实施**(裁-4 原文「合并为一个 P6 批次」),还是**转独立容量批**? | E-2/E-3/E-4 | **转独立批**(理由:① 三者 = **新能力**(新表示语义 + 新记录布局),与「尾批只做已裁定交付/缺陷收口」冲突;② 在野需求 = **0**(语料最大结构体 9 字段、无超限枚举、可选未覆盖面为**响亮**失败);③ 四条前置(读回闸/`ast.cr` 镜像归位/覆盖位表示/冷热判据)**无一满足**,须先出设计) | 本批不实施;维持登记,TODO 挂「容量批(待排期)」条目 | +| **裁-P6-2** | 政策复核三问 + 两处冻结面:**(a)** ICE04/P-A 是否保留硬名单(`main.cr:166`;回退面 = 一行)?**(b)** 非硬名单诊断是否收紧(至少 `build` 面汇总告警)?**(c)** 新硬错入闸判据是否成文化为「判定继续 ⇒ 产出静默错产物」(照 R002/TS01-04/TM03/ICE04/TA02 先例)?**(d)** `*T ← 0` / `*T ← None` 是否作为 **null 初始化**放行(P5 T6 §6 的回退面 = 声明位 + 赋值位两站点同改 + `test_named_face.py` 语料回退)?**(e)** `--verify-named-dedup` 保留(偏离 D27,回退面 = 一行)是否认可? | E-12/E-14 + P5 T4/T5/T6 的待签核面 | (a) 保留(清零判据成立、全语料 0 命中)· (b) 收紧到「`build` 面汇总」· (c) 成文化(该判据已实际指导 5 个门)· (d) **维持拒绝**(赋值位点早已 TA01 拒绝 + bootstrap 参考实现本有此检查,见 T6 §5 三证)· (e) 认可保留(活消费者 + 只增不减) | E-12 不实施(Task 4 只落 E-11);ICE04/TA02 保持现状(已实施面不回退、不回退面不扩大) | +| **裁-P6-3** | `string == int` **能否比较**:编译期拒绝,还是单侧转换(哪一侧),还是维持现状(双路径分歧)? | E-15 混比 | **编译期拒绝**(异型比较无良定义语义;现状 = bootstrap 侧 `str_eq` 自崩路径已被 T1 绕开而非修好) | 不实施;维持登记(T1 的 read-EFAULT 判据继续承担) | +| **裁-P6-4** | 能力面范围:`EXPR_FOR` 元素类型/非可迭代拒绝(iterable 接线)**是否入 P6**?命名消费语法(`T: 可索引`)与形状可写视图是否维持未开工? | E-6 | **入 P6 的只有「`EXPR_FOR` 静默面收口」**(元素类型按被迭代项推导 + 非序列 ⇒ 诊断,report-only 先行);命名消费语法/可写视图维持未开工(须独立设计轮) | E-6 整体不实施(维持登记) | + +--- + +## 任务总表(**本批实施面**;状态一律「待实施」,收官时按实测回填) + +| 任务 | 内容 | 对应附录 E | 依赖 | +|---|---|---|---| +| T0 | 前置侦查 + **冻结基线重建**(pinned `9bcb7083`)+ 起点判据 + 守门复验 + 锚点实核(**源码零改动**) | E-13 前置 | — | +| T1 | 回归网可复现化:重建配方 + sha 白名单 + 语料 runner + 探针语料**入仓**(+ CI 面评估) | E-13 | T0 | +| T2 | ¬ 面过判收口(`ak_disjoint` 异身份原子 ⇒ 不交;三例重钉 + 邻域负控) | E-10 | T0、T1(探针腿) | +| T3 | β:`.ccr` NOD 盘面项索引 + `CCR_VERSION` 8→9 + corearch `atom_of` 读回 | E-1 | T0、T1、**Step 1 两处先裁** | +| T4 | E-11 `never` 调用返回型(独立)+ E-12 调用位点诊断(挂**裁-P6-2**) | E-11/E-12 | T0、T1、裁-P6-2(E-12 半) | +| T5 | 文档/卫生批:`MIN_CASES` 收紧 + 陈旧件 + 先例引用 + 计数器钉 + `run.sh` 自述块更新 | E-7/E-8/E-16 | T1(自述块随入仓改写) | +| T6 | 收官:全量回归 + 判据复验 + 统一台账 + 文档 + 尾批终态与剩余登记 | 全部 | 全部 | + +**顺序理由**:T1 必须先于一切(后续每个任务的判定面判据都含「冻结基线同源对拍」这条腿,而该腿当前既**不可复现**又在**腐坏**);T3(格式级联,最大风险)**早做**以留余量;T4 的 E-12 半身挂裁-P6-2,故排在被裁项之后;T5 的 `run.sh` 自述块依赖 T1 的入仓结果。 + +--- + +## Global Constraints + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**(多 agent 在场:一律 `jj commit -m … `);所有命令 `nice -n 19`;一构建一编译**串行**(每段前 `ps` 实查);cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +- **基线纪律**:每任务开工先取**同任务起点**基线(binary sha + 语料计数 + 探针 rc 表),**不得沿用上一任务的计数**;T1 之后基线 = **入仓配方重建的三二进制**(sha 白名单见 T1),T1 之前 = `/tmp/p5t0/base/`(本轮在位,**任何引用须带 sha 实核**)。 +- **ELF canary(硬闸,全批)**:`clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/…` → sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`。**任一任务变即停下上报**(本批**无**任何任务声明发射面变更 ⇒ 无豁免)。⚠ 该语料对类型层不敏感——它是「发射面零泄漏」证据,**不得**当新语义正确性证据(P3 附录 C.5-6 口径继承)。 +- **三态纪律(不得稀释)**:引擎 -1 不得当 0/1 用;载入失败/段缺失/行不可译/项不可建 ⇒ **拒绝**;save 侧装填遇不可译行/签名项 ⇒ **save 拒绝**。**新硬错先 report-only**:本批新增的任何 rc=1 门(含 T4 的调用位点诊断)必须先在**全语料**(`src/compiler/_import.cr` 自指 + `tests/suite` + `tests/selfhost` **内联源** + `src/stdlib` + `examples` + 58 套件全枚举)report-only 跑一遍,清单审查后才开门。 +- **判定面回归网(P5 T5 起,本批继承并**加固**)** = ① 冻结基线同源对拍(**T1 起 = 入仓配方重建的二进制**;72 档 `check` rc + 日志逐档 diff)② 行为探针(`tests/selfhost/*.py` + **入仓探针语料**)③ 突变控制 ④ 三态纪律(ICE04)。**影子通道已死**(`--type-shadow` / `[type-shadow]` 摘要 / 差异转储 / 站点直方图)——**任何后续批次不得引用其计数/摘要/站点直方图**(TODO #48)。 +- **`.ccr` 记录值引用口径(D28 继承)**:一律带**命令口径**(`corec ccr F -o O` vs `corec build F -o O --static`,差恒 143B)+ **缓存态**(冷/热;非可选程序冷≠热为**预存**豁免,各自确定性);比较任何 `.ccr`/缓存态产物前 `clean-cache`;TYPE 段「纯函数」承诺**项面成立、行面不成立**。 +- **判据口径按 TODO #26**:不采用「与旧版逐字节同」当语义证据;结构性断言 + 语义零变化 + 自举稳定为本仓主判据;「≥N 例」= 下限(可增不可减);**自测用例只增不减**(重钉/改名允许,删除须给死亡证据)。 +- **清单双注册面(B.6,硬性)**:任何清单/段表改动必须**三面同核**——`build_selfhost_native.py`(`corec_files`/`corearch_files`/`corelsp_files`)↔ `src/targets/x86_64-linux/_import.cr` ↔ `src/compiler/_import.cr`;每任务回归面**必含** `tests/selfhost/test_backend_bootstrap.py`(rc=0 + 构建日志 `error[` = 0)。T1 新增入仓文件(`tools/baseline/*` + 探针语料)**不进**任何编译清单(非 `.cr` 编译单元;探针语料 = 测试输入)。 +- **不得回退既有收纳**:TS01-04/TK02 硬门(#29)、TM03/TG02 门(#31/#33)、F2 常量档拒绝语义与「N 不回身份」、F5 槽位契约(#25/#28)、效应/纯度判据(#26/#27)、`iface_satisfies` 轴序 A>C>B、P4 全批(段机制/确定性装填/允许清单制/可选表示)、P5 全批(单槽化/命名面判定化/引擎唯一权威/ICE04/TA02/影子下线)——本计划一律不动;冲突 → 停下上报。 +- **本语言无三元运算符**;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);**不写 `buf == 0` 形态的空判**(string-int 混比在 bootstrap 侧编成 `str_eq` ⇒ 自崩;T1 先例 —— 见裁-P6-3)。 +- **多 agent 纪律**:不并发构建;提交路径限定;实施时逐任务串行;突变控制一律在**仓库外实拷贝**(`tests/` 必须**实拷贝**而非软链——P5 T7 §4-2 的假绿教训)。 + +--- + +## 侦查底座(2026-09-14 实读实核;file:line = 当前工作树 `5b93c9fe`;未标注者为本轮实读) + +**冻结基线 / 回归网** +- `/tmp/p5t0/base/{corec,corearch,corelsp}` 三 sha = 白名单值(实核,见「前置状态」);`/tmp/p3t0_run.sh` sha `12dc571f…`(实核);脚本 `shadow` 模式引用已删旗标 ⇒ **死码**;`check` 模式可用。 +- 语料 = 72 档 = `tests/suite/*.cr` 32 + 自源 2(`main.cr`/`_import.cr`)+ 后端/内核/目标单元 15 + `src/stdlib/*.cr` 19 + `examples/*.cr` 4(P5 T0 §1.2);`check` rc 分布 **34×0/38×1**(既有失败 = 2 档 0 字节 fixture + P21 嵌套 fn 负例 + examples 两档解析错——**不得**当回归)。 +- 探针:`/tmp/p5t0/probes`(11)+ `/tmp/p5t3/probes`(18)= **29 `.cr`**(find 实核);`/tmp/p5t3b/probes` **零 `.cr`**(源丢失,仅 log/dump);runner `/tmp/p5t4/run_probes.sh`(三目录通配 ⇒ 对 p5t3b 静默空跑)。 +- `src/ci/run.sh:68-112` selfhost-tests(**28 档 py 挂点**——`grep -c 'python3 tests/selfhost/'` 实核;原记「14 档」= 陈旧值,T0 表 D-4 勘误 + R2 P6 Task 5 修正);`:70-80` 回归网自述块(T1 改写:冻结基线腿 → `tools/baseline/rebuild.sh` 重建 + 手工判据);`:85` selftest 计数注(T5 后 = **415**);`:89` `test_ccr_types` 计数注(T3 后 = **48**)。 + +**载体面(Task 3 = β 的对象)** +- `ccr_io.cr:135` `CCR_VERSION = 8`(load 校验 `==8`,v7 整类拒收——D10 先例);`:143` `ESZ_NOD_DISK = 36`(7×i32 + 邻接 2×u32);`:353` 段尺寸算式 `4 + instr_count*36`;`:1367` 装载越界核算(`(seg_end3-seg_off3)/ESZ_NOD_DISK`)、`:1373` 逐记录 `ccr_has_bytes(pos, 36, seg_end3)`。 +- NOD 写:`populate_nod_objects`(:500-514,`OFF_NS_TK ← iri_tk(ii)` = **派生码**)→ `save_ccr` 的 NOD 段(:760-772:`buf_write_u32(iri_tk)`);读:`:1384-1389`(`OFF_NS_TK ← tk`)。 +- 语义对象侧表 `OFF_NS_*`/`ESZ_NOD_SEM` 定义在 `src/compiler/ent_kernel.cr`(不在 ccr_io.cr);`OFF_NS_TK` = 派生码槽(P5 T2 后)。 +- 版本面:`CIR_CACHE_VER = 17`(`cir_cache.cr:51`)——`.cir` 快照每节点 8×8B,**项槽不落盘**、装载侧重派生(P4 T4 实测裁定 + P5 T2 保持)。 +- 段表:`ccr_types.cr`(`ccr_type_populate` :54 / `ccr_iface_populate` :94 / `ccr_type_seg_build` :160 / `ccr_iface_seg_build` :196 / `ccr_seg_prepare_save` :300);`test_ccr_types.py` **40 例**(`run.sh:86`)。 +- 项面:`ty_shadow.cr` 分类表(`sh_tk_is_type_face` :424 · `sh_atom_of_term` :443 · `sh_tk_face_of_code` :451 · `sh_tk_split` :474 · `sh_tk_split_load` :487 · `sh_tk_split_impl` :491 · `sh_dfn_code_of_slots` :513 · `sh_tk_term_of_code` :523);桥接两点(`sh_term_of_ti` :321 / `sh_sig_term_of_ti` :885)。 +- DF 槽:`dyn_arr.cr:119` `ESZ_DFNODE=72` · `:122` `OFF_DF_TK`(= 类型项引用)· `:124` `OFF_DF_AUX`(= 辅码);契约注 :104-107。 + +**判定/诊断面(Task 2/4 的对象)** +- `unify_types` 定义 `checker.cr:1891`;**返回值丢弃点 :2000**(泛型形参位);**使用点 :3433**(结构体泛型字段位,负控对照);非泛型调用位点须实施时定位(TODO #20 旧锚 `:1053` 已漂移)。 +- `EC_TF_ARG_TYPE = 5007`(TF07,`ast.cr:423`)当前仅 `checker.cr:3683`(`@raw_int`)一处 raise ⇒ 码槽**现成**。 +- `TI_NEVER` 既有豁免:`:2234`(返回位点 `body_ti != TI_NEVER`)、T6 的 `check_let_annot_compat` 豁免③(`val_ti == TI_NEVER`,`checker.cr:526` 区域)。 +- `EXPR_FOR`:`checker.cr:2929-2940`(`infer_expr(iter)` 丢弃结果 + `def_sym(var_ni, SYM_LOCAL, TI_INT, -1)`)。 +- 硬名单闸:`main.cr:149-176`(实读 :152-:174 八条 `hard = 1`);`:166` ICE04、`:174` TA02。 +- `tt_display`:`ty_shadow.cr:539`(`tt_display_at`)/`:608`(包装);头注 `:535-536` 已声明「只读、零 `str_intern`」。 + +**`MAX_*` / 镜像面(裁-P6-1 的证据)** +- `ast.cr:115-118`:`MAX_GENERICS=4` / `MAX_STRUCT_FIELDS=16` / `MAX_ENUM_VARIANTS=16` / `MAX_VARIANT_TYPES=16`;镜像声明 `:143-145`(`field_names/field_types/field_type_nodes: [.;64]`)——**与布局 16 已不一致**。 +- 受护访问器:`dyn_arr.cr:481-548`(`si_*` 9 个 + `ei_*` ~10 个,逐槽护栏;头注 :520-529 写明「唯一受护写点 + 读护栏 + P022/P023 承担用户面拒绝」);P022/P023 护栏套件 = `tests/selfhost/test_enum_limit.py`(13 例,`run.sh:105`)。 + +**文档面(Task 5 的对象)** +- `src/ci/run.sh:73-76`(**T1 后必须改写**——冻结基线腿自 T1 起可复现);`:82`/`:86`(selftest / test_ccr_types 计数注)。 +- `tests/selfhost/test_type_engine.py:20` `MIN_CASES = 32`;`:37` 断言 `passed == total and total >= MIN_CASES`。 +- `docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md:371`(`.cir` 缓存版本旧记 `CIR_CACHE_VER`=14 + `cir_cache.cr` 行号 `:16`,双陈旧);`:84/127/181/447`(`--type-shadow` 先例);`specs/2026-09-11-policy-injection-design.md:275`(同)。 +- `docs/error-codes.md:100-101`(TA01/TA02)、`:114`(TF02)、`:263`(ICE04)、`:275-287`(码族统计)。 +- 陈旧件:`tools/module_to_ccr.py`(12784B);`pkg/`(`.gitignore` 覆盖,**非入库面**)。 + +--- + +## Task 0(前置,纯侦查 + 基线重建;**源码零改动**) + +**Files:** 无源码改动。产物 = 报告 `.superpowers/sdd/p6-task0-report.md`(工作区,不入提交)+ 重建的冻结基线 `/tmp/p6t0/base/{corec,corearch,corelsp}` + 清点/基线原始输出(`/tmp/p6t0/`)。 + +**Interfaces(产物四表,全部落报告;数字一律本实例实测,未实测标「待实测」):** +- **表 A:冻结基线重建**——`jj` 检出 pinned revision `9bcb7083`(P4 收官;**不得**用工作树直接建)→ `nice -n 19 python3 build_selfhost_native.py`(串行、无并发)→ 三 sha 对白名单(`5d2b15ad…`/`493dc490…`/`18b94bd9…`);与 `/tmp/p5t0/base/` 副本并存则 `cmp` 逐字节核;两次构建取确定性;冒烟 42。 +- **表 B:P6 起点判据基线**——五 CI job;全枚举 58/58;`check src/compiler` rc=0;`selftest-types` 404/404;构建确定性 ×2;自举链(`corec2==corec3` + N06=0 + 冒烟 42 + `--help` rc=1);ELF canary;`.ccr` 两口径四条(冷态、带标);`--dump-objects` 行数;`.cir` DOT 大小/哈希。 +- **表 C:语料与探针清点**——72 档清单逐档实核(**含 0 字节 fixture 与既有 rc=1 档的划界**);探针语料**逐目录实测 `.cr` 计数**(当前 find = 29;`/tmp/p5t3b/probes` 零源 ⇒ 须从 `.log`/`.dump` 与 P5 报告内联片段判定**可复原者**)并出「可入仓 / 需重建 / 不可复原」三栏。 +- **表 D:任务锚点实核**——T2/T3/T4/T5 的每个 file:line 锚逐条实读复核(本计划「侦查底座」节的锚点表即输入;**行号漂移以本轮实读为准**)。 +- **守门复验(裁-4 的 P5 遗留项,本任务一并核)**:E-2/E-3 的未覆盖面**不得退化为静默错值**——实跑 `test_optional.py` 两例(结构体字段裸值 rc=139 / 装箱正确)+ `test_enum_limit.py` 13 例 + 越界样本(17 变体 / 17 字段)**仍为定位硬错 P022/P023**(不得静默通过)。 + +- [x] **Step 1: 基线重建**(`jj` 检出 `9bcb7083` 到临时目录 → 构建 → sha 对白名单 → 拷贝 `/tmp/p6t0/base/`;**仓库工作树零改动**,检出在仓库根之外)。判据:三 sha 与白名单**逐条同** + 冒烟 42 + 构建确定性 ×2。**不符 ⇒ 停下上报**(构建不可复现 ⇒ E-13 前提不成立,其后所有任务的对拍腿失效)。 +- [x] **Step 2: 起点判据**(表 B;cwd = 仓库根、逐段 `ps` 实查无并发)。 +- [x] **Step 3: 语料/探针清点**(表 C;`find` 逐目录 + runner 实跑一次 `check` 面得 rc 分布)。 +- [x] **Step 4: 锚点实核**(表 D)。 +- [x] **Step 5: 守门复验**(`test_optional.py` / `test_enum_limit.py` / 越界样本)。 +- [x] **Step 6: 报告**(不入提交;`jj status` 应收官为空)。 + +**判据**:四表齐 + 基线三 sha 与白名单逐条同 + 源码零改动(`jj status` 干净)+ 报告内数字全部可追溯(命令 + 原始输出片段)。 + +**停条件**:重建 sha 不符白名单 ⇒ 停下(不得改白名单迁就);`/tmp/p5t0/base/` 与重建物不一致 ⇒ 停下(两者必有其一非源出 `9bcb7083`)。 + +### Task 0 实施记录(2026-09-14;**R2 P6 Task 6 补记**——T0 当批只出报告、未回填本计划) + +> 补记理由:T0(`p6-task0`)的报告 `.superpowers/sdd/p6-task0-report.md` **不入版本控制**(`.superpowers/` 被 `.gitignore` 覆盖)⇒ 计划内 T0 面**零留痕**(六步未勾、无实施记录)。T1 报告 §9-5 已登记该事实;**归属裁定 = T6 补**(章程边界:T5 的章程 = E-7/E-8/E-16;同类归口:与「统一台账」同为跨任务收编面;避免计划文件被两批各改一次)。本记录内容 = T0 报告 §1–§5 的实测数字逐条摘录(**非重跑**,出处已标)。 + +**表 A(重建)**:`jj workspace add /tmp/p6t0/src --revision 9bcb7083` → 构建 ×2(日志逐字节同,54565B)⇒ `corec 5d2b15ad…` / `corearch 493dc490…` / `corelsp 18b94bd9…`(= 白名单**逐条同**);与 `/tmp/p5t0/base/` `cmp` **IDENTICAL ×3**;冒烟 rc=42。**表 B(起点判据)**:五 CI job rc=0 · 全枚举 **58/58** · `check src/compiler` rc=0 · `selftest-types` **404/404** · 构建确定性 ×2(`f737e26b…`/`544e9201…`/`4251108b…`)· 自举链 `corec2==corec3` `5559beef…` + N06=0 + 冒烟 42 + `--help` rc=1 · canary `95084e7b…d475`(28822B)· `.ccr` 四条(90823/90966/135341/135484)· `--dump-objects` 3521 行 · `.cir` DOT 165915B `b1bdf480…`。**表 C(清点)**:72 档 = 32+2+15+19+4,rc 分布 **34×0/38×1**(与 P5 T7 `parity_post` `diff -rq` 零差异);**探针官方清点 = 29 `.cr`**(11+18+0)——「30」= 29 + 迁移 runner 的**字面 glob 伪条目**(`_tmp_p5t3b_probes_*.cr`,rc=1,两态恒等 ⇒ 该条对拍空洞;**U-4 收口**);`/tmp/p5t3b/probes` = T3b **输出**目录(27 件 = 21 log + 6 dump)⇒ **源未丢**(T1 报告 §9 的「源已丢」初判据此更正)。**表 D(锚点)**:逐条实读——5 条漂移(`ent_kernel.cr` 实为 `src/lattice/ent_kernel.cr` · `main.cr` 硬名单为 6 语句行覆盖 11 常量且 `return 1` 在 `:178` · `dyn_arr.cr` 契约注为 `:103-118` · `run.sh` 挂点实为 **28** 非 14 · 探针 29 非 30)+ 1 条码名口径(护栏用户面实印 `error[P22]`/`error[P23]`)。**守门复验**:`test_optional.py` **34/34**(未覆盖面裸值 rc=-11 双路径同 = 响亮);`test_enum_limit.py` **13/13**;越界样本(17 变体 / 17 字段)= **P22/P23 定位硬错 rc=1 + 无产物**(未退化为静默)。**停条件**:两条均未触发。 + +--- + +## Task 1:回归网可复现化(E-13)——配方 + sha 白名单 + runner + 探针语料**入仓** + +**Files:** New `tools/baseline/REBUILD.md`(配方 + 白名单 + 纪律)· New `tools/baseline/rebuild.sh`(可执行重建:`jj` 检出 pinned revision → 构建 → sha 校验 → 落到指定目录;**不含**任何二进制)· New `tools/baseline/parity_run.sh`(72 档 `check` 面 runner;**剥掉影子分支**;cwd 参数化)· New `tools/baseline/probes_run.sh`(**实施时增补**:29 档探针 runner——T0 表 C 揭示迁移期 runner 的**字面 glob 伪条目**,探针腿若不连 runner 一起入仓则仍是空洞证据)· New `tests/probes/README.md` + `tests/probes/*.cr`(**29 档实拷**;来源表逐档落 README——**不进 `.cr` 源内**,保逐字节保真)· Modify `src/ci/run.sh`(`:73-76` 自述块改写:冻结基线腿**自本任务起可复现**,指向 `tools/baseline/`)· Modify `TODO.md`(#48 登记① 收口 + 新条目)。**未加** `tests/selfhost/test_baseline_recipe.py`(Step 4 结论 = 不挂钩 CI ⇒ 无需该套件)。 + +**Interfaces:** +``` +tools/baseline/REBUILD.md # pinned revision / 三 sha 白名单 / 构建步骤 / 换代纪律(基线换代 = 更新白名单 + 记旧值) +tools/baseline/rebuild.sh # jj 检出 → build_selfhost_native.py → sha256 对白名单 → 拷贝三二进制 +tools/baseline/parity_run.sh # 72 档 check:逐档 clean-cache + rc/logs(影子分支删除) +tools/baseline/probes_run.sh # 29 档探针:显式清单 + 计数断言(无字面 glob 伪条目) +tests/probes/… # 探针语料(29 档实拷;逐档来源表在 README——**源内不加注**以保逐字节保真) +``` +- [x] **Step 1: 配方 + 重建脚本**(Step 0 的重建**手跑配方固化**;脚本须幂等、失败即非零、**不落二进制进仓库**)。判据:在**干净临时目录**跑通一次 ⇒ 三 sha 与白名单同。**实测**:41s,三 sha 逐条同 + `cmp` 与 `/tmp/p6t0/base`、`/tmp/p5t0/base` IDENTICAL ×3 + 冒烟 rc=42 + 收工 `jj workspace list` 无残留。 +- [x] **Step 2: runner 入仓**(剥影子分支 + cwd 参数化 + 语料清单以表 C 为准)。判据:入仓 runner × 重建基线 × 72 档 = 与 `/tmp/p3t0_run.sh check` 面**逐档 rc + 日志 `diff -rq` 零差异**(同源同比)。**实测**:零差异(72 档 rc 分布 34×0/38×1 两侧同);另:冻结基线同源对拍(vs 当前二进制)亦零差异。 +- [x] **Step 3: 探针语料入仓**——**前提更正(T0 表 C/§3.3 实测)**:`/tmp/p5t3b/probes` 是 T3b 的**输出**目录,**29 档源全在位**(无需重建源;亦不"源已丢")⇒ 处置 = `/tmp/p5t0/probes`(11) + `/tmp/p5t3/probes`(18) **实拷** + 逐档来源表。判据:入仓探针 × 重建基线 × 当前二进制,逐档 rc + 日志在两态间**零差异**(对拍腿自证)+ 与 P5 记录逐条同。**实测**:两态 `diff -rq` 零差异;rc/log **29/29 与 `/tmp/p5t7/probes_post` 逐字节同**;入仓 29 档 sha 与 T0 清单 29/29 同。 +- [x] **Step 4: CI 面评估**(**默认不加挂钩**)。**实测结论 = 不挂钩**:`.github/workflows/core-ci.yml`(`actions/checkout` + `fetch-depth: 2` 浅检出)内**无 `jj`** ⇒ pinned revision 不可得;增量实测(rebuild 41s + 对拍 19s + 探针 1s)虽小,但不解决 jj/浅检出前置 ⇒ 保持**手工判据 + 入仓配方**(`run.sh` 自述块已写明)。 +- [x] **Step 5: 判据**:五 CI job rc=0 · 全枚举 58/58 · `selftest-types` 404/404 · `check src/compiler` rc=0 · **ELF canary IDENTICAL** · 自举链 · `test_backend_bootstrap` rc=0 + `error[`=0 · **冻结基线同源对拍 72 档零差异**(新旧 runner 互证)· 探针全集(29)零差异 · 构建确定性 ×2 · 突变控制 ≥1 + 精确回滚。**实测全绿**(数字见下方实施记录);**突变控制实际 5 条**(M1/M2/M4/M5/M7 转红 + M3 **未转红**,诚实登记)。 +- [x] **Step 6: 提交**:`feat: R2 P6 Task 1——回归网可复现化(冻结基线重建配方 + sha 白名单 + runner/探针语料入仓;探针源腐坏段重建并登记)`(提交信息按实际改写,见实施记录)。 + +**停条件**:① 重建脚本在干净环境复跑不出白名单 sha ⇒ 停下(**不得**改白名单对齐);② 探针重建件与 P5 记录的 rc/诊断不符 ⇒ 停下(说明重建失真,须逐档归因);③ runner 入仓版与 `/tmp` 版出现差异且无法归因 ⇒ 停下;④ 若入仓需要动任何 `.cr` 编译清单(三面同核)⇒ 停下(本任务应零清单面)。**四条均未触发**。 + +### Task 1 实施记录(2026-09-14;报告 `.superpowers/sdd/p6-task1-report.md`;证据 `/tmp/p6t1/`) + +**形态**:`tools/baseline/` 四件(`REBUILD.md` 配方/白名单/换代纪律 · `rebuild.sh` 重建(`jj workspace add --revision 9bcb7083` → `build_selfhost_native.py` → sha **硬闸** → 冒烟;`trap` 收工 forget+删目录)· `parity_run.sh` 72 档 runner(影子分支剥除 + `nullglob` + **计数断言 72**)· `probes_run.sh` 29 档 runner(显式清单 + **计数断言 29**))+ `tests/probes/`(README 逐档来源表 + **29 档实拷**)+ `run.sh` 自述块改写 + TODO #50 新条目 / #48① 闭合。 + +**判据(全部本实例实跑)**:`rebuild.sh` **41s** rc=0 → 三 sha = 白名单 + `cmp` vs `/tmp/p6t0/base`、`/tmp/p5t0/base` **IDENTICAL ×3** + 冒烟 42 · 入仓 runner vs 迁移 runner(**同一二进制**):72 档 `diff -rq` **零输出**,rc 分布两侧 34×0/38×1 · **腿① 主判据**:冻结基线 × 当前源 vs 当前二进制 × 当前源(T0 `parity_start`)**零差异** + 与 P5 T7 `parity_post` 零差异 · **探针**:两态(基线/当前)`diff -rq` 零差异;rc/log **29/29 逐字节同 P5 T7 记录**;rc 分布 17×0/12×1;入仓 sha 与 T0 清单 29/29 同 · 五 CI job 全 rc=0(`check` 16s · `bootstrap-tests` · `selfhost-tests` **108s** · `suite` 21 档 · `full-bootstrap`)· 全枚举 **58/58**(92s)· `selftest-types` **404/404** · canary **`95084e7b…d475`(28822B)IDENTICAL** · 自举链 `corec2==corec3` `5559beef…` + N06=0 + 冒烟 42 + `--help` rc=1 · `test_backend_bootstrap` rc=0 + `error[`=0 · 构建确定性:五 job 内多次构建同 sha(`f737e26b…`/`544e9201…`/`4251108b…` = P5 T7 记录值)· 仓库改动面 = 36 路径(`src/` 仅 `run.sh` 注释块;`src/compiler/checker.cr` sha `fff7a8ba…` = P5 T6 记录值 ⇒ 源码零改动)。 + +**突变控制(5 条;全部在 `/tmp/p6t1/mut/` 副本,仓库零改动)**:**M1** 语料掉一档 ⇒ runner **rc=1**(`CORPUS COUNT 71 != 72`)+ diff 报缺档 · **M2** 撤 `nullglob` + 零匹配目录 glob(复现 U-4 形态)⇒ **rc=1**(`PROBE COUNT 30 != 29`)· **M4** cwd 陷阱(`cd /tmp`)⇒ rc=1 + 缺档 · **M5** rc 通道打哑 ⇒ 72×0 vs 34×0/38×1 转红 · **M7 源级突变**(仓库外实拷贝重建:`checker.cr:592` `e == 0` 判真 = 判定放宽,`corec 527ca428…`)⇒ **12 档日志 + rc DIFF、rc 分布 39×0/33×1(5 档 1→0 = 拒绝静默消失)**——腿① 对**真行为变更**有牙(同 P5 T5 M2 的类别)。**M3(计划建议的「去掉逐档 clean-cache」)实测仍绿 ⇒ 未转红**:`check` 面不读 `.cir` 缓存 ⇒ 该突变在本腿不可检出(诚实登记;clean-cache 纪律对 `build`/`.ccr` 面仍是必需的)。 + +**执行更正(T0 表 C/表 D 的直接后果,逐条落纸)**:① 探针正式清点 **29**(U-4;「30」= 29 + 字面 glob 伪条目)——本任务 runner 以显式清单 + 计数断言消灭该形态;② 「探针源已丢」**按下述更正**:源档全在位,`/tmp/p5t3b/probes` 是输出目录(⇒ **Step 3 不含任何源重建**;计划原写的「重建件」不存在);③ `run.sh` selfhost-tests 挂点实为 **28** 档 py(计划写 14 是陈旧计数——但**本任务未改该计数注**:属 T5 卫生批的面,避免扩面);④ 探针语料**源内不加注**(逐字节保真优先;逐档来源表落 `tests/probes/README.md`)。 + +--- + +## Task 2:E-10 ¬ 面过判收口(引擎判定面) + +**Files:** Modify `src/compiler/type_engine.cr`(`lit_implies` 的 ¬ 面规则 + `ak_disjoint` 的原子相斥判据;**须先出**「异身份原子 ⇒ 不交」的适用域守卫 + 头注)· Modify `src/compiler/type_selftest.cr`(三例 `t3.witness_reverse_over_claim` / `t3.witness_equiv_over_claim` / `t3.product_neg_over_claim` **转 1 并重钉**,另加邻域负控 **11** 例,子 runner `ts_t2_neg_run`)· Modify `src/ci/run.sh`(selftest 计数注 404 → 415)。 + +- [x] **Step 1: RED 复现(本任务自跑)**:三例现状 = 过判(`B ⊆ ¬R` 判 0);用 `tt_display` 打印两侧项 + 手工最小复现(`q3 = B ∩ ¬R ∩ ¬G` 单点)落报告。**冻结基线复证**(`/tmp/p6t0/base/corec`)。 +- [x] **Step 2: 适用域守卫(**先写证明再动码**)**:可加强面 = 「两侧皆为同类 **`AK_SUM` 变体项**(identity atom)」——T3b §3.1 排序表只可复用「链元素可判域」的**事实**,**不可复用其结论方向**(`te_elem_cmp` 判的是不变槽**元素**是否等价;¬ 面判的是**两个整原子的值集是否相交**——元素「不等价」≠ 整原子「不交」)。域外逐条:**`AK_SUM` 链元素皆令牌**(`[枚举名令牌, 变体名令牌]`)⇒ 异链 1 / 同链 0;**AK_NAMED ⇒ 维持 -1**(P0 终审 Important B 在册政策,本批不翻转)· **参数化构造子(SEQUENCE/PTR/REF/PRODUCT/FN)⇒ 维持 0 且不得扩域**(反例 `[int]` vs `[int ∪ string]`;REF 的 mut 标记位是令牌,无类守卫会被误判「异身份」)· **`⊤ₖ` 显式排除 ⇒ 0**(`tt_top_k` 的 c 槽 = 0 非 -1)· **空链 / 链元素域外 ⇒ 0**(P1-a 已裁定:域外逐位不变,零新增未覆盖面)· **μ 变元/裸值走既有路径不变**(逐条落纸,不得猜)。 +- [x] **Step 3: 实现**(落点经 T2 前置侦查修正):`ak_disjoint` **只收类 id、看不到项**(`type_engine.cr:69`;调用面 `:118` / `:327` / 自测均只传类 id)⇒ 「同类 ∧ 异身份」**不可**落在该函数(照字面改 = 同类一律不交 = **假不交**)。**落点 = `lit_implies` 的 ¬ 面**:新增 `atom_same_class_disjoint`(域守卫)+ `sum_chain_same`(AK_SUM 链**逐元素令牌同一性**、三态),在 `:326`(`AK_NAMED` 早退)之后、`:327`(`ak_disjoint` 调用)之前插一行 `if pk == xk { return atom_same_class_disjoint(lp, x); }`;**`ak_disjoint` 本体与正×正路径(`lit_empty_pair:118`)零改动**(后者经 `ty_inhabited → ty_exhaustive` 泄进 match 穷尽性 TM03 生产面,本批不碰,**单列候选**)。**不得**近似、不得把「未知」当 0。 + - ⚠ **实现教训(已修正并补用例)**:首版链比较用 `te_chain_cmp`(命名面槽位语义:槽 0 = 令牌 / 槽 ≥1 = **类型项**)⇒ AK_SUM **槽 1 的变体令牌**走 `te_arg_cmp → ty_equiv`,两个不同令牌在结构比较下**判等**(令牌的 b 槽只是标注)⇒ 误判「同链」⇒ 三例 `t3.*` 仍判 0,而当时 9 例负控**全过**(N5 跨枚举在槽 0 被令牌规则救回)。补 **N10「同枚举异变体」** 后暴露;改用 `sum_chain_same` 修复。 +- [x] **Step 4: 用例**:三例重钉为 1;**邻域负控 11 例**(子 runner `ts_t2_neg_run`,**不**内联进 `type_selftest_run`——巨型函数内联新用例有 rc=139 前科):① 同身份 ⇒ 仍相交(0);② μ 变元作**字面** ⇒ 仍 -1 + 未覆盖面;③④ `⊤ₖ` **两个方向** ⇒ 0(**语义钉子**——既有 `neg.supertype*` 走 **¬×¬** 分支,不覆盖 `:327` 同类路径);⑤ 跨枚举**同名**变体 ⇒ 1 + 同项 ⇒ 0;⑥ AK_UNIT 令牌字面 ⇒ 0(类域钉);⑦ `AK_NAMED` ⇒ **维持 -1**(政策钉);⑧ 空链 / 链元素域外 ⇒ 0(P1-a 钉);⑨ 参数化面 `[int]` vs ¬`[int∪string]` 双向 ⇒ 0;⑩ **同枚举异变体 ⇒ 1**(暴露首版缺陷的那条);⑪ `&T` vs `&mut T` ⇒ 0(mut 标记是令牌 ⇒ 类域守卫的**稳定牙齿**)。 +- [x] **Step 5: 判据**:`selftest-types` ≥404(**只增**)· `check src/compiler` rc=0 · **ELF canary IDENTICAL**(判定面改动**不得**泄进发射面)· `.ccr` 两条口径冷态**逐字节同** · 五 CI job + 全枚举 58/58 + 自举链 + `test_backend_bootstrap` · **冻结基线同源对拍 72 档零差异** · **探针全集零差异** · **突变控制 ≥2**(M1 撤销加强 ⇒ 三例复红;M2 域外守卫去掉 ⇒ 邻域负控红)+ 精确回滚。 + - **实测(2026-09-14;隔离 workspace `e5ce8f9e` + 本任务两文件,**不经主树**)**:`selftest-types` **415/415**(404 + 11)· `check src/compiler` rc=0 · canary `95084e7b…d475`(28822B)**IDENTICAL** · `.ccr` 四条 = T1 记录值(90823 `fb4a3b59…` / 90966 `592afa31…` / 135341 `ddec1ce6…` / 135484 `cd2af565…`,差恒 143B)· `--dump-objects` 3521 行 = T0 记录 · `.cir` DOT 165915B = T0 记录 · 五 CI job 全 rc=0 · 全枚举 58/58 · 自举链 `corec2==corec3` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1 · **同源对拍 72 档零差异**(rc 分布两侧 34×0/38×1)· **探针 29 档逐档内容同 T0 记录**(rc 17×0/12×1)· 对 T0 起点的两档自源日志差异 = **纯行号偏移 +146**(= 本任务两文件净增行数,内容逐字同)。 + - **突变控制实测(判据 = M1/M2a/M2c′/M3;M2b 仅探索记录)**:M1 撤销加强 ⇒ **5 FAIL**(三例 t3 + N5 + N10);M2a 类域守卫去掉 ⇒ **N11 转红**;M2c′ 不可判出口 `return 0 → return 1` ⇒ **N8 转红**;M3 次序对照(新行移到 `ak_disjoint` 之后)⇒ **零差异**(最小性);M3b 同类条件去掉 ⇒ 既有 `exhaust.witness_bool` 转红(条件必要性)。回滚逐条源 sha 复原 ✓。**注**:调用侧空链守卫(`if cp < 0 || cq < 0`)单独删除 ⇒ **零差异**(与 `sum_chain_same` 内层 `p < 0 || q < 0` 行为等价 ⇒ 该行**无牙齿**,保留作显式域注释)。 +- [x] **Step 6: 提交**(路径限定:`src/compiler/type_engine.cr` · `src/compiler/type_selftest.cr` · `src/ci/run.sh` · 本计划) + +**停条件**:① 加强会翻转**任何生产可观测面**(`ty_exhaustive` / 诊断 / rc)⇒ **停下上报**(E-10 的「生产不可观测」前提被推翻);② 域证明里出现**排序混排**(同一 (ak, slot) 既放令牌又放类型项)⇒ 停下(照 T3b 停条件);③ 若收口需要动 `te_elem_cmp`/`te_named_pair` 自身语义(非 ¬ 面)⇒ 停下(超章程)。 + +**实施记录(2026-09-14;隔离 workspace `e5ce8f9e` + 本任务两文件执行,主树零改动)** + +- **停条件核查**:① 生产可观测面**未翻转**(72 档 rc + 日志零差异 / 探针 29 档内容同 / canary IDENTICAL / `.ccr` 四条同)✓;② 域内**无排序混排**(AK_SUM 槽 0/1 皆令牌,逐元素同一规则)✓;③ **未动** `te_elem_cmp` / `te_named_pair` 自身语义,亦**未复用** `te_chain_cmp`(其槽 ≥1 是类型项语义,对 AK_SUM 是错的槽位分派)✓。 +- **勘误三条(已按此落地)**:① Step 3 落点 = `lit_implies` ¬ 面(`ak_disjoint` 只见类 id,按字面改 = 假不交);② Step 2 域收窄 = **仅 AK_SUM 变体项**(T3b 排序表只可复用「链元素可判域」的事实,不可复用结论方向;参数化构造子/`⊤ₖ`/空链/`AK_NAMED` 各自维持原语义);③ 附录 E-10 的「命名链 ⇒ 1」= **政策变更**,本批**维持 -1**(P0 终审 Important B + `named.not_disjoint` 在册)。 +- **登记(本任务发现,未修)**:① `tt_display_at` **参数链分支**对带链原子渲染退化为 `>`(实测量:`sum1`/`seq1`/`ptr` 皆 `>`,裸原子 `int`/`unit` 正常)⇒ ICE04/反例诊断中参数化类型显示退化(**预存缺陷**,建议 TODO);② 调用侧空链守卫 `if cp < 0 || cq < 0 { return 0; }` 与 `sum_chain_same` 内层检查**行为等价**(单独删除 ⇒ 零差异)⇒ **无牙齿行**,保留作显式域注释;③ 正×正(`lit_empty_pair:118`)同类异身份加强 = **单列候选**(经 `ty_inhabited → ty_exhaustive` 达 match 穷尽性 TM03 生产面,本批不碰)。 +- **执行面**:主树当时含他任务在途改动(树不可编译)⇒ 本任务在**仓库外隔离 workspace**(`jj workspace add -r e5ce8f9e`)完成;提交内容 = 本任务两文件 + `src/ci/run.sh` 计数注 + 本计划勘误。 + +--- + +## Task 3:E-1 β——`.ccr` NOD 盘面项索引 + `CCR_VERSION` 8→9 + corearch `atom_of` 读回 + +**Files:** Modify `src/compiler/ccr_io.cr`(版本常量、`ESZ_NOD_DISK`、NOD 写/读、段界检)· `src/compiler/ccr_types.cr`(save 前把项索引装填进 NOD 语义对象;`ccr_seg_prepare_save` 的 D23 复用)· `src/compiler/ent_kernel.cr`(`ESZ_NOD_SEM`/`OFF_NS_*` 若扩槽)· `src/compiler/main.cr`(corearch 侧读回路径:项索引 → 取项 → `sh_atom_of_term` → 派生码;**若** corearch 发射路径读的是 `iri_tk`,则加「装载期解析一次」的时点)· `src/compiler/cir_cache.cr`(**仅当** Step 1 裁 (ii) 时才动——见下)· `tests/selfhost/test_ccr_types.py`(结构断言重锁:版本 9 / NOD 40B / 项索引落盘/读回对拍 / 版本拒收)· `tests/selfhost/test_ccr_v7.py`(若记录值受影响)· `src/ci/run.sh`(计数注)· `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(NOD 记录 §3.3 + 版本族)。 + +**Interfaces(**Step 1 必须先裁两处,不得沿用附录 E 的数字当已决**):** +``` +// ── 先裁项 U-2:NOD 记录布局(附录 E-1 写「36B → 40B」= +4B 只容一个字段, +// 而该项同时点名「项索引 + 辅码」两个量)—— +// L-a: 既有 tk 字段保持「派生码」(辅码面节点即靠它)+ 新增 4B「项索引」(-1 = 无)⇒ 40B +// L-b: 既有 tk 字段语义改为「项索引 ∪ 辅码」单槽(依 D22-② 互斥,一槽可承载二者之一;分类表判读)⇒ 36B 不变 +// 判据:D20「纯函数值与其入参并存 = 第二真源」——L-a 存「码 + 索引」两者 ⇒ 须给 D20 合规论证; +// L-b 无并存但读侧须能判定槽内是项还是辅码(依赖分类表 + 行可译性) +// ── 先裁项 U-3:本任务是否一并收口「暖态项面」(P5 T2 §5-④:缓存命中跳过 ir_gen ⇒ 暖态项表更小 ⇒ 项不可重建) +// (i) 只落盘面/corearch 面(不动快照)⇒ 暖态项面继续登记; +// (ii) 同批给 .cir 快照加项索引面 ⇒ 必须 bump CIR_CACHE_VER 17→18 + 记失效面 +// 推荐:(i)((ii) 收益 = 暖态项槽完整,无在野消费者;且 (ii) 把格式批从 .ccr 扩到 .cir 双格式) +``` +- [ ] **Step 1: 两处先裁 + 布局表 + 版本拒收面**(U-2/U-3 落纸 + 逐字段布局表 + `load_ccr` 的 `!=9` 拒收语义(照 D10:v8 整类拒收,不静默当空表)+ 失效面清单:在野 `.ccr`/缓存条目整体作废)。判据:布局表逐字段有依据 + D20 合规论证 + 失效面登记。 +- [ ] **Step 2: 用例(红)≥10 例**:① 版本闸(v8 文件拒收 rc=1 + 诊断,**不得**静默);② NOD 记录尺寸/字段序(读回逐字段);③ **项索引落盘/读回对拍**(同一 `.ccr` 的 NOD 项索引 → TYPE 段取项 → `sh_atom_of_term` → 码 ≡ 盘上派生码);④ 辅码面节点(`IR_BOUNDS_CHECK` 旗标 1 / `IR_DEREF` 宽度 8)在版式下的保真;⑤ 复合行节点(`ptr_ref_first` BINARY tk=10)保真;⑥ 类型面节点 `atom_of` 回读 ≡ 旧派生码(全语料逐节点);⑦ 越界项索引 ⇒ 拒绝(三态,不得静默);⑧ 段界检(记录增长后的 `seg_end3` 核算);⑨ corearch `--dump-objects` 逐字节同;⑩ `CIR_CACHE_VER` 按 U-3 裁定(不动 17 / 或 18)。 +- [ ] **Step 3: 实现**(写侧装填 + 读侧解析 + corearch 读回;**单源**:项索引与派生码同出 `emit` 期的那一次拆分,不得二次构造)。 +- [ ] **Step 4: 判据(硬闸)**:**ELF canary IDENTICAL**(变即停下)· **`--dump-objects` 逐字节同**(两口径)· `.ccr` 两口径四条**预期变更**(版本/NOD 记录)⇒ **重锁记录值 + 逐段 delta 归因**(照 P3a T4/T5 的「逐段实测」先例)· `test_ccr_types` ≥40 → 实测值 · `test_ccr_v7` 全绿 · `selftest-types` ≥404 · 五 CI job + 全枚举 + `check src/compiler` rc=0 + 自举链 + `test_backend_bootstrap` · **冻结基线同源对拍 72 档零差异**(判定面不受格式面影响——**这是本任务最关键的隔离判据**)· **突变控制 ≥3 条**(项索引写错槽 / 越界项不拒 / corearch 读回退化为直接读盘上码 ⇒ 各自转红)+ 精确回滚。 +- [ ] **Step 5: 文档**:format spec(NOD 记录 + 版本族 + D20 形态说明)· spec §6.3 状态行(**β ✅**)· `docs/error-codes.md`(若新增拒绝码)· TODO 条目。 +- [ ] **Step 6: 提交**:`feat: R2 P6 Task 3——β:.ccr NOD 盘面项索引 + CCR_VERSION 8→9 + corearch atom_of 读回(发射面零泄漏)` + +**停条件**:① U-2 两候选**都无法同时满足** D20 合规与读侧可判定 ⇒ 停下上报(须维护者裁布局);② 版本 bump 若**被迫**同时动 `.cir` 快照格式(U-3 = (ii))而超出批准面 ⇒ 停下(单独裁决);③ canary 或 `--dump-objects` 变化 ⇒ 停下(发射面泄漏);④ 段界检/读回出现任何「静默当空表」形态 ⇒ 停下(三态纪律)。 + +### 实施记录(R2 P6 Task 3,2026-09-14;**本批已实施**) + +**先裁(Step 1 门)**: +- **U-2 = L-a**(40B = 32B 语义区 + 8B 邻接;新字段 `item i32` @+28,邻接域顺移 +32/+36)。L-b **排除**(单槽「项索引 ∪ 辅码」+ 分类表判读 = 读侧不可判定——分类表恰需被丢弃的派生码;反例 = `tests/suite/ptr_ref_first.cr` 的 `IR_BINARY tk=10`)。L-a 的 D20 合规论证三条:① 跨边界引用零新增(只引入文件内项索引 = D20-② 唯一许可形态);② 「码 = 派生量」不变(两值同源于 `ccr_types.cr:ccr_nod_item_populate` 单次装填);③ **分歧即硬拒**(`load_ccr` 的 TYPE 段后一致性校验:域外 / `atom_of(项) ≠ tk` ⇒ 拒绝 rc=1——消灭「盘面值静默胜出」的危害模型)。**登记未取**:更严单源形态 **L-c**(旧槽改载辅码、码完全装载侧重派生——无并存,但改发射路径输入 ⇒ canary/`--dump-objects` 由构造性降为须实测);若维护者取 L-c,本轮写侧装填/校验/回读通道可整体复用(改动面 = 写侧旧槽写 0 + corearch 重派生 + 拒绝面收窄)。 +- **U-3 = (i)**(协调者已裁):只落盘面/corearch 面,`.cir` 快照不动、**`CIR_CACHE_VER` 保持 17**。依据 = 项索引只在 `ccr_type_populate()` 重建后的**文件空间**成立,而 `.cir` 写点(`main.cr:553`)早于该空间 ⇒ (ii) 只能落进程内索引 = D20-③ 禁令 + `bad_term=329` 红证;且快照已载派生码,保存期重派生即得 ⇒ (ii) 零收益。 + +**Files 勘误(实施面实际 = 计划 Files ✚ 补)**: +- ✚ `src/compiler/type_terms.cr`——`tt_atom_of_term` 自 `ty_shadow.cr` **整函数迁入**(corearch 清单不含桥接层,而 `load_ccr` 校验与回读通道需要它;B.6 三面核:两清单**均已**含本文件 ⇒ 无清单增删)。 +- ✚ `src/arch/x86_64/regalloc.cr`——**L-a 下零改动**(corearch 读回实体 = `corearch.cr:243`(`--dump-objects`)+ `regalloc.cr:877`(`iri_set_tk(ni, nod_tk(ni))` 发射侧码落点);计划把读回挂在 `main.cr` 是**文件归属错**)。 +- ✚ `tests/selfhost/test_region_cfg.py`(`:283` 硬算式 36→40 + `:316` 版本断言 8→9——计划漏列的第三/四个格式断言点)。 +- ✚ `src/compiler/ccr_types.cr` 的 D18 **强制**承载(写侧装填必须在 corec-only 文件;`ccr_io.cr` 在 corearch 清单内,不得直调桥接层——新增静态断言 `test_p6t3_d18_purity_static` 守护)。 +- ✚ `src/compiler/corearch.cr`(`--dump-nod-items` 通道)。 +- **路径勘误**:计划写的 `src/compiler/ent_kernel.cr` 实为 **`src/lattice/ent_kernel.cr`**。 + +**Step 3 时序改写(实读矛盾 ⇒ 落地形态)**:计划原文「**单源:项索引与派生码同出 `emit` 期的那一次拆分,不得二次构造**」**不可照字面实施**——`ccr_type_populate()`(`ccr_types.cr:54-81`)在 save 前 `tt_layer_reset()`+`sh_map_reset()` 并 按行序重建 ⇒ emit 期(`sh_tk_split`/DF 两槽)的项引用保存前**整体作废**;文件空间只在 `ccr_seg_prepare_save()` 之后成立。**实施口径**:写侧 = **保存期**由盘上派生码经**同一桥接函数**(`sh_tk_face_of_code` 分类 + `sh_term_of_ti` 建项)重派生(分类/建项单源,无第二套逻辑);与 `.cir` 装载侧重派生(`cir_cache.cr:43-46/:521-523`)同构。**突变 M3 即此改写的机器抓手**(改用 `OFF_DF_TK` 活表索引 ⇒ loader 拒 ⇒ 转红)。 + +**锚点漂移(实施时实读,行号以实施树为准)**:`run.sh` 计数注 `:82/:86 → :85/:89`(T1 改写后)· `dyn_arr.cr` 契约注 `:104-107 → :103-118` · 计划 Task 3 Files 的 `main.cr`(见上)· `ent_kernel.cr` 路径(见上)。 + +**判据(全绿,逐条实测见 TODO #51 / `.superpowers/sdd/p6-task3-report.md`)**:canary IDENTICAL · `--dump-objects` 两口径逐字节同 · `.ccr` 四条重锁 + **delta = 4B×节点数**(pa +5192 = 4×1298;gt +7452 = 4×1863;口径差恒 143B)· 72 档对拍零差异 · 探针 29 档零差异 · 五 CI job + 枚举 58/58 + selftest 404/404 + `check src/compiler` rc=0 + 自举链 · `test_ccr_types` 40→48 / `test_ccr_v7` 27/27 / `test_region_cfg` 22/22 · 突变 M1–M5 转红 + 精确回滚。 + +**残留(登记)**:暖态项面为**部分**(缓存命中跳过 ir_gen ⇒ 暖进程未分配的类型行落 `item = -1`,码由 `tk` 保真);冷态完整、冷/热各自确定性不变。 + +--- + +## Task 4:E-11 `never` 调用返回型(独立)+ E-12 调用位点诊断(挂裁-P6-2) + +**Files:** Modify `src/compiler/checker.cr`(调用推断的返回型映射:被调返回行 = `TI_NEVER` ⇒ 调用推断 `TI_NEVER`;E-12 半:`unify_types` 返回值接线 + 非泛型调用位点定位 + 错误标记/级联抑制,照 T6 豁免③先例)· Modify `src/compiler/main.cr`(E-12 硬名单行——**仅在裁-P6-2(c) 批复后**)· Modify `docs/error-codes.md` · New `tests/selfhost/test_call_check.py`(若裁-P6-2 批复)/ Modify `tests/selfhost/test_let_check.py`(`never` 面)· Modify `src/ci/run.sh`。 + +- [x] **Step 1: RED(E-11,本任务自跑,双侧同证)**:`fn boom() -> never { loop { } }` + `x : int = boom();` ⇒ 现状 **TA02 误发**(机制:调用推断 = unit(`:1491` 钳位)⇒ 声明位豁免③ `:526` 不触发 ⇒ `type_compat_strict(TI_UNIT, TI_INT)` = 0 ⇒ `:530`);`return boom();` ⇒ 现状 **TF01 误发**(机制:`EXPR_RETURN` 推断返调用值 = unit ⇒ `body_ti = TI_UNIT` ⇒ `:2234` 的 `body_ti != TI_NEVER` 成立 ⇒ `:2240`);冻结基线**同值**(预存)。**另记**:本改动**必破**一条既有套件钉(`test_iface_ops.py` 的 `t6_S1_never_unit` 今期望 TF01@2)⇒ 裁① 重钉。**实测(RED,合流树 `9cfa6246` + 未打补丁二进制)**:直调声明位 rc=1 + `error[TA02]: Variable declared as int, got unit` · 直调返回位 rc=1 + `error[TF01]: Function return type mismatch` · 变量传播位同 TF01 · 方法站点 rc=1 + TA02 · 死分支 build rc=1 + 无产物。**E-12 面**:`fn take2(n: int)->int` 以 `take2("s")` 调用 ⇒ 现状 rc=0 零诊断(TODO #20 实证复跑;**锚点实核**:非泛型调用位点**今天不存在**——`EXPR_CALL` 的 SYM_FN 非泛型分支连实参推断都不走,E-12 实施 = 在该分支**新增**判定点)。 +- [x] **Step 2: E-11 实现 + 用例 ≥6**:返回型映射补 `TI_NEVER`(**只改推断结果,不改发射**);用例 = 两站点误报消除 + 正控(真异型仍报)+ 级联(`x : int = nosuchfn()` 仍只发 N06)。**实测(T4a 口径)**:落点 = 直调站点 + 方法姊妹站点两处守卫(后者含 `fi_generic_count == 0` 对齐;extern 面由 `EXPR_FN` 限定排除——裁④ 必需件);**14 条用例**(主判据 5:声明位/返回位/变量传播/死分支 build+run/方法站点;正控 5:真异型 2 既有 + near-miss 负控 + 级联 N06 既有 + 语句位宽面;登记钉 4:泛型调用/extern/泛型方法/`EXPR_ASSIGN` TA01 保持)+ **重钉 1**(`t6_S1_never_unit` 改判 rc=0)。套件计数:`test_let_check` 23→(实读 24,注释陈旧自纠)→ **28** · `test_tf01_fallthrough` 19→ **22** · `test_iface_ops` 76→ **78**。 +- [x] **Step 3: E-11 判据**:新/改套件全绿 · **ELF canary IDENTICAL**(**`never` 调用后的可达性/代码消除 = 本轮不做**;若 canary 变 ⇒ 停下,说明改到了发射面)· `.ccr` 四条冷态逐字节同 · 五 CI job + 全枚举 + 自举链 + `check src/compiler` · 冻结对拍**逐档裁决**(本任务预期出现**收紧面的反向** = 误报消失类 diff,逐条入台账)· 突变控制 ≥2(去掉 `TI_NEVER` 透传 ⇒ 两站点复红)。**实测**:canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条 = T3 新锁值(`ptr_arith` 96015B `680a6f98…` / 96158B `76f36e6a…`;`generics_test` 142793B `41e9d845…` / 142936B `704316c8…`;差恒 143B)· 三套件 28/28 · 22/22 · 78/78 · **72 档对拍零差异**(误报消失类 diff 的载体 = **套件 leg**,不在 72 档语料;`parity_run.sh` 覆盖面实读)· 突变 **Mu1–Mu5**(见下方实施记录)。 +- [ ] **Step 4: E-12 前置门(**须裁-P6-2**)**:报 report-only 清单(全语料 + 58 套件内联源 + examples),零命中 ⇒ 按裁定开门;非零 ⇒ 停下(清单审查:放宽 / 修语料 / 停)。**未取裁 ⇒ 本步止于 report-only 报告**(不发诊断、不开门),登记于 TODO。**实施锚点(T4a 侦查实读)**:新增判定点 = `EXPR_CALL` 的 SYM_FN 非泛型分支内(方法面同形块同款);参数型取法二选一——`fi_param_type(fi, pi)`(自带越界护栏)或 EXPR_PARAM 链导航(照 `infer_gen_call` + 前扫,注意「形参不连续」教训 #21);泛型面丢弃点与使用点维持实读值(零漂移)。 +- [x] **Step 5: 提交**(分段):`fix: R2 P6 Task 4a——never 调用返回型(两站点误报消除;发射面零变化)` / `fix: R2 P6 Task 4b——调用位点实参判定接线(TF07 复用 + report-only 清单;裁-P6-2 批复面)`。**本批只落 4a**(4b 挂裁-P6-2,未取裁 ⇒ 不实施)。 + +**停条件**:① E-11 使 canary 变化 ⇒ 停下(可达性变更超章程;归因入口 = `ir_gen.cr:1662-1663` 的 `new_ir_var("call", call_ti)`);② E-12 的 report-only 命中面涉及**语义裁决**(如泛型体内实参位是否判)⇒ 停下上报;③ 命中面过大(需改自举源码)⇒ 转独立批;④ **E-11 落成后发现任何一处「泛型 / extern / 模块 / 赋值位」面被一并改动**(裁④ 越权)⇒ 停下(尤其 extern:守卫④ 缺失时 `t6_S3_never_unit` 会静默转绿——**该转绿是越权信号,不是收益**)。 + +--- + +## Task 5:文档/卫生批(E-7 残项 + E-8 + E-16) + +**Files:** Modify `tests/selfhost/test_type_engine.py`(`MIN_CASES` 32 → **实测值 415**)· Modify `tools/module_to_ccr.py`(**文件头标废弃**:写 version=1 的远古 `.ccr`、零 in-repo 调用者、当前 corearch 整类拒收 ⇒ 使用该工具产出的文件必被拒)· Modify `docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md`(**5 处**:`:371` 三陈旧——`CIR_CACHE_VER` 旧值 14 → **17**、`cir_cache.cr` 行号 `:16` → **`:51`**、**magic `C1C1…` 分句删去**(该断言与实测不符:自举二进制落盘 magic 全 0,P4 T5/TODO #14 登记);`:84`/`:181` 旗标计数 `8 → 7`(`--type-shadow-dump` 已删);`:127`/`:447` 先例载体改写)· Modify `docs/superpowers/specs/2026-09-11-policy-injection-design.md`(`:275` 同款改写)· Modify `TODO.md`(#53 条目 + 删除候选登记)· Modify 本计划(本节 + 侦查底盘陈旧锚,见下)。**`src/ci/run.sh` 零改动**(实施时逐条实测复核:其全部 `N 例` 注与实测一致——T2/T3/T4a 已各自同步;T1 提到的「14 档挂点」陈旧计数在**本计划**,不在 `run.sh`)。 + +- [x] **Step 1: `MIN_CASES` 收紧**:`:20` → **415**(实测)+ 注释写「只增不减(收紧为下限=实测;新增用例须同步下界)」。判据:`test_type_engine.py` 绿。**实测**:`PASS test_selftest_types_all_pass`(rc=0)。 +- [x] **Step 2: 陈旧件**:`tools/module_to_ccr.py` 头注废弃(**不删**——删件属独立决定;登记「删除候选」入 TODO #53)· `pkg/` **不属入库面**(`.gitignore` 覆盖 ⇒ 仅报告登记,**不进提交**)· 两 spec 的陈旧数字/行号/magic 分句修正。 +- [x] **Step 3: 先例引用改写(E-16)**:`explain-predict` 4 处(`:84`/`:127`/`:181`/`:447`)+ `policy-injection` 1 处(`:275`)→ 「`--verify-*` 家族(默认关)+ 行为探针/结构性断言」,原则不变(审计通道默认关 / 两态产物同);每处**保留历史陈述**(通道曾存在 + 已被 P5 T5 下线),只换现行载体。 +- [x] **Step 4: 复核**(T5 已实跑,结论见下「实施记录」):`type-shadow` **无活跃引用**(`src/` 8 + `tests/` 3 = 全为「已下线/历史」注;`docs/` 64 中仅本任务两 spec 的 5 处为修靶);陈旧版本值在 `docs/ src/ tests/` **零引用**(修靶后)。 +- [x] **Step 5: 判据**:五 CI job rc=0 + 全枚举 58/58 + `check src/compiler` rc=0 + canary IDENTICAL + `.ccr` 四条 = T3 新锁值 + 72 档同源对拍零差异 + `test_type_engine.py` 绿 + `test_backend_bootstrap` rc=0 + `error[`=0;**重建 ⇒ 三二进制逐字节同**(= 零行为变化的构造性证据,见下)。 +- [x] **Step 6: 提交**:`docs: R2 P6 Task 5——文档/卫生批(MIN_CASES 收紧至 415 + module_to_ccr 废弃标注 + 两 spec 陈旧值与 --type-shadow 先例载体改写 + 计划勘误)` + +**停条件**:① 若 `grep` 出 `--type-shadow` 的**非历史**活跃引用(代码/CI)⇒ 停下(影子层下线有残留);② 若改 `MIN_CASES` 暴露实测值 < 32 ⇒ 停下;③ 若 `test_purity.py` 的 `MIN_CASES`(同形面)与本任务产生耦合(维护者要求同批收紧)⇒ 单独裁决(它不在 E-7 点名内)。**三条均未触发。** + +### Task 5 实施记录(2026-09-14;工作树 = 合流树 `/tmp/p6mrg`(head `9bdbdf16`);证据 `/tmp/p6t5/`) + +**形态**:`MIN_CASES` 32 → **415**(+「只增不减」注)· `module_to_ccr.py` 头注废弃块(版本 1 / `ccr_io.cr:135` `CCR_VERSION = 8` 整类拒收 / 零 in-repo 调用者 / 保留不删)· `explain-predict` **5 处**(含 **magic 分句删去**)· `policy-injection` 1 处 · `TODO.md` #53 + 删除候选 · **计划勘误**(本节 Files/Steps 实况化 + 侦查底座陈旧锚 + T1 记录一句不精确)。 + +**判据(全部本实例实跑)**:**重建 ⇒ `corec ae01de75…` / `corearch 228f82e9…` / `corelsp 90eb19c6…` 与编辑前逐字节同**(T5 改动零 `.cr` ⇒ 二进制度不变 = 零行为变化的构造性证据)· 五 CI job 全 rc=0 · 全枚举 **58/58**(66s)· `selftest-types` **415/415** · `check src/compiler` rc=0 · canary **`95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 = T3 新锁值(96015B `680a6f98…` / 96158B `76f36e6a…` / 142793B `41e9d845…` / 142936B `704316c8…`,差恒 143B)· **腿① 同源对拍 72 档零差异**(冻结基线 `/tmp/p6t1/base/corec` × 当前源 vs 当前二进制 × 当前源;rc 分布 34×0/38×1 两侧同)· `test_type_engine.py` 绿(MIN_CASES=415)· `test_backend_bootstrap` rc=0 + `error[`=0 · 自举链 `corec2==corec3` IDENTICAL(`0b2e06d0…`)+ N06=0 + 冒烟 42 + `--help` rc=1。 + +**grep 复核(本实例实跑)**:`type-shadow`:`src/` 8 条 + `tests/` 3 条**全为「已下线/历史」注**(无旗标注册/无调用点——注册面三处实核 = `main.cr` / `corearch.cr` / `targets/x86_64-linux/main.cr`,`--type-shadow*` 零在位);`docs/` 64 条中 5 处为本任务修靶、其余为历史计划/冻结记录(`type-shadow-findings.md` 已自 §16 冻结)。陈旧版本值(14)在 `docs/ src/ tests/` **零引用**;`MIN_CASES` 修靶后 = `test_type_engine.py:20`(415)+ 邻域 `test_purity.py:27`(26,**登记不并入**)。 + +**登记(不静默)**:① `tools/module_to_ccr.py` **删除候选**(本任务只标废弃;删除 = 独立决定)· ② `test_purity.py` 的 `MIN_CASES = 26`(同形面,未纳入 E-7)· ③ **T0 缺失实施记录归 T6**(T5 只登记该事实,**不动 T0 节**——理由:章程边界 / 与 T6 的「统一台账」同类 / 避免计划文件被两批各改一次)· ④ **T2 无 TODO 条目**(#50=T1 / #51=T3 / #52=T4a;建议 T6 补或由 T2 自补)· ⑤ 「14 档挂点」陈旧文本在**计划**(已随本任务勘误),`run.sh` 内无此计数(实核 `grep -n '\b14\b'` = 0)。 + +--- + +## Task 6:收官(回归 + 判据 + 台账 + 文档 + 尾批终态) + +- [x] **Step 1: 全量回归**(cwd = 仓库根、`nice -n 19`、串行、比较前 `clean-cache`):五 CI job 全 rc=0;全枚举 58/58(+ 本批新增套件);`check src/compiler` rc=0;`selftest-types` 终值;构建确定性 ×2。**实测**:五 CI job 全 rc=0 · 全枚举 **58/58** · `check src/compiler` rc=0 · `selftest-types` **415/415** · 构建 ×2 **逐字节同**(`corec`/`corearch`/`corelsp` sha 两次比对 IDENTICAL)。 +- [x] **Step 2: 判据复验**:**用入仓配方重建的基线**(T1 产物)跑同源对拍(72 档 rc + 日志逐档 diff)· 探针全集(入仓语料)· 突变控制快照 · ELF canary · 自举链(`cmp` IDENTICAL + N06=0 + 冒烟 42 + `--help` rc=1)· `.ccr` 两口径(冷态、带标;T3 后为新锁值)· `--dump-objects` · `.cir` DOT。**实测**:`tools/baseline/rebuild.sh` rc=0 ⇒ 三 sha = 白名单(+ 冒烟 42)· 腿① 72 档`diff -rq` **零输出**(两侧 34×0/38×1)· 探针两态 **零差异**(29 档,17×0/12×1)· canary **IDENTICAL** · 链 `0b2e06d0…` + N06=0 + 冒烟 42 + `--help` rc=1 · `.ccr` 四条 = T3 新锁值 · `--dump-objects` **3521 行** · `.cir` DOT **165915B** `b1bdf480…` · `test_backend_bootstrap` rc=0 + `error[`=0。 +- [x] **Step 3: 统一台账**:逐任务列 {实施 / 删除 / 收紧 / 放宽 / 修复 / 登记 / 偏差}——**与各任务的实际 diff 逐条对应**;四裁定(裁-P6-1..4)的**取裁结果与落地情况**单列一节;未取裁项列「已登记、未实施」。**交付** = 本计划「统一台账」节(7 小节;KPI = 实施 6 · 删除 0(+1 候选)· 迁移 1 · 收紧 3 · 放宽 1 · 修复 1(+1 并记)· 登记 19 · 偏差 9 · 未取裁 7)。 +- [x] **Step 4: 文档**:spec §9 加 **P6 行(尾批)**:实施项 / 判据 / 剩余登记;spec §6.3(β ✅ 或「未实施」按 T3 实际);format spec(版本族按 T3 实际);TODO 新条目(P6 收官条目 + 四裁定结果 + 转独立批登记);`CLAUDE.md`(若 T1 引入 `tools/baseline/` 或 `tests/probes/` ⇒ 在相应节补一行;`.cr` 清单无变化则不写)。**实测**:§6.3(β ✅)与 format spec 版本族(v9 行)**T3 已落**,本任务只加 **§9 P6 行** + **TODO #54** + `CLAUDE.md` 一行(`tools/baseline/` + `tests/probes/` 已入仓,属测试/工具面新增 ⇒ 补一行;`.cr` 编译清单零变化)。 +- [x] **Step 5: 尾批终态陈述(一页)**:类型接口化工作流 = P0–P6 全阶段终态;「已实施面 / 已裁定面 / 剩余登记面(含四裁定的未取裁者)」三栏;**下一批 = 容量批或验证切片轮**(按裁-P6-1 结果指向)。**交付** = 本计划「尾批终态陈述(一页)」节。 +- [x] **Step 6: 提交**(路径限定;逐任务提交已在各 Task 内)。 + +**判据**:五 CI job + 全枚举 + canary + 自举链 + 同源对拍 + 探针 + 台账逐条可溯源 + 文档指针链闭合(spec §9 P6 行 → 本计划 Task 6 → 统一台账)。**全部满足**(实测见 Step 1/2)。 + +### Task 6 实施记录(2026-09-14;工作树 = 合流树 `/tmp/p6mrg`(head `4b909996`);证据 `/tmp/p6t6/`) + +**形态**:T6 收口(**零 `.cr` 改动**——纯文档/台账面):全量回归 + 判据复验 + 统一台账(7 小节)+ spec §9 P6 行 + TODO #54 + `CLAUDE.md` 一行 + **Task 0 记录回填**(补记)+ 尾批终态陈述。 + +**判据(全部本实例实跑,证据 `/tmp/p6t6/`)**:构建 ×2 **逐字节同**(`sha1.txt` == `sha2.txt`,`cmp` IDENTICAL)· 五 CI job 全 rc=0(`check`/`bootstrap-tests`/`suite`/`selfhost-tests`/`full-bootstrap`)· 全枚举 **58/58**(66s)· `check src/compiler` rc=0 · `selftest-types` **415/415** · **ELF canary `95084e7b…d475`(28822B)IDENTICAL** · **入仓配方重建**(`tools/baseline/rebuild.sh`)rc=0 ⇒ 三 sha **逐条 = 白名单**(`5d2b15ad…`/`493dc490…`/`18b94bd9…`)+ 冒烟 42 + 工作区零残留 · **腿① 同源对拍 72 档零差异**(`diff -rq` 零输出;两侧 rc 分布 34×0/38×1)· **探针 29 档两态零差异** · `.ccr` 两口径四条 = **T3 新锁值**(96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…`;差恒 143B)· `--dump-objects` **3521 行** · `.cir` DOT **165915B** `b1bdf480…` · 自举链 `corec2 == corec3` IDENTICAL(`0b2e06d0…`)+ N06=**0** + 冒烟 **42** + `--help` rc=**1** · `test_backend_bootstrap` rc=0 + `error[`=**0**。 + +**突变控制快照(继承 + 本批实跑,不重跑全表)**:T1 5 红(M1 语料掉档 / M2 伪条目 / M4 cwd / M5 rc 通道 / **M7 源级判定放宽 ⇒ 12 档 DIFF、5 档 rc 1→0**)+ 1 诚实未红(M3 去 clean-cache:`check` 面不读 `.cir` 缓存 ⇒ 本腿不可检出)· T2 M1/M2a/M2c′/M3(M2b 不作判据、M3b 仅记)· T3 M1–M5(写槽填派生码 / 删校验 / 活表索引 / 版本闸放水 / 只删交叉校验)· T4a Mu1–Mu5(两站点守卫 + 条件必要性 + extern 越裁探测)——**四任务各自的突变集逐条转红 + 精确回滚**,出处 = 各任务报告。 + +**停条件核**:**零条触发**——全部判据与协调者给定基线逐条相符(canary / `.ccr` 四条 / 链 sha `0b2e06d0…` / 415/415 / 58/58 / 72 档零差异)。 + +**待维护者处置项(不在本任务职权内,只登记)**:① **主树 `/home/DslsDZC/core` 工作副本 9 未提交文件**(`ccr_io.cr`/`ccr_types.cr`/`corearch.cr`/`globals.cr`/`ty_shadow.cr`/`type_engine.cr`/`type_selftest.cr`/`type_terms.cr`/`ent_kernel.cr` = T2 的两份过时草稿 + T3 抢跑残留);② **主树 `build/*` 为 T3 味**(`corec aa7bdd5d…`/`corearch 28c5d669…` ≠ P6 起点白名单)⇒ 任何判据不得引用;③ **最终整合**(让主树采用本批终态 `4b909996` 链)涉及工作副本还原 ⇒ **须维护者明确许可**(铁律 #3)。 + +--- + +## 统一台账(P6 全批 T0–T5;**Task 6 收口**) + +> 口径(继承 P5):每件给**证据**(为何敢动)与**替代/覆盖**(现在谁覆盖);删除/收紧件与实际 diff 逐条对应;全语料命中数一律与**同任务起点**基线逐条比。**KPI**:实施 **6** · 删除 **0**(+1 候选)· 迁移 **1** · 收紧 **3 类** · 放宽 **1 类** · 修复 **1 件**(+1 并记)· 登记 **19** · 偏差 **9** · **未取裁 7**。 + +### 1. 实施(6 件;逐件 = 交付 / 判据 / 出处) + +| # | 件 | 提交 | 交付 | 判据(实测) | +|---|---|---|---|---| +| 1 | **T0 前置侦查 + 冻结基线重建**(源码零改动) | 计划定稿 `bc4f0a43` 内 | 四表(A 重建 / B 起点 / C 语料与探针清点 / D 锚点)+ 守门复验 + `/tmp/p6t0/base` | 重建三 sha = 白名单 + `cmp` 与 `/tmp/p5t0/base` IDENTICAL ×3 + 冒烟 42;**U-4 = 29 档**;表 D 5 漂移 + 1 码名口径;守门复验(`test_optional` 34/34 · `test_enum_limit` 13/13 · 越界 P22/P23 硬错) | +| 2 | **T1 回归网可复现化(E-13)** | `e5ce8f9e` | `tools/baseline/{REBUILD.md,rebuild.sh,parity_run.sh,probes_run.sh}`(配方 + 白名单 + 双 runner;**零二进制入库**)+ `tests/probes/`(29 档实拷 + README 来源表)+ `run.sh` 自述块 + TODO #50 | rebuild 41s ⇒ 三 sha = 白名单;入仓 vs 迁移 runner(同二进制)72 档零差异;腿① 零差异;探针两态零差异 + rc/log 29/29 同 P5 T7;五 CI job + 58/58 + canary + 链;**突变 5 红 + 1 诚实未红**(M3 去 clean-cache:`check` 面不读缓存 ⇒ 不可检出) | +| 3 | **T2 ¬ 面过判收口(E-10)** | `911c692d`(兄弟;经 `9bdbdf16` 落地) | `type_engine.cr` +59 行(`atom_same_class_disjoint` + 接线;`ak_disjoint` 本体零改动);`selftest-types` 404 → **415**(+11) | canary IDENTICAL · `.ccr` 四条 = T1 值 · `--dump-objects` 3521 行 · `.cir` DOT 165915B · 五 CI job + 58/58 · 链 · **72 档/探针零差异**(含对 T0 起点仅两自源档行号偏移的可归因)· 突变 M1/M2a/M2c′/M3(M2b 不作判据,M3b 附加证据) | +| 4 | **T3 β(E-1)** | `0819d506`(兄弟;经 `9bdbdf16` 落地) | NOD 记录 **36B → 40B**(+28 = TYPE 段文件空间项索引 `item i32`;`tk` 槽仍 = 派生码)+ `CCR_VERSION` **8 → 9**(旧版整类拒收)+ loader **一致性硬校验**(域外 / `tt_atom_of_term(项) ≠ 盘上码` ⇒ rc=1)+ corearch `--dump-nod-items`;`sh_atom_of_term` → 共享层 `type_terms.cr::tt_atom_of_term`;`test_ccr_types` 40 → **48** | canary IDENTICAL · `--dump-objects` 两口径同 · `.ccr` 四条**重锁**(96015 `680a6f98…`/96158 `76f36e6a…`/142793 `41e9d845…`/142936 `704316c8…`;delta = 4B×节点数;口径差恒 143B)· 72 档/探针零差异 · 三格式套件 48/27/22 · 突变 M1–M5 五条转红 | +| 5 | **T4a `-> never` 调用返回型(E-11)** | 与合流同在 `9bdbdf16` | `checker.cr` 两站点守卫(直调 + 方法调用;四条守卫含 `EXPR_FN` 限定);用例 +9(`let_check` → **29** · `tf01` → **22** · `iface_ops` → **78**);重钉 1(`t6_S1_never_unit` 由误报 rc=1 改判 rc=0) | canary IDENTICAL · `.ccr` 四条 = T3 值 · 72 档零差异(**载体在套件 leg**)· 探针零差异 · 五 CI job + 58/58 · 链 `0b2e06d0…` · 突变 Mu1–Mu5 五条 | +| 6 | **T5 文档/卫生(E-7/E-8/E-16)** | `4b909996` | `MIN_CASES` 32 → **415**;`module_to_ccr.py` 废弃头注(**不删**);两 spec **8 处**(三陈旧 + magic 分句删去 + 旗标 8→7 + 先例载体 ×3);TODO #53 + 计划勘误 | **重建 ⇒ 三二进制与编辑前逐字节同**(零 `.cr` 改动 = 零行为变化的构造性证据)· grep:`--type-shadow` 无活跃引用 · 陈旧版本值零引用 · 五 CI job + 58/58 + canary + `.ccr` + 腿①/探针零差异 | + +### 2. 删除 / 迁移(0 + 1 + 1) + +| # | 件 | 任务 | 证据 | +|---|---|---|---| +| — | **净删除 0 件**(本批无删件——P6 是「落地 + 收口」批,非删旧批) | — | — | +| 1 | **迁移**:`sh_atom_of_term` 整函数 → `type_terms.cr::tt_atom_of_term`(共享层;因 corearch 清单不含桥接层) | T3 | 调用面机械改名(`type_selftest.cr` 7 处)+ 构建 rc=0;突变 M3 守护「save 期重派生」硬约束 | +| 2 | **删除候选(登记未删)**:`tools/module_to_ccr.py` | T5 | 写 version=1 远古 `.ccr`(`wu32(1)`)⇒ 现行 corearch 整类拒收;零 in-repo 调用者 | + +### 3. 收紧(3 类;全语料命中逐条 = 0) + +| # | 形态(前 → 后) | 任务 | 全语料命中 | 牙齿 | +|---|---|---|---|---| +| 1 | ¬ 面:同类原子**异身份**「判不含(0)」→「**确定不交(1)**」(仅 `AK_SUM` 变体项;`AK_NAMED`/参数化/`⊤ₖ` 面**维持原语义**) | T2 | **0**(72 档 rc/日志零差异 + 探针 29 档零差异;生产 `ty_exhaustive`/`ty_disjoint` 不受影响——正×正路径未动) | 突变 M1(撤销加强 ⇒ 三例 `*_over_claim` + 两例转红)· M2a(类域守卫去掉 ⇒ `&int` vs `&mut int` 误判)· M2c′(不可判出口改判 ⇒ 空链误判) | +| 2 | `.ccr` 版本闸:v8 → **v9**(`CCR_VERSION 8→9`)⇒ 旧版**整类拒收**(照 D10 先例,不静默当空表) | T3 | 0(语料/探针在 v9 下自洽;`test_ccr_types` ㊳ 钉拒收面) | 突变 M4(版本闸放水 `!=`→`>` ⇒ 转红) | +| 3 | `.ccr` loader **一致性硬校验**:项索引域外 / `tt_atom_of_term(项) ≠ 盘上码` ⇒ **拒绝 rc=1**(把 D20 的「第二真源」从静默分歧降级为可证伪不变量) | T3 | 0(L-a 布局下写侧单一来源 ⇒ 构造上一致) | 突变 M2(删校验 ⇒ 越界/不一致项被静默接受)· M5(只删交叉校验 ⇒ 域内非原子项被接受) | + +### 4. 放宽(1 类) + +| # | 形态(前 → 后) | 任务 | 全语料命中 | 覆盖 | +|---|---|---|---|---| +| 1 | `-> never` 函数的**调用**:推断 unit → **`TI_NEVER`** ⇒ 声明位 `x : int = boom()` 不再误发 TA02、返回位 `return boom()` 不再误发 TF01 | T4a | 72 档**零差异**(误报类 diff 的载体 = **套件 leg**:`t6_S1_never_unit` 由 rc=1+TF01@2 改判 rc=0) | 用例 +9 三套件;突变 Mu1–Mu5;裁④ 明确不动的同族面(泛型调用 / extern / 模块限定)登记 | + +### 5. 修复(1 件 + 1 件并记) + +| # | 形态(前 → 后) | 任务 | 根因 + 判据 | +|---|---|---|---| +| 1 | 两站点**误报**(TA02@声明位 / TF01@返回位)→ 零误报 | T4a | 声明注册趟的域守卫把 NEVER 钳成 unit(`never` 在 parser 侧走裸码路径)⇒ SYM_FN 类型 = unit ⇒ 两站点误报;判据 = 三套件 +9 例 + 突变 Mu1/Mu4 单点归因 | +| 2 | **并记**:¬ 面过判(witness 项 `B ⊆ ¬R` 判 0 → 1) | T2 | 语义过判;**生产不可观测**(T3b §5-② 推导)⇒ 归「收紧/正确性加强」(§3-#1),不单列修复件 | + +### 6. 登记(19 件;逐条 = 归属 / 状态) + +| # | 件 | 归属 | 状态 | +|---|---|---|---| +| 1 | `tools/module_to_ccr.py` 删除候选 | T5(TODO #53) | 登记未删 | +| 2 | `test_purity.py` `MIN_CASES = 26`(与 E-7 同形面) | T5(TODO #53) | 登记未改 | +| 3 | **T2 无 TODO 条目**(#50=T1/#51=T3/#52=T4a) | T6(本台账) | 待补(建议 #55;或并入 #54 两行指针) | +| 4 | `tt_display_at` **参数链渲染退化**(带链原子一律渲染 `>`;裸原子正常;影响 ICE04/约束反例诊断的参数化类型显示) | T2 报告 §6-2(**预存缺陷**,本批不改——`ty_shadow.cr` 非任何任务面且改诊断文本会动对拍面) | 建议入 TODO | +| 5 | `EXPR_ASSIGN`(赋值位)无 never 豁免 | T4a → TODO #52 | 已登记 | +| 6 | 暖态项面**部分**(缓存命中跳过 ir_gen ⇒ 未分配行 ⇒ `item = -1`,码由 `tk` 保真;冷态完整) | T3 → TODO #51 | 已登记 | +| 7 | E-5 `.csr`/TagNode 图消费面(零调用者) | 验证切片轮 | 维持登记 | +| 8 | E-15 单条目 >1GiB 分片(零真实用例) | 本批 | 维持登记 | +| 9 | **E-12 / #20** 调用位点诊断(「非泛型调用位点」实为**不存在** ⇒ 实施 = 新增判定点;锚点已入计划 Task 4) | 裁-P6-2 | 已登记、未取裁 | +| 10 | E-6 iterable 接线 / 命名消费语法 / 形状可写视图 | 裁-P6-4 | 已登记、未取裁 | +| 11 | 容量批(E-2 对象布局表示位 + E-3 `MAX_*` 解除 + E-4 可选程序收回 `.cir` 缓存) | 裁-P6-1 | 已登记、未取裁(推荐转独立批) | +| 12 | E-15 `string == int` 混比语义(双路径分歧) | 裁-P6-3 | 已登记、未取裁 | +| 13 | 非硬名单诊断不阻 `build`(TA01/TG02 等) | 裁-P6-2(b) | 已登记、未取裁(T0 语料内已有活证据:`ptr_arith` check rc=1 B04 而 build 出 canary) | +| 14 | `--verify-named-dedup` 保留(偏离 P5 D27) | 裁-P6-2(e) | 已登记、未取裁 | +| 15 | `*T ← 0` / `*T ← None` 是否作为 null 初始化放行(P5 T6 回退面 = 两站点) | 裁-P6-2(d) | 已登记、未取裁 | +| 16 | **T2 正×正面候选**(`lit_empty_pair`:正×正同类异身份 ⇒ 不交;语义成立但经 `ty_inhabited → tt_witness → ty_exhaustive` 达 **TM03 生产面**) | 裁(单列候选) | 已登记、未取裁 | +| 17 | **U-1** `tt_display` 零 `str_intern`「改造」的原始意图(源码已满足;P4 文档无出处) | 本批 | **无法追溯**(建议维护者补一句,否则按「已满足」关闭) | +| 18 | **U-2 的 L-c 备选形态**(更严的单源形态) | T3 §1.2 / 本计划 | L-a 已裁(授权面内,**可被维护者推翻**);L-c 登记未取 | +| 19 | **主树遗留**:工作副本 9 未提交文件(T2 过时草稿 + T3 抢跑 7 文件)+ `build/*` 为 T3 味(`aa7bdd5d…`/`28c5d669…` ≠ 起点白名单) | **待维护者处置**(涉还原 ⇒ 铁律 #3 须明确许可) | 已登记(本台账 + T6 报告) | + +### 7. 偏差(9 条;逐条 = 现象 / 处置) + +| # | 现象 | 处置 / 出处 | +|---|---|---| +| 1 | **事故:T3 抢跑**(未取裁前把 T3 实作落进共享工作副本 7 文件;停手指令后仍在写 ⇒ `TaskStop` 硬停) | 零提交、零还原;T2 隔离到独立 workspace 收尾;T3 冻结后重跑。**护栏缺失(任务卡未写「先裁前不得落码」)记在协调者账上** ⇒ 后续含「先裁」的卡把护栏写进正文。出处 = `.superpowers/sdd/p6-incident-2026-09-14-t3-preempt.md` | +| 2 | **提交形态**:T2 `911c692d` 与 T3 `0819d506` 为**兄弟提交**,经合流态 `9cfa6246` 由 T4a 的 **`9bdbdf16` 双亲合并提交**一次性落地(未拆「合并 + T4a」两段) | 协调者已裁决**接受**(树内容正确,不做 rebase/split)。出处 = T4a 报告 §6-1 | +| 3 | **T4a 过程事故**:CI job **执行期间**改 `src/ci/run.sh` ⇒ bash 增量读取截断(`line 137: unexpected EOF`,rc=2) | 源**冻结**后重跑全部五 job(§4 的 5/5 = 重跑结果)。出处 = T4a 报告 §6-2 | +| 4 | **T3 计划偏离三处**:① Step 3 时序(计划「同出 emit 期拆分」→ 实为**保存期重派生**,突变 M3 守护);② Files 漏项(`type_terms.cr` / `regalloc.cr` / `test_region_cfg.py`);③ corearch 读回**归属错**(计划挂 `main.cr`,实为 `corearch.cr:243` + `regalloc.cr:877`) | 逐条落纸;T3 报告 §6-1/§6-2 | +| 5 | **T1 计划偏离四处**:① `probes_run.sh` 为计划外增补(探针腿无 runner 则仍空洞);② 「14 档挂点」陈旧计数在**计划**而非 `run.sh`;③ **CI 不挂钩**(浅检出 `fetch-depth: 2` + 无 `jj`);④ 「探针源已丢」初判被 T0 更正(`p5t3b/probes` 是输出目录) | 逐条落纸并已修正;T1 报告 §9 | +| 6 | **T5 计划偏离**:计划 Files 写「Modify `src/ci/run.sh`(计数注;若有变)」——实测**无变**(T2/T3/T4a 已各自同步) | 按「无变则不动」执行;改在计划文件(陈旧锚的真实所在) | +| 7 | **T0 记录未回填**(只出报告、不入库 ⇒ 计划内零留痕) | **T6 补记**(本计划 Task 0 实施记录 + 六步勾选;归属裁定 = T6) | +| 8 | **T2 执行面隔离**(主树当时含 T3 在途改动、树不可编译 `Undefined name: sh_atom_of_term` ×9)⇒ 全程在仓库外 workspace(`jj workspace add -r e5ce8f9e`) | 主树零改动/零提交/零还原;协调者合流。T2 报告 §6-6 | +| 9 | **T2 首版实现缺陷**(实错与修正:首版加强的负控全绿而缺陷未暴露 ⇒ 靠**新增 ⑩** 才抓出;负控由下限 4 提到 **11**) | 落纸并强化用例;T2 报告 §3.3/§6-5 | + +--- + +## 未决项与登记(**不猜**;Task 6 收口后状态) + +- **U-1(`tt_display` 零 `str_intern` 改造的原始意图)**:附录 E-7 首项写「`tt_display` 零 `str_intern` **改造**(P4 继承)」,但当前源码 `tt_display_at` **已**是只读零驻留(`ty_shadow.cr:535-536` 头注 + P5 T4 §4-9 的实测归因),P4 计划/报告中**未检出**该「改造」条目的原始出处 ⇒ **本计划无法确定**它要改什么。**处置**:Task 5 只做「现状复核 + 守门用例(`x2.no_str_intern` / `isat.no_str_intern_on_missing` / `ifc.mangling_zero_str_intern`)复核」,**不预设改造**。**状态 = 无法追溯**(维护者若知原意请补一句;否则按「已满足」关闭)。 +- **U-2(T3 布局先裁)**:**已裁 = L-a**(40B = 旧 `tk` 槽保留派生码 + 新增 4B 项索引 + 装载期一致性硬校验),该先裁在授权面内(计划 Step 1 原文即「L-a … 须给 D20 合规论证」)。**可被维护者推翻**;更严的 **L-c 形态登记未取**(事故记录 §4 + T3 报告 §1.2 末)。 +- **U-3(暖态项面是否同批收口)**:**已裁 = (i)**(只落盘面/corearch 面;`.cir` 快照不动、`CIR_CACHE_VER` 保持 17)⇒ **暖态项面为部分**(未分配行 ⇒ `item = -1`,码由 `tk` 保真)——残留登记(TODO #51)。 +- **U-4(探针「30 档」的账)**:**已收口 = 29 `.cr`**(T0 表 C:11+18+0;「30」= 29 + 迁移 runner 的字面 glob 伪条目)。「30」此后不得引用(T1 已修 runner + 入仓语料)。 +- **四裁定门(裁-P6-1..4)状态**:**全部未取裁**(逐条 = 「裁决门」节;未取裁项一律未实施)。 +- **登记维持(不做,理由写死)**:E-5 `.csr`/TagNode(消费者属验证切片轮)· E-15 分片(零真实用例)· `tools/module_to_ccr.py` 删除(本批只标废弃;候选登记)· `ty_shadow.cr` 文件名与 `sh_` 前缀改名(P5 T5 §7-2 登记,收益仅命名)· `test_purity.py` `MIN_CASES`(同形面,未纳入 E-7)。 + +--- + +## 尾批终态陈述(一页;P0–P6 全阶段;维护者先读) + +**一、已实施面(本批交付,全部实测判据)** + +| 面 | 终态 | 出处 | +|---|---|---| +| 回归网(判定面) | **四腿齐备且有载体**:① 冻结基线同源对拍(**入仓配方 `tools/baseline/rebuild.sh` + sha 白名单**;三 sha 重建可复现)② 行为探针(**入仓 29 档** `tests/probes/` + runner)③ 突变控制 ④ 三态纪律(ICE04/D23/版本闸) | T1 + 本计划「Global Constraints」 | +| 载体(`.ccr`) | **v9**:NOD 40B 承载 **TYPE 段文件空间项索引**(+ `tk` 派生码保持)+ loader 一致性硬校验 + corearch `--dump-nod-items` 读回 ⇒ 项引用**跨进程可解析**(spec §6.3 兑现) | T3 / spec §6.3 | +| 判定引擎 | ¬ 面同类原子异身份 ⇒ **确定不交**(`AK_SUM` 变体项);`AK_NAMED`/参数化/`⊤ₖ` 面维持原语义(政策不动) | T2 | +| 调用位点推断 | `-> never` 调用 ⇒ `TI_NEVER`(两站点误报消除;同族三面按裁④不动) | T4a | +| 回归判据终值 | `selftest-types` **415/415** · 全枚举 **58/58** · canary `95084e7b…d475`(28822B)IDENTICAL · 自举链 `0b2e06d0…` + N06=0 + 冒烟 42 · `.ccr` 四条 = 96015/96158/142793/142936(差恒 143B)· 72 档对拍零差异 · 探针 29 档零差异 | T6 §Step 1/2 | +| 文档/工具 | `MIN_CASES` 收紧至 415(只增不减)· `module_to_ccr.py` 废弃标注 · 两 spec 陈旧值与先例载体改写(`--type-shadow` → `--verify-*` + 行为探针) | T5 | + +**二、已裁定面**:P5 裁-1(β 归 P6)**已兑现**(T3)· 裁-4(容量批 P6 合批)→ **现提请改判**(见下)· P5 D20/D22/D23/D28 与 TODO #26 判据口径**全批未动**(继承有效)· U-2 = L-a(授权面内,可推翻)· U-3 = (i)。 + +**三、剩余登记面**(19 件,逐条 = 「统一台账」§6;其中影响下一批指向的四条) + +1. **裁-P6-1 容量批**(E-2 对象布局表示位 + E-3 `MAX_*` 解除 + E-4 可选程序收回 `.cir` 缓存):**推荐转独立批**(= 新能力面;在野需求 0;四条前置无一满足)。**下一批指向 = 容量批(若裁「实施」)或验证切片轮(若裁「转批」)**。 +2. **裁-P6-2 政策五问**:ICE04/P-A 签核 · 非硬名单诊断是否收紧 · 入闸判据成文化 · `*T ← 0/None` · `--verify-named-dedup` 保留——**全未取裁**(E-12 的落地挂此)。 +3. **裁-P6-3 / 裁-P6-4**:`string == int` 混比语义 · `EXPR_FOR`/iterable 是否入批——**全未取裁**。 +4. **待维护者处置(涉铁律 #3 还原面)**:主树工作副本 9 未提交文件 + 主树 `build/*` T3 味 ⇒ **最终整合须明确许可**。 + +**四、多 agent 事故与护栏(记在账上)**:T3 抢跑(`.superpowers/sdd/p6-incident-2026-09-14-t3-preempt.md`)——**零提交、零还原、零文件丢失**;护栏措施 = 「含先裁的任务,护栏写进任务卡正文」;T4a 的 CI 期间改源事故(rc=2)⇒ 处置 = 源冻结后重跑五 job(已入台账 §7)。 + +--- + +## 自检记录 + +- **分诊的诚实性**:16 项**未**凑数——7 项实施中有 2 项(E-7 的 `tt_display` 子项、E-8 的 `pkg/`)**实测后缩水为「复核/登记」**(前者已满足、后者非入库面),1 项(E-15 分片)明确降为登记(零用例);7 项待裁**逐条点名问句**(裁-P6-1..4),未用「推荐」冒充裁定;唯一「新能力」性质的实施项(β)**有既有裁定背书**(P5 裁-1 + spec §6.3)并附两处**先裁项**(U-2/U-3)。 +- **根因 vs 止血**:① T1 是把判据腿从 `/tmp` 里**救出来**(实测证据 = `p5t3b/probes` 的 `.cr` **已丢**、runner 对空目录**静默空跑**——不修则「零差异」会退化为**空洞证据**,违三态纪律);② T2 直达 `ak_disjoint` 的相斥判据(不在 `sub_cover` 加特例);③ T3 走 D20 的**许可形态**(TYPE 段文件内项索引),**不**落进程内索引(`bad_term=329` 红证);④ T4a 只改**推断结果**、明令发射面零变化(不做可达性消除——那是另一个语义变更)。 +- **与既有裁决的一致性复核**:spec §6.3(β 前置已齐 → T3)· §6.4/§10(E-5 归验证切片轮)· P5 裁-1/裁-4/裁-2/裁-3 的边界(β 属 P6、容量批转独立批、命名面判定化不得回退、影子层不得复活)· D20(稳定引用禁令)· D28(两口径 + 冷/热豁免)· TODO #26(判据口径,不得回退「逐字节同旧版」)· #24/#30/#32/#35/#43/#44/#45–#49 的已收口面**一律不动**。 +- **占位符扫描**:无 TBD;「待实测」标签逐处给出测量命令或出处;三处**先裁/未决**(U-1 无法追溯、U-2/U-3 需 Task 3 内先裁、U-4 需 Task 0 清点)明写,**未预填数字**。 +- **数字口径(全部实读或既有记录值;出处已标)**:P5 判据终值(`p5-task7-report.md` §0/§2)· 冻结基线三 sha(**本实例 sha256sum 实核**)· runner sha `12dc571f…`(实核)· 探针计数 29 `.cr`(本实例 find 实核)+ `p5t3b/probes` 零源(实核)· `CCR_VERSION=8`/`ESZ_NOD_DISK=36`(`ccr_io.cr:135/:143`)· `CIR_CACHE_VER=17`(`cir_cache.cr:51`)· `ESZ_DFNODE=72`/`OFF_DF_TK=40`/`OFF_DF_AUX=64`(`dyn_arr.cr:119/:122/:124`)· `MIN_CASES=32`(`test_type_engine.py:20`)· `MAX_*`/镜像 64(`ast.cr:115-118/:143-145`)· 硬名单八条(`main.cr:152-174`)· `unify_types` 丢弃点(`checker.cr:2000`)· TF07 单 raise(`checker.cr:3683`;`ast.cr:423`)· `EXPR_FOR`(`checker.cr:2929-2940`)。**凡「≥N 例」皆为下限**。 +- **本计划的自我限制(诚实登记)**:本计划**未跑任何构建/测试**(纯只读侦查 + `sha256sum`/`find`/`ls`/`jj status` 级实核);所有「现状值」均为实读源码/文档或既有报告记录值;需实测的数字(重建 sha、探针账、β 的记录值 delta)一律留给实施任务的 RED 步。**U-1 无法确定**(原始意图不可追溯)、**U-2/U-3 留给 Task 3 先裁**、**U-4 留给 Task 0 清点**——均已显式登记,未猜。 +- **风险面**:① T3 的格式级联(本批最大风险)——缓解 = Step 1 先裁 + 版本闸(整类拒收)+ 隔离判据(判定面对拍零差异)+ `--dump-objects` 逐字节同 + 突变三条;② 基线重建不可复现(⇒ E-13 前提崩,T0 停条件)——缓解 = T0 首步实测 + 与 `/tmp` 副本 `cmp` 交叉;③ 探针重建失真——缓解 = 逐档「重建依据」注 + 与 P5 记录 rc/诊断逐条比 + 对拍腿自证;④ 收紧面(T4b)命中非空——缓解 = report-only + 裁-P6-2 前置 + 停条件;⑤ 多 agent 并发——路径限定提交 + 一构建一编译串行。 diff --git a/docs/superpowers/plans/2026-09-15-capacity-batch.md b/docs/superpowers/plans/2026-09-15-capacity-batch.md new file mode 100644 index 00000000..2ad48b4e --- /dev/null +++ b/docs/superpowers/plans/2026-09-15-capacity-batch.md @@ -0,0 +1,415 @@ +# 容量批(CAP)实施计划:E-2 对象布局表示位 + E-3 `MAX_*` 定长槽解除 + E-4 可选程序收回 `.cir` 缓存 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 兑现 P6 计划「裁决门」裁-P6-1 所指的三项(维护者已裁 = **转独立批**,本批 = 该三项的落地批):① **E-2**——把可选运行期表示面从「局部 / 形参 / 返回 / 调用结果」扩到**聚合面**(结构体字段 / 数组元素 / 全局槽 / match 结果):现状 = 裸值形态 ELF 与解释器**双路径同 rc=-11**(响亮失败,非静默),装箱形态正确;② **E-3**——解除 `src/compiler/ast.cr:115-118` 三上限(`MAX_STRUCT_FIELDS` / `MAX_ENUM_VARIANTS` / `MAX_VARIANT_TYPES`,皆 16):解除 = **记录布局迁侧表**(P5 附录 E-3 登记的方向),四条前置须逐条收口;③ **E-4**——收回「可选程序关 `.cir` 缓存」的临时门(`src/compiler/main.cr:488-490`),依赖 E-2。 + +**边界纪律(容量批 = 新能力面)**:本批引入**新表示语义**(聚合面可选值)与**新记录布局**(进程内符号表迁侧表)。凡未取裁的裁决门项**一律不实施**(对应任务止于 report-only / 登记);任何「顺带」的新能力(iterable / 命名消费语法 / `.csr` 消费面 / `MAX_GENERICS` / 盘面格式改版)**出界**。**本批不做清单**见「本批不做」节。 + +**Architecture:** 四条主线:① **T1 前置侦查 + 冻结基线重建 + 起点判据 + 三面守门复验**(源码零改动;含 E-2/E-3/E-4 的现状 RED 复现与锚点实核);② **T2 = E-2**(表示面聚合扩展;**先裁 = 运行期表示选择律**,见裁-CAP-1);③ **T3 = E-3**(记录布局迁侧表;**先裁 = 上限策略**,见裁-CAP-2);④ **T4 = E-4**(`.cir` 缓存收回;**先裁 = 收口面**,见裁-CAP-3;**机制须先 RED 实证**——本计划实读**未能复现** P4 报告所述的分歧机制,见裁-CAP-3 的「机制存疑」条);⑤ **T5 收官**(全量回归 + 统一台账 + 文档 + 终态)。 + +**Tech Stack(实施面锚点,行号 = 本工作树实读,非 P6 计划转抄)**:`src/compiler/ir_gen.cr`(表示面全域:侧表 `g_ir_var_rep` :214-241 · `opt_rep_slot` :251-259 · `global_decl_optional` :261-277 · `callee_ret_optional` :279-295 · `rep_enc_of_expr` :297-330 · 未覆盖面自陈注 :207-208 · 写点 :1147-1184 / :2105-2172 / :2209-2210 / :2312 / :2346 / :2369 / :2488 · 解包点 :1987-2018 / :2044-2064 / :2442-2461 · `optrep_begin` :2835-2846 / `optrep_prescan` :2848-2861 / `reg_optrep_globals` :2878-2900 · match 结果 :1960/:2076/:2087)· `src/compiler/globals.cr`(`g_ir_var_rep` :63 · `g_optrep_on` :67 · 信道单元 :70/:73 · `g_cur_ret_opt` :75 · 表示码注 :76-80)· `src/compiler/ast.cr`(`MAX_*` :115-118 · 镜像 struct :130-165 · 错误码 `EC_P_TOO_MANY_PARAMS` :370 / `EC_P_ENUM_LIMIT` :372 / `EC_P_STRUCT_LIMIT` :373)· `src/compiler/dyn_arr.cr`(记录尺寸 :56-58 · FuncInfo 偏移 :90-94 · StructInfo 偏移 :97-100 · 枚举偏移 :231-243 · 受护访问器 :462-549)· `src/compiler/parser.cr`(P020 :1448-1461 · struct 写入 :1634-1640 + P023 :1648-1652 · enum 写入 :1695-1716 + P022 :1724-1733)· `src/compiler/ccr_io.cr`(`CCR_VERSION` :135 · SYM 布局注 :312-332 · struct/enum 写 :706-747 · 读回闸 :1203/:1245/:1272 · 清零循环 :1208-1219/:1250-1265)· `src/compiler/ty_shadow.cr`(`sh_match_bit` :1204-1213 · `sh_match_first_missing` :1217-1226)· `src/compiler/checker.cr`(覆盖位消费 :3012-3040,位运算 :3036)· `src/compiler/main.cr`(缓存门 :473/:477-490/:532/:553)· `src/compiler/cir_cache.cr`(`CIR_CACHE_VER=17` :51 · 版本族注 :7-49 · 身份字段 :57-90 · 头部 :214 · 装载闸 :436-468)· `tests/selfhost/test_optional.py`(34 例;未覆盖面 :347-352)· `tests/selfhost/test_enum_limit.py`(13 例)· `tests/selfhost/test_cir_warm_path.py`(21 例;可选面 C 组 :277-287)· `tools/baseline/{rebuild.sh,parity_run.sh,probes_run.sh,REBUILD.md}` · `tests/probes/`(29 档)· `src/ci/run.sh`(selfhost-tests 挂点 28 档)。 + +**Spec / 起点文档**:`docs/superpowers/specs/2026-09-10-type-interface-unification-design.md` **§9 P6 行**(:288 末句「**下一批指向 = 容量批 或 验证切片轮**(按裁-P6-1 结果)」)· **P5 计划附录 E**(`docs/superpowers/plans/2026-09-13-r2-p5-cleanup.md:472-474` = E-2/E-3/E-4 原文)· **P4 计划附录 D-3-7/8/9**(`docs/superpowers/plans/2026-09-12-r2-p4-carrier.md:387-389`)· 判定面纪律继承 = **P6 计划 Global Constraints**(`docs/superpowers/plans/2026-09-14-r2-p6-tail.md:80-93`)逐字有效,本计划据实改写为容量批版本。 + +--- + +## 前置状态(本实例实核 / 实读;未标注者 = 本实例实读) + +- **工作树 = `/tmp/p7wt`**(jj workspace;`.jj/repo/` 为空目录 ⇒ 共享主仓 store)。**本实例实核**:无并发构建/编译/测试(`ps aux | grep -Ei 'corec|corearch|build_selfhost|python3 .*test'` 零命中)。 +- **本计划未跑任何构建 / 测试 / 编译**(硬纪律;见「自检记录」末条)。所有「现状」= 实读源码/文档或既有报告记录值;一切需实测的数字标「须 T1 实测」。 +- **⚠ 修订面勘误(本实例实读证据)**:提示词给定「基线 = P6 终态提交 `97f4394f`」,但本工作树**含 P6 记录之后的修复**,逐条证据:`tests/selfhost/test_params_limit.py`(TODO #8:`src/compiler/dyn_arr.cr:89` `MAX_FN_PARAMS : int = 64;` + `/tmp/p7wt/src/compiler/parser.cr:1452` `if pc > MAX_FN_PARAMS {`)· `tests/selfhost/test_tuple_slots.py`、`test_agg_slots.py`、`test_agg_checks.py`(TODO #25/#28/#29 族)· `tests/selfhost/test_cache_identity.py` + `src/compiler/cir_cache.cr:26-29`(v17 = 编译器身份字段,TODO #5)· `tests/selfhost/test_nested_fn.py`(TODO #16)· `tests/selfhost/test_interp_parity.py`(TODO #11)。⇒ **P6 计划/报告的记录值(`.ccr` 四条、`selftest-types` 计数、自举链 sha、构建确定性 sha)不得直接当本批起点**(见「起点基线值」节:逐条标「须 T1 重测」)。本实例**无法**以 `jj` 核验工作树修订与 `97f4394f` 的一致性(硬纪律:不得跑 jj)⇒ **留给 T1 实核并登记**。 +- **报告位置漂移**:提示词写报告在 `/tmp/p7wt/.superpowers/sdd/`——**该目录在本工作树不存在**(`.superpowers/` 被 `.gitignore` 覆盖 ⇒ 不入 jj workspace 检出)。报告实体在 **`/home/DslsDZC/core/.superpowers/sdd/`**(本实例实读在位:`p6-task0-report.md`、`p6-task1-report.md`、`p6-task3-report.md`、`p6-task4-report.md`、`p6-incident-2026-09-14-t3-preempt.md` 等)。本计划的报告产物同样落该处(不入提交)。 +- **既有安全网(本批起点不变量,不得弱化)**:E-2 的未覆盖面当前 = **响亮失败**(`tests/selfhost/test_optional.py:347` `case_dual_rc("uncovered_field_bare_is_loud", UNCOVERED_FIELD_BARE, -11)`);E-3 的越界写入当前 = **定位硬错 + 无产物**(`tests/selfhost/test_enum_limit.py:189-210`,用户面实印 `error[P22]`/`error[P23]`)。两者都是「不得退化为静默错值」的判据承载。 + +--- + +## 附录 E 三项分诊表(**本计划第一产物**;逐条 = 现状 / 影响面 / 依赖 / 前置 / 处置) + +> 分诊口径:**(a) 实施** = 本批实施面(逐任务落地);**(b) 待裁** = 须维护者答复(逐条点名问句见「裁决门」);**(c) 维持登记** = 本批不做,理由写死。**不得**用本表把待裁项偷渡成实施项。 + +| # | 项 | 现状(出处 = 本实例实读) | 影响面 | 依赖 | 前置 | 处置 | +|---|---|---|---|---|---|---| +| **E-2** | 对象布局表示位 | 表示位机制存在且仅覆盖四类槽:局部(写点 `ir_gen.cr:2105-2108`/`:2171-2172`/`:1147-1149`)· 形参(`:2537-2546`)· 返回与调用结果(`:1745-1762`/`:2209-2210`/`:2578-2598`)· 解包点 = match 臂(`:1987-2018`/`:2044-2064`)与 `?`(`:2442-2461`)。**聚合面未配位**(代码自陈注:`ir_gen.cr:207-208`「未覆盖面(结构体字段/数组元素/全局槽/match 结果/惰性 thunk):不配表示位 ⇒ 解包走**既有**装箱假定」;`:261-262`「globals 无表示位 ⇒ 视作未覆盖面」)。逐站点:字段写 `:1160-1164`(EXPR_ASSIGN)/`:2346`(EXPR_STRUCT)/枚举载荷 `:2312`;字段读 `:2230`;数组元素写 `:1177`/`:1184`/`:2369`/`:1314`,读 `:2287`/`:2295`;全局槽写 `:1152-1155`/`:2735`,读判定 `:262-277` + `:322-327`(可选全局恒 `oe_boxed()`);match 结果 `:1960`(`result_var` 无 `irv_set_rep`)/`:2076`(臂写)/`:2087`(merge),消费端落到 `:329` `return oe_boxed();` | ir_gen 写点约 8 处 + 解包点 2 处 + 类型判定辅助 1 处;**纯 IR 面 ⇒ ELF 与解释器天然同源**(`globals.cr:57-60` 明撰,须保持);若取裁为 packed 位图/stride 案 ⇒ 再触**目标程序**数据布局(`type_size`/`type_align`/ELF 发射/解释器/`@sizeOf` 面),发射面可能变 | 无(独立);与 E-3 共享「容量」裁决面但**不共触**同一布局(见「锚点漂移 #6」) | **成立且是起点安全网**:未覆盖面 = 双路径同 rc=-11 响亮失败(`test_optional.py:347`),非静默 | **(a) 实施**(挂**裁-CAP-1**) | +| **E-3** | `MAX_*` 定长槽解除 | 三上限 = `ast.cr:116-118`(`:115` `MAX_GENERICS=4` 属另一类);**只约束进程内定长记录**——`ESZ_STRUCTINFO=440`/`ESZ_ENUMINFO=4408`(`dyn_arr.cr:57-58`),字段槽 16(`OFF_SI_FIELD_NAMES=8`→`OFF_SI_FIELD_TYPES=136`→…,`:97-100`),变体槽 16×272(`OFF_EI_VARIANT_COUNT=4360`,`:237-243`);写入侧 = `parser.cr:1634-1640`(struct)/`1695-1716`(enum)+ 末尾硬错 P023 `:1648-1652` / P022 `:1724-1733`;读回侧 = `ccr_io.cr:1203`/`:1245`/`:1272`(`> MAX_* ⇒ rc=1`)+ 清零循环 `:1208-1219`/`:1250-1265`(硬编码 16/4);覆盖位 = `ty_shadow.cr:1207-1213`(`sh_match_bit` 循环乘 2)+ `checker.cr:3036`(`m_cover` 单 int) | 访问器调用点 **54 处**(本实例 grep 计数:`ty_shadow.cr` 16 · `checker.cr` 19 · `ir_gen.cr` 9 · `lsp/analysis.cr` 6 · `interp.cr` 2 · `type_selftest.cr` 2 · `ccr_io.cr` 10 · `provenance_verify.cr` 1 · `arch/x86_64/instr.cr` 1)——**受护访问器单源 ⇒ 签名不变则调用点零改动**;真改动面 = `dyn_arr.cr`(记录 + 访问器 + 侧表)· `parser.cr`(写入循环)· `ccr_io.cr`(读回闸/填充)· `ty_shadow.cr`+`checker.cr`(覆盖位)· `ast.cr`(常量 + 镜像);判据面 = `test_enum_limit.py`(13 例)重钉 | 无(可独立于 E-2 实施) | **四条逐条结论见下节**(结论:①未满足 ②未满足且比登记值更宽 ③未满足 ④须重锁/重设) | **(a) 实施** 或 **(c) 维持登记**(在野需求 0);按**裁-CAP-2** | +| **E-4** | 可选程序收回 `.cir` 缓存 | `main.cr:484-487` 注 + `:488-490` `if g_optrep_on != 0 { cache_enabled = 0; }`(文件路径专用:批处理路径 `ir_gen_all` 不经缓存——本实例 grep 实核 `load_cir_cache`/`save_cir_cache` 仅 `main.cr:532`/`:553` 两调用点);快照面 = `cir_cache.cr`(头部 48B+name :214 · vars 24B :215 · NOD 64B :219 · edges 32B :220 · instrs 48B :221 · SG 48B :232;版本 `CIR_CACHE_VER=17` :51;五闸 :446-457);判据面 = `test_cir_warm_path.py`(可选面 C 组 `:277-287` = 两跑产物同 + **零条目**) | 缓存命中面(编译耗时);若快照格式变 ⇒ 版本 bump + `tools/baseline` 判据面同步 | **E-2**(表示面定案后才知道哪些状态须跨进程可重建) | **未验证**:P4 报告陈述「命中恢复的函数解包点回落装箱路径」——本实例静态枚举**未能复现该机制**(`irv_rep`/`irv_set_rep` 全部读取点 = `ir_gen.cr:304`/`:1148`/`:1987`/`:2443`,**均在同函数生成内**);⇒ 机制**须实施时 RED 实证或证伪**(见裁-CAP-3) | **(a) 实施**(挂**裁-CAP-3**);机制证伪且无分歧 ⇒ 按 **(c)** 收口并登记 | + +**分诊计数**:实施 **3**(E-2 · E-3 · E-4,各自挂裁决门)/待裁 **0 项整条**(但三条各含设计裁决,见「裁决门」3 主问 + 3 附问)/维持登记 **0**(E-3 若取裁 (c) 则转登记)。**本计划不把任何一条提前判死**:三条皆有在野收益(E-2 = 消除响亮失败面;E-3 = 解除能力上限;E-4 = 可选程序编译耗时)与明确成本(见各任务)。 + +--- + +## E-3 四条前置逐条实核(**逐条结论**;P5 附录 E-3 登记的「前置四条」) + +| 前置 | 登记内容(P5 附录 E-3) | 本实例实核结论 | 缺什么 | +|---|---|---|---| +| ① 读回闸同步 | 「读回闸(`ccr_io.cr` 的 `> MAX` ⇒ rc=1)」 | **未满足**。实读三闸:`:1203` `if fc > MAX_STRUCT_FIELDS { println("error: .ccr struct field count exceeds max"); return 1; }` · `:1245`(`vc > MAX_ENUM_VARIANTS`)· `:1272`(`tc > MAX_VARIANT_TYPES`);且清零循环硬编码槽数(`:1208`/`:1250`/`:1256`/`:1260` 的 `>= 16`、`:1219`/`:1265` 的 `>= 4`)。解除后若不改:**读回侧仍整类拒绝** >16 的文件、填充仍只覆盖 16 槽 | 三闸改为**动态填充**(或按新上限)+ 清零循环改由侧表形态决定;**注**:盘面格式**已是变长**(`ccr_io.cr:324`「structs: struct_count + {name, field_count, fields×8B}」/`:332`「enums: … variants×{name, tc, types×4B}」;`ccr_sym_seg_size` 用 `si_field_count(sti)` 动态求和,`:309-336`)⇒ 盘面**无需**为解除改版 | +| ② `ast.cr` 镜像归位 | 「`StructInfo.field_*` 镜像 64 vs 布局 16 **已不一致**」 | **未满足,且实读比登记值更宽**。逐条:`ast.cr:143` `field_names: [string; 64]` / `:144` `field_types: [int; 64]` / `:145` `field_type_nodes: [int; 64]` vs 布局 16 槽(`OFF_SI_FIELD_TYPES-OFF_SI_FIELD_NAMES = 128B = 16×8`)**✗**;另**三处登记外不一致**:`ast.cr:147` `StructInfo.generic_names: [string; 16]` vs 布局 4 槽(`OFF_SI_GENERIC_COUNT-OFF_SI_GENERIC_NAMES = 32B`)**✗**·`ast.cr:154` `EnumVariant.types: [int; 64]` vs 布局 16 **✗**·`ast.cr:164` `EnumInfo.generic_names: [string; 16]` vs 布局 4 **✗**。(相符者:`ast.cr:133` `param_types: [int; 64]` = `MAX_FN_PARAMS=64`;`:136` FuncInfo generic 16 = 布局 16;`:162` `variants: [EnumVariant; 16]` = 布局 16) | 镜像归位(或**删除候选**——本实例全仓 grep:`FuncInfo`/`StructInfo`/`EnumInfo` 在 `ast.cr` 之外**仅注释命中**,无类型使用点 ⇒ 是否为死声明**须实施时实证**,含 bootstrap 侧对 struct 声明的处理) | +| ③ 覆盖位表示 | 「`sh_match_bit = 2^vi` 走 int,>63 变体溢出」 | **未满足**。`ty_shadow.cr:1204-1213` 头注自陈「覆盖位:bit(vi) = 2^vi……vi < 63 由 MAX_ENUM_VARIANTS = 16 保证不溢出——**该前提自 R2 P4 Task 6 起由写入侧护栏强制**」;实现 = 循环乘 2。消费点 `checker.cr:3033-3040`:`bit := sh_match_bit(ci)` · `m_cover = m_cover + bit`(**单 int 累积**)· 冗余臂判据 `(m_cover / bit) % 2 != 0`;下游 `sh_match_first_missing(cover_bits, count)`(`ty_shadow.cr:1217-1226`)按 `(bits / b) % 2` 逐位判 | 覆盖位**表示更换**(位图/侧表/多字)+ 两消费点(`:3036` 与 `sh_match_first_missing`)+ 冗余臂判据同批;**>63 变体在现表示下必然溢出** ⇒ 即使只把上限提到 64 也不可(63 = i64 位宽边界,非 16 的问题) | +| ④ 冷热 `.ccr` 与 ELF canary 判据 | 「冷热 `.ccr` 与 ELF canary 判据」 | **未满足(= 判据须重锁/重设)**。本批若只迁**进程内**记录:`.ccr` 四条**预期不变**(盘面逐字段照写,`ccr_io.cr:706-747`);canary **预期 IDENTICAL**(记录布局不进发射面)。**两值都必须以 T1/T3 实测为准**,不得预填;若实测有变 ⇒ 逐段 delta 归因 + 走裁-CAP-2 附问 3(版本面) | 判据重锁表 + 归因链;冷/热口径按 D28(两命令口径差恒 143B + 冷/热各自确定性;比较前 `clean-cache`) | + +**结论汇总**:四条前置**无一满足**(与 P5/P6 登记「四条前置未满足」一致);其中 ② 的实际缺口**大于**登记值(多 3 处镜像不一致),③ 的边界**严于**登记印象(不是「>16 才有问题」,而是「上限一旦越过 63 必溢」,故上限策略与位表示改造**耦合**)。**但注意**:四条前置是「解除」的前置,**不是**「本批启动」的前置——T1/T2 可先行,T3 实施前必须先取裁(裁-CAP-2)并落纸设计。 + +--- + +## 裁决门(**开工前置**;逐条 = 问句 / 影响项 / 推荐 / 未取裁时的行为) + +> 纪律继承 P6:**未取裁的项一律不实施**(对应任务只到「report-only / 登记」为止),且**不得**以「推荐案」冒充裁定;含「先裁」的任务,**护栏写进任务卡正文**(P6 事故教训:`.superpowers/sdd/p6-incident-2026-09-14-t3-preempt.md` §2)。 + +| # | 问句(逐条照抄给维护者) | 影响项 | 本计划推荐 | 未取裁时 | +|---|---|---|---|---| +| **裁-CAP-1** | **E-2 的「运行期表示选择律」取哪一条?** (a) **存储边界规范化**——可选**聚合槽**(结构体字段 / 数组元素 / 全局槽 / match 结果)的**写点**把「裸 T 值」装箱为 `Some(v)`(`None` 保持对象),读取侧**零改动**(恒走既有装箱路径);零新运行期状态、零布局变更、纯 IR(两后端同源)。 (b) **侧表 / 位图**——给聚合槽配运行期表示位(结构体 packed 位图 + 数组 element stride + 全局伴随位 + match 结果位),保持「同一槽可有两种表示」语义;代价 = **目标程序数据布局变更** + ELF/解释器两后端同改 + `type_size`/`@sizeOf` 面。 (c) 静态面收窄(不可静态证者拒绝)——**与 P4 裁决 5「解包侧判表示」相悖**,仅登记不推荐。 | E-2(T2);(b) 会连带**发射面**(canary 豁免须显式声明) | **(a)**。理由:① 与既有机制同源(纯 IR、零 ABI/帧布局改动 ⇒ 两后端天然同源,`globals.cr:57-60` 的既有性质得以保持);② 三态纪律友好(不新增「未配位 ⇒ 静默」面,规范化律 = 全配或硬拒);③ canary 可保持**无豁免硬闸**(非可选程序零足迹、可选程序语料不在 canary 档);④ 语义无损(`T ⊆ T?` 的裸值注入本来就等价于 `Some(v)`——P3 语义口径)。风险留白:写点**漏一处即静默错值** ⇒ 必须配写点完备性审计(T2 Step 3) | T2 不实施;维持「响亮失败」现状并登记(**不得**先按 (a) 落码) | +| **裁-CAP-2** | **E-3 解除后的「上限策略」取哪一条?** (a) **记录布局迁侧表**(无硬上限;P4/P5 登记方向);(b) **对齐镜像**(16 → 64,与 `ast.cr` 镜像逐字一致;变体数受覆盖位限制须 ≤63,且仍是有界,③ 位表示改造仍不可免);(c) **维持现状**(在野需求 0:语料最大结构体 9 字段、无超限枚举)。 | E-3(T3) | **(a)**。理由:① 它同时闭合 ② 的镜像不一致(侧表无「槽数」概念,镜像可删/改写为形态说明);② (b) 只是把问题推迟且**仍需** ③ 的位表示改造(63 硬边界);③ 受护访问器单源(54 调用点零改动)使迁移成本可控。**附问 1**:`MAX_GENERICS=4` 是否同批解除?推荐 **否**(写入侧有界 + parse 期硬报,P5 附录 E-3 已明写「属另一类」)。**附问 2**:解除后 `.ccr` 记录布局**是否再 bump 版本**?推荐 **否**——盘面已是变长(`ccr_io.cr:324/:332` + `ccr_sym_seg_size` 动态算式),本批只迁**进程内**记录 ⇒ 盘面零变化(须实测锁证);若实施中发现盘面必须变 ⇒ **停下另裁**。 | T3 不实施;维持登记(在野需求 0)并保留 P022/P023 护栏为唯一拒绝面 | +| **裁-CAP-3** | **E-4 的收口面取哪一条?** (a) 快照**加表示位面**(格式变化 ⇒ `CIR_CACHE_VER` **17→18**〔注:提示词写「18→19」,**本树实值 = 17**(`cir_cache.cr:51`),该表述不成立〕);(b) **装载侧重派生**(与 NOD 派生同构,`cir_cache.cr:43-46`/`:514-523` 先例;零格式变化——**须先证可派生**);(c) **维持关缓存**(现状;代价 = 可选程序每次全量重建)。 | E-4(T4);依赖 E-2 | **(b) 优先,但以机制实证为门**:**第一步必须先做 RED 实证**(T4 Step 1)——本计划的静态枚举**未能复现** P4 报告所述分歧机制(全部 `irv_rep` 读取点在同函数生成内 ⇒ 命中恢复函数的 IR 自含表示语义);若实证**确实出现**冷/热分歧 ⇒ 按分歧面选 (a)/(b);若**证伪**(无分歧)⇒ 按 (c) 收口并登记「门已无必要」的结论(保留门 = 零成本保险亦可)。**不建议**在机制未明时直接取 (a)(格式变更 = 最贵且须 bump 版本族) | T4 止于**机制 RED 报告**;`main.cr:488-490` 门**保持现状**(不动 = 无害) | + +**三条主问 + 三条附问 = 6 问句**。另有两处**本计划无法确定、须实施时实证**(不猜):① E-2 的「可选的惰性 thunk」面是否属本批(P4 未覆盖面清单含它,P5 附录 E-2 只列 4 面 ⇒ 若裁-CAP-1 未点名,**本批不做**、登记);② E-3 迁侧表后 `FuncInfo`/`StructInfo`/`EnumInfo` 三个 `ast.cr` 声明是否为死声明(可删)——须实施时以「删除后重建 + 全量回归逐字节同」实证。 + +--- + +## 任务总表(**本批实施面**;状态一律「待实施」,收官时按实测回填) + +| 任务 | 内容 | 对应 | 依赖 | +|---|---|---|---| +| **T1** | 前置侦查 + **冻结基线重建(换代到本批起点)** + 起点判据 + 三面 RED 现状 + E-3 四前置实核 + 锚点实核(**源码零改动**) | E-2/E-3/E-4 前置 | — | +| **T2** | E-2 表示面聚合扩展(**Step 1 = 裁-CAP-1 先裁 + 设计落纸**;写点完备性审计;未覆盖面 4 例重钉 + 新增用例) | E-2 | T1、**裁-CAP-1** | +| **T3** | E-3 记录布局迁侧表(**Step 1 = 裁-CAP-2 先裁 + 逐字段布局表 + 镜像归位处置**;覆盖位表示改造;`test_enum_limit` 重钉) | E-3 | T1、**裁-CAP-2** | +| **T4** | E-4 可选程序收回 `.cir` 缓存(**Step 1 = 机制 RED 实证(必做)+ 裁-CAP-3 先裁**) | E-4 | T1、T2、**裁-CAP-3** | +| **T5** | 收官:全量回归 + 判据复验 + 统一台账 + 文档(spec §9 容量批行 / format spec / TODO / CLAUDE.md 如有结构面变化)+ 容量批终态与剩余登记 | 全部 | 全部 | + +**顺序理由**:T1 必须先于一切(本树含 P6 记录之后的修复 ⇒ 所有起点值须重测,且三面现状须以本树 RED 复现为准);T2 先于 T4(E-4 的「须重建状态清单」由 E-2 的表示律决定);T3 与 T2 **技术上可并行**(不同触面,见「锚点漂移 #6」)但**同批串行执行**(多 agent 纪律:一构建一编译串行;且两者都要重跑同一套判据,串行避免判据互相污染);T4 排在 T2 之后且自带机制门。 + +--- + +## 本批不做(**硬边界**;逐条 = 理由) + +1. **不做** E-2 的「可选的惰性 thunk」面(P4 清单含、P5 附录 E-2 未列;未取裁 ⇒ 不实施)。 +2. **不做** `MAX_GENERICS` 解除(写入侧有界 + parse 期硬报;P5 附录 E-3 明写「属另一类」)。 +3. **不做** `MAX_IFACE_METHODS` / `MAX_IFACE_METHOD_PARAMS`(P3b 显式登记**保留**;`dyn_arr.cr:190-195` 注:解除需迁侧表,护栏已是硬错)。 +4. **不做** `.ccr` 盘面格式改版(E-3 只迁进程内记录;盘面已有变长形态;若要变 ⇒ 停下另裁版本面)。 +5. **不做**:P6 未取裁的其余项(E-6 iterable/命名消费语法 / E-12 调用位点 / E-14 政策五问 / E-15 混比 / `.csr`·TagNode / `lit_empty_pair` 正×正面候选)——一律维持登记。 +6. **不做**:新用户面语法、ABI/帧布局改动、后端旁路状态(`g_x86_mw_tag_off` 式两后端各写一份 = 反例)。 +7. **不做**:可达性/代码消除类语义变更(P6 T4a 的边界先例)。 +8. **不做**:`.cir` 缓存**装载读缓冲**与命中路径的既存面(`test_cir_warm_path.py` 已钉的 19+2 例);E-4 只动「可选程序是否启用缓存」这一处门与其快照面(若取裁需要)。 + +--- + +## Global Constraints(照 P6 简版;容量批版本) + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**(多 agent 在场:一律 `jj commit -m … `,配方见记忆条 `jj_commit_recipe`);所有命令 `nice -n 19`;一构建一编译**串行**(每段前 `ps` 实查);cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +- **基线纪律(本批换代)**:每任务开工先取**同任务起点**基线(二进制 sha + 语料计数 + 探针 rc 表),**不得沿用上一任务的计数**。本批 T1 起基线 = **入仓配方重建的二/三件**(`tools/baseline/rebuild.sh`;白名单**换代到本批起点修订**,旧值按 `REBUILD.md`「换代纪律 2」留痕)。 +- **ELF canary(硬闸,全批)**:`clean-cache` → `build tests/suite/ptr_arith.cr --static -o /tmp/…` → sha256 须 = `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(**值须 T1 实测复核**——本树含 P6 之后修复)。**变即停**。**豁免条款**:仅当某任务的**先裁案**被维护者取为「显式声明发射面变更」的形态(唯一现实候选 = 裁-CAP-1 的 (b) 案)时,该任务卡**必须**在正文写明豁免理由与新增判据(逐段 delta 归因 + 两路径同值 + 新语料 canary 重锁),否则**无豁免**。**该语料对类型层/表示层不敏感**——它是「发射面零泄漏」证据,**不得**当新语义正确性证据(P3 附录 C.5-6 口径继承)。 +- **三态纪律(不得稀释)**:引擎 -1 不得当 0/1 用;载入失败/段缺失/行不可译/项不可建 ⇒ **拒绝**;save 侧遇不可译行 ⇒ 拒绝。**未配位/未覆盖面**不得以「假定」静默收口(E-2 的全部判据围绕此条)。**新硬错先 report-only**:本批新增的任何 rc=1 门(含 E-3 若保留上限的 P022/P023 新阈值)必须先在**全语料**(`src/compiler/_import.cr` 自指 + `tests/suite` + `tests/selfhost` 内联源 + `src/stdlib` + `examples` + 全枚举套件)report-only 跑一遍,清单审查后才开门。 +- **判定面回归网(P6 T1 起,本批继承)** = ① 冻结基线同源对拍(`tools/baseline/parity_run.sh`,72 档 rc + 日志逐档 diff)② 行为探针(`tests/selfhost/*.py` + 入仓探针语料 `tests/probes/` 29 档 + `tools/baseline/probes_run.sh`)③ 突变控制 ④ 三态纪律(ICE04)。**影子通道已死**——任何引用其计数/摘要/站点直方图即违规。 +- **`.ccr` 记录值引用口径(D28 继承)**:一律带**命令口径**(`corec ccr F -o O` vs `corec build F -o O --static`,差恒 143B)+ **缓存态**(冷/热;比较任何 `.ccr`/缓存态产物前 `clean-cache`)。 +- **判据口径按 TODO #26**:不采用「与旧版逐字节同」当语义证据;结构性断言 + 语义零变化 + 自举稳定为主判据;「≥N 例」= 下限(可增不可减);**自测用例只增不减**(重钉/改名允许,**删除须给死亡证据**——本批的重钉点 = `test_optional.py` 未覆盖面 4 例(-11 → 正确值)与 `test_enum_limit.py` 的 17 越界族,两者的死亡证据 = 本批的能力变更本身)。 +- **清单三面同核(B.6,硬性)**:任何清单/段表改动必须**三面同核**——`build_selfhost_native.py`(`corec_files`/`corearch_files`/`corelsp_files`)↔ `src/targets/x86_64-linux/_import.cr` ↔ `src/compiler/_import.cr`;每任务回归面**必含** `tests/selfhost/test_backend_bootstrap.py`(rc=0 + 构建日志 `error[` = 0)。本批预计**零清单增删**(全部改动落在既有文件);若需新增 `.cr` 文件 ⇒ 三面同核 + `test_backend_bootstrap`。 +- **不得回退既有收纳**:P4 全批(段机制/确定性装填/允许清单制/可选表示)· P5 全批(单槽化/命名面判定化/引擎唯一权威/ICE04/TA02/影子下线)· P6 全批(`.ccr` v9 项索引/loader 一致性硬校验/`-> never` 调用返回型/回归网入仓/`MIN_CASES` 收紧)· P022/P023 护栏与 `MAX_FN_PARAMS=64`/P020 · F5 槽位契约(#25/#28)与聚合字面量三校验(#29)· 自测用例下限——**一律不动**;冲突 ⇒ 停下上报。 +- **本语言约束(实施时必守)**:无三元运算符;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);**不写 `buf == 0` 形态的空判**(string-int 混比在 bootstrap 侧编成 `str_eq` ⇒ 自崩)。 +- **多 agent 纪律**:不并发构建;提交路径限定;实施时逐任务串行;突变控制一律在**仓库外实拷贝**(`tests/` 必须**实拷贝**而非软链——假绿教训);**含「先裁」的任务:先裁未取前不得落码**(P6 事故护栏)。 + +--- + +## 起点基线值(**提示词给定值 + 出处 + 本树状态**;不得预填实测数字) + +> **总原则**:本工作树含 P6 记录之后的修复(见「前置状态」的勘误条)⇒ **下列值一律不得直接当本批起点**;T1 必须全量重测并重锁,重测结果写入 T1 报告与收官台账。下表「本树可静态核」列已给出本实例**不跑即可核**的部分。 + +| 项 | 提示词给定值(出处) | 本树静态实核(本实例) | T1 须实测 | +|---|---|---|---| +| `.ccr` 两口径四条 | 96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…`,差恒 143B(出处:P6 T3 报告 §4 + T6 报告 §Step 2;本实例实读该两报告) | **须重测**(`ptr_arith`/`generics_test` 两档 × `ccr`/`build --static` 两口径 × 冷/热) | ✔ | +| `selftest-types` | 415/415(出处:`src/ci/run.sh:85` 计数注 + `tests/selfhost/test_type_engine.py:23` `MIN_CASES = 415`——**本实例实读两处在位**) | 计数**下限**在位;实际值须跑 | ✔ | +| 全枚举 | 58/58(出处:P6 T6 报告) | 套件档数 **58**(本实例实核:`ls tests/selfhost/test_*.py` = 51 + `ls tests/bootstrap/*.py` = 7) | ✔(rc 全绿须跑) | +| 五 CI job | 全 rc=0(出处:P6 T6 报告) | — | ✔ | +| 自举链 | `corec2==corec3` `0b2e06d0…` + N06=0 + 冒烟 42 + `--help` rc=1(出处:P6 T6 报告) | — | ✔(sha 必变——本树含 P6 后修复 ⇒ 旧 sha 不得沿用) | +| `test_optional` | 34/34(出处:P6 T0 §5.1) | 用例数 **34**(本实例实核:`ok` 列表条目;含未覆盖面 4 例 `:347-352`) | ✔(rc) | +| `test_enum_limit` | 13/13(出处:P6 T0 §5.2) | 用例数 **13**(本实例实核) | ✔(rc) | +| 冻结基线白名单 | `corec 5d2b15ad…` / `corearch 493dc490…` / `corelsp 18b94bd9…`(= pinned `9bcb7083`,出处:`tools/baseline/REBUILD.md` 白名单表) | 白名单在位(本实例实读 `REBUILD.md`) | ✔(重建核 + **换代**:新白名单 = 本批起点修订) | +| 探针语料 | 29 档(出处:`tests/probes/README.md`「清点」节) | 29 档在位(本实例实核 `ls tests/probes/*.cr`) | ✔(两态零差异) | +| 语料 | 72 档,rc 分布 34×0/38×1(出处:P6 T0 表 C) | — | ✔(**须重清点**:本树套件面已变) | + +--- + +## 侦查底座(2026-09-14 实读实核;file:line = 当前工作树;未标注者为本实例实读) + +**E-2 面(表示位现状与未覆盖面)** +- 表示位侧表:`globals.cr:63` `g_ir_var_rep : string, mut; g_ir_var_rep_cap : int, mut;`(注 `:57-66` 明撰「每个可选槽(局部/形参/调用结果)配一个……**无表示位的槽走既有装箱假定(未覆盖面:结构体字段/数组元素/全局槽/match 结果/惰性 thunk)**」)。 +- 生长/读写:`ir_gen.cr:214-224`(`grow_ir_var_rep`,新槽填 -1 哨兵)· `:230-235`(`irv_rep`)· `:237-241`(`irv_set_rep`)· `:244-249`(`emit_rep_set`)· `:251-259`(`opt_rep_slot`)。 +- 判定辅助:`global_decl_optional` `:261-277` · `callee_ret_optional` `:279-295` · `rep_enc_of_expr` `:297-330`(含 `:318` `EXPR_STRUCT/EXPR_ARRAY/EXPR_TUPLE → oe_bare()` 与 `:329` 兜底 `return oe_boxed();`)。 +- 写点(已配位):`:2138-2141`+`:2172`(LET)· `:1147-1149`(赋值局部)· `:1745-1762`(调用点写实参信道 + 读返回信道)· `:2209-2210`(返回点写信道)· `:2536-2544`(序言读形参信道)· `:2578-2598`(函数尾)· `:2829`(`ir_gen_globals` 尾部 `reg_optrep_globals`)。 +- **未配位写点(E-2 的实施靶)**:字段 `:1160-1164`(EXPR_ASSIGN 的 EXPR_FIELD 分支)· `:2346`(EXPR_STRUCT)· 枚举载荷 `:2312`;数组 `:1177`/`:1184`(EXPR_ASSIGN 的 EXPR_INDEX)· `:2369`(EXPR_ARRAY)· `:1314`(`go` 指数形式建数组)· 全局 `:1152-1155`(EXPR_ASSIGN 全局分支)· `:2735`(全局初值回填);match 结果 `:1960`/`:2076`。 +- 未配位读点:字段 `:2230`;数组 `:2287`/`:2295`;`?` `:2458`;match 载荷 `:2046`/`:2063`(仅当 `rep_v < 0` 的装箱分支)。 +- 判据载体:`tests/selfhost/test_optional.py:347`(`uncovered_field_bare_is_loud` → `-11`)· `:348`(装箱正确)· `:351-352`(全局装箱/None 正确)· 头注 `:22-24`(未覆盖面登记原文)。**数组元素面与 match 结果面零用例**(本实例 grep:`tests/` 内无 `[int?]`/可选数组字面量;`test_optional.py` 未覆盖面只钉字段与全局)⇒ T1 须**新造最小复现**。 + +**E-3 面(上限载体)** +- 常量:`ast.cr:115-118`。镜像 struct:`:130-138`(FuncInfo)· `:141-149`(StructInfo)· `:152-156`(EnumVariant)· `:159-165`(EnumInfo)。 +- 布局:`dyn_arr.cr:56-58`(`ESZ_FUNCINFO=688` / `ESZ_STRUCTINFO=440` / `ESZ_ENUMINFO=4408`)· `:90-94`(FuncInfo 偏移,含 `MAX_FN_PARAMS=64` 的槽区 `:89`)· `:97-100`(StructInfo:`OFF_SI_FIELD_NAMES=8` / `OFF_SI_FIELD_TYPES=136` / `OFF_SI_FIELD_TYPE_NODES=264` / `OFF_SI_FIELD_COUNT=392` / `OFF_SI_GENERIC_NAMES=400` / `OFF_SI_GENERIC_COUNT=432`)· `:231-243`(EnumInfo:`OFF_EI_VARIANTS=8` / `OFF_EI_VARIANT_COUNT=4360` / `OFF_EV_SIZE=272` / `OFF_EV_TYPES=8` / `OFF_EV_TYPE_COUNT=136` / `OFF_EV_TYPE_NODES=144`)。 +- 受护访问器(**迁移单源**):`dyn_arr.cr:462-464`(`fi_param_type` 护栏)· `:485-494`(`si_field_*` 读护栏 -1 哨兵)· `:503-516`(`ei_variant_*` 读护栏)· `:529-549`(`ei_set_*`/`si_set_*` 写护栏)· 契约注 `:518-528`(「唯一受护写点 + 读护栏 + P022/P023 承担用户面拒绝」)。 +- 写入侧:`parser.cr:1448-1461`(P020 形参上限,`MAX_FN_PARAMS`)· `:1634-1640`(`si_set_field_*`)· `:1648-1652`(P023 定位硬错)· `:1695-1716`(`ei_set_variant_*`)· `:1724-1733`(P022 两串:变体数 / 载荷数)。 +- 读回侧:`ccr_io.cr:1203`(struct 字段数闸)· `:1245`(变体数闸)· `:1272`(载荷数闸)· 清零循环 `:1208-1215`/`:1219`/`:1250-1262`/`:1265`。 +- 覆盖位:`ty_shadow.cr:1204-1213`(`sh_match_bit`)· `:1217-1226`(`sh_match_first_missing`)· `checker.cr:3033-3040`(`m_cover` 累积与冗余臂判据)。 +- 判据载体:`tests/selfhost/test_enum_limit.py`(16 边界三面正控 + 17 越界 P022/P023 + 无产物 + 「拒绝发生在**前端**」的 `forbid` 阶段串 `:124-127`)· `src/ci/run.sh:108` 挂点。 + +**E-4 面(缓存)** +- 门:`main.cr:484-487`(注:快照不载侧表 ⇒ 命中恢复路径与冷路径产物分歧)· `:488-490`(`if g_optrep_on != 0 { cache_enabled = 0; }`)· 缓存开关族 `:477-483`(泛型函数在场亦关)· 调用点 `:532`(load)· `:553`(save)· 目录 `:473`(`make_cir_cache_dir`)。 +- 快照面:`cir_cache.cr:51`(`CIR_CACHE_VER=17`)· `:7-49`(版本族注:v12..v18,含「`.cir` 写点早于 TYPE 段重建 ⇒ 项索引不能落盘」)· `:57-90`(编译器身份 = 运行中二进制自哈希)· `:187-249`(save 头部/尺寸)· `:436-492`(load 五闸 + 变量恢复「按**绝对** var id 增长数组」)。 +- 判据载体:`tests/selfhost/test_cir_warm_path.py`(21 例;A 组冷/暖 7 例含 `.ccr` **段级契约** `:191-198`;C 组可选面 2 例 `:277-287`)· `tests/selfhost/test_cache_identity.py`。 + +**回归网 / 工具面** +- `tools/baseline/REBUILD.md`(配方 + 白名单 + 换代纪律)· `rebuild.sh`(`jj workspace add --revision ` → `build_selfhost_native.py` → sha 硬闸 → 冒烟)· `parity_run.sh`(72 档 `check`;影子分支已剥;计数断言)· `probes_run.sh`(29 档显式清单 + 计数断言)。 +- `tests/probes/`(29 `.cr` + README 来源表)· `src/ci/run.sh:68-113`(selfhost-tests;挂点 **28** 档 py,本实例 `grep -c` 实核)· `:85`(selftest 计数注 415)· `:103`(test_optional 34)· `:108`(test_enum_limit 13)。 + +**锚点漂移(本实例实读发现;实施时以实施树为准)** +1. **报告位置**:`.superpowers/sdd/` 不在 `/tmp/p7wt`(jj-ignored,不入检出);实体在 `/home/DslsDZC/core/.superpowers/sdd/`(见「前置状态」)。 +2. **修订面**:本树含 P6 记录之后的修复(逐条证据见「前置状态」勘误条)⇒ P6 记录值不得直接沿用;`97f4394f` 的一致性留给 T1。 +3. **`CIR_CACHE_VER` 实值 = 17**(`cir_cache.cr:51`)——提示词/裁-CAP-3 问句所写「18→19」不成立(`REBUILD.md` 与 format spec 亦记 17)。 +4. **`ast.cr` 镜像不一致面宽于登记值**:除 `StructInfo.field_*`(64 vs 16)外,另有 3 处(`StructInfo.generic_names` 16 vs 4 · `EnumVariant.types` 64 vs 16 · `EnumInfo.generic_names` 16 vs 4);且这些 struct 在 `ast.cr` 之外**仅注释命中**(无类型使用点 ⇒ 疑为死声明,须实证)。 +5. **受护访问器区间**:P6 计划写 `dyn_arr.cr:481-548`,实读 **`:462-549`**(契约注 `:518-528`)。 +6. **「同触 `dyn_arr.cr` 布局」的合批理由需更正**(P4 附录 D-3-8 原话):E-3 触的是**编译器自身符号表记录**(`ESZ_STRUCTINFO`/`ESZ_ENUMINFO`);E-2 的推荐案(裁-CAP-1 (a))**不触**任何布局,若取 (b) 案则触的是**目标程序**数据布局(`type_size`/ELF 发射)——两者**不同物**。合批理由应表述为「同类裁决面(容量/表示)同批」,非「同触布局」。 +7. **E-4 机制存疑**:P4 报告「命中恢复的函数解包点回落装箱路径」在本树静态枚举下**未能复现**(全部 `irv_rep` 读取点 `ir_gen.cr:304`/`:1148`/`:1987`/`:2443` 都在**同函数生成内**;命中函数不生成 ⇒ 其 IR 自含表示语义)⇒ 机制须 RED 实证(裁-CAP-3)。 +8. **`sh_match_bit` 的 63 边界**(`ty_shadow.cr:1204-1213` 头注)是 **i64 位宽**边界而非 16 派生 ⇒ 上限策略(裁-CAP-2)与覆盖位改造**耦合**,登记文字「>63 变体溢出」的边界值须按此理解。 + +--- + +## Task 1:前置侦查 + 冻结基线重建(换代)+ 起点判据 + 三面 RED 现状(**源码零改动**) + +**Files:** 无源码改动。产物 = 报告 `.superpowers/sdd/cap-task1-report.md`(工作区文件,不入提交)+ 重建基线 `/tmp/cap-t1/base/{corec,corearch,corelsp}` + 原始输出 `/tmp/cap-t1/*`。 + +**Interfaces(六表,全部落报告;数字一律本实例实测,未实测标「待实测」):** +- **表 A:冻结基线重建 + 白名单换代**——① 用现行配方核旧白名单仍可复现(`bash tools/baseline/rebuild.sh /tmp/cap-t1/base-p4` ⇒ 三 sha = `REBUILD.md` 白名单);② **换代**:以本工作树的 `@-` 修订(T1 以 `jj log -r @- --no-graph -T …` 实读;**须与提示词给定的 `97f4394f` 核对,不符则登记并以上报裁决**)为源修订,重建本批起点基线并记新白名单(三 sha + 源修订),按 `REBUILD.md`「换代纪律」更新该文件(旧值留痕)。 +- **表 B:本批起点判据**——五 CI job;全枚举(档数以本实例 `ls` 重清点为准);`check src/compiler` rc;`selftest-types` 实测值;构建确定性 ×2;自举链(`corec2==corec3` + N06 + 冒烟 42 + `--help` rc=1);**ELF canary 实测**(值须与 Global Constraints 的白名单值比对,变即停);`.ccr` 两口径四条(冷 + 热);`--dump-objects` 行数;`.cir` DOT;`test_optional` rc;`test_enum_limit` rc。 +- **表 C:语料与探针清点**——72 档清单逐档实核(含 0 字节 fixture 与既有 rc=1 档的划界)+ `parity_run.sh` 一次得 rc 分布(与 P6 表 C 对照);`probes_run.sh` 29 档 rc 表。 +- **表 D:锚点实核**——本计划「侦查底座」每个 file:line 逐条复核;漂移登记(含本计划已列 8 条)。 +- **表 E:三面 RED 现状**——E-2:`test_optional` 未覆盖面 4 例 rc 复现 + **数组元素面 / match 结果面的新造最小复现**(各写载裸值 + Some 臂、装箱 + Some 臂两形态,四路径记录 rc);E-3:16 边界正控 + 17 越界 P022/P023(`check`/`build` 两命令 + 无产物);E-4:可选程序编译**零条目落盘**复现 + 非可选程序冷/暖条目命中对照。 +- **表 F:E-3 四前置实核**——逐条 {结论 / 缺什么}(本计划已给初判,T1 须以本树重核并补实测证据)。 + +- [ ] **Step 1: 基线重建 + 换代**(表 A;重建在仓库工作树之外,收工 `jj workspace forget` + 删目录;**仓库工作树零改动**)。判据:旧白名单重建逐条同 + 新基线三 sha 记录 + 冒烟 42。**不符 ⇒ 停下上报**。 +- [ ] **Step 2: 起点判据**(表 B;cwd = 仓库根、逐段 `ps` 实查无并发、比较前 `clean-cache`)。 +- [ ] **Step 3: 语料/探针清点**(表 C)。 +- [ ] **Step 4: 锚点实核**(表 D;含本计划 8 条漂移的逐条复核)。 +- [ ] **Step 5: 三面 RED 现状 + 四前置实核**(表 E/表 F;**新造数组元素/match 结果两面复现**——这是 E-2 实施的前置证据)。 +- [ ] **Step 6: 报告**(不入提交;收工 `jj status` 应为空)。 + +**判据**:六表齐 + 基线 sha 与换代白名单逐条同 + 源码零改动(`jj status` 干净)+ 报告内数字全部可追溯(命令 + 原始输出片段)。 + +**停条件**:① 重建 sha 不符白名单 ⇒ 停下(**不得**改白名单迁就);② 换代源修订与提示词给定值不符且无法归因 ⇒ 停下上报;③ E-2 的数组元素/match 结果两面**复现不出任何形态**(既非失败也非正确)⇒ 停下(本计划的现状判断有误,须重查);④ canary 实测值 ≠ 白名单值(起点即变)⇒ 停下上报(起点不变量不成立)。 + +--- + +## Task 2:E-2 表示面聚合扩展(**Step 1 = 先裁**;挂裁-CAP-1) + +**Files:** Modify `src/compiler/ir_gen.cr`(写点规范化/表示位 + 解包侧 + `rep_enc_of_expr` 面)· Modify `src/compiler/globals.cr`(若需新全局/常量)· Modify `src/arch/x86_64/instr.cr`(**仅当**新 IR 形态需要后端分派——须实证)· Modify `src/compiler/interp.cr`(同)· Modify `tests/selfhost/test_optional.py`(未覆盖面 4 例重钉 + 新增)· Modify `src/ci/run.sh`(计数注)。 + +**Interfaces(**Step 1 未取裁前不得落码**):** +``` +// 表示律(裁-CAP-1 取裁后落纸;以下为推荐案 (a) 的形态说明,非既成事实): +// 可选聚合槽(字段/数组元素/全局槽/match 结果)的**写点**把「静态裸值」装箱为 Some(v); +// None 保持对象;读取侧零改动(恒装箱路径)。纯 IR(两后端同源),零布局变更。 +// 写点完备性清单(须逐条判定「是否可达可选槽」;漏一处即静默错值 ⇒ 停条件①): +// struct field : ir_gen.cr:1160(EXPR_ASSIGN) · :2346(EXPR_STRUCT) · :2312(枚举载荷,邻域面须实证) +// array elem : ir_gen.cr:1177/:1184(EXPR_ASSIGN) · :2369(EXPR_ARRAY) · :1314(go 指数形式) +// global slot : ir_gen.cr:1152-1155(EXPR_ASSIGN) · :2735(全局初值) +// match result : ir_gen.cr:1960(声明) · :2076(臂写) · 通配臂 / 无体臂路径 +// 读点(保持既有装箱假定或按律解包): :2230 · :2287/:2295 · :2458 · :322-327 +// 若取裁为 (b) 案(位图/stride):布局表 + 两后端同改清单 + canary 豁免声明(须写进任务卡正文) +``` +- [ ] **Step 1: 先裁落纸**(裁-CAP-1 取裁结果 + 与 P4 裁决 5 / D20 / 三态纪律的一致性论证 + 面清单 + 「漏写点 ⇒ 静默」风险表 + 若为 (b) 案则含 `type_size`/ELF/解释器/`@sizeOf` 影响面与 canary 豁免声明)。判据:落纸齐 + 无「大概/应该」措辞。**未取裁 ⇒ 本任务止步于此**(不发任何改动,登记)。 +- [ ] **Step 2: RED 证据**:逐面最小复现(裸值 + Some 臂 / 装箱;含数组元素与 match 结果两面——T1 表 E 的复现件在此转成用例)。判据:四面的 rc/产物矩阵与 T1 记录逐条同。 +- [ ] **Step 3: 实现 + 写点完备性审计**(`grep -n "IR_STORE_FIELD\|IR_STORE_INDEX\|IR_STORE_INDEX_VAR\|IR_STORE_PTR\|IR_STORE," src/compiler/ir_gen.cr` 逐条判定 + 指针/别名写可选槽的可达性实证)。判据:审计表逐条有结论;**存在不可覆盖写点 ⇒ 停下**(规范化律不成立)。 +- [ ] **Step 4: 用例**:未覆盖面 4 例**重钉**(死亡证据 = 本任务的能力变更)+ 新增(4 面 × {裸, 装箱, None, 条件写, 跨函数/跨解包点} 的 `case_dual` 三路同证 + 负控)。 +- [ ] **Step 5: 判据(硬闸)**:`test_optional` ≥ 34(只增)· **ELF canary IDENTICAL**(推荐案下**无豁免**;非可选程序零足迹 = `g_optrep_on` 门)· `.ccr` 两口径四条(冷/热)**预期不变** ⇒ 逐条实测,变则逐段 delta 归因 · 72 档对拍零差异 + 探针 29 档零差异 · 五 CI job + 全枚举 + `selftest-types`(≥ T1 实测值)+ `check src/compiler` rc=0 + 自举链 + `test_backend_bootstrap` rc=0 + `error[`=0 · **突变控制 ≥3**(漏一个写点 / 装箱分支反转 / 解包侧回落装箱路径 ⇒ 各自转红)+ 精确回滚。 +- [ ] **Step 6: 提交**(路径限定):`feat: 容量批 Task 2——E-2 表示面聚合扩展(结构体字段/数组元素/全局槽/match 结果;发射面零泄漏)`。 + +**停条件**:① 任一写点不可覆盖(经指针/别名写可选聚合槽、或路径不可静态判定)⇒ 停下(改取裁);② canary 变 ⇒ 停下(除非任务卡正文已按裁-CAP-1 (b) 声明豁免);③ 需引入后端旁路状态(`g_x86_mw_tag_off` 式)⇒ 停下(两路径同源被破);④ 全语料出现任何**静默错值**(rc=0 而值与语义不符)⇒ 停下(三态纪律)。 + +--- + +## Task 3:E-3 记录布局迁侧表(**Step 1 = 先裁**;挂裁-CAP-2) + +**Files:** Modify `src/compiler/dyn_arr.cr`(记录布局 + 访问器 + 侧表 grow/append + 新偏移常量)· Modify `src/compiler/parser.cr`(写入循环改 append + 上限策略落点)· Modify `src/compiler/ccr_io.cr`(读回闸 → 动态填充;清零循环)· Modify `src/compiler/ty_shadow.cr` + `src/compiler/checker.cr`(覆盖位表示与消费点)· Modify `src/compiler/ast.cr`(常量 + 镜像归位/删除)· Modify `src/compiler/globals.cr`(侧表全局)· Modify `tests/selfhost/test_enum_limit.py`(重钉)+ `tests/selfhost/test_ccr_types.py`(若段尺寸断言涉)· Modify `src/ci/run.sh`(计数注)。 + +**Interfaces(**Step 1 未取裁前不得落码**):** +``` +// 侧表形态(示例;最终以先裁的逐字段布局表为准): +// 结构体字段:(row, fi) → {name_ni, type_code, type_node};行基址侧表 g_si_field_base[row] +// 枚举变体 :(row, vi) → {name_ni, type_base, type_count};行基址侧表 g_ei_variant_base[row] +// 变体载荷 :(variant_row) → type_code / type_node(二级侧表) +// 访问器签名不变(si_field_name(n,fi) / ei_variant_type(n,vi,ti) / si_set_* / ei_set_*) +// ⇒ 54 个调用点零改动(受护访问器单源的价值所在;若必须先裁为改签名 ⇒ 停下另议) +// 覆盖位:sh_match_bit(sh_match_first_missing) + checker.cr:3033-3040 的 m_cover 同批换表示 +// 上限策略(裁-CAP-2):(a) 无硬上限 | (b) 对齐镜像 16→64(变体 ≤63) | (c) 维持现状 +``` +- [ ] **Step 1: 先裁落纸**(裁-CAP-2 取裁结果 + **逐字段布局表**(内存 & 盘面两侧)+ 失效面清单(在野 `.ccr` 版本面 = 见附问 2)+ 镜像归位/删除处置(含死声明实证方案)+ 覆盖位新表示的三态语义)。判据:布局表逐字段有依据 + 与「盘面已是变长」事实一致。**未取裁 ⇒ 本任务止步于此**。 +- [ ] **Step 2: 用例(红)**:新上限族(构造/`match` 穷尽性/`.ccr` 读回/泛型实例化各面)+ 旧 16/17 边界用例的**重钉或死亡证据**(`test_enum_limit.py` 13 例:16 边界正控保留;17 越界拒绝面按新上限重钉;**删除须给死亡证据**)。判据:新用例在当前树上**红**(或按 (c) 案 ⇒ 本任务不实施)。 +- [ ] **Step 3: 实现**(记录 + 访问器 + parser 写入 + ccr_io 动态填充 + 覆盖位)。**单源纪律**:所有读写经受护访问器(`dyn_arr.cr:518-528` 的三条契约不破);`count` 槽保持真值(既有口径)。 +- [ ] **Step 4: 判据(硬闸)**:**ELF canary IDENTICAL**(记录布局不进发射面 ⇒ 无豁免)· `.ccr` 两口径四条(冷/热)**预期不变** ⇒ 逐条实测 + 有变则逐段 delta 归因 + 走附问 2 · 72 档对拍零差异(>16 声明在语料零命中 ⇒ 零差异**是**判据)· 探针 29 档零差异 · 五 CI job + 全枚举 + `selftest-types`(≥ T1 值)+ `check src/compiler` rc=0 + 自举链 + `test_backend_bootstrap` · `test_enum_limit`(重钉后)+ `test_ccr_types` + `test_ccr_v7` + `test_region_cfg` 全绿 · **突变控制 ≥3**(漏改一个访问器 / 读回闸未改(仍按 16 拒) / 覆盖位表示退化到单 int ⇒ 各自转红)+ 精确回滚。 +- [ ] **Step 5: 文档**:format spec §3.2 SYM(`docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md:57`,「不变」句若涉盘面则改,否则补「进程内记录迁侧表」注)· `CLAUDE.md` 若有 `MAX_*` 叙述 · TODO · 本计划回填。 +- [ ] **Step 6: 提交**(路径限定):`feat: 容量批 Task 3——E-3 记录布局迁侧表(MAX_STRUCT_FIELDS/MAX_ENUM_VARIANTS/MAX_VARIANT_TYPES 解除;盘面零变化)`。 + +**停条件**:① 迁移需改 `.ccr` **盘面格式** ⇒ 停下(超授权,另裁版本面);② canary 变 ⇒ 停下;③ 出现任何「越界写静默」形态(含读回侧静默接受/拒收错位)⇒ 停下(三态纪律);④ 覆盖位改造若被迫动 `ty_exhaustive` 的**生产语义**(非表示层)⇒ 停下(超章程;该面经 `ty_inhabited → ty_exhaustive` 达 TM03 生产面——P6 已有先例裁定)。 + +--- + +## Task 4:E-4 可选程序收回 `.cir` 缓存(**Step 1 = 机制 RED 实证(必做)+ 先裁**;挂裁-CAP-3;依赖 T2) + +**Files:** Modify `src/compiler/cir_cache.cr`(快照面或装载侧派生)· Modify `src/compiler/main.cr`(`:488-490` 门)· Modify `tests/selfhost/test_cir_warm_path.py`(C 组 2 例重钉 + 新增)· Modify `src/ci/run.sh`(计数注)。 + +**Interfaces(**机制未实证前不得落码**):** +``` +// Step 1 实证件(临时沙箱,仓库外实拷贝;不得进提交): +// ① 放开 main.cr:488-490 门(沙箱内)⇒ 可选程序连续两跑:比 ELF 逐字节 / .ccr 段级契约 / IR dump +// ② 全命中面:暖跑条目 size+mtime 不变(真命中);混态:部分函数命中 + 部分 miss +// ③ 结论二选一:出现静默分歧(归因到具体状态:侧表 / 信道全局 / var 序)⇒ 落 (a)/(b) 案 +// 无分歧(证伪 P4 机制陈述)⇒ 落 (c) 案 + 「门已无必要」的登记 +// 收口案(裁-CAP-3): +// (a) 快照加表示位面 ⇒ CIR_CACHE_VER 17→18(本树实值 17)+ 失效面登记 +// (b) 装载侧重派生(同 NOD 派生先例 cir_cache.cr:43-46/:514-523)⇒ 零格式变化 +// (c) 维持关缓存(现状) +``` +- [ ] **Step 1: 机制 RED 实证 + 先裁**(沙箱放开门的冷/暖对比;结论 + 归因)。判据:有冷/暖原始产物对比件 + 分歧/无分歧的判据明确。**未取裁 ⇒ 本任务止步于此**(门保持现状)。 +- [ ] **Step 2: 用例**:冷/暖 ELF 逐字节同 + `.ccr` **段级契约**(冷=暖:SYM..IFACE 七段;STR 段冷≠暖为**预存**口径,`test_cir_warm_path.py:191-198`)· 条目真命中(size/mtime 不变)· 表示面各形态(裸/装箱/条件写/跨函数/聚合面——与 T2 的交互面)双跑同产物 · 负控(编译器身份变 ⇒ miss;格式位不符 ⇒ miss)。 +- [ ] **Step 3: 实现**(按取裁;**不新增后端旁路状态**)。 +- [ ] **Step 4: 判据(硬闸)**:**ELF canary IDENTICAL** · `.ccr` 两口径四条(冷/热)逐条实测 · `test_cir_warm_path` ≥ 21 例(C 组重钉须给死亡证据)+ `test_cache_identity` 绿 · 72 档对拍零差异 + 探针零差异 · 五 CI job + 全枚举 + 自举链 + `test_backend_bootstrap` · **突变控制 ≥2**(关掉命中比对 / 快照面丢一个状态 ⇒ 各自转红)+ 精确回滚。 +- [ ] **Step 5: 文档**(`cir_cache.cr` 版本族注 · format spec 的 `.cir` 版本行 · TODO)。 +- [ ] **Step 6: 提交**(路径限定):`perf: 容量批 Task 4——E-4 可选程序收回 .cir 缓存(表示面可重建;冷/暖产物同)`。 + +**停条件**:① 冷/暖对比出现**任何静默分歧**(rc=0 而产物不同)且无法归因 ⇒ 停下;② 机制证伪且无分歧 ⇒ 按 (c) 收口(登记,不落码);③ 快照格式变更被迫波及 `.ccr` 或后端 ⇒ 停下。 + +--- + +## Task 5:收官(回归 + 判据 + 台账 + 文档 + 终态) + +- [ ] **Step 1: 全量回归**(cwd = 仓库根、`nice -n 19`、串行、比较前 `clean-cache`):五 CI job 全 rc=0;全枚举(实测档数);`check src/compiler` rc=0;`selftest-types` 终值;构建确定性 ×2;`test_backend_bootstrap` rc=0 + `error[`=0。 +- [ ] **Step 2: 判据复验**(用**入仓配方重建的本批起点基线**):同源对拍 72 档(rc + 日志逐档 diff;预期差异须逐条归因)· 探针 29 档 · 突变控制快照 · ELF canary · 自举链(`cmp` + N06 + 冒烟 42 + `--help` rc=1)· `.ccr` 两口径(冷/热)· `--dump-objects` · `.cir` DOT。 +- [ ] **Step 3: 统一台账**:逐任务列 {实施 / 删除 / 收紧 / 放宽 / 修复 / 登记 / 偏差},与各任务实际 diff 逐条对应;三裁定(裁-CAP-1..3)的**取裁结果与落地情况**单列;未取裁项列「已登记、未实施」。 +- [ ] **Step 4: 文档**:spec §9 加**容量批行**(实施项 / 判据 / 剩余登记)· format spec(若涉)· TODO 新条目(容量批收官 + 三裁定结果)· `CLAUDE.md`(若引入新文件/目录或 `MAX_*` 叙述变化)。 +- [ ] **Step 5: 容量批终态陈述(一页)**:已实施面 / 已裁定面 / 剩余登记面三栏;下一批指向。 +- [ ] **Step 6: 提交**(路径限定)。 + +**判据**:五 CI job + 全枚举 + canary + 自举链 + 同源对拍 + 探针 + 台账逐条可溯源 + 文档指针链闭合(spec §9 → 本计划 → 统一台账)。 + +--- + +## 停条件汇总 · 未决项(**不猜**)· 自检记录 + +**全批级停条件**:① 任何任务使 ELF canary 变(除任务卡正文已声明豁免的形态)⇒ 停下;② 任何「静默错值/静默分歧/静默接受」形态出现 ⇒ 停下(三态纪律);③ 需改 `.ccr` 盘面格式 / `.cir` 格式面而超出该任务的批准面 ⇒ 停下另裁;④ 需动清单三面(`build_selfhost_native.py` ↔ 两 `_import.cr`)而计划未列 ⇒ 停下(并补三面同核 + `test_backend_bootstrap`);⑤ 与既有收纳(P4/P5/P6 全批 + 本计划的「不得回退」清单)冲突 ⇒ 停下上报。 + +**未决项(本计划**无法确定**、实施时实证或请维护者补一句)**: +1. **修订一致性**:工作树 `@-` 与提示词给定 `97f4394f` 是否同一提交——本实例不能跑 `jj` ⇒ T1 实核并登记。 +2. **E-4 机制**:P4 的「命中恢复 ⇒ 解包回落装箱」陈述在本树静态枚举下**未能复现**(见「锚点漂移 #7」)⇒ T4 Step 1 实证(**不得**照抄「装载侧重派生」方案当既成事实)。 +3. **E-2 的惰性 thunk 面**:P4 未覆盖面清单含它、P5 附录 E-2 未列 ⇒ 归属未裁(默认本批不做;若维护者点名则入 T2 的面清单)。 +4. **`ast.cr` 三个 struct 声明是否为死声明**:本实例 grep 无类型使用点,但 bootstrap 侧处理未实证 ⇒ T3 实核。 +5. **E-2 写点的指针/别名可达性**(经 `IR_STORE_PTR` 写可选聚合槽是否可能)⇒ T2 Step 3 审计实证;若可达且不可静态覆盖 ⇒ 停条件①。 + +**自检记录**: +- **分诊诚实性**:三项**未凑数**——E-2/E-3/E-4 各自给了「现状(file:line)→ 影响面 → 依赖 → 前置 → 处置」,**没有任何一项被推荐案冒充裁定**(三条裁决门逐条点名问句 + 未取裁时的行为写死为「不实施 / 维持现状」);E-3 的四条前置**逐条给了结论**,其中 ② 的缺口**实读发现宽于登记**(多 3 处镜像不一致)、③ 的边界**按位宽理解修正**(63 而非 16 派生)——两项均落进「锚点漂移」。 +- **根因 vs 止血**:① E-2 的落点 = **写点完备性**(不是给每面加特例分支);② E-3 的落点是**记录布局单源**(受护访问器 54 调用点零改动 = 迁移可行性的根据),不是逐点加护栏(护栏已在位,是安全网);③ E-4 先**证伪/证实机制**再定收口面(不先动格式)。 +- **与既有裁决的一致性复核**:P4 裁决 5「解包侧判表示」(裁-CAP-1 的 (c) 案与其相悖 ⇒ 已标不推荐)· D20(进程内引用不得跨进程 ⇒ E-4 若取 (a) 案的表示位面须是**可重派生/文件内**形态)· D22/D23(互斥与派生码纪律)· D28(`.ccr` 两口径 + 冷/热豁免)· TODO #26(判据口径)· P6 事故护栏(「先裁前不得落码」写进 T2/T3/T4 任务卡正文)。 +- **占位符扫描**:无 TBD;所有需实测处均标「须 T1/T* 实测」并给出测量命令或出处;五处未决项明写,**未预填数字**。 +- **本计划的自我限制(诚实登记)**:本计划**未跑任何构建 / 编译 / 测试 / `jj`**(纯只读侦查:`ls`/`grep`/`sed`/`wc`/`diff`/`ps` 级实核);所有「现状值」为实读源码/文档或既有报告记录值;**起点基线值一律标「须 T1 重测」**(理由 = 本树含 P6 记录之后的修复);三处需实测的数字(canary、`.ccr` 四条、selftest 计数)**不得**从 P6 记录沿用。 +- **风险面**:① **E-2 写点漏一处 = 静默错值**(本批最大风险)——缓解 = 写点完备性审计 + 指针/别名可达性实证 + 突变控制 ≥3;② **E-3 的记录迁移面广**(54 调用点虽零改动,但记录布局 + 读写闸 + 覆盖位三处同批)——缓解 = 受护访问器单源 + 旧边界用例重钉 + 突变 ≥3 + 盘面零变化(判据 = `.ccr` 四条不变);③ **E-4 的机制未明**——缓解 = 先实证后落码 + 证伪即按现状收口;④ 修订/基线与提示词给定值不一致(T1 停条件②)——缓解 = T1 首步实核 + 白名单换代留痕;⑤ 多 agent 并发与「先裁抢跑」(P6 事故先例)——缓解 = 任务卡正文护栏 + 一构建一编译串行 + 路径限定提交。 + +--- + +## 任务实施记录(T1–T5;**T5 收官回填**) + +| 任务 | 提交 | 交付 | 判据(实测) | +|---|---|---|---| +| **T1** 前置侦查 + 基线换代 | `4607624c`(白名单换代)+ 报告 `.superpowers/sdd/cap-task1-report.md` | `tools/baseline/{REBUILD.md,rebuild.sh}` 白名单 **`9bcb7083 → 97f4394f`**(旧值留痕);六表侦查(含三面 RED、E-3 四前置、8 条锚点漂移) | 起点判据 **14/14 与 P6 终态记录全同**(canary `95084e7b…d475` · `.ccr` 四条 96015/96158/142793/142936 · selftest 415 · 枚举 58→59 · 72 档 34×0/38×1 · 探针 17×0/12×1);换代自证 = 重跑配方 ×2 三 sha 逐条同 + 冒烟 42 + `cmp` IDENTICAL ×3 | +| **T2** E-2 表示面聚合扩展 | `432c2e40` | `globals.cr`(+5)/`ir_gen.cr`(+230):写点装箱单源件 + A 类 8 点 + B 类 2 点(#10/#11 实证可达升 A)+ match 面裁决 (i);`test_optional` 34 → **48** | canary IDENTICAL(无豁免)· `.ccr` 四条冷+暖逐字节同起点 · 72 档仅行号位移 · 探针零差异 · 五 CI job · 枚举 59/59 · selftest 415/415 · 链 `819af35c…`;突变 M1/M3/M4/M8 全红(**M4 = rc=0 静默错值仅精确值断言可抓** · M8 = canary 变) | +| **T2 登记** | `7c4d6603` | TODO **#56**(`c3` 双路径分歧静默错值;**今天就存在 · 本批不修 · 非本批引入**) | 三子形态 c1 正确但脆弱 / c2 139 / c3 ELF 8 vs interp 216 | +| **T3** E-3 记录布局迁侧表 | `0b0d9aef` | 10 文件(+381/−271):结构体字段/枚举变体载荷**侧表** + **覆盖位无界位图**(`mc_*`)+ 三 `MAX_*` 退役 + P022/P023 停发 + `ast.cr` 四死镜像删除 + `test_enum_limit` 13 → **18** | 17/40 字段 · 17/40/64/70 变体 · 17 载荷 ⇒ 编译+运行正确(旧拒绝面转正)· 70 变体旧态 **SIGFPE rc=136** → 现穷尽正确且反例名正确 · TM03/TM04 真错仍拒 · canary IDENTICAL · **`.ccr` 四条冷+暖逐字节同 ⇒ 「不 bump」成立** · 72 档仅行号 +116 位移 · 探针零差异 · 五 CI job · 枚举 59/59 · selftest 415/415;突变 MA/MB/MC 全红 | +| **T4** E-4 机制实证 | `15357b04` | TODO **#57** + 报告(**零源码改动**):沙箱放开门 ⇒ 冷/暖 × 16 档 + 混态 3 态 × 2 程序 **ELF 逐字节全同** ⇒ **机制证伪 ⇒ 按裁 (c) 收口**;「移除门」登记为待裁候选 | 门原样保留;证据 `/tmp/capt4/**` | +| **T5** 收官 | 本提交 | 全量回归 + 本台账 + spec §9 容量批行 + TODO #58 + 终态陈述 | 见下 | + +--- + +## 统一台账(容量批 T1–T5;**T5 收口**) + +> 口径(继承 P6):每件给证据与替代/覆盖;全语料命中一律与**同任务起点**基线逐条比。**KPI**:实施 **4**(T1 回归网换代 · T2 · T3 · T4 实证收口)· 删除 **3 类**(`ast.cr` 四个死镜像结构 · P022/P023 两闸 · 定长槽清零循环两处)· 迁移 **2 类**(结构体字段→侧表 · 枚举变体/载荷→侧表;另 selftest 直接写点 44 处改访问器)· 收紧 **0** · 放宽 **2 类**(T2 聚合面裸值 139→正确值 · T3 三面上限解除)· 修复 **3 件**(T2 global 双根因:读点常量折叠 + 写点静态度量初值;T3 覆盖位 ≥63 溢出崩溃)· 登记 **12** · 偏差 **6** · **未取裁 2**(T4 移除门候选 · (D) 语言面拒绝候选)。 + +### 1. 实施(4 件) +逐件见上「任务实施记录」表(T1/T2/T3/T4 的提交、交付、判据)。 + +### 2. 删除 / 迁移 +| # | 件 | 任务 | 证据 | +|---|---|---|---| +| 1 | **删除**:`ast.cr` 四个死镜像结构(`FuncInfo`/`StructInfo`/`EnumInfo`/`EnumVariant`) | T3 | 全仓零类型使用点(仅注释命中);`[.;64]` 与实际布局本已不一致;布局真源 = `dyn_arr.cr` | +| 2 | **删除**:`P022`/`P023` 两闸 + `MAX_STRUCT_FIELDS`/`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES` 三常量退役 | T3 | 上限解除 ⇒ 无越界写对象;错误码保留编号并标「已退役、零 raise」 | +| 3 | **迁移**:结构体字段三条 + 枚举变体/载荷五条**侧表**(记录尺寸与 `OFF_*` 不变 ⇒ **110 个访问器调用点零改动**) | T3 | 记录面判据全绿 + 突变 MA/MB 转红 | + +### 3. 放宽(2 类;全语料命中 = 0,均为**新能力面**) +| # | 形态(前 → 后) | 任务 | 覆盖 | +|---|---|---|---| +| 1 | 可选**聚合槽**裸值:双路径 139(响亮失败)→ **正确值**(写点装箱) | T2 | 4 面(字段/元素/全局/match 结果)+ 2 邻域(枚举载荷/元组元素);`test_optional` 48/48;72 档/探针零差异(语料无可选聚合) | +| 2 | 三上限(16)解除:>16 ⇒ 定位硬错拒绝 → **接受且语义正确** | T3 | 17/40/64/70 变体 · 17/40 字段 · 17 载荷;`test_enum_limit` 18/18;72 档零差异(语料最大 9 字段 / 无超限枚举) | + +### 4. 修复(3 件) +| # | 形态 | 任务 | 根因 | +|---|---|---|---| +| 1 | `g : int? = 5` 读点被**常量折叠**成裸常量 ⇒ match 的 tag 读解引用裸值(139) | T2 | `find_global_const_node` 对可选全局的折叠不合法(`T?` 值不是裸常量) | +| 2 | 全局**初值**走静态度量(`.quad` 裸值)⇒ 读侧按装箱解引用 | T2 | `global_needs_runtime_init` 对字面量返回 0 | +| 3 | ≥63 变体:覆盖位 `2^vi` 溢出 ⇒ 70 变体 **SIGFPE rc=136** / 判定失真 | T3 | 单 int 位图 ⇒ 换**无界位图**(`mc_*` + 栈纪律) | + +### 5. 登记(12 件;逐条 = 归属 / 状态) +| # | 件 | 归属 | 状态 | +|---|---|---|---| +| 1 | **`c3` 指针写可选槽双路径分歧静默错值**(高优先;含 c1/c2 三子形态) | TODO **#56**(T5 前已立) | 已登记、本批不修、非本批引入 | +| 2 | 指针面五形态(局部 139 · 数组元素 139 · 全局分歧 · `&s.a` **写入丢失** · 对照正确) | T2 报告 §8-1/8-2 | 已登记(裁 (A) 不修) | +| 3 | **指针面根因链**(同站点双语义 + 绕过 `emit_rep_set` + 别名不可静态闭合) | T2 报告 §8-2 | 已登记 | +| 4 | **(D) 语言面拒绝 `*p = v`(pointee 为 `T?`)候选** | T2 报告 §8-4 → 已上报维护者 | 待裁(影响面已实读:拒绝的是**已错**形态) | +| 5 | **T4「移除门」候选**(E-4 缓存门;性能面) | TODO #57 | 待裁(附补证清单) | +| 6 | 元组/数组字面量的**非 ident/非调用**元素(`elem_node_optional` 保守判 0) | T2 报告 §8-5 | 已登记 | +| 7 | `struct S[T] { a: T }` 实例化为 `T = int?` 的字段(字段类型节点是泛型形参 ident) | T2 报告 §8-5 | 已登记 | +| 8 | `&s.a` 写入丢失(`UOP_REF` 无字段分支 ⇒ 取临时量地址) | T2 报告 §8-1(预存缺陷) | 已登记 | +| 9 | `MAX_GENERICS = 4` **不同批**(写入侧有界 + parse 期硬报) | 裁-CAP-2 附问 | 维持(本批不动) | +| 10 | `sh_match_bit`/`sh_match_first_missing` **遗留**(仅 selftest ≤62 域) | T3 报告 §6-3 | 已登记(生产已换 `mc_*`) | +| 11 | 侧表内存**只增不减**(编译进程内高水位;与既有侧表同族) | T3 报告 §6-4 | 已登记不修 | +| 12 | `.cir` 命中路径按**绝对 var id** 恢复(依赖「同指纹 ⇒ 同生成序」不变量) | T4 报告 §2 关联事实 | 已登记(混态实证成立) | + +### 6. 偏差(6 条) +| # | 现象 | 处置 / 出处 | +|---|---|---| +| 1 | **T2 零足迹事故**:首版实现未门控 ⇒ 非可选 `generics_test.cr` 的 `.ccr` **+168B(全在 TYPE 段)** | 根因 = 辅助函数无条件调 `res_type_node`(对数组/命名类型节点**每次 alloc 新类型行**)⇒ 12 处补 `g_optrep_on` 门后逐字节复原;**教训 = 「新增类型面查询前必须核是否 alloc」**(T2 报告 §3) | +| 2 | **T2 STR 推断被实测推翻**:原推断「新增 `Some` 串」不成立 | 实测新增的是 `box`/`match_res_rep` 两个 IR 变量名(`Some` 早已驻留);m7 另 −`const`;非可选程序 STR 差集为空(T2 报告 §5) | +| 3 | **T3 MB 突变首版假阴性**:跨记录用例只调构造函数(走符号表、不经变体记录)⇒ 基址碰撞未暴露 | 补强为「大枚举缺臂须在声明小枚举之后仍报 TM03 + 正确反例名」后转红;**教训 = 「用例必须落在经记录的判定路径上」**(T3 报告 §7-1) | +| 4 | **T2/T3/T4 三次断电(API 402/畸形响应)** | 固盘纪律 = `t2-state.md`/`t3-state.md`/`t4-state.md`/`t5-state.md` 分段更新(本批新增纪律,已生效三次接力) | +| 5 | **T1 计划勘误**:「本树含 P6 记录之后的修复」推断作废 | 图证据 `jj diff --from 97f4394f --to 661ae569` = 仅 2 个计划 md 新增 ⇒ 起点树 = P6 终态树(T1 报告 §0) | +| 6 | **T2 数组字面量 wrapper 解引用**:首版对 wrapper 判定(非值节点)⇒ 一处漏装箱 | `ast_a(en)` 解引用后修复(T2 实施过程;T3 无同类) | + +### 7. 基线换代与二进制代(逐代留痕) +| 代 | 源修订 | corec / corearch / corelsp | 自举链 | +|---|---|---|---| +| P6 终态 | `97f4394f` | `ae01de75…` / `228f82e9…` / `90eb19c6…`(= 现行白名单) | `0b2e06d0…`(P6 记录) | +| TC02 后 | `7f53305d` | `269d0a47…` / `228f82e9…` / `956cb82c…` | `c8c63985…` | +| T2 后 | `432c2e40` | `290639a9…` / `8f69d87d…` / `1c61a571…` | `819af35c…` | +| **T5 起点(T3/T4 后)** | `15357b04` | **`886a72bb…` / `55ea2a7c…` / `3889d9ed…`** | **`f69240ae…`** | + +--- + +## 容量批终态陈述(一页;维护者先读) + +**一、已实施面(本批交付,全部实测判据)** + +| 面 | 终态 | 出处 | +|---|---|---| +| **E-2 表示面聚合** | 可选**聚合槽**(结构体字段 / 数组·切片元素 / 元组元素 / 枚举载荷 / 全局槽)恒持装箱值或 `None`;裸值在**写点**装箱(纯 IR,零布局变更)⇒ 原「响亮失败(139)」转为**正确值** | T2(`432c2e40`) | +| **E-3 记录布局迁侧表** | 字段/变体/载荷**无硬上限**(侧表;记录尺寸与 `OFF_*` 不变 ⇒ 110 访问器零改动);覆盖位无界位图 ⇒ ≥63 变体判定正确;P022/P023 退役 | T3(`0b0d9aef`) | +| **E-4 缓存门** | **维持关缓存**(机制实证证伪 ⇒ 裁 (c)) | T4(`15357b04`) | +| **回归网** | 白名单**换代到 `97f4394f`**(配方重建 ×2 三 sha 逐条同);腿①/探针/突变/三态四腿齐备 | T1(`4607624c`) | +| 判据终值 | canary `95084e7b…d475`(28822B) IDENTICAL · `.ccr` 四条 96015/96158/142793/142936(**冷+暖**,差恒 143B)· 72 档 34×0/38×1 **零差异(用换代后重建基线 × 当前源)** · 探针 29 档零差异 · 五 CI job rc=0 · 枚举 59/59 · `selftest-types` 415/415 · `check src/compiler` rc=0 · 链 `f69240ae…` + N06=0 + 冒烟 42 + `--help` rc=1 · `test_optional` 48/48 · `test_enum_limit` 18/18 | T5 | + +**二、已裁定面**:裁-CAP-1 (a)(存储边界规范化)已兑现 · 裁-CAP-2 (a)(迁侧表;`MAX_GENERICS` 不同批 · `.ccr` 不 bump)已兑现 · 裁-CAP-3(先机制实证)已兑现且**证伪**⇒ 按 (c) 收口 · match 面裁决 (i) 已兑现 · #9 指针面裁 (A)(登记不修)已遵。 + +**三、剩余登记面**(12 件,逐条 = 台账 §5;其中影响下一批指向的四条): +1. **(D) 语言面拒绝 `*p = v`(pointee `T?`)** —— 已上报维护者,**待裁**(影响面实读:拒绝的是**已错**形态)。 +2. **T4「移除门」候选**(可选程序恢复 `.cir` 缓存)—— 待裁,附补证清单(自源冷/暖 · `test_optional` 同路径冷/暖)。 +3. **`c3` 指针写双路径分歧静默错值**(TODO #56)—— 高优先、预存、本批不修。 +4. **能力面遗留**:元组/数组字面量非 ident 元素 · 泛型形参字段实例化为 `T?` · `&s.a` 写入丢失 · `sh_match_bit` 遗留 · 侧表只增不减 · 绝对 var id 恢复不变量。 + +--- + +## 自检记录(T5 补) + +- **分诊诚实性**:本批三项(E-2/E-3/E-4)**全部落地或按裁定收口**;三项裁决门**逐条取裁并兑现**;**无一项被推荐案冒充裁定**。E-3 的四条前置**逐条在 T1 实核**(结论:全部未满足 ⇒ 本批逐条收口:① 读回闸 → T3 删除 ② 镜像 → T3 删除死镜像 ③ 覆盖位 → T3 无界位图 ④ 冷热/canary 判据 → T3+T5 重锁并实测「不 bump」)。 +- **根因 vs 止血**:① T2 直达「表示律」(写点规范化单源,非逐点特判);② T2 的 global 面挖到**两层预存根因**(常量折叠 + 静态度量初值)而非只补一层;③ T3 走**记录布局**(侧表)而非逐点护栏;④ T4 先**证伪**再收口(不臆造修复面)。 +- **与既有裁决的一致性复核**:P4 裁决 5(解包侧判表示,局部面不变)· D20/D22/D23 · D28(两口径 + 冷/热)· TODO #26(判据口径)· P6 全批收纳(`.ccr` v9 / loader 硬校验 / 回归网 / `MIN_CASES`)· P022/P023 退役属「上限解除」的**必然结果**(非回退)。 +- **占位符扫描**:无 TBD;未实测项(自源冷/暖 · LSP 会话级)逐处标注。 +- **本计划的自我限制**:本计划 T5 前**未跑构建/测试**(纯只读侦查);三任务实施期的全部数字为实跑,出处逐条标注;**T5 的回归与判据为实跑**(`/tmp/capt5/**`)。 diff --git a/docs/superpowers/plans/2026-09-15-fail-closed-diagnostics.md b/docs/superpowers/plans/2026-09-15-fail-closed-diagnostics.md new file mode 100644 index 00000000..1077cade --- /dev/null +++ b/docs/superpowers/plans/2026-09-15-fail-closed-diagnostics.md @@ -0,0 +1,471 @@ +# fail-closed 判据线实施计划:报错即拒(默认全阻断 + 豁免登记表) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 兑现**维护者已裁「要改」的方向 = fail-closed**:把编译器语义收敛到「**报出错误 ⇒ `rc=1` 且不产生产物**」,消灭「诊断照打、二进制照出」的静默错产物类。机制 = 把 `src/compiler/main.cr` 的**硬名单闸反转为「默认全阻断 + 已知命中面豁免登记表」**(现行正向名单 ⇒ 默认 `hard = 1`,豁免表 = 白名单:码 + 语料位点 + 理由 + 生效范围)。**全批纪律:新面 report-only 先行、命中清单审查后才开门;命中面过大 ⇒ 转独立批(停条件)。** + +**Architecture:** 六条主线:① **前置侦查 + 冻结基线重建 + 锚点实核**(T0,源码零改动);② **全语料 report-only 基线(核心产物)**(T1:用**现有二进制**在 build 面语料上采全量命中清单——现有 `check` 面已是 fail-closed 闸,是天然仪器,**零源码改动**即可产出清单);③ **分诊表定稿 + 裁决门取裁**(T2,零代码);④ **闸门机制实现 + report-only 开关**(T3:反转 + 豁免表 + 隐藏旗标 + 采集器);⑤ **命中面处置**(T4:逐条归因——修根因 / 修语料 / 登记豁免);⑥ **开门 + 零产物语义 + 回归收官**(T5/T6)。 + +**Tech Stack / 锚点(本实例实读,行号 = 当前工作树):** 闸门三处 + 一处已收紧面——① **语法面闸**:`src/compiler/main.cr:134`(`parse_all()` 后 `g_diag_count > 0 ⇒ print_diagnostics(); return 1`)+ `:135`(`g_error_count > 0 ⇒ print_parse_errors(); return 1`);② **类型面闸(本批的对象)**:`main.cr:146-179`(`if g_diag_count > 0 { 硬名单扫描 }`;**6 条语句行** `:152`/`:153`/`:154`/`:160`/`:166`/`:174` 覆盖 **11 个码常量**;`if hard != 0 { return 1; }` 在 **`:178`**);③ **安全检查面闸(已 fail-closed)**:`main.cr:594-603`(`base_diags` 快照 → `ptr_analysis_all()` / `region_check_all()` / `provenance_verify_all()` → `g_diag_count > base_diags ⇒ return 1`,**无码过滤 = 任意诊断阻断**);④ **`check` 面闸(已 fail-closed)**:`main.cr:443-451`(`if g_diag_count > 0 { return 1; }`)。 +其余落点:`main.cr:399-402`(`run` 走同一 `run_frontend` 闸)· `:431-438`(`--static` 前置 `rt.cr`)· `:440-441`(`fe_rc` 唯一入口)· `:473`(`make_cir_cache_dir`)· `:565-591`(`cir`,**不过**安全检查面)· `:620-662`(`ccr`)· `:665-703`(`build`:`:671` `.ccr` 路径 / `:674-679` `save_ccr` / `:682-702` 拼 `corearch` 命令行并 `system()` / `:702` `return exit_code`)· `diag.cr:122-150`(`print_diagnostics`,码面 = `cat = ec/1000` + `num = ec%1000`,`diag.cr:86-104` 前缀表 + `:106-110` 补零)· `checker.cr:820-828`(`check_error` 唯一写入器,记录 40B = `globals.cr:182` `DIAG_REC_SIZE`)· `checker.cr:490-495`(`diag_type_incompatible` 组合判定)· `globals.cr:463-500`(`reset_frontend_state` 清 `g_diag_count`;`check_all` 自身亦在 `checker.cr:3805` 清零)· `src/ci/run.sh:31-33`(check job)/`:36-54`(suite job:`:44` `*_mini*.cr` SKIP、`:46` 空文件 SKIP)/`:119-130`(full-bootstrap)· `tools/baseline/parity_run.sh:30`(`CORPUS_TOTAL=72`)/`:32-40`(逐档 `clean-cache` + `check`)/`:68-71`(计数硬断言)· `tools/baseline/probes_run.sh`(`PROBE_TOTAL=29`)。 + +**Spec / 依据:** ① **维护者 2026-09-14 裁定「要改、方向 = fail-closed」**(本批成立的前提;裁定文本经协调者转述,**无独立文件载体 —— 须 T0 向维护者核一句原文并落报告**);② **P6 裁决门 裁-P6-2 的 (b)(c) 两问**(`docs/superpowers/plans/2026-09-14-r2-p6-tail.md:58`:**(b)** 非硬名单诊断是否收紧到 `build` 面;**(c)** 新硬错入闸判据是否成文化为「判定继续 ⇒ 产出静默错产物」)——**本批 = 该两问的最强形式,未取裁前的方向性背书不成立**;③ **P5 已落政策**:P-A(ICE04 硬错,`plans/2026-09-13-r2-p5-cleanup.md` + `main.cr:161-166` 注)、TA02(TODO #32 + `main.cr:167-174` 注)——**本批不得回退这两条**;④ **`docs/error-codes.md`**(码族/消息模板/统计);⑤ **E-14 的活证据**(P6 T0 报告 §6-5:`tests/suite/ptr_arith.cr` `check` rc=1(B04 `12:8`)而 `build --static` rc=0 且产出 canary ELF)。 + +--- + +## 前置状态(**本计划未跑任何构建/测试**;下列全部为实读源码/文档或既有报告记录值,出处逐条标注) + +- **工作树 = `/tmp/p7wt`,基线 = P6 终态提交 `97f4394f`**(协调者给定)。**本实例 `jj` 命令零执行**(纪律:统一由协调者提交);`ps` 实查无并发编译进程的步骤留给实施任务。 +- **诊断通道实读 = 5 条**(全仓 `check_error(` / `g_diags` 写入点 grep 实证): + 1. **词法/语法消息通道**:`lexer.cr:39-43` `add_error()`(`g_errors` = 驻留串下标)+ `parser.cr` 同族 `add_error` —— **20 个调用点**(`lexer.cr` 10:`:147/:226/:233/:236/:238/:240/:246/:436/:454/:456`;`parser.cr` 10:`:468/:591/:704/:1472/:1474/:1477/:1479/:1482/:1531/:1580`)。无码,打印形 `error: `(`diag.cr:154-162`)。**已 fail-closed**(`main.cr:135`)。 + 2. **语法诊断码通道**:`parser.cr` 直接写 `g_diags`(15 处写入 / **8 个码**):`EC_P_EXPECTED`(`:121`/`:130`)、`EC_P_FIELD_SYNTAX`(`:1828`)、`EC_P_PARAM_TYPE`(`:1847`)、`EC_TA_UNKNOWN_TAG`(`:829`)、`EC_P_NESTED_FN`(`:927`)、`EC_P_TOO_MANY_PARAMS`(`:1453`)、`EC_P_STRUCT_LIMIT`(`:1647`)、`EC_P_ENUM_LIMIT`(`:1723`/`:1728`)。**已 fail-closed**(`main.cr:134`)。 + 3. **类型诊断码通道(`check_all` 内,**本批反转对象**)**:`checker.cr` 的 `check_error` 调用面 = **55 处 / 32 个码**(清单见「表 1」)。`check` 面已 fail-closed(`main.cr:448`);**`build` 面只拦 11 码**(`main.cr:152-174`)⇒ **本批的唯一缺口面**。 + 4. **安全检查面通道(build/ccr 专属)**:`region_check.cr:83`/`:134`(`EC_B_LIFETIME`/B11)、`provenance_verify.cr:68`(`EC_TU_DEREF`/TU03)、`:91`/`:99`/`:126`(`EC_TK_INDEX`/TK01 复用)。**已 fail-closed**(`main.cr:600`,任意新诊断阻断)——**注意:`check` 面不跑该面(`main.cr:451` 提前返回)⇒ 该面是「check rc=0 / build rc=1」的**反向不一致**存在性证据**(与 `ptr_arith` 的正向不一致成对;本实例未构造具体语料,**须 T1 实测登记**)。 + 5. **非 `g_diags` 的 `error:` 打印通道**:`main.cr`(`:32`/`:94`/`:421`/`:434`/`:640`/`:675`/`:679`、写入失败 `:650-652`/`corearch.cr:510`/`:586`/`:596`)、`corearch.cr`(装载/链接失败全分支)、`interp.cr:408`/`:413`/`:738`/`:743`。**已 fail-closed**(各自 `return 1`)。 +- **码面实读统计**:`ast.cr` 定义码 ≈ **146**(`docs/error-codes.md:275-287` 统计表自述「总计 ~146」);**可达码(有 raise 点)= 42**(本实例用 `check_error(EC_*` / `diag_type_incompatible(…, EC_*` / `DIAG_REC_SIZE, EC_*` 三形态全仓 grep 去重实得 43 项,其中 `EC_SIZE` 为 `DIAG_REC_SIZE` 的子串假命中,扣 1)⇒ **其余 ≈104 个码「定义零 raise」(本批不动,登记)**。**码名两式口径**(照 P6 T0 表 D-6):常量名 `EC_P_ENUM_LIMIT=1022`(`ast.cr:372`)而**用户面实印 `error[P22]`**(`diag.cr:132-136` 的 `num = ec%1000 = 22`)——**本计划一律「常量名(用户面码)」双写**。 +- **现行 11 码硬名单**(`main.cr:152-174` 逐行实读):`EC_R_OOB`(R002)· `EC_TK_SLICE_BOUNDS`(TK05)· `EC_TK_SLICE_LEN`(TK06)· `EC_TS_MISSING_FIELD`/`EC_TS_UNKNOWN_FIELD`/`EC_TS_FIELD_TYPE`/`EC_TS_FIELD_DUP`(TS01-04)· `EC_TK_ELEM_TYPE`(TK02)· `EC_TM_EXHAUST`(TM03)· `EC_ICE_TY_INDET`(ICE04)· `EC_TA_DECL`(TA02)。 +- **已知非硬名单诊断**(`ast.cr:487-489` 定义、`main.cr` 名单外):`EC_TG_ARG_COUNT`(TG01,**零 raise**)· `EC_TG_BOUND`(TG02,raise 于 `checker.cr:1227`/`:1233`/`:2059`)· 族内其余(N/TF/TB/TC/TM/TK/B 面)。 +- **`check` 面不产生产物**(实读:`main.cr:444-451` 在 `:467` `optrep_begin()`/`:473` `make_cir_cache_dir()` 之前返回)⇒ 「零产物」的缺口**只在 `build`/`ccr`/`cir` 面**。 + +--- + +## 表 0:闸门 × 命令矩阵(**现状**,本实例实读;本批的判定面边界) + +| 命令 | 语法面闸 `:134/:135` | 类型面硬名单闸 `:146-179` | 安全检查面闸 `:594-603` | 现行 rc 语义 | 产物 | 本批是否改 | +|---|---|---|---|---|---|---| +| `check` | ✔ | ✖(不用;`:448` 独立更严) | ✖(`:451` 前返回) | **任意诊断 ⇒ rc=1** | 无 | **不改**(已 fail-closed) | +| `build` | ✔ | ✔ | ✔ | 名单内 ⇒ rc=1;**名单外 ⇒ rc=0 + 照出产物** | `` ELF + `.ccr` | **本批对象** | +| `ccr` | ✔ | ✔ | ✔ | 同上(名单外 ⇒ 照出 `.ccr`) | `.ccr` | 同批(同一 `run_frontend` 闸) | +| `cir` | ✔ | ✔ | ✖(`:565` 分支在 `:594` 前返回) | 同上(名单外 ⇒ 照出 `.cir`) | `.cir` | 同批(闸同源;安全检查面**不在本批扩面**) | +| `run` | ✔ | ✔ | ✖(解释器路径) | 名单外 ⇒ 照跑 | 无 | 同批(闸同源) | +| `selftest-types` / `selftest-purity` | — | — | — | 自测通道,不读源文件 | 无 | **不改** | +| LSP(`src/lsp/lsp.cr`) | — | — | — | 编辑器诊断推送(不产生产物) | 无 | **不改**(登记:非产物面,fail-closed 不适用) | + +**判定面定义(本批语义)**:**error = 阻断**(`rc=1` + 零产物);本仓**现行无 warning 通道**(全仓无 `warning[` 打印面;`TM04`(`EC_TM_REDUNDANT`)在 `main.cr:159` 注中记为「软面登记(同 Rust unreachable-pattern 的警告口径)」但**打印仍是 `error[TM04]`**)⇒ **本批要么把软面码降级为真 warning,要么登记为豁免**(见裁-FC-2)。 + +--- + +## 表 1:全诊断面清点(**核心产物**;`check_all` 面 32 个可达码逐码分诊) + +> 口径:**① 必须阻断** = 语义上「判定继续 ⇒ 产出静默错产物 / 判定不可信」(照 P6 裁-P6-2(c) 的成文化判据);**② 必须豁免** = 有**已实测语料命中**且该命中属已知假阳性/过渡期,或**已有政策裁定**使其非 error;**③ 本批不动** = 在闸门作用域之外(另见表 0/表 2)。**「命中」列 = P6 T0 报告 §3.1 的 72 档实跑记录值(`/tmp/p6t0/corpus_rc.txt`),非本实例实测;套件面命中一律标「须 T1 实测」。** + +| # | 码(用户面) | 常量(`ast.cr` 行) | raise 点(`file:line`) | 语义 | 72 档命中(P6 T0 记录) | 分诊 | 依据 | +|---|---|---|---|---|---|---|---| +| 1 | TA01 | `EC_TA_ASSIGN`(:407) | `checker.cr:3376` | 赋值号两侧类型不匹配 | 0(未见于首条诊断表) | **①阻断** | 与 TA02 同族(声明位/赋值位同形);符号类型与实际值分歧 ⇒ 静默错值 | +| 2 | TA02 | `EC_TA_DECL`(:408) | `checker.cr:530` | 声明注解 vs 初值 | 0(P5 T6 report-only 全语料零命中) | **①阻断(已在名单)** | P5 政策,不得回退 | +| 3 | TF01 | `EC_TF_RETURN`(:417) | `checker.cr:1525`/`:2240` | 返回型不匹配 | **5**(`chan_test`/`conc_test`/`go_e2e_test`/`go_final_test`/`go_parallel_test`,P6 T0 §3.1「TF01 返回型不匹配(并发/goroutine 族)」) | **②豁免(待裁,须逐档归因)** | 命中面 = CI `suite` job **实际 build 的 21 档中 5 档**(`run.sh:36-54`)⇒ 直接入闸 = 套件 job 转红 + 可能改写 ELF canary 族判据。**同码先例 = 已修的假阳性**(`src/compiler` 内两条 TF01 系误报已由 P6 TF01 清理批修,TODO #40 ⇒ `check src/compiler` rc=0)⇒ 本批须先判这 5 档是同族误报还是真错 | +| 4 | TF07 | `EC_TF_ARG_TYPE`(:423) | `checker.cr:3727`(**唯一 raise**,`@raw_int` 内建位;非调用位点——#20 登记的「调用位点判定点不存在」实读 = 已知 `SYM_FN` 非泛型分支 `checker.cr:2766-2797` **连实参推断都不走**(直接 `return sym_type(si)`;T4a 报告锚 `:2744-2753` 为打补丁前行号,本实例实读漂移 **+22**)) | 实参类型不匹配 | **1**(`ptr_ref_first.cr`,P6 T0 §3.1「TF07 实参类型 1」) | **②豁免(待裁)** | 同行 3:命中档在 suite job 的 build 集内 | +| 5 | TF08 | `EC_TF_METHOD_NOT_FOUND`(:424) | `checker.cr:2277` | impl 缺接口方法 | 0 | **①阻断** | 接口契约未履行 ⇒ 表项/调用面不可信(同 TG02 族) | +| 6 | TF09 | `EC_TF_METHOD_ARG_CNT`(:425) | `checker.cr:2288` | 方法实参个数 | 0 | **①阻断** | 同上 | +| 7 | TF10 | `EC_TF_METHOD_ARG_TYP`(:426) | `checker.cr:2283`/`:2301` | 方法实参类型 | 0 | **①阻断** | 同上 | +| 8 | TB01 | `EC_TB_ADD`(:436) | `checker.cr:2474`/`:2990`/`:2991` | 算术操作数类型错 | 0 | **①阻断** | 算术无良定义 ⇒ 发射按错宽度 | +| 9 | TC01 | `EC_TC_IF_COND`(:452) | `checker.cr:2489`/`:2851` | 条件非 bool/int | 0 | **①阻断** | 分支判定按非布尔值 ⇒ 静默错控制流 | +| 10 | TC02 | `EC_TC_IF_BRANCH`(:453) | `checker.cr:2908` | 两分支类型不同 | 0 | **①阻断** | 结果型随分支漂移(同 TK02 族);`checker.cr:2906-2908` 已含 `TI_NEVER` 双侧豁免(返回值合并面) | +| 11 | TC04 | `EC_TC_WHILE_COND`(:455) | `checker.cr:2965` | while 条件非 bool | 0 | **①阻断** | 同 TC01 | +| 12 | TM03 | `EC_TM_EXHAUST`(:463) | `checker.cr:3097` | match 非穷尽 | 0(P3 Task 3 report-only 零命中) | **①阻断(已在名单)** | 未匹配值静默得 0(`main.cr:155-159` 注) | +| 13 | TM04 | `EC_TM_REDUNDANT`(:464) | `checker.cr:3100` | 冗余臂 | 0 | **②豁免(政策既定)** | `main.cr:159` 原文:「TM04(冗余臂)**不入**本名单 = 软面登记(同 Rust unreachable-pattern 的警告口径)」⇒ 默认全阻断会**推翻该裁定**,须登记豁免或改判为真 warning | +| 14 | TK01 | `EC_TK_INDEX`(:472) | `checker.cr:3353` + `provenance_verify.cr:91`/`:99`/`:126` | 非可索引类型 / 指针越界 | 0 | **①阻断** | 索引兜底门(P3b Task 2 接线);后三处属安全检查面(已阻断) | +| 15 | TK02 | `EC_TK_ELEM_TYPE`(:473) | `checker.cr:3567` | 数组元素异型 | 0 | **①阻断(已在名单)** | TODO #29 三校验 | +| 16 | TK05 | `EC_TK_SLICE_BOUNDS`(:476) | `checker.cr:3304` | 常量档切片越界 | 0 | **①阻断(已在名单)** | F2:修复前静默生成越界二进制 | +| 17 | TK06 | `EC_TK_SLICE_LEN`(:477) | `checker.cr:3306` | 切片长度负 | 0 | **①阻断(已在名单)** | 同上 | +| 18 | R002 | `EC_R_OOB`(:504) | `checker.cr:3320`/`:3335` | 常量档越界索引(数组/字符串) | 0 | **①阻断(已在名单)** | 同上 | +| 19 | N001 | `EC_N_UNDEFINED`(:376) | `checker.cr` **17 处**(`:2257`/`:2432`/`:3205`/`:3673`…`:3776`) | 未定义名 | **25**(P6 T0 §3.1「库单元单独 check(N01/N06/N11——本该由 concat 供货)25 档 = t2 1 + t3 13 + t4 10 + t1 1」) | **②豁免(范围 = check 面)** | 该 25 档**从不在 build 面被编译**(语料 runner 只跑 `check`,`parity_run.sh:36`)⇒ **若裁-FC-1 取「两面同闸」则必须豁免,取「build 面单闸」则无需豁免(自然不触发)** | +| 20 | N006 | `EC_N_FUNC`(:381) | `checker.cr:2809` | 未定义函数 | 同上 25 档内嵌(t1 `at_test_mini.cr` + t3 7 档 + t4 5 档) | **②豁免(范围 = check 面)** | 同上 | +| 21 | N008 | `EC_N_METHOD`(:383) | `checker.cr:2396` | dyn 方法不存在 | 0 | **①阻断** | 调用面不可解析 ⇒ 静默错分派 | +| 22 | N009 | `EC_N_GENERIC_TYPE`(:384) | `checker.cr:1000` | 泛型应用基类型不存在 | 0 | **①阻断** | 实例化不可进行 | +| 23 | N011 | `EC_N_DUPLICATE`(:386) | `checker.cr:1530`/`:1550` | 重复定义(含 hotpatch 形参不符) | **1**(t3 `ccr_io.cr`,P6 T0 §3.1 表「N11×1」) | **②豁免(范围 = check 面)** | 同上(concat 供货面) | +| 24 | TG02 | `EC_TG_BOUND`(:489) | `checker.cr:1227`/`:1233`/`:2059` | 泛型约束不满足 | 0(P5 台账:全语料 0 命中;唯一命中 = `test_generic_constr.py` 1 例登记面已重钉) | **①阻断** | 约束未满足 ⇒ 实例化假定不成立 | +| 25 | B001 | `EC_B_BORROW_MUT`(:492) | `checker.cr:2510` | 对已借用者取可变借用 | 0(P6 T0 §3.1 借用族仅 `ptr_arith` 1 档,且首条 = B04) | **①阻断(待 T1 实测复核)** | 借用模型判定;**若 T1 实测出套件面命中 ⇒ 并入 ②**(见裁-FC-3) | +| 26 | B002 | `EC_B_BORROW_IMMUT`(:493) | `checker.cr:2512` | 对可变借用者取共享借用 | 0(同上) | **①阻断(待 T1 实测复核)** | 同上 | +| 27 | B004 | `EC_B_USE_WHILE_BORROWED`(:495) | `checker.cr:2427` | 借用存续期内使用被借用变量 | **1**(`tests/suite/ptr_arith.cr` `12:8`,P6 T0 §3.1 + §6-5 活证据) | **②豁免(必选之一,见裁-FC-3)** | **该档 = ELF canary 硬闸的唯一载体**(canary 命令即 `build tests/suite/ptr_arith.cr --static`,P6 T0 §2#7)⇒ 入闸 = **canary 不可产** ⇒ 三选一(修 B04 假阳性 / 登记豁免 / 换 canary 载体) | +| 28 | ICE04 | `EC_ICE_TY_INDET`(:521) | `checker.cr:559` | 类型判定不可判(引擎 -1/桥接缺口) | 0 | **①阻断(已在名单)** | P-A 政策,不得回退 | +| 29 | TS01 | `EC_TS_MISSING_FIELD`(:482) | `checker.cr:3449` | 缺字段 | 0 | **①阻断(已在名单)** | TODO #29 | +| 30 | TS02 | `EC_TS_UNKNOWN_FIELD`(:483) | `checker.cr:3433` | 未知字段 | 0 | **①阻断(已在名单)** | 同上 | +| 31 | TS03 | `EC_TS_FIELD_TYPE`(:484) | `checker.cr:3478`/`:3502` | 字段类型不符 | 0 | **①阻断(已在名单)** | 同上 | +| 32 | TS04 | `EC_TS_FIELD_DUP`(:485) | `checker.cr:3436` | 字段重复初始化 | 0 | **①阻断(已在名单)** | 同上 | + +**表 1 计数:① 必须阻断 = 25 码**(TA01/TA02/TF08/TF09/TF10/TB01/TC01/TC02/TC04/TM03/TK01/TK02/TK05/TK06/R002/N008/N009/TG02/B001/B002/ICE04/TS01/TS02/TS03/TS04)**② 必须豁免 = 7 码**(TF01 · TF07 · TM04 · N001 · N006 · N011 · B004)**③ 本批不动 = 表 0 的七行作用域外命令 + ≈104 个「定义零 raise」码 + 5 条通道里已 fail-closed 的 4 条**。 + +**表 1-补:已 fail-closed 的 10 个可达码(本批不改,清点收编)**:`EC_P_EXPECTED`/`EC_P_FIELD_SYNTAX`/`EC_P_PARAM_TYPE`/`EC_P_NESTED_FN`/`EC_P_TOO_MANY_PARAMS`/`EC_P_STRUCT_LIMIT`/`EC_P_ENUM_LIMIT`/`EC_TA_UNKNOWN_TAG`(语法面 8,`main.cr:134`)· `EC_B_LIFETIME`(B11,`region_check.cr:83`/`:134`)· `EC_TU_DEREF`(TU03,`provenance_verify.cr:68`)(安全检查面 2,`main.cr:600`)。⇒ **可达码 42 = 25 + 7 + 10。** + +--- + +## 表 2:语料面与**转红面**预估(第 4 项交付的输入;全部为记录值或实读构成) + +| 语料面 | 构成(实读) | 面 | 现行状态 | 全阻断后的预估转红面 | 出处 | +|---|---|---|---|---|---| +| 72 档 | t1 `tests/suite/*.cr` 32 + t2 自源 2 + t3 后端/内核 15 + t4 stdlib 19 + t5 examples 4 | `check` | rc **34×0 / 38×1**;38 档划界 = 0 字节 fixture 2 + P21 负例 2 + examples 解析错 2 + 库单元单独 check(N01/N06/N11)25 + TF01 5 + TF07 1 + B04 1 | **若 check 面不改:零变化**(表 0);若取「两面同闸」:N 族豁免条生效 ⇒ **≈25 档 rc 1→0**(精确档数取决于 T2 豁免表的 `scope` 字段 ⇒ 判据换代 + 逐档重锁) | P6 T0 §3.1(`/tmp/p6t0/corpus_rc.txt`)+ `tools/baseline/parity_run.sh:30` | +| CI `suite` job | `tests/suite/*.cr` **实 build 21 档**(32 − 2 空 − 9 `*_mini*`;`run.sh:44`/`:46`) | `build` | 全 rc=0 + 运行通过 | **≥7 档**(TF01×5 / TF07×1 / B04×1;见下「转红面预估方法」) | `run.sh:36-54` + P6 T0 §3.1 | +| CI `full-bootstrap` job | `src/compiler/main.cr --static -O 0` ×2(corec2/corec3)+ `--help` | `build` | rc=0 · `cmp` IDENTICAL | **0**(`check src/compiler` rc=0 ⇒ 零诊断 ⇒ 闸无差别) | `run.sh:119-130` + P6 T0 §2#6 | +| CI `check` job | `build_selfhost` + `check src/compiler` | `check` | rc=0 | **0** | `run.sh:31-33` | +| ELF canary | `clean-cache` → `build tests/suite/ptr_arith.cr --static` | `build` | rc=0 + `95084e7b…d475`(28822B) | **1 档 = 硬冲突**(B04 命中)⇒ **canary 不可产** | P6 T0 §2#7 | +| `.ccr` 四条判据 | `ptr_arith` / `generics_test` 两口径 | `ccr`+`build` | 四条 = T3 新锁值(96015/96158/142793/142936) | **≥1 档**(`ptr_arith` 含 B04) | P6 T6 §Step 2 + `p6-task4-report.md` §4 | +| `tests/selfhost/*.py` 套件 | 51 档 py;其中 **44 档含 `"build"` 调用(126 处)**(本实例 `grep -l`/`grep -rho` 计数) | `build` | 全绿 | **须 T1 实测**(负例套件断言 rc=1 不受影响;风险 = 「断言 build rc=0」的档) | `src/ci/run.sh:84-111` 挂点 28(`grep -c 'python3 tests/selfhost/'` 实核)+ 本实例 `grep -rho '"build"'` | +| 探针语料 | `tests/probes/*.cr` 29 档 | `check` | **17×0 / 12×1**(12 档为**故意的负例**) | **零变化**(check 面不改 ⇒ 两态对拍仍为零差异) | `tests/probes/README.md:58` | +| `examples/*.cr` | 4 档 | `check` | 2 档解析错(既有) | 零变化(check 面) | P6 T0 §3.1 | + +**转红面预估方法(**硬要求,T1 落地**)**:① **不靠猜**:T1 用**现有二进制**对「build 面语料全集」逐档跑 `build`(`-o /tmp/fc1/out/`,**保留产物供二次比对**),采集 `rc` + 日志中全部 `error[XX]` 行的(码, `file:line`)二元组 ⇒ 产出 `hits.tsv`;② **逐档归因表**:每档给「码 → 归因(真错 / 假阳性 / 语境缺失 / 政策软面)→ 处置(修根因 / 修语料 / 登记豁免)」;③ **分批开门办法**:批次 1 = 全反转 + 豁免表覆盖 T1 全清单(**先保 CI 绿**);批次 2..n = 逐条撤销豁免(每条以「根因已修 / 语料已正」为撤除判据),**只减不增**。 + +--- + +## 裁决门(**开工前置**;逐条 = 问句 / 影响 / 本计划推荐 / 未取裁时的行为) + +> 纪律继承 P6:**未取裁的项一律不实施**;**不得**以「推荐案」冒充裁定;凡「先裁」项,护栏写进任务卡正文(P6 事故 §7-1 的教训)。 + +| # | 问句(逐条照抄给维护者) | 影响 | 本计划推荐 | 未取裁时 | +|---|---|---|---|---| +| **裁-FC-1** | `check` 面与 `build` 面**是否用同一闸门**?**(A)** 现状(check 严 / build 宽)+ 本批只反转 build 面;**(B)** 两面同闸同表(check 也走豁免表 ⇒ 72 档 rc 分布换代、parity 基线重锁);**(C)** 两面同闸但豁免表带**生效范围**字段(`build`/`check`/`both`),初始绝大多数条 = `build` 范围 ⇒ check 基线不动。 | 表 0 全行;72 档 + 探针两条对拍腿的可用性 | **(C)**(同机制、显式登记差异、check 基线零扰动;(B) 会让 ≈25 档 rc 1→0,parity 腿从「零差异」退化为「大面 diff」,丧失回归网价值) | 按 (A) 执行:只动 build/ccr/cir/run 四面,check 不动;差异显式登记 | +| **裁-FC-2** | **豁免表的载体与粒度**,以及**软面码(TM04)**的处置:**(a) 粒度** = 码级 / 码×语料位点级(`file:line`)/ 码×范围级;**(b) 载体** = 源码内静态表(照 `corearch.cr` 的 `g_instance_decl` 表先例)/ 仓库配置文件 / CI 白名单;**(c) 换代纪律** = 只减不增 + 撤除须带根因证据 + 新增须维护者批?**(d)** TM04 是登记豁免还是改为**真 warning 通道**(`warning[...]` 前缀,不阻断)? | T3 的实现形态与全批的「豁免可审计性」 | **(a) 码级 + 位点列作证据字段**(位点级更窄但脆弱:语料一行漂移即失效;码级简单但允许同码在别处静默 ⇒ 用「每次开新面必重跑 report-only」补偿);**(b) 源码内静态表**(Core 无配置文件读取面、CI 不跑本地判据);**(c) 只减不增**;**(d) 登记豁免**(引入 warning 通道 = 新诊断类别,超本批章程) | 表形态 = 源码内 `code → {scope, locus, reason}` 静态表;TM04 登记豁免 | +| **裁-FC-3** | **B004(`ptr_arith.cr:12:8`)与 ELF canary 硬闸的冲突**:B004 入闸 ⇒ canary 不可产。三选一:**(i) 修 B004 假阳性**(实现末次使用/NLL 级借用收缩——**语义变更,工作量未估**);**(ii) 登记豁免 B004**(canary 保住,但「check rc=1 / build rc=0」的活证据继续存在);**(iii) 换 canary 载体**(判据换代:canary sha 全链作废、须重建白名单与全部历史比对基线)。 | ELF canary 判据(本批硬闸)+ suite job | **(ii) 本批登记豁免 + 同批开 TODO 条目把 (i) 立为独立批**((i) 是语义变更不是闸门变更;(iii) 代价 = 判据换代,不能由实施者自裁) | T5 不实施 B004 阻断;B004 豁免条目标「退出条件 = 借用收缩落地」 | +| **裁-FC-4** | **命中语料(≥7 档)的处置**:TF01×5(并发族)/ TF07×1(`ptr_ref_first`)/ B04×1 —— **修前端假阳性 / 修语料(删除或改判负例)/ 登记豁免**?(修语料涉 `tests/` 面改动,且 `tests/suite` 是 CI 正例集;改 neg 例会动 `run.sh:36-54` 的 SKIP 语义) | CI `suite` job 转红面 | **先逐档归因(T1)再判**:真误报 ⇒ 修前端(照 P6 TF01 清理批先例 `p3-task3-report`/TODO #40);真错误 ⇒ 语料归为负例(照 `*_mini*` SKIP 先例,**须维护者批**);**本批不预判** | 全部登记豁免 + CI 保持绿;修复批次另立 | +| **裁-FC-5** | **与 P6 待裁项的接口**:**(a)** 本批是否即为 裁-P6-2**(b)**(非硬名单诊断收紧)与 **(c)**(入闸判据成文化)的**最强形式**?若维护者只取 (b) 的「`build` 面汇总告警」档(不阻断),本批**降级为 report-only**(T3 只落采集器、不落闸);**(b)** 裁-P6-2**(d)**(`*T ← 0/None`)/**(e)**(`--verify-named-dedup`)与本批无耦合((d) 是规则语义、(e) 是调试通道)——**本批不动**,确认? | 全批章程 | **(a) 取最强形式**(否则本批无对象);**(b) 确认无耦合、不动** | 本批降级为「report-only 常设」:T3 落采集器 + 清单,**不反转默认闸**;命中表交维护者处置 | +| **裁-FC-6** | **「零产物」的边界**:失败时 **(a)** 是否删除**本次**写入的 `.ccr`(`main.cr:678` 写在 `corearch` 之前 ⇒ corearch 失败即留半成品;纯静态路径的 ELF 在 `ctx_emit_static` 末尾一次性写(`ld.cr:525-529`),本身不留半成品)?**(b)** `.core/cache/cir/*.cir` 过程缓存与 `.core/cache/cir/` 目录(`main.cr:473`)是否算产物? | T5 的实现面 | **(a) 删除**(仅限本次 `save_ccr` 成功且 `corearch` rc≠0 的路径;**已存在的旧产物不动**——不删用户文件);**(b) 登记为口径豁免**(过程缓存非交付产物;且 frontend 闸在 IR gen 之前 ⇒ 闸触发时不产生任何缓存) | 按推荐执行(不外扩删除面) | + +--- + +## 任务总表(**本批实施面**;状态一律「待实施」,收官按实测回填) + +| 任务 | 内容 | 依赖 | +|---|---|---| +| T0 | 前置侦查 + **冻结基线重建**(P6 终态 `97f4394f`)+ 起点判据(表 B)+ 锚点实核(表 D)+ 维护者裁定原文核录(**源码零改动**) | — | +| T1 | **全语料 report-only 基线(核心产物)**:`tools/baseline/build_gate_run.sh` + 命中清单 + 逐档归因表(**零源码改动**,用现有二进制) | T0 | +| T2 | 分诊表定稿 + 豁免表草案 + **裁决门提交**(零代码) | T1 | +| T3 | 闸门反转 + 豁免表 + 隐藏 report-only 通道 + 采集器(**默认关**) | T2 + 裁-FC-1/2 | +| T4 | 命中面处置(逐条:修根因 / 修语料 / 登记豁免)+ 新套件 | T3 + 裁-FC-3/4 | +| T5 | **开门**(默认全阻断)+ 零产物语义 + CI 面同步 | T4 + 裁-FC-6 | +| T6 | 收官:全量回归 + 判据复验 + 统一台账 + 文档 + 批终态 | 全部 | + +**顺序理由**:T1 必须先于一切(**先量后改**:本批最大风险 = 一次改动让大量既有语料转红);T2 的取裁决定 T3 的实现形态(同闸 vs 单面、豁免粒度);T4 依赖 T3 的采集器做二次核对;T5 的开门是**单点翻转**(默认值一行),其判据全在 T1–T4 的证据链上。 + +--- + +## Global Constraints(照 P6 简版) + +- **jj only(禁 git 含只读/复合)**;**提交路径限定**(多 agent 在场:一律 `jj commit -m … `);所有命令 `nice -n 19`;一构建一编译**串行**(每段前 `ps` 实查);cwd = 仓库根(`/tmp` cwd 触发 import 解析假失败)。 +- **基线纪律**:每任务开工先取**同任务起点**基线(binary sha + 语料 rc 表 + 探针 rc 表),**不得沿用上一任务的计数**。冻结基线 = 由 `tools/baseline/rebuild.sh`(配方 `tools/baseline/REBUILD.md`;pinned revision + 三 sha 白名单)重建;**P6 终态的白名单值须 T0 实核后落表**(P6 期的白名单 = P4 收官 `9bcb7083` 产物,**不是**本批基线 ⇒ 本批须以 P6 终态 revision 重建并**新锁白名单**)。 +- **ELF canary(硬闸,全批)**:`clean-cache` → `build tests/suite/ptr_arith.cr --static` → sha256 `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(28822B)。**本批的 canary 语义有变体**:T5 开门后若 B04 豁免生效,canary 应**逐字节同**(豁免 = 不阻断,发射面零变化);**若 canary 不可产 ⇒ 立即停下**(= 裁-FC-3 未取裁或取 (i)/(iii) 的信号)。 +- **发射面声明(本批必须显式声明)**:**本批声明发射面零变更**(闸门 = 前端 rc 语义;不触 `ir_gen`/`lower_to_ccr`/`emit`)。任何 canary/`--dump-objects`/`.ccr` 逐字节差异 ⇒ 停下(越界信号)。 +- **`.ccr` 记录值引用口径(D28 继承)**:一律带**命令口径**(`corec ccr F -o O` vs `corec build F -o O --static`,差恒 143B)+ **缓存态**(冷/热;非可选程序冷≠热为**预存**豁免);比较任何 `.ccr`/缓存态产物前 `clean-cache`。 +- **判据口径按 TODO #26**:不采用「与旧版逐字节同」当语义证据;结构性断言 + 语义零变化 + 自举稳定为本仓主判据;「≥N 例」= 下限(可增不可减);**自测用例只增不减**(重钉/改名允许,删除须给死亡证据)。 +- **三态纪律(不得稀释)**:引擎 -1 不得当 0/1 用;载入失败/段缺失/行不可译/项不可建 ⇒ **拒绝**。**新硬错先 report-only**:本批新增的任何 rc=1 门(含闸门反转本身)必须先在**全语料**(72 档 + `tests/selfhost` **内联源** + `src/stdlib` + `examples` + `src/compiler` 自指 + **58 套件全枚举**)report-only 跑一遍,清单审查后才开门。 +- **清单三面同核(B.6,硬性)**:任何清单/段表改动必须三面同核——`build_selfhost_native.py`(`corec_files`/`corearch_files`/`corelsp_files`)↔ `src/targets/x86_64-linux/_import.cr` ↔ `src/compiler/_import.cr`;每任务回归面**必含** `tests/selfhost/test_backend_bootstrap.py`(rc=0 + 构建日志 `error[` = 0)。本批新增的 runner/语料(`tools/baseline/*`、`tests/probes/*`)**不进**任何编译清单。 +- **不得回退既有收纳**:TS01-04/TK02 硬门(#29)、TM03/TG02 门(#31/#33)、F2 常量档拒绝语义与「N 不回身份」、F5 槽位契约(#25/#28)、效应/纯度判据(#26/#27)、`iface_satisfies` 轴序 A>C>B、P4 全批、P5 全批(ICE04/TA02/影子下线)、P6 全批(β/¬ 面/never/文档)——**一律不动**;冲突 → 停下上报。 +- **本语言无三元运算符**;取模须非负;键比较不得依赖 i64 回绕;noclobber(`>|`);**不写 `buf == 0` 形态的空判**(string-int 混比在 bootstrap 侧编成 `str_eq` ⇒ 自崩);bootstrap 构建的 corec 对 **`&&`/`||` 两侧无条件求值** ⇒ **不得写 `guard && 表读`**(`p6-task4-report.md` §3 先例:须嵌套 `if`)。 +- **多 agent 纪律**:不并发构建;提交路径限定;实施时逐任务串行;突变控制一律在**仓库外实拷贝**(`tests/` 必须**实拷贝**而非软链)。 + +--- + +## 起点基线值(**P6 终态记录值;本计划未复测**,出处逐条标注) + +| # | 项 | 值 | 出处 | +|---|---|---|---| +| 1 | 72 档 `check` rc 分布 | **34×0 / 38×1**(含 2 档 0 字节 fixture `test_generics.cr`/`test_control_flow.cr`、P21 嵌套 fn 负例 `at_test_mini4/6.cr`、examples 两档解析错 `test_scale.cr`/`test_sum.cr`——**不得当回归**) | P6 T0 §3.1(`/tmp/p6t0/corpus_rc.txt`) | +| 2 | 五 CI job | 全 **rc=0** | P6 T6 §Step 1 | +| 3 | `check src/compiler` | **rc=0**(零诊断;P6 TF01 清理批后成立,#40) | P6 T0 §2#2 / P6 T6 | +| 4 | `selftest-types` | **415/415** | P6 T6 §Step 1(T5 收紧 `MIN_CASES=415`,`test_type_engine.py:20`) | +| 5 | ELF canary | `95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475`(**28822B**) | P6 T0 §2#7 / P6 T6 | +| 6 | 自举链 | `corec2 == corec3` IDENTICAL(**`0b2e06d0c8b2f78402d08950364fb224db697fdf2e5b6e5d0d6a06e85fc581c1`**)+ N06=**0** + 冒烟 **42** + `--help` rc=1 | P6 T6 §Step 2 | +| 7 | `.ccr` 两口径四条(冷态) | `ptr_arith` **96015B** `680a6f98…` / **96158B** `76f36e6a…`;`generics_test` **142793B** `41e9d845…` / **142936B** `704316c8…`;**差恒 143B** | P6 T3 新锁值(P6 T6 §Step 2 复验) | +| 8 | 全枚举 | **58/58**(51 selfhost + 7 bootstrap) | P6 T6 §Step 1 | +| 9 | 探针 29 档 | rc **17×0 / 12×1**(12 档为故意负例) | `tests/probes/README.md:58` | +| 10 | `--dump-objects` / `.cir` DOT | 3521 行 / 165915B `b1bdf480…` | P6 T6 §Step 2 | +| 11 | 冻结基线三 sha(P6 期白名单,**本批须重建换锁**) | `5d2b15ad…` / `493dc490…` / `18b94bd9…` | `tools/baseline/REBUILD.md` | + +--- + +## Task 0:前置侦查 + 冻结基线重建 + 锚点实核(**源码零改动**) + +**Files:** 无源码改动。产物 = 报告 `.superpowers/sdd/fc-task0-report.md`(工作区,不入提交)+ 重建的冻结基线 `/tmp/fc0/base/{corec,corearch,corelsp}` + 清点原始输出(`/tmp/fc0/`)。 + +**Interfaces(产物四表,数字一律实测;未实测标「待实测」):** +- **表 A:冻结基线重建**——`tools/baseline/rebuild.sh`(或等价手跑配方:`jj workspace add --revision 97f4394f` → `nice -n 19 python3 build_selfhost_native.py`)→ 三 sha **新锁白名单**(写入本任务产出的 `REBUILD.md` 换代记录);构建 ×2 取确定性;冒烟 42。 +- **表 B:本批起点判据基线**——起点基线值表 1–11 项**逐条复跑**(五 CI job / 全枚举 / `check src/compiler` / `selftest-types` / 构建确定性 ×2 / 自举链 / canary / `.ccr` 四条 / `--dump-objects` / `.cir` DOT)。 +- **表 C:语料清点(**本批的新增面**)**——① 72 档逐档清单 + rc;② **build 面语料全集**:`tests/suite` 实 build 档(`run.sh:36-54` 语义实跑逐档计数)、`src/compiler/main.cr --static -O 0`、canary 档、`.ccr` 两档、`tests/selfhost/*.py` 内 `build` 调用逐处清点(含内联源去重)、`examples/*.cr`;③ 探针 29 档。 +- **表 D:锚点实核**——本计划「Tech Stack」「表 0/1/2」的每个 `file:line` 逐条实读复核(**行号漂移以本轮实读为准**,漂移逐条落纸)。 +- **表 E:裁定原文核录**——向维护者核一句「fail-closed 方向已裁」的原文/出处(来源 = 协调者转述);**无原文 ⇒ 本批章程不成立 ⇒ 停**。 + +- [ ] **Step 1: 基线重建**。判据:三 sha 与**新白名单**逐条同 + 冒烟 42 + 构建确定性 ×2(日志逐字节同)。**不符 ⇒ 停下上报**。 +- [ ] **Step 2: 起点判据**(表 B;cwd = 仓库根、逐段 `ps` 实查无并发、比较前 `clean-cache`)。 +- [ ] **Step 3: 语料清点**(表 C;`ls`/`find`/`grep -c` 实核,逐类计数)。 +- [ ] **Step 4: 锚点实核**(表 D)。 +- [ ] **Step 5: 裁定原文核录**(表 E)。 +- [ ] **Step 6: 报告**(不入提交;`jj status` 收工为空)。 + +**判据**:五表齐 + 基线三 sha 逐条同 + 源码零改动(`jj status` 干净)+ 报告内数字全部可追溯(命令 + 原始输出片段)。 + +**停条件**:① 重建 sha 不可复现 ⇒ 停下(判据腿失效);② 表 E 无原文 ⇒ 停下(本批方向性背书缺失);③ 表 B 任一项与上表记录值不符且无法归因 ⇒ 停下(起点漂移 ⇒ 后续所有对拍无意义)。 + +--- + +## Task 1:全语料 report-only 基线(**核心产物**;零源码改动) + +**Files:** New `tools/baseline/build_gate_run.sh`(**build 面**采集 runner:逐档 `clean-cache` → `build` → 记 rc/日志/产物 sha;**不新增任何二进制入库**)· New `.superpowers/sdd/fc-task1-report.md`(工作区)· 证据 `/tmp/fc1/**`。**零 `.cr` 改动、零闸门改动。** + +**Interfaces:** +``` +tools/baseline/build_gate_run.sh + # 语料 = 显式清单(照 probes_run.sh 先例:`shopt -s nullglob` + 计数硬断言,杜绝字面 glob 伪条目) + # t1 suite 实 build 档(run.sh 语义:排除 *_mini*.cr 与空文件) + # t2 src/compiler/main.cr --static -O 0(full-bootstrap 口径) + # t3 canary 档(tests/suite/ptr_arith.cr --static)+ .ccr 判据两档(ptr_arith/generics_test,ccr 与 build 两口径) + # t4 tests/selfhost/*.py 的内联源(**由各套件自跑**:本 runner 不重造,改为采集各套件日志—— + # 实现 = 58 套件枚举 + `grep -oE 'error\\[[A-Z0-9]+\\]'` 聚合) + # t5 examples/*.cr(check 面 + build 面各一遍) + # 产物: /hits.tsv(列 = tag | path | face(build/check) | rc | 码集合(去重) | 首条诊断 file:line | 产物 sha256|缺失) +``` +- [ ] **Step 1: 采集 runner 入仓**(显式清单 + 计数断言 + cwd = 仓库根)。判据:跑一遍 rc=0 且 `hits.tsv` 行数 = 清单计数;与手工抽查逐档一致。 +- [ ] **Step 2: 采全量命中清单**(现有二进制;build 面 + 58 套件枚举)。判据:每档给(rc, 码集合, 首条 `file:line`);**与 P6 T0 §3.1 的 check 面 rc 分布交叉核对**(build 面 rc 应 ⊇ check 面 rc=1 档的**子集语义**——逐档差异须能归因到「该码是否在 11 码名单内」)。 +- [ ] **Step 3: 逐档归因表**(表 2 的「转红面」列落纸):每档 = {码 → 归因(真错 / 假阳性 / 语境缺失 / 政策软面)→ 处置建议}。判据:**无「未知/待查」行**(不能归因的档 = 停条件下上报)。 +- [ ] **Step 4: 转红面汇总**:给「全阻断后 CI 五 job 各红几档 + canary 是否可产 + `.ccr` 四条是否可产」的**逐 job 表**。 +- [ ] **Step 5: 报告**(不入提交;`jj status` 收工为空)。 + +**判据**:`hits.tsv` 与归因表齐备 + 每一行的归因有证据(命令 + 原始输出片段)+ 零源码改动。 + +**停条件**:① **命中面过大 ⇒ 转独立批**(阈值:**build 面转红档 > 10 档 或 > CI `suite` job 实 build 档的 1/3**,或**任一 CI job 出现「无法用登记豁免保住」的档**)⇒ 停,出「转独立批」建议书(含命中面全表)交维护者;② 归因表出现「无法判定真/假」的档 ⇒ 停下上报(不得猜);③ 采集 runner 与手工抽查不符 ⇒ 停(仪器失真)。 + +--- + +## Task 2:分诊表定稿 + 豁免表草案 + 裁决门提交(**零代码**) + +**Files:** Modify 本计划(把 T1 实测值回填表 1/表 2 + 落「豁免表草案」节)· 报告 `.superpowers/sdd/fc-task2-report.md`。 + +**Interfaces(豁免表草案的**逐条字段**,本任务定稿后交维护者):** +``` +{ code, // 码常量(如 EC_B_USE_WHILE_BORROWED) + scope, // build | check | both(裁-FC-1) + locus, // 证据位点清单(如 tests/suite/ptr_arith.cr:12:8),**证据字段** + reason, // 为何不是真错误(假阳性机制 / 政策裁定 / 语境缺失) + exit, // 退出条件(撤除判据:根因修复 / 语料转负例 / 政策改判) + hit_count, // T1 实测命中档数 + owner_task } // 归属(本批 T4 / 独立批) +``` +- [ ] **Step 1: 表 1 回填**(32 码的「72 档命中 / 套件命中」两列由 T1 实测值替换「须 T1 实测」)。 +- [ ] **Step 2: 豁免表草案**(≤10 条;每条六字段齐;**无 `reason` 或 `exit` 的条目不许入表**)。 +- [ ] **Step 3: 裁决门提交**(裁-FC-1..6 逐条照抄问句;附 T1 的转红面逐 job 表作事实输入)。判据:六问**全部**有明文答复(**口头/转述不计**——照 P6 `p6-task4-report.md` §6 的「协调者已裁」需落纸的先例,**须维护者本人答复**)。 +- [ ] **Step 4: 报告**。 + +**判据**:表 1 无占位符 + 豁免表每条六字段齐 + 六问答复落纸(含「不裁则按未取裁分支执行」的显式声明)。 + +**停条件**:① 任一裁-FC 的答复与 T1 的事实冲突(如裁「不豁免 B04」但 T1 证 canary 依赖它)⇒ 停下重报;② 豁免表条目 **>10 条** ⇒ 触 T1 停条件①(转独立批)。 + +--- + +## Task 3:闸门反转 + 豁免表 + report-only 通道(**默认关**) + +**Files:** Modify `src/compiler/main.cr`(`:146-179` 的硬名单块反转为「默认 `hard = 1` + 豁免表查询」;新增隐藏旗标 `--diag-gate-report`(默认关,照 `--verify-named-dedup` 同址同式,注册面 = `main.cr:249`,消费面 = `main.cr:196`))· Modify `src/compiler/globals.cr`(豁免表数据 + 只读访问器;**若**表需要跨文件可见——变量/常量跨文件按声明序可见)· New `tests/selfhost/test_fail_closed.py`(≥12 例,下限)· Modify `src/ci/run.sh`(selfhost-tests 挂点 + 计数注)· Modify `docs/error-codes.md`(判定面表)· 报告 `.superpowers/sdd/fc-task3-report.md`。 + +**Interfaces:** +``` +// 豁免表(形态 = 源码内静态表;载体与粒度按裁-FC-2) +// 行 = {code:i32, scope:i32, reason_ni:i32}(± locus 字段) +// 查询 = diag_gate_exempt(code, scope) -> int(1 = 豁免;0 = 阻断) +// 闸门(main.cr 原硬名单块处): +// hard : ., mut = 1; // **反转**:默认阻断 +// loop { ec := …; if diag_gate_exempt(ec, GATE_BUILD) != 0 { hard = 0 的**逐条**语义 } } +// —— 注意语义:任一**未豁免**诊断 ⇒ hard = 1(不是「全部豁免才 1」) +// ⇒ 实现 = hard = 0 起始;出现未豁免诊断即 hard = 1;break/或全扫后判定(保诊断打印顺序) +// 报告通道(默认关):--diag-gate-report ⇒ 打印一行机器可读标记: +// [diag-gate] face=build codes=B04,TF01 would-block=1 n_diag=3 +``` +- [ ] **Step 1: 表 + 查询函数 + 单测(红)**:`test_fail_closed.py` 机制面(≥6 例:默认阻断 / 豁免命中不阻断 / 多诊断混合(一豁免一未豁免 ⇒ 阻断)/ 空名单 / 双面 scope / 越界码)。 +- [ ] **Step 2: 闸门反转**(落点 = `main.cr:146-179`;**嵌套 `if`,不得写 `&&` 串表读**;只改 rc 判定,**不改诊断产生与打印**)。判据:单测绿 + 全语料 report-only(Step 3)零意外。 +- [ ] **Step 3: report-only 全语料**(`--diag-gate-report` + T1 的 runner 与 58 套件枚举)⇒ 产 `would_block.tsv`。判据:与 T1 `hits.tsv` **逐档逐码一致**(差异逐条归因;**任何未在豁免表内的命中 ⇒ 停下**)。 +- [ ] **Step 4: 行为面用例(≥6 例)**:真错误 build ⇒ rc=1 + **无产物**(`` 与 `.ccr` 均不存在);豁免码 build ⇒ rc=0 + 产物在 + ELF 可运行;`check`/`cir`/`ccr`/`run` 四面各自同闸验证;级联(多诊断混合)。 +- [ ] **Step 5: 判据(硬闸)**:**ELF canary IDENTICAL** · `.ccr` 四条 = P6 T3 值 · `--dump-objects` 3521 行 · `.cir` DOT 165915B · 五 CI job rc=0 · 全枚举 58/58 · `check src/compiler` rc=0 · `selftest-types` ≥415 · 自举链 · 72 档对拍零差异 · 探针 29 档零差异 · `test_backend_bootstrap` rc=0 + `error[`=0 · **突变控制 ≥3**(M1 豁免表清空 ⇒ 命中档全部转红;M2 默认值反转回 0(不阻断)⇒ 未豁免码静默放行(转红 = 本批要消灭的形态复现);M3 单条豁免删除 ⇒ 对应档转红)+ 精确回滚。 +- [ ] **Step 6: 提交**:`feat: fail-closed 闸门——默认全阻断 + 豁免登记表 + report-only 通道(默认关;发射面零变化)`。 + +**停条件**:① report-only 出现**豁免表外的命中** ⇒ 停下(清单不完整);② 任一 canary/`.ccr`/`--dump-objects` 变化 ⇒ 停下(发射面泄漏);③ 默认关路径下**任何**既有判据发生变化(rc/日志/产物)⇒ 停下(本任务应零行为变化);④ 裁-FC-1 未取裁 ⇒ **只落 report-only 通道,不落闸**(Step 2 跳过)。 + +--- + +## Task 4:命中面处置(逐条:修根因 / 修语料 / 登记豁免) + +**Files:** 按 T1 归因表逐条定;预期面 = `src/compiler/checker.cr`(若修前端假阳性:如 TF01 并发族、TF07 `@raw_int` 位、B004 末次使用)+ `tests/suite/*.cr`(若语料转负例,**须裁-FC-4 批准**)+ `src/ci/run.sh`(SKIP 语义若改)+ `tests/selfhost/test_*.py`(重钉)+ 本计划(豁免表终稿)。 + +- [ ] **Step 1: 逐条归因复核**(每条:RED 复现 → 冻结基线复证 → 机制定位 → 处置判定)。判据:每条有 `file:line` + 触发语料 + 机制。 +- [ ] **Step 2: 假阳性根因修复**(照 P6 TF01 清理批先例:`stmt_cannot_fall_through` + `lits_copy` 类型洗白,TODO #40)。判据:修复后该码在全语料零命中 + 原用例转绿 + **反例仍红**(强负控)。 +- [ ] **Step 3: 语料处置**(**仅限裁-FC-4 批准面**)。判据:语料改动逐条有维护者批复 + 语料只增不减纪律的例外声明。 +- [ ] **Step 4: 豁免表终稿**(撤除已修条;每条保留 `exit` 条件)。判据:表内条目全部有 T1 实测命中支撑(**不得有空条**)。 +- [ ] **Step 5: 判据**:同 T3 Step 5 全套 + **命中档逐档转绿(或登记明确)的对照表**。 +- [ ] **Step 6: 提交**(按处置分批;路径限定)。 + +**停条件**:① 修假阳性过程中发现**语义变更面**(如 B004 的借用收缩)⇒ 停下(超本批章程,转独立批);② 语料处置超出裁-FC-4 批准面 ⇒ 停下;③ 命中档在修复后**仍红且未登记** ⇒ 停下。 + +--- + +## Task 5:开门(默认全阻断)+ 零产物语义 + CI 面同步 + +**Files:** Modify `src/compiler/main.cr`(闸门默认值/表终稿;**零产物**:`corearch` 失败路径删本次 `.ccr`——落点 `:702` 附近,`exit_code != 0` 分支)· Modify `src/ci/run.sh`(若 CI 需承认新语义:如 suite job 对已知豁免档的注释块 + 自述)· Modify `docs/error-codes.md`(判定面/闸门语义成文化)· Modify `TODO.md` · Modify 本计划(实施记录)。 + +- [ ] **Step 1: 开门**(裁-FC-5 取最强形式时;默认全阻断生效)。判据:`hit` 档的 build rc 由 0 → 1(逐档对照 T1 表),而**豁免档 rc=0 且产物逐字节同**(canary/`.ccr` 四条为证)。 +- [ ] **Step 2: 零产物语义**(裁-FC-6)。判据:新套件断言——失败路径 `` 与 `.ccr` **均不存在**;**已存在的旧产物不被删除**(前置造一个旧文件 + 触发失败 ⇒ 旧文件仍在);`.core/cache/cir/` 口径落纸。 +- [ ] **Step 3: CI 面同步**(`run.sh` 注释/自述;若需新 job 或新挂点,走三面同核)。判据:五 CI job rc=0 + 全枚举 58/58。 +- [ ] **Step 4: 判据(硬闸)**:canary IDENTICAL · `.ccr` 四条 = T3 值 · 全枚举 · selftest-types ≥415 · 自举链 · 72 档对拍**换代后的期望值**(若裁-FC-1 取 (B)/(C),此处须给新期望分布 + 逐档归因)· 探针 29 档 · 突变 ≥3 + 精确回滚。 +- [ ] **Step 5: 提交**:`feat: fail-closed 开门——默认全阻断生效 + 零产物语义(豁免表 N 条;发射面零变化)`。 + +**停条件**:① CI 任一 job 转红且非豁免表覆盖 ⇒ 停下;② canary 变化 ⇒ 停下;③ 零产物断言在**失败路径先于** `save_ccr`(前端闸)与**后于**(corearch 失败)两种形态任一处不成立 ⇒ 停下。 + +--- + +## Task 6:收官(回归 + 判据 + 台账 + 文档 + 批终态) + +- [ ] **Step 1: 全量回归**(cwd = 仓库根、`nice -n 19`、串行、比较前 `clean-cache`):五 CI job;全枚举 58/58;`check src/compiler`;`selftest-types`;构建确定性 ×2。 +- [ ] **Step 2: 判据复验**:重建基线(T0 白名单)× 同源对拍(72 档)· 探针全集 · **build 面采集器(T1 产物)复跑 ⇒ 命中面 = 豁免表**(**逐条等式判据**,本批的核心不复现性判据)· canary · 自举链 · `.ccr` 四条 · `--dump-objects` · `.cir` DOT。 +- [ ] **Step 3: 统一台账**:逐任务列 {实施 / 收紧 / 豁免 / 修复 / 语料处置 / 登记 / 偏差}——与各任务实际 diff 逐条对应;**豁免表终稿(含每条 exit 条件)单列一节**。 +- [ ] **Step 4: 文档**:`docs/error-codes.md`(判定面语义 + 闸门机制 + 豁免表指针)· `CLAUDE.md`(若新增 `tools/baseline/build_gate_run.sh` 或新套件 ⇒ 相应节补一行;`.cr` 清单无变化则不写)· `TODO.md`(Fail-closed 条目 + 转独立批登记 + 豁免退出条件追踪)· 本计划实施记录。 +- [ ] **Step 5: 批终态陈述(一页)**:「已实施 / 已豁免(含退出条件)/ 剩余登记 / 下一批指向」四栏。 +- [ ] **Step 6: 提交**(路径限定)。 + +**判据**:五 CI job + 全枚举 + canary + 自举链 + 同源对拍 + 探针 + **命中面=豁免表的逐条等式** + 台账逐条可溯源 + 文档指针链闭合。 + +--- + +## 风险面(本批最大风险 = **一次改动让大量既有语料转红**) + +| # | 风险 | 缓解(硬要求) | +|---|---|---| +| R1 | **转红面超预期**(套件 job 7 档 + 49 档 py 内联源的未知命中面) | T1 **先量后改**(零源码改动采清单)+ T2 逐档归因 + 停条件「>10 档 / >1/3 ⇒ 转独立批」+ 分批开门(批次 1 = 全豁免保绿,批次 2..n 只减不增) | +| R2 | **ELF canary 不可产**(B04 命中 canary 载体) | 裁-FC-3 三选一(本批推 (ii) 豁免)+ T5 判据「canary 不可产 ⇒ 立即停下」 | +| R3 | **豁免表变成新的静默面**(码级豁免在别处静默放行) | 表只减不增 + 每条 `exit` 条件 + **每次开新面必重跑 report-only** + 台账单列一节 + 突变 M2/M3 咬合 | +| R4 | **两面语义分歧长期化**(check ⊃ build) | 裁-FC-1 + 表 0 成文化 + `docs/error-codes.md` 记明差异 + CI 自述块 | +| R5 | **零产物语义误删用户文件** | 只删**本次写入**的 `.ccr`(`save_ccr` 成功后 + `corearch` rc≠0);旧产物不动;用例覆盖 | +| R6 | **判据腿失效**(若取「两面同闸」,72 档/探针两条对拍腿的「零差异」判据换代) | 裁-FC-1 推荐 (C);若取 (B) ⇒ 先出「新期望分布 + 逐档归因」再开门 | + +--- + +## 未决项与登记(**不猜**) + +- **U-1(维护者裁定原文)**:本批的章程前提 = 「要改、方向 = fail-closed」,来源 = 协调者转述,**无独立文件载体** ⇒ T0 表 E 核录;**无原文则本批停在 T2**。 +- **U-2(`测试面 `check` rc 分布 vs `build` 面)**:72 档 runner 只跑 `check`(`parity_run.sh:36`),**build 面无既有 runner** ⇒ T1 必须新建(`build_gate_run.sh`);「命中面」的完整定义**在本批首次建立**。 +- **U-3(安全检查面的反向不一致)**:`region_check`/`provenance_verify` 只在 build/ccr 面跑 ⇒ 存在「check rc=0 / build rc=1」档(本实例**未构造具体语料**)⇒ T1 实测登记(不得当已知事实引用)。 +- **U-4(TM04 的打印前缀)**:现行以 `error[TM04]` 打印但语义声明为软面(`main.cr:159`)⇒ 「报错即拒」与「软面」字面冲突 ⇒ 裁-FC-2(d)。 +- **U-5(≈104 个「定义零 raise」码)**:`ast.cr` 定义 ≈146 / 可达 42 ⇒ 其余为**死码面**(本批不删、不改、登记;删除属独立决定,照 `tools/module_to_ccr.py` 的「只标废弃不删」先例)。 +- **U-6(LSP 面)**:`src/lsp/lsp.cr` 消费 `g_diag_count` 但**不产生产物** ⇒ fail-closed 不适用(表 0 登记)。 +- **登记维持(不做,理由写死)**:`EXPR_FOR` 元素类型恒 int(E-6/裁-P6-4)· `.csr`/TagNode(验证切片轮)· 容量批(E-2/E-3/E-4)· `string == int` 混比(裁-P6-3)· `--verify-named-dedup` 保留(裁-P6-2(e))——**本批一律不动**。 + +--- + +## 自检记录 + +- **本计划的自我限制(诚实登记)**:**本计划未跑任何构建/测试/`jj` 命令**;所有「现状值」= 实读源码/文档或既有报告记录值(出处逐条标注);需实测的数字(基线重建 sha、build 面命中清单、套件面命中档数、转红面逐 job 计数)一律留给 T0/T1 的实测步。 +- **判定面清点的诚实性**:「42 可达码」= 全仓 grep 三形态(`check_error(EC_*` / `diag_type_incompatible(…, EC_*` / `DIAG_REC_SIZE, EC_*`)去重实得 43 减 1 个假命中(`EC_SIZE` 为 `DIAG_REC_SIZE` 子串);**未**用「源码注释/文档统计」冒充实测(`docs/error-codes.md` 的 ~146 为文档自述值,已标出处)。分诊的 7 条豁免**全部带命中证据或政策出处**;其余 25 条阻断**全部按「判定继续 ⇒ 静默错产物 / 不可信」判据成文**(裁-P6-2(c) 口径),未用一个「大概/应该」。 +- **根因 vs 止血**:闸门反转直达根因(**默认允许**是缺口本身,不是名单不全);豁免表被明确设计为**临时面**(每条带 `exit`)+ 只减不增 + 每次开新面重跑 report-only(防退化成语义黑洞)。 +- **与既有裁决的一致性复核**:不回退 TM03/TG02(#31/#33)、TS01-04/TK02(#29)、ICE04(P-A)、TA02(#32)、F2/R002(先例即入闸依据);TM04 的「软面」裁定经裁-FC-2(d) 显式处置(**不得**由实施者私自改判)。 +- **占位符扫描**:无 TBD;「须 T1 实测」逐处给出测量命令或产物格式;六条裁决门**逐条点名问句**,未用「推荐」冒充裁定。 +- **本批最大风险(一句话)**:**一次反转会让 CI `suite` job 至少 7 档(TF01×5/TF07×1/B04×1)与 ELF canary 载体同时转红——而 canary 载体 `ptr_arith.cr` 自身就是 check rc=1 档 ⇒ 不先取裁-FC-3,本批无法开门。** + +--- + +## 勘误与进展(2026-09-14,TC02 极小批落地后回填) + +- **本条只动一处**:表 1 第 10 行(TC02)的处置由「① 必须阻断」改判为「**已修**(真误报)」。归因报告 = `/tmp/fct1/tc02_attribution.md`;设计稿 = `/tmp/fct2/tc02_minibatch.md`;落地 = `checker.cr` 新增 `stmt_diverges` + EXPR_IF 合并点守卫(P3:**仅 else 支发散 ⇒ 不报**;不对称是有意的)。 +- **对「表 2 转红面」的影响**:`test_native_float` 从「裸反转 6 档红套件」中**移出**;CI `selfhost-tests` 的裸反转真红面由 **6 → 5 档**(`test_ccr_types` [TF07+TB01] · `test_interp_float` [B04] · `test_interp_parity` [B04] · `test_match_exhaust` [TM04] · `test_xcut_iface` [TK01])。 +- **对「豁免表草案」的影响**:`/tmp/fct1/exemption_draft.tsv` 的 **TC02 条目可撤**(退出条件「逐例归因后」已达成)⇒ 豁免表初稿由 **10 码降为 9 码**(TF01 · TF07 · TB01 · TM04 · TK01 · N01 · N06 · N11 · B04)。**表调整由 FC 批 T2 统一执行**(维护者 2026-09-14 裁)。 +- **未覆盖面(本批不判,登记)**:`break`/`continue` 收尾的分支(需循环上下文);`x := if c { } else { return 1; }`(值被用且 else 发散)形态按 P3 不报(if 型 = `then_ti` = unit 是真值 ⇒ 误用由 TA02/TF01 兜住;**未构造端到端用例**)。 + +--- + +## 勘误与进展(2026-09-15,FC 批 T2 落地后回填) + +- **本条 = 本计划的 T2 段落地记录**(设计稿 = `/tmp/fct3/fc-t2-impl-draft.md`;报告 = `.superpowers/sdd/fc-task2-report.md`)。 +- **落地形态(与设计稿的差异逐条)**: + 1. **`diag_gate_exempt` 落 `diag.cr`**(表 9 条;`scope` 常量落 `globals.cr`)——照设计稿,零清单改动(`main.cr`/`globals.cr`/`diag.cr` 均在 corec 清单内;`corearch_files` 不含 `diag.cr`)。 + 2. **类型面闸反转**(`main.cr` 硬判定循环 → `diag_gate_exempt(ec, GATE_SCOPE_BUILD) == 0 ⇒ hard = 1`;旧 6 语句行/11 码名单整体退役);**语法面 `:134/:135` 与安全检查面 `:594-603` 不动**(照设计稿)。 + 3. **零产物删除点**:`exit_code := system(cmd2)` 之后 `if exit_code != 0 { system("rm -f \"\"") }`(只删**本次**写下者;既有旧 `` 不删——裁-FC-6)。 + 4. **新增(设计稿未含)= 进度行保真修正**:`[5/5] frontend done` 由「仅 `hard==0` 路径」改为「诊断之后、返回之前一律打印」⇒ 72 档语料日志除**纯行号偏移**外零差异(否则 25 档会缺该行)。**唯一可见差异 = 探针 `n05_recursive.cr` 多一行**(其 `TS03` 属旧硬名单 ⇒ S0 提前返回无该行)。 + 5. **新套件** = `tests/selfhost/test_diag_gate.py`(**17 例**:正控 9 + 负控 6 + 零产物 3)+ `run.sh` 挂点(设计稿写 ≥14;实际 17)。 +- **对表结论**:**无表外命中**(T1 `would_block.tsv` 的 7 条 build 面行全部放行、34 条 check 面行逐条不变;T1 的 6 档红套件全绿)。**豁免表 = 9 条**(10 − TC02;TC02 已在 `7f53305d` 修掉)。 +- **72 档差异口径(先例继承)**:日志差异 **2 档 = 纯行号偏移 +53**(编辑编译器自身源码 ⇒ 语料含编译器 ⇒ 行号位移;先例 = P6 T2 的 +146);rc 分布两侧 34×0/38×1 零差异。 +- **新登记(本批施工中发现,另批)**:**安全面诊断随 `.cir` 暖缓存静默消失**(冷 rc=1 / 暖 rc=0 出 ELF;P6 期二进制同病 ⇒ 预存)⇒ TODO #60(含派生建议:两条 runner 加「暖态腿」,**本批不实施**)。 + +## 勘误与进展(2026-09-15,FC 批 T3 落地后回填) + +- **T3 = 隐藏通道 `--diag-gate-report`**(默认关;同 `--verify-*` 家族纪律):注册于 `main.cr` 的 `cli_flag_bool` 段;输出 = 闸门内只读报告行 `[diag-gate] face=build blocked=N total=M codes=…`(用户面码名)。 +- **判据**:**默认关两态零差异**(72 档 `diff -rq` 零 + 探针 29 零)· 开态 rc/产物 sha 逐字节同(仅多一行标记)· 重放 101 档 **无表外命中**(语料 25 档 blocked 与 T1 的 34 行 − 7 build 豁免 − 2 P21 对齐;探针 11 档 = T1 未覆盖面,口径差登记)· 五 CI 5/5 · 枚举 60/60 · canary IDENTICAL · `.ccr` 四条同 · 链 `24802386a1…` IDENTICAL · `backend_bootstrap` rc=0。 +- **口径注**:报告面只覆盖**前端类型面闸**(`g_diag_count > 0` 块);**解析阶段闸**(`:134/:135`)与**安全检查面闸**(`:594-603`)的诊断不出现在报告行中(前者如 P21、后者如 TU03)——重放对表时须按此口径解读。 +- **环境干扰事件(非本批代理所为,登记)**:T3 测量期间 `build/`(整目录)两次被外部删除、`examples/*.ccr` 等 7 个 tracked 文件被删(`jj status` 见 `D`)、`/tmp/fct3/parity_*/parity.out` 被删 ⇒ 首轮 parity/probe 腿作废(rc=127 满屏);重建后(corec sha 复原 `308f06a3…`)**全部重跑并取有效值**。**未提交、未还原**那 7 条 `D`(铁律 #3)。 + +--- + +## 统一台账(FC 批 T1–T6;**T6 收官**) + +> 口径(继承 P6/P5):逐件给**证据**与**替代/覆盖**;全语料命中数一律与**同任务起点**基线逐条比。 +> **KPI**:实施 **3 件**(T2/T3 + T1 扫面)· 收紧 **1 类**(闸门反转 = 默认阻断)· 放宽 **0** · +> 修复 **1 件**(TC02,前批 `7f53305d`,本批引用)· 豁免 **9 条**(带退出条件)· 登记 **4 件** · +> 偏差 **6 条** · 环境干扰 **1 起** · **T4/T5 折入 T2(已批准)**。 + +### 1. 实施(三件;逐件 = 交付 / 判据 / 出处) + +| # | 件 | 提交 | 交付 | 判据(实测) | +|---|---|---|---|---| +| 1 | **T1 report-only 全语料扫面**(零源码改动) | 无提交(工作区报告 + `/tmp/fct1/**`) | `hits.tsv` 203 · `would_block.tsv` 41 · `exemption_draft.tsv` 10 · 影子树 + 闸门包装器法(`GATE=bare/exempt7/9/10/off`)· 伪影对照(`test_cache_identity`/`test_optional`) | 裸反转 ⇒ 2 CI job 红(`suite` 7/21 档 · `selfhost-tests` 6 套件);`exempt10` ⇒ 套件面全清 | +| 2 | **T2 闸门反转 + 豁免表 + 零产物** | **`b01a948c`** | `main.cr` 反转(旧 11 码名单退役)+ 进度行保真 · `diag.cr::diag_gate_exempt`(**9 条**)· `globals.cr` scope 常量 · 零产物删除点 · 新套件 `test_diag_gate.py`(**17 例**)+ `run.sh` 挂点 | 五 CI 5/5 · 枚举 60/60 · selftest 415/415 · canary IDENTICAL · `.ccr` 四条同 · 链 `5154425478…` · 72 档 rc 零差异 · 对表无表外命中 · 突变 4/4 | +| 3 | **T3 隐藏通道 `--diag-gate-report`** | **`3990a0a5`** | `main.cr` 只读报告行(默认关;同 `--verify-*` 纪律)· TODO #61 · 计划勘误 | **默认关两态零差异**(72 档 + 探针 `diff -rq` 零)· 开态 rc/产物 sha 逐字节同(仅多一行)· 101 档重放对表无表外命中 · 五 CI 5/5 · 枚举 60/60 · 链 `24802386a1…` | + +### 2. **T4/T5 折入 T2 声明**(维护者 2026-09-15 批准) + +- **T4(命中面处置)**= **由 T2 覆盖**:命中面以**豁免表 + 退出条件**形式落定(9 条逐条带「位点证据/理由/退出条件」),**无根因修复需求**(维护者裁定方向 = 豁免主导;唯二根因件 TC02 已在前批 `7f53305d` 修复)。 +- **T5(开门 + 零产物 + CI 面同步)**= **由 T2 覆盖**:**开门已在 T2 生效**(默认阻断 = 唯一语义,无「report-only 默认关」形态需切换);零产物删除点 + 边界(只删本次写下者)在 T2 落定;CI 面(`run.sh` 挂点)已在 T2 同步。 +- **覆盖理由**:T2 的实施顺序把「先 report-only 清单审查 → 再开门」与「豁免表定稿」合并为一次落地(T1 已提供清单审查面),故 T4/T5 无独立交付物。**不静默略过**:本条即其显式记录。 + +### 3. 收紧 / 放宽(各 1 / 0) + +| # | 形态(前 → 后) | 件 | 全语料命中 | 牙齿 | +|---|---|---|---|---| +| 1 | 类型面闸:**正向名单(11 码)→ 默认阻断 + 豁免表(9 条)** | T2 | 起点即含全部命中(T1 已清点);T2 后**无表外命中** | 突变 M1(删豁免条 ⇒ 复红)· M2(scope 失效 ⇒ 静默放行被抓)· M3(去零产物 ⇒ 半成品)· M4(全放行 ⇒ 负控转绿) | +| — | **放宽 0 件**(本批无放宽动作;TC02 的放宽发生在前批) | — | — | — | + +### 4. 豁免表 9 条(含触发位点与退出条件;载体 = `diag.cr`,只减不增) + +| # | 码 | scope | 触发位点(实测) | 退出条件 | +|---|---|---|---|---| +| 1 | TF01 | build | 5 档并发族 suite build | 修前端根因后撤 | +| 2 | TF07 | build | `ptr_ref_first.cr:8`(`@raw_int`) | #20/F3 收口后撤 | +| 3 | TB01 | build | `ptr_ref_first` + 8 档库单元(**级联码**) | 随根因码消失 | +| 4 | TM04 | build | `test_match_exhaust.py:217`(用例钉「软面 rc=0」) | 政策改判 | +| 5 | TK01 | build | `test_xcut_iface.py:234-241`(用例钉「软诊断面」) | 政策改判 | +| 6 | B04 | build | `tests/suite/ptr_arith.cr:12:8`(canary 载体) | 裁-FC-3 (c)/(i) 后撤 | +| 7-9 | N01 / N06 / N11 | check(**仅登记**,(C) 终局 ⇒ 惰性) | 25 档库单元单独 check | 口径固定;不得据此实现 (B) 活分支 | + +### 5. 登记(4 件) + +| # | 件 | 归属 | 状态 | +|---|---|---|---| +| 1 | **TODO #59** FC 批 T2 落地(含判据全套与对表结论) | 本批 | 已落 TODO | +| 2 | **TODO #60** 【高危·预存】安全面诊断随 `.cir` 暖缓存静默消失(冷 rc=1 / 暖 rc=0 出 ELF;P6 期二进制同病)+ **派生建议:runner 加暖态腿(本批不实施)** | 独立批候选 | 已落 TODO | +| 3 | **TODO #61** FC 批 T3 隐藏通道(形态/判据/对表/口径注/登记) | 本批 | 已落 TODO | +| 4 | **环境干扰事件**:`build/` 被外部删两次 + `examples/*.{ccr,cir,o}` 等 **12 个 tracked 文件**被删(工作副本 `D`) | **待维护者处置** | 未提交、未还原(铁律 #3) | + +### 6. 偏差(6 条;逐条 = 现象 / 处置) + +| # | 现象 | 处置 / 出处 | +|---|---|---| +| 1 | **72 档日志差异 2 档 = 纯行号偏移 +53**(`_import.cr` / `ccr_io.cr`:29935→29988 / 23506→23559) | 语料含编译器自身源码、本批编辑了它 ⇒ 位移;**rc 与诊断逐条同**;先例 = P6 T2「+146」 | +| 2 | **探针 1 档差异**(`n05_recursive` 多一行 `[5/5] frontend done`) | 其 `TS03` 属旧硬名单 ⇒ S0 提前返回无该行;本批改为「诊断后、返回前一律打印」后补上;rc/诊断同 | +| 3 | **进度行保真修正(设计稿外)**:`[5/5]` 首版移至闸门**前** ⇒ 25 档顺序差;二次修正为**同址打印** | 差异降至「2+1」(见偏差 1/2);首版问题已在本报告留痕 | +| 4 | **corearch sha 口径澄清**:lead 给的 `f69240ae…` = **自举链** sha(非 corearch) | 已核;本批 corearch = `22e96d03…` | +| 5 | **B6 项(安全面负控)如实保留**:冷态 PASS / **暖态受预存缺陷影响**(诊断消失 ⇒ rc=0 出 ELF) | **未加豁免、未改判据**;归 TODO #60 | +| 6 | **T3 测量期环境干扰**:`build/` 与 `examples/*` 被外部删 ⇒ 首轮 parity/probe 腿作废(rc=127 满屏);`/tmp/fct3/parity_*/parity.out` 被删 | 重建(corec sha 两次复原 `308f06a3…` + 冒烟 42 ⇒ 干扰不改构建产物)后**全部重跑取有效值**;证据目录未删(本批自用仅 11MB,`/tmp` 95% 占用非本批所致) | + +### 7. 口径注(T3 引入;对表/复放须按此解读) + +**`--diag-gate-report` 的报告面只覆盖前端类型面闸**(`g_diag_count > 0` 块)——**解析阶段闸**(`main.cr:134/:135`,如 P21)与**安全检查面闸**(`:594-603`,如 TU03)的诊断**不出现在报告行**(实测:TU03 档 on/off 两态 0 行差异)。⇒ T3 重放对表时,「T1 有而重放无」的 2 行(`at_test_mini4/6`)即由此口径解释。 + +--- + +## 批终态陈述(一页;FC 批 T1–T6) + +**一、已实施面(全部实测判据)** + +| 面 | 终态 | 出处 | +|---|---|---| +| **闸门语义** | **默认阻断 + 豁免登记表(9 条)**——任何不在表内的类型面诊断 ⇒ `rc=1` + **零产物**;语法面(P/L 族)与安全检查面(B11/TU03…)本就「任一即 rc=1」(**未改动**) | T2 `b01a948c` | +| **豁免表** | `diag.cr::diag_gate_exempt`(码级 9 条:build ×6 + check ×3[仅登记];逐条带位点证据/理由/退出条件;**只减不增**纪律入头注)· scope 常量落 `globals.cr` | T2 | +| **零产物** | corearch 失败 ⇒ 删**本次**写下的 `.ccr`;既有旧文件不删(裁-FC-6);ELF 侧本就无半成品(内存缓冲后一次写) | T2 | +| **隐藏通道** | `--diag-gate-report`(默认关、只读、两态零差异;报告行 = `[diag-gate] face=build blocked=N total=M codes=…`) | T3 `3990a0a5` | +| **回归判据终值** | 五 CI **5/5 rc=0** · 枚举 **60/60** · selftest **415/415** · canary **`95084e7b…d475`(28822B)IDENTICAL** · `.ccr` 四条 96015/96158/142793/142936(差恒 143B)· dump 3521 · cir 165915B · 链 `24802386a1…` IDENTICAL + N06=0 + 冒烟 42 · `backend_bootstrap` rc=0 · 72 档 rc 零差异 · 探针零差异 | T6 | +| **测试** | 新套件 `test_diag_gate.py` **17 例**(正控 9 / 负控 6[三面] / 零产物 3)+ `run.sh` 挂点;`test_tc02_branch.py` 15 例(前批) | T2 | + +**二、已裁定面**:裁-FC-1 = **(C) 终局**(同机制 + scope 字段;check 基线不换代;(B) 非活选项)· 裁-FC-2 = 码级表 + 位点作证据 + 源码内载体 · 裁-FC-3 = **销项**(保持 `ptr_arith` 载体 + B04 带退出条件豁免——数据自解:B04 不豁免则 `test_ccr_types` 必红)· 裁-FC-4 = 逐档归因(豁免表清账)· 裁-FC-5 = (b)(c) 最强形式 · 裁-FC-6 = 零产物只删本次写下者(**报告写明边界**)。 + +**三、剩余登记面**:**TODO #60**(缓存静默缺陷 + runner 暖态腿建议——**独立批候选**,维护者已采信)· **环境干扰事件**(12 个 tracked `D`,**待维护者处置**,不得擅自还原)· 豁免表 9 条的**退出条件**(逐条在表内;撤条须带根因证据、加条须维护者批)· T3 报告面口径注(P21/TU03 不在报告行)。 diff --git a/docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md b/docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md new file mode 100644 index 00000000..04463677 --- /dev/null +++ b/docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md @@ -0,0 +1,280 @@ +# TODO #60 批实施计划:暖缓存诊断静默(warm-cache diagnostics)+ 判据网暖态腿 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 消灭**暖 `.cir` 缓存命中后安全检查面诊断静默消失**这一类缺陷(不安全程序**第二次编译起静默通过**),并把「判据网对该面失明」这一**元缺陷**一并收口(给两条 runner 加**暖态腿**)。范围 = 两条腿:**主腿 = TODO #60 缺陷**;**派生腿 = 判据网暖态腿**。 + +**Architecture:** 五段:① **前置侦查 + 冻结基线 + 侧表面全集清点**(T0,源码零改动);② **根因实证**(T1:冷/暖两态 + 装载期不变量探针,把「疑似」变「实锤」);③ **修法落地**(T2,按裁决门取向;候选 a/b/c/d 见 §2);④ **判据网暖态腿**(T3:`parity_run.sh`/`probes_run.sh` 加「同源二次构建」腿);⑤ **判据复验 + 文档 + 台账**(T4)。 + +**Tech Stack / 锚点(本实例实读,行号 = 当前树 `1592a1b5`):** +- **缓存面**:`src/compiler/cir_cache.cr`(`CIR_CACHE_VER = 17` `:51` · `save_cir_cache` `:187` · `load_cir_cache` `:436` · `cir_read_snapshot` `:401` · `cir_compiler_identity` `:95` · `make_cir_cache_dir` `:629`);快照段 = 头(magic/ver/identity/fp/sig/name,v17)++ **vars/24B**(`name/id/type` 三字段全载)++ **nodes/64B**(op/dest/s1/s2/s3/**派生码**/first_edge/edge_count;两槽装载期重派生)++ **edges/32B** ++ **instrs/48B** ++ 字符串段 ++ **SG 记录/48B**(v13)。 +- **var 记录字段全集** = `name/id/type` 三(`dyn_arr.cr:429-434` `irv_*` 访问器)⇒ **var 表本身被完整搬运**。 +- **类型行空间**:`alloc_type(kind, data, extra)` = **只追加、不去重**(`checker.cr:10-19`);**`ir_gen` 期 5 处分配**(`:1403` `alloc_type(TYP_PTR, elem_ti, 0)` / `:1409` `alloc_type(TYP_PTR, irv_type(op_var), 0)` / `:2584` `alloc_type(TYP_ARRAY, …)` / `:2641` `alloc_type(TYP_PTR, get_type_data(target_ti), asp)`(`asp = get_type_extra(source_ti)` = `as *T` 的地址空间/外部标记)/ `:2951` `alloc_type(TYP_ARRAY, TI_INT, cnt)`)——**这些行不随快照**(快照只存 var 的 `type` 行号,不存行内容)。 +- **消费点(本缺陷的观测点)**:`provenance_verify.cr:63-70`:`ptr_ti := irv_type(s1); if ptr_ti >= 0 && get_type_kind(ptr_ti) == TYP_PTR && get_type_extra(ptr_ti) != 0 { check_error(EC_TU_DEREF, "external pointer dereference requires unsafe", 0, 0) }`;`get_type_kind/data/extra` 定义 = `checker.cr:322-334`。 +- **build 路径时序**:`main.cr:496-561`(逐函数缓存循环)→ `:565` `df_state_finalize` → `:597-603` **安全检查面**(`ptr_analysis_all` `:598` → `region_check_all` → `provenance_verify_all` → `g_diag_count > base_diags` `:603` ⇒ rc=1)→ `:612` CSE → `:615` `lower_to_ccr` → `ccr`/`build` 分支。 +- **判据网**:`tools/baseline/parity_run.sh:32-40`(逐档 `clean-cache` + `check`)· `tools/baseline/probes_run.sh`(同款)· `src/ci/run.sh:68-113`(selfhost-tests 挂点)。 +- **既有同族先例(必须继承)**:`cir_cache.cr:1-50` 的 v12–v18 版本注——每一版都是「**快照携带了会随进程态漂移的量 ⇒ 静默类** ⇒ bump 或改设计」;**本缺陷 = 同族第 N 例**(v18 注已把「项表」按「不落盘、装载期重派生」收口;**类型行空间尚无同等处理**)。 + +**Spec / 依据:** TODO **#60**(本批主腿;含「派生建议:runner 加暖态腿」)· FC 批报告 `.superpowers/sdd/fc-task2-report.md` §7(冷/暖对照表:P6 期 `ae01de75…` 与本批 `d107b34b…` **同病 ⇒ 预存**)· `cir_cache.cr` 版本族注(v14/v16/v17/v18 的「静默类」判据)· FC 批 Global Constraints 的 **fail-closed 精神**(「宁可 miss 不可静默」)。 + +--- + +## 前置状态(**本计划未跑任何构建/测试**;下列全部为实读或既有报告记录值,出处逐条标注) + +- **现象(FC T2 施工中实测,出处 `.superpowers/sdd/fc-task2-report.md` §7)**:同一源、同一二进制,`clean-cache` 与否为唯一变量——**冷态 `error[TU03]` 报出(rc=1、无产物);暖态诊断消失 ⇒ rc=0 且照常产出 ELF(28822B)**;`build --static` 与 `ccr` **两面同病**;**P6 期二进制 `ae01de75…` 同值复现 ⇒ 预存**。 +- **复现源(最小)**:`fn main() -> int { p := 4096 as *int; return *p; }\n`(= `tests/selfhost/test_pointer_safety.py:131-138` 的形态;`as *int` 带地址空间 ⇒ `alloc_type(TYP_PTR, …, asp)`)。 +- **性质**:**不安全程序第二次编译起静默通过**(「判定继续 ⇒ 静默错产物」的教科书形态)。 +- **判据盲区(元缺陷)**:两条 runner 逐档 `clean-cache`(`parity_run.sh:35` / `probes_run.sh`)⇒ **整个判据网对该面失明**;CI 五 job 亦不含暖态腿。 +- **既有缓解(**部分面**)**:可选表示程序**关缓存**(`main.cr:488-491`:`g_optrep_on != 0 ⇒ cache_enabled = 0`,实读 `:491`)——**先例证明「关缓存」是本仓可接受的兜底手段**(本条是候选修法的锚)。 + +--- + +## 1. 根因侦查落纸(T0/T1 的内容;**已实读** vs **待实证** 分明) + +### 1.1 已实读的事实链(本实例逐条实读源码) + +| # | 事实 | 锚 | +|---|---|---| +| F1 | 快照**完整搬运 var 表**(`name/id/type` 三字段)——`irv_set_type(v_id, v_type)` | `cir_cache.cr:436-500` 的 vars 段 | +| F2 | 快照**不搬运类型行内容**(`g_types` 行 = `{kind, data, extra}`);装载只恢复 **行号** | `cir_cache.cr` 段清单(`:215-232`:vars/nodes/edges/instrs/字符串/SG,**无 types 段**) | +| F3 | `alloc_type` **只追加不去重** ⇒ 行号空间 = **分配序的纯函数** | `checker.cr:10-19` | +| F4 | **`ir_gen` 期有 5 处 `alloc_type`**(非前端)⇒ 缓存命中(跳过 `ir_gen_func`)时**这些行不会被分配** | `ir_gen.cr:1403/1409/2584/2641/2951` | +| F5 | 本缺陷的消费点读的就是**行号 → 行内容**(`get_type_kind/get_type_extra`) | `provenance_verify.cr:63-70` + `checker.cr:322-334` | +| F6 | 安全检查面**在缓存命中路径上照常运行**(`ptr_analysis_all` 等对全体节点跑)——所以问题不在「pass 没跑」 | `main.cr:597-603` | +| F7 | `g_pts`/`g_offsets` 每次构建**清零重算**(`w64(g_pts, var_idx*8, 0)`)⇒ 它们本身不是「缺失」源;但**其输入含 F5 的行内容** ⇒ 缺陷经由类型行传导 | `ptr_analysis.cr:172-210` | +| F8 | 同族先例:v18 已把「项表」按「**不落盘、装载期重派生**」收口(避免第二真源 + 跨进程索引悬空);**类型行空间无同等处理** | `cir_cache.cr:24-50`(v18 注)+ `sh_tk_split_load` | +| F9 | 「关缓存」为本仓既有兜底(可选表示程序即如此) | `main.cr:488-491` | + +**⇒ 根因假说(待 T1 实锤)**:**暖路径的行号空间与冷路径不一致**——缓存命中跳过 `ir_gen_func` ⇒ F4 的 5 处行不分配 ⇒ 后续函数/前端的 `alloc_type` 序号整体前移(或后移),而快照里的 `v_type` 行号是**冷路径**的编号 ⇒ 命中后 `get_type_kind(v_type)` 读到**别的行**(或未初始化行)⇒ `== TYP_PTR && extra != 0` 不成立 ⇒ TU03 静默跳过。 + +### 1.2 缓存命中路径数据流(实读) + +``` +[逐函数] main.cr:535 `if cache_enabled != 0 { cached = load_cir_cache(cache_path, fi); }` + ├─ 命中(`cached == 0`,main.cr:536-549):恢复 vars/nodes/edges/instrs/SG; + │ **不执行** ir_gen_func(⇒ F4 的 5 处 alloc_type、g_ir_locals、字符串常量、 + │ 外链重定位、内层 SG push/pop 等**生成期副作用全部缺席**) + └─ miss(main.cr:550-557;`ir_gen_func` `:552` + `save_cir_cache` `:556`):ir_gen_func + save_cir_cache(冷路径,副作用全有) +[全函数后] main.cr:565 `df_state_finalize()`(链重建,幂等) +[安全面] :598 `ptr_analysis_all()` → `region_check_all()` → `provenance_verify_all()` ⇒ `:603` 判定 + ⇒ 消费 irv_type(快照来的行号)+ 行内容(**本次进程的行空间**)⇒ F5 的判定 +``` + +### 1.3 **侧表面全集**(「进程内状态、快照不载」的候选面;T0 出「逐条实证版」) + +> 口径:仅列**生成期(`ir_gen`/`lower`/passes)建立**且**被缓存命中后的消费者读取**的表。**判定列** = 本实例实读结论(`✓` 已重建 / `⚠` 不载 ⇒ 嫌疑 / `?` 待实证)。 + +| # | 侧表 | 建立期 | 快照是否载 | 消费者 | 判定 | +|---|---|---|---|---|---| +| 1 | **`g_types` 行内容(`{kind,data,extra}`)** | 前端 + **`ir_gen`(5 处)** | **不载**(只载行号) | 判定引擎桥接 / ICE04 / **TU03 检查** / `type_size` / `lower` | ⚠ **本缺陷主嫌**(F2/F4/F5) | +| 2 | `g_ir_locals`(局部变量表) | `ir_gen` 逐函数 | 不载 | `lower_to_ccr` / 帧布局 | ⚠ 嫌疑(T1 实证:命中函数的 local 计数/内容) | +| 3 | `g_ir_str_consts`(字符串常量池) | `ir_gen` 逐函数 | **部分**(快照有字符串段,`:221-227` ——须实证覆盖面) | `.ccr` STR 段 / 运行期常量 | ? 待实证 | +| 4 | `g_x86_ext_rel_*`(外链重定位表) | `ir_gen`(`IR_CALL_EXTERN` 等) | 不载 | **corearch 静态链接符号解析**(`ctx_emit_static` 的 unresolved 检查) | ⚠ 嫌疑(命中 ⇒ 重定位缺失 ⇒ F16② 类静默?) | +| 5 | **`g_df_node_region` / `g_sgs` 内层 region** | `ir_gen`(`sg_push/sg_pop`) | **部分**(v13 载 SG 记录;装载把节点归属到 `g_cur_sg`) | `region_check`(B011)/ `subgraph_containing` / DFG DOT | ⚠ 已知近似(`cir_cache.cr` 装载段自注「Minor #4」) | +| 6 | `g_ir_var_rep` / `g_ir_var_decl_ti`(可选表示侧表) | `ir_gen` | 不载 | 解包点表示判定 | ✓ **已由「可选程序关缓存」缓解**(`main.cr:488-491`) | +| 7 | `g_pts` / `g_offsets` / `g_pa_alloc_nodes` | `ptr_analysis_all`(**每次构建**) | 不载(且不需) | `provenance_verify` / `region_check` | ✓ 每构建清零重算(F7)——**但输入受 #1 污染** | +| 8 | `g_type_terms` / `g_term_map`(项表/映射) | emit 期按需追加 | **不载、装载期重派生**(v18 设计) | 判定引擎 / `.ccr` TYPE 段 | ✓ 已收口(F8) | +| 9 | 状态链(state 边) | `df_state_finalize`(幂等) | 不载(v15 起设计如此) | 纯度/效应面 | ✓ | +| 10 | `g_ir_globals` / `g_x86_is_global` | `ir_gen_globals`(**循环前一次**) | 不载 | 全局寻址 | ✓(循环前跑,与命中无关) | +| 11 | 前端族(`g_syms`/`g_funcs`/`g_structs`/`g_enums`/`g_ifaces`/`g_impl_for`/`g_methods`/`g_type_aliases`/`g_gen_*`/`g_borrow_*`/`g_global_lets`/`g_cov_bits`/`g_purity_inst_*`/`g_line_fileid`/`g_segs`) | `tokenize`/`check_all`(**每次构建**) | 不载(且不需) | 各前端消费者 | ✓ 前端每次全跑(`main.cr:440` `run_frontend` 在缓存循环之前) | +| 12 | `g_ccr_type_seg`/`g_ccr_iface_seg`/`g_ccr_nod_item`(段体缓冲) | `ccr_seg_prepare_save`(save 前) | 不载(且不需) | `save_ccr` | ✓ | +| 13 | `g_strs`/`g_str_hash`(驻留表) | 全流程 | 不载 | 一切名字索引 | ✓ **由编译器身份字段覆盖**(v17 / TODO #5) | +| 14 | `g_dyn_type_sets` / `g_gen_map` / `g_gen_apply_data` | 前端 | 不载 | 前端 | ✓ | +| 15 | `g_hotpatch_*` / `g_rt_builtin_*` | 前端注册 | 不载 | 调用/注册面 | ✓ | + +**⇒ 侧表面全集 = 15 条**(`⚠` 嫌疑 4 条:#1 主嫌 · #2/#4/#5 待实证;`✓` 11 条)。**T0 的交付 = 逐条把「判定」列做实**(含一条脚本:列出全部 `g_*` 侧表 × 建立期 × 是否在 `cir_cache.cr` 段清单内 × 消费者)。 + +### 1.4 证实 / 证伪实验设计(T1 执行) + +| 实验 | 做法 | 判读 | +|---|---|---| +| **E1 行号空间对照** | 冷/暖两态各跑复现源,**打印**:命中函数的每个 var 的 `irv_type` + `g_type_count` + 该行 `{kind,data,extra}`(经新增**隐藏 dump 通道**,照 `--dump-tk-terms`/`--dump-nod-items` 先例) | 冷/暖两态**行内容不同**(或行号越界/行错位)⇒ 假说成立 | +| **E2 归因钳位** | 在 `provenance_verify.cr:66` 的判定前插入**只读**计数(冷/暖两态各跑)——统计「`ptr_ti >= 0` 通过数 / `TYP_PTR` 通过数 / `extra != 0` 通过数」 | 若暖态在 `TYP_PTR` 或 `extra` 步归零 ⇒ 归因定死该判定 | +| **E3 反向钳位(证伪)** | 关缓存(`COREC_...` / 代码内 `cache_enabled = 0` 的**等价物**:先删 `.core/cache/cir`)后重跑 ⇒ 诊断恒在 | 若「关缓存仍消失」⇒ 假说被证伪(另有他因) | +| **E4 覆盖面扫(report-only)** | **冷/暖两态对拍**跑全语料(72 + 探针 29 + 套件内联源),diff「诊断码集」⇒ 产出**暖态静默面清单**(不只 TU03) | 清单 = 本缺陷的**真实影响面**(也是 T3 暖态腿的**预期基线**) | +| **E5 侧表面逐个实证** | 对 §1.3 的 `⚠` 各条做 E1 式对照(locals 计数 / 重定位表长 / SG 内层结构 / 字符串池) | 逐条给「有缺陷 / 无缺陷」定论 | + +**E4 是「先量后改」的核心**:修法取向未定前,先量出「暖态静默面到底有多大」(可能一例,也可能一打 ⇒ 见裁决门 ③)。 + +--- + +## 2. 修法候选(4 条)+ 推荐 + +> 判据统一为:**暖态不得静默**(宁可 miss 不可静默)+ 不引入第二真源(D20)+ 缓存收益不塌方(命中率可接受)。 + +| 候选 | 形态 | 影响面 | 风险 | 判据 | +|---|---|---|---|---| +| **(a) 命中时重建** | 装载后补跑生成期副作用(重放 `alloc_type` 5 处 / `g_ir_locals` / 重定位 / SG) | 需**精确复现分配序**(跨函数、含部分命中) | **高**:分配序依赖函数内控制流 ⇒ 相当于「再跑一遍 ir_gen」=缓存收益归零 | 冷/暖两态 `g_type_count` 与行内容逐行同 | +| **(b) 命中时检测缺失 ⇒ 拒绝条目(fail-closed)** | 装载后**不变量校验**:录 `type_count`/行指纹于条目头,装载期不符 ⇒ 视作 **miss**(无害重建) | 小(快照 +8~16B/条目;VER bump 视形态) | 命中率下降(若校验过粗 ⇒ 大面积 miss);**必须先量**(E4 + 命中率实测) | 暖态诊断**恒在**(= 冷态);命中率下降幅度有实测数字 | +| **(c) 快照扩容载入侧表** | 把生成期 `g_types` 行(或必要的 5 类行)落盘 + 装载期恢复 | 中(格式变更 + `CIR_CACHE_VER` bump + 旧条目整类失效) | 第二真源(D20):行内容可由 (kind,data,extra) 重派生 ⇒ 与 v18 的「不落盘重派生」先例**冲突**,须论证 | `.ccr`/canary 逐字节同 + 冷/暖 dump 逐行同 | +| **(d) 把依赖侧表的安全检查移出缓存盲区** | 让安全面**不依赖生成期行内容**:如把「外部指针」标记**在 IR 内**承载(辅码/新 opcode 变体)或由前端(每构建必跑)落定 ⇒ 安全检查只读 IR | 中(触 `ir_gen`/`.ccr` 布局/消费点) | 改发射/格式面 ⇒ canary 硬闸;须先证 IR 面能无损承载 | canary IDENTICAL + 冷/暖两态诊断同 + `.ccr` 记录值重锁论证 | + +### 推荐(**两步走**) + +1. **第一步(无条件):候选 (b) 的**最小形态**——**装载期不变量校验 + 违则 miss**(fail-closed 兜底)。理由:① 与 FC 批「宁可 miss 不可静默」同精神;② 改动小、不触格式/发射面;③ **立刻消灭静默**(命中率代价待 E4 量化后可再优化);④ 与 v14/v16/v17 的既有 bump 先例同族(**先止血**)。 +2. **第二步(按 T1 实证 + 裁决):(d) 优先于 (a)/(c)**。理由:(d) 把「生成期量表」从安全面依赖中移除 = **根因级**(隐患不只 TU03:§1.3 的 ⚠ 各条同源);(a) 的收益/成本比最差(等于放弃缓存);(c) 与 v18「不落盘重派生」先例冲突,须维护者裁(**待裁**:格式面变更)。 + +**⚠ 待裁标注**:候选 (c) 涉**快照格式面**(`CIR_CACHE_VER` bump + 失效面);候选 (d) 涉**IR/发射面**(canary 硬闸 + `.ccr` 记录值)——两者均须维护者批。 + +--- + +## 3. 裁决门(开工前置) + +| # | 问句 | 影响 | 本计划推荐 | 未取裁时 | +|---|---|---|---|---| +| **裁-W1** | 修法取向:**(a) 重建 / (b) 检测即 miss / (c) 快照扩容 / (d) 移出盲区**?(可组合,如 b+d) | 全批 | **b(先行,兜底)+ d(根因,若 T1 证实 §1.3 ⚠ 面不止一处)**;(c) 须另批(格式面) | 只落 b(fail-closed 兜底),其余登记 | +| **裁-W2** | **暖态腿是否进 CI**?本仓五 CI job 现无暖态面;加入 = CI 时长 ↑(`selfhost-tests` 已 108s)vs 盲区闭合 | T3 + `run.sh` | **进 `selfhost-tests`(最小面:单档冷/暖对拍)+ 全量暖态扫描留手工判据**(照 `tools/baseline/` 的「手工判据 + 入仓配方」先例) | 只入仓 runner(手工跑),CI 不动(登记盲区) | +| **裁-W3** | 是否**顺带扫其它侧表面**(§1.3 的 ⚠ 4 条 + `?` 1 条)?一次可能暴露多例 | T1/T2 范围 | **扫**(E4/E5,report-only 零风险)——**命中面过大 ⇒ 转独立批(停条件)** | 只修 TU03 面(其余登记) | +| **裁-W4** | 可否用「**对该类程序关缓存**」作为兜底(照 `g_optrep_on` 先例 `main.cr:488-490`)? | 修法面 | **可作为 b 的降级备选**(若 (b) 的校验不可实现或命中率崩) | 不实施;仅登记 | + +--- + +## 4. 任务总表 + +| 任务 | 内容 | 依赖 | +|---|---|---| +| **T0** | 前置侦查 + 冻结基线(`rebuild.sh`)+ 起点判据 + **侧表面全集逐条做实**(§1.3 的 15 条 × {建立期/是否载/消费者/判定})+ 复现源归档 | — | +| **T1** | **根因实证**(E1–E5):冷/暖 dump 通道 + 归因钳位 + 证伪项 + **暖态静默面全语料对拍清单**(先量后改) | T0 | +| **T2** | 修法落地(按裁-W1;b 先行 → d 视证据)+ 用例(冷/暖两态行为钉 + 负控「不安全程序不得静默通过」) | T1 + 裁-W1 | +| **T3** | **判据网暖态腿**:`parity_run.sh`/`probes_run.sh` 加「同源二次构建」腿(冷/暖 rc + 诊断对拍);CI 面按裁-W2 | T2 | +| **T4** | 判据复验(五 CI/枚举/selftest/canary/`.ccr` 四条/自举链/`backend_bootstrap`)+ 文档(TODO #60 收口 + `cir_cache.cr` 版本族注 + 本计划台账)+ 批终态 | 全部 | + +--- + +## 5. Global Constraints(照 P6/FC 简版) + +- **jj only**;**提交路径限定**;`nice -n 19`;一构建一编译串行(每段前 `ps` 实查);cwd = 仓库根;比较任何 `.ccr`/缓存态产物前 `clean-cache`。 +- **/tmp 空间(本批新增)**:`/tmp` 仅剩 ~5G ⇒ **构建/产物控制在 /tmp/fct4 之外小量**;**遇 ENOSPC ⇒ 停下报告**,只清本批自己的临时物并逐条登记(禁删证据目录)。 +- **ELF canary(硬闸)**:`95084e7b…d475`(28822B);**本批若触发射面(候选 d)⇒ 变即停**(须先裁)。 +- **`.ccr` 四条(口径带命令 + 冷/暖)**:96015/96158/142793/142936(差恒 143B);**本批正是「冷≠暖」缺陷 ⇒ 记录值必须**分冷/暖两栏**(冷栏 = 上述锁值;暖栏 = T0 实测)。 +- **三态纪律**:装载期任何「不可证」⇒ **拒绝**(= miss),不得静默采用。 +- **先量后改**:T1 的 E4 清单未出、裁决门未取裁 ⇒ **不得落修法**(report-only 先行)。 +- **测试只增不减**;用例带「冷/暖两态」维度;**`.cir` 缓存条目不是交付产物**(口径豁免,照 FC 批裁-FC-6)。 +- **不得回退既有收纳**:v12–v18 的版本族处理、`sh_tk_split_load` 重派生、编译器身份字段(TODO #5)、FC 批闸门(默认阻断 + 豁免表 9 条)——一律不动。 + +--- + +## 6. 起点基线值(出处逐条;**本计划未复测**) + +| # | 项 | 值 | 出处 | +|---|---|---|---| +| 1 | 起点提交 | `1592a1b5`(FC 批 T6 收官) | 协调者给定;本实例未跑 `jj` | +| 2 | 二进制 | `corec 308f06a3…` / `corearch 22e96d03…` | FC T6 报告(重建后与 T3 逐字节同) | +| 3 | 五 CI job / 全枚举 / selftest | 5/5 rc=0 / **60/60** / **415/415** | FC T6 报告 §1 | +| 4 | canary / `.ccr` 四条 | `95084e7b…d475`(28822B)/ 96015·96158·142793·142936(差恒 143B) | FC T6 报告 §1 | +| 5 | 72 档 / 探针 29 | 34×0/38×1 / 17×0/12×1 | FC T6 报告 §1 | +| 6 | 自举链 / `backend_bootstrap` | `24802386a1…` IDENTICAL + N06=0 + 冒烟 42 / rc=0 | FC T6 报告 §1 | +| 7 | **暖态缺陷证据** | 冷 rc=1 无产物 / 暖 rc=0 出 ELF(`build --static` 与 `ccr` 同病;P6 期二进制同值) | FC T2 报告 §7(本批主腿的 RED 起点) | + +--- + +## 7. 停条件 · 未决项 · 自检记录 + +### 7.1 停条件 +1. **E4 命中面过大**(暖态静默面 **> 3 档**或涉及 **2 + 类码**)⇒ **停下上报**(修法取向需重裁;可能转「缓存面重设计」独立批)。 +2. 修法须**触快照格式**(候选 c)或**发射面**(候选 d)而**未取裁** ⇒ 停。 +3. canary / `.ccr` 四条 / `--dump-objects` / `.cir` DOT 任一变化 ⇒ 停(发射面泄漏)。 +4. **关缓存兜底也无救**(E3 证伪:关缓存仍静默)⇒ 停(假说崩塌,须重侦查)。 +5. `/tmp` ENOSPC ⇒ 停手报告(禁删证据)。 +6. 暖态腿**入 CI** 后任一 job 由绿转红且非本批预期 ⇒ 停。 + +### 7.2 未决项(不猜) +1. **行号空间漂移的确切形态**(越界 / 错位 / 未初始化)——**待 T1 的 E1 实测**;本计划只给假说与锚。 +2. 命中率代价(候选 b 的校验粒度 ⇒ miss 率)——**待实测**(T1/T2)。 +3. §1.3 的 `?`/`⚠` 各条(#2/#3/#4/#5)是否有独立缺陷——**待 T1 的 E5**。 +4. `cir_cache.cr` 版本族注是否需新增 v19 段——按修法形态定(**待裁-W1**)。 +5. 暖态腿的 CI 挂点与时长增量——**待裁-W2** + T3 实测。 + +### 7.3 自检记录 +- **本计划未跑任何构建/测试/`jj`**;所有「现状值」= 实读源码或既有 FC 报告记录值(出处已标);需实测的数字(暖态静默面档数、命中率、行号空间对照)**一律留 T1**,未预填。 +- **根因段落的诚实性**:**假说**与**事实**分离(§1.1 九条实读事实 + 一条标注「待 T1 实锤」的假说);未把「疑似机制」写成「已证机制」。 +- **与既有裁决的一致性**:不触 FC 批闸门/豁免表(9 条不动);候选 (c)/(d) 的越界面已显式标「待裁」;「关缓存」备选有既有先例(`g_optrep_on`)但不擅自实施。 +- **本批最大风险(一句)**:**修法若选「命中时重建」(候选 a)会吃掉缓存收益(等于每构建全量 ir_gen),选 (c)/(d) 则触格式/发射面**——故推荐 **b 先行止血 + d 根因(须裁)**,且**先量后改**(E4 清单未出不得动码)。 + +--- + +## 8. 统一台账(T1–T4 收官;2026-09-15) + +> 提交链:`e0e92eef`(T2 = 修复 + 套件 + TODO)→ `227b747e`(T3 = 语料 + 暖态腿 + 文档)→ 本 T4 收官提交(台账 + 终态 + 文档指针)。 +> 报告:`/tmp/fct4/task1-report.md`(T1)· `/tmp/fct4/task2-report.md`(T2)· `.superpowers/sdd/warm-task3-report.md`(T3)· `.superpowers/sdd/warm-task4-report.md`(T4/本文件同批)。 + +### 8.1 根因链(F1–F9 实读 + E1 实锤) + +**F1–F9(计划 §1.1,实读锚不变)**:快照载 var 表(含 `type` 行**号**)不载行**内容**(F1/F2)· `alloc_type` +只追加不去重(F3)· `ir_gen` 期 5 处分配(F4:`ir_gen.cr:1403/1409/2584/2641/2951`)· 消费点读「行号 → +行内容」(F5:`provenance_verify.cr:63-70`)· 安全面在命中路径照常跑(F6)· `g_pts`/`g_offsets` 每构建清零 +重算但**输入含行内容**(F7)· 项表已有「不落盘 + 装载重派生」先例而类型行空间无同等处理(F8)· 「关缓存」 +是既有兜底(F9)。 + +**E1 实锤(T1 决定性实验,零源改动)**:无诊断对照源冷/暖各跑 `--dump-types` ⇒ 冷 **13 行** / 暖 **11 行**, +**恰缺 `row 11 (TYP_PTR extra 0)` 与 `row 12 (TYP_PTR extra 1)`**(外部指针标记行);`.ccr` −174B。 +**形态判决(修正计划假说的措辞)**:本例缺失行是**尾部两行** ⇒ 命中后对它们的引用**越域**(`get_type_kind` +的域检查落空返默认)⇒ `== TYP_PTR && extra != 0` **判定落空** = TU03 静默;**一般形态**(多函数交错、 +跳过的是中间函数的分配)可为**行号错位**——两者由**同一条见证规则**闭合(分配行的函数必重放 ⇒ 行空间 +逐行恒等,见 8.2 归纳论证)。 + +### 8.2 修法 (b):形态 · 一般化论证 · 代价 + +- **形态**(裁-W1 = (b) 先行):`main.cr` miss 分支 `tc0 := g_type_count` → `ir_gen_func` → **仅当 + `g_type_count == tc0` 才写条目**(= 裁决语「快照记录该位、命中时该位为真 ⇒ 当 miss」的零格式等价实现; + 把 bit 写进快照反而触格式变更 ⇒ 违约束②)。**零格式(`CIR_CACHE_VER` 仍 17)/ 零发射面**。 +- **一般化论证**:判据 = 「生成期对**快照不载**的共享面有副作用 ⇒ 不可缓存」——补丁零处引用 TU03/TYP_PTR。 + **完备性旁证(实核)**:`g_types` 只增不改(写点 = `alloc_type` + 装载器)⇒ 计数式见证对该面完备; + `ir_gen` 对 `g_ast` **零写点**(`ast_patch_node` 唯一调用者 = 自身 = 死码)⇒ AST 非生成期共享面。 +- **正确性归纳(行号空间守恒)**:冷跑落盘者 ⇒ 冷跑分配 0 行 ⇒ 暖跑命中亦 0 行;冷跑未落盘者(含被抑制者) + ⇒ 暖跑**重放** `ir_gen_func`,在相同输入态下确定性产出相同行 ⇒ 两态 `g_types` **逐行恒等**。 +- **代价(实测,Phase C2)**:语料 101 档 **989→986 条目(0.3%)**(被抑制者 = `ptr_arith`/`ptr_ref_first`/ + `n19_ref_named` 的 `::main`)· 编译器自源 **110→109(0.9%)**;被抑制者全为**分配复合行**的函数。 +- **连带面(㊲ 重钉的死亡证据)**:**复合 mint 行的唯一来源 = ir_gen 期分配复合行** ⇒ 见证下这类函数 + **结构上不落盘**(101 档全扫:仅 `ptr_ref_first.cr` 有复合行、全缺)⇒ `test_ccr_types.py` ㊲ 原判据 + **可达主体为空** ⇒ 重钉(可缓存夹具 + 「带码 mint 行」全集;死亡证据入 docstring)+ 新增 ㊳ 能力变更钉 + + ㉟ 收口(暖态丢项**钉死为 0**)。 + +### 8.3 (c)/(d) 裁决(性能面,非正确性)+ 两个可证伪触发条件 + +依 T2 报告三条判据:① 正确性下界 = 已知面冷/暖已一致(无残留静默)⇒ **对正确性不必要**;② 代价上界 = +(c)/(d) 收益是**恢复「分配行函数」的命中**(0.3%/0.9% + 复合行类缓存归零)⇒ 性能面;③ 未覆盖面(8.6 四条) +消费者未构造出 ⇒ (c)/(d) 是未判面的最终归宿但触发条件未达。**维护者裁(2026-09-15):本批不实施**,挂 +**两个可证伪触发条件**:**(i)** 任一未判面构造出暖态消费者;**(ii)** 缓存命中率成实测瓶颈。触发后**先试 +「加一行见证」的廉价路径**(`main.cr` miss 分支扩展点),不够再转「快照承载类型行」(格式变更 + +`CIR_CACHE_VER` bump,与 TODO #5 编译器身份字段同批评估)。 + +### 8.4 判据网盲区三因(本缺陷为何从未现身) + +1. **语料无形态**:全仓 `as *T`(外部指针)在 `tests/suite`/`tests/probes`/`src/stdlib`/`examples` **零命中** + (唯一 grep 命中为 `type_selftest.cr:3405` 的 `t3_gas * 8` 假匹配)⇒ 101 档冷/暖**码集零差异**。 +2. **runner 逐档 `clean-cache`**:`parity_run.sh:35` 等 ⇒ 判据面结构性无暖态。 +3. **套件唯一 temp 路径**:缓存键 = 源路径::函数名 ⇒ 键唯一 ⇒ **结构性无暖态**(T1 方法学发现;真触发 = + **同一路径重复编译** = 增量构建场景)。 + +### 8.5 决定性实核两条(新知识) + +- **`check` 面写 0 条缓存条目**(`ccr` 面 15)⇒ 暖态腿**必须走 `ccr` 面**;只给既有 runner 加「同路径二跑」 + = 恒绿空洞(T3 §2/§5)。 +- **承重性实证(T3 突变)**:**M3** 把暖跑改成唯一 temp 路径 ⇒ 缺陷在场仍 FAIL=0(**定路径是承重设计**); + **M4** 断言致盲 ⇒ 缺陷不可见(**断言是承重件**)。另 M1(去见证 ⇒ 牙齿腿 FAIL=2 红)/ M2(不变量过宽 ⇒ + FAIL=0 但**暖态生效 0 = 空洞警报**)。 + +### 8.6 遗留未判面(4 条,登记不猜) + +① `g_ir_locals`(无 dump 通道,未单测);② 字符串常量池(arena 档 `.ccr` 差 −108B 但类型段全同 ⇒ 落点未 +逐段归因);③ 外链重定位(extern 探针未产生 ext rel ⇒ 面未构造);④ region/SG(B011 两形态探针只触发 +TA01/TU03 ⇒ 未构造)。**处置** = 任一构造出暖态消费者即触发 8.3 条件 (i)。 + +### 8.7 自纠登记(三件,均如实入报告) + +① 腿首跑快照取在**冷跑前**(条目=0 假读数)⇒ 修为冷跑后取;② M3 突变脚本 `$RANDOM` 用两次 ⇒ `cp` 与 +`f` 指向不同文件(假红)⇒ 修为单一变量;③ M3 首跑未重定向 stdin ⇒ 广度层 `read` 挂起 10 分钟 ⇒ kill 后 +以 `< /dev/null` 重跑。三件均**不影响最终读数**(修正后全量重跑)。 + +### 8.8 腿本体不进 CI(维护者裁 2026-09-15) + +理由:与套件**同语料**、覆盖重叠,收益仅「校验 runner 自身」⇒ **手工判据**;「**何时该跑**」四条已写入 +`tools/baseline/REBUILD.md`(改 runner / 改缓存面 / 关·降级缓存 / 改语料)。CI 最小面 = 套件 +`tests/selfhost/test_warm_cache_gate.py`(10 例,0.5s,已入 `selfhost-tests`)。 + +--- + +## 9. 批终态陈述(三栏) + +见 `.superpowers/sdd/warm-task4-report.md` §「批终态三栏」与 §「起终点对照」(逐项标同/异 + 原因)。 diff --git a/docs/superpowers/specs/2026-08-08-crasm-design.md b/docs/superpowers/specs/2026-08-08-crasm-design.md index 093853e4..1e1465bf 100644 --- a/docs/superpowers/specs/2026-08-08-crasm-design.md +++ b/docs/superpowers/specs/2026-08-08-crasm-design.md @@ -3,6 +3,8 @@ 日期:2026-08-08 状态:已批准(brainstorming 会话,五节逐节确认);2026-08-09 整理为正式文档 → `docs/crasm.md`(本 spec 保留为批准记录) +> **2026-09-05 退役**:独立 .crasm 格式取消——绑定经典硬件过深;诉求被硬件接口表(HIT)吸收(见 `docs/superpowers/specs/2026-09-05-hardware-interface-table.md`)。本文保留为历史批准记录。 + ## 1. 背景与动机 Core 的内核路线(project-book 第五阶段)需要汇编级能力(MMIO、特权指令、中断), diff --git a/docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md b/docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md index 2ab525f3..4695a878 100644 --- a/docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md +++ b/docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md @@ -1,7 +1,7 @@ # 格形态 IR 设计:.ccr 升级为格形态(C 路线) 日期:2026-08-27 -状态:**设计定稿(方向)**——执行成本交由第二维护者(TODO 横向扩展),验证闭环归 DslsDZC。关联:`docs/regalloc-cache-mapping.md`、`docs/memory-model-capability-lattice.md` v4。 +状态:**方向定稿 → 已实质化实施(2026-09)**——v6 格式与数据面落地(`specs/2026-09-05-lattice-ir-v6-format.md` 格式定稿 + `plans/2026-09-05-lattice-ir-v6.md` 实施计划 Task 1-5 完成:存在区间/条目版本化/共存推导/段表 IO/判定自检);v5→v6 一次性转换按定稿取消(.ccr 中间产物零生态,v6-only)。关联:`docs/regalloc-cache-mapping.md`、`docs/memory-model-capability-lattice.md` v4。 ## 1. 背景与动机 diff --git a/docs/superpowers/specs/2026-09-05-hardware-interface-table.md b/docs/superpowers/specs/2026-09-05-hardware-interface-table.md new file mode 100644 index 00000000..05d12256 --- /dev/null +++ b/docs/superpowers/specs/2026-09-05-hardware-interface-table.md @@ -0,0 +1,132 @@ +# 硬件接口表设计(Hardware Interface Table) + +日期:2026-09-05 +状态:设计定稿(结构拍板:最小核 4 事件 + 表驱动运行架构);**M1 最小核运行闭环已完成(2026-09-06)**——表文件/编码器/合成层/冒烟落地(`src/arch/hit/` + `emit_instr_tabled` + `lower_to_core.cr`);M2 挂账见 TODO +关联:`docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md`(v6 格式)、`docs/superpowers/specs/2026-08-23-hw-map-design.md`(MMIO 设备表——同家族前件)、`docs/regalloc-cache-mapping.md`(优化/判定独立于表)、width-out-of-language 定案(编码 = hw-map 领域)。 + +## 1. 定位与命名 + +**硬件接口表(HIT)= 运行的唯一平台依赖**。corearch 除优化问题外零平台代码——指令发射、运行时资源、系统边界全部查表。**替换表 = 换目标平台/范式**:同一 Core 程序(v6 NOD + 图语义)配不同表直接运行。 + +- 命名:**接口**表而非映射表——接口 = 语义契约(签名),实现 = 范式投影;映射暗示经典绑定,接口自明范式无关 +- 与类型系统定案同族:语言类型接口注册表(int/dex/string 原生条目)+ 硬件接口表 = 接口 = 图上契约的编码层实例 +- 与 MMIO hw-map 同构:语义表(范式无关)+ 投影表(平台实例)——本表是该结构在指令/运行层的推广 + +## 2. 最小核(运行契约,4 事件) + +**底层实现这 4 个接口事件即可运行 Core**(其余语义全部编译/合成为这 4 个);硬件内部实现自由(微码/查表/专用加速),契约只管对外暴露。 + +| 事件 | 签名要点 | 不可去论证 | +|---|---|---| +| `sub` | 算术完备单点:add(x,y) = x−(0−y);比较 = 差符号;neg 免费 | 全算术在它上合成 | +| `nand` | 逻辑完备单点:全部布尔(and/or/xor/not)组合可得;布尔 = 算术进位基础 | 全位逻辑在它上合成 | +| `load` | 效果事件:「读世界」(外部状态 → 值流) | 计算完备替代不了读世界 | +| `store` | 效果事件:「写世界」(值流 → 外部状态,state edge 语义落点) | 同上 | + +- 事件签名**无宽度、无寄存器、无寻址**(图边已含搬运——Core 不需要 mov 类事件;宽度/形状归投影) +- 可合成项全部落扩展事件:add/mul/div/移位/and/or/xor/not/cmp/select/const/branch/call… +- 合成代价是编译器问题不是契约问题(硬件内部可加速) + +## 3. 表结构 + +### 3.1 事件条目(接口 = 签名) + +``` +事件条目: + event_id (最小核固定 1-4:sub=1 nand=2 load=3 store=4;扩展从 5 起) + 签名:输入角色数 / 输出角色数 / 副作用类(纯 | effect | volatile) + 语义锚:指向规约层(.corespec)的可选引用 +``` + +签名只描述「接口」,不描述实现。任何范式的投影 = 「该事件在范式里如何实例化」: + +### 3.2 投影条目(范式实例) + +``` +投影条目: + event_id → 范式实例描述 + 经典离散:编码规则(ISA 模板/编码序列)——「实现它」 + 通道类范式(oracle/交互):外部通道映射——「接通它」 + 连续/量子类:动力学元件描述——「演化它」 + 无实现:条目缺失 → 编译期拒绝或 unsafe 外部入口 + (超图灵事件 × 经典投影 = 缺失;与 MMIO 动态地址同角色,先例已在) +``` + +**投影不一定是「编码成字节」**——投影 = 事件 → 范式资源的实例化;经典行恰好是 ISA 编码。 + +### 3.3 运行条目(「关于运行的都跟表相关」) + +除指令发射外,运行所需资源同为表条目(替换表即可换 ABI/运行时): + +``` +运行时条目: + 调用资源:call/ret 机制(栈约定 | 续体 | 通道)——最小核无 call,属扩展 + 存储资源:load/store 的地址语义(线性内存 | 区域 | 通道寻址) + 系统边界:extern/入口点约定 + 宽度/形状:事件无宽度,投影在此声明(int 无限精度如何落到机器字) +``` + +优化(寄存器分配/调度/CSE)**不走表**——判定与分配在格形态层(regalloc-cache-mapping 四条),表只描述「材料」不描述「放置策略」。 + +## 4. 大表(扩展事件全集) + +最小核之上,大表 = 平台完整事件目录: + +``` +1-4 最小核(契约,必须) +5+ 扩展事件(可选加速/平台能力): + add sub mul div shl shr and or xor not cmp select const branch call ret + atomics / vector / float / extern(oracle) … +``` + +- 程序只用最小核 → 任意表可跑(可移植子集) +- 程序用扩展事件 → 该表须含对应投影(查表缺失 = 编译期拒绝,或编译器降级合成) +- **表即编译器与平台的能力协商面**:corec 产出的事件不超表 → 平台保证运行 + +## 5. 合成层(IR → 核事件) + +Core 语言/IR 操作数(38 op)→ 合成到表事件: + +- 编译器持有「事件降低规则」(op → 核事件组合):add → sub 反减;and → nand;移位 → 重复;const → 图常量注入机制 +- 表声明了扩展事件时,降低规则改为直接发射(查表优先,无表项再合成) +- 合成层位置:corec(IR 层降低)或 corearch(读表后降低)——实施期定(倾向 corearch:表是运行层,降低随表走,换表换合成策略) + +## 6. 实施里程碑 + +**M1 最小核运行闭环**(当前目标——「先把最小核这个表先成功运行起来」): +1. HIT 表文件格式定稿(文本/二进制?——先行文本 toml/json 或 Core 语法,v6 后并入二进制段) +2. 表驱动发射框架:corearch 读表发射(先 x86 投影:sub/nand/load/store 4 条真编码) +3. 合成层最小实现:现有 IR op → 4 核事件的降低规则(先覆盖冒烟程序所需子集) +4. 冒烟闭环:`.cr`(加减/位运算/内存读写程序)→ 最小核表 → ELF 运行正确 +5. 回归不破:大表(38 op 直通条目)保留期间,全套测试仍绿——最小核表作为新后端路径与现路径并行验证 + +**M2 全表驱动**:instr.cr e2_* 硬编码 → 大表数据;运行时条目接入;v6 接表 +**M3 范式扩展**:第二张表(arm64 或参考机);超图灵接口条目语义定稿 + +## 7. 边界(YAGNI) + +- 优化不进表(判定/分配 = 格形态层) +- 本设计不定义「图执行契约」(范式无关保证在图层——验证清单 8 项承接),只定义事件解释器族的标准路径 +- 合成规则的完备性证明(一切 IR op 可降到 4 核)是验证层事项,实施期以冒烟 + 回归实证 + +## 8. crasm 取代说明(2026-09-05 定稿) + +独立 `.crasm` 格式取消(旧文档 `docs/crasm.md` 与 `specs/2026-08-08-crasm-design.md` 标注废弃/退役): + +| crasm 诉求 | HIT 框架下的取代 | +|---|---| +| 跨平台统一汇编 | HIT 事件 + 投影表——换表即换平台,无中间汇编语言 | +| 无限虚拟寄存器 | 寄存器概念整个取消——图边含搬运,值 = 流引用 | +| 平台映射表(x86/ARM/RISC-V 私有) | HIT 投影条目(唯一表,编译器与手写共用) | +| 特权指令固定集(mmio/barrier) | HIT extern/接口事件 + `.cr` unsafe——不预绑硬件 | +| 寄存器生命周期验证 pass | 验证语义上移图/格框架(值流无生命周期问题) | + +内核路线(project-book 第五阶段)的「汇编级」诉求 = `.cr` unsafe + HIT 接口事件;事件层可读形态 = v6 NOD 文本 dump(工具,非语言)。 + +## 9. 关联 + + +- `2026-09-05-lattice-ir-v6-format.md` — v6 NOD op 引用事件 id(实施期对齐:NOD op ↔ 表 event_id) +- `2026-08-23-hw-map-design.md` — MMIO 设备表(同构前件:语义表 + 投影表) +- `2026-08-30-type-system-direction-design.md` — 接口注册表定案(同族概念) +- width-out-of-language — 宽度/形状归 hw-map 领域(本表投影条目) diff --git a/docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md b/docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md new file mode 100644 index 00000000..81e1efc3 --- /dev/null +++ b/docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md @@ -0,0 +1,200 @@ +# 格形态 IR v6 二进制格式设计(存在结构主体) + +> ## ⛔ 文档状态:已被 v7 取代(2026-09-10 Task 3 收官注记) +> +> v6 字节格式(5 段、NOD 28B、ENT 恒空、边不落盘)未单独实现即被 v7 演进取代—— +> 2026-09-09 起字节权威 = `2026-09-09-lattice-ir-v7-format.md`(v7 = 6 段 + NOD 36B 邻接 + +> EDG 段必落 + ENT 实记录);实施 = `plans/2026-09-09-lattice-ir-v7.md` Task 1-3(2026-09-10 +> 收官)。**v7-only**:loader 拒 version≠7,无 v6 兼容/转换工具(.ccr = 管线中间产物 +> 现生成)。本文件保留作历史基线。v6 时代裁决点/文字在 v7 语境下的结局: +> +> - **原则 6(v5 的 IR 变量槽模型保留)= P1 泄漏源标注**:槽模型下从未定值 var(全局/ +> 参数/0 字面量槽)的 `g_df_var_producer` 播种 = 0 → 幽灵数据边 0→X + 自环 (0,0)(v7 +> 实施计划 Task 0 注 A 实测);v7 以播种修复(全 var 槽恒 -1)落地。语义侧修复(op +> 字典演进)独立后续——v7 载体设计 §2.2:泄漏从「载体结构泄漏」降级为「字典引用 +> 泄漏」(显式遗留标注)。 +> - **§7 开放点 1(NOD 边表是否必须)= 裁决:必落**:v6.0「先省略」路线未发生——v7 +> 以 EDG 段(tag 6,8B {to_nod, kind},邻接式)落地(P3 修复)。 +> - **§4.1 ③ 文字 vs 实现(差异①)**:「区间 = [函数首节点, last_ref+1)」与实现不符—— +> def=-1 条目(参数等)区间 = **[first_ref, last_ref]**(live_start = 首引用指令,非 +> 函数首节点);盘上 live_end 半开 = last_ref+1。**实现语义为准**——v7 同规则(v7 +> 载体设计 §4.1 注记同步)。 +> - **§3.2「局部变量由 ENT 全量携带」未兑现**:v6.0 保守实施实际把局部变量声明并入 +> SYM func 记录(var 声明区)——v7 同此实现形状(字节 spec §3.2),ENT = 区间/版本 +> 视图(非唯一声明面)。 + +> ## 📌 本文档用途(先读这段) +> +> **这份文件回答一个问题:v6 的 `.ccr` 文件在磁盘上长什么样(段布局、每段字段、字节语义)。** +> +> - **字节级最终规格的家 = `docs/ir-schema/coreir-schema.md`**(三形态总 schema:.cir 图 / .csr 规约 / .ccr 线性化——本文档的字节设计是草案,Task 4 开工时以 ccr_io.cr 实现为基、按 schema 文档惯例对齐并入;见计划 Task 6)。 +> - **它是「设计定稿」,不是实现现状,更不是代码文档。** 代码里现在仍是 v5 格式(`ccr_io.cr` 头注释写明 v5 段序列);本文描述的 v6 段(SYM/NOD/ENT/REG)**一行代码都还没写**。读到「v6 有 XXX 段」不等于代码里有——实现状态以实施计划为准。 +> - **它在文档家族中的位置**: +> ``` +> 2026-08-27-lattice-form-ir-design.md ← 方向定稿(为什么升 v6、存在结构是什么) +> ↓ §4.2 的实质化 +> 本文档(v6-format) ← 格式设计(字节怎么排)——就是本文 +> ↓ 实施依据 +> 2026-09-05-lattice-ir-v6.md(计划) ← 怎么实现(任务分解;Task 4 = 格式 IO 落地) +> ``` +> - **与硬件接口表(HIT)的关系**:HIT(`2026-09-05-hardware-interface-table.md`)是**后到**的概念(2026-09-05 同日稍晚定稿),本文档早于它、未含它。衔接点:v6 的 NOD op 是平台无关的 IR 操作,HIT 事件(sub/nand/load/store…)是它的**编码层接口**——「NOD op ↔ HIT event_id」的对齐是实施期事项(本文 §7 开放点 1 同源)。 +> - **代理工作提示**:若你的任务涉及 .ccr 读写/后端发射/存在结构数据,先确认任务的坐标与格式版本——实施计划(`plans/2026-09-05-lattice-ir-v6.md`)里标了「格数据面 Task 1-3 先行(纯内存推导,不落盘)」;格式落盘是 Task 4,**当前代码与测试全部仍是 v5**。不要按本文设计去改 v5 代码,除非你的任务明确是格式 IO。 + +日期:2026-09-05 +状态:~~格式设计定稿(待实现)~~ → **已被 v7 取代(2026-09-10 收官)**——v6 仅以实施中间态存在(2026-09-09 段表 5 段 + NOD 28B + ENT 恒空落地后同日切换 v7,零生态,无 v6 文件存续),直接演进为 v7 真图载体(见文首 ⛔ 状态注记;字节权威 = `2026-09-09-lattice-ir-v7-format.md`)。决策拍板记录(D1 图节点坐标 / D2 corearch 重建投影 / D3 共存推导 sweep)均被 v7 继承。 +关联:`docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(方向定稿,§4.2 本设计为其实质化)、`docs/regalloc-cache-mapping.md`(判定四条)、`docs/memory-model-capability-lattice.md` v4、`docs/ir-schema/coreir-schema.md`(v5 规格,v6 实现时同步)、`src/compiler/cir_cache.cr`(图序列化先例)、`docs/superpowers/plans/2026-09-05-lattice-ir-v6.md`(实施计划——实现状态以它为准)、`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(HIT——后到概念,NOD op ↔ event_id 对齐见 §7)。 + +## 1. 设计原则 + +1. **存在为主体,执行为投影**:v5 文件描述「按什么顺序执行什么」(线性指令数组);v6 描述「什么存在(条目 × 版本)、存在多久(区间)、由什么产生(配方)、嵌套在哪(region)」——执行序是可重建的投影,不再是文件主干。 +2. **图节点坐标**(D1):存在区间端点、region 边界、条目定值点全部以 **NOD id**(图节点序)为坐标——与 `.cir`(图形态)同坐标系,兑现「图 = 唯一真相层」。 +3. **执行序重建**(D2):corearch 从 NOD + REG 重建线性投影。图节点文件序即合法拓扑序(`dataflow.cr:351 lower_to_ccr` 注释:「sequential walk is already a valid topological schedule」)——重建 = 文件序直出 + region 边界标注,无排序成本。 +4. **共存不落盘**(D3):判定消费时从存在区间推导,sweep 算法 O(n log n)(排序)+ O(n)(扫描),不超线性、不冗余存储。 +5. **v6-only**:不兼容 v5、无转换工具(.ccr 为管线中间产物,corec→corearch 现生成,零持久生态——2026-09-05 计划定稿)。 +6. **v5 的 IR 变量槽模型保留**:NOD 的 dest/srcs 语义与 v5 instrs 相同(引用 vars 表变量 id)——corearch 指令发射逻辑最小改动;ENT 提供变量 × 版本的存在视图,经变量 id 与 NOD 关联。 +7. **层定位(2026-09-05 补定,依 `memory-model-capability-lattice.md` v4 §四)**:本文档描述的格式横跨两层,阅读时区分—— + - **格层(范式无关承诺在此)**:存在结构语义——条目(位置+配方+存在)、版本、存在区间、共存关系(对称、无传递性,最弱理论)、判定规则条目泛型。`超图灵属于图不属于格`;格不需要理解范式。 + - **编码层(hw-map 域 = 经典投影实例,无范式无关承诺)**:字节布局——段表/字段宽/变长记录/对齐。字节层事务实现时定(Task 4),仓库先例 `cir_cache.cr` v13(函数级图序列化变长记录)。字节只是「把格编码成经典机器的字节」(备忘 §4.2 第三层定义)。 + +## 2. 总体布局 + +``` +┌ Header(16B)──────────────────────────────┐ +│ magic u32 = "CCR1" │ +│ version u32 = 6 │ +│ seg_count u32 │ +│ reserved u32 = 0 │ +├ 段表(seg_count × 12B)────────────────────┤ +│ {tag: u32, offset: u32, size: u32} │ ← offset 相对文件头;段序自由 +├ 段体(按段表寻址,段序任意)────────────────┤ +│ STR / SYM / NOD / ENT / REG │ +└─────────────────────────────────────────────┘ +``` + +段 tag 常量:`1=STR 2=SYM 3=NOD 4=ENT 5=REG`(`6+` 预留:驱逐标注段 v6.1、证书段)。 +所有 i32/u32 小端;offset/size 为 u32——中间产物 < 4GB(沿用 `ccr_i32_fits` 校验先例)。 + +## 3. 段定义 + +### 3.1 STR — 字符串表(不变) + +`str_count u32` + `str_count × {len: u32, data: len bytes}`。语义同 v5。 + +### 3.2 SYM — 符号表(归并 v5 func_meta/structs/enums/globals) + +| 记录 | 字段 | 说明 | +|---|---|---| +| 函数 | `name→STR, param_count, param_ents[param_count]×u32`(参数 = 条目 id)、`ret_type u32`、`root_region i32`(REG id)、`first_ent/last_ent i32`(本函数条目范围,-1 = 无) | v5 func_meta 的 instr_start/count 由 root_region + NOD 范围取代 | +| 全局 | `name→STR, init_val i64` | 同 v5 globals(条目引用经 ENT flags=全局) | +| 结构 | `name→STR, field_count, fields×{name→STR, type u32}` | 同 v5 | +| 枚举 | `name→STR, variant_count, variants×{name→STR, fields…}` | 同 v5 | + +变量(局部)不入 SYM——由 ENT 全量携带(存在即声明,v5 vars 表并入 ENT 的 var 属性)。 + +### 3.3 NOD — 节点表(图事件 = 配方;v5 instrs 的图坐标化)〔格层语义 + 编码层字节:节点 = 图事件(格层);字段宽 = 编码层投影〕 + +`nod_count u32` + `nod_count × 28B`: + +| 字段 | 类型 | 语义(同 v5 instrs) | +|---|---|---| +| op | i32 | 操作码 | +| dest | i32 | 产出变量 id(-1 = 无产出) | +| src1 | i64 | 源变量/立即数 | +| src2 | i32 | 源/辅助 | +| src3 | i32 | 辅助/宽度 | +| tk | i32 | 类型码 | + ++ `edge_count u32` + `edges × 24B {from_nod u32, to_nod u32, kind u32, reserved u32}`(v6 新增显式边——v5 边不落盘;格式以 `cir_cache.cr` v13 的 DFEdge 32B 记录为基线压缩而来;kind = 数据/state 边,.cir 语义)。 + +坐标约定:**NOD id = 文件序索引 0..nod_count-1**——即图节点坐标(D1),存在区间端点直接引用它。执行重建 = 文件序 + REG 边界。 + +### 3.4 ENT — 条目表(存在结构核心,v6 新增)〔**格层本体**:条目的数学结构(位置+配方+存在)在此;字节宽(28B)是编码层投影,可按编码空间调整〕 + +`ent_count u32` + `ent_count × 28B`: + +| 字段 | 类型 | 语义 | +|---|---|---| +| var_id | i32 | 变量 id(与 NOD dest/srcs 同命名空间;-1 = 匿名常量条目) | +| version | u32 | 该变量第几版(1-based;每次定值 +1) | +| def_nod | i32 | 定值节点 id(-1 = 函数参数/全局) | +| live_start | u32 | 存在区间起点(NOD id,含定值) | +| live_end | u32 | 存在区间终点(NOD id,**开区间**:最后使用点 +1) | +| home | i32 | 槽位(分配器回填;-1 = 未分配/寄存器候选) | +| flags | u32 | bit0 无配方(图内不可重算,条款 4b → 必须有 home);bit1 参数;bit2 全局;bit3 驱逐候选(v6.1 用) | + +**版本化语义**:Core IR 非 SSA(变量多定值)——变量每次定值切分一个新版本条目(定值点收集规则 = §4.1:dest ≥ 0 的 NOD;`IR_STORE` 的定值目标在 s1——ρ(s1):=ρ(s2),dest 恒 -1;`IR_STORE_INDEX_VAR`/`IR_STORE_PTR`/`IR_DYN_DISPATCH` 的 dest ≥ 0 非定值——值为源/元数据/占位,GC 批 2 勘定);相邻版本的存在区间按定值点切割(版本 k 区间终点 = 版本 k+1 定值点)。 +**匿名常量条目**(var_id = -1):仅当常量被判定消费(如跨调用存活)时物化,否则常量内联于 NOD src1——避免条目爆炸。 + +### 3.5 REG — region 表(SG 嵌套,坐标升级)〔格层语义 + 编码层字节〕 + +`reg_count u32` + `reg_count × 24B`: + +| 字段 | 类型 | 语义 | +|---|---|---| +| kind | u32 | SG_IF/LOOP/FOR/FLOW/UNSAFE/FUNC | +| parent | i32 | 父 region id(-1 = 根) | +| enter_nod | u32 | 入口节点 id(v5 enter 指令号的坐标升级) | +| exit_nod | u32 | 出口节点 id | +| first_ent | i32 | 区内首条目(-1 = 无) | +| last_ent | i32 | 区内末条目 | + +## 4. 推导与判定(消费侧,不落盘)〔**纯格层**:区间/共存的数学推导,与字节编码无关〕 + +### 4.1 条目重建(corec 产生 v6 时) + +输入:图(NOD + 边 + REG)。步骤: +1. 定值点收集:扫 NOD,dest ≥ 0 的节点 = 该变量的定值点 → 版本切分(def_nod、version) + ——两处 carve-out(规则全文与出处 = §3.4「版本化语义」,GC 批 2 勘定): + ① IR_STORE 单列:定值目标在 s1(ρ(s1):=ρ(s2),dest 恒 -1)——不走 dest 列; + ② 三 op 排除:STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 的 dest ≥ 0 **非**定值 + (值 = 被存值源/基址标注/占位)——不产版本。①/② 之外 dest ≥ 0 方为定值点 +2. 活区间:图坐标扫描(同 `alloc_registers` 的 [first_ref, last_ref] 逻辑,坐标单位 = NOD id)——版本 k 区间 = [def_k, def_{k+1}) 与 [def_k, last_ref+1) 的截断 +3. 参数/全局条目:def_nod = -1,区间 = [函数首节点, last_ref+1) + +### 4.2 共存判定(判定消费时现算,O(n log n) 不超线性——D3) + +`entries_coexist` 不逐对枚举。算法(sweep): +1. 按 live_start 排序条目(O(n log n),自举编译器内自带排序——归并或插入,函数级 n ≤ 数千) +2. 单遍扫描:维护**活跃条目集**(start < 当前点 ≤ end 的条目);新条目 start 进入时,与活跃集中 end > start 的条目即共存——用按 end 排序的活跃集弹出(end ≤ start 的移出) +3. 判定四条消费此关系(如共存互斥:分配到同 home 的条目对是否相交——同 home 组内做 2 的扫描,O(k log k) per 组) + +无需输出全量共存表——判定四条都是「存在性/∀ 检查」,sweep 给出相交证据即止。 + +### 4.3 投影重建(corearch,D2) + +corearch 读 v6:NOD 文件序 = 拓扑序(图创建序合法,先例注释)→ 线性指令流 = NOD 直出 + REG 边界插入(region 开始/结束标注,SG 段语义同 v5 消费方式)→ 现有 ELF 发射逻辑几乎不变(读段改走段表 + NOD 字段与 v5 instrs 同布局)。 + +## 5. 与 v5 差异总表 + +| 面 | v5 | v6 | +|---|---|---| +| version | 5 | 6(magic/扩展名/CLI 不动——方案 A) | +| 组织 | 定序段 | Header + 段表(tag/offset/size) | +| 指令 | instrs 28B 线性数组(主干) | NOD 28B 图事件 + 显式边表(与 ENT 并列,非唯一主干) | +| 坐标 | 指令序 | NOD id(图坐标,D1) | +| 变量 | vars 表 | ENT(变量 × 版本,含区间/home/flags) | +| region | sgs 24B(enter/exit 指令号) | REG 24B(enter/exit NOD id + 区内条目范围) | +| 共存 | 无 | 不落盘,sweep 推导(D3) | +| 执行序 | 文件即序 | corearch 从 NOD+REG 重建(文件序即拓扑序,D2) | +| 兼容 | — | v6-only,无转换工具 | + +## 6. 消费方影响 + +- **corec(前端)**:lower 阶段产 NOD+REG(现 lower_to_ccr 坐标化)+ 条目重建(§4.1)→ save v6 +- **corearch(后端)**:段表寻址 → 投影重建(§4.3)→ 发射;判定(分配器/一致性自检)消费 ENT(§4.2) +- **判定/证书**:共存/驱逐/证书是判定产物,独立于 .ccr(格形态之上),不落本文件 +- **宽度标签**:不进入 v6(width-out-of-language;v5 格式本就无宽度字段,NOD tk 为类型码不变) + +## 7. 开放点(实现期定夺) + +1. NOD 边表是否必须(corearch 若只用文件序 + REG 即可发射,边仅供判定/未来 pass 消费——v6.0 可先省略,v6.1 按需引入;cir_cache 有完整先例)——**编码层问题,实现时定** +2. 变长记录编码(SYM 函数 param_ents 等)/ 对齐 / 校验规则——**编码层问题,Task 4 开工时定**(参照 cir_cache v13 先例;格层语义不依赖这些细节) +3. ENT home 由谁回填:分配器(corearch 内)原地改写 .ccr 还是内存态回填后另存——若 .ccr 只作 corec→corearch 传输,home 字段可为 -1 直通(判定在 corearch 内存态跑) +4. 段表预留 tag 6+(驱逐标注/证书段)不占空间,仅常量定义 + +## 8. 关联 + +- `docs/superpowers/plans/2026-09-05-lattice-ir-v6.md` — 实施计划(Task 4/5 依本设计) +- `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md` — 方向定稿(§4.2 的实质化) +- `docs/ir-schema/coreir-schema.md` — v5 schema(实现期更新为 v6) +- `docs/regalloc-cache-mapping.md` §四 — 判定四条(共存互斥/读点无陈旧/驱逐配对/调用失效) diff --git a/docs/superpowers/specs/2026-09-06-core-maintainer-docs-reorg-design.md b/docs/superpowers/specs/2026-09-06-core-maintainer-docs-reorg-design.md new file mode 100644 index 00000000..df25f82b --- /dev/null +++ b/docs/superpowers/specs/2026-09-06-core-maintainer-docs-reorg-design.md @@ -0,0 +1,149 @@ +# 核心维护文档重组与首批重写设计(2026-09-06) + +状态:已获用户逐节批准(2026-09-06)。本文档为设计定稿,执行按 writing-plans 拆分。 + +## 一、背景与问题 + +`docs/` 当前是平铺的单目录,156 份 md ≈ 5.6 万行,包含至少 6 种不同性质的内容: + +1. 用户向文档(语法、学习路径、错误码、编辑器配置) +2. 定稿的架构参考(执行模型、内存模型、指针模型、术语表) +3. 未定稿/已实现的特性设计稿(泛型、FFI、并发、惰性求值、概率编程…) +4. 任务产物(CompCert 对照审查、数值类型迁移盘点) +5. 伪代码 TDD 交付物(73 份 ≈ 3.3 万行,自成一体) +6. 工具链约定目录(superpowers/plans、specs) + +同一目录混放多种性质文档 → 无导航、状态不明(active/superseded/deprecated 靠各文件自行散落标注)、新维护者无从下手。 + +## 二、目标形态:分类树 + +按「主题 × 读者 × 状态」三维分类。目录树: + +``` +docs/ +├── README.md 【新建】导航索引 + 分类规则说明(每文档唯一职责) +├── language/ 【用户向】不依赖编译器知识 +│ ├── syntax.md ← language-syntax.md +│ ├── learning-path.md ← learning-path.md +│ ├── error-codes.md ← error-codes.md +│ └── editor-setup.md ← editor-setup.md(用户配置部分) +├── design/ 【维护者向·定稿参考】 +│ ├── project-book.md ← project-book.md(重写拆职责,见 §六) +│ ├── execution-model.md ← execution-model.md +│ ├── dataflow-design.md ← dataflow-design.md(头注 superseded by execution-model) +│ ├── memory-model.md ← memory-model.md +│ ├── pointer-model.md ← pointer-model.md +│ ├── regalloc-cache-mapping.md / ir-op-semantics.md / glossary.md(校准,见 §六) +│ ├── spec-design.md / verifier-kernel.md / at-intrinsics.md +│ ├── corelsp.md ← editor-setup.md 拆分出的 corelsp 架构/维护部分 +│ └── crasm.md ← crasm.md(废弃收尾,见 §六) +├── proposals/ 【讨论中】特性设计 +│ ├── generics.md / comptime.md / ffi.md / concurrency.md +│ └── dynamic-typing.md / lazy.md / distributed.md / probabilistic.md +│ (已实现的特性标 active 并指向 design/ 或源码;未实现的保持 proposal) +├── archive/ 【任务产物】 +│ ├── compcert-reference.md / compcert-round4-findings.md +│ └── numeric-migration-inventory.md +├── maintainer/ 【新建】维护者操作手册 +│ ├── onboarding.md 【新建】仓库结构导览 + 构建管线 + 分支模型操作 +│ └── testing.md 【新建】测试/验证操作手册 +├── adr/ 【新建】决策记录(编号 0001 起) +├── z-vision.md 保留顶层(愿景入口,README.md 链接之) +├── pseudocode/ superpowers/ 不动 +└── coq/ ir-schema/ verifier/ 原样,头部定位声明对齐 +``` + +**规则(每份文档强制):** + +1. 头部加定位声明块:受众(users/maintainers/contributors)+ 状态(active/superseded/deprecated/archive/proposal)+ 真源声明(源码/grammar/本文件) +2. 一份文档一个职责;状态与内容冲突时更新文档,不添加免责段 +3. 分类树 + 定位声明双轨,README.md 为唯一导航索引 + +## 三、归档处置 + +- memory-model-capability-lattice.md → archive/(v1-v4 讨论备忘,内容演进并入 memory-model.md) +- crasm.md → 压缩为一页说明 + ADR 链接(见 §六) +- compcert-reference.md / compcert-round4-findings.md / numeric-migration-inventory.md → archive/ +- 伪代码/任务产物不做内容清理,只移动与标注 + +## 四、新增文档:maintainer/onboarding.md + +面向新维护者(含第二维护者分工),内容: + +1. 仓库结构导览(src/compiler 模块地图、src/arch、src/stdlib、src/runtime) +2. 构建管线:三级自举(Stage 0/1/2)怎么跑、产物是什么 +3. 分支模型:feature → develop → main,jj 命令速查(create/move/push) +4. 已知阻塞项与雷区(corec2 tokenizer 全局变量问题等,同步自 TODO.md) +5. 贡献清单检查列表 + +## 五、新增文档:maintainer/testing.md + +1. 三套测试的定位与跑法(tests/bootstrap、tests/selfhost、tests/suite) +2. 每套测试怎么改/怎么加用例(内联 Core 源码字符串约定) +3. 回归验证流程(自举三阶段 + 字节一致性验证) +4. 常见失败模式与定位手段 + +## 六、存量重写范本(首批三份,覆盖三种典型工作流) + +### 6.1 project-book.md — 拆分示范 + +拆成职责单一的多份: + +- 愿景/哲学部分 → 已在 z-vision.md 的归 z-vision.md 管,重复段删(project-book 保留"项目定位"核心) +- IR 系统/验证架构细节 → 已有 spec-design.md、verifier-kernel.md、ir-op-semantics.md,交叉引用而非重复 +- 重写后体积目标 < 150 行,成为导航性质文档 + +### 6.2 glossary.md — 校准示范 + +- 术语条目与源码逐一对照(lexer.cr 关键字唯一真源等既有惯例) +- 头注定位声明:受众 maintainers + 状态 active +- 失效条目删除或标 archive,不保留"冲突时以源码为准"的免责式堆叠 + +### 6.3 crasm.md — 废弃收尾示范 + +- 压缩成 ≤40 行:是什么、为什么废弃(2026-09-05)、替代方案 +- 新 ADR 记录决策,正文链接 ADR + +## 七、新增文档:ADR(首批 4 份起) + +目录 `docs/adr/`,模板: + +```markdown +# ADR-000N: 标题 +日期/状态(accepted/superseded)/决策者 +## 背景 +## 决策 +## 后果(正面/负面) +## 关联 +``` + +首批选题(从近两月 git 历史决策): + +1. ADR-0001: .corespec/.crasm 退役——规约并入 .cr 语法(2026-09-05/06) +2. ADR-0002: ELF 直出 vs .s 汇编产物(2026-08 自举贯通) +3. ADR-0003: .ccr v6 段表架构 + ENT 存在结构段(2026-09-05) +4. ADR-0004: 自举目标锁定 x86-64 ELF(或执行时按历史补选) + +原则:先写最近且决策依据仍可追溯的;每个新决策落地时续号;ADR 不重复 design/ 细节,只记"为什么"。 + +## 八、执行顺序 + +1. **建目录骨架** + docs/README.md(索引先占位,逐步回填链接) +2. **存量归位迁移**(按 §二/三 清单移动文件,同步更新文件内互链路径) +3. **头部定位声明**逐份补齐(迁移时顺手做) +4. **重写范本三份**(§六),立项标、立规范 +5. **maintainer/ 两份手册**新建(§四/五) +6. **ADR-0001~0004** 新建(§七) + +## 九、验证方式 + +- `grep` 检查无残留失效链接(文档间互链路径更新完整) +- 仓库级 md 链接检查脚本(如有)或手工抽查 +- README.md 索引与目录树一致 +- 新维护者视角走查:onboarding 读完后能否独立跑通构建/测试 + +## 十、范围外(后续子项目) + +- 开发者文档重写(面向外部,将来进文档网站)——另立项 +- 文档网站基建(Astro Starlight 等)——另立项,内容成熟后再动 +- docs/pseudocode 73 份内容审查——不在本次 diff --git a/docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md b/docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md new file mode 100644 index 00000000..1935d832 --- /dev/null +++ b/docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md @@ -0,0 +1,99 @@ +# int 多字表示设计(后端表示策略 B:64 位快路径 + 溢出扩展) + +日期:2026-09-06 +状态:**设计定稿;M1 已实现完成(2026-09-07,实施 plan `plans/2026-09-06-int-multiword-m1.md` 六任务收官)**——add/sub 溢出全链:快路径 jo 检测 + 固定 2-limb(128 位)多字 + tagged 槽(快值/堆对象指针 + 帧 tag)+ tag 卫生 + 128 位混合算术/比较 + return 截断语义。M2 挂账清单见 §8。 +关联:`specs/2026-09-06-int-unbounded-semantics.md`(语义层定稿——int = 数学整数无溢出概念,本设计是其编码层投影)、width-out-of-language 定案(编码层宽度目标自动决定)、hw-map(表示策略 = 表投影条目域)。 + +## 1. 定位 + +- **语义层零改动**:前端/图/格照旧——int 算术是数学整数(IR 无溢出概念、无宽度) +- **表示 = 后端编码层事务**:corearch 对 int 值选择投影——快路径 64 位机器字直算(零开销)+ 溢出时自动提升多字(罕见路径付费) +- 与 dex 精确两线不混(dex = 定点精确小数,另线) + +## 2. 值表示(决策点 D1:槽内形态) + +现状:int 变量 = 64 位栈槽/寄存器(IR 槽模型 + 分配负编码)。 + +候选: +- **D1a 旁路标记**:槽仍 64 位存「快值或指向多字对象的指针」+ 每 int 槽伴生 1 位 tag(在栈上按槽扩展位图/或类型区分的并行 tag 槽)——int 槽宽变 65 位等价(栈布局扩展) +- **D1b 统一指针化**:int 值一律 = tagged 指针(小整数 = 指针内嵌 tag 如低位 1 标记;大数 = 堆多字对象)——全部 int 操作走解引用(快路径损失大——违背 B 初衷) +- **D1c 双态槽 + 静态判定**:编译期区间证明不溢出的变量 = 纯 64 槽(免 tag);证明不了的 = tagged 槽(D1a 形态)——静态分层(与图推导衔接) + +倾向 **D1a/D1c**(快路径真零开销;tag 只付给推导不出的值)。**D1c 是 D1a 的子集优化**——先 D1a 全 tagged-able 槽(实现简单),静态免 tag 优化后置。 + +## 3. 算术指令(决策点 D2:溢出检测位置) + +快路径算术须能察觉「结果超 64 位」: +- **D2a IR 层加溢出感知**:新 IR 操作(ADD_OVF 等)或算术指令带标志——语义层加东西(违背「语义零改动」精神?——溢出检测是编码层行为,IR 若承载=格层污染) +- **D2b 后端纯发射**:corearch 对 IR 算术(64 位投影时)发射「op + 溢出跳转」;溢出 → 慢路径(运行时多字例程)——IR/图零改动,纯后端 +- **D2c 检查插入 pass**:后端前 pass 按区间分析插检查指令(静态可证免插) + +倾向 **D2b + D2c 渐进**(纯后端发射溢出检测;静态免检由区间 pass 后置)。 + +## 4. 多字运行时(决策点 D3:扩展表示) + +溢出后的多字形态: +- 长度前缀数组(小端 limb 数组,符号位处理)——经典 bignum +- 运算原语在运行时(rt.s/rt.cr)还是 stdlib?——编译器后端慢路径调运行时函数 +- 溢出链:add 溢出 → 分配多字对象(arena)→ 存 [原值低位, 进位扩展]——多字 add 再溢出?多字加法本身可再溢出(limb 进位链)——多字对象动态增长 + +范围控制建议:**M1 = 单扩展字(128 位表示 = 2×64 limb 固定)**——一次溢出到 128 位足够覆盖绝大多数「差点溢出」场景;超过 128 位再报错(编码层限制抬到 128)或动态增长(后置)。固定 2-limb = 简单正确(无动态分配连锁)。 + +## 5. 受波及面(后端/编码层) + +- 槽模型:tagged 槽(快值 64 或 2-limb 指针) +- 算术发射:add/sub/mul/div 溢出检测 + 慢路径 +- 比较/转换/打印:int_str 等须处理多字(或保证打印前值 ≤64?——不:无上限语义下大值打印合法) +- 字面量:超 64 编译期常量 → rodata 2-limb 对象 + 引用(前端 lexer 守卫:不再拒?——字面量超 64 进入 IR 常量(多字表示)——IR_CONST s1 是 i64 槽装不下 128——需 IR 常量扩展(决策点 D4)或后端从字面量串重建) +- **return 出逃边界(M1 定案)**:M1 内 return-2L 到内部调用方 = 确定性截断(低 64 wrap——return 解指针取低 limb = 数学值低 64 位,非指针垃圾;调用方以 64 位快值接回,跨函数无 2-limb 协议);全 128 return(调用方 tag 读 + 2-limb 对象传递)= M2。main → OS exit 同机制(exit 低 8 位 = 数学值低字节——M1 验证通道;全 128 断言经测试钩子 syscall3 直读 16B) +- 验证:溢出路径行为(快路径结果 = 数学结果 64 内一致;慢路径 = 扩展精确) + +## 6. 决策点清单(拍板后定稿) + +1. **D1**:槽内形态(倾向 D1a tagged 槽,D1c 静态免 tag 后置) +2. **D2**:溢出检测位置(倾向 D2b 纯后端 + D2c 区间免检后置) +3. **D3**:扩展表示(倾向固定 2-limb 128 位 M1——超 128 报编码层错或动态后置) +4. **D4**:超 64 编译期字面量路径(IR_CONST 扩展 vs 后端从词素重建) +5. **范围分期**:先 add/sub(最常用溢出场景)全链(发射/槽/慢路径/打印/测试)→ mul/div/比较后续 +6. **D6(M1 定案,Task 5,2026-09-07)**:tagged 变量**不排除**寄存器分配——plan 原案「多字变量只栈」的动机(多字值不可驻单寄存器)被 D1a 表示消解:槽/寄存器驻留的是快值或 2-limb 堆对象指针(128 位载荷在堆),tag 为帧字节,与值形态无关;值读写单 seam g2_slot、tag 单 seam g2_tag_off(含慢路径回存/2L 块装载/return 解指针),reg 形态状态完整。O2 实证:tagged 变量(含运行时 2L 态:循环携带/参数/双对象比较)被 CAG 分配寄存器且行为全绿(tests/selfhost/test_mw_task5.py 元断言 tagged∩REG_ASSIGN≠∅ + 16B 通道)。排除零收益有代价(tagged = 最常见 add 循环行,O2 性能)。若将来引入 reg 形态不完整机制(动态 spill/驱逐)再议。 + +## 7. 验证 + +- 快路径行为不变(64 内程序逐字节/exit 一致——全套回归) +- 慢路径:构造溢出程序(大常量乘法/循环累加)断言数学正确 +- tagged 槽正确性:快/慢值混用(推导不出变量 vs 推导出)交叉 +- 判定/分配器适配:tagged 槽的寄存器分配(多字值不可驻单寄存器——分配器须知——决策点 D6:多字变量不进寄存器只栈?——**已定案:不排除**,见 §6 条目 6 与 plan Task 5 执行记录) + +## 8. M1 完成范围 + M2 挂账(2026-09-07 收官记录) + +**M1 已实现(测试:`tests/selfhost/test_mw_task{1..6}.py`,回归全套绿)**: +- tagged 槽框架(不动点闭包识别 A/B/B′ + 帧 tag 区——无 tag 程序帧公式逐字节不变) +- 快路径 jo(rel32)→ 每站点函数尾慢路径块:128 位真值修正(add hi=CF?−1:0 / + sub hi=CF?0:−1)+ alloc(16) 2-limb 对象 + tag 置位 +- tag 卫生四类定值点(快路径清 0 / STORE+LOAD 运行时拷贝 / 参数 prologue 清 0 / + 慢路径置 1) +- 消费者:return 解指针(低 64 截断语义)、128 位比较(高 limb 带符号 / 低 limb + 无符号)、2L 再参与 add/sub(混合进位链 + fits 降级——d1/d2 判别在案) +- O0/O1/O2 全绿含 O2 reg 形态(D6 定案:不排除,§6 条目 6) +- 边界套件(i64max±1 链 / −2^63 快值邻域 / 上下边界比较真值表 / 循环携带) + +**M2 挂账**(超出 M1 范围 = 计划线外,逐一披露): +1. **mul/div 溢出链与 2L 参与**(M1 只 add/sub);UOP_NEG 的 −2^63 环绕链 +2. **2L 值打印**(int_str 超 64 正确输出——现 exit/16B 通道只验证数值层) +3. **D4 超 64 编译期常量**:IR_CONST 64 位槽 = 格层表示,扩展需 v6 常量段 + (与 dex 精度同族待 v6)——现 lexer 编码层错误消息保留(int-unbounded 归位) +4. **超 128 码域动态增长**(现报编码层限制错;理论需 ~2^63 步链才触界) +5. **D1c 静态区间证明免 tag**(保守 tagged 集代价 = 每变量行 1 字节 tag + 每站点 + jo 6B + 函数尾块 ~45-160B 冷区;精化后免) +6. **return 全 128 内部传递**(现 M1 语义 = 确定性截断低 64 wrap,§5 出逃边界句; + 调用方 tag 读 + 2L 对象传递 = M2);出逃写 / 跨函数 2L(全局/堆/数组/调用实参 + 返回值——测试语料规避中) +7. **表(HIT)模式 × 运行时 2L**:tagged add/sub/比较整条落旧路径(表 preflight + 拒 reg 形态)——语义正确,表模式下含运行时 2L 态的溢出程序组合未验证 +8. **arena 生命周期债**:含分配作用域 + 慢路径 + 2L 跨 arena_reset = 悬挂 + (正确方向:2L 对象永久 bump 或跨 reset 复制;现无 arena 作用域 = 全局 bump + 进程生命期,测试全在此形态) +9. **共享块/模板压缩**:jo/2L 块每站点独立(~45-160B/站)——冷区线性增长的 + 后续优化,非正确性项 +10. **零 diff 基线 CI 化**(过程债非正确性——快路径零变化防漂移载体现为本地手工基线;Task 2 评审指派 Task 6 补账) +11. **hi∈{0,−1} 不变量放宽**:2L 链可达 hi=−2/1(−2^64−2 实证 d1)——实现者勿假设 hi 限 {0,−1}(test_mw_task4 d 系列判别在案) diff --git a/docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md b/docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md new file mode 100644 index 00000000..80465c09 --- /dev/null +++ b/docs/superpowers/specs/2026-09-06-int-unbounded-semantics.md @@ -0,0 +1,94 @@ +# int 无上限语义定稿:数学整数公理化接口 + 编码层投影(int-unbounded-semantics) + +日期:2026-09-06 +状态:**定稿**(落实 width-out-of-language / type-system-direction 的 int 语义条目;语义与验证部分立即可用,编码层事务为方向性陈述) +关联定案: +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(图本体 + 接口统一;原生接口条目;纯数学叙事 = CompCert 精神推广到原语层) +- TODO `width-out-of-language`(2026-08-30 定案:int 无上限为默认;宽度移出语言;编码层宽度由目标自动决定,hw-map 为未来显式控制通道;三层映射对应 = 图/格 = 纯数学,编码 = hw-map 领域) +- TODO `bounds-inference`(2026-09-05 挂账:图推导边界 pass——静态证明免检的图 pass 同族) +- `docs/memory-model-capability-lattice.md` §四(三层映射:图 → 格 → 编码) +- `docs/spec-design.md` §9.4(整数语义:机器整数 vs 数学整数——int 已决为无界数学整数,翻译桥无分歧)+ §9.x 免检先例 +- `docs/superpowers/specs/2026-08-16-numeric-types-design.md`(2026-08-23 修订:int = 无上限数学整数)+ `docs/superpowers/specs/2026-08-23-hw-map-design.md`(hw-map = 语义表 + 投影表) +- TODO `dex-precision`(dex 任意精度精确小数挂账) + +> 一句话:**int 在语义层 = 数学整数的公理化接口(无溢出概念);「64 位」「i64 上限」「环绕」全部是编码层(hw-map 域)的经典投影实例形状——溢出不是语义事件,溢出错也不是语义错误。** + +## 一、语义层定稿:int = 数学整数公理化接口 + +1. **int 是数学整数(Z)的公理化接口**,不是任何位宽机器整数。语义承诺: + - 加法/乘法满足结合律、交换律、分配律;序为全序;无最大/最小值(无界双向); + - **无溢出概念**——`x + y > x`、`x * 2 / 2 == x` 之类性质在语义层无条件成立,与在规约层(CIC 整数 Z)证明的结论一致(spec-design §9.4:翻译桥在 int 上无分歧); + - 溢出**不是语义事件**:没有「值越出 int 域」的状态、错误或行为——int 域无边界,值只会超出某个**投影**的承载形状。 +2. **接口地位**(type-system-direction 定案 #2):int = 原生接口条目——规则内建在编译器、公理引用规约层、用户不可实现(公理是编译器领地,防伪造证明)。语义保鲜:图节点标注 + 规则解释,int 的性质是图上的契约不是代码约定。 +3. **纯数学叙事**(type-system-direction §二):int 无上限、dex 精确 = 公理化接口 + 原生实现——原语语义也可对照验证。本文档是 int 这条目的语义定稿。 + +## 二、编码分层(hw-map 域):经典 64 位 = 快路径投影 + +1. **语义层无宽度**:宽度(i64/u32/w32…)不进入语言类型/标签体系(width-out-of-language——单标签单范式;apx 保持单一语义 = 精度降级开关,不扩张为宽度标签)。 +2. **经典 64 位机器字 = 快路径投影实例**:今天全部实现(自持编译器内部表示、ELF 后端、解释器)都是「把数学整数投影到经典 64 位机器字」的一份实例;同语义可有其他投影(任意精度载体、多字、扩展表示、未来范式)。 +3. **超界值 = 编码层事务**(方向性承诺,落在图推导 pass 家族,见 §六挂账): + - **静态区间证明免检**:图上的区间传播/符号证明(与 TODO `bounds-inference` 图推导边界 pass 同族——下标/长度证明的推广形态)证出「此值恒在该投影形状内」→ 不发射检查/不升级,**证明成功零成本**; + - **证明不了才检查/提升**:区间证不出 → 运行时检查(保形状内)或提升表示(换更大形状)——检查/提升是编码层兜底,不是语义事件; + - 表示策略(扩展/多字/BigInt、何时提升、提升形态)= **hw-map/远期事项,非本语义层承诺**。 +4. **三层映射对应**:图/格 = 纯数学(int 语义在图/格层无界,值不携带宽度);编码 = hw-map 领域(64 位形状、环绕、格式字段宽都是编码层实例参数)。 +5. **与数值类型设计(2026-08-16/08-23)的措辞关系**:该设计说 int 实现 = 「i64 快路径,装不下时自动升级为堆上 BigInt,无溢出错误」——其语义意图(永不报「溢出错误」、数学语义)本定稿继承;其**表示策略措辞**(具体升级时机/形态)按 width-out-of-language 归编码层/hw-map 域,非语义层承诺。语义(无上限)是已决项,表示(怎么装得下)是工程项——两者分属两层,不在本文档混淆。 + +## 三、错误分类定稿:溢出类错误 = 编码层限制错误 + +**分类原则**:错误发生在哪一层,就归哪一层。「整数太大」不是语义错误——语义层 int 无上限、无溢出概念;它是**投影到经典 64 位目标时的形状限制**(该值在当前经典投影中无载体)。现有溢出相关错误路径逐一归位: + +| 错误路径 | 位置 | 归类 | +|---|---|---| +| lexer 字面量守卫「integer literal overflow」(十进制 > i64max 及各进制的 base 校准阈值) | `src/compiler/lexer.cr` `str_int_literal` / `bootstrap/corec/frontend/lexer.py`(前缀进制守卫) | **编码层限制错误**——编译器内部表示 = 经典 64 位机器字(快路径投影),字面量超形状 = 投影限制拒绝;非语义错误(语义层可表示任意整数) | +| dex 字面量整数部分过宽「decimal literal integer part is too wide」 | `src/compiler/lexer.cr` `str_to_scaled`(定点 S=10⁶,整数部分 ≤ 9.2e12) | dex 表示层现状限制(定点方案形状;见 TODO `dex-precision`)——同族但属 dex 线,不并入 int 线 | +| CCR i32 字段校验拒写(`ccr_i32_fits` / `ccr_validate_i32_fields`:IR 指令索引、region 字段超 i32 不落盘) | `src/compiler/ccr_io.cr` `save_ccr` | **编码层文件格式限制**——v6 段表格式字段宽 i32(文件布局形状),本就归编码层(hw-map 域 = 经典投影实例);与 int 语义无涉,仅在此归位 | +| 死错误码条目 `EC_P_LIT_OVERFLOW`(P019)、`EC_R_OVERFLOW`(R003) | `src/compiler/ast.cr`(定义无发射点) | 挂账注记——若将来启用 R003,其语义必须是「经典投影运行时机器字环绕事件」,**不是语义溢出**(语义层无此事件);勿在启用时把语义层写成可溢出 | + +**调整方式与保守边界**:以上是分类(语义层无此概念 → 编码层限制),不是行为变更——字面量拒绝、CCR 拒写照旧(当前编码只有经典 64 位一份投影,拒绝是形状事实)。错误文本/诊断码体系不动(见 §七注记)。 + +## 四、与 dex 精确/任意精度:两线不混 + +1. **int 无上限 = 语义层事实**(公理化接口,表示延后,§一/§二);**dex 精确 = 表示层工程**(精确小数需要表示层实现才成立)。两条线不混: + - int 的「无上限」在图/格层已封闭——语义不依赖任何表示工程;「装不下」永远只是投影问题(§二 3); + - dex 的「精确」依赖表示层现状:定点 S=10⁶(`src/stdlib/dex.cr`)——6 位小数 + int64 硬上限(加减 ≤ 9.2e12、乘 |a|·|b| ≤ 9.2e6),设计意图与实现为语义级偏差(已记账 TODO `dex-precision`:修复方向 = 动态位数任意精度,重活排期)。 +2. **不混的含义**:int 的无上限**不是**「表示层做任意精度工程」的承诺——int 语义不欠表示债(债在编码层投影与 hw-map 策略);dex 的精确是表示层债(表示达不到则语义达不到)。int 不因 dex 挂账而等待;dex 不因 int 无上限而自动精确。 +3. **apx** 保持单一语义 = 精度降级开关(机器语义授权,CPU 后端兑现 binary64),只作用于 dex 类表示层选择,**不成为 int 的宽度标签**(width-out-of-language 明示)。 + +## 五、与验证衔接:int 公理可被规约对照验证(CompCert 精神) + +1. **规约直映**:int 公理可被 .cr requires/ensures(.corespec 规约层语法)表达——`requires x >= 0`、`ensures result * result >= 0` 等数学整数性质直接成立;int → CIC Z 翻译桥无分歧(spec-design §9.4),规约证明与图语义同一整数域,**不需要位向量/溢出模式建模**。 +2. **原语语义对照验证**:int 公理化接口 = 原语层的「语义模型」——编译器(及投影)的正确性可与公理接口对照验证,CompCert 精神从「后端对 C 语义」推广到「原语对数学语义」(type-system-direction §二 纯数学叙事)。这是后续验证切片的证明义务来源:不是证明「int64 不溢出」(经典投影下可证伪),而是证明「编码层投影/拒绝行为与无界语义一致」(形状内 = 值相符;形状外 = 拒绝或显式提升,永不静默错值)。 +3. **免检先例同族**:静态区间证明免检(§二 3)= spec-design 已有「编译期证明免检的规约版」先例(DEREF 运行时边界检查在证明后直接消除)与 checker R002 编译期越界拒绝的推广形态——三态(证明恒安全 → 免检零成本 / 证明恒越界 → 编译期拒绝 / 证明不了 → 保留运行时检查并带图推导必要性标注)与 TODO `bounds-inference` 同构。 + +## 六、现状盘点与挂账 + +- **现状(均为经典 64 位投影实例行为,语义定稿后不变)**: + - int 运算:int64 机器字环绕、无陷阱(dex.cr 注「越界环绕——无陷阱,与 int 溢出一致」为同一投影事实); + - 字面量入口:超 i64 形状 = lexer 拒绝(§三); + - .ccr 落盘:超 i32 格式字段 = 拒写(§三); + - 解释器/Python bootstrap 内部:Python int 任意精度——同一语义的另一份投影实例(两编译器「分歧」在此叙事下不是语义分歧,是各自投影形状不同;`1_000` 分歧复核仍按 width-out-of-language 语言侧清理推进)。 +- **挂账**(本语义定稿派生,均非语义层承诺): + 1. 图推导区间证明 pass(bounds-inference 同族推广:值区间传播 → 免检/拒绝/保留检查三态,§二 3); + 2. hw-map 表示策略(扩展/多字/提升形态——远期)——**M1 实例已落地(2026-09-07)**:add/sub 溢出 2-limb(128 位固定)多字投影完成(64 快路径 + jo 检测 + tagged 槽),见 `specs/2026-09-06-int-multiword-backend-design.md`(§8 M2 挂账:mul/div、打印、超 64 常量、动态增长、静态免 tag 等仍挂); + 3. lexer 溢出守卫消息文本与诊断码体系(§七注记); + 4. R003/P019 死错误码条目启用时的语义边界(§三末行)。 + +## 七、注记:错误文本维持方案(任务 B 落实结论) + +**现状评估**:lexer 溢出消息无诊断码(`print_parse_errors` 直打 `error: `,diag.cr);CCR 拒写路径返回 -1 无消息;两处均无测试断言消息文本(唯一断言的「too wide」属 dex 线消息,不动)。 + +**决策:注释归位 + 文档归位,错误文本与错误码体系不动**,理由: +1. 分类调整的消费方是「人读的语义框架」——文档 + 守卫注释已完整承载;行为无需变(当前只有经典 64 位一份投影,拒绝是形状事实); +2. 自持编译器消息改动需全量重建(`build_selfhost_native.py`,铁律 CPU ≤ 10% 下耗时甚巨)且并行会话共享工作副本/build 目录,重建互相污染;价值 ≈ 0(消息仅超界字面量用户可见); +3. 文本若要与「编码层」语义对齐,应随 lexer 诊断错误码体系建立一并做(P019 死条目在 ast.cr 等待归位),避免同一文本两轮演化。 +**未来若动**:消息宜改为「超出经典 64 位编码宽度」类表述(语言无上限、经典投影受限),或在诊断前缀区分层类别;错误码启用时 R003 语义按 §三末行边界。 + +## 八、引用锚点汇总 + +- type-system-direction(TODO:234 + `specs/2026-08-30-type-system-direction-design.md`):原生接口条目 #2、纯数学叙事 §二、14 条定案 +- width-out-of-language(TODO:233):int 无上限已定、宽度移出语言、v6 编码层宽度由目标自动决定、三层映射 = 图/格纯数学 + 编码 hw-map 域 +- 三层映射:`docs/memory-model-capability-lattice.md` §四(图 → 格 → 编码;编码 = 物理编码空间)+ project-book「三层映射(2026-08-27 正式晋升)」 +- 图推导边界 pass 挂账:TODO `bounds-inference`(:235)——区间证明免检/拒绝/保留检查三态同族 +- spec-design §9.4(机器 vs 数学整数已决 + 修订)+ §九免检先例(编译期证明免检) +- hw-map:`specs/2026-08-23-hw-map-design.md`(语义表 + 投影表范式)+ HIT `specs/2026-09-05-hardware-interface-table.md`(指令/运行层接口;事件签名无宽度,宽度/形状归投影) +- dex 线:TODO `dex-precision`、`dex-precision` 定点现状(`src/stdlib/dex.cr`) diff --git a/docs/superpowers/specs/2026-09-07-hit-m2-design.md b/docs/superpowers/specs/2026-09-07-hit-m2-design.md new file mode 100644 index 00000000..05c24b55 --- /dev/null +++ b/docs/superpowers/specs/2026-09-07-hit-m2-design.md @@ -0,0 +1,128 @@ +# HIT M2 设计(运行时核事件集 + 全表驱动收口) + +日期:2026-09-07 +状态:设计定稿(方案 1 拍板 + 节 1-3 逐节确认) +关联:`specs/2026-09-05-hardware-interface-table.md`(HIT 母 spec——§2 事件模型修订 + §6 M2 细化,本文为其实施设计)、`plans/2026-09-05-hit-minimal-core-m1.md`(M1 收官 + 偏差四项)、`TODO.md`「HIT 最小核」节(M2 挂账五条)、`specs/2026-09-07-regalloc-backend-design.md`(优化不进表——分配已归位 corearch 格形态层,本 M2 与其正交) + +## 1. 背景与 M1 边界 + +M1 已落地最小核运行闭环:`core-x86.toml`(4 事件 × 单步投影模板 `{opcode ≤2B, reg_role, rm_role, rm_mode}`)→ `hit.cr`(表加载/查询)+ `lower_to_core.cr`(IR 直线子集 → 事件流 + 常量池)→ `emit_instr_tabled`(corearch `--table`,与旧路径并行、逐字节对照)。冒烟 = sub/add/mem exit 正确,11/11 测试绿。 + +M1 边界(本设计的起点): +- **直线子集**:无 IR_BRANCH/JUMP/LABEL/CALL/RETURN 值/SPAWN——超子集 op 大声拒绝('needs more events') +- **4 事件 = 纯数据事件**:sub/nand/load/store 无控制转移能力——经典投影下分支/调用**不可合成到它们**(控制转移 = PC 语义,非数据事件)。M1 的「4 事件即可运行任意 Core 程序」表述仅在数据计算子集上成立 +- **混合模式**:表覆盖 op 走表、未覆盖落旧路径(`emit_instr_tabled` 返回 -1 → `emit_instr`) +- nand 真语义(and+not 两步)未实现(单步 and 占位);const 池无负值/宽值载体;`--table` × `--link/--shared` 显式拒绝;tagged/2L 路径整条落旧路径 + +## 2. 事件模型修订(运行时核) + +**拍板(D-a):表事件集 = 运行时核 9 事件**——4 数据事件保持(= 数据计算可移植子集),新增 5 个控制/调用/系统事件: + +| id | 事件 | 签名要点 | 投影形态(x86) | +|---|---|---|---| +| 1-4 | sub/nand/load/store | 不变(M1 定稿) | 不变(M1 模板) | +| 5 | jump | 无条件转移,目标 = 事件流位置 | rel32 无条件跳转(模板 rel32 回填角色) | +| 6 | branch | 条件转移:条件 = 槽源(s1 值 ≠ 0 真),真/假双目标(事件流位置) | cmp + jcc 序列或 test+jcc(两步序列 + 双 rel32 回填) | +| 7 | call | 函数调用,目标 = 函数索引(.ccr REG root span 行序) | x86 call rel32(函数体 = 事件流子序列;回填目标 = callee 事件流位置) | +| 8 | ret | 函数返回(调用栈机制 = 运行时条目) | x86 ret(栈约定见 §7) | +| 9 | extern | 外部符号调用(系统边界;IR_CALL_EXTERN) | call 至外部符号(重定位机制复用旧路径 ext_rel) | + +- **母 spec §2「最小核 4 事件即可运行 Core」表述修订为**:数据计算可移植子集 = 4 事件;完整程序 = 运行时核(9 事件,每表必备)——换表仍换平台(运行时核 + 投影列均为表内容) +- 扩展直通事件(add/and/or/xor/mul/div/cmp/shift 等 = id 10+)**可选**:表声明则降低层直通,未声明则 4 核合成(spec §5「查表优先,无表项再合成」落地)。**x86 大表 = 数据事件直通条目全量声明**(M2c 收口产物) +- branch 双目标 vs 单目标形态:IR_BRANCH(s1=条件, s2=真目标, s3=假目标) 天然双路;模板支持双 rel32(无条件侧 = 相邻 +5 跳过技巧由模板序列表达,不新增事件) + +## 3. 表格式 schema v2(投影模板语言扩展) + +**拍板(D-b):`core-x86.toml` schema v2**——模板步(step)从 M1 的 5 字段扩展为完整形态描述;按 instr.cr 现有形态族收敛(YAGNI:不做全 x86 指令集,只覆盖编译器发射面): + +``` +step = { + prefix: [u8...] # 前缀(66/0F 等,≤2) + rex: {w, r, x, b} # REX 位(无 REX = 省略) + opcode: [u8 1..3] + modrm: {reg_role, rm_role, rm_mode} # rm_mode: 0=reg / 1=rbp+disp32 / 2=SIB + disp: {size, src} # disp32 寻址(rm_mode 1/2) + imm: {size 1|2|4|8, role} # 立即数角色(槽值/常量) + rel: {role, kind} # rel32 回填(目标 = 事件流位置 | 函数 | 外部符号——kind 决定回填表) + cond: {role} # branch 条件源槽(cmp/test 序列的操作数角色) + seq: [step...] # 两步以上序列(nand = and+not;branch = cmp+jcc) +} +``` + +- 回填角色(rel kind):M1 常量池 `hit_pool_patch` 机制扩展为三张回填表——事件流内部目标(jump/branch/call 函数体)、函数起点、外部符号 +- 角色解析复用 M1 约定:操作数角色(dst/src1/src2/val/addr/cond)→ 槽(`g2_slot`/`e2_load_var` 机制)或事件流位置 +- **每事件多形态**:事件可带多个 proj 条目(如 load 的 rm_mode 0/1/2 各一形态),发射按操作数形状选择(现 instr.cr 按类型/形状分发——数据化后 = 表内选择) +- **字段扩展注(Task 0 盘点 2026-09-07 回填,先于 Task 2 落地)**:modrm 增 base_role(rm_mode 1 泛化——缺省 rbp,可 = 槽值寄存器;rm_mode 2 细化 = SIB 字段组 {scale, index_role, base_role};base_role=rip = mod=00 rm=101 池/全局/rodata 形);modrm 可缺(E8/E9/C3/0F05/0F0B)且 reg_role 可为 /digit 操作码扩展(C6/C7/F6/F7/D3/C1);rel 补 size {8|32}(rel32 三角色 kind 已有);imm 补 lit 字面量与 imm64 回填 kind(movabs 函数绝对地址);操作数角色集补寄存器字面量/隐含(rcx 移位计数、rax/rdx:rax idiv/cqo 对、ABI 调用寄存器、xmm0-7 为 SSE 面预留);disp.size 支持 auto(槽偏移回填按幅度选 1|4——解释器规则,与旧路径 e2_ld/e2_st 选择一致);字节拼接序 = prefix → REX → opcode → ModRM → SIB → disp → imm(解释器固定序,非字段)。 + +## 4. 降低层设计(lower_to_core 全 op 覆盖) + +**拍板:直通优先、合成兜底**: + +- **数据 op**(add/sub/mul/div/and/or/xor/not/shift/cmp/select/const 等):表含直通事件(id 10+)→ 事件直发;表无 → 4 核合成规则表补齐(M1 已有 add 反减;nand 真语义 = and+not 两步序列;or = 德摩根;xor = 组合;cmp = sub + 符号判别落槽;const = 池 load 扩展**负值/宽值**——池记录带宽度域,8B 槽装不下时借位链落 16B(M1 偏差 #3 载体落地)) +- **控制流**:IR_LABEL/IR_JUMP/IR_BRANCH → jump/branch 事件;**标签 → 事件流位置映射**(IR label id 表 + 两遍法:遍 1 记 label 事件序位置、遍 2 回填 rel32——事件流生成后回填,同常量池 patch 先例);函数级控制(loop/for/if-else 已由 IR 标签序展开,region 结构不参与事件流) +- **调用**:IR_CALL → call 事件(callee = 函数索引——.ccr SYM func 行序 + REG root span 边界;函数体事件流位置 = 降低时按函数序排布后回填);IR_RETURN 值 → ret 事件 + 返回值槽约定(§7);IR_CALL_EXTERN → extern 事件(符号名 → 外部符号表) +- **tagged/2L 路径(多字)**:2L 算术/比较的 IR 形态 = BINARY 溢出链 + 辅助块——M1 偏差「tagged 落旧路径」的根因 = 发射层块(jo 慢路径/2L 操作数块)不走表。M2 处置:**2L 快路径(无溢出)op 走表直通/合成;溢出慢路径块 = 序列算法保留代码**(§5 数据化边界同款——块内单指令查表,块序算法保留) +- **SPAWN/goroutine/通道**:IR_SPAWN 等 = 编译器自用面(corec 自举需要)——降低层接现有运行时函数(goroutine 包装)→ extern/call 事件表达;若个别 op 无事件且不可合成 → 保留旧路径(混合)至 M2d 终态前逐项清点(自举语料实证覆盖) + +## 5. 发射数据化收口(全表驱动) + +**拍板(D-c 边界):数据化范围 = 单指令编码形态;序列算法保留代码(调模板)**: + +- instr.cr 2381 行按形态族逐块迁移(sz_* 族 + e2_* 族清单在 writing-plans 时逐函数盘点):单指令编码(前缀/REX/opcode/ModRM/disp/imm/rel)→ 表数据;**每个迁移块以旧路径逐字节对照为判据(表模式产物 byte-identical = 模板正确性判据延续 M1)** +- **保留为解释器算法的序列**(非平台特有 = 表外):prologue/epilogue、参数拷贝、2L 慢路径块、函数帧布局(g2_slot 帧公式)、分配/判定(格形态层——regalloc-backend 已定优化不进表) +- 收口完成态:`emit_instr` 硬编码分发 → 表数据查询 + 模板解释器;旧路径与表路径**汇合为同一条**(表 = 唯一发射真源;「混合模式」概念消失——所有 op 都有表条目或合成规则) + +## 6. 运行时条目(最小描述) + +**拍板(D-d):运行时条目数据入表 + 校验;解释器实现当前 x86 ABI 一档**(多 ABI 通用解释器 = YAGNI): + +``` +runtime = { + call: {机制 = 栈, 返回地址位置, 栈生长方向} + param: {传递 = 栈 | 寄存器集(条目声明;x86 档 = 栈传递 + 现行约定)} + save: {callee-saved 集} # 与分配器 callee-saved 集一致(rbx/r12-15) + extern: {入口约定, 符号解析} +} +``` + +- 解释器读取运行时条目生成 call/ret 序列(参数拷贝/栈帧 = 代码算法按条目参数化——**条目数据驱动序列选择,非每 ABI 一份代码**;x86 档 = 参数化实例之一) +- rt.s/静态链接机制复用(自举产物 = 静态自包含 ELF 同现路径) +- 表校验:事件集必备性(运行时核 9 事件缺失 → 拒绝)、rel kind 与模板一致性 + +## 7. 测试与验收链 + +1. **模板对照**:每个新形态/新事件模板 → 同源程序表模式 vs 旧路径 ELF **逐字节对照**(M1 判据延续)+ 单事件定向用例 +2. **suite 覆盖**:`tests/suite/*.cr` + 自写覆盖(控制流/循环/函数调用/递归/打印/内存/位运算)表模式 exit 断言全绿——**混合过渡态判据(M2b)** +3. **组合验证**(M1 偏差清点):HIT × tagged/2L(2L 快路径走表 + 慢路径块)、`--table` × `--link/--static`(extern 重定位)、O2 分配在表模式(**M2 拍板:表模式恒 O0 全栈发射不变**——优化 = 格形态层与表正交;表模式 O2 恢复 = 发射器汇合后自然解锁,挂账 M3)) +4. **自举验收**:M2b 末——表模式(混合过渡态)corec 编译自己跑通(提前暴露降低/调用层的全语言面问题);M2d 末——汇合后纯表(零旧路径残留)corec 编译自己跑通(全表驱动收口终验) + +## 8. 里程碑分段(M2a-d,writing-plans 分解为任务) + +- **M2a 事件集 + 模板语言 v2**:schema v2 解析(hit.cr 扩展)+ jump/branch/call/ret/extern 投影模板 + 模板解释器新形态 + 逐模板对照测试 +- **M2b 降低层全 op**:控制流标签映射/回填、调用降低、合成规则补齐(nand 真语义/负值宽值池/直通优先)+ suite 与自写覆盖绿(混合过渡态)+ **自举冒烟(混合态先行验证——全语言面提前实证,风险处置)** +- **M2c 发射收口**:instr.cr 形态族逐块数据化(byte-identical 回归逐块绿)+ 汇合(emit 单路径——表 = 唯一发射真源,混合模式概念消失) +- **M2d 运行时 + 纯表自举**:运行时条目数据化 + call/ret/extern 序列参数化 + 组合验证清点 + 纯表自举收口(零旧路径残留终验)+ 文档(母 spec §2/§6 修订回填 + TODO 清账) + +## 9. 风险与处置 + +- **自举工程量**:表模式必须支持编译器自用全语言面——处置:全 op 降低覆盖在 IR 层(与源特性正交——IR op 全集固定);自举语料 = 实证收口,混合态先验降风险 +- **模板表达力边界**:个别编码形态 schema v2 未覆盖 → 处置:形态族清单先盘点(writing-plans 前置任务——读 instr.cr 全 2381 行分类),schema 按清单定稿;迁移中发现新形态 → 字段扩展 + 对照测试先行 +- **call/ret 与 2L 慢路径/帧布局交互**:调用点 2L 值跨函数 = M1 已知边界(无协议)——处置:保持现状边界(表模式与旧路径同语义面),不扩 2L 调用协议 +- **性能/体积**:自举 .ccr 事件流 + 回填规模——处置:对照测试先行,必要时降低器局部优化(非本里程碑验收项) +- **回归面**:instr.cr 逐块迁移风险——处置:每块迁移独立提交 + byte-identical 对照 + 全套回归(mw1-6/live_ranges/ccr_v6/region_cfg/compile/backend_bootstrap) + +## 10. 拍板记录(2026-09-07 用户确认) + +1. **范围 = A+B 全量**(母 spec §6 M2 全表驱动 + TODO 挂账全项;按依赖排序分批执行) +2. **验收语料含自举**(M2 终态 = corec 经表模式编译自己成功运行;suite + 自写覆盖为过程判据) +3. **方案 1**(运行时核事件集 + 降低完备 + 发射数据化收口;方案 2 图调度、方案 3 crasm 直通表 = 否决) +4. **节 1**:9 事件 id 分配 + 每表必备运行时核 + schema v2 字段集 +5. **节 2**:数据化边界 = 单指令编码(序列算法保留代码调模板);运行时条目最小描述 + x86 一档 +6. **节 3**:M2a-d 分段 + 验收链(模板对照 → suite → 组合 → 自举两段) +7. **M2 附加拍板**:表模式恒 O0(优化 = 格形态层,与表正交;表模式 O2 恢复挂账 M3) + +## 11. 关联文档同步项 + +- 母 spec(2026-09-05-hardware-interface-table.md)§2 表述修订 + §6 M2 状态(M2d 收尾回填) +- TODO.md「HIT 最小核」节 M2 挂账清账(逐项随里程碑提交划除) +- M1 plan 偏差注记(随 M2b/c 清点划除) diff --git a/docs/superpowers/specs/2026-09-07-regalloc-backend-design.md b/docs/superpowers/specs/2026-09-07-regalloc-backend-design.md new file mode 100644 index 00000000..b04e5323 --- /dev/null +++ b/docs/superpowers/specs/2026-09-07-regalloc-backend-design.md @@ -0,0 +1,56 @@ +# 寄存器分配移后端设计(编码层资源决策归位) + +日期:2026-09-07 +状态:**设计定稿 + 实施完成(2026-09-07——R1-R5 全部拍板落地,见 §6 决策记录与 +实施计划执行记录)** +关联:`docs/regalloc-cache-mapping.md`(判定四条/上下文贪心)、`specs/2026-09-05-lattice-ir-v6-format.md`(v6 ENT home)、`specs/2026-09-06-int-multiword-backend-design.md`(多字表示 = 后端)、`specs/2026-09-05-hardware-interface-table.md`(优化不进表——判定/分配在格形态层)、compile-time-linearity 计划(corec 时间分布)。 + +## 1. 归属论证 + +**寄存器是物理资源——分配是编码层决策**(与 int 多字表示、指令发射同层)。现状分配器在 corec(opt.cr)纯属历史(v5 时代后端无决策能力);「g2_slot 负编码 = 纯机械翻译分配结果」的表述把后端钉成执行者——分配归位后此表述取消:后端自己做分配,g2_slot 是后端的槽解析 seam(决策的一部分),非机械翻译。 + +``` +现状:corec(数据面+分配+判定)→ .ccr 传 REG_ASSIGN → corearch 机械消费负编码 +目标:corearch(数据面+分配+判定+发射 = 全部编码层决策) + corec 只产语义(.ccr 无 opt_meta——分配结果不再跨进程传输) +``` + +## 2. 迁移形态(决策点 R1 —— **R1a 拍板落地**) + +opt.cr 相关函数(compute_live_ranges/compute_entries/共存/alloc_registers/verify)——**按层拆分(R1a)**: +- opt.cr 现既在 corec 闭包(优化 pass 含 CSE 等仍属 corec 吗?——CSE/pass_cse 是图优化=语义层?分配是编码层——opt.cr 里两类混居) +- **R1a(拍板)**:opt.cr 拆分——语义优化(AST 常量折叠 + pass_cse 留 corec)与编码决策(数据面/共存/分配/判定/注入)移 corearch(新文件 `src/arch/linux/ld/regalloc.cr`,加入 corearch 闭包)。corec 侧 opt.cr 1940→312 行(剩件 = AST 折叠 + CSE + optimize_all 死码编排入口);main.cr O2 段/`cir` 调试通道摘除 +- R1b(弃):整文件入 corearch 闭包——CSE 是图优化应留 corec + +## 3. .ccr 简化(决策点 R2 —— **D-1=Y 拍板落地**) + +- opt_meta(含 REG_ASSIGN)出 .ccr——分配结果不再传输(corearch load 后自算) +- **ENT 段恒空(D-1=Y)**:corec save 不再落条目——SYM func/REG `first_ent/last_ent` 恒 -1、param_ents 恒 -1、ENT 段 0 条、opt_meta 子节 0 块;**格式结构保留**(loader 空表语义已支持:段缺失/0 条 + pcnt==0 ↔ -1 对照),**零 version bump**。ENT home 字段随 ENT 落盘消亡(内存表 home 字段保留——corearch 自算内存态,M1 分配不回填) +- 数据面计算单宿主 corearch:compute_live_ranges/compute_entries 随 regalloc.cr 迁入,载入 NOD 流自算(与 .ccr 载荷同坐标)——corec lower 尾/落盘直写废止 + +## 4. 判定位置(决策点 R3 —— 拍板落地) + +verify_regalloc_consistency/regalloc_verify_all 随分配移 corearch(regalloc.cr):corearch O2 = load 后自算分配 + 自检(emit 前,违反 = 编译错误,成功静默)。corec build 路径判定触发移除(main.cr);corec `cir` 通道随迁 corearch 同名隐藏 flag(--dump-entries/--dump-coexist/--check-regalloc/--inject-*/--dump-regassign),红注入用例在 corearch 路径仍红(test_live_ranges 13/13)。 + +## 5. 多字整合 + +tagged 识别在 corearch(instr.cr mw_setup_tags)——分配移入后与 tagged 信息同侧(迁移前 Task 5 靠 REG_ASSIGN 元数据跨进程读取——现同进程)。判定消费 ENT(自算)+ tag 表——零通道。test_mw_task5 元断言改经 corearch `--dump-regassign`(O2 自算分配逐对输出):2L 溢出函数 tagged∩REG_ASSIGN ≠ ∅ 保持(reg 形态裁决在 corearch 自算下成立)。 + +## 6. 决策点清单(**2026-09-07 全部拍板 + 实施**) + +1. **R1** = **R1a**:按层拆——语义优化留 corec(AST 折叠 + CSE)、编码决策(数据面/共存/判定/注入/分配)迁 `src/arch/linux/ld/regalloc.cr`(corearch 闭包) +2. **R2** = **D-1=Y**:opt_meta 子节恒 0 + ENT 段恒空(first/last_ent/param_ents 恒 -1)——格式结构保留、loader 空表语义已支持、零 version bump;home 字段随落盘消亡(内存表保留,corearch 自算) +3. **R3**:verify 触发点 = corearch O2(load 后 alloc + verify,emit 前,违反 = 编译错误);corec 判定触发与 `cir` 通道移除(注入通道随迁同名 flag) +4. **R4**(注):编译时间分布——分配/数据面计算移 corearch 进程;compile-time-linearity 的 corec 侧减负面按实测另行记录(未列入本批) +5. **R5** = **拍板**:corec `-O` 门语义——O1 = CSE(corec 进程内,实证:对 .ccr 产物零效果——NOD O0/O1/O2 逐字节同);O2 分配在 corearch(corec build 透传 `--opt-level`;直接调 corearch 未传 -O = O0 全栈)。CLI 帮助文案已更新(corec/corearch) +6. **D-3**(附加拍板):pass_stack_share 停用摘除(产物死路径实证——g_stack_map 不落盘、corearch 恒空;恢复 = corearch 内 alloc 同 seam 实现,挂账) + +## 7. 受波及面(**均已处置**) + +- opt.cr 拆分(1940→312 行——语义 vs 编码切分;纯搬移提交 + 切换提交两步可独立评审) +- ccr_io.cr(ENT/opt_meta save 恒空 + func/REG first/last -1)+ 测试(test_ccr_v6 8/8 重写恒空断言;region_cfg walker 兼容零改) +- corearch 闭包(build_selfhost_native.py + regalloc.cr)+ backend_bootstrap(stage0-2 绿——分配在后端自举语义验证) +- main.cr O2 门/判定触发移除 + `cir` 调试通道摘除 +- 「纯机械映射」表述清除(regalloc.cr/instr.cr/globals.cr/regalloc-consistency.cr 注记更新) +- regalloc-consistency.cr(参考实现指针 → regalloc.cr + 停用/缺口注记更新) +- 多字 mw 测试(REG_ASSIGN 元数据通道 → corearch `--dump-regassign`;test_mw_task5 哨兵更新绿) diff --git a/docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md b/docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md new file mode 100644 index 00000000..dc952442 --- /dev/null +++ b/docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md @@ -0,0 +1,148 @@ +# corearch 重写设计:范式无关内核 + 实例定制 + +日期:2026-09-09 +状态:设计定稿(蓝图——模块归属裁决 + 演进序);**演进序步骤 2(内核抽取)已实施完成(2026-09-10,plan `2026-09-10-corearch-kernel-extraction.md` Tasks 1-4)**;**步骤 2.5(内核组件完备化——中立化波,plan `2026-09-10-corearch-kernel-completion.md` Tasks 1-5)已实施完成(2026-09-10)**——执行状态注记见 §1.1/§2/§3(步骤 2.5 执行注记 = 设计 spec `2026-09-10-corearch-kernel-completion-design.md` §7) +性质:裁决性 spec——按 `2026-09-09-lattice-encoding-boundary-design.md`(边界 C1-C3 契约)执行的目标架构蓝图;蓝图本体无代码改动。 + +关联: +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(格层边界——§3 转换器契约 C1-C3 = 本设计的裁决依据;C4 = 判据裁决) +- `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md` + `2026-09-09-lattice-ir-v7-format.md`(输入侧 = v7 图载体) +- `docs/superpowers/plans/2026-09-09-lattice-ir-v7.md`(v7 实施计划——演进序第 1 步) +- `docs/regalloc-cache-mapping.md`(判定四条 = 内核判定引擎族的已实现成员) +- `docs/memory-model.md` §一(缓存语义七条 + 无配方统一规则——内核的语义基座) + +--- + +## 0. 定位与判据 + +### 0.1 起因与定性 + +边界 spec 裁决:corearch 病灶 = 与经典计算机绑定太深(病灶 = 转换编码层;.ccr 大体成立局部不纯)。转换器契约(C1-C3):输入 = 纯格、输出 = 实例形态、核心只含通用机制。用户拍板(2026-09-09):**一个范式无关的小核心,其他架构针对各自范式/机器定制**——本设计为该拍板的蓝图实质化。 + +### 0.2 本设计回答的问题 + +1. 「小核心」的实际内容是什么(§1.1) +2. 实例层的定制边界是什么、x86 实例由什么组成(§1.2) +3. 现 corearch 每个模块归核心还是实例(§2 裁决表) +4. 以什么顺序演进到目标形态(§3) + +### 0.3 判据 + +- 输入 = v7 图载体(.ccr 格语义文件——边/条目/区域;op 字典 = 显式遗留的配方种类引用) +- 重写判据 = **行为等价 + 语义正确性方向**(边界 C4 已裁:逐字节复刻判据废止——实例算法可自由设计,正确性判据 = 投影/映射正确性方向,缓存语义条款 7) +- 哲学约束:内核判定**不读值内容**(条目泛型);规则封闭(永不新建);无格承诺(具体代数 = 映射参数);最弱理论(共存 = 对称关系) +- **内核不需要理解 op 字典语义**——判定作用于条目类别(位置+配方+存在),配方内容(字典含义) = 消费侧解释 + +--- + +## 1. 目标架构 + +``` +v7 图载体(.ccr) ──► 范式无关小内核 ──► 实例(每范式/机定制) + │ ①语义对象模型 │ x86 实例: 调度重建 + lower/发射 + │ ②判定/分配引擎族 │ + 帧/ABI/tag 算法 + 实例数据 + │ ③实例注册契约 │ 范式 B 实例: (未来) + └──────────────────── + 判定结果(home/共存/驱逐) 回传实例 +``` + +### 1.1 范式无关小内核(三组件) + +**① 语义对象模型**(输入契约 C1 的执行者) +- v7 图载体 → 条目 × 版本 × 区域语义对象流(loader/解析 = 内核职责——格式校验/拓扑不变量/ENT 校验属内核输入契约守卫) +- 对象 = 条目(位置 + 配方 + 存在)、版本区间、区域嵌套、无配方/持久条目(统一规则,条款 4b) +- op 字典:NOD 的 op = 配方种类**引用**——内核持有引用但不解释语义(最弱理论:判定不需要知道值怎么算出来的,只需要知道它存在、由谁产生、何时失效) +- 归一化角色:v7 载体的「槽语义投影」解释(v7 设计 §2.3 双通道的槽语义通道)不在内核——那是执行型实例的翻译事务 + +**② 判定/分配引擎族**(语义侧通用——regalloc-cache-mapping 判定四条的推广) +- 共存关系推导(sweep,条目泛型——v6 §4.2 算法 = 内核成员) +- 资源互斥约束(判定四条:共存互斥/读点无陈旧/驱逐配对/调用失效——**与资源域无关的语义判定**) +- 驱逐候选/写回规则(持久条目 home 保真——条款 4b 的判定形态) +- **资源域参数化**:引擎判定「互斥」但不知「寄存器/槽/量子位」——资源域 = 实例声明(无格承诺:实例的资源代数 = 映射参数传入引擎) +- 先例确认:regalloc-cache-mapping 判定四条 = 本引擎族第一个已实现成员——内核抽取 = 从 regalloc.cr 拆出语义侧(§2 裁决) + +**③ 实例注册契约** +- 实例 = {能力声明(接受什么语义对象流、产出什么)、资源域/代数参数、请求分派形态(实例消费判定服务的方式)} +- 注册/校验/选择机制——corearch 引导时选实例 +- 注册契约 = 内核与实例的唯一耦合面——新范式实例 = 新注册,不碰内核(规则封闭对象开放) + +**核心不包含**:任何机器编码知识(寄存器名/帧/ABI/ELF)、任何调度/执行序知识(执行者才需要调度——验证器消费不需要)、op 字典语义解释、HIT 表概念(表 = 实例数据形态,非内核概念——边界 spec C4 已定位表为实例数据载体)。 + +**三组件完成态(2026-09-10 步骤 2.5 收官注记——plan `2026-09-10-corearch-kernel-completion.md` Tasks 1-5,执行注记详见设计 spec §7)**:内核 = **范式无关三组件完整**——① 语义对象模型(对象面 API nod_\*/v7_edge_\*/配方查询 nod_inputs 完整 + 调度重建移实例 = 实例侧 build_linear_schedule,loader 收敛为纯对象载入 + 校验守卫;region_of_nod = 定夺不含——区域面仅 REG 行遍历);② 判定/分配引擎(位置登记表 kern_loc_\* 通道 + home 字段独立遍——中立性 guard A+B 全绿 = 内核零线性流引用/零实例布局读/零位置编码/零域分类,`tests/selfhost/test_ent_kernel_neutrality.py` 入回归面);③ 实例注册契约(实例声明行扩 needs_eviction/needs_call_sites ③④ 形式声明 {0,0}——引擎不实现 = 演进点 + kern_verify_all 双向输出面确认)。判据:行为零变化(全量回归 + backend_bootstrap stage 链/ccr_v7 产物/28/28 判定通道 byte-identical)+ 自举重建 + full-bootstrap guard(corec2/corec3 cmp 同 + N06=0)。x86 实例化(演进序步骤 3)= 纯实例内工作——接缝 = 登记 API + 实例声明 + 对象面。 + +### 1.2 实例层(每范式/机定制) + +实例 = 该范式/机器把语义对象变成自己编码的**全部定制内容**——无跨范式共性,不强建通用翻译层: + +**x86 实例组成**(从现 corearch 模块归位,§2): +1. **调度重建**(v7 设计 §3 投影契约):图载体 → 线性调度(文件序合法化 + REG 展开)——只有执行型实例需要;合法性校验(沿边约束)在实例内(内核不做调度承诺) +2. **lower/发射翻译**:语义对象/调度 → x86 编码(现 instr.cr 发射 + lower_to_core 的翻译逻辑下放——其泄漏面(帧/ABI/调用)在实例内**不再叫泄漏**:实例 = 经典绑定合法域) +3. **实例算法族**:帧布局(prologue/epilogue/帧公式)、调用序列(参数拷贝/返回协议)、tag/2L 编码选择、寄存器物理分配(CAG)——按边界 C2/C3:映射参数实例(推广 regalloc-cache-mapping 先例,同族必同类) +4. **实例数据**:HIT 表/模板/投影条目(M1/M2 资产 = 实例数据机制;字节层数据化 = x86 实例的内部优化,判据 = 行为等价/投影正确性——C4 裁决) +5. 运行时/ELF/链接/syscall 输出面(编码层合法内容) + +**x86 实例与内核的接口**:实例向内核注册(资源域 = 寄存器/槽的代数)→ 内核判定(共存/互斥/驱逐写回)→ 判定结果(home 分配等)回传实例 → 实例完成物理编码。语义对象流 = 内核输出、实例输入。 + +--- + +## 2. 现 corearch 模块归属裁决表 + +| 模块(现) | 现状职责 | 裁决 | +|---|---|---| +| ccr_io.cr loader 侧(load_ccr/段表/校验/ENT parse) | 文件 → 内存指令流 + ENT 校验 | **内核**(语义对象模型——v7 后对象 = 边/条目/区域) | +| ccr_io.cr ENT/区间校验 + REG root span 守卫 | 输入契约守卫 | **内核** | +| regalloc.cr compute_live_ranges/compute_entries | 存在区间/版本推导 | **内核**(判定引擎——v6 §4.1 规则 = 语义侧) | +| regalloc.cr entries_coexist/判定四条 | 共存/互斥/驱逐/失效 | **内核**(引擎族核心——参数化资源域后实例复用) | +| regalloc.cr CAG alloc_registers/物理分配 | 寄存器物理分配 | **x86 实例**(实例算法——寄存器 = x86 资源域) | +| regalloc.cr verify/注入钩子 | 判定自检/测试通道 | 判定部分 = 内核、分配部分 = 实例(随拆分) | +| dataflow 概念(corearch 侧无图——重建线性) | — | 实例调度重建(v7 §3——输入图载体的重建方 = x86 实例) | +| instr.cr emit_instr 发射 | 线性指令 → x86 字节 | **x86 实例**(lower/翻译——泄漏面在实例内合法化) | +| instr.cr 帧/ABI/调用/ret_patch 序列(R1-R12 面) | 实例算法硬编码 | **x86 实例**——按 C2/C3 参数化为实例算法(数据 + 序列函数) | +| elf.cr/ld.cr/resolve.cr | ELF/链接/回填 | **x86 实例**(编码层输出面) | +| HIT hit.cr/表/模板解释器/注入通道/lower_to_core | 表驱动编码 | **x86 实例**(实例数据机制——表 = x86 实例的编码数据载体) | +| corearch.cr 编排/flag 分派 | 引导/测试通道 | **内核引导 + 实例注册**(flag = 实例选择参数) | +| MW tag 语义 vs 编码 | 耦合在帧/op 面 | 语义(int 无上限/2L 值域) = 格/图层(文件);编码 = x86 实例算法 | +| v7 EDG/ENT 消费 | (v7 后新增) | 语义对象 = 内核;EDG 的调度消费 = x86 实例 | + +**拆分原则**:语义侧(存在/区间/共存/驱逐/写回/互斥——不读值内容、不知资源名) → 内核;机器侧(物理资源/编码/布局/协议) → 实例。含糊处按 G3-2 先例类推(对缓存语义的机器侧资源决策 = 实例算法;对条目的语义判定 = 内核)。 + +**执行状态注记(2026-09-10,演进序步骤 2 = plan `2026-09-10-corearch-kernel-extraction.md` Tasks 1-4 全落地,行为零变化判据绿)**: +- regalloc.cr 拆分(本表首四行裁决执行化):语义侧(compute_live_ranges/compute_entries/共存族/verify 判定面/诊断通道/dump)逐函数**纯搬**入 `src/arch/linux/ld/ent_kernel.cr`(内核,双 concat 共享——同时进 corec 与 corearch 二进制);CAG alloc_registers + g_opt_meta 写入(meta_set/append/remove/meta_reg_assign_total)+ 注入钩子留 `regalloc.cr` = x86 实例机器侧。LOC_HOME_BASE 裁决修正为单份随判定入内核(双份不可构建实证——concat 单编译单元自举 NameResolver 拒重复顶层声明,且机器侧零引用)。 +- ccr_io 写侧单源化:`compute_entries_v7` 镜像 + `ccr_grow_*` 副本删除——corec 写侧直调内核 `compute_live_ranges`(尾部逐 func 调 compute_entries),loader/写侧均单源 = 双转录 R5 收敛(产物 byte-identical 判据 23/23);半开盘布局转换(写 +1/读 −1)保留在 ccr_io 写点(内核算闭区间)。 +- corearch.cr 引导收敛为**实例声明表 + 表驱动 dispatch**(本表「corearch.cr 编排/flag 分派」行裁决的最小面落地):`g_instance_decl` 两行(INST_X86 [0,3] allow_link=1 needs_alloc/needs_verify=1;INST_TABLE [0,0] 恒 O0 职责窗口 allow_table=1)→ instance_select() → 决策逐点查询活动实例行——`--check-regalloc` 顺序保序(强制 O2 → alloc → 看门狗 → 注入 → verify_all);三路分派(--table×opt 门/--opt-level/O2 verify)收敛行为不变(评审独立 48/48 通道 base↔head 逐字节同)。 +- **注册契约需双向(勘探发现——本设计 §1.1③ 表述为单向「实例向内核注册」不完整)**:实证数据流 = 实例(分配器)**写入** g_opt_meta(meta_set/append/remove/meta_reg_assign_total)→ 内核判定(verify 规则①②)经 `meta_reg_for_var`(纯读通道)**消费**实例输出。该读通道随判定入内核(Task 1 裁决)——注册契约的请求/消费面天然双向:实例产出 → 内核读通道消费;蓝图后续「资源域参数化/双向契约(home 回填)」步骤在此基础上扩展。判定→机器侧唯一读通道 = meta_reg_for_var(分配输出输入通道)。 +- **grow/accessor 收敛成果**:ccr_io 局部 lr 表机制/ccr_grow_entries/ccr_grow_func_entry_meta 删除后,grow 单实例化——loader 与写侧同调内核 ent_kernel 的 grow(共享 globals.cr 声明 + 内核文件进双 concat = 单源化路径实证可行);ccr_ent_* 访问器按真引用保留。 +- **挂账注记**:① rl_print_loc 内 x86 注释随搬入内核(ent_kernel.cr:593「x86 寄存器枚举号(3=rbx, 12-15=r12-r15)」——注释层遗留,范式无关声明以代码为准,清理 = 后续步骤);② 按实例钳制时记起 corearch.cr:255-256 钳制点(现钳制直写 x86 实例行窗口非查活动实例行——Task 3 评审 Minor M1)与 :244 拒绝消息带 `--table` 措辞(未来非表实例 allow_link=0 误伤面,今日不可达——M4,消息措辞参数化时处理);③ 实例侧描述双份头注(ent_kernel.cr/regalloc.cr——演进漂移风险,当前互引一致——M5);④ ent_kernel.cr 头注函数名漂移已顺手修(reg_assign_total → meta_reg_assign_total——Task 3 评审 Minor M3)。 + +--- + +## 3. 演进序(蓝图裁决——实施另立) + +1. **v7 实施**(计划 d76d14c3 已就绪):图载体落盘 + loader 双端改——内核输入契约的前提。先行理由:内核对象模型的数据源 = v7 文件;ENT/EDG 不落盘则内核无对象可管 +2. ~~**内核抽取**:loader → 语义对象模型 + 判定引擎从 regalloc 拆出(compute_entries/live_ranges/共存/判定四条入内核;CAG 留实例)——**行为判据**:抽取中每步回归绿(判定语义不动只搬家——regalloc 移后端先例同款流程);内核接口 = 注册契约最小面先立~~ ✅ **完成(2026-09-10,plan `2026-09-10-corearch-kernel-extraction.md` Tasks 1-4;执行状态注记见 §2)**——判定语义引擎族(compute_entries/live_ranges/共存/verify 规则①②/诊断通道)纯搬 `ent_kernel.cr` 进双 concat + 写侧镜像消除 + 注册契约最小面(实例声明表 + 表驱动引导)落地;判据:test_live_ranges 13/13 + test_ccr_v7 23/23(byte-identical)+ 全量回归绿(compile/backend_bootstrap 链/hit_table 24/24/region_cfg 22/22/mw1-6/slice_bounds 7/7/bootstrap 三套)+ full-bootstrap guard(corec2 N06=0、corec2/corec3 cmp=0、冒烟绿)+ 自举重建冒烟。**范围克制保持**:资源域参数化/双向契约 home 回填/loader 语义对象化(loader = 段表读取面,对象模型改造 = 后续步骤)未做——最小面已立 +2.5. ~~**内核组件完备化(步骤 2.5——中立化波)**:三组件按 §1.1 补完 + 全量中立化(用户原则 2026-09-10:内核零经典概念)——对象模型补完(对象面 API + 调度重建移实例)、A 通道中立化(compute 系改 NOD 对象面 + 写侧 populate)、B 通道中立化(登记表 + LOC_HOME_BASE 移除)、注册契约完整(③④ 形式声明)~~ ✅ **完成(2026-09-10,plan `2026-09-10-corearch-kernel-completion.md` Tasks 1-5,提交 035ea289+72c40389 / 99fc6af3 / b4d8c74f / b9685f5c / 收官提交;执行注记 = 设计 spec §7)**——**内核 = 范式无关三组件完整**(完成态声明见 §1.1);判据:中立性 guard A+B 全绿(红→绿记录核:A 组 10 token 红 → Task 2 绿 / B 组 11 token 红 → Task 3 绿 / Task 5 终态复跑零残留)+ 行为零变化(全量回归绿:compile/backend_bootstrap 11/11 链 byte-identical/hit_table 24/24/region_cfg 22/22/mw1-6/slice_bounds 7/7/live_ranges 13/13/ccr_v7 24/24/bootstrap 三套 29+4+3 + 判定通道 28/28 逐字节同 + 冷编译语料 3/3)+ 自举重建冒烟 + full-bootstrap guard(corec2/corec3 N06=0、cmp 同、冒烟 rc 42)。范围克制保持:F4 双坐标域统一 = 挂账(后续专项);判定③④ 引擎实现 = 注册契约演进点(needs_* = 1 实例出现时) +3. **x86 实例化**(= M2 复启的接续形态):实例边界内的数据化/参数化(帧/ABI/调用序列/tag 编码 → 实例算法 + HIT 表数据化推进)——判据按边界 C4 重定(行为等价/投影正确性,非逐字节复刻);M2 挂起资产的承接点;**接缝就绪(步骤 2.5 产物)** = 登记 API(kern_loc_\*)+ 实例声明(needs_* 行)+ 对象面(nod_\*/v7_edge_\*/nod_inputs)——步骤 3 = 纯实例内工作 +4. **第二范式/非经典实例验证**(远期):注册契约的实证——范式 B 实例 = 新注册 + 新算法数据,零内核改动(规则封闭的验收) + +**时机注记**:步骤 1(v7)与步骤 2-3(重写)可分可合——v7 的 loader 改动若按内核对象模型的形状做(loader 产出语义对象而非线性流假设),可避免二次重构(实施计划的接缝优化项)。 + +--- + +## 4. 风险与挂账 + +- **内核抽取最大风险 = regalloc 拆分的行为零变化**:判定四条语义不动只搬家——判据 = 每步回归绿(O2 分配 + verify 通道先例);拆分时禁止顺手改语义 +- **调度重建归属**:内核不做调度承诺(语义消费不需要)——若未来验证器消费发现需要调度无关的图接口 = 语义对象模型的扩展(对象 API 先立最小面) +- **HIT 表定位**:表 = x86 实例数据形态——蓝图不承诺其他范式实例用表(换表即换平台表述校准为:换实例即换平台;表 = x86 实例的数据化方式) +- **op 字典**:内核不解释 → 验证器(语义消费通道)需要字典语义 = 字典语义文档化/形式化的独立工作(语义革命级——v7 设计 §2.2 显式遗留的后续) +- **M2 资产**(Task 0/1/2 完成品):实例数据机制的既有实现——实例化推进(步骤 3)时按 C4 判据承接,不报废 +- 本设计不包含:任务分解(实施另立)、x86 实例内部的具体数据/算法设计(实例化推进时按 C2/C3 出) + +--- + +## 5. 关联同步项 + +- 边界 spec:本设计 = C1-C3 的实质化——C6 总表中「corearch 模块」行按 §2 裁决表更新(实施时) +- v7 设计/格式/计划:输入侧(步骤 1 先行);v7 实施计划 Task 1/2 的 loader 改动接缝 = §3 时机注记 +- regalloc-cache-mapping:判定四条 = 内核引擎族成员——文档定位随内核抽取更新(实施时) +- M2 计划/spec:复启 = 步骤 3 的接续形态——判据按 C4 修订后承接(复启时执行) +- TODO.md:corearch 重写方向挂账(本 spec 落盘后) diff --git a/docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md b/docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md new file mode 100644 index 00000000..56e62061 --- /dev/null +++ b/docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md @@ -0,0 +1,225 @@ +# 格层边界定稿:判据 + v6 审计 + 转换器契约 + +日期:2026-09-09 +状态:设计定稿(裁决性 spec——后续一切工作按此判定) +性质:**裁决 + 边界定稿,非实现承诺**——不含 v7 格式设计、不含代码改动;自举可用性不受影响(现状零变化)。 + +关联: +- `docs/z-vision.md`(三层映射链晋升)、`docs/memory-model.md` §一(缓存语义七条 = 格层语义本体)、`docs/memory-model-capability-lattice.md` v4(4.2 程序顺序判据 / 4.5 规则封闭对象开放 / 4.6 无格承诺 / 11.5 寄存器分配 = 缓存语义映射实例)、`docs/regalloc-cache-mapping.md`(判定四条)、`docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md`(v6 格式)、`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(HIT)、`docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(图 = 唯一真相层演进顺序)、`docs/dataflow-design.md` §8(执行标注空间) + +--- + +## 0. 背景与定位 + +### 0.1 起因 + +HIT M2 实施(表驱动发射收口)在 Task 3 停摆:branch 模板形态(spec 两步 cmp+jcc vs 旧发射器实际四原子 load+test+je+jmp)、ret 事件(裸 C3 vs 旧路径 IR_RETURN = 值序列+jmp→epilogue+ret_patch)连续撞出计划级矛盾。排查结论:**矛盾是症状**——模板在逐字节复刻一个本身经典形状的旧发射器。这暴露了更上层的问题:转换编码层(corearch)与经典计算机绑定太深。 + +### 0.2 定性(2026-09-09 用户确认) + +1. **.ccr(格形态)大体成立**——图坐标 NOD + REG 嵌套 + ENT 存在结构,方向对,不推倒 +2. **.ccr 局部不纯**——NOD 语义仍为 v5 变量槽指令(load/store 字节槽、变量 = 可寻址位置而非条目 × 版本);ENT 恒空(存在结构 = 侧表,未成主干);在「格不重新引入程序顺序」判据的边线上 +3. **真正的病灶 = 转换编码这一层(corearch)**——结构把经典机形状硬写进转换器逻辑:槽→rbp 帧公式、寄存器分配、ABI 分派、tag/2L 编码选择、call/ret 控制栈、ELF;降低器(lower_to_core:IR → 槽操作 + 栈控制事件)同样经典形状化 +4. **表驱动(HIT/M2)只数据化了最后 1 英寸**(指令字节),经典机绑定在转换器中段(帧/ABI/调用/降低)纹丝不动——本 spec 把实例化边界推到它该在的地方 + +### 0.3 本 spec 的权威用法 + +任何格式字段、模块、算法、计划任务,都可以被本 spec 的判据问四组问题(§1),按审计方法(§2)与契约条款(§3)判定归属。**裁决结果先于新工作**:M2/HIT 后续任务、corearch 重写、v7 方向、第二维护者的横向扩展,凡涉层归属者均须能回答「格语义对象 | 实例数据 | 实例算法 | 泄漏待归一」。 + +--- + +## 1. 判据完备化(可逐类判定的准则集) + +四组判据从既有锚点推导。每组末尾给出**判定问题**——对任意对象可逐条作答;任何一条答「是泄漏侧」即进入 G4 类型学点名。 + +### G1 语义本体判据(缓存语义七条的判定形态) + +锚点:`docs/memory-model.md` §一(值 = 配方 / 驱逐不变量 / 再生等价 / 边界公理 / 赋值 = 版本化 / 地址 = 映射 / 映射实例正确性)+ 无配方条目统一规则。 + +判定问题: + +1. **配方性**:该对象有配方吗(可由 (产生它的图节点, 输入边) 再生)?驱逐它、保留配方,可观测语义变吗? +2. **赋值形态**:赋值表达为版本创建/失效/绑定移动,还是内存单元修改? +3. **位置形态**:位置语义 = (条目标识, 偏移),还是字节地址? +4. **身份形态**:身份 = 产生节点(图节点身份),还是独立存储事实? +5. **边界形态**:无配方条目(边界/图内不可重算)是否按统一规则处理(不按范式枚举)? + +### G2 层形状判据(格层形态的充分条件) + +锚点:capability-lattice v4 §4.2(**格不重新引入程序顺序**——传统 IR/SSA/LLVM 本质仍是「另一种程序」,按执行路径组织;这是三层链成立的关键判据)、§4.5(规则封闭对象开放)、§4.6(无格承诺)。 + +判定问题: + +1. **程序顺序**:该结构是否按执行路径组织(文件序/指令序 = 执行序)?若序仅为图的合法拓扑序列化(图语义完整可重建),判「序列化」,不判「程序顺序」——**判据是语义载体是否完整,不是文件是否线性** +2. **范式枚举**:规则/判定是否按范式枚举?层本体规则封闭(永不新建),对象开放(无界) +3. **条目泛型**:判定是否操作「条目」类别(位置 + 配方 + 存在)而不读值内容? +4. **最弱理论**:语义是否写在比具体格代数更弱的理论里(共存 = 对称关系即可,无传递性承诺)? + +### G3 实例/参数判据(编码层内容的合法形态) + +锚点:z-vision 三层映射链(编码 = 物理编码空间——把格编码成具体机器的地址、字节、指令、位模式;编码层绑定经典机 = 合法且必须)、capability-lattice §11.5(寄存器分配 = 缓存语义映射实例 = 已接纳先例)、§4.6(具体格代数 = 映射参数)、memory-model §二(区域/arena/字节权限 = 经典映射实例)。 + +判定问题: + +1. **可换性**:换平台(目标机/范式实例)时该内容语义不动吗?动 = 映射参数/实例数据;不动 = 该内容属格层;「换不掉且硬编码在通用机制里」= 泄漏 +2. **先例类推**:是否与「寄存器分配 = 缓存语义映射实例」同族(对缓存语义的机器侧资源决策)?同族 → 实例化处理,先例已接纳 +3. **参数形态**:具体代数/组织(乘积格、ABI、帧序)是否作为映射参数存在,而非层本体承诺? + +### G4 泄漏类型学(点名用分类) + +经典绑定泄漏的四种形态——审计与契约条款引用此分类: + +| 类 | 名称 | 定义 | 典型表现 | +|---|---|---|---| +| ① | 存储形状泄漏 | 变量 = 可寻址存储位置(帧槽/字节内存操作)进入格语义层 | load/store 字节槽、变量 = 内存单元、`&x` = 字节地址 | +| ② | 控制形状泄漏 | 经典控制流形状进入格语义层 | 函数 = 可调用子程序、标签/跳转、栈 call-ret、行式执行序 = 语义 | +| ③ | 编码选择泄漏 | 目标机编码选择固化进格格式/转换器通用机制 | tag 字节布局、2L 堆指针表示、jo 溢出链、imm 宽度选择 | +| ④ | 平台惯例泄漏 | 平台惯例出现于格层或转换器核心 | ABI 寄存器分派、参数栈序、ELF/rodata 布局、调用约定 | + +判定规则:G1-G3 答「泄漏侧」的对象 → 归入 G4 类别点名;归入后按 §3 契约裁决去向(实例数据 / 实例算法 / 入口归一化)。 + +--- + +## 2. v6 审计裁决表 + +审计对象 = `.ccr` v6 格式的**设计语义**(以 `2026-09-05-lattice-ir-v6-format.md` 为基——v6 spec 自身明言若干妥协,见下);实现现状差异单列注记(§2.6)。格式 = 逐成分 × 判据 → 裁决。 + +### 2.1 格式骨架(Header/段表/坐标体系)——格侧成立 ✅ + +- 段表组织/变长记录/字节宽 = **编码层事务**(v6 spec §7 开放点自认;G3-1:换编码空间可调)——不构成格层问题 +- **坐标 = NOD id(图坐标)**(D1):存在区间端点/region 边界/定值点全部以图节点序为坐标——与 .cir 同坐标系,兑现「图 = 唯一真相层」坐标承诺——**格侧成立** +- 版本化 v6-only、无转换工具:管线中间产物定位,OK + +### 2.2 NOD(节点表 = 图事件 = 配方载体)——方向成立,语义带泄漏 ⚠️ + +- **载体形状 ✅**:NOD = 图事件(每节点 = 配方:op + dest/srcs + 类型码 + 边)——格层本体条款 1「值 = 配方」的载体承诺正确 +- **op 语义 ⚠️**:NOD 的 op/dest/srcs 语义 = **v5 instrs 相同**(v6 spec 设计原则 6 明言:变量槽模型保留,为 corearch 最小改动)——这是**显式妥协,不是哲学决定**: + - ① 存储形状泄漏:变量 = vars 表 id = 槽;LOAD/STORE/LOAD_FIELD/LOAD_INDEX… 的语义 = 字节内存操作(G1-3:位置 = 字节地址形态) + - ② 控制形状泄漏:JUMP/BRANCH/LABEL/RETURN/CALL = 行式控制流形状(G2-1 边线);REG 提供嵌套但 op 语义仍按「执行路径」组织 + - ③ 编码选择泄漏:tag/2L 相关 op 与帧 tag 语义在 op/类型码面(G3-1:换平台编码选择被当作 op 语义) +- **正面先例 ✅**:ENT 版本化规则对 STORE 的勘定(定值目标在 s1,ρ(s1):=ρ(s2)——v6 spec §3.4)——**赋值 = 版本化(条款 5)的正确语义雏形**,审计底稿中作为「格语义形状」参照系 +- **op 全集裁决方法**:逐 op 以 Task 0 盘点清单 3(ast.cr 编号 0-51 全分类)为底稿,按 G1-G4 裁决——**裁决结果 = §4.1 的输入表**(本 spec 不逐 op 落表;新工作引用本 spec 时按 §1 判据 + 清单 3 底稿执行,落表 = 引用者的任务) + +### 2.3 ENT(条目表 = 存在结构核心)——格本体正确形状,两处问题 ✅⚠️ + +- **形状 ✅**:条目 = (var_id, version, def_nod, 存在区间, home, flags)——「条目 × 版本 = 赋值条款 5 的直接载体」、「位置 = (条目标识, 偏移) 条款 6 的位置形态」、「身份 = def_nod(图节点)」——**ENT 是格层本体在 v6 里最纯的成分** +- **问题 1 ⚠️ 恒空未主干化**:regalloc 移后端(2026-09-07)后 ENT 恒空(loader 空表语义支持、零 version bump)——存在结构 = 侧表,实际主干仍是 NOD 线性流 +- **问题 2 ⚠️ home 字段的语义定位**:home = 分配器回填槽位。裁决:**home = 缓存语义映射实例的注记**(同族于「寄存器分配 = 缓存语义映射实例」;regalloc 移 corearch 后 -1 直通 = 已在实例化走向)——格本体最小集 = var_id/version/def_nod/区间/flags,home 可留但语义定位 = 实例层数据 + +### 2.4 REG(region 表)——格侧成立 ✅ + +SG 嵌套 + 区内条目范围 = 图锚定区域/共存推导的坐标载体(G2-4:共存不落盘、sweep 推导 = 最弱理论形态)——**格侧成立**。区域 = 经典映射实例的组织面(memory-model §二),但 region 结构本身(嵌套坐标) = 图语义 → 保留在格侧正确。 + +### 2.5 NOD 边表缺席(v6 开放点 1)——格层纯度缺口 ⚠️ + +- 现状:边表 v6.0 可省略(文件序 = 合法拓扑序 → 重建无成本) +- 裁决:**边 = 配方语义的必要成分**(条款 1「(产生它的图节点, 输入边)」——缺边则配方不完整,「图 = 真相层」的文件载体不闭合,文件序退化为「另一种程序」的执行序——G2-1 从「序列化」滑向「程序顺序」) +- 落盘与否 = 编码层事务(v6 spec §7 开放点 1 的编码层定性正确);语义存在性 = 格层必需——**格形态的纯度判据:文件必须能重建图语义(含边),执行序只是图的一个投影** + +### 2.6 实现现状注记(本 spec 裁决为格式设计层;代码现状以复核为准) + +- v6 spec 头部声明「代码按实施计划推进」;regalloc 移后端记录:ENT 恒空、opt_meta 恒 0、first_ent/param_ents 恒 -1、loader 空表语义已支持 +- NOD op 与 v5 instrs 的精确对应、边表是否实际落盘、tag 元数据是否在 .ccr 内——**引用本 spec 做具体裁决前,以 ccr_io.cr / corec lower 阶段复核为准** + +### 2.7 产出:不纯点清单与洗纯边界 + +不纯点(按 §1 判据点名): + +| # | 位置 | 泄漏类 | 程度 | +|---|---|---|---| +| P1 | NOD op 语义 = v5 变量槽指令 | ①+②+③ | 结构性(v6 设计原则 6 的显式妥协) | +| P2 | ENT 恒空未主干化 | (本体缺位而非泄漏) | 结构性 | +| P3 | NOD 边表缺席 | (配方不完整) | 结构性缺口 | +| P4 | ENT home 混入格本体语义 | 实例注记定位不清 | 轻(语义定位裁决即可) | + +**洗纯边界声明**:本 spec 只定判据与目标纯度,**不设计 v7、不改代码**。P1-P3 的修复方向(NOD 配方语义化、ENT 主干化、边表语义必须)指向未来的格式演进——凡涉格式重构的工作须先过本 spec 判据;「洗纯边界内可修」(P4 类语义定位 + 不触格式的判据应用)可随时按本 spec 执行。 + +--- + +## 3. corearch 转换器契约 + +### 3.1 定位 + +corearch = **格 → 编码投影器**:把格语义对象(条目/版本/配方/区域)投影为具体机器的物理编码。**不是执行器**——执行标注空间(FIXPT/模糊/神谕/BSS/CORE)属于图(dataflow-design §8);「图执行契约」定义为图层事务(HIT spec §7 YAGNI 现状维持——本 spec 不推翻,仅确认:格→编码投影不需要理解范式)。编码层绑定经典机 = 合法且必须(z-vision 三层链),**问题只在绑定形态**(§3.3 C3)。 + +### 3.2 C1 —— 输入契约(纯格) + +- 转换器输入 = 纯格形态:条目 × 版本(ENT 语义主干)+ 图事件/配方(含边,语义完整可重建)+ 区域嵌套(REG)+ 符号/类型信息 +- 现状 .ccr 的泄漏(§2.7 P1/P3)在转换器**入口做归一化**:把变量槽/字节操作形状还原为条目/版本/配方语义后再投影——**泄漏不得作为转换器的输入假设向下传播**(现状转换器把槽 = rbp 帧偏移当输入假设 = 病灶的机制形态) +- 归一化层自身的正确性判据 = 语义保真(还原后格语义 ⊇ 原形状可观测语义——引用缓存语义条款 7 的映射实例正确性方向) + +### 3.3 C2 —— 输出契约(实例形态) + +- 输出 = 目标机物理编码(今日 x86 ELF)。经典绑定在**实例层**全部合法: + - **实例数据**:投影表/模板(HIT 表 = 正确载体方向,§3.5 C4) + - **实例算法**:帧布局(prologue/epilogue/帧公式)、调用序列(参数拷贝/返回协议)、寄存器缓存映射(regalloc = 已接纳先例)、tag/2L 编码选择——**全部为映射参数实例** +- **实例算法推广先例**:`regalloc-cache-mapping`(寄存器分配 = 缓存语义映射实例)是已接纳的实例化样板——帧布局/ABI/调用序列/tag 编码与寄存器分配同族(对缓存语义的机器侧资源决策,G3-2),**同族必同类处理**:入实例层,不入通用机制 + +### 3.4 C3 —— 转换器核心只含通用机制 + +核心允许(且只允许): + +1. **格语义 → 实例请求的通用翻译**:版本 → 位置分配请求、共存 → 资源互斥约束(判定四条形态)、配方 → 指令选择请求、区域 → 存储区段请求 +2. **实例注册/选择机制**:目标机实例(数据 + 算法)的登记、校验、按需装载 + +核心禁止(= G3 判据的可执行形态):帧公式、寄存器名、ABI 参数序、tag 位布局、调用序列、ELF/rodata 布局等**硬编码进通用机制**。凡现 corearch 硬编码处 = 泄漏点,去向 = 实例层。 + +### 3.5 C4 —— M2/HIT 裁决 + +1. **方向 ✅**:HIT 表驱动 = 编码层实例数据的正确载体(「换表即换平台」 = C2 实例数据形态的兑现路线)——不废弃 +2. **定位偏差 ① —— 判据错**:M1/M2 的对照判据 = 表路径 ELF **逐字节等于旧发射器产物**——服务对象错了:模板的正确性判据应为「格语义 → 实例的投影正确性」(行为/结构等价 + 映射正确性定理方向,缓存语义条款 7),不是复刻注定退役的旧发射器的字节(含其偶然形态:冗余 REX 位、jmp rel 0、ret_patch 戏法) +3. **定位偏差 ② —— 范围窄**:M2c 数据化只覆盖单指令编码形态(清单 1),帧/ABI/调用/返回序列(清单 2 R1/R2/R7 等)仍留代码——**实例化边界应覆盖全部实例算法族**(R1-R12 按 G3-2 裁决:实例算法 → 参数化进实例层;纯翻译机制 → 留核心) +4. **具体症状消解**:ret 裸 C3 矛盾、branch 模板四原子 vs 两步文本矛盾 = 旧字节复刻判据的必然产物——按本裁决重定判据后,模板 = 实例的正确语义序列,不再追旧字节 + +### 3.6 C5 —— 挂账裁决 + +- **「事件流/表数据入 v6 段(NOD op ↔ HIT event_id 对齐)」(TODO 挂账)**:裁决 = **事件流与表数据 = 编码层事务**(x86 实例的投影内容),不入格形态段——格形态携带语义对象,不携带任何目标机的投影中间物;NOD op ↔ event_id 对齐 = 编码层实例注册问题(实例数据引用格语义对象的方式),非格内容 +- **regalloc 移后端(已完成)** = C3/C2 的正面先例(分配判定 = corearch 内实例化 + .ccr 不承载分配结果 = 实例数据不出现在格格式)——方向确认 +- **tag/2L(MW)编码**:语义(int 无上限、2L 值域) = 格/图层;编码(tag 字节、jo 链、堆指针表示) = 实例选择(G4③)——现实现把二者耦在帧布局/op 面 = 泄漏形态,实例化时解耦 + +### 3.7 C6 —— 现有资产定位总表 + +| 资产 | 定位 | +|---|---| +| .ccr v6 格式骨架 / NOD id 坐标 / REG | 格语义对象(成立) | +| NOD op 语义(v5 槽指令) | 泄漏待归一(P1;入口归一化) | +| ENT 条目 × 版本 / 区间 / def_nod | 格语义对象(本体;恒空 = 未主干化 P2) | +| ENT home | 实例数据(映射注记) | +| NOD 边(缺席) | 格语义必要成分(P3) | +| SYM 函数/全局/结构/枚举符号 | 符号/类型语义(格侧携带 OK;函数 = 可调用子程序的形状语义 = ②泄漏的裁决点——函数形状去留 = 格式演进裁决项) | +| regalloc(corearch 内) | 实例算法(先例已接纳) | +| HIT 表 / 模板 | 实例数据(方向正确;判据与范围偏差见 C4) | +| 帧公式 / prologue / 参数序列 / ret_patch | 实例算法(现硬编码 = 泄漏,去向 = 实例层) | +| MW tag 语义 vs tag 编码 | 语义 = 格/图层;编码 = 实例选择(现耦合 = 泄漏) | +| ELF / 链接 / syscall | 实例输出(编码层合法内容) | +| lower_to_core(IR → 事件) | 现状 = 泄漏输入假设的传播者(C1 归一化后重定位) | + +--- + +## 4. 应用指引 + +### 4.1 裁决流程(引用本 spec 的新工作) + +1. 对象过 G1-G3 判据 → 泄漏则 G4 点名 +2. 对照 §2 审计表与 C6 总表:已在表 → 按表执行;未在表 → 按 §1 判据落表(落表结果回填本 spec 注记) +3. 涉格式演进(P1-P3 修复方向 / v7)→ 先出设计 spec 过本 spec 判据,再谈实现 +4. 涉实例化(M2 重定判据与范围 / corearch 重写)→ 按 §3 契约条款 C1-C3 逐条核对 + +### 4.2 即时应置(M2 停摆注记) + +- HIT M2a Task 3+(branch/call/ret 模板实施)**挂起**——原因 = C4 判据裁决前不扩大旧判据下的实施;复启条件 = 按 C4 重定对照判据(或用户另行拍板) +- M2 计划/spec 的「逐字节对照」「汇合消灭混合」等条款在 C4 裁决后的修订 = 引用本 spec 的后续工作 +- Task 0/1/2 已完成成果(schema v2 加载、注入通道、模板解释器)= 实例层数据/机制资产,不报废;判据修订影响其测试基准则 = C4 裁决的应用 + +### 4.3 与「两条根基革命不得同时进行」(type-system-direction)的关系 + +本 spec 是**边界定稿**(判据 + 定位),不是格式革命——不启动 v7、不动主链;它给未来的格形态演进/后端重写提供裁决基准,使这类革命发生时不再以「最小改动」妥协(v6 设计原则 6 的教训)。 + +--- + +## 5. 关联同步项 + +- v6 格式 spec(2026-09-05):§1 设计原则 6(v5 槽模型保留 = 显式妥协)在本 spec 裁决下 = P1 泄漏源——留待格式演进时修订,本 spec 不覆盖其条款 +- HIT spec / M2 plan:判据条款修订 = C4 裁决的应用(复启 M2 时执行) +- TODO.md:挂账「事件流入 v6 段」按 C5 裁决划销或改注(M2 复启时随 M2 修订执行) +- memory-model / capability-lattice v4:本 spec 不改写其条款(锚点全部直接引用) diff --git a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md new file mode 100644 index 00000000..095d3f1f --- /dev/null +++ b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md @@ -0,0 +1,189 @@ +# v7 格形态设计定稿:真图载体(P1-P3 修复) + +日期:2026-09-09 +状态:设计定稿(载体革命级——两段式第一段;字节格式 spec 与实施计划后置) +性质:**格式演进裁决的执行文档**——过 `2026-09-09-lattice-encoding-boundary-design.md`(格层边界)判据门;语义内容不变(op 全集不动);不含字节布局、不含代码改动。 + +关联: +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(格层边界——判据 G1-G4 + v6 审计 + 转换器契约;本 spec 的裁决依据,§4.1「涉格式演进先出设计 spec 过判据」) +- `docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md`(v6 格式——被本 spec 演进;原则 1「存在为主体,执行为投影」的兑现、原则 6「v5 槽模型保留」= P1 泄漏源的显式遗留化) +- `src/compiler/dataflow.cr`(VSDG 构建机制先例:节点 + def-use 数据边 + kind=1 state 边链 + 区域嵌套) +- `src/compiler/cir_cache.cr`(图序列化编码先例 v13——字节 spec 引用) +- `docs/ir-schema/coreir-schema.md`(三形态 schema——.ccr 列语义修订) +- `docs/memory-model.md` §一(缓存语义七条)、`docs/memory-model-capability-lattice.md` v4 + +--- + +## 0. 背景与定位 + +### 0.1 边界 spec 的裁决输入 + +格层边界 spec 审计结论(v6 不纯点): + +- **P1**:NOD op 语义 = v5 变量槽指令(①存储 + ②控制 + ③编码泄漏)——v6 设计原则 6 的显式妥协 +- **P2**:ENT 恒空未主干化——存在结构 = 侧表,实际主干 = NOD 线性流 +- **P3**:NOD 边表缺席——配方(条款 1:(产生节点, 输入边))不闭合,「图 = 真相层」的文件载体不完整,文件序从「序列化」滑向「程序顺序」(G2-1 判据) + +### 0.2 载体革命级(2026-09-09 用户拍板) + +修复范围 = **载体结构**:真实边闭合 + ENT 主干化 + NOD = 真图节点——结构性纯度达成;**op 语义不变**(v5 槽操作仍在 op 面)。P1 的语义侧修复(op 字典演进)不属本 spec——那是语义革命级,独立后续步骤。 + +### 0.3 定位与边界 + +- 本文 = v7 **语义/载体设计定稿**(结构、边语义、ENT 语义、投影契约)——无字节 +- 字节格式 spec = 后置(引用 cir_cache v13 先例) +- 实施计划 = 后置(corec/corearch 双端同日切换,v7-only) +- 判据门:§1-§4 每个结构成分过 G1-G3 判定(自检表在 §2.4) + +--- + +## 1. 目标载体 + +### 1.1 图 = 载体主干 + +v7 文件携带(语义层,无字节): + +1. **NOD——节点表**:图事件 = 配方载体。op/dest/srcs/类型码语义与 v6 相同(语义内容不变声明,§2.2);**坐标 = NOD id = 图节点坐标(v6 D1 延续)** +2. **边表(v7 必落,P3 修复)**:每节点的输入边显式落盘。两类边(沿 dataflow.cr 现有分类): + - 数据边(kind 0):值流 = def-use 引用 + - state 边(kind 1):副作用序 = VSDG 状态链(dataflow.cr `df_connect_state` 机制:副作用节点按程序序连成链 + 区域退出终止依赖) +3. **REG——区域表**:SG 嵌套坐标(v6 不变——区域 = 图锚定存储的组织面,格侧成立已裁决) +4. **ENT——条目表(主干化,P2 修复)**:条目 = var × 版本的存在视图(§1.2) +5. **调度 = 文件序**:文件序 = 生产者选择的合法调度(沿边约束的拓扑序之一)——**不再是唯一语义** + +**配方闭合(P3 的语义承诺)**:文件的语义载体 = 图(节点 + 边 + 区域);「值 = (产生它的图节点, 输入边)」可直接从文件读出。驱逐不变量 / 再生等价 / 版本化(条款 1/2/3/5)成为可在载体上陈述的性质,而非格式注释。 + +### 1.2 ENT = 存在结构主干(P2 修复) + +- 条目由 **corec 产时重建**(v6 §4.1 规则执行化——§4),恒空消灭 +- 条目字段语义:var_id + version + def_nod + 存在区间 + flags(无配方 = 图内不可重算/边界)+ home +- **home 语义定位**(边界 spec §2.3 裁决沿用):home = 实例映射注记——-1 直通(分配/缓存映射决策 = 实例层 = corearch/映射实例),格本体最小集不含 home 的决策权 +- 条目与边的关联:条目 def_nod = 产生节点;版本区间端点 = NOD id(v6 D1 坐标延续) + +### 1.3 调度 = 投影(执行序降级为投影) + +- 执行序不再是文件主干——它是**图的线性投影**(重建规则 = §3) +- 文件序的角色 = 生产者的调度选择:合法(沿边约束)但非唯一——图语义允许的其他调度(验证器重排、未来执行模型)不被文件序封顶 +- 这兑现 v6 原则 1「存在为主体,执行为投影」——v6 有承诺无载体(无真实边 → 无法验证调度合法性);v7 以边为约束使该承诺可检验 + +--- + +## 2. 语义纯度声明与边界 + +### 2.1 结构性纯度(v7 达成的) + +| 成分 | 判据通过项 | +|---|---| +| 图节点 + 输入边 | 配方闭合(G1-1:值 = 配方可直接陈述) | +| 条目 × 版本 | 赋值 = 版本化(G1-2)——STORE 的 ρ(s1):=ρ(s2) 勘定 = 版本推进的语义雏形 | +| 位置 = (条目标识, 偏移) | 条目标识 = (var, version)(G1-3);字节地址 = 投影层 | +| 共存 = 对称最弱关系 + 不落盘推导 | 最弱理论(G2-4,v6 §4.2 延续) | +| 判定 = 条目泛型 | G2-3(v6 ENT 判定规则延续) | +| 区域 | 图锚定(REG = 格侧成立,边界 spec §2.4) | +| home = 实例注记 | G3-2(寄存器分配 = 缓存语义映射实例同族) | + +### 2.2 显式遗留:op 字典(诚实标注) + +- NOD op 全集语义 **不变**(v5 变量槽指令:LOAD/STORE/BRANCH/CALL/arena/tag 面等)——v7 不承诺 P1 语义侧修复 +- 遗留的语义角色:op 字典 = 引用的「计算配方种类」参照——变量的槽解释 = 归一化解释层(§2.3)的一部分;泄漏从「载体结构泄漏」(v6:结构无图、语义无条目)降级为「**字典引用泄漏**」(v7:结构 = 图语义,字典 = 显式遗留,被载体声明包围并标注) +- op 字典演进(语义革命级)= 独立后续步骤:把字典逐族替换为配方/版本/区域上的定义——替换发生时以本 spec 的载体为底座,不动载体结构 + +### 2.3 归一化契约(双消费通道) + +v7 载体的消费 = 两个通道,接口 = 载体(图 + 条目),归一化发生在消费侧: + +1. **槽语义投影通道**(现状 corearch 路径的延续形态):图 → 线性调度(§3)→ 槽语义投影(现 IR 发射语义)——行为 ≡ v6 链(§3.3 判据),保证自举链无中断切换 +2. **语义消费通道**(验证/优化/未来后端):直接在图 + 条目上推理——驱逐/再生/版本化/共存为可陈述性质;本通道的消费形态 = 验证链工作(翻译桥/证书)的设计输入,非本 spec 承诺 + +归一化的正确性判据(两通道共享)= 语义保真(引用缓存语义条款 7 的映射实例正确性方向):投影/推理产物 ⊇ 载体可观测语义。 + +### 2.4 判据自检表(逐成分 × G1-G3) + +| 成分 | G1(本体) | G2(层形状) | G3(实例) | 结论 | +|---|---|---|---|---| +| NOD(op 语义) | 槽形态泄漏面(遗留) | 执行路径组织(遗留) | — | 显式遗留(§2.2),载体包围标注 | +| 边表(data/state) | 配方输入(条款 1 直接载体) | 无程序顺序引入(约束集,非路径) | — | 格侧 ✅ | +| ENT 条目 × 版本 | 赋值 = 版本化(条款 5 载体) | 条目泛型判定 | home = 实例注记 | 格侧 ✅(home 除外) | +| REG | 区域 = 图锚定 | 共存不落盘(最弱) | — | 格侧 ✅ | +| 文件序 | — | 合法调度投影(非语义) | — | 编码层事务(§3) | +| 符号/类型信息 | 语义对象 | — | — | 格侧 ✅(延续) | + +--- + +## 3. 投影重建契约(消费侧) + +### 3.1 图 → 线性调度 + +重建规则(corearch 侧,字节 spec/实施计划落细节): + +1. 以文件序为基始调度(生产者已保证沿边拓扑合法——**合法性校验** = 重建时沿边约束验证,失败 = 文件损坏拒绝) +2. region 边界按 REG 展开(区域开始/结束标注,语义同 v6 消费方式) +3. 投影产物 = 线性指令流(现发射层输入形态) + +### 3.2 state 边的调度角色 + +- state 链 = 副作用序约束:投影必须保持 state 链拓扑(同一副作用序)——文件序满足时直出;校验 = 沿 state 边验证 +- 数据边 = 值流约束:不参与调度(值流经条目/槽解析),但为配方闭合与语义消费服务 + +### 3.3 行为判据(渐进验证) + +**v7 重建产物 ≡ v6 重建产物**(同源程序:v6 链 vs v7 链——corearch 发射输入形态逐字节相同,ELF 逐字节/行为相同)。判据用途: + +- v7 切换(corec 产 v7 + corearch 重建)可分步验证:先「v6 语义等价」后「结构正确」 +- 测试族:ccr_v6(9/9)→ v7 同构测试 + 对照测试(v6 链产物 vs v7 链产物) +- 判据性质:这是**调度投影的等价判据**(新旧载体 → 同一投影),不是「复刻旧发射器字节」判据(边界 spec C4 裁决已废止后者在实例模板对照中的使用——两者不冲突:C4 裁的是实例模板的对照基准,本判据裁的是载体切换的安全性) + +--- + +## 4. ENT 主干化执行化 + +### 4.1 条目重建规则(v6 §4.1 照搬执行化) + +- 定值点收集:扫 NOD,dest ≥ 0 的节点 = 定值点 → 版本切分(def_nod、version) +- 两处 carve-out(v6 spec 勘定保留): + ① IR_STORE 单列:定值目标在 s1(ρ(s1):=ρ(s2),dest 恒 -1) + ② 三 op 排除:STORE_INDEX_VAR / STORE_PTR / DYN_DISPATCH 的 dest ≥ 0 非定值 +- 活区间:图坐标扫描([first_ref, last_ref] 逻辑,坐标单位 = NOD id);版本 k 区间 = [def_k, def_{k+1}) 截断 +- 参数条目(从未定值但有引用的 var——函数参数等):def_nod = -1,区间 = **[first_ref, last_ref]** + (**差异①注记,2026-09-10 Task 3 同步**:v6 spec §4.1 ③ 文字「[函数首节点, last_ref+1)」 + 与实现不符——live_start = 首引用指令而非函数首节点;盘上 live_end 半开 = last_ref+1。 + **实现语义为准**,v7 同规则(loader 只校验 els < ele ≤ instr_cnt,两语义都过,但 + corec 写侧/测试期望按实现语义);「全局条目」面实现不存在(全局 var 不在任何函数 + var 窗口 → 恒无条目——差异②,SYM globals 记录 = 全局唯一存在面,flags bit2 零实例) +- 无配方条目(边界/图内不可重算):flags 标注 + home 必须(持久存储语义——边界 spec 引用条款 4b 统一规则) + +### 4.2 生产与消费分工 + +- corec(产):图化(节点 + 边 + 区域——§5 机制先例)+ 条目重建(§4.1)→ 落盘 +- corearch(消费):loader 解载 → 判定(共存 sweep / 判定四条)消费条目区间;home 决策 = 实例层(分配器),不写回格式(v6 开放点 3 延续:.ccr = 传输中间物,内存态回填) +- 判定消费方式(区间在文件内后分配器是否直接消费 vs 内存重建) = 实施计划定夺,本 spec 不锁 + +--- + +## 5. 与既有资产的关系 + +| 资产 | 关系 | +|---|---| +| dataflow.cr VSDG | 图构建机制先例:节点 + def-use 数据边 + kind=1 state 链 + 区域——v7 生产者 = 该机制输出落盘(或等价重建);**构建时机/主链集成 = 实施计划核实项**(现图构建服务于 dump/分析面还是全量编译——决定 v7 生产成本) | +| cir_cache.cr v13 | 图序列化编码先例——字节 spec 的基线 | +| ir-schema coreir-schema(三形态) | .ccr 列语义修订:「线性化」→「图载体」(执行序 = 投影);.cir dump 视图不动 | +| ccr_v6 测试族 | v7 切换时迁移(v6 链对照 + v7 结构测试) | +| v6 格式 spec | 原则 1 兑现(本 spec §1.3)、原则 6 标注为 P1 泄漏源(§2.2 显式遗留化)、开放点 1(边表)裁决 = 必落 | +| 自举链 | v7-only;corec/corearch 双端同日切换(管线中间产物,零持久生态,v6 先例同款);切换期 = §3.3 行为判据绿后 | + +--- + +## 6. 挂账与后续 + +- **字节格式 spec**(后置):引用 cir_cache v13 编码先例;段布局/边表编码/变长记录在 v6 字节基线(段表架构)上扩展 +- **实施计划**(后置):corec 图化/条目重建 + ccr_io 双端 + corearch loader/重建 + 测试迁移——依赖字节 spec +- **op 字典演进**(语义革命级,独立):P1 语义侧修复——逐族替换为配方/版本/区域定义,底座 = 本 spec 载体 +- **corearch 重写**(边界 spec 决策点 3):消费本 spec §3 投影契约 + 边界 spec §3 转换器契约(C1 输入 = 纯格 = v7 载体) +- **M2/HIT**:维持挂起(边界 spec §4.2);复启时实例模板的对照判据按边界 spec C4 修订——v7 链的行为判据(§3.3)与 C4 裁决同向(投影等价而非字节复刻) + +--- + +## 7. 术语同步 + +- 「调度」在本 spec = 图 → 线性投影的产物(文件的合法拓扑选择),与「执行」区分:执行 = 图语义的运行时展开(未来执行模型/验证消费),调度只是其经典投影形态 +- 「图载体」= 以图为语义主干的文件形态(.ccr v7),与 .cir(dump 视图)区分:前者 = 编译主链的格形态,后者 = 调试/可视化视图 diff --git a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md new file mode 100644 index 00000000..346ebeab --- /dev/null +++ b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md @@ -0,0 +1,208 @@ +# 格形态 IR v7 字节格式设计(真图载体) + +日期:2026-09-09 +状态:~~格式设计定稿(待实现)~~ → **已实施(2026-09-10,`plans/2026-09-09-lattice-ir-v7.md` Task 1-3 收官)**——v7 段表架构时代,字节状态以此为准;实施期裁决回填见 §4 规则 4 / §6 开放点 3、5。 +**2026-09-12 追加(R2 P4 Task 1,D9/D10)**:**版本 7 → 8** + 段集合 +TYPE(7)/IFACE(8)(Task 1 落空壳;内容面归 Task 2/3)。文件名保留「v7」= 段表架构代号(改名引用面 40+ 处、收益为零);**v8 = v7 的加法扩展**:前六段字节布局不变,加段 + 版本 bump。旧 v7 六段文件由版本闸**整类拒收**(不静默当「两段缺席 = 空表」)——本条为现行字节权威(§1/§2/§3.7/§3.8/§4 已同步)。 +**2026-09-14 追加(R2 P5 Task 2/7)**:**版本 / 段集合 / 记录布局零变化**——单槽化只在**内存面**(DFNode `OFF_DF_TK` = 类型项引用 / `OFF_DF_AUX` = 辅码,72B 不变);`.ccr` 面 `tk` 语义定为**派生码**(§3.3 的「`tk` 字段语义」块,含 D20 稳定引用形态规范);`version` 保持 8、`CIR_CACHE_VER` 保持 17;`test_ccr_types` 34 → **40** 例。 +**2026-09-15 追加(#60 批 T2/T3:暖缓存静默缺陷修复 + 判据网暖态腿)**:`.cir` 快照**格式零变化**(`CIR_CACHE_VER` 保持 17)。修复落在**保存侧策略**:生成期对「快照**不载**的共享面」(实锤面 = `g_types` 类型行表;`checker.cr:10-19` 只追加不去重、快照只载 var 的 `type` 行号)有副作用的函数**不写条目** ⇒ 下跑必 miss ⇒ 重放全部生成期副作用(「宁可 miss 不可静默」);实现 = `main.cr` miss 分支 `tc0 := g_type_count` 比对。代价实测 **0.3%**(语料 101 档 989→986 条目)/ **0.9%**(编译器自源 110→109)。**§3.3 的「暖态缺行 ⇒ 写 `-1`」支因此收窄**:分配行的函数不再被命中(复合 mint 行在见证下**结构上不落盘**,实核 101 档扫描仅 `ptr_ref_first.cr` 有复合行且全缺 ⇒ 原盘面判据不可达,重钉见 `tests/selfhost/test_ccr_types.py` ㊲ + `/tmp/fct4/task2-report.md` §3.2)。**判据网补齐暖态腿**(本缺陷长期不可见 = 判据面对暖态零覆盖):入仓语料 `tests/probes/warm/`(7 档,**定路径**是该语料的设计要点)+ `tools/baseline/warm_leg.sh`(两层)/`warm_run.sh`(牙齿层);**实核:`check` 面写 0 条缓存条目 ⇒ 暖态腿必须走 `ccr` 面**。口径/预期值/时长见 `tools/baseline/REBUILD.md`;根因链与统一台账见 `docs/superpowers/plans/2026-09-15-warm-cache-diagnostics.md` §8-9 与 `.superpowers/sdd/warm-task4-report.md`。 +**2026-09-14 追加(R2 P6 Task 3,D20-② 兑现 = β)**:**版本 8 → 9** + **NOD 记录 36 → 40B**(语义区 28 → 32B:+28 新增 `item i32` = **TYPE 段文件空间项索引**,-1 = 无项;邻接域顺移 +32/+36)——前六段其余字段布局与 `tk` 槽语义/字节零变化;旧 v8 及更早文件由版本闸**整类拒收**(D10 先例)。`.cir` 快照面**零改动**(`CIR_CACHE_VER` 保持 17——项索引只在 TYPE 段重建后的文件空间成立,`.cir` 写点早于该空间 ⇒ 落盘只能得到进程内索引 = D20-③ 禁令;保存期由派生码重派生即得)。写侧装填 = `ccr_types.cr:ccr_nod_item_populate`(保存期单遍);读侧 = `load_ccr` 的 TYPE 段后**一致性硬校验**(域外 / `atom_of(项) ≠ 盘上码` ⇒ 拒绝)+ corearch `--dump-nod-items` 读回通道。`test_ccr_types` 40 → **48** 例。 +性质:两段式第二段(字节格式);语义定义 = `2026-09-09-lattice-ir-v7-carrier-design.md`(权威);本文件 = 字节怎么排。 + +关联: +- `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md`(v7 载体设计——语义权威:边必落/ENT 主干化/调度 = 投影/op 字典显式遗留) +- `docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md`(v6 字节基线——本文件在其段表架构上扩展) +- `src/compiler/cir_cache.cr`(图序列化编码先例 v13:邻接式 first_edge/edge_count——NOD/EDG 编码基线) +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(格层边界——P3 边必落裁决) +- `docs/ir-schema/coreir-schema.md`(三形态 schema,实现时同步) + +--- + +## 1. 与 v6 的差异(先读总表) + +| 面 | v6 | v7 | v8(R2 P4 Task 1 起) | **v9**(R2 P6 Task 3 起 = 现行版本) | +|---|---|---|---|---| +| version | 6 | **7**(magic `"CCR1"` 不变) | **8**(magic 不变;**旧 v7 六段文件由版本闸整类拒收**——D10) | **9**(**旧 v8 及更早整类拒收**——D10 先例) | +| 段集合 | STR/SYM/NOD/ENT/REG | + **EDG**(边段,tag 6) | + **TYPE(7) / IFACE(8)**(D9——原 `7+` 预留段顺移 9/10;两段内容面现行) | 不变(8 段;`9+` 预留不变) | +| NOD 记录 | 28B(op/dest/s1/s2/s3/tk) | **36B** = 28B 原字段 + `first_edge u32` + `edge_count u32` | 不变(36B) | **40B** = 32B 语义区(28B 原字段 + **`item i32` @+28**)+ 邻接(`first_edge`/`edge_count` 顺移 +32/+36) | +| 边 | 开放点(v6.0 省略) | **EDG 段必落**(P3 修复)——记录 8B 邻接式 | 不变 | 不变 | +| ENT | 恒空(loader 空表语义) | **实记录**(corec 产时重建——P2 修复);记录布局 28B 不变 | 不变 | 不变 | +| 语义 | 文件序 = 执行序(唯一主干) | 文件序 = 合法调度(拓扑投影);语义 = 图(NOD+EDG+REG+ENT) | 不变(TYPE/IFACE = 信息面,不参与发射) | **β 兑现**:NOD `item` = TYPE 段文件空间项索引 ⇒ 类型项**跨进程可解析**(D20-②;`tk` 仍 = 派生码,发射面零变化) | +| 前六段字节 | — | — | **布局逐字节不变**(取值随 STR 段增长重编号——IFACE 注册名驻留 ⇒ 冷路径晚于 `init_types` 的串 ni 后移;语义不变、ELF canary 不变;判据按 TODO #26 = 结构性断言) | NOD 段 `tk` 槽**字节不变**;新增 `item` 槽(0 → 内容);其余段逐字节不变(`.ccr` 四条记录值差 = **4B × 节点数**,实测 pa +5192B / gt +7452B) | + +v6 其余字节惯例沿用:小端、i32/u32、offset/size u32、`ccr_i32_fits` 界校验、段表 12B×n 段序自由、v7-only(无转换工具)、中间产物 < 4GB。 + +## 2. 总体布局 + +``` +┌ Header(16B)──────────────────────────────┐ +│ magic u32 = "CCR1" │ +│ version u32 = 9 │ +│ seg_count u32 = 8 │ +│ reserved u32 = 0 │ +├ 段表(seg_count × 12B)────────────────────┤ +│ {tag: u32, offset: u32, size: u32} │ ← offset 相对文件头;段序自由 +├ 段体 ──────────────────────────────────────┤ +│ STR / SYM / NOD / EDG / ENT / REG │ +│ + TYPE / IFACE │ +└─────────────────────────────────────────────┘ +``` + +段 tag 常量:`1=STR 2=SYM 3=NOD 4=ENT 5=REG 6=EDG` + **`7=TYPE 8=IFACE`**(R2 P4 Task 1,D9——两段必备,D11;`9+` 预留顺移:驱逐标注段 v6.1 延续、证书段)。corec 写侧用**规范序**(tag = 行号 1..8、offset = 前段尾、段体紧随段表连续);loader 仍按规范序校验(`tg == ri+1` + 段体连续两闸形状自 v7 不变)。 + +## 3. 段定义 + +### 3.1 STR — 字符串表(不变) + +`str_count u32` + `str_count × {len: u32, data: len bytes}`。语义同 v6。 + +### 3.2 SYM — 符号表(不变) + +同 v6(函数/全局/结构/枚举)。函数 `param_ents` = 条目 id——v7 ENT 实记录后解析到真实条目。 + +### 3.3 NOD — 节点表(40B)〔格层语义 + 编码层字节;first_edge/count = 邻接索引〕 + +`nod_count u32` + `nod_count × 40B`(R2 P6 Task 3 起;v8 及更早 = 36B,版本闸整类拒收): + +| 字段 | 类型 | 语义 | +|---|---|---| +| op | i32 | 操作码(op 字典——语义不变声明,见载体设计 §2.2) | +| dest | i32 | 产出变量 id(-1 = 无产出) | +| src1 | i64 | 源变量/立即数 | +| src2 | i32 | 源/辅助 | +| src3 | i32 | 辅助/宽度 | +| tk | i32 | 类型码 = **派生码**(见下方语义块) | +| **item** | **i32** | **TYPE 段文件空间项索引**(R2 P6 Task 3 起;-1 = 无项;`≥0` ⇒ 该节点原子行对应项——与 `tk` 的关系 = `tt_atom_of_term(项) == tk`,装载期硬校验) | +| first_edge | u32 | 出边在 EDG 段的起始下标(+32) | +| edge_count | u32 | 出边数(+36) | + +邻接约定:节点出边在 EDG 段**连续**(节点 i+1 的 first_edge = 节点 i 的 first_edge + edge_count)——重建流式零索引(cir_cache v13 同款)。文件序 = 节点 id 序 = 生产者选择的合法拓扑调度。 + +**`item` 字段语义(R2 P6 Task 3 起 = β 兑现;`tk` 槽字节零变化)**:`item` = **TYPE 段文件空间项索引**(D20-② 唯一许可跨边界形态;D13 确定性装填 ⇒ 文件内索引稳定)。写侧 = `ccr_types.cr:ccr_nod_item_populate`(**保存期**由盘上派生码经桥接层 `sh_tk_face_of_code`/`sh_term_of_ti` 重派生——**不得**沿用 emit 期活表索引:`ccr_type_populate` 的 `tt_layer_reset` 在 save 前作废活表引用,D20-③/`bad_term=329`)。读侧 = `load_ccr` 的TYPE 段后**一致性硬校验**(`item < -1` / `≥ tt_count()` / `atom_of(项) ≠ tk` ⇒ 拒绝 rc=1,**不得**静默当无项)+ corearch `--dump-nod-items` 读回通道。暖态缺行(缓存命中跳过 ir_gen)⇒ 写 `-1`(码仍由 `tk` 保真;暖态项面登记为部分)。 + +**`tk` 字段语义(R2 P5 Task 2 起 = 现行;字节/偏移零变化——R2 P6 Task 3 后仍为派生码)**:`tk` = **派生码**——类型面 op → 类型行(`IR_CONST`/`IR_BINARY` ∪ `{IR_ALLOC, IR_CALL, IR_LOAD, IR_I2F, IR_F2I}`);辅码面 op → 旗标/宽度/计数(`IR_BOUNDS_CHECK`/`IR_DEREF`/`IR_STORE_PTR`/`IR_SPAWN`/`IR_HOTPATCH_ROUTE`);其余 → 0。由 `sh_dfn_code_of_slots(类型项, 辅码)` **单源派生**(emit / `lower_to_ccr` / `.cir` 装载侧三处同源;分类表 = `ty_shadow.cr` 的 `sh_tk_face_of_code`)。**盘面不承载进程内项引用**——D20 禁令:进程内项表索引不得跨进程/跨复位边界(P4 `bad_term=329` 红证);未来跨边界唯一许可形态 = **TYPE 段文件内项索引**(**β 已兑现** = 上方 `item` 槽,R2 P6 Task 3)。P5 单槽化只在**内存面**(DFNode 72B 不变:`OFF_DF_TK` = 类型项引用 / `OFF_DF_AUX` = 辅码),NOD `.ccr` 面实测与 P4 逐字节同(`ptr_arith`/`generics_test`;`--dump-objects` 同)——详见 `plans/2026-09-13-r2-p5-cleanup.md` Task 2。 + +### 3.4 EDG — 边表(8B/记录,v7 新增)〔配方闭合的载体〕 + +`edg_count u32` + `edg_count × 8B`: + +| 字段 | 类型 | 语义 | +|---|---|---| +| to_nod | u32 | 目标节点 id(from = 所属节点,邻接隐含) | +| kind | u32 | 边类:0 = 数据(def-use 值流)、1 = state(副作用序 = VSDG 状态链);(2+ 预留) | + +`edg_count` 必等于 Σ 各节点 edge_count(校验规则 ②)。 + +### 3.5 ENT — 条目表(28B,布局不变;内容恒空 → 实记录)〔格层本体〕 + +`ent_count u32` + `ent_count × 28B`: + +| 字段 | 类型 | 语义 | +|---|---|---| +| var_id | i32 | 变量 id(-1 = 匿名常量条目) | +| version | u32 | 版本(1-based;每次定值 +1) | +| def_nod | i32 | 定值节点 id(-1 = 参数/全局) | +| live_start | u32 | 存在区间起点(NOD id,含定值) | +| live_end | u32 | 存在区间终点(NOD id,开区间) | +| home | i32 | **恒 -1 直通**(实例映射注记——分配决策 = 实例层,不写回格式) | +| flags | u32 | bit0 无配方(图内不可重算);bit1 参数;bit2 全局;bit3 驱逐候选(v6.1 延续) | + +产生规则 = 载体设计 §4(v6 §4.1 规则执行化 + 两处 carve-out)。 + +### 3.6 REG — region 表(24B,不变) + +同 v6(kind/parent/enter_nod/exit_nod/first_ent/last_ent,坐标 = NOD id)。 + +### 3.7 TYPE — 类型面(tag 7;R2 P4 Task 2 内容面落地) + +**现行(Task 2)**:两小节 = `row_count u32` + `row_count × 24B {kind,data,extra}` +(**i64×3**——与内存 `g_types` 槽逐位同源;24B/条按 8B 字段计)→ `term_count u32` ++ `term_count × 40B {tag,a,b,c,d}`(**i64×5**;类型项 DAG,**哈希不落盘**——加载侧 +由五字段重算 `tt_hash5`)。字段宽度注记:Task 1 此处曾以 `i32` 记法书写但字节数 +写 24B/40B(自相矛盾)——**实施权威 = 8B 字段**(内存槽宽度,无截断面), +`ccr_io.cr` 头注释同步(实现权威)。 + +**段内容确定性(D13)**:段体 = 类型表/接口表的**纯函数**(与判定历史无关)—— +写侧 `save` 前做「项层规范化重建」(`ccr_types.cr:ccr_type_populate`:项层/引擎/ +桥接三面复位 → 按行序逐行装填 → 序列化),故项 DAG 行号 = 行序遍历的构造序。 +(注:**行表本身**的冷/热 `.cir` 缓存态分歧 = 既有的前端行分配面——缓存命中跳过 +ir_gen 的 `alloc_type`,`ptr_arith` 实测冷 15 行/热 12 行且热 = 冷前缀;见 P4 +Task 2 报告,本 spec 只记段面。) + +**loader 校验(违规 = 拒绝,三态纪律 C.5-3)**:段必备(D11)+ 两小节计数/长度 +自洽 + 段体无尾随字节 + `tag ∈ 0..10`(TT_*)+ `a..d ≥ -1` + **子项引用 -1 或 +< 自身行号**(拓扑无环;标注槽按 tag 分派跳过——ATOM 的 a/b、MU/VAR 的 a 等, +字段表见 `ccr_io.cr:ccr_type_term_ref_ok`)。载入 = 重建 `g_types` 行表 + +`g_type_terms` 项表 + `g_tt_index` 索引(`grow_tt_index`/`tt_reindex` 重放), +失败整体拒绝(**不得**留空表)。 + +**读回通道**:`corearch --dump-types`(corec 侧 `ccr --dump-types` 同一打印路径, +含项层原语 probe 行 = `tt_norm`/`tt_is_dnf`/`tt_is_literal` 在同构重建表上的 +逐点同值证据)——跨进程行格式对拍载体(tests/selfhost/test_ccr_types.py)。 +**未覆盖面**:判定原语(`ty_sub`/`ty_equiv`/`ty_disjoint` 族,`type_engine.cr`) +跨进程同值未覆盖——该文件不入 corearch 清单(两条既有 TF01 诊断触发 project-mode +`error[`=0 门),归 Task 3/4/P5 收口(T2 报告登记)。 + +### 3.8 IFACE — 接口面(tag 8;R2 P4 Task 3 内容面落地) + +**现行(Task 3)**:五小节(D14;各带 count u32;**字段宽度 = 逐字段声明形**): +① `native_count u32 = 16`(扩列硬值:13 → +AK_NULL/AK_SUM/AK_FN——裁决 3/7) ++ `× 24B {ak i32, ti_row i32, name_ni i32, lit_code i32, ops i64}`(`g_iface_entries` +原样;`name_ni` = R2 P4 Task 3 起的注册名,STR 段增长按裁决 1 接受); +② `shape_count u32` + `× 8B {name_ni i32, term i32}`(六条 D17 生产名:`sequence` / +`sequence_ro` / `sequence_rw` / `indexable` / `iterable` / `product` → 形状项); +③ `iface_count u32` + `× {name_ni i32, method_count i32, generic_count i32, pad i32}` ++ `method_count × 80B {name_ni i32, param_count i32, self_mode i32, ret_term i32, +param_terms[8] i32, param_codes[8] i32}`——**方法记录字段序取计划「声明行」序** +(同块尺寸式两数组次序相反 ⇒ 取声明行为准,登记见 Task 3 报告);`ret_term`/ +`param_terms` = **类型项索引**(签名项;-1 = 不可建——写侧遇不可建 = 拒绝落盘, +`ccr_iface_populate`),`param_codes` = **信息面**裸码(S6 消费点不入段); +④ `impl_count u32` + `× 8B {trait_ni i32, type_ni i32}`(`g_impl_for`——裁决 2: +声明元数据,不入判定);⑤ `method_count u32` + `× 12B {type_ni i32, method_ni i32, +mangled_ni i32}`(`g_methods`——`iface_find_method` 的唯一数据面)。 + +**loader 校验(违规 = 拒绝,三态纪律 C.5-3)**:段必备(D11)+ 五小节计数/长度 +自洽 + 行走完 == 段体(无尾随字节)+ `native_count == 16` + 结构域(ak ∈ 0..AK_NULL、 +method_count ≤ `MAX_IFACE_METHODS`、param_count ≤ `MAX_IFACE_METHOD_PARAMS`、 +self_mode ∈ 0..3)+ **跨段引用域**:`name_ni`/`type_ni`/`method_ni`/`mangled_ni` ∈ +[0, STR 串数)、`ti_row` ∈ {-1} ∪ [0, TYPE 行数)、`term` ∈ {-1} ∪ [0, TYPE 项数) +(TYPE(7) 先于 IFACE(8) 解析 ⇒ 两域已建立)。载入 = 重建 `g_iface_entries` +(`g_iface_registry_ok = 1`——**段 = corearch 侧真源**)/ `g_iface_shape_*` / +`g_ifaces`(corearch 无 AST ⇒ 节点槽恒 -1,**项槽** = 段值)/ `g_impl_for` / +`g_methods`;失败整体拒绝(**不得**静默当空表)。 + +**读回通道**:`corearch --dump-ifaces`(corec 侧 `ccr --dump-ifaces` 同一打印路径; +含 `ifaceprobe` 行 = 跨段项索引在重建项表上的解析同值证据;`ifacesig*` 节 = corec-only +活算对拍)——跨进程行格式对拍载体(tests/selfhost/test_ccr_types.py)。**零判定面 +变化**:扩列三行 ops = 0(纯信息面),`iface_kind_of` 的行分派与 P2b 域逐格保持。 + +## 4. 不变量与校验规则 + +1. **拓扑不变量(语义约束的字节形态)**:EDG 每条边 `所属节点 < to_nod`(数据/state 边前向——DAG 拓扑调度);违规 = 文件损坏拒绝 +2. **边界声明(非边)**:branch/jump/调用目标的 NOD id 引用(可后向——回边语义)是**操作数字段,不是边**;循环语义 = REG(SG_LOOP 嵌套)+ state 链表达(VSDG 先例)——EDG 只承载数据/state 两类前向边 +3. `edg_count` == Σ edge_count;NOD/ENT/REG 引用 id 界内(NOD id < nod_count 等) +4. 段表 offset/size 界、ENT home/flags 读入放行——**裁决(2026-09-10 Task 2 review R3)**:loader 对 home≠-1 / flags≠0 **接受不拒绝**——home = 实例映射注记,.ccr = corec→corearch 传输中间物,实例层(分配/缓存映射)决策不写回格式;非 -1/非 0 值不构成损坏证据(无消费方依赖恒 -1/0 前提之外的安全面)。开放点 3 保留:未来实例层选择写回(非传输中间物用途)时重议 +5. magic/version(**`version == 9`**——R2 P6 Task 3 起;`version ≠ 9` 整类拒收,含全部 v7 六段/v8 八段文件——D10 先例);`ccr_i32_fits` 沿用(中间产物 < 4GB) +6. **段集合完备性(R2 P4 Task 1,D11)**:`seg_cnt` 未满 / 缺任一必备段(STR/SYM/NOD/REG/EDG/**TYPE/IFACE**)⇒ 拒绝;ENT 仍可缺(v5 精神:旧段缺失 = 空表,`ccr_io.cr` 既有口径)。**「缺段 = 空表」仅适用于 ENT**——TYPE/IFACE 缺席必须响亮拒绝(可选段 = 两种 `.ccr` 在野 = 静默降级面) +7. **~~空壳期段体校验(R2 P4 Task 1)~~(已退役——R2 P4 Task 3)**:TYPE 段自 Task 2 起、IFACE 段自 Task 3 起皆为内容面,校验 = §3.7/§3.8 的逐条不变量(含跨段引用域);两段**必备**(规则 6)不变 + +## 5. 消费方影响 + +- **corec(产)**:图化(节点 + 数据/state 边 + 区域——dataflow.cr VSDG 机制先例;构建时机核实 = 实施计划项)→ 条目重建(载体设计 §4)→ 邻接化(first_edge/edge_count 连续布局)→ 写 v7 +- **corearch(载)**:段表寻址 → NOD/EDG/ENT 载入 → 校验(§4)→ 图 → 线性调度重建(载体设计 §3:文件序 + 沿边合法性校验 + REG 展开)→ 发射(现路径语义) +- 判定/证书:共存 sweep(v6 §4.2)消费 ENT 区间;图语义消费(验证/优化)直接消费 NOD+EDG +- **TYPE/IFACE 段(R2 P4 Task 1 起)**:Task 1 = 空壳,**零消费者**(loader 仅做存在性/空壳校验);内容面(Task 2/3)落地后由 corearch 载入**重建**类型行表/类型项表与接口表(统一设计 spec §6.3 的 `atom_of` 承接面)——纯信息面,**不参与 ELF 发射**(发射面零泄漏判据 = ELF canary 不变) +- 测试族:test_ccr_v7.py 27/27(2026-09-10 Task 3 迁移收官;2026-09-12 R2 P4 Task 1 结构断言重定 = 8 段/`HEADER_TABLE=112`/`seg_count==8`/坏版本 (8,7,6,5))+ test_ccr_types.py **48/48**(T1 新建 9 例 = 段机制/loader 负分支/旧 v7 文件拒收;T2 内容面 +10(行表/项 DAG 序列化 + 确定性装填 + corearch 读回对拍 + 拓扑/标注槽负分支);T3 IFACE 内容面 +10(五小节/扩列 16/形状命名化/签名项化/impl 边与方法表/跨段引用域);T4 DFNode.TK 项槽 +6(逐节点允许清单规则/dex 语料/NOD 36B 同序/冷·暖快照对称/dump 零产物影响/`.cir` 布局未变);**P5 T2 单槽化 +6**(复合行走辅码 / 互斥不变量 / F1 hotpatch 无面 / 复合行冷·暖对称 / 复合行 NOD 同序 / 快照盘面派生码保真);**P6 T3 项索引 +8**(版本闸 v8 拒收 / NOD 40B 字段序 / 项索引落盘读回对拍(非退化)/ 两通道面一致 / 辅码面与复合行保真 / 项索引越界与不一致 ⇒ 拒绝(三子例)/ 段界检 / D18 纯度静态;**突变 M1–M5 逐条转红**);机制面随批重定)——v6 测试族已合并退役;无版本链对照(v8-only 世界,行为判据 = 全量回归 + stage 链 byte-identical,见载体设计 §3.3 注记)。**判据口径(TODO #26,T1 起)**:不以「与旧版逐字节同」为准 = 结构断言全绿 + 语义零变化 + 自举稳定。 + +## 6. 开放点(实施期定夺) + +1. EDG kind 扩展(2+ 预留:驱逐标注/证书边)——仅常量定义,不占空间(v6 先例同款) +2. NOD 28B 原字段与 v5 instrs 的同构声明是否需版本字段级文档对齐(ir-schema coreir-schema 同步项) +3. ENT home 非 -1 的处理级——**裁决(2026-09-10):接受不拒绝**(§4 规则 4 注记回填);若未来实例层选择回写(非传输中间物用途)时重议 +4. 图化生产的构建时机(dataflow 现服务于 dump/分析面或全量编译——决定 v7 产成本;实施计划核实) +5. loader 侧 per-parameter ENT 内容核对与嵌套 REG 行条目范围内容核对——**推迟裁决(2026-09-10 Task 2 review R4)**:per-param 核对(param_ents 应指向该参数 var 的 def=-1 条目等)需 fn_meta 参数窗口展开(现 fn_meta 无 per-param 声明面),在 Python 测试族净锁(test_ccr_v7.py 全量块切分/param_ents/SYM 双写断言 = 测试侧同语义锁,2026-09-10 Task 3 迁移收官)期间不值得扩 fn_meta——测试锁保持,loader 内容锁挂账(未来 fn_meta 展开时激活) + +## 7. 关联同步项 + +- v7 载体设计(语义权威)——本文件实现时以 ccr_io.cr 为基、按 schema 文档惯例对齐并入(v6 先例同款流程) +- 实施计划(后置)——依赖本文件 §6 开放点 1-4 的定夺 +- corearch 重写设计(决策点 3)——消费 §5 corearch 载入 + 载体设计 §3 重建契约 diff --git a/docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md b/docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md new file mode 100644 index 00000000..5e18a5e2 --- /dev/null +++ b/docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md @@ -0,0 +1,248 @@ +# corearch 内核组件完备化设计(语义对象模型 + 判定中立化 + 注册契约完整) + +日期:2026-09-10 +状态:**已实施(2026-09-10 Task 1-5 收官——plan `2026-09-10-corearch-kernel-completion.md` Tasks 1-5 全落地:对象面 + 调度重建移实例 / A 通道中立化 / B 通道中立化 / 输出面中性化 + ③④ 声明 + 收官全量回归自举;中立性 guard A+B 全绿 + 行为零变化判据全绿)**——执行注记见 §7 +性质:**内核完备化裁决文档**——补完 `2026-09-09-corearch-rewrite-design.md`(蓝图)§1.1 三组件缺口;判定③④ 形式化声明不实现(用户裁决);调度重建移实例(用户裁决)。行为零变化。 + +关联: +- `docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md`(蓝图——§1.1 三组件/§1.2 实例/§2 裁决表/§3 演进序) +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(边界——C1 输入纯格/C3 核心只含通用机制/G3 实例判据) +- `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md` + `-format.md`(输入载体——NOD/EDG/ENT/REG 语义) +- `docs/regalloc-cache-mapping.md`(判定四条——内核引擎族已实现成员) + +--- + +## 0. 背景与缺口审计 + +蓝图 §1.1 三组件(范式无关小内核)在步骤 2(内核抽取,2026-09-10 收官)后的完成度: + +| 组件 | 蓝图承诺 | 现状 | 缺口 | +|---|---|---|---| +| ① 语义对象模型 | loader/解析 = 内核;对象 = 条目/版本/区域(配方可读) | loader 解析进全局表(ENT 24B 对象化 ✓;EDG 载入零消费方;NOD → 线性流重建**在 loader 内**) | 调度重建归属错位(蓝图 §1.2:执行者才需要调度 = 实例事务);无对象访问面 API;EDG(配方边)无消费接口 | +| ② 判定/分配引擎 | 语义判定不读值内容、不知资源名(资源域参数化 = 实例声明传入) | 判定引擎已抽取(ent_kernel.cr)——但 verify 规则①② 经 `meta_reg_for_var` **直读 g_opt_meta**(x86 实例输出布局);LOC_HOME_BASE=10⁶ + reg# 0..15 编码 seam 在内核 | 资源域未参数化——"内核不知平台"只兑现 95%(两处耦合:meta 读 + 位置编码 seam) | +| ③ 注册契约 | {能力声明、资源域/代数参数、请求分派}(双向——判定消费实例输出 + 判定结果回传) | 最小面已立(实例声明表 + 表驱动引导——步骤 2 Task 3);判定③④ 无实现 | 资源域代数参数缺失;双向输出面未形式化;③④ 无形式声明 | + +**用户裁决(2026-09-10)**: +1. ① 边界:**调度重建移实例**——loader 只产语义对象;线性调度重建 = x86 实例事务(蓝图 §1.2 "执行者才需要调度"归属兑现) +2. 判定③④:**形式化声明不实现**——实例能力声明(needs_eviction/needs_call_sites)承载,引擎不实现(静态放置无驱逐事件 + callee-saved 平凡满足——现状论证维持) + +--- + +## 1. 语义对象模型完备化(组件 ①) + +### 1.1 对象集定稿 + +内核持有的语义对象 = v7 载体内容的进程内形态: + +| 对象 | 内存载体(现) | 语义 | +|---|---|---| +| 条目 × 版本 | g_ir_entries(24B:var/def/LS/LE/home/flags——dyn_arr.cr:109-115) | 位置 + 配方引用 + 存在区间(闭区间,内核面) | +| 图节点 | NOD 记录(盘 36B → 内存对象视图:op/dest/s1-3/tk + 邻接索引) | 配方种类引用 + 操作数 | +| 图边 | g_v7_edges(8B {to,kind} 邻接——载入缓冲,零消费方) | 配方输入(数据 kind 0 / state kind 1)——节点出边 = 配方输入集 | +| 区域 | g_sgs(48B:kind/parent/enter/exit/nstart/ncount) | 嵌套作用域(图锚定) | + +**配方可读承诺(v7 载体设计的兑现)**:对象面必须支持「值 = (产生它的图节点, 输入边)」从文件内容直接读出——节点 → 出边遍历 → 边 → 目标节点。EDG 从"零消费缓冲"升为对象面的正式成员。 + +### 1.2 对象访问面 API(ent_kernel 扩展——内核接口面) + +- 节点面:`nod_op/nod_dest/nod_s1/nod_s2/nod_s3/nod_tk(n)` + `nod_edge_first/count(n)`(读邻接索引) +- 边面:`v7_edge_to(e)/v7_edge_kind(e)`(读 EDG 缓冲) +- 条目面:现有 `ent_*` 族(已 = 内核面) +- 区域面:现有 sg 访问器 + `region_of_nod(n)`(节点 → 所属区域——dataflow g_df_node_region 的 load 侧重建;v7 文件无此表——REG enter/exit 端点推导:**实施盘点项**——loader 是否已重建/需要重建节点→区域映射)→ **已决(2026-09-10 Task 0 盘点定夺:不含)**——零现消费方(corearch concat 内 g_sgs 唯一消费者 = loader 自身)+ 预计算表 = 无消费者内核数据(YAGNI)+ EDG 配方可读承诺不依赖区域;区域面 = 仅 REG 行遍历,按需后补推导可行(REG enter/exit 层流嵌套区间) +- 配方查询:`nod_inputs(n)` 语义 = 出边集合(邻接索引区间)——查询函数薄封装 + →(2026-09-10 Task 1 评审 M-1 注记)实现 = **文档化遍历模式**(ent_kernel.cr + :857-860 配方查询节注释:邻接索引区间 [first, first+count) 内逐边读 + v7_edge_to/kind——访问器齐备后遍历即查询,配方可读判据已实证);`nod_inputs` + 函数封装未交付 = 计划合规(实施计划禁止新增接口抽象)——封装 = 消费方按需 + (YAGNI)。→ **已交付(Task 4,2026-09-10——评审 M1 取代注记)**:`nod_inputs` + 于消费者出现时落地(dump_object_surface 收敛调用, ent_kernel.cr:1046——YAGNI 门开启如预期)。 +- 线性流 accessor(iri_*)**不再是内核对象面**——属实例调度产物(§1.3) + +### 1.3 调度重建移实例 + +- **loader 收敛**:load_ccr 的 NOD→g_ir_instrs 线性重建段移出——loader 产出 = 对象(NOD/EDG/ENT/REG 载入 + 校验守卫)。守卫(段表规范序/逐段越界/拓扑不变量/root span 上界/ENT 双写对照)留内核——守卫不依赖线性流存在(盘点确认:GC-3 上界校验消费 nod_count 而非 instr 流——实施 Task 0 核对) +- **实例侧新函数**:`build_linear_schedule()`(x86 实例)——从对象(NOD 文件序 + REG 展开)重建 g_ir_instrs(48B 线性流)——现 loader 重建段的函数体搬移;corearch 发射前置调用 +- **判据**:corearch 全链行为零变化(重建产物逐字节同——纯搬移);corec 写侧不受影响(写侧 g_ir_instrs = lower 产物,不经 loader) +- interp(corec run)直食 DF 节点流——不经 loader——零影响 + +--- + +## 2. 判定引擎中立化(组件 ② 资源域参数化) + +### 2.1 统一位置登记表(判定输入通道) + +- 内核持有位置登记表:{entry 序 → loc i32}(判定输入 = 实例分配结果的统一形态——实例经内核 API 写入) +- 内核 API:`kern_loc_assign(entry_idx, loc)` / `kern_loc_clear(entry_idx)` / `kern_loc_of(entry_idx)`(登记表访问——内核面新增) +- **verify 规则①② 读通道切换**:`meta_reg_for_var`(直读 g_opt_meta = 实例私有布局)→ 改读登记表。`meta_reg_for_var` 函数体 = 表查询改写(同语义——登记表由实例在分配后填充) +- **g_opt_meta 保留** = 实例 emit 面私有结构(instr.cr get_reg_for_var 消费——不动);实例(alloc_registers phase 5 + 注入钩子)写完 g_opt_meta 后调 kern API 登记/清除——实例动作 → 内核记录的契约方向(双向契约的产物侧) +- 双份数据注记:g_opt_meta(emit 面)+ 登记表(判定面)——同一分配结果的两种视图;同步责任 = 实例(写入点成对)。替代方案(判定直接消费 g_opt_meta 经布局描述)= 否决——布局描述 = 隐性耦合,登记表 = 显式契约 + +### 2.2 位置域声明化(**2026-09-10 修正——用户原则:内核不得有寄存器/域分类概念**) + +- **删除域参数方案**:原设计(kern_set_loc_domain + home_base/reg_domain 分类语义「loc < reg_domain = reg 类」)**废弃**——分类/编码合成 = 实例侧事务,内核不解释位置 +- **修正后形态**:位置 = **不透明整数**——内核只做相等、排序(sweep 门禁)、同值互斥;判定① = 两条独立同值互斥规则: + 1. **登记表 loc 相等**(实例分配输出的统一登记——reg 面;若实例选择把 home 也登记,合成发生在其登记动作内——内核只见不透明值) + 2. **条目 home 字段相等**(g_ir_entries.home = 语义对象字段——纯语义遍,独立于登记表;现状 home 恒 -1 = 无事件面,规则照旧) +- **LOC_HOME_BASE 完全不出现在内核**(现常量 :500 + 引用面 :590/:744 = Task 2 移除/改写——home 合成若需保留 = 实例侧(regalloc.cr 现有常量本就在实例文件)) +- reg_domain/home_base 实例声明字段(原 §3.1)= **从完备化范围删除**(无内核消费点——YAGNI;实例 emit 面如需域描述 = 实例私有数据,本就不在内核) + +### 2.3 注入钩子通道 + +- 实例侧注入钩子(try_inject_*:改分配结果制造判定红)——现直写 g_opt_meta——改为写 g_opt_meta(emit 面一致)+ 调 kern 登记(判定面一致)——注入 = 双面同步的实例测试通道 + +--- + +## 3. 注册契约完整化(组件 ③) + +### 3.1 实例声明扩展(g_instance_decl 行) + +| 字段(现) | 字段(新增) | 语义 | +|---|---|---| +| id/name/opt_min/opt_max/allow_table/allow_link/needs_alloc/needs_verify | — | 能力 + 引导(已有) | +| — | `needs_eviction i32` | 判定③ 形式声明(x86 = 0——静态放置无驱逐事件) | +| — | `needs_call_sites i32` | 判定④ 形式声明(x86 = 0——callee-saved 平凡满足) | + +(home_base/reg_domain 域字段 = 已从完备化范围删除——§2.2 修正:内核无域分类概念,实例侧域编码 = 实例私有数据) + +### 3.2 判定③④ 形式化(不实现——用户裁决) + +- needs_eviction/needs_call_sites = 实例能力声明字段;引擎**不实现**两判定(现状论证:③ 无事件面/④ 平凡真——蓝图注记引用);契约完整性 = 字段存在 + 文档(未来实例声明 = 1 时引擎扩展 = 注册契约演进点) + +### 3.3 双向输出面形式化 + +- 内核判定结果输出 API:`kern_verify_all()`(现 regalloc_verify_all——violations 计数)命名/位置确认 = 内核面;共存证据查询(entries_coexist 族 = 内核面既有) +- home 生产回填:**保持无**(分配决策 = 实例事务——蓝图后续注记维持);双向契约的"判定结果回传实例" = violations/证据查询面(实例自取),非内核写实例结构 + +--- + +## 4. 判据与中立性自检 + +1. **行为零变化**:全回归(compile/backend_bootstrap 链/hit_table/region_cfg/mw1-6/slice_bounds/live_ranges/ccr_v7/bootstrap 三套)+ byte-identical 面(backend_bootstrap stage 链 + ccr_v7 产物)全绿 +2. **判定通道判据**:test_live_ranges 13/13——判定红/绿路径经登记表通道输出与 g_opt_meta 直读时代逐字节同 +3. **中立性静态 guard**(新——防平台知识回渗内核):ent_kernel.cr 零实例符号引用断言——g_opt_meta/寄存器名(rbx/r12 等)/ELF/段名(NOD/EDG 除外语义名)——测试或脚本守卫,入回归面 +4. **对象面判据**:EDG 消费接口测试(配方可读——节点出边遍历断言——已知小程序的边集 = v7 Task 0 表一去幽灵后期望的复用) + +--- + +## 4.5 全量中立化审计(2026-09-10——用户原则:内核零经典概念, 全部中立化) + +严苛透镜逐函数审计结论(审计报告 = .superpowers/sdd 审计产出, 本节约 = 权威注记):全部 (C) 类集中于**一个根因族(两条通道)**,无清单外新类别: + +- **通道 A 线性流坐标派生与读取**(4 函数 4 个 iri_\* 使用点):compute_live_ranges :99 / compute_entries :227/:230/:233 / **rl_rule2_func :677-680(F1——判定② 读点扫描)** / dump_entries_summary :367(F2);F3 = verify/rl_rule2_func 段窗口表消费(g_ir_func_instr_* :711-714)+ 签名带实例流窗口参数(ist/ic/vs/vc);F4 = 双坐标域(live 表函数内 vs 条目全局——**挂账:内核自有表内部一致性, 非中立性阻塞**);**F5 = v7 index 对齐(NOD 节点序 = 指令序)→ 中立化 = 同 index 机械替换** +- **通道 B 位置通道**:meta_reg_for_var(返回 x86 寄存器号语义)+ LOC_HOME_BASE 合成(§2.1/2.2 修正方案) +- 文本层:rl_print_loc/rl_report 措辞 + 头注自证矛盾(:819-823 宣称 iri_\* 非对象面却 4 处直读——中立化后自洽) + +**写侧载体裁决(2026-09-10 用户授权——哲学最净 + 速度最快)**:corec 写侧(不经 loader)在 lower 后单遍 `populate_nod_objects()`(g_ir_instrs → g_v7_nod_sem 镜像——与 loader 解析对称)——compute 系单实现只读对象面;线性流退役时该点 = 现成写路径。F4 挂账。判定 = ENT 产物 byte-identical(同坐标同值)+ 全回归。 + +## 5. 与步骤 3 的关系 + +完备后:内核 = 范式无关三组件完整(对象面 + 判定经登记表/声明域服务任意实例 + 契约含资源域代数与③④ 形式声明);x86 实例化(步骤 3)= 纯实例内工作——帧/ABI/调用/tag 参数化 + HIT 表数据化推进,接缝 = 登记 API + 实例声明 + 对象面。规则封闭(新范式实例 = 新声明 + 新登记实现,零内核改动)获得实证基座。 + +## 6. 挂账注记 + +- 节点→区域映射(region_of_nod):v7 文件无 g_df_node_region 表——REG enter/exit 端点推导或实例重建期派生——**Task 0 盘点定夺 = 不含**(零现消费方 + YAGNI;区域面仅 REG 行遍历,按需后补推导可行——见 §1.2 注) +- 登记表与 g_opt_meta 双份同步责任 = 实例——写入点成对纪律(代码注记 + 注入钩子测试覆盖)——**实证(2026-09-10 Task 3/5:28/28 判定通道 byte-identical 语料 + test_live_ranges 红路径断言面——见 §7)** +- 判定③④ 引擎实现 = 注册契约演进点(needs_* = 1 的实例出现时)——不属本设计——**落点实证:needs_eviction/needs_call_sites 声明字段已落地(g_instance_decl 行扩展,行数据 {0,0}——见 §7)** +- F4 挂账(双坐标域:live 表函数内 vs 条目全局——内核自有表内部一致性,非中立性阻塞)——**随收官留挂(见 §4.5/§7——后续专项)** +- 中性化注释层残留(中立性原则达注释层——Task 4 review Minor 3)——**登记清单入 §7(只登记不入代码——措辞批范围克制,注释清理 = 未来 doc pass)** + +## 7. 执行注记(2026-09-10 Task 1-5 收官回填) + +> 本节约 = 设计落地确认(plan `2026-09-10-corearch-kernel-completion.md` Tasks 1-5)。 +> 提交落点:Task 1 = 035ea289(+ 补正 72c40389)/ Task 2 = 99fc6af3 / Task 3 = b4d8c74f / +> Task 4 = b9685f5c / Task 5(收官)= 本状态行随收官提交落盘。行为零变化判据(§4 判据 1-2) +> 与中立性 guard(§4 判据 3——tests/selfhost/test_ent_kernel_neutrality.py 入回归面;注: +> src/ci/run.sh 现仅跑四旧套件族,guard 与新内核套件由每批显式调用——CI 聚合层恢复时补入)全绿。 +> Task 4 终态:配方查询 nod_inputs 已交付(§1.2 M-1 注记取代——见 §1.2)。 + +### 7.1 §4.5 审计结论落地确认(A/B 通道清零——红→绿记录核) + +- **通道 A(线性流——4 函数 4 使用点)清零(Task 2)**:compute_live_ranges/compute_entries/ + rl_rule2_func/dump_entries_summary 的 iri_\* 4 使用点 → nod_\* 对象面同 index 机械替换 + (F5:v7 NOD 节点序 = 指令序 index-aligned)——guard A 组红(10 token:iri_op×3/iri_s1×3/ + iri_dest×2/iri_s2×2)→ **绿**。F3 随附(函数窗口 g_ir_func_instr_\* 值 = NOD 节点坐标语义 + 注记 + rl_rule2_func 签名 ist/ic → nst/nc 措辞中性化,值不变)。 +- **通道 B(位置通道)清零(Task 3)**:meta_reg_for_var 函数体等价改写 = 登记表查询 + (var 级驻留语义保持——调用点 rl_rule2_func + regalloc.cr 注入探针不动);LOC_HOME_BASE + 常量 + 引用面(rl_print_loc 分类打印/verify home 合成)移除 → 规则① = 登记表遍 + home + 字段遍两条独立同值互斥遍(双态条目不可达 = else-if 语义保持)——guard B 组红(11 token: + LOC_HOME_BASE×4/OPT_KEY_REG_ASSIGN×1/OPT_META_STRIDE×1/g_opt_meta×4/g_opt_meta_count×1) + → **绿**。 +- **文本层(Task 4)**:rl_print_loc 不透明 loc 直印(去 reg/home slot 分类措辞)+ rl_report + 前缀 "regalloc-consistency" → 检查域语义名 "entry-consistency"(spec 文件名引用保留于 + 判定区头注注释层);头注自证矛盾(宣称 iri_\* 非对象面却直读)随 A 通道中立化消除。 +- **终态(Task 5 收官复跑)**:guard A+B **全绿零残留**(`[PASS] neutrality A ... clean / + [PASS] neutrality B ... clean` rc=0)——无 ent_kernel 代码面残留,无回通道补漏发生。 + +### 7.2 写侧载体裁决实证(populate——Task 2) + +- corec 写侧 `populate_nod_objects()`(ccr_io.cr——lower 后、save_ccr 内 compute_live_ranges + 前单遍 g_ir_instrs 48B → g_v7_nod_sem 28B 镜像,字段序/宽度与 loader 解析逐字节对称)—— + compute 系单实现只读对象面(corec/corearch 双进程同值)。 +- 实证判据:test_ccr_v7 24/24(ENT 产物 byte-identical——Python 独立模型逐行断言 + loader + 负分支 9 测试)+ Task 2 冷编译语料 3/3 整文件 byte-identical(同源三程序 cmp)+ dump 通道 + 9/9 byte-identical(3 程序 × 3 通道)。g_v7_nod_sem 进程内留存 = 线性流退役时的现成写路径 + (裁决注兑现)。 + +### 7.3 登记表双份纪律实证(Task 3/5) + +- 登记 API = kern_loc_assign/kern_loc_clear/kern_loc_of(4B/槽,-1 = 未登记)+ 位置 = + 不透明整数零域分类(§2.2 修正执行——无 kern_set_loc_domain/无域参数);登记表代际清理 = + loc_registry_reset 挂 compute_entries func_i==0 整代重建点(内核数据面自身不变量承担, + 实例配对只负责与 g_opt_meta 同步)。 +- 实例写点成对实证 = phase 5 尾逐函数登记 + meta_set_reg/meta_remove_var 辅助 + (reg_assign_var_entries/reg_clear_var_entries——全条目登记/清除);注入钩子 + try_inject_read_gap 经登记表等价读(判定与探针单真源)。 +- 实证判据:判定通道输出逐字节同 28/28 对(4 程序 × 7 通道,含注入红路径 "loc" 违规行—— + Task 3 基线/对照语料 .superpowers/sdd/kc-task-3-base|post)+ test_live_ranges 13/13 + (check_regalloc_violations 规则 1/1/2 红 + read_gap_nonfunc0 + 绿路径全过 + watchdog 93 + pairs)+ test_backend_bootstrap 11/11 O2 stage 链 byte-identical(--check-regalloc 红/绿 + 路径经登记通道)。 + +### 7.4 ③④ 声明落点(Task 4) + +- needs_eviction/needs_call_sites 声明字段 = g_instance_decl 行扩展(INST_DECL_STRIDE + 32→40 + INST_OFF_NEEDS_EVICTION=32/CALL_SITES=36 + 访问器 inst_needs_eviction/ + inst_needs_call_sites + instance_decl_add 签名扩展——corearch.cr);行数据 x86/表路径 + = {0, 0}(表路径无驱逐/调用点事件——恒 O0 直线)。引擎不实现(用户裁决——§3.2 引用: + 静态放置无驱逐事件 + callee-saved 平凡满足);契约完整性 = 字段存在 + 判定区头注文档 + (③④ TODO 尾追加落点注);kern_verify_all(现名 regalloc_verify_all)输出面注记 + §3.3 + 双向输出面确认。needs_* = 1 的未来实例出现时 = 注册契约演进点(§6 挂账维持)。 + +### 7.5 F4 挂账 + region_of_nod 已决 + 注释层残留清单(Task 5 收官) + +- **F4 挂账(维持)**:双坐标域(live 表函数内窗口坐标 vs 条目全局坐标)——内核自有表内部 + 一致性,非中立性阻塞;统一 = 后续专项(不属本计划范围克制)。 +- **region_of_nod 已决**:对象面 API 不含(Task 0 盘点——零消费方 + YAGNI)——区域面仅 REG + 行遍历,按需后补推导可行(见 §1.2/§6)。 +- **注释层残留清单(Task 4 review Minor 3 登记化——中立性原则达注释层)**:guard 剥注释 + 后不拦(代码面 = 中立性已证),注释层寄存器时代术语 ~10 主簇(终态扫描,行号 = 2026-09-10 + 收官日锚点,ent_kernel.cr)——**只登记不入代码**(措辞批范围克制;注释清理 = 未来 doc + pass,本清单 = 工作集): + + 1. 头注 :3-8 —— 来源句「自 regalloc.cr 逐函数纯搬」+ 中立声明反证句「无寄存器名/ABI + 常量…资源域(寄存器/槽的代数)= 实例侧声明」+ 实例侧行「机器侧(x86 实例)= + regalloc.cr:CAG alloc_registers + g_opt_meta」 + 2. 头注 :11-14 —— B 通道节「meta_reg_for_var…调用点 = rl_rule2_func + regalloc.cr 注入 + 探针」「instr.cr get_reg_for_var 消费」 + 3. 头注 :32-35/:39 —— 实例侧行复述 +「x86 实例 + 表模式路径声明」 + 4. 数据面节 :54-57/:100/:106/:153 ——「alloc_registers 改读本表——与原内联 iv_buf 构建 + 逻辑…」「同 alloc_registers 的 iv_buf 初始化语义」「live_range_slot 前缀累计」 + 「alloc_registers(O2)两处被调」 + 5. 访问器节 :206-209 ——「不能调 r32:Python bootstrap 的 StackAsmGen 把名为 r32 的调用 + 内联成 `mov eax,[rdi+rsi]`」「w32 内联为 `mov [rdi+rsi],edx`」(x86 助记符背景注) + 6. :328-329/:538-539 —— 「自 regalloc.cr 迁入——机器侧零引用」来源注(2 函数位) + 7. 判定区头注 :514-521 —— 「实例侧 g_opt_meta 的 OPT_KEY_REG_ASSIGN 对(var_idx → + 位置——x86 寄存器号;instr.cr g2_slot/get_reg_for_var 发射时按对落寄存器 = emit + 面)」「元数据表 + 后端 g2_slot 查询」「alloc_registers(CAG 升级后仍)是变量级」 + 8. :525-530 —— 规则① 表述注「同寄存器两 var 的窗口相交」+ ③④ TODO 帧「静态放置失败 = + 栈驻留」「spill/调用点解锁」「callee-saved」 + 9. :782/:953 —— 规则② 框架句「寄存器驻留变量的读点必须有活跃版本覆盖」(判定框架措辞 + ——中立面表述 =「已登记位置驻留变量」,最实质清理候选) + 10. :890/:967/:554-555 ——「统一登记——现 = 寄存器面」「regalloc_verify_all = x86 时代 + 历史名保留」「旧名 g_loc_reg_buf 的 reg 缩写 = 寄存器时代措辞…改全词 registry」 + (自指/历史名记录注) + + 分类:类 A = 实例分裂面/历史名/来源注(保留信息必需——doc pass 措辞整饰候选:簇 1-4/ + 6-7/10);类 B = 内核自身描述措辞(清理候选:簇 5 背景注措辞化、簇 8 ③④ TODO 帧随 + 实例化推进消除、簇 9 规则框架句中立措辞改写)。 diff --git a/docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md b/docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md new file mode 100644 index 00000000..062e7c3f --- /dev/null +++ b/docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md @@ -0,0 +1,97 @@ +# 语言面收窄与接口化交叉执行(定长退役裁决 + 宽度清理 + 依赖序) + +日期:2026-09-10 +状态:**已实施(R1)**——波 R1 语言面收窄批(计划 `docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md`)Task 1-5 于 2026-09-10 收官:定长裁决注记落位、宽度三项清理完成、全局定长/初始化路径修复;全回归 + 自举守护全绿。R2(接口化主设计轮)后启。 +性质:前端/语言面战线(根基革命 A:图本体/类型→接口)的启动裁决 + 与既有战线的交叉执行序。**本 spec 只裁方向与顺序,实施计划后立。** + +关联: +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(类型方向定案——条目 7 宽度移出语言 / 条目 8 类型标注自动(数组/切片 = 序列接口 + 元素接口 + 长度值约束);本 spec 为其条目 7/8 的执行裁决) +- `docs/superpowers/specs/2026-09-10-x86-instance-design.md`(实例化——波 2/3 与本 spec 的映射侧交叉) +- `TODO.md` width-out-of-language 条目(语言侧清理三项) +- `TODO.md` 边界判定图 pass(F11 长度来源链——长度语义归处的现状基础) + +--- + +## 1. 定长数组裁决(2026-09-10 用户拍板) + +### 1.1 裁决 + +**`[T; N]` 退役类型构造器身份;保留为「内联容量存储」表示提示**(A+B 融合): + +- **类型系统层**:`[T; N]` 不再作为类型构造器——类型相等/子类型判定不以其为成员概念;语义面归处: + - N 元聚合 → **product 类型**(条目 8:元组/记录 = product 自动组合) + - N 长序列 → **序列接口 + 长度值约束**(条目 8:F11 长度走 where 三档) + - 长度入型做验证 → **图/F11 长度来源链**(比字面量构造器强——可表达依赖长度) +- **表示层**:`[T; N]` 语法保留 = **内联容量存储的表示提示**(映射参数——与 hw-map 同层;随实例选择生效/退化;非经典范式实例可忽略或另定——"内联"在量子/光学映射中未定义是正常的) +- **自举代码**:`ast.cr` 9 处 + `kernel/term_io.cr` 1 处用法零改造(它们本就是表示提示的正当消费——编译器需内联存储) + +### 1.2 裁决依据(用途拆解) + +| 用途 | 语义本质 | 归处 | 需要构造器身份? | +|---|---|---|---| +| N 元聚合(矩阵/坐标/记录) | product | 条目 8 product 自动组合 | 否 | +| N 长序列 + 索引 | 序列 + 长度 | 条目 8 序列接口 + where | 否 | +| 长度入型做验证 | 长度事实 | F11/图(更强——字面量 N 表达力不够) | 否 | +| 内联存储/免分配/布局 | **经典映射性能关切** | 映射实例(表示层) | **这是真需求——但不属语义层** | +| 语法便利 | 糖 | 表示提示 | 否 | + +关键判据(与宽度原则同构):内联存储对经典机越有用、对非经典范式越无意义——正因此它不得升格为语言语义(机器形状出语言的既有原则)。 + +### 1.3 连带处置 + +- **全局定长数组 SIGSEGV**(实测:check 过/build rc=0/run rc=139——含解释器路径):过渡期处置 = 按表示层语义修或先编译期拒绝;**修复归属 = 实施批**(见 §3 波 R1)。**R1 落地形态(2026-09-10 实测,与本节初稿括注不同)**:运行期分配持久存储 + 全局槽持指针(与局部数组/bootstrap 参考实现同语义);**「内联 N 格」表示未兑现**——内联容量 = 映射参数层的表示提示,属 R3 表示提示机制(对非经典范式可退化),故 R1 不建该机制 + - **✅ R1 已处置(选「按表示语义修」)**:全局(聚合 / 非常量初值)降级为运行期初始化序列——main 序言 IR 注入 + 解释器初始化阶段;落点 71cb6278;判据 `tests/selfhost/test_global_init.py` 15 例双路径(解释器 / ELF)逐例精确 rc。非编译期拒绝(保留语义,符合「表示提示」裁决)。 +- **checker 落实时点**:`[T; N]` 从类型判定中降格 = **接口化轮(R2)重做 checker 类型判定时自然完成**(届时不经类型相等/子类型);R1 只做:裁决注记(ast.cr/checker 加「表示层概念」标注)+ 全局路径处置 + 语法面文档 + - **✅ R1 三项均已做**:注记 aa76f7f5(ast/checker/parser 七处,零行为)/ 全局路径 71cb6278 / 语法面文档(本 spec + grammar 同步 51d74be3) +- **表示提示机制**(提示如何被映射层消费) = 映射侧(R3/hw-map 通道)设计时定——R1 不建机制(现状语法行为不动) + +--- + +## 2. 宽度语言侧清理(条目 7 执行项) + +| 项 | 内容 | 裁决 | 结果 | +|---|---|---|---| +| 死条目移除 | `ast.cr` `T_INT_I8..T_INT_U64`(77-84)+ `T_FLOAT_F32/F64`(85-86)——**勿重编号**(编号空间稳定) | R1 直接做(机械) | ✅ ce3e541c——ast.cr 墓碑注释(勿重编号)+ `W_*` 值域 + parser 两处死分支(lexer 从不发射,引用面归零) | +| `_f32/_f64` 后缀 | 实况:`parser.cr:414` 注「data = 宽度标注(_f32/_f64,**保留**)」——后缀现作为宽度标注随字面量存 | **R1 侦查后裁**:若承载 apx/binary64 精度语义(语言语义)→ 保留并重命名注解(精度非宽度);若纯机器宽度 → 随死条目清(或转 R3 映射通道标注) | ✅ 裁为**纯机器宽度 → 退役**(51d74be3):lexer 不再消费后缀、一律响亮报错;宽度 = 机器形状,归映射层(parser 宽度分支随死条目一并清)。侦查结论:后缀位宽信息在词法层即被丢弃(`num_str` 已剔除),data 槽恒 0——无语言语义承载 | +| `1_000` 分歧 | bootstrap vs self-hosted 两编译器解析分歧复核 | R1 做(词法对照测试) | ✅ 51d74be3——`_` 分隔符**不支持**,两编译器统一响亮报错(原双向皆错:self-hosted 静默成值 0 / bootstrap 静默成 1000);新增 `tests/selfhost/test_lexer_parity.py` | + +--- + +## 3. 交叉执行序(前置依赖) + +``` +波 R1:语言面收窄批(可立即做——纯清理 + 小侦查 + 一处处置)——**✅ 已完成(2026-09-10,Task 1-5 收官)** + ① 定长裁决注记(ast/checker「表示层概念」标注——零行为改动)→ ✅ aa76f7f5(ast/checker/parser 七处,零行为) + ② 全局定长数组 SIGSEGV 处置(按表示语义修 或 过渡期编译期拒绝)→ ✅ 按表示语义修(71cb6278) + ③ 宽度死条目移除 + `1_000` 分歧复核 + `_f32/_f64` 后缀侦查裁决 → ✅ ce3e541c(死条目)+ 51d74be3(词法面收窄) + 判据:全回归 + 自举 + 词法/语法面测试(行为零变化除外——全局定长修复是行为变化,单独标注) + → ✅ Task 5 收官(2026-09-10):11 套回归全绿 + 自举重建 BUILD SUCCESS / 三处 [GUARD] OK / + full-bootstrap 链 cmp IDENTICAL + N06=0 + 冒烟 rc=42 + 行为变化(三处——两处响亮报错 + 一处修复,均为本批「收窄」意图的直接后果): + 1. `1_000`(`_` 分隔符):不再接受,bootstrap / self-hosted 两编译器**统一响亮报错** + (原双向皆错:self-hosted 静默消费 → `x := 1_000` 得 0 的实质误编译面; + bootstrap 静默接受 → 1000) + 2. 宽度后缀(`_i32`/`_u64`/`_f32`/`_f64` 等):**退役 + 一律响亮报错** + (原被 lexer 静默消费、data 槽恒 0,parser 分支为死代码) + 3. 全局初始化:文件级全局(聚合 `[T; N]` / 非常量初值)两路径语义一致—— + 原 `[T; N]` 全局 run rc=139(SIGSEGV)或静默 0;现 `main` 序言注入 + 解释器 + 初始化阶段双路径逐例判据(15 例) +波 R2:接口化主设计轮(前置:R1 完成) + 类型 = 图标注 / 接口注册表(int/dex/string 原生条目)/ 检查器删特判统一查表 + / 数组 = 序列接口 + 长度 where / 泛型 = 编译期接口具体化 + —— 定长「类型身份退役」在此落实(checker 重做时自然剔除) +波 R3:映射侧(与 x86 实例化波 2/3 合批) + hw-map 宽度显式通道 + 表示提示机制(内联容量存储的映射消费)+ 实例参数化 +``` + +交叉点(已核):R1③ 的词法面与 R2 的 checker 重做同文件族(parser/ast);R2 的接口条目在 R3 的映射实例上实现;R3 与实例化波 2 是同一件事(编码层参数化)两个名字。 + +## 4. 挂账与不承诺 + +- R1 不做:表示提示机制(R3)、checker 类型判定重构(R2)、F11/where 落地(R2) +- 全局定长数组修复若改为「编译期拒绝」先行 = 过渡措施,R2/R3 后恢复完整语义 + - **R1 实际选择 = 按表示语义修**(非编译期拒绝)——不产生过渡态;R2/R3 只需在 checker/映射侧接续(已记 TODO「全局初始化机制」条目:未来非 main 入口 / 库形态需迁移到独立 init 区) +- 本 spec 不裁 `_f32/_f64` 最终归属(R1 侦查后裁) + - **✅ R1 已裁**:退役 + 响亮报错(51d74be3)——后缀无语言语义承载(词法层即丢弃);未来若需精度/位宽标注,走 R3 映射通道 +- TODO 登记:定长裁决(随 R1 提交记 TODO 划销/挂账)、宽度清理三项(随 R1 完成划销) + - **✅ 已办(Task 5)**:宽度三项划销 + 定长注记挂账(类型身份退役 = R2)+ 新登记(全局初始化机制 / lexer 诊断前缀不一致 / 枚举族 callee 内联缺口 / 嵌套类型别名一层解析 / test_compile 清单陈旧) diff --git a/docs/superpowers/specs/2026-09-10-type-interface-unification-design.md b/docs/superpowers/specs/2026-09-10-type-interface-unification-design.md new file mode 100644 index 00000000..ef74eed7 --- /dev/null +++ b/docs/superpowers/specs/2026-09-10-type-interface-unification-design.md @@ -0,0 +1,332 @@ +# 接口化主设计轮(R2):类型 = 图标注 + 双轴接口注册表 + 语义子类型引擎 + +日期:2026-09-10 +状态:**设计定稿**(四节逐节获用户确认)——实施分期(P0→P5),本 spec 含载体设计(.ccr TYPE/IFACE 段)。 +性质:前端/语言面战线(根基革命 A:图本体/类型→接口)的**主设计轮**。本 spec 定语义基座、注册表模型、类型项层与引擎、检查器接线与迁移、能力落点、载体、诊断、判据与分期。**实施计划后立(writing-plans)。** + +关联: +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(14 条定案——本 spec 是其「类型概念收敛」第一次实施轮) +- `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md`(R1——定长裁决与宽度清理;本 spec §5.1 是其「类型身份退役在 R2 落实」的兑现) +- `docs/superpowers/specs/2026-09-07-lattice-ir-v7-format.md`(载体格式——本 spec §6 的类型/接口段为其扩展) +- `src/compiler/checker.cr`(现状类型判定)+ `src/compiler/monomorph.cr`(泛型)+ `src/compiler/ccr_io.cr`(载体) + +--- + +## 0. 本轮裁决(用户拍板 2026-09-10,六项) + +| # | 议题 | 裁决 | +|---|---|---| +| 1 | 产出形态 | **全量单 spec 一次成型**(设计一次看全局限;实施分期) | +| 2 | 注册表模型 | **双轴:本质条目 + 横切接口**(类型可同时满足多接口;与定案 8 的「序列接口 + 元素接口」一致) | +| 3 | 改造面 | **设计含载体**(.ccr 类型/接口段 + DFNode.TK 升格进设计);**实施分期**(载体另批) | +| 4 | 引擎基准 | **全量语义子类型引擎本轮实施**(union/intersection/negation + 递归类型 + 穷尽性一次到位) | +| 5 | 用户 interface/impl | **接管且重定义语义**(图上契约;mangling 路径废弃;旧语义不兼容窗口) | +| 6 | 收紧处置 | **改源码 + 逐处记录**(旧接受→新拒绝逐条留档;语义争议停下上报) | + +路线(用户确认):**路线 1(类型项层 + 语义包含引擎 + 分层替换)为路径 + 路线 2(位集合)作穷尽性快路径 + 路线 3(大步删除)作收尾**。 + +--- + +## 1. 语义基座 + +### 1.1 语义解释 + +- 类型项 τ 的语义 = 值集合 ⟦τ⟧;子类型 `A <: B` ⟺ ⟦A⟧ ⊆ ⟦B⟧ +- 等价 = 双向包含;不相交 = ⟦A⟧ ∩ ⟦B⟧ = ∅ +- **穷尽性 = 补集空性**:模式集 P 对域 D 穷尽 ⟺ ⟦⋃P⟧ ⊇ ⟦D⟧(判定实现为 `¬(⋃P) ∩ D` 的可空性) + +### 1.2 类型项语法(内部表示) + +``` +τ ::= ⊥ | ⊤ | ⊤ₖ | τ₁ ∪ τ₂ | τ₁ ∩ τ₂ | ¬τ | μX.τ | X | k(τ₁…τₙ) +``` + +- `⊥` = 空集;`⊤` = 全集;`⊤ₖ` = 原子类 k 内的全集(如「任意 int 值」) +- `k` = 原子构造子: + - **原生八员**:`int`(数学整数,无上限)`dex`(定点/指数族)`string`(字节序列)`bool` `unit` `never` `char` `dyn` + - **结构构造子**:`product`(tuple / struct / 记录)、`sum`(enum / variant)、`sequence`(数组 / 切片)、`ref`、`ptr`、`fn` + - **参数化命名**:`named<τ…>`(用户类型 + 泛型实参) +- `μX.τ` 绑定的递归 = 注册表引用图上的**环**(定案 10:无 `rec` 关键字,环检测自动;值递归自动拒绝 = 无限大小 → 编译错误,唯一必须存在的规则) + +### 1.3 规范化与判定输入 + +- **DNF 规范化**:类型项规范化到 union of products(product = 原子 + 否定原子集合);`⊤ₖ`/`⊥` 为规则内建项 +- 判定算法输入 = 规范化形态;规范化结果按结构哈希缓存(见 §3.3) +- **声明式边界(定案 14)**:本层只碰声明式上限(类型/契约);执行/副作用/内存的命令式下限(state edges / region / 调度)不动 + +--- + +## 2. 双轴接口注册表 + +### 2.1 本质轴(每原子构造子一条目) + +条目字段: + +| 字段 | 内容 | +|---|---| +| kind id | 原子构造子标识(与 `TYP_*`/`TI_*` 的对应见 §2.4) | +| 名字 / 语义域 | 如 `sequence` = 长度 + 元素集合;`int` = 数学整数 | +| 良构性规则 | 构造合法性(如 `sequence<τ>` 需元素项;`product` 需字段表;递归环可接受) | +| **操作许可** | 哪些 op 可作用(如 `+` 对 `int`/`dex`/`string` 合法)——**就地替代现状 `infer_expr:1397-1441` 的内联公理区** | +| 字面量定型 | 字面量 → 类型项(替掉 `infer_expr:1371-1376` 的内联 if 链) | +| 尺寸 / 对齐 | **标注为映射层 / hw-map 领域**(非语义;语义层不承诺布局) | + +- **原生条目**(公理内建、用户不可实现):`int` `dex` `string` `bool` `unit` `never` `char` `dyn` +- 用户类型(struct/enum/…)的条目 = 结构构造子的实例化(其「本质」由 `product`/`sum` 条目 + 其字段/变体项给出) + +### 2.2 横切轴(结构自动判定,用户不声明) + +- 每条横切接口 = **一个形状类型项**:满足判定 = 一条包含判定 `T <: 形状`(与引擎同一套判定,不另建规则体系) +- 初始条目:`序列接口` ⟺ `sequence<⊤>`(**待细化**为只读/可写两形状,见 §5.1 变型)|`product` ⟺ `product<⊤…>`|`可比较`|`可索引`|`可迭代` +- 只读/可写分野:只读视图**协变**、可写视图**不变**(变型规则落地点) + +### 2.3 用户轴 + +- `interface I { … }` → 形状类型项:方法集 = `product` of `fn` 原子(方法签名各自为 `fn(τ₁…τₙ)->τ` 项) +- `impl I for T` → 图上「`T <: I`」的**满足关系**:入注册表 + 图上边 +- 满足判定统一入口 `iface_satisfies(T, I)`(原生 / 横切 / 用户三类同入口) +- **mangling 废弃**:方法解析从「`Type.method` 字符串拼接查找」(现状 `find_func`/`type_has_method`)改为「类型项 + 接口条目」查表 +- 旧语义不兼容窗口:**无**(用户裁决 5)——`impl` 语义按本节重定义,旧 mangling 行为不保留 + +### 2.4 与既有表的衔接 + +- **本质轴条目** ↔ 现有 `g_types`(24B/条 `kind/data/extra`):`g_types` 保留为**原子条目层**(原子实例 + 用户类型 + 参数化模板头);新增语义域/操作许可/字面量规则字段(扩展条目或旁挂规则表,实施批定布局) +- **横切/用户轴** ↔ 现 `g_ifaces`(1432B/条:name + 16 方法 × 88B)+ `g_impl_for`(16B:iface_ni, type_ni):**升级**为「条目(形状类型项引用)+ 满足关系」,上限(16 方法 / 8 参数)随升级**解除或显式登记** +- **编号撞车清理(必办)**:现状 `TYP_GENERIC_PARAM=7 == TY_GENERIC_PARAM=7 == TI_DYN=7`、`TYP_GENERIC_APPLY=8 == TY_DEX_S=8`、`TYP_NAMED=1 == TY_DEX=1` 为纯数字撞车(`checker.cr:379` 靠巧合成立)——本轮**按命名空间分家**(`TYP_*` = 条目 kind / `TY_*` = 基类型码 / `TI_*` = 行号,或合并为单空间;实施批择一并记录) + +### 2.5 统一查询 API(checker 唯一入口) + +``` +iface_kind_of(ti) // 行号 → 原子 kind +iface_ops(kind) // 操作许可集(替 72 处 == TI_* 直比) +iface_satisfies(T, I) // 原生/横切/用户三类统一 +iface_size(ti) / iface_align(ti) // 映射层标注(语义层不消费) +iface_of_term(τ) // 类型项 → 其本质 kind(若非单一原子 → 分解/⊥/⊤) +``` + +--- + +## 3. 类型项层与判定引擎 + +### 3.1 存储(分层) + +- `g_types`(24B/条)= **原子条目层**(保留) +- 新增 `g_type_terms` = **类型项表**:定长条目 `{tag, a, b, c, d}` + - `tag ∈ {BOT, TOP, TOP_K, UNION, INTER, NOT, MU, ATOM, VAR}` + - `a..d` = 子项索引;`ATOM` 的 `a` = 原子条目行号(+ 参数项索引) + - 类型项构成 **DAG**(共享子项 + 结构哈希去重 → 同一化免费) + +### 3.2 判定 API 与算法 + +``` +ty_sub(a, b) -> int // 包含 +ty_equiv(a, b) -> int // 双向包含 +ty_disjoint(a, b) -> int +ty_inhabited(a) -> int // 可空性(穷尽性/反例用) +ty_witness(a, b) -> term // 反例值(判定失败时给具体值示例) +``` + +- **算法**:DNF 规范化(union of products;product = 原子 + 否定原子集)→ 结构对照(product 覆盖判定 + **同类原子分解**:仅同类原子需展开子项)→ `μ` 展开用**假设-判定 memo**(递归判定标准做法) +- 判定失败时输出**非空反例值**(`ty_witness`):如 `[int;2] <: [int;3]` 的反例 = 「长度为 2 的序列」 + +### 3.3 性能与诚实 + +- **记忆化**:`(a,b) → 结果` 判定缓存;规范化结果按结构哈希缓存 +- **位集合快路径(路线 2)**:有界原子宇宙(枚举变体集 / 可选集 / 穷尽性)用位运算(先例:`g_dyn_type_sets` 64 位图;**须解除或显式登记 64 上限**) +- **判定步数预算守卫**:预算超限 → 显式诊断(见 §7),并登记「未覆盖面」——**不与「类型不兼容」混淆** + +### 3.4 图标注承载(迁移期双槽 → 收尾单槽) + +- **迁移期**:`irv_type`(原子 ti;后端/解释器读,**语义不变、零风险**)+ 新增 `irv_type_term`(完整类型项;前端/验证/载体读) +- **收尾**:单槽 = 类型项引用 + `atom_of(term)` 取原子(后端改动面 ≈ 20 处比较点,收尾批一并改) +- 理由:引擎未对拍完之前**不扰动后端**(后端现在只认 `TI_INT/TI_DEX/TI_STR` 基类型常量) + +**收尾落地(R2 P5 Task 2,2026-09-13;✅ 已实施)**:单槽 = **类型面单源 + 辅码显式分离**(**非**「字段减一」——`tk` 是混用槽,同槽承载旗标/宽度等非类型信息,物理上不可减): + +- 布局(`dyn_arr.cr`):`OFF_DF_TK`(40) 语义改为**类型项引用**(`g_type_terms` 行号;-1 = 无项)= **类型面唯一真源**;原 `OFF_DF_TK_TERM`(64) 更名 **`OFF_DF_AUX`** = 辅码(旗标/宽度/计数/不可逆行原码;0 = 无);`ESZ_DFNODE` 保持 72B ⇒ **布局与版本位零改动**(`CCR_VERSION=8` · `CIR_CACHE_VER=17` · NOD 36B · `.cir` 快照 64B/节点)。 +- **码 = 派生量**:`iri_tk` / `.ccr` NOD / `.cir` 快照一律 `sh_dfn_code_of_slots(项, 辅码)`(emit / `lower_to_ccr` / 装载侧**单源**)。**分类表**(`ty_shadow.cr`:`sh_tk_is_type_face` / `sh_tk_face_of_code` / `sh_tk_split(_load)` / `sh_atom_of_term`)= 唯一分类真源:类型行面 = `{IR_CONST, IR_BINARY}` ∪ P4 六排除项再裁 `{IR_ALLOC, IR_CALL, IR_LOAD, IR_I2F, IR_F2I}`(P4 排除理由「后端不按 ti 分派」= **发射面**判据,不适用于**语义**类型面);辅码面 = `{IR_BOUNDS_CHECK, IR_DEREF, IR_STORE_PTR, IR_SPAWN, IR_HOTPATCH_ROUTE}`;其余无面。 +- **可逆行判据**:分类表只覆盖**原子行**(`atom_of(项) == 行号`);**复合行**(ptr/ref/array/slice/optional/null/tuple)**不建项**(`term=-1`,不近似),原码进**辅码槽保真** ⇒ 派生码逐字节 ≡ 旧混用码。 +- **两条不变量**(D22):① 派生码 ≡ 旧值(94 档 338,207 节点逐节点 diff,仅 F1 的 3 节点 = 已裁决偏差);② 两槽互斥。 +- **D23 闸**:类型面行不可译 ⇒ `ccr_seg_prepare_save` 拒绝落盘(rc=1 + 诊断);**仅 emit 路径**计数(暖进程类型表可小于冷态 ⇒ 装载侧重派生不计数,缓存命中绝不误拒)。 +- **稳定引用形态(D20 规范,立即生效)**:本批**不引入任何跨进程项引用**——凡跨边界者一律派生码;未来需要时唯一许可形态 = **TYPE 段文件内项索引**;**禁令**:进程内项表索引不得跨进程/跨复位边界(`bad_term=329` 红证)。 +- **β(盘面项索引 + corearch `atom_of` 读回)推迟 P6**(P5 计划裁-1:β = 新能力而非收尾,无在野消费者);**登记**:缓存命中跳过 `ir_gen` ⇒ 暖态类型/项表可**小于**冷态,越出暖表处项不可重建(`term=-1`)——派生码面/发射面/盘面零影响,根治 = β。 +- 判据(P5 T2/T7 实跑):`selftest-types` +12 例(终值 404/404)· `test_ccr_types` 34→**40** · ELF canary IDENTICAL · `.ccr` 两套口径四条与 P4 记录逐字节同 · `--dump-objects` 逐字节同(`generics_test` 两口径 3521 行;P4/T2 期的 2427 行为当时语料与口径)· 突变控制 4 条(T2)+ 2 条(T7 网复核)。详见 `plans/2026-09-13-r2-p5-cleanup.md` Task 2/7。 + +--- + +## 4. 检查器接线与迁移(四步,每步带判据) + +| 步 | 内容 | 判据 | +|---|---|---| +| 1 建层 | 类型项表 + 引擎 + 查询 API(新文件 `src/compiler/type_engine.cr`)+ **判定用例表**;checker 不动 | 用例表全绿;零产物变化 | +| 2 旁挂对拍 | 旧判定与 `ty_sub` 并行(影子模式)→ 差异清单(输入 + 两结果 + 分类) | 差异**逐条裁决**;裁决记录留档 | +| 3 逐点替换 | `type_equal` → `ty_equiv`(保留同一原子快路径);`infer_expr` 公理区 → `iface_ops`;索引/字段/match/转换 → 引擎 + 横切接口;`res_type_node` 的**双份 TY→TI 映射合一**(`checker.cr:367-380` 与 `:886-893`) | 全套件 + 自举 byte-identical + N06=0 + 差异清单归零 | +| 4 收尾(路线 3) | 删旧分支 / 旧映射 / 双槽,单源化 | 同上 + 单槽化 | + +- **每步判据(全步共同)**:27 selfhost + 3 bootstrap 套件全绿;`corec2/corec3` `cmp` IDENTICAL + N06=0;冒烟 rc=42 +- **收紧处置**(裁决 6):既有代码在新语义下不合法 → 改源码 + **逐处记录**(文件/用例/旧判定/新判定/为何);语义争议 → **停下上报** + +--- + +## 5. 能力落点 + +### 5.1 定长退役(R1 裁决的兑现) + +- `[T; N]` **不再是原子构造子实例**;语法保留为**表示提示**(映射参数层,携带 N 供实例消费——`type_size`/`arr_len_lit_of` 继续读它,**语义判定不看**) +- 类型呈现 = `sequence` + **长度约束项**,档位按 F11 三档: + - 常量档(N 字面量)→ 判定编译期定(恒真/恒假) + - 符号档 → VC 义务(验证管线消费) + - 动态档 → 运行时检查 +- 子类型:`[T;N] <: [T]`(长度约束更强者为子类型);**变型**:只读序列视图协变、可写视图不变 +- 改造点(现状消费面,侦查已定位):`checker.cr:110-119`(身份判等分支)**删除**;`:2080-2089`(F2 字面量越界)改读长度约束;`:2062-2075`(F11 切片界)升级为约束项;`ir_gen.cr:120-135`(`arr_len_lit_of`)与 `:476-480`(`type_size`)**保留**(表示提示层) +- 自举源码 10 处 `[T; N]` 用法**零改造**(R1 已注记;表示提示语义不变) + +### 5.2 泛型 = 编译期接口具体化 + +- 声明:`fn f[T]`(约束 = `⊤`)/ `fn f[T: I]`(约束 = 类型项 `I`);**结构/枚举泛型补上约束**(现状仅函数泛型有 `g_generic_constr`) +- 实例化检查 = `ty_sub(实参, 约束)`;失败给非空反例 +- monomorph 升级:类型参数以**类型项**传递(现状 `gen_build_subst` 的**纯文本字符串替换** → 类型项替换);实例去重 = 类型项**结构哈希**(现状 `istr_eq` 字符串相等去重,脆弱) +- 无运行期字典(定案 6) + +### 5.3 impl/interface 重定义(用户轴) + +- `interface I` = 形状类型项;`impl I for T` = 图上满足关系;满足判定走引擎 +- mangling 废弃(`type_has_method` 从名字查找 → 表查询);变型规则记条目内 +- 现状**零测试覆盖**(`test_impl.py` 5 例全为固有方法、`interface` 零命中)→ **新建覆盖集**(impl-for 正向/反向/多接口/泛型约束交互/错误例) + +### 5.4 穷尽性 + 联合/可选 + +- `match` 穷尽性 = 补集空性(`EC_TM_EXHAUST`:从「定义了从不抛」→ **真判定**) +- enum = `sum` 类型项(变体 × payload product);`T?`/Option = `T ∪ null`(**不再依赖内建 Option 枚举名**——现状 `checker.cr:591-599` 自动注册内建枚举的做法退役) +- 反例给**具体值**;有界变体集走位集合快路径 + +--- + +## 6. 载体(设计全、实施分期) + +### 6.1 `.ccr` 新增段(v7 格式扩展) + +| 段 | 内容 | +|---|---| +| `TYPE` | 类型项表序列化(DAG:tag/a..d)+ 原子条目扩展(kind/data/extra + 语义域标注 + 用户类型名) | +| `IFACE` | 条目(原生 / 横切 / 用户)+ 满足关系(impl 边) | + +- 段号 / 条目布局 / 版本 bump 策略对齐 `docs/superpowers/specs/2026-09-07-lattice-ir-v7-format.md`;**v7-only,不设兼容层**(先例:v6→v7 同日切换) +- 现状缺口(侦查):`ccr_io.cr` 段表仅 6 段(STR/SYM/NOD/ENT/REG/EDG,**无类型段**);类型表本体不落盘 → 类型只作为 u32 索引出现在 SYM/func/var/struct/enum/NOD/EDG 里 + +### 6.2 DFNode.TK 升格 + +- 从「基类型常量槽」(`OFF_DF_TK=40`,仅 0-8)→ 类型项引用(迁移期双槽,收尾单槽) + +**落地记录(R2 P4 Task 4,2026-09-13)**: + +- **迁移期双槽 = 内存面**:`ESZ_DFNODE` 64 → 72B,`OFF_DF_TK=40` **语义/字节零变化**(仍是混用码: + 基类型行 + 小旗标/宽度码),新增 `OFF_DF_TK_TERM=64`(类型项引用,-1 = 无项)。 +- **项槽派生 = 允许清单制**(`sh_tk_term_of_code`):仅 `IR_CONST`/`IR_BINARY` ∧ `0 ≤ tk < g_type_count` + ⇒ `sh_term_of_ti(tk)`(与桥接层**同项**,无第二套建项逻辑);清单外/旗标码/宽度码/越界一律 -1 + (不得给 `IR_BOUNDS_CHECK` 的 `ti=1` 凭空安 dex 项——`1` 数值上恰是 `TI_DEX` 行)。 +- **`.ccr` 面零变化**:NOD 36B 记录只读 `tk` 槽(`iri_tk`)⇒ 盘面与 `--dump-objects` 逐字节同 + (实测 `ptr_arith`/`generics_test` 八段逐字节同)。 +- **`.cir` 快照面**:项槽**不落盘**(快照记录仍 8×8B=64B ⇒ `CIR_CACHE_VER` 保持 17),装载侧 + 在 emit 等价时点(逐函数逐节点序)重派生。理由 = 项索引是**进程内**项表(`g_type_terms`) + 的索引,跨进程只在写侧成立:实测入槽版本暖路径 329/329 项引用越出暖进程项表(`bad_term=329`); + 且「纯函数值与其入参并存」= 第二真源。重派生后冷/暖两态 `(op,tk,term,tag,a,b,c)` 逐行相同。 +- **收尾(单槽化)归 P5**:届时 `tk` 本身成为类型项引用 ⇒ 盘面项槽/`.csr` 消费面另定; + 项引用的**稳定形态**(内容寻址 / 落盘项 ID,或同款重派生)是 P5 的前置裁决项——不得直接落 + 进程内索引。 + +**收尾落地(R2 P5 Task 2,2026-09-13;✅ 已实施)**:`tk`(`iri_tk`)自此**是**类型面单源派生量——内存槽 `OFF_DF_TK` = 类型项引用(唯一真源)、`OFF_DF_AUX` = 辅码;**盘面(NOD)/`.cir` 快照一律派生码**(形态与判据 = §3.4 的「收尾落地」块)。 +**项引用稳定形态(D20,规范立即生效)**:① 本批**不引入任何跨进程项引用**;② 未来需要跨边界时唯一许可形态 = **TYPE 段文件内项索引**(D13 确定性装填 ⇒ 文件内索引稳定;corearch 已能重建该项表);③ **禁令**:进程内项表索引**不得**跨进程/跨复位边界(P4 `bad_term=329` 红证)。 +**β(盘面项索引 + corearch `atom_of` 读回)未实施,转 P6**(P5 计划裁-1;前置与判据 = 计划附录 E-1)。**暖态项面局限(登记,同 §3.4)**:缓存命中跳过 `ir_gen` ⇒ 暖态类型/项表可小于冷态,越出暖表处项不可重建(`term=-1`)——派生码/发射/盘面零影响;冷/暖比对判据随之重定为「**派生码面(op,tk,aux)逐行同 + 项内容(tag/a/b/c)等价**」(P5 T2,实测驱动)。 + +### 6.3 corearch 读回 + +- 载入 TYPE/IFACE 段重建类型项表 → 承接收尾期 `atom_of` 与未来验证消费(`.csr`/TagNode 图验证) + +**状态(R2 P6 Task 3 收官,2026-09-14)**:**β ✅ 已实施**——NOD 记录扩面(36B → **40B**:+28 = **TYPE 段文件空间项索引** `item i32`,-1 = 无项;`tk` 槽仍 = 派生码,字节/语义零变化)+ `CCR_VERSION` **8 → 9**(旧 v8 及更早整类拒收)。**读回 = 两条腿**:① `load_ccr` 在 TYPE 段解析后的**一致性硬校验**(域外 / `tt_atom_of_term(项) ≠ 盘上码` ⇒ 拒绝 rc=1;实现 = `type_terms.cr:tt_atom_of_term`——R2 P6 T3 自 `ty_shadow.cr` 整函数迁入**共享层**,因 corearch 清单不含桥接层);② corearch `--dump-nod-items` 通道(逐节点 `item → 项表取项 → atom_of → 行`,跨进程可解析的显式证据)。**发射面零变化**(实证:ELF canary `95084e7b…d475` IDENTICAL · `--dump-objects` 两口径逐字节同):发射路径仍读 `tk` 派生码,`item` 是**语义面**增量(`.csr`/TagNode 图验证等未来消费者的直接入口)。**布局先裁 = L-a**(40B;D20 合规论证 = 单一写点 + 读侧单一权威 + **分歧即硬拒**——见 `plans/2026-09-14-r2-p6-tail.md` Task 3 与 `.superpowers/sdd/p6-task3-report.md` §1;更严的单源形态 L-c 登记未取)。**残留登记**:暖态(`.cir` 缓存命中跳过 ir_gen)文件对被缓存函数引用、而暖进程未分配的类型行只能落 `item = -1`(码由 `tk` 保真)⇒ 暖态项面为**部分**;`.cir` 快照不动(`CIR_CACHE_VER` 保持 17)。 + +### 6.4 语义保鲜兑现点 + +- 类型/接口信息随载体出前端 → 验证工具**直接消费**(不必重跑前端) + +--- + +## 7. 诊断与错误处理 + +- **判定失败**:期望项 vs 实际项 + 非空反例值 +- **穷尽性失败**:未被覆盖的具体反例模式(补集代表值) +- **预算超限**:显式诊断「判定预算耗尽」+ 建议(放宽约束 / 加标注)+ 登记未覆盖面 +- **位置**:图节点 → 源码映射(`diag.cr` + LSP 基建);全推断类错误落调用点,缓解 = 可选参数标注 + 调用点引用声明点(损失账本项) +- 错误码:沿用 `error[XXXX]` 体系,新增「类型引擎 / 接口 / 穷尽性」码段 + +--- + +## 8. 测试与判据 + +1. **引擎单元层**:判定用例表(子类型 / 等价 / 不相交 / 可空 / 穷尽性 / 递归 / 参数化 / 否定 + **最小反例断言**)+ 预算用例(性能不回退) +2. **检查器行为层**:27 selfhost + 3 bootstrap 套件 + suite + **新增能力用例集**(联合/可选/穷尽性/定长退役/泛型约束/impl 契约) +3. **对拍层**:影子差异清单(**零差异门**才允许替换;差异逐条裁决留档) +4. **自举链**:每批 `corec2/corec3` byte-identical + N06=0 + 冒烟 rc=42 +5. **零回归锚**:未使用新能力的程序产物 **byte-identical**(同 R1 判据) +6. **收紧清单**:每处「旧接受 → 新拒绝」记录表(文件/用例/旧判定/新判定/处置) + +--- + +## 9. 实施分期与风险 + +**分期(每期独立可验收)** + +| 期 | 内容 | +|---|---| +| P0 | 建层:类型项表 + 引擎 + 查询 API + 用例表(checker 零改动)——**✅ 已实施(2026-09-10)**:类型项 DAG 表(48B/条 + 开放寻址索引 + 哈希去重)、NNF/DNF 规范化、三态判定引擎(`ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited`/`tt_witness`/`ty_exhaustive`,含原子类互斥公理、μ 展开余归纳 memo、预算守卫)、`corec selftest-types` 自测通道(**49 例**,判据 `tests/selfhost/test_type_engine.py`;P0 终审 NOT READY → 修复轮 `13b8b564` 闭环 3 Critical + 4 Important,用例 38→49)。落点:计划 `plans/2026-09-10-r2-p0-type-engine.md`;提交链 `52c15d53`→`595999dd`→`58117dad`→`efad9d06`。**P0 未覆盖面(显式登记)**:① 参数化原子的参数仅同形判等(变型规则 = P3);② `AK_NAMED` 具体行不展开(待 P2 接入 checker 类型表);③ 空递归(μX.X)保守按深度守卫返回 -1;④ 查询 API `iface_*` 属 P2 接线面,P0 只出判定 API | +| P1 | 影子对拍:旧判定并行 + 差异清单(收紧面在此暴露)——**✅ 已实施(2026-09-11)**:① 桥接层(`checker` ti → 引擎类型项 + per-ti 开放寻址缓存,N 不入身份)② `type_equal` 包装(改 `type_equal_core`)+ **8 站点挂点** + 分类计数(AGREE/OLD_STRICTER/OLD_LOOSER/UNKNOWN)+ 摘要/转储通道(`--type-shadow` **默认关**、`--type-shadow-dump`;关/开**两态产物逐字节同**,sha256 `95084e7b…d475`)③ 三档语料(+2 档扩面)对拍清单:**67 有效文件 / 26,704 判定 → old_looser=0、old_stricter=0**,9 条差异全部为引擎侧 unknown(根因 = 同名 named 多行未规范化,致 `AK_NAMED` 不展开;bridge 缺口 0、预算耗尽 0)④ 收官(全回归 + 自举 `corec2/corec3` cmp IDENTICAL)。落点:`src/compiler/ty_shadow.cr`(+`checker.cr` 包装/`globals.cr`/`main.cr`/`type_selftest.cr`);清单 `specs/2026-09-10-type-shadow-findings.md`;计划 `plans/2026-09-10-r2-p1-shadow-parity.md`;提交链 `e4c293b2`→`844cec6c`+`514956a1`→`75c20297`。**未覆盖面与交 P2 项(显式登记)**:① **F1 同名 named 多行未规范化 = P2 硬前置**(不先做,替换会把主流同类型比较从「真」变「未判定」);② F2 数组长度 N 属 P2 裁决(R1 已裁 IR 身份不含 N,checker 侧须另裁);③ 站点 1/2/4/6 零命中、站点 3 仅 2 次 → 「零差异」**只覆盖赋值/返回/if 面**,P2 验收须报站点覆盖(不得只看差异数);④ F3/F4(调用位点实参不匹配无诊断 / 泛型后续形参声明类型不生效)= checker 缺陷面,单开任务 | +| P2 | 逐点替换:`type_equal` / `infer_expr` 公理区 / 索引·字段·match·转换 / `res_type_node` 两表合一(+ 收紧清单)——**(a)部分 ✅ 已实施(2026-09-11)= P2a**:① **F1**(P1 硬前置)同名 `TYP_NAMED` **建表去重**(唯一分配点收敛 `alloc_named_type` + 开放寻址侧表:装填守卫/重建重放/回写重探;12 读取点读名字故语义不变)② **F2**(用户裁决)数组长度 `N` 迁为**常量档长度约束**(`array_len_constraint_ok` 沿 6 个结构递归位下钻比 N;身份去 N;判定点 `type_compat_strict` 显式补检、恒先调 `type_equal` 保站点采样面;拒绝语义保持、措辞分因、码不变)③ **判定替换**:`type_equal` → 引擎 `ty_equiv`(快路径 + 桥接 + 三态;**unknown/桥接失败回落 `type_equal_legacy` 并计数**,不静默)④ 对拍复跑(71 文件 **26989/26989 agree**,`old_stricter`/`old_looser`/`unknown`/`replace_*` 全 0)+ 站点覆盖同报(1/2/4/6 零命中、3 仅 2 次)⑤ **收紧清单 = 空**(`old_looser=0` 全语料)。行为面:ELF 逐字节 `95084e7b…d475`(开/关两态)、23 套件绿、自举 `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 42、`.ccr` 冷缓存 4 语料(含自指)IDENTICAL。落点:`src/compiler/checker.cr`(+`ty_shadow.cr`/`globals.cr`/`main.cr`/`type_selftest.cr`/`tests/selfhost/test_named_dedup.py`);计划 `plans/2026-09-10-r2-p2-replace.md`;提交链 `3c8402e8`+`8c9d773f` → `b36d8e76`+`507daf7a` → `10718ba8` → 收官提交。**未覆盖面(显式登记)**:站点 1/2/4/6 语料零命中(差异归零效力范围 = 赋值/返回/if 面,其余面由定向探针承担);语料 `replace_unknown=0`(未知面证据 = 探针 + C-1)。**P5 继承项**:① `type_equal_legacy` **删除归 P5**(现为对拍对照物 + 回落实现双用);② **unknown 清零的前提 = 引擎命名展开**(`AK_NAMED`/`TYP_GENERIC_APPLY` 不展开 → -1 → 回落 legacy;命名类型/泛型应用面判定仍由 legacy 承担);③ 站点 6 挂点去留随 P5 站点面清理。**(b)部分 ✅ 已实施(2026-09-11)= P2b**:① 本质条目表(新建 `src/compiler/iface_registry.cr`:13 条 = 8 原生 + product/sequence/ref/ptr/named,40B/条 × 5 字段,静态常量、不 alloc `g_types` 行)+ 查询 API `iface_*` 11 个与 `ty_code_to_ti` 单表 ② **接线(保语义 = 零行为变化,表的每格 = 现状逐格转录、格注 `file:line`)**:字面量定型 5 处 ← `iface_lit_ti`;算术门(ANY)/ 逻辑门(ALL)/ `if` 与 `while` 条件门(ONE)← `iface_permits`;索引兜底拒绝 ← `IP_INDEX`;`res_type_node`/`res_call_type` 双份 TY→TI 映射 + 同族 6 处内联链 → 单表(站点域显式保留)③ 桥接分派(`sh_native_ak`/`sh_base_ak`)与注册表单源化。落点 = 上列各文件 + `globals.cr`(`g_iface_*`/`IP_*`)+ `type_selftest.cr`(95→212 例)+ `tests/selfhost/test_iface_ops.py`(76 例,`run.sh` selfhost-tests 挂钩);计划 `plans/2026-09-11-r2-p2b-iface-ops.md`;提交链 `7f520dce`→`31143cd0`→`9b15fd2a`→`6d2790cd`→`a99ee825`→`7145eea5`→`30545da7`→ 收官提交。判据:46 套件 rc=0 · `selftest-types` 212/212 · ELF `95084e7b…d475`(开/关两态)· `.ccr` 4 档逐字节同 · 同源双编译器对拍(语料/自源/影子)逐字节同 · **收紧清单 = 空** · 自举 `corec2==corec3` + N06=0 + 冒烟 42。**未覆盖面(显式登记,转 P3 交接包)**:`iface_satisfies` 未交付(零调用者)· `iface_size/iface_align`(hw-map 面)· 横切轴/用户轴条目 · `AK_SUM`/`AK_FN` 无 checker 对应 · 31 个不可接线站点(逐条实测理由)+ 10 项 P3 裁决点 → `plans/2026-09-11-r2-p3-capabilities.md` 附录 A、findings §12;**P5 继承项**:`sh_*_ak_legacy`/`type_equal_legacy` 删除 + unknown 清零(同 P2a,见 TODO #24/#30)。 | +| P3 | 能力落地:定长退役 + 序列接口/长度约束 + 穷尽性/联合/可选 + 泛型约束 + impl 契约——**(a)P3a ✅ 已实施(2026-09-12)**:五任务提交链 `61d3a8b4`(引擎展开层——**只服务满足判定与域查询,等价判定保持原子名义**)→ `ef61f002`(定长退役收口:变型表 `ty_variance_of` 只读协变/可写不变 + `array_len_constraint_ok` 方向档(视图→固定拒)+ mut 维度入参数链;关闭 P2a「切片→固定长」静默放宽)→ `e9818d62`(穷尽性真判定 = 补集空性 + 具体变体反例;TM03 硬门,report-only 先行)→ `c8c7731b`(`T?` = `T ∪ null`:`AK_NULL` 原生第九员 + `TYP_OPTIONAL`/`TYP_NULL` + `EXPR_OPTIONAL`;退役内建 Option 注册 + `EXPR_TRY` 结构解包(废名字串);枚举载荷类型节点列)→ `9c0a83ec`(泛型约束:结构/枚举保留 + 实例化点 `ty_sub` 本质轴判定(TG02 + witness 反例)+ monomorph 实例键类型项化;**`T: I` 半边 = P3b**);落点/判据/台账 = 计划 `plans/2026-09-11-r2-p3-capabilities.md`(**附录 B = P3b 开工前必读**)+ TODO #34。判据:全量回归五 job + 49 套件(42 selfhost + 7 bootstrap)· `selftest-types` 288/288 · ELF canary `95084e7b…d475` IDENTICAL · `corec2==corec3` cmp IDENTICAL + N06=0 + 冒烟 42 · 影子终态 `decisions=30128 agree=30128`(P3a 收官修复 target 清单后由 30338 → 30128,−210 = monomorph 退出 target 两档闭包;全计数 0、站点 1/2/4/6 零命中)· 同源双编译器对拍(72 档)零差异 · `.ccr` 变更面逐段实测(T4 = Option 串退役 STR −10B;T5 = `generics_test` +370B 反折叠;T0/T1/T3 不变)· 收紧 **18** 条 / 放宽 **8** 条(全语料零命中,自举源码零改造)。**(b)P3b ✅ 已实施(2026-09-12)**:Task 0b 契约回补 `9aa1786c`(`iface_satisfies` 三态 + 轴 A>C>B 分派 + 实例化点 `T: I` 真判定 + 形状表注册面)→ Task 2 横切接口 `44100ba4`(六条形状项 + 索引/切片消费点换位;**行为保持**:收紧 0 / 放宽 0)→ Task 6 impl 契约 `e838f18b`(签名类型项化(方法条目类型节点槽)+ 形状项逐成员判定 + mangling 退役(**零 `str_intern`**)+ 泛型体内方法调用**崩溃修复**(旧产物运行 rc=139 → 正确值);收紧 4 / 放宽 0);落点/判据/统一台账 = 计划 **Task 7b** + **附录 C(P4 开工必读)** + TODO #36~#39。判据:`selftest-types` **333/333** · 六套件全绿 · 五 CI job(`check` = 既有 2 条 TF01 误报划界)· ELF canary `95084e7b…d475` IDENTICAL + `.ccr` 逐字节同(P3b 三件零 `.ccr` 变更)· 同源对拍 72 档零差异 · 自举链(`corec2==corec3` + N06=0 + 冒烟 42)稳定。**P3 未开工面(显式,转 P4/专批)**:iterable 接线(`EXPR_FOR` 零类型检查)· 形状命名消费语法(`T: 可索引`,含 STR 段增长裁决)· 整形状 `ty_sub` 路由(实测不可用:`AK_NAMED` 不展开 + product 结构相等非包含)· TODO #38 用户轴名义 vs 结构(维护者裁决)。**P3 未覆盖面登记(显式)**:符号档长度约束(= VC 义务,不实现)· 动态档 = 既有运行期检查 + 字面量界检查 · MAX_GENERICS=4 / MAX_ENUM_VARIANTS / MAX_VARIANT_TYPES 处置(先加护栏;MAX_IFACE_METHODS=16 / MAX_IFACE_METHOD_PARAMS=8 已立硬错护栏,P3b T6)· ~~运行期可选表示未统一(裸值 vs `Some(...)` 对象)⇒ **`Some` 臂双路径 SIGSEGV(check/build rc=0 零诊断 + 产物/解释器 rc=139,非 P3a 引入)= 计划附录 B.7 一等条目(P3b 未动 ⇒ P4 必修,与 TYPE 段一并裁)**~~ **✅ 已闭合(R2 P4 Task 5,2026-09-13;裁决 5「解包侧判表示」)**:表示位运行期分派(IR 层,0 = 裸值 / 1 = 装箱)+ 跨函数面隐藏全局信道(纯 IR);未覆盖面(结构体字段/数组元素/全局槽/match 结果/惰性 thunk)= 响亮失败双路径同(裸值)或照常正确(装箱),已登记;见计划 `plans/2026-09-12-r2-p4-carrier.md` Task 5 · `EXPR_LET` 无检查(TODO #32)· 空递归保守 -1 · bootstrap `&&`/`||` 不短路根因 · 非枚举域不判穷尽 · 空枚举域 = 空洞穷尽 | +| P4 | 载体:.ccr TYPE/IFACE 段 + DFNode.TK 升格 + corearch 读回——**✅ 已实施(2026-09-13)**:**8 提交链** `7c822003`(T0 引擎核纯化拆分——iface 簇 + `iface_kind_of` → `iface_axis.cr`,corearch 链接面开路)→ `97febdbf`(T1 段机制:`TYPE(7)`/`IFACE(8)` 加段 + 版本 7→8 + loader 规范序/连续/必备集 + 两段空壳)→ `3d604e11`(T2 TYPE 段内容:类型行表 24B + 项 DAG 40B(哈希不落盘) + 确定性装填 D13 + corearch 读回重建)→ `8f4af99b`(T3 IFACE 段内容:原生条目扩列 13→16 + 六形状命名化 + 签名项化 + impl 边/方法表 + 读回)→ `387abf2e`(T4 DFNode.TK 迁移期双槽:允许清单 {IR_CONST,IR_BINARY};**计划 D15 被实测推翻**——`.cir` 快照不落项槽、`CIR_CACHE_VER` 保持 17、装载侧重派生)→ `2be26cc5` + `cf368259`(T5 可选运行期表示解包侧判表示:**B.7 一等条目闭合**(裸值 + `Some` 臂双路径 rc=139 → 正确值)+ `IR_LOAD_ENUM_TAG` 全局行分派)→ `cc015ae4`(T6 `MAX_*` 写入侧护栏 P022/P023;三上限**不解除**)+ 附带件 `6822102a`(TF01 收口 = T2 §9-② 路径 (a))。落点/判据/统一台账/P5 交接 = 计划 `plans/2026-09-12-r2-p4-carrier.md`(**附录 D = P5 开工必读**;D-1 = `.ccr` 两套记录口径 + 冷/热判据勘误)+ TODO #41~#44。判据:五 CI job 全 rc=0(`check` 自 TF01 收口后按 rc=0)· 全枚举 **55/55**(48 selfhost + 7 bootstrap)· `selftest-types` 333 → **355** · ELF canary `95084e7b…d475` **IDENTICAL**(全批)· `corec2==corec3` cmp IDENTICAL(双 2792078B `4919023b…`)+ N06=0 + 冒烟 42 · 同源对拍(pre-P4 二进制 × 当前源):影子 dumps 空 + `decisions=32620=agree`(全计数 0、站点覆盖同报),check/影子日志面差异 100% = TF01 收口类 · `.ccr` 两套口径(`ccr` 90823B/134701B · `build --static` 90966B/134844B)。**统一台账**:收紧 **10 类**(T1 3 + T2 2 + T3 2 + T6 3)/ 放宽 2(T1 空壳闸退役,非语义)/ 崩溃修复 **8 形态 + 1 静默错值类**(T5)/ 全语料命中 **0**。**未覆盖面(显式登记,转 P5)**:NOD 盘面项槽 + `.csr`/TagNode 消费面零调用者 + corearch 侧 IFACE 段无判定消费者(§6.3 `atom_of` 承接面)+ 命名消费语法未开工 + 形状可写视图不可表达 + 单槽化与项引用稳定形态(`bad_term=329` 红证)+ `type_equal_legacy` 删除 / unknown 清零 / 桥接缓存退出 / 站点 6 去留 + 对象布局表示位(T5 未覆盖面,与 `MAX_*` 同批)+ `.cir` 缓存两席面(可选程序收回 + **预存暖态 SIGSEGV**,归 TODO #5 家族)+ 签名编码面收口(`isat.axis_c_ret_code` / `test_iface_satisfies.py:318`);逐条 = 计划附录 D。 | +| P5 | 收尾:单槽化 + 大步删旧路径(路线 3)+ 文档/台账——**✅ 已实施(2026-09-14)**:**12 提交链** `2b9b5463`(T0 前置侦查 + 冻结基线,源码零改动)→ `11913f8a`(T1 `.cir` 暖态 SIGSEGV 根因修复:装载侧逐函数读缓冲累计 1.1674GiB > 1GiB 堆 ⇒ `alloc` 返回 0 ⇒ `str_len(0)`;修 = 单一复用读缓冲 + 载入失败 ⇒ miss;格式零变化 ⇒ `CIR_CACHE_VER` 保持 17)→ `7f6c38cb`(T2 单槽化 α:`OFF_DF_TK` = 类型项引用 / `OFF_DF_AUX` = 辅码 / **码 = 派生量** / 分类表 / F1(r9 残留 UB 消除)+ F2(复合行走辅码保真)裁决 / D23 闸;布局与版本位零改动)→ `461d96f3` + `5a333e0c`(T3 unknown 清零 = **命名面判定化**:身份链 + `te_named_pair`;不接结构性展开,P3a 反证口径保持)→ `faca94e0`(T3b 不变槽元素三态 `te_elem_cmp`:令牌按节点同一性 / 类型项按 `ty_equiv`)→ `e6db38b8`(T4 删 `type_equal_legacy` + 全部回落面 + 桥接 legacy 副本 + 站点 6/死分支;**引擎唯一权威** + 残留 -1 政策 **P-A** = ICE04 硬错(入硬名单 ⇒ `build` 拒绝落盘))→ `b6bf33cb` + `d65ff9ad` + `66316751`(T5 **影子层整体下线** + 回归网切换 = 冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律)→ `04c20487`(T6 `EXPR_LET`/全局声明位点兼容判定 = TA02 硬错,TODO #32 收口)→ **T7 收官**(全量回归 + 统一台账 + 文档 + P6 交接包 = 计划附录 E)。判据(T7 实跑):五 CI job 全 rc=0 · 全枚举 **58/58**(51 selfhost + 7 bootstrap)· `selftest-types` **404/404** · `check src/compiler` rc=0 · ELF canary `95084e7b…d475`(28822B)IDENTICAL · `corec2==corec3` cmp IDENTICAL(`5559beef…`,2854910B)+ N06=0 + 冒烟 42 + `--help` rc=1 · **冻结基线同源对拍 72 档零差异**(pre-P5 `5d2b15ad…` × 当前源 vs `f737e26b…` × 当前源)· **行为探针全集 30 档零差异**(P5 期记录值;**U-4 勘误(R2 P6 T0/T1)**:实为 **29 实档 + 1 条字面 glob 伪条目**——本值保留历史原样,见下行 P6 行) · 突变控制(M1 派生面 400/404 + 35/40 + `.ccr` sha 变;M2 判定面 10/18 + 397/404 + 对拍子集 12/12 DIFF)逐条转红 + 精确回滚 · `.ccr` 两套口径冷态四条(90823/90966/135341/135484;差恒 143B)· `--dump-objects`(3521 行)/`.cir` DOT(165915B)逐字节同 · 构建确定性 ×2。**统一台账**:删除/退役 **6 件 + 1 改名族**(legacy 判定族 + 7 自调 + 2 回落 · `g_replace_*` 计数 · 桥接 legacy 副本 · 站点 6 + `ir_gen` 死分支 · 影子层 12 函数 + `sh_finish` + 17 全局 + 2 CLI + 9 挂点 · hits 存-复原 hack;`g_shadow_map` → `g_term_map`)· 收紧 **5 类**(D23 · TG02 混类 · `te_elem_cmp` 不变槽 0 · ICE04 · TA02)· 放宽 **0** · 修复 **4**(暖态 SIGSEGV / F1 UB / 声明位点静默错产物 / 死分支)· 全语料命中 **0**(各任务 report-only 逐条)· 实施偏差 **8 条**。**未覆盖面(显式登记,转 P6)**:β 盘面项索引 + corearch `atom_of` 读回 · 暖态项表可小于冷态 · 对象布局表示位 · `MAX_*` 解除 · 可选程序收回 `.cir` 缓存 · `.csr`/TagNode 消费面 · 命名消费语法/iterable/形状可写视图 · ¬ 面过判(T3b 揭开)· `-> never` 调用面(T6)· #20/F3 调用位点 · 冻结基线复现配方 · ICE04/P-A 签核 · 非硬名单诊断不阻 `build` · `string == int` 混比 · 单条目 >1GiB 分片 · 设计先例引用面;逐条(现状/出处/影响面/建议/前置)= 计划 `plans/2026-09-13-r2-p5-cleanup.md` **附录 E**;收官总述与统一台账 = 同计划「收官总述」/「统一台账」节。 | + +| P6 | 尾批:附录 E 十六项分诊收口——回归网可复现化(E-13)+ β 盘面项索引(E-1)+ 判定缺口(E-10/E-11)+ 文档/卫生(E-7/E-8/E-16)+ 政策面登记——**✅ 已实施(2026-09-14)**:**提交链** `bc4f0a43`(P6 计划定稿:附录 E 16 项分诊 = **实施 7 / 待裁 7 / 维持登记 1.5**)→ **T0**(前置侦查 + 冻结基线重建 pinned `9bcb7083`;源码零改动;四表 + 守门复验)→ `e5ce8f9e`(T1 回归网可复现化 = **E-13**:`tools/baseline/{REBUILD.md,rebuild.sh,parity_run.sh,probes_run.sh}` + `tests/probes/` **29 档**入仓;**U-4「30 档」收口**)→ `911c692d` + `0819d506`(T2/T3 兄弟提交)经合流态由 `9bdbdf16`(T4a;**双亲合并提交**,一次落地)→ `4b909996`(T5 文档/卫生)→ **T6 收官**。**逐任务**:T2 = ¬ 面过判收口(同类原子异身份 ⇒ 不交;仅 `AK_SUM` 变体项;`selftest-types` 404 → **415**;负控 11 例);T3 = **β ✅**(NOD 36B → **40B** 承载 TYPE 段文件空间项索引 + `CCR_VERSION` **8 → 9** + loader 一致性硬校验 + corearch `--dump-nod-items` 读回;U-2 先裁 = **L-a**;`test_ccr_types` 40 → **48**);T4a = `-> never` 调用返回型(两站点误报消除;用例 +9);T5 = `MIN_CASES` 32 → **415** + `module_to_ccr.py` 废弃标注 + 两 spec 陈旧值与 `--type-shadow` 先例载体改写(**E-16**)。判据(T6 实跑):五 CI job 全 rc=0 · 全枚举 **58/58** · `selftest-types` **415/415** · `check src/compiler` rc=0 · canary `95084e7b…d475`(28822B)IDENTICAL · **入仓配方重建**三 sha = 白名单(`5d2b15ad…`/`493dc490…`/`18b94bd9…`)· 自举链 `corec2==corec3` IDENTICAL(`0b2e06d0…`)+ N06=0 + 冒烟 42 + `--help` rc=1 · **腿① 同源对拍 72 档零差异** + **探针 29 档零差异** · `.ccr` 四条**重锁**(96015 `680a6f98…` / 96158 `76f36e6a…` / 142793 `41e9d845…` / 142936 `704316c8…`;delta = 4B×节点数;口径差恒 143B)· `--dump-objects` 3521 行 · `.cir` DOT 165915B · 构建确定性 ×2 · `test_backend_bootstrap` rc=0 + `error[`=0。**统一台账**:实施 **6** · 删除 **0**(+1 候选)· 迁移 **1** · 收紧 **3 类**(¬ 面异身份不交 / 版本闸 v9 整类拒收 / loader 一致性硬校验)· 放宽 **1 类**(`-> never` 调用返回型)· 修复 **1 件**(+1 并记)· 登记 **19** · 偏差 **9**(含 **T3 抢跑事故**——零提交/零还原;护栏 = 「先裁」写进任务卡正文)· **未取裁 7**。**剩余登记/未取裁**(逐条 = 计划 `plans/2026-09-14-r2-p6-tail.md`「统一台账」§6 +「尾批终态陈述」):容量批(E-2/E-3/E-4,**推荐转独立批**)· 政策五问(E-14 + E-12)· `string == int` 混比(E-15)· iterable/命名消费语法(E-6)· `.csr`/TagNode(归验证切片轮)· `tt_display_at` 参数链渲染退化(预存)· `EXPR_ASSIGN` never 豁免 · 暖态项面部分 · **主树遗留**(9 未提交文件 + T3 味 `build/*`——**待维护者处置**,涉还原许可)。**下一批指向 = 容量批 或 验证切片轮**(按裁-P6-1 结果)。 | +| 容量批(CAP) | 独立批(裁-P6-1 推荐「转独立批」已取裁):**E-2 表示面聚合** + **E-3 记录布局迁侧表** + **E-4 缓存门**——**✅ 已实施(2026-09-15)**:**提交链** `4607624c`(T1:白名单换代 `9bcb7083 → 97f4394f` + 六表侦查)→ `432c2e40`(T2:E-2 写点装箱;`test_optional` 34 → **48**)+ `7c4d6603`(TODO #56 登记)→ `0b0d9aef`(T3:三面上限解除 + 无界覆盖位;`test_enum_limit` 13 → **18**)→ `15357b04`(T4:机制实证证伪 ⇒ 裁 (c) 收口 + TODO #57)→ T5 收官。**逐项**:**E-2**(裁-CAP-1 (a) 存储边界规范化)= 可选聚合槽(字段/数组·切片元素/元组元素/枚举载荷/全局槽)**写点装箱**(纯 IR、零布局变更、canary 无豁免);同批挖出并修**两条预存根因**(读点常量折叠 + 写点静态度量初值)⇒ 原「双路径 139 响亮失败」转正确值。**E-3**(裁-CAP-2 (a) 迁侧表)= 记录尺寸与 `OFF_*` 不变、字段/变体/载荷迁侧表(**110 个访问器调用点零改动**)+ 覆盖位 `2^vi` → **无界位图**(旧态 70 变体 SIGFPE rc=136)+ `MAX_STRUCT_FIELDS`/`MAX_ENUM_VARIANTS`/`MAX_VARIANT_TYPES` 退役 + P022/P023 停发 + `ast.cr` 四死镜像删除;**`.ccr` 不 bump**(四条冷+暖逐字节同)。**E-4**(裁-CAP-3 先机制实证)= 沙箱放开门 ⇒ 冷/暖 × 16 档 + 混态 3 态 × 2 程序 **ELF 逐字节全同** ⇒ **机制证伪、按 (c) 维持关缓存**;「移除门」登记为待裁候选(TODO #57)。**判据(T5 实跑)**:canary `95084e7b…d475`(28822B)IDENTICAL · `.ccr` 四条 96015/96158/142793/142936(冷+暖,差恒 143B)· **换代后重建基线 × 当前源的 72 档同源对拍零差异**(34×0/38×1)· 探针 29 档零差异 · 五 CI job rc=0 · 全枚举 **59/59** · `selftest-types` **415/415** · `check src/compiler` rc=0 · 自举链 `f69240ae…` + N06=0 + 冒烟 42 + `--help` rc=1 · 突变 T2 四条 + T3 三条全按设计转红。**剩余登记**:**12 件**(逐条 = 计划 `plans/2026-09-15-capacity-batch.md`「统一台账」§5)——其中**待裁 2**:(D) 语言面拒绝 `*p = v`(pointee `T?`;影响面实读 = 拒绝的是**已错**形态)+ T4「移除门」;**高优先 TODO #56** = `c3` 指针写双路径分歧静默错值(**预存**、本批不修)。**过程教训**(已入台账 §6):① 「新增类型面查询前必须核是否 alloc」(T2 零足迹事故:未门控 ⇒ 非可选 `.ccr` +168B TYPE 段)② 「用例必须落在经**记录**的判定路径上」(T3 MB 假阴性)③ 断线固盘纪律(`*-state.md`,本批生效三次)。 | + +**风险面** + +| # | 风险 | 缓解 | +|---|---|---| +| R1 | 判定算法正确性(语义子类型细节) | 用例表先于接线 + 影子对拍兜底 | +| R2 | 判定爆炸(性能) | 规范化 + memo + 位集合快路径 + **预算守卫** | +| R3 | 自举源码在收紧后不合法(面未知) | P1 影子期即出清单,不拖到 P2 | +| R4 | impl 语义重定义且零测试覆盖(用户可见) | 新建用例集 + 收紧清单记录(无兼容窗口 = 裁决 5) | +| R5 | 双槽期漂移 | 双槽期**单源化**:term 为准,atom 由 `atom_of(term)` 派生;收尾项登记 | + +--- + +## 10. 明确不做(YAGNI)与未覆盖面 + +- **不做**:HKTs(定案已定)|运行期字典 / 存在类型(推迟平台桥 / hw-map)|**用法驱动接口自动推导**(定案 12,本轮不做,登记后置)|借用 / 并发共享(定案 9 唯一欠账,另轮)|完整证明搜索(归验证切片轮) +- **未覆盖面登记机制**:引擎不支持的形态 → **编译期显式拒绝 + 登记**(不静默、不猜) + +--- + +## 11. 现状事实底座(2026-09-10 侦查,file:line 供实施批引用) + +- 类型表 `g_types`(24B/条 `kind/data/extra`)唯一定义点 `checker.cr:9-16`;扩容 `dyn_arr.cr:186-190`;访问器 `checker.cr:85-98`;**只有 checker/ir_gen 读**,后端/解释器只比基类型常量(`instr.cr:386,880,1214-1271`) +- 原生类型清单:`TI_INT=0 / TI_DEX=1 / TI_BOOL=2 / TI_STR=3 / TI_UNIT=4 / TI_NEVER=5 / TI_CHAR=6 / TI_DYN=7`(`checker.cr:19-33`);`TI_DEX_S=8` = 哨兵(永不查表) +- 判定函数仅 3 个:`type_equal`(`checker.cr:100-158`,8 个 kind 分支)、`unify_types`(`:936-975`)、`infer_expr`(`:1367-2401`,1034 行单体) +- 原生公理散点:字面量定型 `:1371-1376`;运算符 `:1397-1441`(含 `str 拼接` / `dex 支配` / **比较不校验操作数**);一元 `:1447-1480`(`&x` 产 `TYP_PTR`,类型位 `&T` 产 `TYP_REF`——**双表示并存**);索引 `:2056-2130`;字段/方法 `:1985-2045`;转换 `:2244-2261`;dyn `:1304/1315/1340-1366`(位图 **64 上限** `:1348-1349`) +- `match` 穷尽性:**不存在**(`EC_TM_EXHAUST=9003` 定义于 `ast.cr:443`,全仓零 raise) +- 接口机制:`T_INTERFACE=97` / `T_IMPL=18`;表 `g_ifaces`(1432B/条,16 方法 × 88B,`dyn_arr.cr:135-141`)+ `g_impl_for`(16B,`dyn_arr.cr:818-821`);查询 `find_iface`(`checker.cr:468`)/`type_has_method`(`:497`,**名字拼接查找**)/`check_iface`(`:505-530`)/`check_impl_for`(`:1225-1286`);**ir_gen 零消费、.ccr 零序列化、无原生条目**;`test_impl.py` 5 例全为固有方法、`interface` 零命中 +- 泛型:`monomorph.cr` 440 行;`gen_build_subst`(`:56-83`)纯文本名替换;去重 `gen_find_cached`(`:354-370`)用 `istr_eq` 字符串相等;约束仅函数泛型(`g_generic_constr`,`parser.cr:1050-1061`,调用点校验 `checker.cr:1043-1075`);`MAX_GENERICS=4`(`ast.cr:115`) +- 承载面缺口:`.ccr` 段表 6 段无类型段(`ccr_io.cr:108`);`DFNode.TK`(`dyn_arr.cr:98`)仅基类型常量;接口/约束不在图上 +- `TYP_ARRAY` 消费面(定长退役改造点):`checker.cr:110-119`(身份)/`:402-404`(构造)/`:2208`(字面量)/`:2062-2075`(F11)/`:2080-2089`(F2);`ir_gen.cr:120-135`(`arr_len_lit_of`)/`:476-480`(`type_size`)/`:527`(`type_align`);**后端与解释器对 `TYP_ARRAY/TYP_SLICE` 零命中**(布局全靠 IR opcode) +- 数字撞车三处:`TYP_GENERIC_PARAM=7 == TY_GENERIC_PARAM=7 == TI_DYN=7`;`TYP_GENERIC_APPLY=8 == TY_DEX_S=8`;`TYP_NAMED=1 == TY_DEX=1`(`checker.cr:379` 靠巧合成立) +- `res_type_node` 双份 TY→TI 映射:`checker.cr:367-380` 与 `:886-893` +- 规模:`checker.cr` 2483 行;`== TI_*` 比较 72 处;`get_type_kind` 调用 checker 29 / ir_gen 10 / 其余 pass 4 + +--- + +## 12. 自检记录 + +- **占位符扫描**:无 TBD/TODO;「待细化」仅 §2.2(只读/可写两形状)——已在 §5.1 给出变型规则,实施批细化条目布局 +- **内部一致性**:§0 裁决 ↔ §1-§10 逐节落实;§5.1 与 R1 spec §1(退役裁决)一致;§6 载体与 v7 spec 的段设计对齐(分段号待实施批定,非设计缺口) +- **范围检查**:单一实现计划?**否**——本 spec 明分 P0-P5 六期,**每期独立成计划**(writing-plans 逐期立) +- **歧义检查**:`⊤ₖ` 与 `⊤` 的区分(§1.2 已定义);「表示提示携带 N」与「类型判定不看 N」(§5.1 已明写);「预算超限」与「类型不兼容」的区分(§3.3/§7 已明写) diff --git a/docs/superpowers/specs/2026-09-10-type-shadow-findings.md b/docs/superpowers/specs/2026-09-10-type-shadow-findings.md new file mode 100644 index 00000000..dfce9508 --- /dev/null +++ b/docs/superpowers/specs/2026-09-10-type-shadow-findings.md @@ -0,0 +1,437 @@ +# R2 P1 影子对拍差异清单(三档语料 + 归因与裁决建议) + +日期:2026-09-10(Task 3 落地日 2026-09-11 复核;**P2a 替换后复跑 = §11**,2026-09-11 Task 4 收官追加;**R2 P5 Task 5(2026-09-13)起影子通道已整体下线 ⇒ 本文件为历史记录,§15 = 终态基线**) +范围:R2 计划 P1「影子对拍」——把 P0 类型判定引擎与旧 `type_equal_core` 在**真实语料**上逐点对账, +产出「收紧面(old_looser = 旧受新拒)暴露清单」与「未覆盖面清单」,供 P2(替换旧判定)裁决。 +上游:P0 引擎 + P1 Task 1 桥接层 + Task 2 挂点/通道(`844cec6c` + 评审补强 `514956a1`)+ Task 3 Step 0 拆因 + 本清单(同一提交)。 + +引擎/通道事实来源:`src/compiler/ty_shadow.cr`(桥接 + 挂点 + 摘要/转储)、`src/compiler/checker.cr`(`type_equal` 包装 + 8 站点)、 +`src/compiler/type_engine.cr`(三态 + 成因位)。 + +--- + +## 0. 结论(TL;DR) + +> **⚠ 影子通道已下线(R2 P5 Task 5,2026-09-13)**:本文件记录的通道(`--type-shadow` / `[type-shadow]` 摘要 / 差异转储 / 站点直方图 / `sh_compare`)**已整体删除**——对照物 `type_equal_legacy` 在 P5 Task 4 删除后对拍停摆(全语料 `decisions=0`),P5 Task 5 按 D24/D26 下线(§15 = 终态基线记录)。判定面回归网自此 = **冻结基线同源对拍 + 行为探针 + 突变控制 + 三态纪律(ICE04)**;**后续批次不得引用本文件的计数/摘要/站点直方图作为判据**(它们只在通道存活期可复现)。 +> +> **本清单已闭环(2026-09-11 注记)**:P1 的两条硬项 F1(硬前置)/F2(裁决)已在 **R2 P2a** 实施(用户裁决 = 建表去重根治 / 落常量档长度约束),`type_equal` 判定权已移交引擎。**替换后复跑见 §11**(26,989/26,989 agree、`old_looser`/`old_stricter`/`unknown` 全 0、站点覆盖口径不变)。§1-§10 保留为 **P1 时点**记录(数字随自指语料漂移,跨提交不可逐字复现);未结项 = F3/F4(TODO #20/#21)与 F5(TODO #25,P2a 新发现)。 + +1. **真收紧面 = 0**:三档语料(+2 档扩面)共 **67 个有效文件 / 26,704 次判定**(71 个候选中 4 个排除,见 §2),`old_looser=0`、`old_stricter=0`。 + 全部 9 条差异条目(**去重后仅 3 个类型对**)都落在 `unknown` 桶的 **engine 侧**。 +2. **unknown 拆因(Step 0)**:`bridge`(任一侧翻译失败 = 桥接缺口)= **0**;`engine`(引擎三态负值)= **9**, + 其中 **未覆盖面 9 / 预算耗尽 0**。桥接缺口在现有 `TYP_*`×`TY_*` 全集下**结构性不可达**(非语料不足,见 §3.3)。 +3. **未覆盖面的实体**:`AK_NAMED` 不展开,且**根因是「同名 named 类型占多行」**—— + `MemLayout`(2 行)、`Box`(5 行)。旧判定按**名**判等(true),桥接按**行号**建原子(两个不同原子 → 引擎判不了 → -1)。 + 这是 P2 替换的**硬前置**:不先做名字规范化,替换会把大量「同类型比较」从真变未知(见 §6.F1)。 +4. **站点覆盖极不均衡**:74.4% 的判定来自站点 8(`assign-node`),18.4% 来自站点 5(`fn-body-ret`),7.1% 来自站点 7(`if-branch`); + **站点 1/2/4/6 全语料 0 命中**,站点 3 仅 2 次。→「零差异」结论**只覆盖赋值/返回/if 合并面**, + 泛型、热补丁、兜底等价面**零证据**(不得据此判这些面收敛)。 +5. **三类排除项计数全为 0**(brief 要求单列,见 §5):dyn 差异 0(且 dyn 行在判定面出现 **0 次**); + `&T` vs `&mut T` 差异 0(且 `TYP_REF` 行在判定面出现 **0 次**,源级亦不可构造该对); + 站点 4 命中 **0**(注意:不是「恒 agree」,而是「本轮语料根本没跑到」)。 +6. **另立三类结构性发现**(对 P2/P3 裁决有直接影响,见 §6): + F1 同名多行 named 未规范化(P2 硬前置)、F2 数组长度 `N` 旧比引擎不比(替换即放宽,R1 已裁 N 不入身份)、 + F3 调用位点实参类型不匹配**无诊断**(旧判定结果被丢弃)+ F4 泛型函数后续形参声明类型在推断中不生效。 + +--- + +## 1. Step 0:unknown 桶拆两因(先改码,再采数) + +### 1.1 改动 + +| 文件 | 改动 | +|---|---| +| `src/compiler/ty_shadow.cr` | `sh_compare`:`a<0 \|\| b<0`(桥接缺口)→ `kind=3` + `g_shadow_unknown_bridge`;`e<0`(引擎负值)→ `kind=0` + `g_shadow_unknown_engine`,并按引擎自报成因位细分 `unc=ty_uncovered()` / `exh=ty_exhausted()`(**必须在第二次 `ty_budget_reset` 之前读**);`sh_kind_name` 增 `unknown_bridge`;`sh_report` 增 4 个字段(**前 5 组 key=value 前缀不变**,既有 grep 读取方不受影响) | +| `src/compiler/globals.cr` | +4 全局:`g_shadow_unknown_bridge` / `g_shadow_unknown_engine` / `g_shadow_unknown_uncovered` / `g_shadow_unknown_budget` | +| `src/compiler/ty_shadow.cr`(Task 3 扩面) | +站点直方图:`sh_site_begin` 累计 8 个站点的**判定次数**(`g_shadow_site_counts`,8×8B),`sh_report` 增第二行 `[type-shadow-sites]`。理由:环形缓冲(256 条)只装「有差异/未知」的条目,**0 差异语料下「某站点是否真的跑到过」没有别的证据通道**,而 brief 要求登记「站点 4 恒 agree / 站点 6 不可达」 | + +摘要行(示例,`tests/suite/ptr_arith.cr`): + +``` +[type-shadow] decisions=74 agree=74 old_stricter=0 old_looser=0 unknown=0 unknown_bridge=0 unknown_engine=0 unknown_engine_uncovered=0 unknown_engine_budget=0 +[type-shadow-sites] hotpatch-ret=0 generic-arg=0 generic-apply-base=0 unify-fallback=0 fn-body-ret=16 assign-binary=0 if-branch=7 assign-node=51 +``` + +转储 kind 空间:`0 = unknown_engine` / `1 = old_stricter` / `2 = old_looser` / `3 = unknown_bridge`(dump 的 `kind` 列同步改名)。 +**读 dump 者须知(M4,Task 3 评审)**:Step 0 前 `kind=0` 的名字是 `unknown` → 现名 `unknown_engine`(契约变更);按字面旧名匹配的分析脚本会漏读该列(仓内已核**无消费者**,破坏面仅限外部/未来脚本)。 + +### 1.2 判据(全部通过,原始输出) + +``` +$ nice -n 19 python3 build_selfhost_native.py +[GUARD] corec: manifest OK (41 files) ; build log clean (error[ = 0, undefined = 0) -> build/corec +[GUARD] corearch / x86_64-linux target / corelsp 全绿 ; === BUILD SUCCESS === +$ nice -n 19 ./build/corec selftest-types | tail -1 +71/71 type-engine cases passed + +$ nice -n 19 ./build/corec clean-cache +cleaned .core/cache/cir/ +$ nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/p1t3_off2 +$ nice -n 19 ./build/corec clean-cache +$ nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static --type-shadow -o /tmp/p1t3_on2 +$ cmp /tmp/r1t4_base_bin /tmp/p1t3_off2 && echo OFF-IDENTICAL +OFF-IDENTICAL +$ cmp /tmp/r1t4_base_bin /tmp/p1t3_on2 && echo ON-IDENTICAL +ON-IDENTICAL +$ sha256sum /tmp/r1t4_base_bin /tmp/p1t3_off2 /tmp/p1t3_on2 +95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 /tmp/r1t4_base_bin +95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 /tmp/p1t3_off2 +95084e7bc68d6550d21d3d96fa3afd89c67a5d89edce5656a3d2e74fc923d475 /tmp/p1t3_on2 +``` + +回归(加固判据,非 brief 要求):`tests/selfhost/test_compile.py` All passed · `test_type_engine.py` PASS · +`test_impl.py` 7/7 · `test_borrow.py` 7/7。 + +### 1.3 拆因必要性实证(拆因前后同一条目可复归) + +拆因前 dump 里该条目写作 `… unknown 0`,无法分辨「桥接没译出来」与「引擎判不了」。 +拆因后同一条目(编译器语料)写作 `fn-body-ret 5 28 25 1 unknown_engine 0`,且摘要给出 `unknown_engine_uncovered=1 / unknown_engine_budget=0` +——**归因可直接读出**(探针副本进一步解析出两侧类型名,见 §4.2)。 + +--- + +## 2. 语料、命令与排除 + +三档 = brief 指定;t4/t5 为**扩面**(同类真实语料,成本 ~1.5s/文件)。全部运行 cwd = 仓库根,**每文件前 `clean-cache`**, +命令模板 = `nice -n 19 ./build/corec check --type-shadow --type-shadow-dump `。 + +| 档 | 内容 | 文件数 | 有摘要 | 排除(原因) | +|---|---|---|---|---| +| ① | `tests/suite/*.cr` | 31 | 27 | `test_control_flow.cr` / `test_generics.cr` = **0 字节空文件**(`error: cannot read`;Task 2 报告曾记为「预期失败 fixture」,实为空文件——勘误);`at_test_mini4.cr` / `at_test_mini6.cr` = rc=139(**函数体内嵌套 fn 声明**,既有缺陷,TODO #16 已登记,非本批回归) | +| ② | `src/compiler/main.cr` + `src/compiler/_import.cr` | 2 | 2 | 无。**注意**:`src/compiler/*` 任一文件的 check 都会经该目录 `_import.cr` 加载**整个编译器**(前端 + stdlib + lattice),故两行是**同一语料**的两次运行(`main.cr` 额外汇总 main.cr 自身的重复贡献 ~120 判定);「纯整编译器」数取 `_import.cr` 行 | +| ③ | `src/compiler/ccr_io.cr` + 后端/内核单元(`src/format/elf/*` `src/os/linux/*` `src/arch/x86_64/*` `src/lattice/*` `src/targets/x86_64-linux/*`) | 15 | 15 | 无(`src/compiler/elf.cr` 在本轮单独扫描中 rc=0 但 2 个 parse error、decisions=0——**陈旧遗留文件**,不在本档命令内) | +| ④(扩面) | `src/stdlib/*.cr` | 19 | 19 | 无 | +| ⑤(扩面) | `examples/*.cr` | 4 | 4 | `test_scale.cr` / `test_sum.cr` decisions=0(陈旧样例,parse 不通过) | + +原始日志:`/tmp/r2p1t3/all_tiers2.txt`(逐文件主行 + 站点行)、`/tmp/r2p1t3/logs2/*.log`(全文)、`/tmp/r2p1t3/dumps2/*.txt`(差异转储)。 + +**语料自指说明**:②③ 的语料是编译器自身,而 Step 0/扩面对 `ty_shadow.cr`/`globals.cr` 的改动本身也进入该语料 → +同档判定数在仪表化前后有 **+4/+2 的漂移**(如 `main.cr` 3960 → 3964,探针副本再 +2)。这是自指语料的预期行为,不影响分类。 + +--- + +## 3. 计数表 + +### 3.1 主计数(逐档,全部为原始摘要行求和) + +| 档 | 文件 | decisions | agree | old_stricter | **old_looser** | unknown | unk_bridge | unk_engine | engine_uncovered | engine_budget | +|---|---|---|---|---|---|---|---|---|---|---| +| ① suite | 27 | 2,743 | 2,740 | 0 | **0** | 3 | 0 | 3 | 3 | 0 | +| ② 编译器自身 | 2 | 7,808 | 7,806 | 0 | **0** | 2 | 0 | 2 | 2 | 0 | +| ③ 后端/内核 | 15 | 13,960 | 13,957 | 0 | **0** | 3 | 0 | 3 | 3 | 0 | +| ④ stdlib | 19 | 2,042 | 2,041 | 0 | **0** | 1 | 0 | 1 | 1 | 0 | +| ⑤ examples | 4 | 151 | 151 | 0 | **0** | 0 | 0 | 0 | 0 | 0 | +| **合计** | **67**(+4 排除) | **26,704** | **26,695** | **0** | **0** | **9** | **0** | **9** | **9** | **0** | + +不变式:`agree + stricter + looser + unknown == decisions` 在**全部 67 条摘要行**成立(71 候选 − 4 排除 = 67;逐行核对,0 违例)。 + +**「Task 3 时点」注(终审 M-3)**:上表逐行数字为 **Task 3 时点**(提交 `75c20297`)实测,**在最终提交上不可逐字复现**——②③ 含**编译器自身语料**(§2 末「语料自指说明」),Task 4 对 `ty_shadow.cr`/`type_terms.cr`/`type_selftest.cr` 的改动进入该语料 → 自指行一致 **+3**:`main.cr` 3964→3967、`_import.cr` 3844→3847、`ccr_io.cr`(③)4177→4180(Task 4 实测,提交 `7624a53c`)。非自指行不变(源与导入面均未改;抽检 `ptr_arith.cr` = 74、`toml.cr` = 120,与表/§1.2 一致)。机制见 §9.4;跨提交比较判定数须**同源同二进制**。**该组自指数字本身也随编译器自身源码改动而移动**(含注释行)——引用时须连同提交号(本注即为示例)。 + +### 3.2 站点直方图(逐档) + +| 档 | hotpatch-ret(1) | generic-arg(2) | generic-apply-base(3) | unify-fallback(4) | fn-body-ret(5) | assign-binary(6) | if-branch(7) | assign-node(8) | +|---|---|---|---|---|---|---|---|---| +| ① suite | 0 | 0 | 2 | 0 | 760 | 0 | 215 | 1,766 | +| ② 编译器自身 | 0 | 0 | 0 | 0 | 1,401 | 0 | 647 | 5,760 | +| ③ 后端/内核 | 0 | 0 | 0 | 0 | 2,217 | 0 | 864 | 10,879 | +| ④ stdlib | 0 | 0 | 0 | 0 | 501 | 0 | 166 | 1,375 | +| ⑤ examples | 0 | 0 | 0 | 0 | 34 | 0 | 15 | 102 | +| **合计** | **0** | **0** | **2** | **0** | **4,913** | **0** | **1,907** | **19,882** | + +两行互为校验:`sum(site) == decisions` 全部成立(26,704 = 26,704)。 + +**占比**:站点 8 = 74.4%、站点 5 = 18.4%、站点 7 = 7.1%、站点 3 = 0.007%;站点 1/2/4/6 = **0**。 + +### 3.3 桥接缺口(kind=3)= 0 的结构性解释 + +`sh_term_of_ti` 对 `TYP_BASE/NAMED/ARRAY/SLICE/PTR/REF/TUPLE/DYN/GENERIC_PARAM/GENERIC_APPLY` **全集有分支** +(未知 kind 兜底 → `AK_NAMED`,不是失败);`sh_base_ak` 对 `TY_*` 全码(`INT/DEX/DEX_S/BOOL/STRING/UNIT/NEVER/CHAR/GENERIC_PARAM`)**逐项有映射**。 +故 `-1` 只剩「`ti<0` / 行号越界」两条入口——正常语料不可达。 +→ 本桶本轮 = **结构性 0**;拆分的收益是「未来新增 kind/码或行号竞态」的**守门**,不是本轮语料结论。 + +--- + +## 4. 差异清单 + +### 4.1 原始条目(9 条;转储文件逐字) + +| # | site | t1 | t2 | old_ok | kind | 来源(档/文件) | +|---|---|---|---|---|---|---| +| 1 | fn-body-ret (5) | 28 | 25 | 1 | unknown_engine (0) | ② `src/compiler/main.cr`、`src/compiler/_import.cr`、③ `ccr_io.cr`(同一整编译器语料,3 行) | +| 2 | fn-body-ret (5) | 27 | 11 | 1 | unknown_engine (0) | ③ `src/targets/x86_64-linux/main.cr` + `_import.cr`(同一组合根语料,2 行) | +| 3 | fn-body-ret (5) | 11 | 9 | 1 | unknown_engine (0) | ① `hotpatch_test.cr`、④ `toml.cr`(同一 stdlib 预导入语料,2 行) | +| 4 | generic-apply-base (3) | 9 | 29 | 1 | unknown_engine (0) | ① `generics_test.cr` | +| 5 | generic-apply-base (3) | 9 | 34 | 1 | unknown_engine (0) | ① `generics_test.cr` | + +**旧/新对照**:全部 9 条的旧判定 = **受**(`old_ok=1`),引擎 = **-1(未判定)**,成因位 `unc=1 exh=0`。 +即:**没有一条是「旧受新拒」(真收紧面)**,也没有一条是「旧拒新受」——差异全部是「旧能判、引擎判不了」。 + +### 4.2 归因(行号 → 类型名,探针副本实测) + +行号是**类型表行号**(0..8 为原生占位行,用户类型从 9 起;同名类型占多行)。用 `/tmp` 仓库副本加 `[dbg]` 探针(**未触碰仓库文件**)读出行号对应的类型名: + +| #(去重对) | 实测 | 旧判定依据 | 引擎判定 | 归因 | +|---|---|---|---|---| +| MemLayout | `named(MemLayout)` vs `named(MemLayout)`(行号随编译单元变化:28/25、27/11、11/9) | `type_equal_core` 的 `TYP_NAMED` 分支比较 **name_idx**(`get_type_data`)→ 同名 = **true** | 桥接把**行号**建成原子 `tt_atom(AK_NAMED, ti, -1)` → 两个不同原子 → `sub_cover` 命中 `AK_NAMED` 不展开 → `g_ty_uncovered=1` → **-1** | **未覆盖面(同名多行)**:`MemLayout`(`src/stdlib/toml.cr:260` 唯一声明)在编译器单元占 **2 行**(`[dbg-dup] MemLayout rows=2`) | +| Box | `named(Box)` vs `named(Box)`(行 9 vs 29、行 9 vs 34) | 同上(按名) | 同上(按行 → 不同原子 → -1) | **未覆盖面(同名多行)**:`Box`(`tests/suite/generics_test.cr:17` 唯一声明)在泛型实例化后占 **5 行**(`[dbg-dup] Box rows=5`) | + +**同名多行是语言侧既存事实**(`res_call_type` 在符号不可见处 `alloc_type(TYP_NAMED, name_idx, 0)` 新建行;泛型实例化另建行), +不是影子层的构造。影子层只是**如实按行建原子**,于是暴露了它。 + +--- + +## 5. 三类排除项(brief 要求单列;本轮计数**全为 0**) + +| 排除项 | 差异条目数 | 参与判定的次数(实测) | 备注 | +|---|---|---|---| +| (a) `dyn`(映射 `AK_DYN`=⊤ 的过宽近似) | **0** | `dyn_involved = **0**`(全语料 66 个有摘要文件求和) | 连 `dyn_test.cr`(88 判定)都 0 参与 → 风险**未触发**(≠ 不存在);探针 E(`dyn` 值赋给 `int` 变量)在站点 8 判定为 **agree**(旧拒 + 引擎 0),且旧路径另发 `error[TA01]`——dyn 的 ⊤ 过宽在本轮**没有**产生假等价 | +| (b) `&T` vs `&mut T`(桥接丢 mut,旧比 mut = by-design,P3 面) | **0** | `ref_involved = **0**`(同上求和) | 且**源级不可构造**:`&x` 产 **PTR**(探针 A 实测 `q := &x` → `ptr(to=int)`)而非 `TYP_REF`;`TYP_REF` 仅出现在**声明型位置**(`&T`/`&mut T` 形参、`&self`/`&mut self`),而这些行本轮未进入任何判定点(探针 H/I 均 0 命中)。→ 该 by-design 面本轮**零证据**,P3 建模时须补定向样例 | +| (c) 站点 4(`res_call_type` 无 `EXPR_ARRAY`/tuple 分支 → 曾判「恒 agree」) | **0** | `unify-fallback 命中 = **0**` | **勘误**:不是「恒 agree」,而是**本轮语料根本没跑到**(0 命中 = 0 agree)。定向探针实测其行为随 pattern 侧形态分化:**base 对 → agree**(探针 K:`int` vs `str` → 两侧同拒)、**含 named 的对 → unknown(未覆盖面)**(探针 B:`int` vs `named(Q)` → 引擎 -1)。→ 不得据此面判收敛(brief 的告诫成立,理由需更正为「零命中」) | + +三项均**未计入**收紧面/宽松面统计(上表与 §3.1 相互独立)。 + +--- + +## 6. 结构性发现(本轮最大产出;对 P2/P3 裁决直接相关) + +### F1(P2 硬前置)同名 named 多行未规范化 → 替换旧判定会把「真」变「未知」 +- 事实:`type_equal_core` 的 `TYP_NAMED` 按 **name_idx** 判等;类型表同一名字可占**多行**(`MemLayout` 2 行、`Box` 5 行;编译器语料 `named_rows=20, dup_groups=1`;generics_test `named_rows=6, dup_groups=1`)。 +- 桥接按**行号**建原子 → 同名不同行 = 两个不同原子 → 引擎 **-1**。 +- 影响:**9 条差异全部由此产生**(占差异总数 100%);且这正是 8 个站点的主流量(命名类型比较极常见)。 +- 裁决建议:P2 替换**前**必须二选一——① 引擎侧引入 named 身份的规范化(同名 → 同一原子,需 name 注册表);② 桥接把同名行折叠到同一项(会掩盖「同名不同声明域」的区分,需先裁决同名是否恒等价)。 + 未做则该面在替换后从「真」变「未判定」;若 P2 把未判定当拒绝 → **大面积假拒**,若当通过 → **静默失去命名类型检查**。二者都不可接受。 + +### F2(替换即放宽)数组长度 `N`:旧比、引擎不比 +- 事实:`type_equal_core` 的 `TYP_ARRAY` 分支比较 `extra`(= N);桥接按 R1 裁决**不把 N 入身份**(`AK_SEQUENCE` 参数链只含元素项)。 +- 探针 D(`fn ret4() -> [int;3] { b:[int;4]; return b; }`):旧 = 拒(`error[TF01]`),引擎 = 受 → `old_stricter=1`;探针 G(泛型形参先绑 `[int;3]` 再以 `[int;4]` 调用):站点 2 同样 `old_stricter=1`。 +- 影响:**替换后 N 不匹配的代码会静默通过**(当前是编译错误)。本轮语料 0 触发 → 影响面取决于用户代码。 +- 裁决建议:R1 的「N 不入身份」是 IR 侧身份裁决;checker 侧是否保留 N 检查需**单独裁决**(保留 → 需在引擎/桥接把 N 作为维度/字面量入判定;不保留 → 记为有意的语言放宽并写进 spec)。 + +### F3(旧路径的洞,P2 需接线)调用位点实参类型不匹配**无诊断** +- 事实:`infer_gen_call` 调 `unify_types(pattern_ti, concrete_ti);`(`checker.cr:1053`)**丢弃返回值**;非泛型调用位点同样不报。 + 探针证据:`fn take2(n: int)->int` 以 `take2("s")` 调用 → 输出 `ok`(无诊断);`fn take[T](a: T, n: str)` 以 `take(1, 2)` 调用 → `ok`。 +- 与影子的关系:影子只对账 `type_equal` 的 verdict(此例两侧皆「拒」= agree),**不负责诊断**;但 P2 若以引擎判定作为诊断来源,必须补「判定 → 诊断」这一环(含 `unknown` 的处置策略)。 +- 建议:单开任务(属 checker 缺陷面,非影子层)。 + +### F4(新发现,建议单开任务)泛型函数后续形参的声明类型在推断中不生效 +- 事实:`fn take[T](a: T, n: int)` 以 `take(1, "s")` 调用时,`unify_types` 收到的 pattern 是 **`gparam(T)`** 而非 `int`(探针副本 `[dbg-unify]` 实测); + `fn take[T](a: T, n: str)` 以 `take(1, 2)` 调用同样收到 `gparam(T)`。而**首个形参为具体类型**时正常(探针 K:`fn take[T](n: int, a: T)` → pattern = `base(int)`,正确进站点 4)。 +- 影响:泛型函数的非泛型形参类型约束在调用推断中被绕过(与 F3 叠加 → 完全无诊断)。 +- 建议:单开任务定位 `ast_data(pn)` / 形参链导航(`infer_gen_call`,checker.cr:1030-1060)。 + +--- + +## 7. 定向探针(**合成语料,不计入 §3 统计**) + +| 探针 | 意图 | 站点 | 结果 | 结论 | +|---|---|---|---|---| +| A `pa_refmut` | `&mut T` 形参 + `&x` 实参 | 2 | agree(两侧同拒) | `&x` 是 **PTR** 不是 REF → mut 面未触达 | +| B `pb_named2` | 站点 4 + named 实参 | **4** | **unknown**(engine/uncovered) | 站点 4 **不是恒 agree**(含 named → 未覆盖面) | +| C `pc_site4base` | 泛型函数 + 具体形参不匹配 | (2) | agree | 印证 F4:pattern 变 T,站点 4 未命中 | +| D `pd_arrlen` | `[int;4]` 返回给 `[int;3]` | 5 | **old_stricter**(旧拒新受)+ `error[TF01]` | F2 实证(N 面) | +| E `pe_dyn` | `dyn` 值赋给 `int` 变量 | 8 | agree(另发 `error[TA01]`) | dyn ⊤ 近似本轮未产假等价 | +| F `pf_hotpatch` | 同名 hotpatch 两版本 | **1** | agree | 站点 1 可达(语料未触达,探针补上) | +| G `pg_genarg` | `T` 先绑 `[int;3]` 再以 `[int;4]` 调用 | **2** | **old_stricter** | 站点 2 可达;N 面在泛型实参位同样暴露 | +| H `ph_refmut2` / I `pi_selfmut` | `&int`/`&mut int` 变量、`&self`/`&mut self` 方法 | — | 0 命中、全 agree | 印证 (b):REF 面源级不可构造 | +| K `pk_site4base2` | 泛型函数**首**形参为 base + 不匹配实参 | **4** | agree | 站点 4 的 base 对行为 = 两侧同拒(真判等) | +| J `pj_nongen` | 非泛型调用实参不匹配 | — | `ok`(无诊断) | F3 实证 | + +探针源:`/tmp/r2p1t3/probes/*.cr`(非仓库文件)。 + +--- + +## 8. 裁决建议(后续任务清单) + +**P2(替换旧判定)前置/清单**: +1. **F1 必须先行**:named 身份规范化(引擎侧 name 注册表,或桥接同名折叠)——否则替换会把主流「同类型比较」变未判定。建议 P2 第一项即此,并补同名多行的守门用例。 +2. **F2 需裁决**:数组 `N` 是否保留检查(R1 已裁 IR 身份不含 N;checker 侧需显式裁决 + spec 记档)。 +3. **F3/F4 接线**:判定结果 → 诊断的接线(含 `unknown` 策略:拒绝 / 降级警告 / 放行,需与「语义保鲜」目标一致);泛型形参类型推断修复(F4)。 +4. **替换清单的验收判据**:不能只看「差异数 = 0」,须同时报告**站点覆盖**(本轮 4 个站点 0 命中)——否则「零差异」无法区分「一致」与「没跑到」。 + +**引擎需补规则**: +- named 身份(F1,最高优先); +- `AK_REF`/`AK_PTR` 的可变性/地址空间变型(P3「条目化 + 变型」面,本轮 0 证据但 by-design 已知); +- (若裁决保留)数组长度维度进入身份。 + +**未覆盖面登记**(P1 输出):本轮 9 条差异**全部**属「`AK_NAMED` 不展开」;`AK_DYN` 的 ⊤ 近似本轮未触发; +`unknown_engine_budget = 0`(预算 200000 步在全部 26,704 次判定中**从未耗尽**)。 + +**其他已登记项(非本任务范围)**: +- 站点 6(`assign-binary`)不可达:parser 已把 `=` 一律降为 `EXPR_ASSIGN`(`parser.cr:222-224`),挂点保留但无样本 → **Task 4 裁决(终审 M-1):保留挂点,去留归 P2**(零成本、留证据面;不删——删须同步本节与 `ty_shadow.cr` 站点表); +- 站点 4 的分类行为(§5(c))需在 P2 语料里补定向样例; +- `tests/suite/test_control_flow.cr` / `test_generics.cr` 为 0 字节空文件(勘误 Task 2 的「预期失败 fixture」措辞);`at_test_mini4/6` rc=139 属 TODO #16(嵌套 fn); +- `src/compiler/elf.cr`(566 行)在本轮 rc=0 但 2 个 parse error、0 判定 → 陈旧遗留文件,建议单开清理; +- `src/compiler/linker.cr` 为 0 字节空文件。 + +--- + +## 9. 限制(诚实边界) + +1. **零收紧面 ≠ 收紧面为空**:本轮「零差异」仅覆盖站点 5/7/8 主导的面(99.99% 决策);站点 1/2/4 **零命中**、站点 3 仅 2 次。 + 泛型实参/泛型应用基型/兜底等价/热补丁声明组四个面**无证据**。 +2. **探针是合成语料**:§7 的差异(`old_stricter`)不计入 §3 统计,仅证明「面可达 + 分类正确」。 +3. **dyn/REF 面为「未触发」而非「已证安全」**:两者参与判定 0 次,过宽/丢 mut 的风险**未被本轮语料检验**。 +4. **自指语料漂移**:②③ 的判定数随仪表化代码变化(+4/+2),跨提交比较判定数需同源码同二进制。 +5. **`AK_NAMED` 的具体判定语义仍未建模**(引擎不去名、不比对声明)——本轮只把它判为「未判定」,未回答「同名是否恒等价」这一语义问题(F1 裁决所需)。 +6. **未测吞吐**:本轮只证「产物零影响 + 判定正确性」,影子开销(开态)未做基准。 + +--- + +## 10. 复现命令 + +```bash +# 构建 + 自测(CPU 限制) +nice -n 19 python3 build_selfhost_native.py +nice -n 19 ./build/corec selftest-types # 72/72(Task 4 后:+ 第二次重建守门 bridge.grow_rehash2;Task 3 时点 = 71/71) + +# 两态判据(开/关均须与基线逐字节相同;每次判据前 clean-cache,cwd = 仓库根) +nice -n 19 ./build/corec clean-cache +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static -o /tmp/off +nice -n 19 ./build/corec clean-cache +nice -n 19 ./build/corec build tests/suite/ptr_arith.cr --static --type-shadow -o /tmp/on +cmp /tmp/r1t4_base_bin /tmp/off && cmp /tmp/r1t4_base_bin /tmp/on + +# 三档语料(逐文件 dump;脚本见 /tmp/r2p1t3_run2.sh) +for f in tests/suite/*.cr; do nice -n 19 ./build/corec check "$f" --type-shadow --type-shadow-dump /tmp/d.txt; done +nice -n 19 ./build/corec check src/compiler/main.cr --type-shadow --type-shadow-dump /tmp/d.txt +nice -n 19 ./build/corec check src/compiler/ccr_io.cr --type-shadow --type-shadow-dump /tmp/d.txt + +# 归因(行号 → 类型名):/tmp 仓库副本 + [dbg] 探针(未触碰仓库文件) +rsync -a --exclude .jj --exclude .git --exclude build --exclude .core ./ /tmp/r2p1t3_probe/ +# 在副本内给 sh_compare 加 [dbg]/[dbg-shape]/[dbg-dup] 打印并重建,再跑上表语料 +``` + +摘要行读取:`grep '^\[type-shadow\] '`(主计数)/ `grep '^\[type-shadow-sites\]'`(站点直方图,串内 `name=N` 逐项)。 + +--- + +## 11. P2a 后复跑(2026-09-11,R2 P2a Task 3 替换 → Task 4 收官复验) + +**背景**:本清单的 ①②(F1 硬前置 / F2 裁决)已在 R2 P2a 落地(用户裁决:F1 = 建表去重根治;F2 = 落常量档长度约束保持拒绝语义),`type_equal` 的判定权已移交引擎(`ty_equiv`)。本节记录**替换后**在**同一批语料**上的复跑结果(对拍门)。 + +**通道差异(与 §1-§5 的关键区别)**:影子对照物的「旧」侧已从 `type_equal_core` 切到 **`type_equal_legacy`**(同一函数体,改名后降级为对照物 + 回落实现)。故分类语义 = **引擎 vs 旧结构判等**——正是替换门要的对账。§3 的 `unknown` 拆因字段(`unknown_bridge` / `unknown_engine*`)保留;另增 `replace_bridge` / `replace_unknown`(**判定路径**的回落计数,与影子侧计数器互为交叉证据:逐文件相等)。 + +**条件**:cwd = 仓库根;**每文件先 `clean-cache`**(**必须**——`.ccr`/缓存态会让同一二进制二次运行产出不同中间态,见 TODO #5 同族第二实例);71 候选中 67 有摘要(4 排除同 §2)。 + +| 指标 | P1(§3.1,Task 3 时点) | **P2a 后(Task 4 复核,final 产物重算)** | +|---|---|---| +| 判定数 / agree | 26,704 / 26,695(unknown 9) | **26,989 / 26,989(unknown 0)** | +| `old_stricter`(旧拒新受) | 0 | **0** | +| `old_looser`(旧受新拒 = 真收紧面) | **0** | **0** | +| `unknown`(引擎未判定) | 9(全部 `AK_NAMED` 不展开) | **0**(F1 去重消除根因) | +| `unknown_bridge`(桥接缺口) | 0(结构性) | **0** | +| `unknown_engine_budget`(预算耗尽) | 0 | **0** | +| `replace_bridge` / `replace_unknown`(判定路径回落) | —(替换前无此计数) | **0 / 0**(语料面未触达;探针面 3 处 =1,逐条登记于 Task 3 报告 §5.4) | + +**站点覆盖(与差异数同报——P1 交接硬性要求)**:直方图(final 重算)= `fn-body-ret=4949 · if-branch=1919 · assign-node=20119 · generic-apply-base=2 · hotpatch-ret/generic-arg/unify-fallback/assign-binary = 0`,合计 26,989 = decisions(逐文件恒等,独立重算 67 行 0 违例;**原运行脚本不做该校验**,见 Task 3 报告 §5.3 更正)。 +- **覆盖面 = 站点 5(返回位)/ 7(if 分支)/ 8(赋值)**;站点 3 仅 2 次(`generics_test`);**站点 1/2/4/6 语料零命中(同 P1)**。 +- ⇒ 「差异归零」的效力范围**仍是赋值/返回/if 面**(口径与 §9.1 一致,未因替换扩大);站点 1/2/4 面由定向探针承担,站点 6 无生产点(TODO #17)。 + +**N 面口径(Task 2 评审裁决,硬性)**:F2 之后影子对 **N 面已「失明」**——对照物 `type_equal_legacy` 的数组分支自身已 N-free ⇒ `old_stricter` 在 N 面**必为 0**(同义反复、无诊断力;P1 探针 D 的 `old_stricter=1` 已随 F2 消失)。故**本节的「对拍归零」不得作为 N 面证据**;N 面验收 = **行为探针**(异长拒绝 / 同长通过),覆盖位:顶层数组、嵌套元素位、泛型实参位、指针元素位、元组字段位(单节点元素)+ **元组字段位(复合表达式元素)= 漏检,登记 F5 衍生**(见 §6.1 / TODO #25)。探针在 `--type-shadow` 下同时跑:影子全 agree(证明「N 不入身份」)而 rc=1(证明「拒绝只来自 `array_len_constraint_ok`」)= 该面正确的证据形态。 + +**残留与交 P5**:① 命名类型 / 泛型应用面的等价判定**仍由 legacy 承担**(`AK_NAMED`/`TYP_GENERIC_APPLY` 不展开 → 引擎 -1 → 回落+计数);**P5 删 `type_equal_legacy` 的前提 = 引擎命名展开落地 + `replace_unknown` 清零**(否则「回落」变「未判定」);② 站点 6 挂点去留随 P5 站点面清理;③ F3/F4(调用位点无诊断 / 泛型后续形参类型不生效)仍未接线(TODO #20/#21)。 + +--- + +## 12. P2b 后:公理区事实表(2026-09-11,R2 P2b 收官——供 P3 收紧直接消费) + +**背景**:R2 P2b 把 `infer_expr` 的公理区(操作许可 / 字面量定型)接入 `iface_registry.cr` 的本质条目表,并把 `res_type_node`/`res_call_type` 的双份 TY→TI 映射合成单表 `ty_code_to_ti`(提交链 `7f520dce`→`31143cd0`→`6d2790cd`→`a99ee825`→`7145eea5`→`30545da7` + 收官提交)。本批硬口径 = **保语义(零行为变化)**:表的每一格 = 现状代码逐格转录 ⇒ 现状「宽松面」**原样保留**;本节的表 = 这些宽松面的**实测事实**(行号 = P2b 末提交实测,括注 = Task 4/5 报告的接线前行号)。 + +**判据口径(本节的证据形态)**:表中「现状诊断」列全部来自**行为探针**(Task 4 的 32 例 + Task 5 的 44 例,接线前/后逐字节同)与**行为回归**(`tests/selfhost/test_iface_ops.py` 76 例:正控 / 负控 / 登记面三类);「不接线」列的理由全部来自**全行枚举 / 集等价 / 突变控制**(各任务报告 §突变节),不是读码推断。 + +| # | 现状宽松面(P2b 末行号 ← T4/T5 报告号) | 现状事实(行为探针) | 表中格 | P2b 处置 | P3 收紧建议 | +|---|---|---|---|---|---| +| 1 | 比较不校验操作数 `:1975-1977` ←`:1965-1967` | 6 个比较 op 恒 `TI_BOOL`,`(1,"a")`/struct/数组比较零诊断 | 6 比较位对**全 13 类**置 1(无拒绝路径) | 不接线(无门可换) | 加「可比性」判据(结构/序列需元素可比)后逐类收紧 | +| 2 | 算术门「任一侧数值即可」`:1969` ←`:1959` | `1 + [int;3]` / `"a" * 2` **静默为 int**;`"a" * "b"` / `*T + *T` → `error[TB01]`(负控钉红) | ADD..MOD **只含 {int, dex}**(门 = ANY 语义,两侧皆非数值才报错) | **原样保留**(接线后逐字等价) | 改 ALL 语义(两侧都须数值)或引入显式提升规则;须先有全语料证据 | +| 3 | 串拼接优先且单向 `:1951` ←`:1946` | `1 + "a"` → string(`OP_ADD` 一侧为串即成) | **无位**(早退规则留代码 = 结果规则) | 原样保留 | 拼接位独立建模(`+` 的 string 合法性上表);`1 + "a"` 是否应为 string 属语义裁决 | +| 4 | 一元透传 `:1994-1996` ←`:1979-1981` | `-"a"` rc=0(直接返回操作数类型) | NEG/NOT 对全 13 类置 1 | 原样保留(登记) | 数值性判据(NEG 只收数值类、NOT 只收 bool/int) | +| 5 | 解引用兜底透传 `:2018-2029`(兜底 `:2029`)←`:2003-2014` | `*x`(x: int)rc=0(非 REF/PTR/GPARAM 原样返回) | DEREF 对全 13 类置 1 | 原样保留(登记) | 兜底分支改为拒绝(REF/PTR/GPARAM 之外报错) | +| 6 | 索引**表达式**类型不校验 `:2618` 的 `idx_ti` 无消费者 ←`:2583` | `arr[true]` rc=0 | **无双侧位**:`IP_INDEX` 是**容器侧**位 | 本批未动 | 索引侧另设「须 int」位(ALL 语义;`IP_INDEX` 不能兼职) | +| 7 | 字段落空静默 `:2613` ←`:2603` | `x.a`(x 非 struct)/越界元组位/未知字段/方法当字段 **12/12 rc=0 零诊断** | `IP_FIELD` 恰 {product, named}(拒绝集与落空集**同集** = 恒真门) | 不接线(登记 P3 旋钮) | 落空分支加诊断(TS/TK 系);此时 `IP_FIELD` 才可被门推翻 | +| 8 | 转换无校验 `:2953` ←`:2932` | `"a" as int` / `arr as int` / `1 as S` … **10/10 rc=0**(除 PTR 结果规则外恒等透传) | `IP_AS` 对全 13 类置 1 | 不接线(无拒绝路径) | 转换合法性矩阵(源→目标允许表) | +| 9 | dyn = ⊤ 近似 + **逐行**方法表判定 `:1870-1893` ←`:1865-1888`(64 钳位 `:1834-1843`) | `d.nosuch()` → `error[N08]`——**int/string 候选行也报**(名拼接方法表判定,非类级) | `IP_METHOD` 恰 {dyn, named}(类级位表达不了逐行谓词;装上 = 抑制既有 N08 = 放宽) | 不接线 | ① (方法名 × 类型行) 二维判定;② 解除位图 64 上限(spec §3.3) | +| 10 | **range 索引非数组落空静默** `:2637` ←`:2611-2628` | 串/切片/int 的 range 索引 **rc=0 静默**(落空 `return TI_UNIT` 无诊断) | `IP_INDEX_RANGE` 恰 {sequence}(未接线) | 不接线(无诊断 ⇒ 门恒真) | 落空分支加诊断,或把「串 range」定为 slice 语义(**语义裁决**,须先有全语料证据) | + +**P2b 收紧清单(「旧接受 → 新拒绝」)= 空**(零条;反向「旧拒绝 → 新接受」亦零条)。证据 = 同源双编译器对拍(旧二进制 × 新源 == 新二进制 × 新源):21 档 `tests/suite` 语料 check 输出(stdout+stderr+rc)逐字节同 · 自源 `check src/compiler` 逐字节同(仅既有 2 条 TF01 误报 `lits_copy`/`ty_memo_slot_no_grow`,两侧一致)【**2026-09-13 更新(TODO #40 TF01 收口)**:`lits_copy` 为真·类型洗白(返回型改 `string`),`ty_memo_slot_no_grow` 为 checker 落空分析缺失(`stmt_cannot_fall_through` 落空分析入 checker.cr)⇒ `check src/compiler` 现 rc=0;本句为当期实跑记录】· 4 档 `.ccr` 逐字节同(`ecd7a9df…`/`891377232b…`/`35cf0f26…`/`dfb82d2b…`)· 32 档影子通道逐字节同 · ELF canary `95084e7b…d475`(开/关两态同);逐任务另有各自的前一任务基线对拍(Task 1~6 报告 §判据)。**站点台账(31 个未接线站点 + 接线面清单 + P3 裁决点)见 `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md` 附录 A。** + +--- + +## 13. P3a 后复跑(2026-09-12,R2 P3 收官——P3a 终态) + +**背景**:R2 P3a(Task 0/1/3/4/5,提交链 `61d3a8b4`→`ef61f002`→`e9818d62`→`c8c7731b`→`9c0a83ec` + 收官提交)落地:§6 的 F4 由 Task 5 关闭(F1 已在 P2a、F2 的**方向/变型面**由 Task 1 接续)+ 三条新面(引擎展开层 / match 穷尽性 / 联合可选)。本节 = **终态**在同一批 72 档语料上的复跑(口径同 §11/§12:逐档 `clean-cache`、cwd = 仓库根、对拍 = 旧二进制 × 新源 vs 新二进制 × 新源;站点覆盖与差异数**同报**)。 + +**终态数字(2026-09-12 收官复跑;新旧两二进制逐值同)**: + +| 指标 | 值 | +|---|---| +| 文件 | 72(70 有摘要;2 = 0 字节空 fixture,同 §2 排除项) | +| 判定数 / agree | **30,128 / 30,128** | +| `old_stricter` / `old_looser` | 0 / 0 | +| `unknown`(含 bridge/engine/uncovered/budget 四拆因) | 0 | +| `replace_bridge` / `replace_unknown`(判定路径回落) | 0 / 0 | +| check 面对拍(rc + 日志) | `diff -r` **空**;rc 分布 33×rc=0 / 39×rc=1(两二进制同) | +| shadow 日志 / dump 对拍 | `diff -r` **空**(两处) | +| 站点直方图 | `assign-node=22785 · fn-body-ret=5338 · if-branch=1971 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`;站点 1/2/4/6 零命中、3 = 2 次 | +| `sum(sites)==decisions` / `agree==decisions` | True / True(独立重算校验) | + +**判定数口径变化(30,338 → 30,128,−210)——精确归因**:唯一来源 = 收官发现的回归修复(计划附录 B.6:删 `src/targets/x86_64-linux/_import.cr` 的 `import monomorph`)⇒ monomorph.cr 退出 target project 两档(`main.cr` / `_import.cr`)的导入闭包。**逐档切换实测**:target `main.cr` 3577 → **3472**、target `_import.cr` 3542 → **3437**(各 −105,合计恰 −210 = 总差);同两档的 `error[N06]` 从 **33 → 0**(该修复的直接目的)。语义面零变化 = 同源对拍三面空(上表)。 + +**P3a 逐任务对拍(汇总,不重推导;出处 = 各任务报告 §3/§4)**: + +| 任务 | 对拍结果(旧二进制 × 新源 vs 新二进制 × 新源) | 判定数(同批口径) | +|---|---|---| +| T0 | check/shadow/dump 三面 `diff -r` 空 | 29,090 / agree 29,090 | +| T1 | 三面空 | 29,267 / agree 29,267 | +| T3 | check 面**仅 2 处行号位移**(既有 TF01 误报随新增源码行漂移;诊断集合/条数/rc 全同);shadow/dump 空 | 29,442 / agree 29,442 | +| T4 | check 面逐档**诊断码集合与 rc 同**(3 档自源行号位移);dump 与 T3 逐字节同 | 29,687 / agree 29,687 | +| T5 | **0 档差异**(rc + 诊断码 + 逐条消息含行号全等);dump 与 T4 逐字节同 | 30,338 / agree 30,338 | +| 收官 | 三面空(修复后源;上表) | **30,128 / agree 30,128** | + +**P3a 面在语料上的命中 = 0**:新能力面(展开层/方向/变型/mut/穷尽性/`T?`/Some·None/泛型约束)在 72 档语料**全部零命中**(§3 语料事实:零 match、零 enum 声明、零 `T?`、零约束语法)。⇒ 上表「零差异」**不是**新能力的正确性证据,正确性由各任务行为探针(T1 19 · T3 22 · T4 14 · T5 16 例)+ 用例表(288)+ 新套件(16+12+14 例)+ 突变控制承担(同 §11/§12 的效力范围口径)。 + +**§12 事实表(九条宽松面)在 P3a 后的状态 = 全部未动**(比较不校验操作数 / 算术门 ANY / 串拼接单向 / 一元透传 / 解引用兜底 / 索引表达式类型 / 字段落空静默 / 转换无校验 / dyn 逐行 + 64 钳位 / range 索引落空静默)——P3a 的能力面在 §6 与计划任务面;§12 的「公理区操作许可」面仍是 P3b/P4 的收紧对象。与 §12 相邻、P3a 已动的只有 **N/固定性面**(§6-F2 的方向/变型收口,§12 未列)。 + +**收紧/放宽台账(P3a 汇总)**:收紧 **18** 条(T0 0 / T1 6 / T3 6 / T4 3 / T5 3;其中 1 条 rc 不变、仅诊断码集合扩张)+ 诊断面新增 1(TM04 软)+ 放宽 **8** 条(T4 7 含 1 条非语义放宽 / T5 1)+ 实例名忠实化 6(T5)+ 挂死修复 2 端(T4);**全语料零命中** ⇒ 自举源码零改造。逐条 = `docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md` **附录 B.2**;报告 = `.superpowers/sdd/p3-task{0,1,3,4,5,7}-report.md`。 + +**新登记面(P3a 引入/更新;详版 = 附录 B.4)**:非枚举域不判穷尽 · 空枚举域 = 空洞穷尽(判据 1)· 空递归保守 -1 · MAX_* 族处置(MAX_GENERICS / MAX_ENUM_VARIANTS / MAX_VARIANT_TYPES)· 运行期可选表示未统一 · witness 空析取支(反例走覆盖位)· `EXPR_ENUMPAT` 名字槽 = `ast_a` · bootstrap `&&`/`||` 短路根因(T4 只关触发链)· `EXPR_LET` 无检查(TODO #32)· 符号档长度约束(VC 义务,显式登记不实现)。 + +**残差与交 P5**:同 §11 尾(`type_equal_legacy` / `sh_*_ak_legacy` / 站点 6 / 影子层下线)——**状态更新**:T0 的展开层按设计只服务满足判定/域查询 ⇒ §11 的「unknown 清零前提 = 引擎命名展开」**不会被满足**(等价面展开属裁决);P5 须裁「接受命名展开(推翻 P2a 对拍基线)」或「legacy 长期化」(见计划附录 B.5)。 + +## 14. P4 后复跑(2026-09-13,R2 P4 收官——载体批终态) + +**口径**:pre-P4 二进制(P3b 终态构建,sha `9a583215…`)× **当前源** vs 当前二进制(`5d2b15ad…`)× **当前源**;语料 = 72 档(`tests/suite` + 自源 `main.cr`/`_import.cr` + 后端/内核单元 + `src/stdlib` + `examples`;runner `/tmp/p3t0_run.sh`,冷缓存逐档 `clean-cache`),两面 {`check`, `check --type-shadow --type-shadow-dump`}。 + +| 面 | pre-P4 侧 | 当前侧 | 结论 | +|---|---|---|---| +| 影子摘要(70 档出摘要) | `decisions=32620 agree=32620`,`old_stricter`/`old_looser`/`unknown*`/`replace_*` 全 0 | 同值(逐档同) | **判定面零变化** | +| 站点覆盖 | assign-node=24721 · fn-body-ret=5828 · if-branch=2037 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2(其余 0) | 逐项同(Σ = 32620 = decisions) | 同上 | +| check 日志面 | 8 档差异,**100% = TF01 收口类**(5 处 `ty_memo_slot_no_grow` 误报消失 + 3 档 rc 1→0) | — | P4 内**有意修复**,非语义回归 | +| rc 分布(72 档) | 31×0 / 41×1 | 34×0 / 38×1(恰 3 档 1→0) | 同上 | + +- **dumps 通道说明(勿当强证据)**:两侧 `dumps/` 目录**均无文件** ⇒ 常被引用的「dumps `diff -r` 空」是**空洞证据**——`sh_dump_write`(`ty_shadow.cr:651`)在无差异条目(`g_shadow_ring_count ≤ 0`)时早退、不落盘。真证据 = 上列摘要全 0 + 站点覆盖同报 + rc 分布差恰为 TF01 面。 +- **效力范围(继承 §11/§13 口径)**:影子通道只覆盖 `type_equal` 站点面——**对 P4 载体面(TYPE/IFACE 段、corearch 读回、DFNode.TK 项槽、运行期表示位)零覆盖**;载体正确性证据 = 结构性断言(`test_ccr_types` 34 例)+ corearch 读回对拍(`--dump-types`/`--dump-ifaces`)+ 定向探针,见计划 Task 1–6。 +- **承接 P5**(同附录 D-3):`type_equal_legacy` 删除 / unknown 清零(前提不会被自动满足,须二选一裁决)/ `sh_*_ak_legacy` 桥接缓存退出生产路径 / 站点 6 去留(须先补站点评级说明)/ 影子层下线。P4 未新增站点、未改站点语义(本表可作 P5 的基线)。 + +## 15. 影子通道终态(R2 P5 Task 5,2026-09-13——**通道下线;本节 = 最后一次基线**) + +**通道下线**(D24/D26):对照物 `type_equal_legacy` 已在 P5 Task 4 删除 ⇒ 判定对账 `sh_compare` 无调用点、停摆(中间态);P5 Task 5 按 D26-① **整体删除**——站点挂点(checker 9 处)/ 判定对账 / 差异环缓冲(前 256 条)/ 摘要 `[type-shadow]` / 站点直方图 `[type-shadow-sites]` / 转储 `--type-shadow-dump` / CLI 旗标 `--type-shadow` / 全局态 `g_shadow_*`(17 个)/ 桥接调试计数 `g_shadow_hits`(含存-复原补偿 hack)。桥接缓存**保留**(生产面)并改名 `g_shadow_map` → `g_term_map`。**本文件此后的任何计数均不可复现**(通道已不存在;`--type-shadow` 不再是合法旗标)。 + +**终态基线(本任务实测,起点二进制 `eae13428…`(P5 T4)× 72 档语料,逐档 `clean-cache`;runner `/tmp/p3t0_run.sh shadow …`)**: + +| 面 | 终值 | +|---|---| +| 影子摘要(70 档出摘要) | **70/70 档 `decisions=0 agree=0 old_stricter=0 old_looser=0 unknown=0 unknown_bridge=0 unknown_engine=0 unknown_engine_uncovered=0 unknown_engine_budget=0`** | +| 站点直方图 | `assign-node=24996 · fn-body-ret=5911 · if-branch=2045 · struct-field-type=28 · array-elem-type=4 · generic-apply-base=2`(`assign-binary`/`generic-arg`/`hotpatch-ret`/`unify-fallback` = 0) | +| `check` rc 分布 | 34×0 / 38×1 | + +**沿革对照(各时点口径,仅历史)**:P1 26,704 次判定(`old_looser=0`,unknown 9 = 同名多行)→ P2a 26,989 agree 全额 → P3a 收官 30,128 → P3b 收官 32,620 → P5 T2 `32767` → P5 T3 `32917` → P5 T3b `32922` → P5 T4 `32988`(**最后一次有 meaning 的读数**:`decisions=agree`,全差异桶 0)→ **P5 T4 后停摆 `decisions=0`**(对照物已删)→ **P5 T5 通道下线**。 + +**口径继承(对后续批次 = 强制)**:① **N 面(数组长度)对拍恒 0 是失明而非等价**(legacy N-free)⇒ N 面证据只能来自行为探针;② 本通道对**载体面**(TYPE/IFACE 段、corearch 读回、DFNode.TK 槽、运行期表示位)**零覆盖**——载体正确性证据 = 结构性断言 + 读回对拍 + 定向探针;③ 「零差异」只在通道存活期 = 证据,此后引用 = 空洞。**替代网与判据见 `TODO.md` #48 与 `.superpowers/sdd/p5-task5-report.md` §5。** + +## 16. P5 收官(R2 P5 Task 7,2026-09-14——终态复核;**本文件自此冻结**) + +**§15 之后不再有新影子读数**(通道已删,`--type-shadow` 不再是合法旗标、`g_shadow_*` 全局已不存在)⇒ **本文件冻结**:不再增「X 后复跑」节;本节 = 收官复核与替代网口径的唯一收口点。 + +**终态复核(T7 实跑)**:五 CI job 全 rc=0 · 全枚举 **58/58**(51 selfhost + 7 bootstrap)· `selftest-types` **404/404** · ELF canary `95084e7b…d475`(28822B)IDENTICAL · `corec2==corec3` `cmp` IDENTICAL(`5559beef…`)+ N06=0 + 冒烟 42 · **冻结基线同源对拍**(pre-P5 二进制 `5d2b15ad…` × 当前源 vs `f737e26b…` × 当前源;72 档 `check`)**rc + 日志零差异** · **行为探针全集 30 档零差异** · 突变控制(M1 派生面 / M2 判定面)逐条转红 + 精确回滚。统一台账 = 计划 `plans/2026-09-13-r2-p5-cleanup.md`「统一台账」节;P6 交接 = 同计划 **附录 E**。 + +**替代网口径(强制继承)**:判定面回归网 = ① 冻结基线同源对拍 ② 行为探针 ③ 突变控制 ④ 三态纪律(ICE04/D23:未知/不可译 ⇒ 拒绝,绝不静默当 0/1)。**任何后续批次不得引用本文件 §1–§15 的计数/摘要/站点直方图**(§15 已声明不可复现)。**载体面判据**(段/读回/TK/表示位)= 结构性断言 + corearch 读回对拍 + 定向探针(本通道对其零覆盖——历史声明继续有效)。 diff --git a/docs/superpowers/specs/2026-09-10-x86-instance-design.md b/docs/superpowers/specs/2026-09-10-x86-instance-design.md new file mode 100644 index 00000000..26c4223a --- /dev/null +++ b/docs/superpowers/specs/2026-09-10-x86-instance-design.md @@ -0,0 +1,191 @@ +# x86 实例化设计(三轴组合:架构 × 格式 × OS) + +日期:2026-09-10 +状态:**已实施(波 1 结构波 2026-09-10 Task 1-7 收官——plan `2026-09-10-x86-instance-wave1.md`:三轴目录搬迁 + 组合根 `src/targets/x86_64-linux/` + 序列算法文件化(entry/frame/tag2l/callseq/syscall)+ 表数据迁架构轴 + 构建清单分段守卫;判据 = 行为零变化全绿)**——波 2 参数化 / 波 3 能力未启;执行注记见 §8(步骤 3——蓝图演进序第 3 步) +性质:**实例层设计裁决**——把 x86/ELF/Linux 的全部定制内容按三轴归位到实例边界内(内核零改动——规则封闭);判据按边界 C4 = 行为等价 + 投影正确性方向。实施分波(结构波先、能力波后)。 + +关联: +- `docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md`(蓝图——§1.2 实例组成/§2 裁决表/§3 演进序步骤 3 = 本设计的窗口;"实例边界内的数据化/参数化 = M2 复启的接续形态") +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(边界——C2 输出实例形态/C3 核心只含通用机制/C4 判据裁决:逐字节复刻废止 → 行为等价/投影正确性) +- `docs/superpowers/specs/2026-09-10-corearch-kernel-completion-design.md`(内核完备化——三组件完成态;本设计消费其契约面:登记 API/对象面/声明表) +- `docs/regalloc-cache-mapping.md`(寄存器分配 = 缓存语义映射实例——切分的已定稿基线) +- `docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(HIT——表 = 实例数据形态;M2 挂起中,本设计的表归属条款为其复启前提) + +--- + +## 0. 定位与商讨结论(2026-09-10 用户拍板) + +蓝图 §3 步骤 3 = 实例边界内的数据化/参数化。商讨结论: + +1. **实例 = 三轴组合**:架构(x86_64)× 格式(ELF)× OS(Linux)——前两个轴大体通用(架构目录跨 OS 复用、格式目录跨架构复用),OS 轴最薄。三轴皆遵循同一模式:**通用机制 + 特化参数**(与 hit 的引擎/表数据同构) +2. **寄存器分配切分原则**:需求推导(哪些条目要位置/窗口/可共享——语义侧)vs 物理选择/搜索/编码(机器侧)——切分表见 §4;**先结构后能力** +3. **spill/驱逐 = 独立能力波**:设计预留契约形状(needs_eviction 激活 → 判定③④ 引擎扩展 = 注册契约演进的正式演练),实施后置 + +## 1. 实例 = 三轴组合 + +``` +架构(src/arch/x86_64/) × 格式(src/format/elf/) × OS(src/os/linux/) + 指令/寄存器/编码/序列 容器/段/重定位/链接 syscall/入口/ABI 约定 + └── 构建设置选择组合 = 一个实例配置(x86_64 × elf × linux) +``` + +- 组合选择机制:构建时(build_selfhost_native.py / Core.toml)声明三轴选择 + 交叉适配参数(如 ELF 的 machine ID / reloc 类型 = 格式层的架构特化参数位——由架构轴提供) +- 换实例 = 换轴组合(arm64 × elf × linux = 换架构轴;x86_64 × pe × windows = 换格式+OS 轴)——内核与其余轴零改动(规则封闭的实例级验收) +- ABI = 交叉轴:机器寄存器集(架构轴)× 调用约定(OS 轴)——callseq 归 OS 轴、参数寄存器表引用架构轴 + +## 2. 三轴目录与文件归属 + +``` +src/arch/x86_64/ ← ① 架构轴 +│ instr.cr / sizes.cr 指令编码(自 ld/ 迁) +│ regalloc.cr 寄存器分配 CAG(自 ld/ 迁——实例算法) +│ frame.cr 帧布局(自 elf.cr 抽取——序列算法文件化) +│ tag2l.cr tag/2L 编码选择(自 instr.cr/序列面抽取) +│ core-x86.toml 表数据(自 src/arch/hit/ 迁入——蓝图裁决:表 = 实例数据) +src/format/elf/ ← ② 格式轴 +│ elf.cr 容器/段/phdr(泛型骨架——自 ld/ 迁) +│ resolve.cr 重定位(自 ld/ 迁) +│ ld.cr 链接机制(自 ld/ 迁;动态链接的 Linux 特有面→OS 轴交叉) +│ (架构特化参数位:machine/reloc 类型 = 架构轴提供的数据——交叉适配) +src/os/linux/ ← ③ OS 轴 +│ syscall.cr syscall 约定(自序列面抽取) +│ callseq.cr 参数传递/调用序列(SysV ABI——自 instr.cr 序列面抽取) +│ entry.cr 入口/启动集成(自 elf.cr emit_start 面抽取) +src/arch/hit/ ← 表引擎(保持——未来实例可复用的框架:hit.cr 加载查询 +│ + lower_to_core + 注入通道;模板解释器在 instr.cr = 实例内) +src/lattice/ / src/kernel/ ← 语义内核 / 验证内核(已落,本设计不动) +src/runtime/ ← 运行时(rt.s——OS 轴补充) +``` + +- **序列算法文件化** = 本设计的实质内容之一(现散在 elf.cr/instr.cr 巨型文件里的帧/调用/syscall 序列显式成文件——C2/C3"实例算法"的目录化) +- 平铺不设子目录(文件规模 10 级——concat/导入机制成本不值;文件增多再分) +- ld project-mode 入口(main.cr)随迁 OS 轴或架构轴——**实施计划定夺点**(其 flag 注册分歧 = TODO #6 的处置并入本设计实施波) + +## 3. 构建清单分段(按轴组织) + +- `build_selfhost_native.py` 清单重构为分段组合:`common_files`(shared:globals/dyn_arr/ast/ccr_io 等)+ `kernel_files`(lattice 内核)+ `non_x86` 无关面(corec 前端)+ `arch_x86_64_files` + `format_elf_files` + `os_linux_files` + hit 引擎段——concat = 按确定顺序组合 +- 守卫(吸收 ld 单元静默缺陷教训):①清单文件存在性断言(防路径漂);②"目录新文件未入清单"检查(构建设置期提示);③构建日志 `error[` 计数非零 = 失败门(TODO #6 建议③的落地) +- corelsp 等其余 concat = 同一分段机制复用 + +## 4. 寄存器分配切分(CAG 五阶段侧归属)与能力演进预留 + +### 4.1 切分判定表(原则——实施波按需) + +| 阶段 | 内容 | 侧归属 | 依据 | +|---|---|---|---| +| 1 门控扫描(DEX/DYN/UNIT/CONST/REF/I2F 排除) | XMM 路径/imm 编码理由 | 实例 | e2_* 发射器能力反射 | +| 2 回边探测 + 携带值标记 | 跨回边存在性/条件定值差分 | **内核候选(需求推导)** | 条目/区域语义——跨范式通用 | +| 3 候选窗口构建 | 区间+扩窗 | **内核候选(需求推导)** | 存在区间推导 | +| 4 First Fit 搜索 | 不透明位置的搜索 | 灰区——搜索泛型、可用位置集 = 实例域 | 实施按需(YAGNI:单一实例时留实例侧) | +| 5 终分配 ≤5 对/块写 meta | 块格式编码 | 实例 | 编码 | + +- **实施节奏**:需求推导上收 = 原则记录 + 按需(实例 B 出现前的真收益低);本设计波次先物理侧归位/参数化 + +### 4.2 能力演进预留(spill/驱逐——契约形状预演,实施后置) + +- **激活点**:`needs_eviction=1` 的实例声明 → 判定③(驱逐配对)引擎扩展 +- **双向契约三段形状**(定稿): + 1. **实例→内核**:登记(kern_loc_*——已立)+ 驱逐/替换事件登记(预留 API 位) + 2. **内核→实例**:判定输出(violations/共存证据——已立)+ **持久条目 home 需求查询**(条款 4b:无配方条目必须 home 保真——预留接口位,现 IR 面无实例 = 空槽) + 3. **判定③④ 引擎实现**:needs_* = 1 时的契约演进点(现 {0,0}——静态放置无事件/callee-saved 平凡满足) +- spill 引入同时解锁:判定④(调用点失效——被调用者保存集与调用点未维护条目的配对检查) + +### 4.3 判据 + +- 结构波(波 1-2):**行为零变化**——全回归 + backend_bootstrap stage 链 byte-identical + ccr_v7 产物 byte-identical + full-bootstrap guard(纯搬移/文件化证明) +- 能力波(波 3,后置):行为等价按边界 C4(**非逐字节复刻**——实例算法可自由设计,正确性判据 = 投影正确性方向)——M2 复启的判据修订同步落地 + +## 5. 演进波次(实施另立) + +1. ~~**波 1 结构**:三轴目录重组(文件迁移)+ 序列算法文件化(frame/callseq/tag2l/syscall/entry 抽取)+ 表数据迁架构轴 + 构建清单分段 + 守卫——判据 = 行为零变化(纯搬移)~~ ✅ **完成(2026-09-10,plan `2026-09-10-x86-instance-wave1.md` Tasks 1-7,提交 29d177ac49a5 / b23f008000cf / b0ba8c1c9b01 / d65fc1b8f3c3 / 3ab39f6eaa16 / 94d1cac15359 + 各自 docs 提交;执行注记 = §8)**——三轴目录(`src/arch/x86_64/` + `src/format/elf/` + `src/os/linux/`)+ 组合根 `src/targets/x86_64-linux/` 落位;序列算法文件化五件(entry 整搬 / frame 抽取含帧公式双源合流 / tag2l 整搬 / callseq 抽取含 SysV 三处同源合流 / syscall 抽取);表数据 `core-x86.toml` 迁架构轴(hit 引擎留原位);清单八段化 + 存在性守卫 + `error[` 计数门。判据:**行为零变化全绿**(每任务 stage 链 byte-identical + 收官全量回归逐套计数 + full-bootstrap guard corec2/corec3 cmp 同 + N06=0)+ 自举重建冒烟 + syscall4 套件持久覆盖(`tests/suite/syscall4_test.cr`) +2. **波 2 参数化**:序列算法按实例边界参数化/整理(C2/C3 实例算法形态——含 TODO #6 双入口处置) +3. **波 3 能力**(后置独立):spill/驱逐 + 判定③④ 激活 + 双向契约第三段落地 + M2 复启(判据按 C4 修订) +4. 远期:实例 B(arm64 或非经典)——三轴组合验证(换轴零内核改动 = 规则封闭验收) + +## 6. 挂账与风险 + +- **M2 交互**:HIT 表数据迁架构轴(波 1)与 M2 挂起状态——搬迁判据 = 全回归(表路径测试 hit_table 24/24 保持);M2 复启时表定位(引擎/数据分离)已由本设计解决。**波 1 已实施**:表数据 `src/arch/x86_64/core-x86.toml` + 引擎 `src/arch/hit/` 分离落位,hit_table **24/24 保持** ✓(M2 挂起态未动——本波只搬定位面) +- **rc/cmp 判据**:文件移动不改产物(asm 后端无 .loc——评审曾实测注释变化 asm byte-identical);stage 链 run 内 byte-identical 判据保持 +- **main.cr 双入口**(TODO #6):波 2 处置(注册收敛或显式分歧留档)。**波 1 已搬未收敛**——双入口之一随 Task 1 迁入组合根(`src/arch/linux/ld/main.cr` → `src/targets/x86_64-linux/main.cr`),分歧本体(ld project-mode 入口缺 HIT/表旗标注册)逐字保持、`test_backend_bootstrap` stage 链仍走该入口;按 H7 纪律——波 1 只同步注记措辞、flag 面收敛留波 2 +- **架构特化参数位**(格式层的 machine/reloc):波 1 仅搬迁不建参数机制;交叉适配机制 = 波 2 或实例 B 时定 +- **波 1 评审裁决遗留**(全部登记 TODO,**均不改波 1 结构面**):TODO #8 = 前端 ≥18 形参静默误编译类(高优先级——根因前端参数表/AST bookkeeping,本波零接触;其 ④ 项「22 参 runtime 用例入 tests/suite」= 该项缺陷修复后才可达,**非波 1 收口条件**);TODO #9 = `IR_CALL_EXTERN` >6 int 参语义缺口(callseq.cr 指针落地——波 1 按"零变化"逐字保留预存不对称,修复 = 波 2 FFI 面);TODO #10 = `src/format/elf/elf.cr` 三处手写 syscall 序列(mmap/clone/exit,syscall1/5/6 形)留在格式轴 = **实例 B「换轴零改动」承诺的证伪面**,收编 `src/os/linux/` = 波 2 / 实例 B 前置(Task 6 只抽 syscall3/4 内置体发射面,未扩面) +- **本设计不承诺**:需求推导上收(§4.1 内核候选——按需)、spill/驱逐实施(§4.2 预留)、格式轴跨 OS 全面参数化(PE 出现前 YAGNI) + +## 7. 关联同步项 + +- 蓝图 §1.2/§3 步骤 3 → 本设计为其定稿实现(波 1 已实施,见 §5 波 1/§8) +- TODO #6(双入口分歧)→ 波 2 处置(波 1 已搬未收敛,见 §6);TODO #4/#5 不受影响 +- TODO #8/#9/#10(波 1 评审裁决登记)→ 全属波 2 / 后续专项,本波只登记不改结构面(见 §6 末条) +- M2 计划/spec:复启时按本设计表归属 + C4 判据修订执行 +- 目录分层先例(src/lattice 搬迁 712946a0)机制复用(module.cr 回退链/清单/守卫) + +## 8. 执行注记(波 1 收官回填——2026-09-10 Task 1-7) + +> 本节约 = 设计落地确认(plan `2026-09-10-x86-instance-wave1.md` Tasks 1-7)。 +> 提交落点:Task 0 盘点 327c2cf01af3(设计定稿 6694f358f376)/ Task 1 三轴搬迁 + 组合根 +> 29d177ac49a5(+ 补正 c927525baf35、docs 58e6be3a1c18)/ Task 2 entry.cr 整搬 +> b23f008000cf(+ docs 8dcfaeca52b2)/ Task 3 frame.cr 抽取 b0ba8c1c9b01 / Task 4 +> tag2l.cr 整搬 d65fc1b8f3c3(+ docs c6b6cbfe9a75)/ Task 5 callseq.cr 抽取 +> 3ab39f6eaa16(+ docs 525a872a0ba7)/ Task 6 syscall.cr 抽取 94d1cac15359(+ docs +> d2f3e0968d8c)/ Task 7(本状态行随收官提交落盘)。行为零变化判据(§4.3 结构波) +> 全绿——**逐任务 stage 链 byte-identical**(搬迁任务 R 重命名内容 verbatim;抽取任务 +> 内容进新文件 + 调用点替换,每步独立构建验证)+ **收官全量回归逐套计数**。 + +### 8.1 落点摘要(三轴 + 组合根 + 清单) + +- **架构轴** `src/arch/x86_64/`:`instr.cr`(指令编码)/`sizes.cr`(字节尺寸单源)/ + `regalloc.cr`(CAG)/`core-x86.toml`(HIT 表数据,自 `src/arch/hit/` 迁入)——Task 1; + `frame.cr`(帧布局:`pf_frame_size`/`pf_prologue`/`pf_epilogue` + `g2_tag_off`/ + `mw_frame_size`,**H2 帧公式双源合流单入口**)——Task 3;`tag2l.cr`(mw 族纯编码整搬) + ——Task 4。tag2l ↔ frame 同轴双向引用 = 裁定可接受(注记随代码)。 +- **格式轴** `src/format/elf/`:`elf.cr`/`resolve.cr`/`ld.cr`——Task 1(纯搬)。 +- **OS 轴** `src/os/linux/`:`entry.cr`(`emit_start`/`emit_start_size` 整搬)——Task 2; + `callseq.cr`(`cs_args_dispatch`/`cs_arg_on_stack`/`cs_stack_count`/`cs_stack_args`/ + `cs_stack_cleanup`/`cs_ret_value`/`cs_call_direct`——SysV 三处同源合流,per-slice + 字节比对核对后合并;`IR_CALL_EXTERN` 预存不对称**未合并未顺手修** = TODO #9)——Task 5; + `syscall.cr`(`sys_syscall3_stub`/`sys_syscall4_stub`,r10 第四参约定)——Task 6。 +- **组合根** `src/targets/x86_64-linux/`:`main.cr` + `_import.cr` + `Core.toml` + (target triple 命名;project-mode 入口链,不入 concat)——Task 1。stage0 硬编码点 + `test_backend_bootstrap.py:13` **先改后搬**(H6)。 +- **清单分段 + 守卫**(`build_selfhost_native.py`):八段化(`common_files`/`hit_engine_files`/ + `kernel_files`/`arch_x86_64_files`/`format_elf_files`/`os_linux_files`/`backend_support_files`/ + `x86_linux_target_files`);守卫 = 清单文件存在性断言 + 构建日志 `error[` 计数非零 = 失败门 + (TODO #6 ③ 建议的构建面落地)+ project-mode 面 `run_checked` 同门(`test_backend_bootstrap.py`, + Task 1 补正 c927525baf35)。 + +### 8.2 收官判据(Task 7——逐套计数) + +构建(清 `.core/cache`,`nice -n 19 python3 build_selfhost_native.py`):BUILD SUCCESS; +四段守卫全 OK(corec 37 文件 / corearch **31** 文件 / x86_64-linux target 3 文件 / corelsp 26 文件), +构建日志 `error[` = 0(三产物各 0)。 + +| 套件 | 计数 | +|---|---| +| `test_compile.py` | 3/3(interp run / lexer 源编译 / 自编译 native) | +| `test_backend_bootstrap.py` | **11/11**(stage1=stage2=stage3 byte-identical + O0/O2 冒烟 rc=7) | +| `test_hit_table.py` | **24/24**(表路径保持——M2 挂起态不动) | +| `test_region_cfg.py` | **22/22** | +| `test_slice_bounds.py` | **7/7** | +| `test_live_ranges.py` | **13/13** | +| `test_ccr_v7.py` | **24/24** | +| `test_ent_kernel_neutrality.py`(中立性 guard A+B) | PASS ×2(A clean / B clean) | +| `test_mw_task1..6.py` | 1-6 全 **ALL PASS**(1 含 `s7_framediff` 帧差专项——H2 判据) | +| bootstrap 三套 | pipeline **29/29** / borrow **4/4** / generics **3/3** | +| full-bootstrap guard | `corec → corec2` rc=0、`corec2 → corec3` rc=0、**cmp byte-identical**、两段 `error[N06]=0` / `error[` 总 0;`corec3 --help` rc=1(CI 断言形) | +| 重建冒烟 | `corec run` rc=42 / `corec check` rc=0 / `corec build` rc=0 → 运行 rc=42 / `corec3 check` rc=0 | + +**syscall4 持久套件覆盖**(波 1 遗留 carry——原覆盖仅 /tmp 探针):新增 +`tests/suite/syscall4_test.cr`(`import io` + `import os`;`system("exit 3")` → 断言 rc==3、 +`system("exit 42")` → 断言 rc==42 以排除常量/陈旧缓冲区回传;suite 惯例 = main 返 0 为通过, +失败回 1/2)。构建日志 `error[` = 0,运行 `ALL PASS` rc=0(`build/corec` 与 stage 链 `corec3` +两工具链均绿)——覆盖 `os.cr` × `wait4` × `syscall4` r10 第四参径。 + +### 8.3 本波**未**做(范围克制——波 2 起) + +- `IR_CALL_EXTERN` 栈参/栈清理不对称未修(TODO #9);前端 ≥18 形参缺陷未修(TODO #8)—— + 两者皆预存、与结构波正交,按「零变化」纪律不混入。 +- `src/format/elf/elf.cr` 三处手写 syscall 序列未收编(TODO #10);内置体名索引扫描段留原位 + 加注(`elf.cr` 877-886)——整段参数化 = 波 2。 +- `pf_frame_overhead` 预存双计(无发射影响)未清;`sizes.cr` tag2l 同源化声称已软化(值一致 + 尚未同源)——Task 4 评审注记,波 2 清。 +- 架构特化参数位(格式层 machine/reloc)未建机制;spill/驱逐(§4.2)未启。 diff --git a/docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md b/docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md new file mode 100644 index 00000000..da86aa44 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md @@ -0,0 +1,570 @@ +# 可解释 · 可预测 · 语义增量设计(S-D) + +日期:2026-09-11 +状态:**设计定稿(2026-09-11 收敛批 v3)**(三条线索各自可实施 + 路线图 §1.4 确定性报告面并入;标注「待论证」的条目除外——见 §0.3 裁决表与 §7.2 开放问题)。**收敛批 v3 变更**:本地裁决编号统一为 `L`(原 `D`,与用户裁决 `★D` 撞号——B 线 N2;对照表见路线图 §4)· 边界来源与 S-A 对齐(B 线 N4)· v7 段位表述精确化(B 线 N8) +性质:路线图 §2 清单 **S-D 行**的展开。本 spec 只填自己的范围;术语与不承诺**逐字承袭路线图 §1**,不重定义。 + +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**:§1.1 术语 / §1.2 原则 / **§1.4 确定性轴与「不确定性 = 显式请求」(2026-09-11 追加)** / §1.3 三类不可消残留 / §2 S-D 行) +- `docs/superpowers/specs/2026-09-11-merge-semantics-design.md`(**S-E**:`merge_deterministic` / `merge_ordered` / `race` / `select_any` 四形态与 `select` 重定——**本 spec 不定义**,只归因呈现) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2 引擎:`ty_sub`/`ty_equiv`/`ty_inhabited`/`ty_witness` 三态 + 预算守卫——explain 与义务判定的**接口**) +- `docs/superpowers/specs/2026-08-28-lsp-production-design.md` + `2026-08-08-lsp-design.md`(LSP/coreview 基建:输出面的既有承载) +- `docs/superpowers/specs/2026-07-29-incremental-cache-design.md`(现状函数级 `.cir` 缓存)+ `TODO.md` #5(缓存键家族,含 2026-09-11 `.ccr` 缓存态方差同族条目) +- `TODO.md`「边界判定图 pass(bounds-inference)」(上界/可预测的既有种子)+「性能自动化」族(成本模型交 S-C) +- `docs/superpowers/specs/2026-08-15-cache-semantics-design.md`(缓存语义七条:值=配方 / 驱逐不变量 / 再生等价 / 边界公理 / 赋值=版本化 / 地址=映射 / 映射实例正确性) +- `docs/superpowers/specs/2026-07-30-concurrency-design.md`(go/channel/每 goroutine arena)+ `docs/verifier/kernel-spec.md` + `src/kernel/`(证明内核) +- 实现落点:`src/compiler/cir_cache.cr`、`src/compiler/main.cr`、`src/compiler/ext_safety.cr`、`src/compiler/ir_gen.cr`(F11 长度侧表)、`src/compiler/type_engine.cr`、`src/compiler/dataflow.cr`、`src/lsp/{analysis.cr,json.cr}`、`src/stdlib/{goroutine.cr,sched.cr}` + +--- + +## 0. 范围与裁决 + +### 0.1 契约承袭(路线图 §1,逐字遵守) + +- **术语**:本 spec 使用且不重定义 §1.1 全部条目——**属性轴** / **义务** / **四档义务**(① **prove** ② **enforce** ③ **proof-required** ④ **reject**)/ **policy** / **declassify** / **sink / source 模型**。 +- **原则**:遵守 §1.2 五条。本 spec 的两个落点直接引用:原则 4「**判定统一走引擎**」——§2 的资源约束判定与 §3 的失效判定不得另建判定体系;原则 5「**声明式上限 + 命令式下限**」——本 spec 只碰声明式上限,region / state edges / 调度 / arena 的**命令式下限不动**(这正是 §2 上界可以「读图」而非「改图」的原因)。 +- **确定性轴族(路线图 §1.4,2026-09-11 追加)**:`determinism` 族并入 §1.1 轴表——`D_value` / `D_effect` / `D_memory` / `D_timing` / `D_resource` / `D_external` / `D_mapping`;执法 = §1.1 四档义务(路线图 §1.4 的同构表)。论题逐字承袭:**每条可观测的不确定性,必须可归因到「显式语义源」或「已证明的汇合无关性」**。本 spec 承担其中的**报告面**(§1.7 `core analyze --determinism` + 不确定性 provenance 链)。 +- **不承诺**:§6 逐条引用 §1.3 三类不可消残留,并登记本 spec 特有的四条。 +- **本 spec 不拥有的面**(写清边界,避免与兄弟 spec 交叉): + - **义务阶梯本体、属性轴本体(含 `determinism` 轴族的落档)、policy 接口** → S-A(本 spec 只**实例化/呈现**,见 §2.5、§1.7.4)。 + - **汇合语义本身**(`merge_deterministic` / `merge_ordered` / `race` / `select_any` 的图级定义、`select`「最早到达」现语义的重定与兼容窗口)→ **S-E**(路线图 §1.4 边界逐字:**它改的是执行模型,不得稀释进通用机器**)。本 spec **不定义不确定性从哪来**,只把**已存在的**语义源标注**归因并呈现**(§1.7.4)。 + - **注入/授权/信息流/围栏/状态版本五族判据** → S-B1..S-B5。 + - **成本模型**(`MODELED` 数字的唯一来源)、机器描述、优化承诺、向量化决策的产出 → S-C(+ hw-map)。本 spec 只定义**决策记录的格式、查询面与消费接口**(§2.6),不定义成本模型本身。 + - **跨族接口格式 → 本 spec 统一**(★D29):缓存键失效面、不确定性 / 竞态报告链格式、台账格式的定义归本线;S-B2 / S-E 只写「消费点」,不写格式(S-E 的「最小字段面」落点 = §1.7.1 硬约束 7)。 + - **证明搜索 / 不变量推断 / 验证管线** → 验证切片轮(`docs/verifier/kernel-spec.md` + `src/kernel/`)。本 spec 只定义 **ΔProof 的失效面**,不定义证明算法。 + +### 0.2 论题 + +三条线索是**同一个机制**的三个投影: + +> **把「编译器自己的决定」变成图上的一等对象(决策记录)**—— +> 解释 = 读它(为什么这样做);预测 = 它的一个子类(这样做的资源后果的界);增量 = 它的依赖闭包(它依赖什么 ⇒ 什么变了它作废)。 + +可解释是**人**的通道,可预测是**调度/约束**的通道,语义增量是**机器**的通道;三者共用 §4 的记录表与依赖边,不各建一套。 + +**路线图 §1.4 的报告面(2026-09-11 追加)是同一机制在确定性轴族上的投影**:`D_value`…`D_mapping` 的逐项状态 = 该轴上的决定记录;**不确定性 provenance 链** = 同一条 `evidence` 闭包换个读法(从「为何选」读成「这个不确定性从哪来」)。故它是**第四条线索,不是第四个系统**(§1.7)。 + +### 0.3 裁决表 + +| # | 裁决 | 状态 | +|---|---|---| +| **L1** | 决策记录是**旁路载体**(pass 产出 → 记录表 → 落盘),`explain` **只查不重算**——不得为回答问题重跑编译 | 定稿 | +| **L2** | 一个查询核(`explain_query(anchor, qclass)`)+ 三个前端(CLI / LSP / coreview),前端零分析逻辑 | 定稿 | +| **L3** | 记录**必须**携带确定性分级字段(`det` / `heuristic` / `env`)——它同时是 §3 的失效面输入(启发式决策不可跨环境复用) | 定稿 | +| **L4** | 上界分三层:**结构层**(语义层可判,本 spec 承诺)/ **映射层**(需 hw-map 尺寸标注,S-C)/ **时间层**(需成本模型,只能 enforce/proof-required) | 定稿 | +| **L5** | 状态值域与 §1.4 统一为 `{PROVED, MODELED, UNKNOWN, EXPLICIT}`(见 §2.2);**`measured` 不进值域、不是保证**——不得渲染为 bound | 定稿(2026-09-11 随 §1.4 并入修订) | +| **L6** | 缓存键从「单哈希」升级为**依赖集**(多分量);TODO #5 的「编译器身份分量」是其中一维,不是并行机制 | 定稿 | +| **L7** | 增量正确性判据 = 冷热两态**逐字节同**——目标是把现有「判据比较前必须 `clean-cache`」纪律**消灭**,不是沿用 | 定稿 | +| **L8** | 决策记录**必须随缓存条目一起存取**——否则冷热两态的 `explain` 输出不同(§4.4 的反例论证) | 定稿 | +| **L9** | 记录 id **内容寻址**(对内容哈希),不用构建序号——现状图对象身份是序号(DFNode 序号 / `.ccr` 段内索引),插一个节点全体漂移 | 定稿(是 S-D1/S-D3 的共同前置) | +| **L10** | `.ccr` 是否新增决策记录段 | **定稿(★D4,2026-09-11)**:**随 R2 P4 同波进 `.ccr` 新段**(决策记录段等;**段 tag 空间可扩展**——`2026-09-09-lattice-ir-v7-format.md:44` 的 `7+` **未固定分配**,其既有注明用途 = 驱逐标注段 / 证书段,新段名随实施批定;策略 / 标签 / 成本事实同批)——**先行实现走旁路文件**(§4.3),段化随 P4 波次落地 | +| **L11** | 节点级 ΔBinary(而非函数级) | **待论证**(O7;前置 = 稳定身份,见 L9) | +| **L12** | **确定性报告面 = 只读**:输出轴族逐项状态 + provenance 链,**不拒绝编译、不执法**(执法点「隐式竞争 → reject」归 S-A/S-E)。`analyze --determinism` 是同一查询核在「程序级锚点」上的投影,**不是新工具**(L2) | 定稿(承路线图 §1.4 边界) | +| **L13** | **时间三值化(硬性承路线图 §1.4)**:`D_timing ∈ {PROVED, MODELED, UNKNOWN}`;**`MODELED` 必须明标「模型非证明」**,**禁止**呈现为 WCET 证明;`measured` 不进状态值域(§2.2) | 定稿(契约硬性) | +| **L14** | provenance 链 = 记录表 `evidence` 闭包在「输出」锚点上的投影;链**终止于边界节点**(缓存语义条款 4「边界公理」)或显式语义源标注;链不完整 ⇒ `UNKNOWN` + **断链登记**,**不得补全猜测** | 定稿 | +| **L15** | 与 S-C 接口**单向**:本报告面只「调用 + 贴标签 + 记版本」,不重造成本模型、不缓存其内部状态;成本模型版本进 `phase_ver`(§3.3)⇒ 模型换 ⇒ 报告及依赖它的记录失效 | 定稿 | + +### 0.4 与在飞工作线的关系 + +不打断 R2(P2b/P3/P4/P5)、x86 实例化波 2/3、HIT M2。S-D3 与 TODO #5 的修复是**同一件事的两个名字**(本 spec 是其设计化);S-D1 与 **S-D4(`analyze --determinism` 只读版)**可立即开工(路线图 §3.2:可解释面用户可见收益直接;S-D4 是四条切片里最快能落地的——**只报告,不执法**);S-D2 的结构层可先做,声明档排在 S-A 之后(`EXPLICIT` 的**标注来源**待 S-A,边界来源今天可用——见 §1.7.4)。 +**L8(记录随缓存条目存取)由 S-D3 承接**(范围行已加,B 线 I1/★D26)——故 S-D1 / S-D4 的「冷 / 热两态一致」判据**先行 = 冷态-only**,冷 / 热两态版随 S-D3 验收;原「S-D3 独立并行」的排序按此修正(§5 依赖与顺序表)。 + +--- + +## 1. 可解释(`core explain`) + +### 1.1 原则:automation without opacity + +编译器拿走工作,**不拿走理解**。可解释的判据不是「能打印一堆东西」,而是: + +1. 用户能从一个**他能指认的锚点**(源码位置 / 符号 / 图节点)出发,问一个**他关心的问题类**,得到**编译器实际做过的决定**及其因由; +2. 答案是**产物的函数**,不是「再跑一遍编译器,这次开着日志」——同一产物两次查询必须逐字节同(与 §3 同一条纪律); +3. 答不出就说答不出(`unattributed`),**不得编造理由**——这是 §1.2 原则 3「义务不静默消失」在解释面的同构条款:**解释不静默编造**。 + +反面(明确不做):把 `explain` 做成「第 N 个 dump 通道」。现状已有 7 个 dump 旗标(`--dump-entries` / `--dump-coexist` / `--dump-regassign` / `--dump-objects` / `--dump-events` / `--dump-table` / `--check-regalloc`),它们是**整图转储**:无锚点、无问题类、无稳定 schema、不回答「为什么」。(原第 8 个 `--type-shadow-dump` 已随 R2 P5 Task 5 影子通道下线删除——R2 P6 Task 5 勘误。) + +### 1.2 查询面:锚点 × 问题类 + +**锚点**(用户指认得出来的东西): + +| 锚点形态 | 语法(草案) | 解析路径 | +|---|---|---| +| 源码位置 | `corec explain FILE.cr:LINE:COL` | 复用 `src/lsp/analysis.cr` 的「位置 → 令牌 → 符号」反查(快照只读,不触发检查) | +| 符号 | `--fn NAME` / `--global NAME` | `g_funcs` / 符号表 | +| 图对象 | `--node DFN` / `--instr IRN` / `--region ID` | `.cir` / `.ccr` 产物 + dump 通道 | +| 记录 | `--record ID` | 决策记录表(L9 内容寻址 id) | + +**问题类**=由**生产者注册的开放清单**(不是 CLI 硬编码 switch——否则每加一个 pass 就要改 CLI,退化成特判): + +| 问题类 | 问什么 | 生产者(决策产出点) | 就绪度 | +|---|---|---|---| +| `checks` | 这条指针/下标**为何插了 runtime check** | `ext_safety.cr:18` `ext_safety_on_array_access` + `ir_gen.cr:17-65` F11 长度侧表(`g_ir_slice_lens`)+ `IR_BOUNDS_CHECK`(`ast.cr:559`)+ checker R002 | **今天有**(S-D1 切片) | +| `ptr` | 这条指针**为何判安全** | `ptr_analysis.cr` 三 pass + `region_check.cr` + `provenance_verify.cr`;LSP hover v1 已有「points-to 闭包近似 / 配方 = 产生节点」 | **今天有(近似)** | +| `placement` | 这个节点**为何放 CPU / 为何被驱逐/再生** | `docs/regalloc-cache-mapping.md`(寄存器分配 = 缓存语义映射实例)+ `--dump-regassign` / `--check-regalloc` | 部分(后端侧已有 dump,缺记录化) | +| `bounds` | 这个循环/序列的**上界从哪来** | `bounds-inference`(**挂账**,TODO「边界判定图 pass」)+ F11 长度来源链 | 待 pass 落地(§2) | +| `vectorize` | **为何没向量化** | auto-vectorize(TODO 性能自动化族 → **S-C**) | **未实现**——`explain` 必须能答「生产者未产出记录(功能未实现)」,而不是猜 | + +最后一行是设计的关键约束:查询面对**没有记录**的问题类必须给确定答案,答案分三种——「未实现」「未评估」「评估后拒绝」,三者不得混同(`alternatives` 字段区分:空 = 未评估,非空 = 已评估被拒)。 + +### 1.3 决策记录的承载(本 spec 的核心载体) + +一条记录 = 一次编译器决定的**内容寻址**快照: + +| 字段 | 语义 | 备注 | +|---|---|---| +| `id` | 内容哈希(kind + subject + 输入摘要 + 因由码) | **L9**:不随构建序号漂移 | +| `kind` | 记录类别(开放注册:`check_emitted` / `ptr_verdict` / `reg_choice` / `bound` / `obligation` / `cache_verdict` …) | 与问题类多对一 | +| `subject` | 图对象引用(DFNode / 边 / region / IR 指令 / 符号 / 源码 span) | 与 `.cir`/`.ccr` 的既有 id 空间对齐 | +| `phase` | 产生阶段(checker / ir_gen / dataflow / 判定引擎 / regalloc / emit) | 决定「谁负责」 | +| `choice` | `selected` / `rejected` / `forced` / `unknown` | 四态,与引擎三态(真/假/unknown)同构 | +| `cause` | 因由码 + 参数(规则 id / 启发式 id / 计数器 / 预算余量) | **因由码是稳定标识符,不是自由文本**;人读文本由渲染层生成 | +| `alternatives` | 被拒方案 + 各自因由码(可空) | 空 ≠ 拒绝,空 = 未评估 | +| `evidence` | 输入引用集(图节点 / 侧表项 / 类型项 / 约束) | **同时是 §3 的依赖边**(§4.1) | +| `determinism` | `det` / `heuristic` / `env` | **L3**;`heuristic`/`env` 记录不得跨构建复用(§3) | + +**单一真源(★D27 / A 线 M1)**:本表 = 全仓**唯一** schema 真源;消费者(如 S-C §5 R1)只引用本表字段集 + 各自补充项,**不得**另立字段表(S-D §4.1 禁止双源漂移)。 + +**不重造的部分(复用判据)**:记录表**不是**新分析。它是对既有产出的**记录化**——**迁移期影子对拍通道**(R2 P1 起,已随 R2 P5 Task 5 整体下线)曾把判定决策记成分类计数(AGREE / OLD_STRICTER / OLD_LOOSER / UNKNOWN),只是没有 id、没有 subject、没有落盘;现行同类载体 = `--verify-named-dedup` / `--verify-evp-nodes`(默认关的校验通道)+ 行为探针与结构性断言;`--dump-regassign` 已经是 `placement` 记录,只是没有因由码。本 spec 要求的是**统一条目与统一查询**,不是重写这些 pass。(先例载体更新 = R2 P6 Task 5 / E-16。) + +**开销纪律(承 R2 P1 先例)**:R2 P1 已实证「记录通道默认关 + 关/开两态产物逐字节同(sha256 `95084e7b…d475`)」。本 spec 沿用该纪律的**加强版**: + +- **记录生成常开**(轻量:只写 `id/kind/subject/phase/choice/cause 码`); +- **记录展开按需**(`explain` 时才把 `evidence` 引用与 `cause` 参数渲染成人读文本); +- 关掉记录通道 = 产物逐字节同(守卫判据,见 §5 S-D1)。 + +理由:若记录生成也默认关,则**缓存命中路径上的决定永远不会被记录**(缓存条目里没有记录),`explain` 将随冷热态给出不同答案——这正是 L8 要禁止的。 + +### 1.4 输出契约 + +**一份数据,两种渲染**(人读由结构化生成,绝不反向): + +``` +{ + "schema": "core.explain/1", + "query": { "anchor": {...}, "qclass": "checks" }, + "records": [ + { "id": "...", "kind": "check_emitted", "phase": "ir_gen", + "subject": { "src": "FILE.cr:42:7", "instr": 118 }, + "choice": "selected", + "cause": { "code": "E-CHK-SLICE-DYN", "args": {"len_source": "var"} }, + "alternatives": [ { "code": "E-CHK-NONE", "rejected_because": "E-CHK-SLICE-DYN" } ], + "determinism": "det", + "evidence": [ { "kind": "side_table", "ref": "g_ir_slice_lens:17" } ] } + ], + "unattributed": [ { "subject": ..., "qclass": ..., "why": "no_record" } ], + "truncated": 0 +} +``` + +硬约束: + +1. **确定性**:同产物两次 `explain`(含 stdout)逐字节同——无时间戳、无 PID、无地址、无目录绝对路径;排序 = 记录 id 序。 +2. **versioned**:`schema` 字段 + 破坏性变更 bump;golden 快照测试直接比字符串。 +3. **`unattributed` 必须显式**:不可归因(启发式内部状态 / 记录缺失 / 功能未实现)走这一栏,**不得**以空 `records` 静默等价于「没发生任何决定」。 +4. **人读模式**(默认):`cause.code` → 稳定措辞表(`diag.cr` 风格:因由码 → 一句中文 + 一行证据链);`--json` 出结构化。**因由码 → 措辞表**是独立文件/表(照 `error_codes` 先例),保证措辞可审、可译、可测。 +5. **零协议扩展**(承 LSP spec §6):LSP hover 用 markdown 渲染同一结构;coreview 读 JSON。不新增 LSP 方法。 + +### 1.5 与 LSP / coreview 的分工 + +| 前端 | 场景 | 读什么 | 现有基建 | +|---|---|---|---| +| `corec explain`(CLI) | 批处理 / CI / golden 快照 / 无编辑器时 | 产物 + 决策记录旁路文件 | `main.cr` 命令分派(现有 7 命令:`run`/`check`/`cir`/`ccr`/`clean-cache`/`selftest-types`/`build`) | +| corelsp | 光标处按需(hover / 诊断 code 链接) | 同一查询核,快照只读 | `src/lsp/analysis.cr`(**已确立「查询不触发检查、只读最后一次成功检查的快照」**——正是 L1 的先例)、`json.cr:614` `json_stringify` | +| coreview | 图级/管线级浏览、代码↔图双向 | `.cir`/`.ccr` dump + 记录叠加 | **未建**(目录尚不存在;LSP spec §七)= 记录表的第三个消费者 | + +**分工原则**:`explain` 是**答案**,coreview 是**全景**,LSP 是**就近**。三者共用查询核与记录表;coreview 的图渲染不重算语义(LSP spec §三已定「数据源全部来自编译器既有产物,只做接线 + 呈现」——本 spec 逐字沿用该原则到决策记录)。 + +**依赖关系**:LSP P1 的 ProjectSession(全项目 + 缓存失效)落地后,`explain` 直接受益(跨文件失效面归 §3);**不等待 LSP**——CLI 切片今天可做。 + +### 1.6 现状基础与缺口(诚实清单) + +有:`ext_safety` 钩子 + F11 长度侧表(`g_ir_slice_lens`,编码:≥0 字面量 / ≤−2 长度变量 / −1 清除)+ `IR_BOUNDS_CHECK` + checker R002 + 7 个 dump 旗标 + 审计通道(默认关;**两态产物逐字节同**的纪律沿用——现行载体 = `--verify-named-dedup` / `--verify-evp-nodes`)+ `analysis.cr` 快照查询 + `json.cr` 读写。(原 `--type-shadow` 两态纪律的载体已随 R2 P5 Task 5 下线——R2 P6 Task 5 勘误。) +缺:统一记录表(0 实现)|内容寻址 id|因由码体系(现状无稳定规则标识符)|`unattributed` 概念|向量化决策(生产者不存在)|边界节点标注(`unsafe`/边界是语义概念,但**无查询通道**)|成本模型(S-C)|~~**效应事实不可靠(B 线 C2 登记)**~~(**已修,2026-09-11 效应/纯度批 `762bd429`+`c9099d73`**:`fi_ispure` 真计算 + `IR_CALL_EXTERN`/间接调用等全入链 ⇒ 本项移出「缺」清单;残余限定与证据见 §1.7.1 硬约束 8 修订与 TODO #27;历史快照:恒 1 写入点 `checker.cr:1107`/`:1142`、`IR_CALL_EXTERN`(`ast.cr:572`=45)不入链、链每函数重置 `dataflow.cr:401`)。 + +### 1.7 确定性报告面:`core analyze --determinism`(路线图 §1.4,2026-09-11 追加) + +**定位(L2/L12 的直接推论)**:`analyze --determinism` **不是新工具**——它是 §1.2 查询核在**程序级锚点**上的投影: + +| 命令 | 锚点 | 问题 | 共用 | +|---|---|---|---| +| `corec explain FILE:LINE --qclass <类>` | 单锚点(源码/符号/图对象) | 为什么**这里**这样做 | 同一记录表 + 同一查询核 | +| `corec analyze --determinism [--at ANCHOR]` | 程序 / 每个**可观测输出**(边界 sink:打印/写文件/返回值/网络) | 这个输出的**不确定性从哪来** | 同上 | + +#### 1.7.1 报告契约 + +输出 = **轴族逐项状态** + **不确定性 provenance 链**(两份都由 §1.3 的记录表生成): + +``` +$ corec analyze --determinism FILE.cr +axis status evidence / residual +D_value PROVED merge_deterministic @12:3;无可达边界 +D_effect UNKNOWN state edges 覆盖 #41→#44→#52(store 家族);调用 print@#58 未入链——按保守面降级(硬约束 8) +D_memory PROVED region 锚定 + bump/reset 全静态(arena #0/#3) +D_timing UNKNOWN residual: 循环 #61 迭代次数依赖外部输入 +D_resource PROVED 结构层:在飞任务 ≤ 4(spawn_count);值个数 ≤ … +D_external EXPLICIT boundary #27(stdin)——边界节点 = 一类合法显式源,主人 = #27(★D19) +D_mapping PROVED 无机器相关假设(映射无关段) + +uncertainty provenance: + boundary #27 → parser → branch #61 → output "report" + (每跳 = 记录 id + kind + 边类型;断链 → UNKNOWN + 断链位置登记) +``` + +硬约束(承本 spec §1.4 输出契约约束 1–5 + 路线图 §1.4 全部): + +1. **状态值域** = `{PROVED, MODELED, UNKNOWN, EXPLICIT}`(路线图 §1.4 逐字);**`D_timing` 只用前三值**(时间三值化,硬性——不设 `EXPLICIT`:耗时的物理性使「源码明文允许」不构成状态;源码显式放行不确定时,该不确定落在 `D_value`/`D_effect`,`D_timing` 相应为 `UNKNOWN`)。 +2. **每行必须带证据或残余**:`evidence`(可指认的记录链)或 `residual`(剩余不确定源清单)。**空 evidence + 空 residual 非法**(= 静默,路线图 §1.4 精神禁止)。 +3. **链可指认性**:provenance 链的每一跳必须能在记录表里查到(L9 内容寻址 id);查不到 ⇒ `UNKNOWN` + 断链位置登记(**L14**,不得补全猜测)。 +4. **链终止**:终止于**边界节点**(缓存语义条款 4「边界公理」:MMIO / FFI / 输入 / 测量 = 唯一无配方条目)或**显式语义源标注**。到达既非边界又无配方的内部节点 ⇒ 断链(这同时是标注器/pass 的缺陷信号,须登记)。 +5. **状态传播 = 沿依赖闭包取 join**(部分序,**不**假设全序): + - 任一环节 `UNKNOWN` ⇒ 整体 `UNKNOWN`(**不可归因优先暴露**——这是报告的诚实底线) + - 无 `UNKNOWN`、含 `MODELED` ⇒ 整体 `MODELED`(时间轴专属:混入机器相关估计即**不得**称证明) + - 无 `UNKNOWN`/`MODELED`、含 `EXPLICIT` ⇒ 整体 `EXPLICIT` + - 全 `PROVED` ⇒ `PROVED` + - `MODELED` 与 `EXPLICIT` **不可比**:同时存在 ⇒ **双标**(`MODELED + EXPLICIT`),并必须让 `EXPLICIT` 的源可指认——不得用任一值吞掉另一个。 +6. **报告自身确定性**:同产物两次运行逐字节同(含 stdout,无时间戳/路径/地址;排序 = 轴名序 + 记录 id 序)——照本 spec §1.4 约束 1。 +7. **汇合与不确定性的最小字段面(承 S-E §5.2.3;B 线 I3/★D20)**:报告项**必须**含——形态 / 汇合点 id / 输入边集 / 顺序源(`ordered`)/ k 与 `Obs(M)` 基数界(`race`)/ 见证引用(witness,若暴露)/ 分类(已证明 · 已声明 · 冗余声明 · 拒绝)。**`冗余声明` 类照 S-E §1.5 证明优先规则单列**(标了 `race` 而已证确定者)。此前「格式归 S-D」两侧都以为对方承载 ⇒ **落点即本条**(S-E §5.2-3 已回指)。 +8. **效应证据的充分性(★D17/B 线 I4)**:`D_effect` 的 `PROVED` 证据**必须声明 state 链的覆盖范围**;链未覆盖 `IR_CALL_EXTERN` 或未被判定为纯的调用时,按 **S-E §1.1 保守判定**降级 `UNKNOWN` + `residual`。**覆盖现状(2026-09-11 效应/纯度批后)**:链覆盖 = 函数内 store 家族 + `purity_op_effect` 全清单(`IR_CALL_EXTERN`/`IR_SPAWN`/`IR_YIELD`/`IR_HOTPATCH_ROUTE`/`IR_DYN_DISPATCH`/`IR_STORE_PTR`/`IR_AWAIT`)+ 一切不可解析调用 + 一切未证纯的可解析调用(**修复前口径 = 「所有调用」**,见 §1.6 历史条);残余 = lazy 判定冻结(`ir_gen.cr:1590` 生成期读乐观默认值)+ 链每函数重置。**「有 state 边 ≠ 确定」**(S-E §4.1 方向性提醒)——不得把「state edges 全序」单独读成 `PROVED`。 +9. **局部 prove 的覆盖标注(D10)**:`race` / `select_any` 的 `EXPLICIT` 项**必须**标注「该保证未覆盖观测点」(S-E §5.2-6:prove 范围 = 局部,只覆盖汇合点本身)。 + +#### 1.7.2 时间三值化(`D_timing`,硬性) + +| 值 | 语义 | 数字来源 | 呈现禁令 | +|---|---|---|---| +| **PROVED** | **机器无关上界**:分配次数/字节 · 在飞任务数 · 通信量(**可由 region / 图证明**) | §2 结构层(本 spec) | 不得声称这是「时间上界」(次数 ≠ 时间) | +| **MODELED** | 机器相关时间 = **成本模型估计** | **S-C 成本模型**(§2.6 接口) | **必须明标「模型估计,非证明」**;**禁止**呈现为 WCET 证明 | +| **UNKNOWN** | 判不出 / 模型不可用 / 链断 | — | 不得渲染为任何界(§2.2 同纪律) | + +**「PROVED 的机器无关量」与「时间」的分界是本报告面的核心纪律**:分配次数/在飞任务数/通信量可证,但**它们不是时间**——报告面必须把两者分列,不得让读者把「已证明的指令数上界」读成「时间上界」(这正是路线图 §1.4「不许撒谎」的落点)。 + +#### 1.7.3 与 S-C 的接口(`MODELED` 的来源,L15) + +``` +cost_model_query(kind, subject, mapping_desc) -> { + cost, // 估计值 + band, // 不确定带(必填:单点数字给人"精确"错觉 = 过度承诺) + model_id, // 成本模型标识 + model_ver, // 版本 + assumptions[] // 模型成立的前提(必须随报告呈现) +} +``` + +报告面**只做三件事**:调用、贴标签(§1.7.2 的禁令措辞)、把 `model_id`/`model_ver`/`assumptions` 记进记录的 `evidence`(⇒ 进 §3.3 的 `phase_ver`,**模型换版本则报告与相关记录自动失效**)。 +**不做**:不重造成本模型、不读其内部状态(内部状态不可归因 ⇒ 违反 §1.2「解释不静默编造」)、不缓存模型结果。 +**禁令**:模型不可用(S-C 未落地 / 预算耗尽)⇒ 必须是 `UNKNOWN`,**不得**用「经验常数」兜底成 `MODELED`。 + +#### 1.7.4 与 S-A / S-E 的边界(**只归因,不定义来源**) + +**与路线图 §1.4 执法落档表的逐行对应**(本报告面只出「呈现列」,执法列为谁负责): + +| 路线图 §1.4 情形 | 本报告面呈现 | 执法归属 | +|---|---|---| +| 依赖/状态边足证「汇合与调度无关」 | `PROVED`(零成本) | S-A | +| 顺序由显式顺序源固定(`merge_ordered`) | `PROVED` + evidence 指向该顺序源 | S-E 定义形态 / 本面呈现 | +| 语义明文允许(`race` / `select_any`) | `EXPLICIT`(**必须可指认主人**) | S-E 定义形态 / 本面呈现 | +| 既未证明也未标注(**隐式竞争**) | `UNKNOWN` + 归因链(只读版**不拒绝**,L12) | **S-A/S-E 执法 = reject**(真正的执法点) | + +- `EXPLICIT` 的判定 = 「该不确定可追溯到一条**已登记的**显式语义源标注」**或**「链终止于**边界节点**」。**标注的存在性与合法性归 S-A(轴标注登记)/ S-E(汇合形态定义)**;本报告面只回答「归因到哪」,**不**判定「某个 `race` 标注是否该合法/是否该 reject」——**执法点(隐式竞争 → reject)归 S-A/S-E**(L12:本 slice 只读)。 +- **边界节点 = 一类合法的显式不确定源(★D19/B 线 I5)**:边界链(`D_external`:MMIO / FFI / 输入 / 测量,缓存语义条款 4 的既有四类)的**主人 = 边界节点 id**,链状态 = `EXPLICIT`,与 S-A 的轴标注登记**并列**——**不因「无标注」降为 `UNKNOWN`**。边界清单的最小面按上述四类起步(O11),扩展须登记。 + **与 S-A §1.9 来源列的对齐(B 线 N4,规范性)**:S-A 的 `D_external` 来源列写「领域库/部署声明」;本报告面读作——**边界 = 声明的一种**(**边界公理即其声明**,条款 4 的「无配方条目」本身即标记)。两者不冲突:**归因**(链终止于边界 ⇒ 主人 = 边界 id ⇒ `EXPLICIT`)今天可用、不依赖 S-A 落地;**放行**(该边界是否被允许/被治理层认可)**不由本报告面判定**,也不构成放行——报告面只归因(L12 只读)。故 S-D4「依赖 = 无」与其判据② 相容(② 只要求归因,不要求声明登记)。 +- **局部 prove 的呈现(D10)**:`race` / `select_any` 的项标注「该保证未覆盖观测点」(硬约束 9)——保证范围与呈现范围必须一致,不得让读者把局部保证读成整条路径的保证。 +- **汇合语义本身**(`merge_deterministic` / `merge_ordered` / `race` / `select_any` 四形态、及其 lowering/调度不得引入未声明不确定的**降级义务**)**逐字归 S-E**,本 spec 零定义。 +- **现状如实呈现**:`select` 现语义(「最早到达的令牌」= 物理调度顺序泄进语义,路线图 §1.4 定性 = 待修)——报告面会**如实把链指向该语义源**,不掩盖、不替它开脱;重定与兼容窗口归 S-E。 +- **`D_mapping`(Core 特有轴)**:语义 ≥ 映射 ⇒ 报告口径 = 「该输出的语义是否依赖某条映射假设」;映射假设的清单来自 hw-map/S-C,本报告面只查询与呈现。 + +#### 1.7.5 只读版即最小切片 + +本节的可实施最小版本 = **§5 S-D4**(只报告、不执法、无成本模型时 `D_timing` 恒 `UNKNOWN`;`EXPLICIT` 的**边界来源今天可用**,标注来源待 S-A——★D19)。它是三线里**最快能落地**的切片:不依赖 S-A/S-C/S-E 任何一项先行。 + +--- + +## 2. 可预测(资源上界) + +### 2.1 上界三层与可判定性 + +| 层 | 量 | 可判定性 | 归谁 | +|---|---|---|---| +| **结构层** | 最大**在飞任务数**(并发 goroutine 数)· 最大**通信条数/值个数** · 循环迭代次数上界 · 每 arena 的**值个数**上界 | 图 + region + state edges + spawn 计数**直接可判**(无依赖 region 的可并行性判定已有声明,TODO 性能自动化族) | **本 spec** | +| **映射层** | 最大**字节内存** · 最大**通信字节量** · 峰值 live storage | 需尺寸/对齐标注——R2 §2.1 已裁定「尺寸/对齐 = 映射层 / hw-map 领域,**语义层不承诺布局**」 | **S-C / hw-map**(本 spec 只定义记录格式与「界如何被消费」) | +| **时间层** | **deadline**(墙钟/周期上界) | **语义层不可判**(成本模型属 S-C;共享机器/OS 调度/中断不可归因——§1.3 残留第 2 类) | 只能 ②enforce / ③proof-required(§2.4) | + +三层不可混报:`explain --bounds` 的输出必须标层。**结构层上界不蕴含映射层上界**(值个数少但值大);把两层混成一个「内存上界」是本设计明确禁止的过度承诺。 + +### 2.2 保证 vs 平均:状态值域(**统一承路线图 §1.4**) + +上界与轴族状态**共用同一个值域**(不另造一套标签——这是「三线共用一套」的直接后果): + +| 状态 | 含义 | 渲染约束 | +|---|---|---| +| `PROVED` | 对**声明的全部执行**成立(符号传播 + 图结构给出,机器无关) | 可称「保证」;须附 `assumptions[]`(空 = 无条件) | +| `MODELED` | 依赖**成本模型**(机器相关) | **必附 `model_id`/`model_ver`/`band`/`assumptions[]`**;人读必须打「模型估计,非证明」标(§1.7.2 禁令) | +| `UNKNOWN` | 判不出(预算耗尽 / 依赖不可判 / **生产者未实现** / 链断 / 模型不可用) | **不得渲染为 bound**;走 `unattributed`(本 spec §1.4 约束 3)+ 登记 | +| `EXPLICIT` | 语义**明文允许**该不确定(有主的不确定) | 必须指明**主人**(标注的位置与来源);不得与 `UNKNOWN` 混同 | +| (`measured`) | 实测(PGO/剖析/回归数据) | **不是状态、不是保证**;只允许出现在诊断/建议语境,样式中不得与前四者同形 | + +**假设是修饰不是状态**:`PROVED`/`MODELED` 都可以带假设清单(原「assumed」= `PROVED + 非空 assumptions` 的读法);假设不成立则界不成立——此口径比单列一档 `assumed` 更诚实(不会让读者以为「有假设的界」与「无条件证明」是同一类东西)。 + +这就是 §1.2 原则 3「义务不静默消失」在预测面的同构条款:**上界不静默降级**——`PROVED` 退到 `MODELED` 必须写清模型与假设;退到 `UNKNOWN` 必须显式登记。**禁止**把 `MODELED` 呈现为 WCET 证明(L13,契约硬性)。 + +### 2.3 现状基础(可复用,不重造) + +1. **每 goroutine 独立 arena + 不共享内存 + 值在 channel 边界拷贝**(`docs/superpowers/specs/2026-07-30-concurrency-design.md`;实现 `src/stdlib/goroutine.cr:54` 写 `arena_id`、`sched.cr:45` 主 goroutine `arena_id = -1`)。 + ⇒ **并发内存上界可分解**:无共享 ⇒ 最坏内存 = Σ(每 arena 上界),无需全程序指针逃逸分析——这是本 spec 能承诺结构层的**唯一理由**。 +2. **静态 spawn 计数已在 IR 里**:`IR_SPAWN`(`ast.cr:556`)`s3 = fn_name_ni`、`type_kind = spawn_count`,**`-1` = dynamic**。 + ⇒ 并发数上界对 `spawn_count ≥ 0` 的部分**今天即可判**(`PROVED`);`-1` 部分登记为 `UNKNOWN`(不是猜)。 +3. **region 嵌套 + state edges**(`dataflow.cr`:SG_IF/LOOP/FOR/FLOW/UNSAFE + state edges):并发路径结构可判(TODO「无依赖 region 的可并行性可判定」)。 +4. **F11 长度来源链**(`g_ir_slice_lens`)+ **bounds-inference 挂账**:迭代次数/序列长度上界的传播基建已存在(今天是侧表,目标是图化 + 三态:恒安全 → 不发射检查;恒越界 → R002 拒绝;判不了 → 保留检查**并带图推导必要性标注**——最后这条正是 §1 `checks` 问题类的答案来源)。 +5. **判定走引擎**(原则 4):资源约束的判定(如「迭代次数 ≤ 声明值」)复用 `ty_sub` 族;**不另建判定体系**。 + +**缺面(诚实)**:无值个数/尺寸的上界推导 pass(`bounds-inference` 挂账)|无并发数收集通道(`spawn_count` 存在但无汇总查询)|无成本模型(S-C)|无「上界声明」的语法/数据载体(§2.5)。 + +### 2.4 deadline 约束的语义 + +**超时动作 = 仅诊断(已裁,★D8,2026-09-11)**:编译期标注 + 报告,**不改运行时语义**——语言只说「此处声明了 deadline 约束」,**不说「超时会怎样」**;超时的硬行为(终止 / 降级 / 补偿)留给**库 / 运行时**实现,且**不得**呈现为语言保证(与「模型非证明」一致:语言不承诺时间上界)。据此,deadline 在语言语义上只能是: + +| 档 | 语义 | 何时可用 | +|---|---|---| +| ③ **proof-required** | 要求用户提供「步数/成本上界」的证明义务 | 硬实时子集,验证切片轮后 | +| ④ **reject** | 高安全模式下无法处置 → 拒绝编译 | policy 数据裁量 | +| (② enforce **不在语言面**) | 若库 / 运行时自行实现超时动作,那是**库的语义**——语言不承诺其行为,也不得把运行期动作升格为语言保证(★D8) | — | + +**明确不在 ①**:即使 someday 有成本模型,deadline 的 prove 仍依赖调度/硬件假设(路线图 §1.3 残留第 2 类)——`PROVED` 的 deadline 只可能在「声明域内 + 单核 + 无中断」这类**显式声明的封闭假设**下成立,且必须把假设挂在 `assumptions[]` 里。 + +**`MODELED` 的位置**:deadline 可以在**非安全关键**语境以 `MODELED`(S-C 成本模型估计 + 不确定带)呈现——但必须明标「模型估计,非证明」(L13),且**不得**作为调度/安全/适航类判据的输入。**超时动作 = 仅诊断**(已裁,★D8;O5 已闭合):报告面只呈现「该约束的 `D_timing` 状态 + 残余」,**不得**承诺任何超时行为。S-C 侧同款(其 §1 资源行 + §8 O5)。 + +### 2.5 与义务阶梯的关系(实例化,不重定义) + +资源上界 = **义务阶梯在资源轴上的实例**(本节只做映射,不新增档): + +| 资源约束的处置 | 四档落点 | +|---|---| +| 静态可证界 | ① **prove**(零成本,状态 `PROVED`) | +| 不可证界但可运行时强制(计数器 + 检查) | ② **enforce**(状态 `PROVED + assumptions[]` 或 `MODELED`+enforcement 记录) | +| 判不了也不可强制 | ③ **proof-required**(要求用户声明上界 = policy 数据) | +| 高安全模式仍无法处置 | ④ **reject** | + +**载体 = policy 数据**(原则 2 / §1.1:`G + P ⊢ Safe` 的 `P`),**不升格为语言 primitive**——资源上界声明是图上约束数据,不是新关键字。**依赖 S-A**:`P` 的接口与义务判定通道归 S-A,本 spec 提供的是「结构层界的计算 + 记录格式 + 状态值域纪律」。 + +### 2.6 与 S-C 的接口(`MODELED` 数字的唯一来源,L15) + +时间层的 `MODELED` 与映射层(字节)的数字来源**不在本 spec**:时间 = **S-C 成本模型**,字节 = **hw-map 尺寸标注**(R2 §2.1 已裁:尺寸/对齐是映射层领域,语义层不承诺布局)。接口形态与禁令见 **§1.7.3**(`cost_model_query`:`cost` + **必填 `band`** + `model_id`/`model_ver` + `assumptions[]`): + +- 本报告面**只调用 + 贴标签 + 记版本**;模型版本进 §3.3 `phase_ver` ⇒ **模型换代 ⇒ 报告与依赖它的记录自动失效**(这是增量面的直接收益)。 +- **不读模型内部状态**(内部状态不可归因 ⇒ 违反「解释不静默编造」)。 +- 模型不可用 ⇒ `UNKNOWN`;**不得**用经验常数兜底成 `MODELED`。 +- 结构层(本 spec 承诺的部分)**不依赖** S-C:这也是 S-D2 结构档可先行的理由。 + +--- + +## 3. 语义增量(ΔSource → ΔGraph → ΔProof → ΔBinary) + +### 3.1 四段 Δ 与各自的失效面 + +| 段 | 输入 → 输出 | 今天的机制 | 失效面(语义节点级) | +|---|---|---|---| +| **ΔSource → ΔGraph** | 源变更 → 图变更 | 函数级指纹:`cir_cache.cr:74-75` `func_fingerprint`(函数体原文 hash)+ `sig_fingerprint`;键 = `src_path + "::" + name`(`main.cr:470`) | 变更的**函数** + 调用它的函数(签名变更传播)+ **导入闭包**(改 stdlib 不动本函数)+ **单态化实例**(`main.cr:442-446`:只要存在泛型函数就**整关缓存**;命中不重放 monomorph) | +| **ΔGraph → ΔProof** | 图变更 → 义务/证明变更 | **无**(验证管线 0 实现;引擎 `ty_sub` 有进程内 memo,R2 §3.3) | 义务的**输入摘要**(类型项 + 约束 + 判定引擎版本);引擎版本变 ⇒ 全判据作废(`type_equal → ty_equiv` 替换已实证:P2a 换判定即需全量对拍复跑) | +| **ΔProof → ΔBinary** | 证明变更 → 产物变更 | **无**(`lower_to_ccr` → ELF 整体跑,增量缓存设计 §「编译管线集成」第 7 步明写「整体跑」) | 段级(STR/SYM/NOD/ENT/REG/EDG)+ **节点身份稳定性**(前置,见 O7) | + +### 3.2 与现有缓存的差距(含两个已实测缺陷) + +**现状**:函数级 `.cir` 快照,`CIR_CACHE_VER = 17`(`src/compiler/cir_cache.cr:51`),头部 = magic / 版本 / **编译器身份**(v17 起引入——TODO #5 修复,见 `cir_cache.cr:26` 与 `:214` 的字段序注)/ 函数体指纹 / 签名指纹 / name_len+name;命中即跳过 check + IR gen。(原记 `CIR_CACHE_VER`=14 / `cir_cache.cr` 行号 16 / magic `C1C1…` 三处双陈旧已在 R2 P6 Task 5 修正;magic 值不作承诺——`CIR_CACHE_MAGIC` 常量声明见 `cir_cache.cr:50`。) + +**差距 A(TODO #5,预存)**:键**缺编译器身份分量** ⇒ 二进制重建后旧条目存活 ⇒ 旧 CIR 与新版字符串表(`g_strs` 驻留序)错位 ⇒ dump 通道变量名缺失/错位(实证:806 → 381 个 `name=` 翻转;清 cache 即复现判据)。 + +**差距 B(TODO #5 同族第二实例,2026-09-11)**:**`.ccr` 输出与缓存态相关**——同一二进制冷缓存首次 `corec ccr` sha256 `53ebcf12…`,紧接第二次(已填热)`5054e26d…`,三次起稳定。故现状**判据比较 `.ccr` 前必须 `clean-cache`**。 + +差距 B 是本 spec 的**核心证据**:它说明现状缓存的输出**不是产物状态的函数**,而是「历史路径」的函数。L7 把它写成可检验的目标。 +**范围注(C4/★D25)**:差距 B 的**闭环**(命中路径产物保真:字符串驻留序 / 全局初始化注入的重放)落在「条目内容」面,**超出 S-D3 声明的范围**(S-D3 只做键的依赖集,明确不做增量后端与 `.ccr` 格式改动)⇒ 其判据② 已**降级为登记**(S-D3 表;不得按其字面读作 S-D3 的验收线)。 + +**差距 C(缓存 × 语义的静默交互,已有登记)**:缓存命中**跳过 `ir_gen_func(main)`** ⇒ 全局初始化注入(main 序言 IR 注入)在命中路径上**不重放**;配合差距 A ⇒ 升级编译器后旧 cache 让该修复**静默失效**(TODO「全局初始化机制」缓存交叉引用;批前该场景本就 rc=139,非回归)。 +**差距 D**:缓存与单态化的交互脆弱(`main.cr:442-446` 注释明写:缓存命中不重放 monomorph ⇒ 泛型函数存在时缓存整关)——即「性能特性」与「正确性」在现状实现里靠**关掉缓存**兜底。 +**差距 E**:命中判据只有 <函数体, 签名> 两维——**不含**构建配置(`--static` / opt-level / target / `--table` / 实例声明)、**不含** policy 集(S-A 落地后义务集变了缓存必须失效)、**不含**依赖闭包。 + +### 3.3 缓存键 = 依赖集(L6) + +| 分量 | 内容 | 解决 | +|---|---|---| +| `src_fp` | 函数体原文 hash(现状) | — | +| `sig_fp` | 签名指纹(现状)——**应升级为类型项指纹**(R2 P4 载体后;现状是文本/编码哈希,对类型项不敏感) | 差距 E | +| `deps_fp` | **导入/调用闭包摘要**(被调函数的 sig_fp 传递闭包 + 引用常量/全局) | 差距 E | +| `policy_fp` | **policy 集摘要**(S-A 的义务规则集版本 + 生效标注) | 差距 E(S-A 后) | +| `unit_fp` | **编译单元身份**:编译器产物内容哈希(或编译器身份分量)+ 目标/配置(`--static`/opt-level/target/表模式/实例声明) | **差距 A(= TODO #5)** | +| `phase_ver` | 各阶段格式版本(`CIR_CACHE_VER` 等)+ **判定引擎版本**(`type_engine` 变更 ⇒ 判定类记录作废)+ **成本模型版本**(`model_ver`,L15:模型换代 ⇒ `MODELED` 记录与 `analyze --determinism` 报告作废) | 差距 A / ΔProof / §1.7.3 | +| `det` | 条目内所含记录的确定性分级:含 `heuristic`/`env` 记录者**不得跨构建复用**(L3) | 差距 C 类 | + +**与 TODO #5 的关系(写清)**:TODO #5 的修复方向(「缓存键 += 编译器指纹(产物内容哈希或编译器身份分量)」)**就是本表的 `unit_fp` 一行**。本 spec 不另立机制,只是把单分量修复扩展成依赖集,并把「手工 bump `CIR_CACHE_VER` + 测试前清 cache」这条兜底纪律(TODO 原文)**降级为过渡手段**。 + +### 3.4 正确性判据(增量 ≡ 全量) + +**主判据(L7)**:对任意源变更集,**增量产物 ≡ 全量产物**——三级,全部逐字节比较: + +| 级 | 比较对象 | 现状可测性 | +|---|---|---| +| L1 产物级 | ELF / `.ccr` / `.cir`(冷 vs 热两态) | **今天可测**(R2 全程在用逐字节判据,如 ELF `95084e7b…d475`、`.ccr` 冷缓存 4 语料 IDENTICAL) | +| L2 记录级 | `explain` 输出(冷 vs 热两态,含 `unattributed`) | **S-D1 后可测**(依赖 L8:记录随条目存取) | +| L3 义务级 | 义务/证明集合(ΔProof 后) | 待验证管线 | + +**反向判据(必须同时有,否则主判据可被「永远全量重编」作弊)**:**增量必须真的生效**——命中率/复用率统计 + 一条「改动 1 个函数 ⇒ 未变更函数的 `.cir` 条目**字节未变**」的断言。只测「等价」不测「真的省了」= 把缓存删掉也能全绿。 + +**负控集(失效面覆盖,每条一个断言)**:① 重建编译器(差距 A:跨重建不复用)② 改 stdlib 依赖(`deps_fp`)③ 改 policy 集(`policy_fp`,S-A 后)④ 改 `--static`/opt-level/target(`unit_fp`)⑤ 泛型/单态化路径(差距 D)⑥ 引擎版本变更(ΔProof)。 +**验收升级(C4/★D25 修正)**:现状纪律「判据比较前必须 `clean-cache`」的消灭 = **整体目标(L7)**,其闭环依赖「命中路径产物保真」机制(**不属 S-D3 范围**);S-D3 的验收线改为「**差距 A 转绿 + 差距 B 仍红并附根因清单** + L7 保留为整体目标」(§5 S-D3 判据②/③)。 + +### 3.5 未覆盖面与回落纪律 + +- 缓存条目损坏/版本不识别/指纹分量缺失 ⇒ **响亮回落全量重编**(`cir_cache.cr:251-254` 已有 ver/magic 拒绝先例,扩展为分量缺失 ⇒ miss),**不得**按旧条目静默采用(同「义务不静默消失」纪律)。 +- 未覆盖的变更面(如编译器 schema 之外的构建参数)⇒ 登记进 `unit_fp` 的保守面(宁可多失效),并在 §7.2 登记。 + +--- + +## 4. 三者的公共基建 + +### 4.1 一套记录表 + 三个投影 + 一条追踪 + +**是共用一套**,理由不是「省代码」,而是**语义上必须**: + +- §1 问「**为何选**」——读记录的 `cause` / `alternatives`; +- §1.7 问「**这个不确定性从哪来**」——读**同一条记录**的 `evidence` 闭包(沿依赖边走,终止于边界/显式源); +- §3 问「**依赖了什么**」——读**同一条记录**的 `evidence` / `determinism`; +- §2 的界是 `kind = bound` 的**同表记录**(`choice = selected` 的界 + `alternatives` = 被拒的更弱界 + `cause` = 判定来源)。 + +即:**失效追踪 = 决策记录的输入依赖闭包**。一条记录若没有 `evidence`,它就**不可增量**(只能整块失效)——这把「记录完整性」与「失效精度」变成同一个变量,是设计上必须承认的成本驱动(粒度先粗后细,§5)。 + +**三个投影**:解释视图(人读「为何选」)|归因视图(人读「不确定性从哪来」= §1.7 的 provenance 链)|失效视图(机器读依赖闭包)——**同数据、同边、不同渲染**。**不允许**为失效追踪或归因链另建一套依赖表:那会立刻产生「记录说 A 依赖 B,依赖表说 A 依赖 C」的双源漂移(先例:`res_type_node` 双份 TY→TI 映射,R2 §4 步 3 明确要合一)。 + +### 4.2 依赖图三层(一层是既有) + +| 层 | 内容 | 来源 | +|---|---|---| +| 图内 | HDFG 节点/边 + region 嵌套 + state edges | **既有**(`dataflow.cr`,零改动) | +| 记录内 | 记录 → 其 `evidence`(输入引用) | 新增(§1.3) | +| 单元间 | 文件/符号/导入闭包/单态化实例/构建配置 | 既有 `module.cr res_imports` + 新增 `deps_fp` 摘要 | + +### 4.3 开关与开销 + +- **记录生成**:常开(轻量条目);**记录展开**:按需(§1.3)——保证缓存命中路径也有记录(L8),同时不给常态编译加可观测负担。 +- **落盘位置**:**先行 = 旁路文件**(S-D1 落地路径;与产物同目录或 `.core/`,命名待实施批定);**终态 = `.ccr` 决策记录段**(**已裁,★D4**:随 R2 P4 同波进段——**段 tag 空间可扩展**,`2026-09-09-lattice-ir-v7-format.md:44` 的 `7+` **未固定分配**(既有注明用途 = 驱逐标注段 / 证书段,与本段并列取号,段名随实施批定))——段化前旁路文件是唯一载体;段化后**两态产物须逐字节同**(守卫判据)。 +- **守卫判据**:记录通道关 → 产物逐字节同(照**审计通道默认关 + 两态产物 sha256 同**的既有纪律;现行载体 = `--verify-named-dedup` / `--verify-evp-nodes`;迁移期 `--type-shadow` 已随 R2 P5 Task 5 下线——R2 P6 Task 5 勘误)。 + +### 4.4 为什么不能各建一套(反例) + +差距 B(`.ccr` 冷热两态 sha256 不同)就是「没有共用载体」的代价:**编译器的决定没有随产物走**,于是缓存命中路径「少做了一些决定」,而没有任何数据结构能告诉你少做了什么。若 §1 自建一套解释数据、§3 自建一套失效数据,则: +- `explain` / `analyze --determinism` 在冷/热两态给出不同答案(因为决定没随条目走)——**直接违反本 spec §1.4 约束 1 与 §1.7.1 约束 6**; +- 缓存键无法知道某条目依赖哪些 policy/配置(因为依赖边与解释数据分家)——**差距 E 无解**。 + +⇒ L8 的强制:**记录随缓存条目一起存取**。 + +--- + +## 5. 最小可实施切片 + +四条各一个(三线索 + 路线图 §1.4 报告面),均独立可验收。顺序:**S-D4(最快,只读)→ S-D1(同批,共用记录表)→ S-D3(与 TODO #5 同批;承接 L8——不再是"独立并行",B 线 I1/★D26)→ S-D2**(S-A/S-C 就位后补声明档与映射层)。 + +### S-D3(语义增量,**先行**):缓存键依赖集 v1 + +| 项 | 内容 | +|---|---| +| 范围 | 键从单哈希升级为多分量:`src_fp`(现状) + `sig_fp`(现状) + **`unit_fp`**(编译器产物哈希 + 配置)+ `phase_ver` + `deps_fp` 粗档(本文件的导入闭包签名摘要) + **记录随缓存条目存取(L8;I1/★D26——存取机制在本切片,记录表本体来自 S-D1,建议同批)**。**不做**:`policy_fp`(等 S-A)、**`det` 分级复用面**(L3,随 S-D1 的记录表——**B 线评审 M6** 修法)、ΔProof、ΔBinary | +| 依赖 | 键修复**无前置**(今天可开工)。与 TODO #5 = 同一件事(本 spec 是其设计化);**L8 的记录表本体依赖 S-D1(建议同批)** | +| 落点 | `src/compiler/cir_cache.cr`(头部字段 + 读写 / `CIR_CACHE_VER` bump)、`src/compiler/main.cr:442-470`(键构造 + 泛型整关路径)、记录随条目的存取面 | +| 判据 | ① 差距 A 复现转绿(跨编译器重建**不复用**旧条目)② **差距 B 降级为登记(C4/★D25)**:同二进制冷 / 热两态 `corec ccr` 输出**仍不同**(`53ebcf12…` ≠ `5054e26d…`)——须附**根因清单**(字符串驻留序 / 全局初始化注入不重放,§3.2 差距 A/C);**L7 保留为整体目标**,其闭环机制**不在本切片范围** ③ 负控集(§3.4)① ② ④ ⑥ 各一条断言转绿;「判据比较前必须 `clean-cache`」纪律**在 L1 冷 / 热判据上仍保留**(随 L7 整体目标闭环后移除)④ 反向判据:未变更函数的条目字节未变 ⑤ 全套件 + 自举 `corec2/corec3` cmp IDENTICAL 保持(**计数口径引 R2 §4 该行,不在此写数字**——A 线评审 M1:门禁口径一律引 R2 §4 该行) | +| 不做 | 不实现增量后端;不改 `.ccr` 格式;不动 monomorph(差距 D 只登记);**不做命中路径的产物保真机制**(字符串驻留序 / 全局初始化重放——L7 整体目标,另立项,C4/★D25) | + +### S-D1(可解释):runtime check 必要性 explain + +| 项 | 内容 | +|---|---| +| 范围 | 决策记录表(§1.3 字段,L9 内容寻址 id)+ `checks` 问题类的生产者接线(`ext_safety.cr:18` / F11 侧表 `g_ir_slice_lens` / `IR_BOUNDS_CHECK` / checker R002)+ `corec explain FILE:LINE`(`--json` / 人读)+ `unattributed` 通道。**一条真实链端到端**,不做全问题类 | +| 依赖 | **无**(路线图 §3.2:可解释面可先做)。与 S-D3 建议同批(同一记录表 + 同一冷态纪律;冷 / 热两态随 L8 在 S-D3 验收) | +| 落点 | 新增决策记录模块(旁路落盘)+ `src/compiler/main.cr` 命令分派 + `src/compiler/diag.cr` 风格因由码措辞表;查询侧复用 `src/lsp/analysis.cr` 位置反查、`src/lsp/json.cr:614` | +| 判据 | ① 三条既有真实决策出记录:字面量界(编译期已知)/ 变量下标 + 字面量长度(`ext_safety.cr:31-33` 路径)/ 切片长度变量(F11 侧表)② 恒越界路径答 R002 ③ 无记录时**必须**落 `unattributed`(负控:`vectorize` 问题类今天答「生产者未实现」)④ 记录通道关 → 产物逐字节同 ⑤ 同产物两次 `explain` 输出逐字节同(**先行 = 冷态-only**;冷 / 热两态版随 S-D3 的 L8——I1/★D26)⑥ golden 快照入套件 | +| 不做 | 不做 `placement`/`ptr`/`vectorize` 问题类(`ptr` 已有 LSP hover 近似,接线排后续);不新增 LSP 方法;不做图级交互 UI(coreview 面) | + +### S-D2(可预测):静态 go 子集的在飞任务上界 + +| 项 | 内容 | +|---|---| +| 范围 | **结构层**上界:对 `IR_SPAWN`(`ast.cr:556`)`spawn_count ≥ 0` 的部分,在 region/依赖结构上求「最大在飞任务数」+「并发 region 清单」;输出状态 `PROVED`,`spawn_count = -1`(dynamic)部分落 `UNKNOWN` 并登记。**声明档**(`PROVED + assumptions[]`,载体 = policy 数据)排 S-A 后 | +| 依赖 | 结构层:无(`spawn_count` 字段今天在)。声明档:**S-A**(policy 数据 + 义务四档)。映射层(字节):**S-C / hw-map**(§2.6) | +| 落点 | 新增上界查询(读 `IR_SPAWN` + region 树;判定走引擎,原则 4);`explain --bounds` / `analyze --determinism` 的输出面复用 S-D1 的记录表 | +| 判据 | ① 固定 `go` 数例程出确定上界,与运行时实测并发峰值**一致**(正控)② 动态 spawn 例落 `UNKNOWN` 且**不渲染为 bound**(负控)③ 记录通道关 → 产物逐字节同 ④ 输出标层(结构层,不冒充字节数;`D_timing` 上不得把「在飞任务数 ≤ 4」读成时间上界,§1.7.2) | +| 不做 | 不做字节内存上界(S-C);不做时间/deadline(§2.4);不做自动并发(TODO auto-parallel,属 S-C 族) | + +### S-D4(路线图 §1.4 报告面,**最快落地**):`analyze --determinism` 只读版 + +| 项 | 内容 | +|---|---| +| 范围 | **只报告、不执法**(L12):轴族七项逐项状态(`PROVED`/`MODELED`/`UNKNOWN`/`EXPLICIT`)+ **不确定性 provenance 链**(`boundary #27 → parser → branch #61 → output`,§1.7.1)+ `residual` 残余清单 + S-E §5.2.3 最小字段面(`D_value`/`D_effect` 行含汇合项时;§1.7.1 硬约束 7)。**无成本模型时 `D_timing` 恒 `UNKNOWN`**(不得编 `MODELED`);**`EXPLICIT` 档的两个来源**:① **边界节点链——今天可用**(主人 = 边界节点 id,★D19);② 轴标注登记——待 S-A(无登记时该来源呈现为 `UNKNOWN + 来源待登记`) | +| 依赖 | **无**(不依赖 S-A/S-C/S-E 任何一项先行)。记录表与 S-D1 共用(建议同批);豁免:`MODELED` 数字需 S-C;`EXPLICIT` 的**标注来源**需 S-A(**边界来源今天可用**,★D19) | +| 落点 | 复用 S-D1 的记录表 + `src/compiler/main.cr` 命令分派新增 `analyze`;链的每跳复用同一 `evidence` 边(§4.1 归因视图);边界节点识别走既有 `unsafe`/边界语义 + 缓存语义条款 4 | +| 判据 | ① 固定例程出确定轴状态(正控:无边界纯函数程序 `D_value`/`D_memory` = `PROVED`)② 外部输入驱动程序出**非空 provenance 链**且链终止于边界节点,**该链状态 = `EXPLICIT`**(主人 = 边界节点 id;★D19)③ 断链例落 `UNKNOWN` + **断链位置登记**(不得补全)④ 空 evidence + 空 residual **非法**(§1.7.1 约束 2 负控)⑤ 同产物两次运行逐字节同(**先行 = 冷态-only**;冷 / 热两态版随 S-D3 的 L8——I1/★D26)⑥ `D_timing` 无模型时 `UNKNOWN`,**禁止**任何 `MODELED` 字样 ⑦ **效应保守面负控(B 线 C2/★D17)**:含 `print` 类调用的输出,`D_effect` **不得**仅凭「state edges 全序」出 `PROVED`(硬约束 8) | +| 不做 | **不拒绝编译、不做隐式竞争 reject**(执法点归 S-A/S-E);不定义汇合语义(S-E);不重造成本模型(S-C);不做图级交互 UI(coreview 面) | + +### 依赖与顺序 + +| 切片 | 前置 | 被谁阻塞 | +|---|---|---| +| S-D4 | 无(只读版) | 无——**最快落地**;`MODELED` 待 S-C,`EXPLICIT` 的**标注来源**待 S-A(边界来源今天可用,★D19);冷 / 热两态判据(⑤)随 S-D3 的 L8 | +| S-D3 | 键修复无;**L8 的记录表本体需 S-D1(建议同批)** | 无(roadmap §3.2 明写语义增量依赖缓存键修复——本切片就是修复);**原「独立并行」排序已按 L8 承接修正**(B 线 I1/★D26) | +| S-D1 | 无 | 无(建议与 S-D4 同批:共用记录表 + 同一冷态纪律);冷 / 热两态判据(⑤)随 S-D3 | +| S-D2 | 结构层无;声明档需 **S-A**;映射层需 **S-C** | S-A 定稿(声明档)、hw-map 落地(映射层) | + +--- + +## 6. 不承诺 + +### 6.1 路线图 §1.3 三类不可消残留在本 spec 的落法 + +| 残留 | 本 spec 的落法 | +|---|---| +| **错误 specification** | `explain` 报告的是「编译器**为何**这样做」,**不是**「这样做**对**」。记录的 `cause` 是因由码而非正确性证明;增量主判据(L1 逐字节同)是「同一编译器的两态一致」,**不等于语义正确**——不得当作验证证据引用。 | +| **恶意/失陷外部实体** | 决策记录的**载体是磁盘文件(旁路 + 缓存)**,可被篡改;伪造的记录能让 `explain` 撒谎、能让增量复用错误的决定。故:① `explain` 输出**不是**安全证据(它只谈编译器自己的决定);② 对抗篡改靠可复现构建/签名(路线图范围内、本 spec 范围外);③ 记录必须随缓存条目**一起**校验(分量不匹配即 miss,§3.5)。 | +| **需求本身允许危险行为** | 本 spec 不禁止任何能力:上界是**约束数据**,解释是**只读视图**。隔离边界(S-B 族)与上界声明是两件事——上界不构成隔离证明,反之亦然。 | + +**路线图 §1.4 面的同一读法**(确定性报告): + +- **错误 specification**:报告说「这个输出的不确定性归因到边界 #27 / 标注 #12」——归因正确**不等于**该归因合法、也不等于该 `race` 标注是用户本意。报告面只呈现,不判合法性(执法归 S-A/S-E)。 +- **恶意/失陷外部实体**:**物理不确定性的主要来源正是这一类**(调度、中断、外部输入、受损环境)——报告面能指出「不确定性从哪来」,**不能**消除它;`D_mapping`/`D_external` 的 PROVED 只对**声明域内**成立。 +- **需求本身允许危险行为**:`race` / `select_any` 是**显式请求**的不确定(§1.4 第 3 档)——报告面把它呈现为 `EXPLICIT`(有主),**不阻止、不试图消除**;「不确定性 = 显式请求」的全部要求就是它**不得偷偷发生**。 + +### 6.2 本 spec 特有的四条不承诺 + +1. **不承诺所有决策都可解释**。启发式的内部状态(预算余量、搜索顺序、成本启发式)可能**不可归因**;此类情形**必须**落 `unattributed` + 登记(§1.4 约束 3),**不得**事后编造因由码。`heuristic`/`env` 分级(L3)就是为这条服务。 +2. **不承诺上界对所有程序可判**。`UNKNOWN` 是**合法输出**(§2.2);动态 spawn、外部输入驱动的循环界、无尺寸标注的映射层、`D_timing` 无成本模型——都落在 `UNKNOWN`。把 `UNKNOWN` 渲染成 bound 等同静默降级(禁止)。 +3. **不承诺增量对所有变更正确**。承诺的是:**判据可测**(§3.4 三级 + 负控)+ **失效面覆盖**(分量表 §3.3)+ **未覆盖即响亮回落全量**(§3.5)。未覆盖的变更面**必须登记**(§7.2 / TODO),不得沉默。 +4. **不承诺 `MODELED` 是证明,也不承诺报告面能消除不确定性**。报告面只**归因**(每条不确定性有一个主人:边界 / 显式源 / 成本模型 / 断链),不承诺把它变成 `PROVED`。`MODELED` 的数字永远带 `band` + `assumptions[]` + 模型版本,**禁止**呈现为 WCET 证明(L13);`EXPLICIT` 是被明文请求的不确定,呈现它、不替用户消除它;`UNKNOWN`(含断链)是合法终局,**不得**用启发式常数补成 `MODELED`(§1.7.3 禁令)。 + +--- + +## 7. 不做项与开放问题 + +### 7.1 不做(YAGNI) + +- **不建成本模型、不推导时间上界、不做 PGO/向量化决策** → S-C(+ hw-map)。 +- **不做证明搜索/不变量推断**(ΔProof 只定义失效面)→ 验证切片轮(`src/kernel/`)。 +- **不做节点级 ΔBinary**(O7;前置 = 稳定身份 L9 的全面落地)。 +- **不做命中路径的产物保真机制**(字符串驻留序 / 全局初始化重放,§3.2 差距 A/C)——L7(冷 / 热逐字节同)保留为**整体目标**,其闭环机制另立项;S-D3 只做键的依赖集(C4/★D25)。 +- **不做自动修复建议**(code actions)→ LSP P1。 +- **不做分布式/跨机缓存的 explain**(`cir_cache_fetch`/`store` 预留接口保持原样,语义不变)。 +- **不新增 LSP 协议扩展**(LSP spec §6 纪律)。 +- **不做「解释优化器统计模型的内部量」**——内部搜索过程不是可审的对象;只解释**决定 + 因由码 + 输入证据**。 +- **不把查询面做成 Datalog/图查询语言**——起步 = 锚点 + 问题类;复杂查询语言是复杂度陷阱(与「不把机器写进程序意义」同纪律)。 +- **不定义汇合语义、不做不确定性执法**:`merge_deterministic` / `merge_ordered` / `race` / `select_any` 的图级定义与 `select` 重定 **逐字归 S-E**;「隐式竞争 → reject」的执法点归 S-A/S-E(路线图 §1.4 边界;本 spec 的 `analyze --determinism` = **只读**,L12)。 +- **不做物理不确定性的消除**(调度固定化 / 时间隔离 / 确定性执行沙箱 / WCET 工具链)——那是执行模型面(S-E)与环境面,不是报告面。 + +### 7.2 开放问题(各标处置方式) + +| # | 问题 | 处置 | +|---|---|---| +| **O1** | 记录**常开**的实测开销(记 `id/kind/subject/phase/choice/cause 码` 的每条成本)是否可接受?若不可接受,命中路径是否会退化成「无记录」而违反 L8? | **需实测**(S-D1 首批准据之一) | +| **O2** | 因由码体系的最小面:每个 pass 自定码 vs 全局码表?与既有 `error[XXXX]` 体系(`ast.cr` `EC_R_*`)合流还是分家? | **已裁(★D28)**:**全局码表,与 `error[XXXX]` 同源注册**(同一个注册表;用前缀区分来源,如 `E-CHK-*` / `E-EXP-*`),各 pass **只引用不自定**;措辞表按可审 / 可译 / 可测组织(§1.4 约束 4) | +| **O3** | 记录完整性的粒度 ↔ 失效精度的取舍(§4.1 成本驱动):哪些 pass 的 `evidence` 必须完整、哪些可粗? | **待论证**(S-D3 实施后按实测数据决定) | +| **O4** | 上界声明的载体:policy 数据(推荐,不升格)vs `where` 值约束扩展(三档语义已在)? | **已裁(★D5:守零新语法)**:走**既有通道**(插件标签通道 / policy 数据表——§2.5 的「policy 数据、不升格」保持);**不启用 `where` 值约束扩展**(破例面) | +| **O5** | deadline 超时动作的语义:panic 回落 / 显式 handler / 仅诊断? | **已裁(★D8)**:**仅诊断**——编译期标注 + 报告,不改运行时语义;硬行为留给库 / 运行时;语言不承诺时间上界(与「模型非证明」一致)。已回填 §2.4 | +| **O6** | 决策记录是否进 `.ccr` 段(与 R2 P4 的 TYPE/IFACE 段同波?)——会改字节,需 v8/扩展裁决 | **已裁(★D4 / L10)**:**随 R2 P4 同波进 `.ccr` 新段**(决策记录段;v7 `7+` 段位预留)——先行实现走旁路文件(§4.3),段化随 P4 波次 | +| **O7** | 节点级 ΔBinary 是否值得(稳定身份改造成本 vs 收益)? | **待论证**(L11) | +| **O8** | L2 判据(记录级等价)是否应升为**独立门**?产物相同不蕴含决策相同(做了不同决定碰巧同字节),而决策相同更弱于产物相同——两门的相对强度需定 | **待论证**(S-D1 落地后定) | +| **O9** | `MODELED` 与 `EXPLICIT` 同现时的**双标呈现**(§1.7.1 约束 5)是否够用?报告应按「输出」分列还是按「轴」分列(大程序下链数量控制)? | **待论证**(S-D4 出原型后定) | +| **O10** | `band`(不确定带)的口径:成本模型自带 vs 报告面统一要求的分位数? | **部分已裁**:`band` **必填**已由 S-C §2.3 承接(B 线 C1/★D24);**口径**(模型自带 vs 统一分位数)**待实测**(T4) | +| **O11** | **边界节点清单**的口径(链终止面):哪些节点算边界(`unsafe` / FFI / 输入 / 测量)——§1.6 已登记「边界是语义概念但无查询通道」,只读版需要一个最小清单 | **部分已裁(★D19)**:边界节点 = 一类**合法的显式不确定源**(主人 = 边界节点 id;链状态 `EXPLICIT`);**最小清单 = 缓存语义条款 4 的既有四类**(MMIO / FFI / 输入 / 测量)起步,扩展须登记——**S-D4 前置已解除** | +| **O12** | `analyze --determinism` 的默认锚点粒度(程序级 vs 每个可观测输出)与输出规模控制 | **需实测**(S-D4 首批准据之一) | diff --git a/docs/superpowers/specs/2026-09-11-merge-semantics-design.md b/docs/superpowers/specs/2026-09-11-merge-semantics-design.md new file mode 100644 index 00000000..3ee2de72 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-merge-semantics-design.md @@ -0,0 +1,504 @@ +# S-E:汇合语义定稿(merge semantics) + +日期:2026-09-11 +状态:**设计起草(2026-09-11 收敛批 v3)**——四形态图级定义 / 物理⇏语义降级义务 / `select` 重定与兼容窗口定稿;**收敛批 v3 = B 线 N1 必修闭环**(汇合判据从 region 身份改锚语义 + 判据非空性);实现计划后立(承路线图 §3 节奏:S-A 定稿并实施最小切片后,各族逐族出实施计划)。 +性质:路线图 §2 S-E 行的展开。本文只填本族范围,**不重定义**共享契约的 §1.1 术语 / §1.2 原则 / §1.3 不承诺(逐字遵守)。 +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**——术语/不承诺/§1.4 确定性轴族的唯一真源) +- `docs/dataflow-design.md`(`:61-74` load_balancer 例;`:102` select 行「最早到达的令牌」) +- `docs/project-book.md`(`:99` select → 动态图;`:241` 「等待最先到达的令牌」) +- `docs/execution-model.md`(`:15` DAG ⟹ 确定性;`:154` 逻辑时间保证事件偏序;`:236` go / 动态 select 的图) +- `docs/superpowers/specs/2026-08-08-region-cfg-design.md`(SG_* region / state edges / `g_df_node_region`) +- `TODO.md`(自动并发,2026-08-30 记) +- `2026-09-11-policy-state-version-toctou-design.md`(**外部效应的序通道**——权威事实锚点 = 其 **§1.2 事实底座**:修复前 = `df_connect_state` 不含 `IR_CALL_EXTERN` + 纯度标注乐观(**2026-09-11 效应/纯度批已修**,见该处回填条);本 spec §1.1/§4.1/N6 引用该事实,不复制其清单) +- **依赖 S-A**:`2026-09-11-semantic-safety-obligations-design.md`(属性轴 + 四档义务 + policy 接口)——**已落盘(2026-09-11 收敛批 v2/v3)**(原「并行起草」表述作废;本文引其 §1.9 / §2.2 / §2.6 定稿文字)。其 §1.9 已裁决本族的档位映射(race/select_any = **① prove + 报告义务,不新增第五档**)与族级 policy(`allow_enforce=false` → 隐式竞争固定落 ④ reject),并给出对本族的**硬约束**(其 §1.9 末条:`select` 重定完成前不得被当作「显式顺序源」用于 prove)。本文承接该约束(§1.2 否定例、§4.1)。 + +--- + +## 0. 范围与裁决 + +### 0.1 论题 + +> **汇合点的语义必须由图决定,不得由调度决定。** +> 物理到达顺序可以决定"谁来处理",但只有两种情形合法:① 图中已**证明**该影响不可观测(`merge_deterministic`);② 图中已**显式声明**该影响(`race` / `select_any`)。二者之外 = 隐式竞争 = 拒绝编译。 + +本 spec 处理的是**执行模型层**的语义:汇合(merge)这个图事件。它不碰属性轴与义务机制本身的定义(S-A),不碰报告面的 CLI 与输出契约(S-D)——只给二者的**接口需求**(§5)。 + +**现状缺陷(用户 2026-09-11 裁决 = 待修)**:三处文档把 `select` 的语义写成「**最早到达的令牌**」(`docs/dataflow-design.md:102`、`docs/project-book.md:241`;`docs/dataflow-design.md:61-74` 的 load_balancer 例是该语义的实例)。这是**把物理调度顺序泄进语义**——而 `docs/execution-model.md:15` 同时宣称「DAG ⟹ 依赖偏序——无反馈,确定性」。两句互相拆台:要么 DAG 的确定性是假的(汇合点可以由物理顺序注值),要么 select 的语义是错的。按共享契约 §1.4 的「不确定性 = 显式请求」,**后者是错的那一个**。 + +### 0.2 覆盖 / 不覆盖 + +| | 范围 | +|---|---| +| **覆盖** | 四形态的**图级**定义与判定判据、每形态的义务落档、观测可见性;物理⇏语义的降级义务(发射层/调度器/求值顺序三检查面);`select` 现状的重定、兼容窗口与文档同步清单;与 state edges / region / 自动并发的接口;给 S-A / S-D 的接口需求;正负控判据 | +| **不覆盖** | 属性轴与四档的定义(**S-A**);`core analyze --determinism` 的 CLI/输出契约与 provenance 链的呈现格式(**S-D**,本 spec 只给链的**形状要求**);成本模型与并行粒度启发式(**S-C**);调度器实现(部署配置面,本 spec 只给"不得引入"的判据);内存模型的数据竞争(不同问题——本族管可观测语义序,不管字节级竞争);`recv` / channel 的语法面 | + +### 0.3 裁决清单(本族的定案) + +- **L1 汇合点 = 图对象,四形态 = 图标注**。汇合点 M 定义为「**跨执行流 / region 汇合**的 DFNode」——**可判定判据(B 线 C3 收窄 → 收敛批 v3 语义锚,取代原「入数据边 ≥ 2」字面**与原 region 身份锚**;B-N1)**:M 的入数据边 ≥ 2 **且**至少满足其一: + - ① **并发汇合**:M 的输入前驱之间**无依赖路径**(在数据边 / state 边 / region 迭代序的传递闭包中互不可达),**且每个前驱各自可达一个不确定源根**——**并发根** `IR_SPAWN`(`ast.cr:556` = 27)|**迭代根**(`SG_LOOP` / `SG_FOR` 的 `nstart → exit` 序,`dataflow.cr:213`)|**边界根**(无配方条目,memory-model 条款 4)。 + - ② **迭代汇合**:M 的输入前驱在 **`SG_LOOP` / `SG_FOR`** 上跨**迭代边界**汇合(同一节点在两个迭代上的产出)。 + - ③ **定序汇合(`merge_ordered` 候选面)**:M 的输入前驱之间存在**跨 ≥ 1 个 region 边界**的定序路径(state 边链 / region 迭代序)——M 走 §1.5 步 3 判定(**不是**「无依赖路径」那一支)。 + - ④ **边界汇合**:M 的每个输入前驱**各自可达**边界节点(无配方条目)。 + **为何从 region 身份改锚到语义(B-N1 实测,硬性)**:今日 `sg_push` 的全部产生点 = `SG_FUNC`(`dataflow.cr:473`)、`SG_IF`(`ir_gen.cr:1622`)、`SG_LOOP`(`ir_gen.cr:1693`)、`SG_FOR`(`ir_gen.cr:1751`,经 `sg_alloc_push`)、`SG_UNSAFE`(`ir_gen.cr:2158`,同);**`SG_FLOW` 无任何产生点 ⇒ 恒不产生**(§4.2 现状断言已改);**`IR_PHI` 今日零发射**(只被消费:`ptr_analysis.cr:310` / `dump.cr:196` / `ent_kernel.cr:369`)。而**今日并发 = `IR_SPAWN` 且 `go` 体降为独立函数** ⇒ **两路 spawn 的产出在其消费者处汇合时,两个前驱同属调用者的 `SG_FUNC`** ⇒ 原 ① 不成立、原 ② 不适用、原 ③ 恒假、原 ④ 不适用 ⇒ **执法会静默变成 no-op,而验收面察觉不到**。语义锚使该场景可判,且不依赖 region 区间细节。 + **负控**:普通直线程序的二元节点(`a - b`、`s1 + s2 + s3` 的中间节点)**与 `SG_IF` 两分支交叉**(`SG_IF` 覆盖 `[condition, merge)`,`ir_gen.cr:1622` ⇒ 分支内取值的节点与分支外的节点在 merge 后同现时前驱分属 `SG_IF` / `SG_FUNC`)**都不满足任一条件** ⇒ 不属于汇合点,不得进入 §1.5 决策序、更不得 reject(N7 两例;原字面的过捕获 = B-C3/D18)。 + **登记(不作为今日判据)**:`IR_PHI` 及将来的 phi 形节点落地后,其出现可重启为**独立充分条件**(原判据 ②);`SG_FLOW` 若将来落地,其容器语义同样重启为充分条件(原判据 ③)。四形态**不是四个关键字**,而是 `determinism` 轴族(契约 §1.4)上的**开放标注**:`deterministic` / `ordered(src)` / `race(k)` / `any`。**命名对照**(本文两套记法同义,避免误读为八种东西):形态名 `merge_deterministic` ↔ 标注名 `deterministic`;`merge_ordered` ↔ `ordered(src)`;`race` ↔ `race(k)`;`select_any` ↔ `any`。形态名用于论述,标注名是图上实际标注的拼写。不加关键字、不改 Graph 本体(承契约 §1.2-1「属性不升格为语言 primitive」、定案 1「类型 = 图标注」)。标注的载体与注册通道归 S-A(**存储载体随 D4 裁决:与 R2 P4 同波进 `.ccr` 段**);本 spec 只给**语义内容**与**必填字段**(§5.1)。 +- **L2 四形态的区分量 = 观测结果对到达序的函数依赖**。设 M 的语义函数 `F_M(c, π) → obs`,其中 `c` = 输入值多重集、`π` = 物理到达序(**π 不是图对象**,这是本 spec 一切定义的前提):恒定 → `deterministic`;仅经显式顺序源 σ 依赖 π → `ordered`;依赖但**声明允许** → `race`;只声明对**来源身份**不敏感 → `any`。未证明且未标注 → **reject**(唯一执法点——**并发面**;外部不可见面的同形执法归 S-B5,§4.1 D23)。 +- **L3 观测(observation)面必须先定义,否则四形态不可判定**。`obs(·)` = ① 值输出(数据边出边上的值);② 效应(`STORE` / 通道 send / extern 调用 / `YIELD`);③ 控制去向(后续 region 归属,SG_IF/SG_LOOP 的走向)。**唯一不在观测面内的量 = 到达位置 / 到达时刻 / 到达顺序本身**——这就是本族的去物理化操作。任何把物理序升格为可观测值的图结构(读时钟、自旋计数、原子序号、优先级抢占痕迹)都是 π 的**代理量**,见 §2.2。 +- **L4 `race` 的结果集语义 = 只承诺集合,不承诺元素**。`Obs(M) ≜ { obs(F_M(c, π)) : π ∈ Π }`——**集合本身确定**,唯一的不确定是"集合内选哪个"。声明的附加义务:**集合可枚举**(或显式声明不可枚举 → 高安全模式 reject)。 +- **L5 `merge_ordered` 的顺序源白名单(首版即终版)= state 边链 ∪ region 迭代序**。两者都是**图上显式对象**。**白名单外的一切"顺序"都不是顺序源**:物理时间戳、`sleep`、线程 id、就绪队列位置、声明式优先级——它们读的是 π 的代理,落 `race` 或 reject(见 §1.2 否定例)。显式序号域(用户标注 order key)**已裁(D5):不纳入**——守零新语法,白名单不扩。 +- **L6 `select` 保留关键字拼写,语义重定为 `race(1)` 的糖衣**。存量无实现(§3.1 已核实),因此迁移是**文档语义迁移**而非代码迁移。未标注的 `select` 默认结论 = 「未证明未标注 → reject」;兼容窗口 **W1 = 1 个发布周期**(D3)内降为 warning + 自动标注 `race(1)`(§3.3;strict 档 / CI 门槛窗口内不放宽)。 +- **L7 自动并发只搬运已证明的 `merge_deterministic`**(承 TODO 2026-08-30 与契约 §1.4)。搬运 = lowering,须满足 §2 的全部三条检查面。`merge_ordered` 的**保序并行已裁纳入(Q5/D21)**:前置 = 两道门槛(可判定性 + 确定性判定),lowering 须证「state 边序 = 观测序」——见 §4.3。 +- **L8 本族**没有** enforce 通道(定稿结论)**。确定性**不可能**运行时强制:事后检测到"结果不同"已经太晚,事前强制 = 换实现 = 改语义形态(那是 `ordered` 的事,需要用户同意)。因此本族在四档上只落 `prove` / `proof-required` / `reject`——**enforce 档空白**。**S-A §1.9 已按此裁决**:族级 policy 取 `allow_enforce=false`(隐式竞争因此固定落 ④ reject,无需改决策树)。**不得**用"插入运行时检查"充数(§5.1-(2))。 + +--- + +## 1. 四形态语义(图级定义) + +### 1.0 共同图模型与判定问题 + +| 记号 | 含义 | 是否是图对象 | +|---|---|---| +| `M` | 汇合点:**跨执行流 / region 汇合**的 DFNode(可判定判据见 L1——**语义锚 = 输入前驱间无依赖路径 ∧ 各自可达不确定源根**;非普通二元节点) | 是(`g_df_nodes`) | +| `c` | M 的输入值多重集(无视顺序) | 否(运行时值) | +| `π ∈ Π(M)` | M 的输入令牌的**物理到达排列** | **否**——不是图对象,不可读、不可命名 | +| `obs(·)` | 观测(L3):值 + 效应 + 控制去向 | 是(出边 + state 边 + region 归属) | +| `F_M(c, π)` | M 的语义函数(图 + 输入 → 观测) | 是(图决定其形状) | +| `σ` | 显式顺序源:图上的定序对象 | 是(state 边链 / region 迭代序) | + +**判定问题**:`∀ π, π' ∈ Π(M),obs(F_M(c, π)) = obs(F_M(c, π'))` 是否成立? + +**记号前提(B 线 C3 附加)**:`c` 写作**多重集**仅在「位置绑定由图固定」时充分——入边的**位置绑定**(哪个输入接哪个操作数位)由**边身份**决定(图对象),**不是** π 的一部分、也不计入 π;若某形态的位置绑定随调度可变,则该形态不在四形态管辖内(须先修本表记号)。普通二元节点的位置绑定是编译期固定的 ⇒ **不属本族**(L1 预筛拦截;D18 选项 ③ 的记号问题在此收口)。 + +四个形态 = 对这个问题四种**语义承诺**,其区别只在「F 对 π 的依赖被如何处置」: + +| 形态 | 承诺 | +|---|---| +| `merge_deterministic` | **恒定**:F 与 π 无关 | +| `merge_ordered` | **投影依赖**:`σ(π) = σ(π') ⟹ F(c,π) = F(c,π')`,且 σ 由**图**决定 | +| `race(k)` | **集合确定**:`Obs(M)` 是确定的有限集,元素由实现自由选 | +| `select_any` | **身份不敏感**:F 只依赖"有输入"这件事,不依赖"从哪条边来" | + +这四行是**语义**,不是实现建议;下文的图表示 / 判据 / 落档 / 可见性全部从这四行导出。 + +--- + +### 1.1 `merge_deterministic` + +**图表示**:M 携带 `deterministic` 标注(或因无标注而被分析器**证明**后赋予)。要求 M 的输入集合**闭合**:所有可能到达 M 的输入边在图上是可枚举的(无"运行时新连的边"——动态图重连须先经 §2 判据)。 + +**判定判据(三合取)**: + +| # | 判据 | 判定面 | 手段 | +|---|---|---|---| +| (a) | **无 state 边入边**:M 不在副作用链上(kind=1 入边计数 = 0) | 结构 | 分析器自动(O(边数)) | +| (b) | **无 π 代理量**:从 M 沿 def-use + state 链可达的节点中,不存在读取到达位置/时刻/顺序的量(`now_monotonic`、自旋计数、原子序号、线程 id、就绪队列位置),也不存在以"到达次数/第几个到达"为条件的 BRANCH | 结构 | 分析器自动(**π 不可命名 ⇒ 一切"序依赖"都必然经由代理量**,故本条可判定) | +| (c) | **组合函数序无关**:M 的计算在输入多重集上良定义——组合算子满足交换 ∧ 结合(+ 幂等,若存在重复到达路径) | 值 | 引擎判定(R2:类型项/形状项);证不出 → 用户给证明 | + +**判据 (b) 的可判定性论证(本族的关键结构事实)**:π 不是图对象,图上没有"到达序号"这种输入端口。因此"依赖物理序"在实践中**只能**通过代理量实现,而代理量是图上的普通节点、可被扫描。这使「隐式竞争」成为**可判定**的(而非传统自动并行化那样靠依赖猜测——TODO 2026-08-30 记的同一论据)。 + +**判据 (a) 的可靠性前提(硬性——假阴性风险;**已于 2026-09-11 效应/纯度批满足**,修复前快照见下)**:判据 (a)「无 state 边入边」只在 **state 链覆盖全部可观测效应**时才是充分的;否则 (a) 会**假阴性**——把实际有序依赖的汇合误判为 `deterministic`。**前提已于 `762bd429`+`c9099d73` 满足**: +- 入链判据 = 单一真源 `purity_op_effect`(`checker.cr:2912`)——store 家族 + `IR_CALL_EXTERN` + `IR_SPAWN`/`IR_YIELD`/`IR_HOTPATCH_ROUTE`/`IR_DYN_DISPATCH`(间接调用)/`IR_STORE_PTR`/`IR_AWAIT` 全入链(`df_connect_state`,`dataflow.cr:146`); +- `fi_ispure` = `compute_all_purity`(`checker.cr:2959`)真值(保守起点「全不纯」+ 调用图不动点 + 递归/SCC 保守 + 不可解析调用保守)⇒ **一切未证纯的可解析调用**(`print` / `read_file` / `chan_send` / `sched_go`…)入链。**保守方向保证无假阴性**(宁可多判 `ordered`,不可漏判 `deterministic`)。 +- 修复前(历史快照,保留备查):`df_connect_state`(`dataflow.cr:160-177`)分类表不含 `IR_CALL_EXTERN`(`ast.cr:572` = 45);`fi_ispure` 唯一写入点(`checker.cr:1107`「all functions are pure」/ `:1142` extern)值恒 1 ⇒ 除 store 家族与不可解析调用(builtin)外没有任何调用进入 state 链;且链每函数重置(`dataflow.cr:401`)。 + +⇒ **外部效应已有保证的序通道(函数内)**(权威事实锚点 = `2026-09-11-policy-state-version-toctou-design.md` §1.2 回填条,即 S-B5 §1 事实底座;其处置面在那边)。 +**残余限定(硬性——(a) 的证据仍须带覆盖声明)**:① 链**每函数重置**(设计不变,`df_replay_state_chain`——判据域内调用节点 = 被调者效应的序代理;跨函数不连);② 覆盖率上界 = `purity_op_effect` opcode 清单的完备性(**新增 IR opcode 必须同步该单一真源**,否则链/纯度两判据漂移——D7 机械保证);③ lazy 判定冻结(`ir_gen.cr:1590` 生成期读乐观默认值)——不进链判据、不影响 (a),属本批未覆盖面。 +**本 spec 对 (a) 的实施要求(修复后修订)**:(a) 可按图结构直接判定——无 kind=1 入边 ⇒ 无函数内可观测效应序依赖;**证据仍须声明链覆盖**(承 S-D §1.7.1 硬约束 8:覆盖 = 上列清单 ∧ 三条残余限定)。方向选择理由不变:**误判 deterministic 是不可修复的语义错误**(报告面事后看不见)⇒ 覆盖声明缺失时保守落 ③/④,而非默认 `deterministic`。 +**修复史(原 D7 两步走分工,已闭合)**:`fi_ispure` 写入点更正 + extern 入清单(止血)与 `df_connect_state` 覆盖全部调用(root cause)**已作为 P0 插队批落地**(`762bd429`/`c9099d73`/`c7ca4251`;落地登记 = TODO #27),不再挂「单独立项」。 +**回填记录(2026-09-11,Task 4 Step 4 = 原「待纯度批落地后回填」marker 的落点)**:实测证据 = TODO #27(全量回归 38/38 rc=0 · 语料级旧前端 vs 当前 ELF 逐字节 20/20 同 · canary `95084e7b…d475` IDENTICAL · 自举稳定 · `.ccr` 差异仅 chain(kind=1)边;§4.1 与 N6 的保守面同批随动)。 + +**义务落档**:(a)(b) 自动 → 贡献零成本;(c) 自动证不出 → **proof-required**(用户提供证明/契约)。**不落 enforce**(L8)。**(a) 注(2026-09-11 回填后)**:前提**已成立**(见上)⇒ 无 kind=1 入边的汇合可按 (a) 判 `deterministic`(证据带覆盖声明);修复前口径 = 一律落 ③/④(历史)。 + +**观测可见性**:完全确定。**不得出现在 S-D 的"不确定"列表**(可出现在"已证明"列表)——这条是 §1.5 证明优先规则的产物。 + +--- + +### 1.2 `merge_ordered` + +**图表示**:M 携带 `ordered(src)` 标注,`src ∈ {state-chain, region-order}`(L5 白名单);且图上存在从 `src` 到 M 的**每条**入边的定序见证(witness)。标注必填字段 = src 的类型 + 见证的边集。 + +**判定判据**: + +1. **溯源**:M 的每条入数据边可溯源到一个顺序源(state 边链的链序,或 region 迭代序 `nstart → … → exit`); +2. **全序**:src 在 M 的输入集合上是**全序**(任意两条输入可比较;不冲突、不并列);并列时不得静默 tie-break(那会把 π 从后门放回来); +3. **非代理**:src 是图上显式对象,**不是** π 的代理(即 §1.1(b) 的反面检查——src 自身不得含 π 代理量)。 + +**否定例表(必须写清,否则"优先级 select"会被误当 ordered)**: + +| 候选 | 为什么不是顺序源 | +|---|---| +| 物理时间戳 / `sleep` 时长 | 读物理时间 ⇒ π 的代理 | +| 线程 id / 就绪队列位置 | 调度器内部状态 ⇒ π 的代理 | +| **声明式优先级**("req 优先于 signal") | 只决定**谁赢在同时就绪时**;而"同时就绪"本身由物理决定 ⇒ 仍是 π 的函数。⇒ 落 `race`(可选:优先级作为 **tie-break 规则**记录进报告,但不消除不确定性) | +| 通道 FIFO 序 | **半合法**:同一 channel 上的 send 序是图上的(生产者之间的 state 边链),跨 channel 则不是。⇒ 只在"每个输入边各自有 state 链"时可作 src | + +**义务落档**:**prove**(src 全序 + F 被唯一确定);src 存在但覆盖不全 → **proof-required**(补顺序源或补证明)。 + +**观测可见性**:确定。不进不确定列表;可进"已证明(顺序源:state 边 / region 序)"列表。 + +--- + +### 1.3 `race` + +**图表示**:M 携带 `race(k)` 标注(k = 胜者数;缺省 k=1)。**必须显式**——无标注的竞争 = 隐式竞争 = reject(§3.3 窗口内降为 warning)。 + +**判定判据**:`Obs(M) = { obs(F_M(c, π)) : π ∈ Π }` 良定义且**可枚举**(基数已知或可静态上界),或显式声明为不可枚举(→ 高安全模式 reject)。 + +**义务落档**:**① prove + 报告义务**(S-A §1.9 已裁决:显式请求 = 义务已被「程序说的是它想说的」处置,**不新增第五档**)+ **报告面强制登记**(S-D)。**prove 的证明内容不是空话**——须证明「该汇合点是此处**唯一**的不确定源」(声明不得成为隐藏相邻不确定性的挡箭牌)。**证明范围已裁(D10)= 局部**:只覆盖汇合点本身;**报告面必须显式标注「该保证未覆盖观测点」**(诚实性由报告面承担——S-D §1.7.1 硬约束 9)。升档到「到观测点整条路径」为**未来候选**(若实测证明挡箭牌不足再升;只动义务范围,不返工已有判据)。 + +**观测可见性**:报告面**必须**列出:① race 点 id;② 输入边集;③ k;④ 结果集 `Obs(M)` 的基数上界(或"不可枚举"标记);⑤ provenance 链(§5.2);⑥ **prove 范围 = 局部(未覆盖观测点)**。 +**禁止**:分支体把"我是第一个到达"当作**不经 witness 通道**的可读事实(例如以到达序索引写共享输出)。 +胜者**身份**作为显式出参(witness)= **已裁(D9):允许**——witness 是图上的一条**显式值边**(不是 π 代理),登记为 `EXPLICIT` 不确定源 + 报告义务;`select` 现有分支体写法(`req = recv => …` 用了 `req`)据此可平迁,`load_balancer` 例保留。 + +**race 不豁免确定性**:它承诺的是"**集合**确定"。把 race 读成"此处随便"是误读——`Obs(M)` 本身是程序语义的一部分,报告面要能把它列出来。 + +--- + +### 1.4 `select_any` + +**图表示**:M 携带 `any` 标注。语义 = `obs` 对**来源身份**不敏感:分支体的观测不依赖"哪个输入先到/到了哪个"。 + +**判定判据(两条路径)**: + +| 路径 | 条件 | 结果 | +|---|---|---| +| (i) **等价升级** | 各分支体**观测等价**(值 + 效应 + 控制去向三项等价,由引擎的等价判定给出) | 升级为 `merge_deterministic` → **prove**,零成本 | +| (ii) 默认 | 不可证等价 | 与 `race` 同:**显式不确定** + 报告面;`Obs(M) = { 各分支体的观测 }`(有限,可枚举) | + +**路径 (i) 的地位(区分设计与待论证)**:【设计已定】等价即确定的推理是直接的(分支选谁都一样 ⇒ obs 恒定)。【待论证】"观测等价"的**判定能力**——R2 引擎目前判定 `ty_sub`/`ty_equiv` 等类型项,**尚无效应等价(effect equivalence)判定器**。因此 (i) 是目标形态,落地依赖引擎的效应判定扩展;在此期间 (ii) 是实际路径。**不得**把 (i) 呈现为已可用。 + +**义务落档**:(i) **prove**(确定性,零成本);(ii) **① prove + 报告义务**(同 race:证明内容 = 该点是此处唯一不确定源;**范围 = 局部,D10**——报告面标注「未覆盖观测点」)。 + +**观测可见性**:(i) 不进报告;(ii) 进报告,附"来源身份不敏感"说明。 + +--- + +### 1.5 判定序(本族的决策过程) + +给定 M,按下序判定,**先到先得**: + +``` +0. 汇合点预筛:M 满足 L1 判据(跨执行流 / region 汇合,语义锚:① 无依赖路径 ∧ 各自可达不确定源根 / ② 迭代边界 / ③ 跨 region 定序路径 / ④ 边界根) + → 否则**不进入本序、且不做任何判定**(既不得 reject、也不得落 ③;判据不得因「根本不识别」而空过——B-N1) + (普通二元节点与 `SG_IF` 分支交叉均不属于本族管辖——不得 reject,B 线 C3 负控 + N7) +1. 结构预筛:无 state 边入边 ∧ 无 π 代理量 ∧ 输入集闭合 → 候选 deterministic +2. 引擎判定组合函数序无关(交换 ∧ 结合) → merge_deterministic [prove] +3. 顺序源 σ 在白名单内 ∧ 对 M 的输入全序 ∧ src 非代理 → merge_ordered [prove] +4. 顺序源存在但覆盖不全 → proof-required(补证明/补顺序源) +5. 有 race/any 显式标注 → ① prove(声明与实现一致)+ 报告义务(+ 可枚举义务) +6. 其余(未证明 ∧ 未标注) → reject ← 唯一执法点(并发面;与 S-B5 的分界见 §4.1 D23) +``` + +**证明优先于声明(冲突规则)**:若第 2 步已证明序无关,则即使 M 带 `race`/`any` 标注,**报告面按实际分类**(= 确定),标注降级为**冗余声明**诊断(warning,非错误)。理由:报告必须显示程序**实际**的不确定性,而不是用户**声明**的不确定性——否则 `race` 会成为"掩盖本来可证的程序"的通道,与「不确定性 = 显式请求」的意图相反(请求要显式,但不得伪装)。 + +--- + +## 2. 物理 ⇏ 语义:降级义务 + +### 2.1 「引入不确定性」的判据(定义) + +> **引入**:存在同一张图 G、同一输入 c 的两次执行 e₁, e₂,使 `obs(G, c, e₁) ≠ obs(G, c, e₂)`,而 `G` 上**没有**任何节点声明 `race`/`any`,**且**差异可归因于 {发射层, 调度器, 求值顺序} 三个物理面之一。 + +即:`∀G, c:obs(G,c,·)` 恒定,除非 G 上有对应声明。**违反 = reject**(编译错误,属"物理⇏语义被破坏");无法判定 → proof-required。 + +**本节论题句(写进实现规则)**:被限制的**不是调度器的自由度**,而是「自由度泄漏进 `obs`」。调度器可以任意选择(性能面),只要选择不影响观测。 + +### 2.2 三个检查面 + +| 面 | 检查内容 | 例子(合法 / 非法) | +|---|---|---| +| **发射层(lowering)** | IR → 机器码 / 表条目的 lowering 不得引入序依赖 | 合法:`merge_deterministic` 的 n 输入加法降低为"按寄存器分配顺序发射的链"——加法可结合,序不影响值。**非法**:把非结合算子(浮点加/乘、饱和加法、定长截断/溢出包裹、多字 tag/2L 编码下的**饱和**路径)降低为序敏感实现;或把"先到先累加、超界即截断"当作确定性汇合 | +| **调度器** | 就绪队列顺序(round-robin / work-stealing)不得成为语义输入 | 选择函数只允许读**图上声明的顺序源**(state 边就绪标记、region 迭代指针);**禁止**用时间片计数 / 线程 id 作 tie-break 并让该 tie-break 影响 `obs` | +| **求值顺序(编译器内部)** | pass 重排 / 内联 / CSE / 寄存器分配不得改变 merge 点的输入到达集合,或引入序依赖 | 特别:LICM / 惰性下沉**不得**把带 state 边的节点跨越 merge 点移动;CSE 合并两个 race 点会改变 `Obs(M)` 基数 ⇒ 须检查 | + +**这条给编译器 pass 的可陈述不变量**:每个变换都保 `obs`。原文(TODO / pass 文档)里"凭感觉安全"的表述,应由本判据替代。 + +### 2.3 与「部署配置不改语义」的关系 + +`docs/execution-model.md:19`「执行方式(串行/并行/调度策略)由部署配置决定,图语义不变」——本节的判据正是这句话的**可检查形式**:部署配置切换调度策略后 `obs` 必须不变(除已声明点)。因此本族**加强**而非削弱该原则:把"图语义不变"从口号变成合规条件。 + +--- + +## 3. `select` 现状的重定与迁移 + +### 3.1 现状盘点(**已核实的仓内事实**,2026-09-11) + +| 面 | 事实 | 证据 | +|---|---|---| +| 词法 | **无 `select` 关键字、无 `recv` 关键字** | `src/compiler/lexer.cr:72-108` 关键字表:有 `go` / `await` / `flow` / `yield`,无 `select` / `recv` | +| 语法 | **无 Select 产生式** | `grammar/core.ebnf` 只有 `FlowDecl` / `GoExpr` / `YieldStmt` | +| 语法树/IR | **无 select 或 merge 汇合 IR** | `src/compiler/ast.cr:553-563` 并发相关仅 `IR_SPAWN`(27) / `IR_YIELD`(28) / `IR_AWAIT`(29) | +| 源码 | `src/compiler/*.cr` 内 `select` 出现处全为同名无关物(`instance_select` HIT 表实例选择、`selectionRange` LSP 字段、注释 `selection`) | 逐处核对(interp.cr:385 / corearch.cr / ent_kernel.cr:47 / lsp/analysis.cr:916 / ir_gen.cr:1936) | +| 测试 | `tests/suite/` 无 select 用例;并发测试用 `chan_recv`/`chan_send` | chan_test.cr / conc_test.cr / go_*_test.cr | +| 文档 | `select` 语义仅存在于三处文档(本 spec §0.1 所列) | — | + +**结论**:**现状 `select` 语义是纯文档语义,无实现**。因此本迁移**没有已编译的用户代码要兼容**——兼容窗口的对象是**文档 / 示例 / 未来的语法设计**。这是本 spec 与其它 S-* 族最大的不同,必须在实施批反复强调(避免为不存在的存量付出迁移成本)。 + +### 3.2 迁移目标 + +`select { p = recv => body }` 重定为:**多输入汇合 + 内容相关分派**,其图级默认 = **未标注**。语义不再是"最早到达"——"最早"是 `race(1)` 的**实现选择**,不是**语义承诺**。 + +| 情形 | 判定形态 | 落档 | +|---|---|---| +| 分支体观测等价(可证) | `select_any` → 升级 `deterministic` | prove | +| 到达顺序由 state 边 / region 序固定 | `merge_ordered` | prove | +| 分支体不等价 ∧ 无顺序源 ∧ 有显式标注 | `race(1)`(+ `any` 若身份不可观测) | ① prove + 报告义务(范围 = 局部,D10;witness 允许,D9) | +| 分支体不等价 ∧ 无顺序源 ∧ 无标注 | **reject**(窗口 W1 内:warning + 自动 `race(1)`;strict 档 / CI 门槛下窗口不放宽) | reject / 窗口内警告 | + +### 3.3 兼容窗口 + +| 阶段 | `select` 行为 | 诊断 | +|---|---|---| +| **W0**(现状 → 本 spec 生效前) | 文档语义 = 最早到达(无实现) | — | +| **W1**(窗口期) | 未标注 `select` **接受**,自动按 `race(1)` 处理 | **warning**:「隐式竞争:`select` 隐式请求不确定性;请显式声明 `race(k)` / `any`,或提供确定性证明(顺序源 / 分支等价)」+ 报告面登记 | +| **W2**(窗口关闭) | 未标注 `select` = **reject** | error + 指向三形态修复通道 | + +- **窗口触发条件** = 首个 `select` 实现落地(当前无实现 ⇒ 窗口尚未开始,W0)。 +- **窗口长度 = W1 = 1 个发布周期**(**已裁,D3**:分层默认——本地 / 默认档窗口内 lenient + 诊断 + 自动 `race(1)`;**strict 档 / CI 门槛 = 严**:未标注 `select` 直接 reject,迁移窗口不构成严格档的例外)。**W2 之后不设「永久 lenient」**(D3)。 +- **关键区分**:窗口内容是「**接受并标注**」,**不是**「保留旧语义」。旧语义(最早到达)在**任何阶段**都不再是语义——它降格为 `race(1)` 的一种实现选择。这条必须写进诊断文案:**不承诺"最早到达"**,因为承诺它等于承诺调度器行为。 + +### 3.4 文档同步清单(**实施批执行**;本 spec 不改动这些文件) + +| 文件:行 | 现状 | 应改为 | +|---|---|---| +| `docs/dataflow-design.md:102` | `select { ... }` 行:「多输入汇合点,任一输入到达即激活对应分支 / **最早到达的令牌**」 | 保留前半(图语义正确);激活条件改为「**由汇合形态声明决定**(S-E)」 | +| `docs/dataflow-design.md:61-74` | load_balancer 例(隐含最早到达) | 例保留,补注「`select` 的不确定性须显式声明」 | +| `docs/project-book.md:241` | 「select 在多个输入边之间等待**最先到达的令牌**」 | 「select 在多个输入边之间汇合,**其形态由显式声明决定**;未声明且未证明汇合无关 ⇒ 拒绝编译」 | +| `docs/project-book.md:99` | 「flow、go、yield、select → 运行时动态图」 | 保留,补「(含显式声明的不确定)」(该行图分类正确,只需标注) | +| `docs/execution-model.md:15` | 「DAG ⟹ 依赖偏序——无反馈,确定性」 | **收窄措辞**:确定性成立的前提是**无未声明汇合**;补一行「汇合点的确定性由 S-E 四形态判定」 | +| `docs/execution-model.md:236` | 「类型:带有 go、动态 select 的图」 | 保留,补「(select 的不确定性为显式声明)」 | +| `docs/execution-model.md:154` | 「系统时钟提供逻辑时间,保证事件偏序」 | 保留(与本族一致:逻辑时间保证**事件偏序**,不等于保证**到达序**——到达序仍是物理量,恰是本 spec 的去物理化对象)。**建议补一句区分** | +| `docs/language-syntax.md:567-580` | §11.3 `flow` / `yield` / `recv`(recv 为 `f.recv()` 拼写) | 按 Q3/D5 裁决:`select` **拼写保留、语义重定**(§3.5;不新增关键字);补语法小节属实施批,**以本 spec §3 的语义为准** | + +**同步时机**:属实施批(本 spec 只列清单)。**本 spec 不修改上述任何文件。** + +### 3.5 `select` 是否保留关键字 + +**裁决:保留拼写、重定语义。** + +理由:① 现状无实现 ⇒ 无迁移成本(§3.1);② 拼写保留使既有文档/示例保持可读,改动面收敛到语义段;③ 四形态**不进关键字表**(L1:形态是图标注,源码拼写由 S-A 的标注通道承载,如 `where`/annotation)——所以"保留 select"与"不加新关键字"不冲突:`select` 是**汇合语法**(图构造),`race`/`any`/`deterministic` 是**图上标注**(属性面)。 + +**未采用备选**:把 `select` 收编为库函数/宏(`chan.select(...)`)。理由:汇合是**图构造**而非库调用——它改变图的拓扑(多输入边汇合点),库函数无法在类型层表达"入边集合"这一图结构。 + +--- + +## 4. 与既有机制的接口 + +### 4.1 state edges(`dataflow.cr`,kind=1) + +- **双重角色**:① `merge_ordered` 的**第一顺序源**(副作用链的链序 = 图上全序);② `merge_deterministic` 判据 (a) 的**排除面**(有 state 边入边 ⇒ 不是候选确定)。 +- **方向性提醒(易错)**:state 边保证的是**序**,不是**结果与序无关**。两条边的作用方向相反——`ordered` 用序定结果,`deterministic` 用"无序可依赖"绕过序。实现时不得把"有 state 边"直接判成"确定"或"不确定",须按 §1.5 走判定序。 +- 现有构件:`df_add_edge_kind`(`:136`)、`g_last_state_node` 副作用链(`:174`)、loop exit 终止依赖(`:93`)、`ESZ_DFEDGE` = 32B(`dyn_arr.cr:102`,kind 字段已在)。 +- **`select` 不是顺序源**(承接 S-A §1.9 硬约束,§5.1-(5)):汇合点的定序**只能**来自 state 边链或 region 迭代序;`select` 的"胜出边"是 `race` 的结果,**不得**回填为顺序源用于 prove(否则把"最早到达"从后门固化为证明前提)。 +- **外部效应的序缺口(**已闭合**,2026-09-11 效应/纯度批;与 S-B5 同源;事实面历史比"extern 缺口"更宽,B 线 C2)**:修复前 `IR_CALL_EXTERN`(`ast.cr:572` = 45)不在 `df_connect_state` 分类表内,**且纯度标注恒"纯"**(`fi_ispure` 唯一写入点 `checker.cr:1107`/`:1142`,值恒 1)⇒ **任何可解析调用**(`print`/`read_file`/`chan_send`…)同样不入 state 链(§1.1 可靠性前提之历史)。**修复后**(`762bd429`+`c9099d73`:分类表 = 单一真源 `purity_op_effect` + `fi_ispure` 真值)三个后果逐条状态:① **闭合**——`merge_ordered(state-chain)` 可为 extern / 间接调用 / 并发 / 挂起 / 裸指针写与一切未证纯调用定序(函数内);② **闭合**——判据 (a) 的假阴性通道由「保守方向入链」封死(证不纯者恒入链);③ **限定**——S-D 的 `D_effect` 报告须声明链覆盖范围(S-D §1.7.1 硬约束 8 已同步修订;残余 = lazy 判定冻结 + 链每函数重置)。 + **处置(未变)**:见 §1.1 实施要求 + **全仓唯一一份效应清单**(承 D7/D16:权威面挂 S-A policy 面 / HIT 投影,事实锚点 = `policy-state-version-toctou-design.md` §1.2 回填条;本 spec 只引用不复制)+ 链覆盖 opcode 清单 = `purity_op_effect` 单一真源。修复史(止血 + 修链 = 原「单独立项」)已作为 P0 插队批落地(TODO #27)。 +- **与 S-B5 的执法分界(D23,2026-09-11;B 线 I3 的 S-E 侧)**:本族的「隐式竞争 → reject」与 S-B5 的「外部不可见面 → 非 prove」是**同一形状**,**不得两处各判一次**——分界**按变更源身份**(唯一真源 = `policy-state-version-toctou-design.md` §2.1):`mutator ∈ 图节点集合` ⇒ **本族**;`mutator ∈ 边界条目 / 未知 extern` ⇒ **S-B5**;**交集非空 ⇒ 先 S-B5 的保守面,本族不重复报**(恰一条诊断)。对应**交叉负控** = S-B5 §5.4;本 spec 侧只消费该定义,不另立判据(此前「本族唯一执法点」的措辞按本条限定为「并发面唯一执法点」)。 + +### 4.2 region(SG_* 与 `g_df_node_region`) + +- **第二顺序源**:SG_LOOP / SG_FOR 的迭代序(`nstart → … → exit`)是图上全序。**现状事实(已核)**:`SG_LOOP`(`ir_gen.cr:1693`)与 `SG_FOR`(`ir_gen.cr:1751`,经 `sg_alloc_push`)**今日均产生**。 +- **region 种类的现状事实(2026-09-11 复核;原「SG_FLOW = 动态汇合的容器(已有)」不成立,已改)**:`sg_push` 的全部产生点 = `SG_FUNC`(`dataflow.cr:473`)· `SG_IF`(`ir_gen.cr:1622`)· `SG_LOOP`(`ir_gen.cr:1693`)· `SG_FOR`(`ir_gen.cr:1751`)· `SG_UNSAFE`(`ir_gen.cr:2158`)。**`SG_FLOW`(`dyn_arr.cr:129` = 3)无任何产生点 ⇒ 预留、未产生**——它只出现在命名/转储表(`dataflow.cr:505` / `dump.cr:295`)。**登记**:跨执行流汇合的容器语义**待 `SG_FLOW` 落地后启用**;在此之前 L1 的判据**不得**引用它(B-N1)。**这是本 spec 的现状陈述,不是设计删减**:四形态定义不依赖 region 种类。 +- **`SG_IF` 覆盖区间(过捕获来源,判据不得据此识别汇合)**:`SG_IF` = `[condition, merge)`(`ir_gen.cr:1622`)⇒ 分支内取值的节点与分支外的节点在 merge 后同现时,前驱**分属 `SG_IF` / `SG_FUNC`**——**这不是两个执行流**,L1 负控与 N7 已按此设例(B-N1 ④)。 +- **SG_IF** = merge 后的**控制去向**——是观测面的一部分(L3 第 ③ 项),故 `merge_deterministic` 的判据必须覆盖"汇合后的分支走向是否稳定"。 +- **跨 region 汇合**(汇合点在父 region、输入来自两个子 region)是典型候选;判定用 `g_df_node_region` 归属 + parent 链 O(1) 查询(照 region-cfg §6 的迁移方式)——**这就是 L1 收窄后的汇合点判据 ① 的实现面**(B 线 C3)。 + +### 4.3 自动并发(`TODO.md` 2026-08-30 记) + +- **硬性接口(L7)**:自动并行**只搬运已证明的 `merge_deterministic`**(+ `merge_ordered` 的保序并行——Q5 已裁纳入,见下)。搬运 = 一次 lowering(引入 `IR_SPAWN` + 汇合),因此**必须**满足 §2 的三个检查面;违反 ⇒ 该次分派非法。 +- **与 S-C 的互引(B 线 I2/D21)**:S-C §2.2 的合法性清单已加**汇合点确定性门**(本 spec §1.5 决策序)、§2.4 已承接本 spec §2.2 的三检查面、§4 自动并发行已注明**两道门槛**(成本模型与调度开销归 S-C)——**只过依赖门即搬运 = 违反 L7**。 +- **与 TODO 原文的衔接**:TODO 说"无依赖 region → 自动分派"("可判定"而非"依赖猜测")。本 spec 补上的是**第二道门槛**:可判定性给的是"能并行",确定性判定给的是"并行后语义不变"。**两道都过才搬运**。 +- **merge_ordered 的保序并行**(pipeline)= **已裁(D21/Q5):纳入**可搬运集合——**前置 = 两道门槛都过**(可判定性 + 确定性判定);lowering 须**证明「state 边序 = 观测序」**,证不出则不搬运。该门槛是 §2 判据在 lowering 面的实例(见 §2.2 发射层)。 +- **与 R-HLS 的差异**(TODO 已引):RVSDG 动态调度暴露图内并行面;本 spec 新增"搬运前必须证明序无关"的门槛——**这是 Core 与常规自动并行化的分野**,也是 40 年自动并行化失败史的正面回应。 +- **成本模型**(调度开销 vs 收益)= **S-C 范围**,本 spec 不碰。 + +### 4.4 `g_df_*` 数据面呈现 + +- 汇合点识别(**按 L1 收窄判据**:跨执行流 / region 汇合——非"入度 ≥ 2"字面)、四形态标注、顺序源见证**全部落在现有 DFNode/DFEdge 结构上**:只加标注(`determinism` 轴族,S-A 的机制),**DFNode/DFEdge 不加新字段**。若实现上必须有索引,用 `g_df_node_region` 同款的并行数组(跟随 `g_df_node_cap` 增长的旁路数组),作为实现选项,实施批定。标注的**存储载体**(`.ccr` 标签段)随 D4 裁决面(与 R2 P4 同波),不在本 spec 定。 +- 呈现面:`.`cir`/`.ccr` 的 DOT 输出(state 边已用虚线区分)增加四形态渲染;`--dump-entries` 通道照 HIT 表的既有调试面模式。具体格式 = 实施批。 + +--- + +## 5. 义务与报告接口(给 S-A / S-D) + +### 5.1 给 S-A(轴 / 四档 / 义务机制) + +本 spec **不定义**轴与四档,只给这三项输入: + +**(1) 落档表(本族四形态 + 隐式竞争行 → 四档)——与 S-A §1.9 同源(其已裁决,本文不另立)** + +> **行形说明(A 线评审 M4 修法)**:表内 6 行 = **4 形态 + 隐式竞争行 + `select_any` 可证等价(子情形)**;**前两行 = S-A §2.6 行 1**(依赖 / 状态边足证「汇合与调度无关」);`ordered` 的 ③ 与 `select_any` 可证等价行为**本 spec 供给的子情形**(S-A §2.6 无对应行,不构成冲突——S-A 只消费标记存在性,本 spec 是供给侧)。 + +| 形态 | 落档 | 备注 | +|---|---|---| +| `merge_deterministic` | **① prove** | 结构判据自动 + 值判据引擎 | +| `merge_ordered` | **① prove** / **③ proof-required** | 顺序源全序可证 / 覆盖不全 | +| `race(k)` | **① prove + 报告义务**(S-A §1.9:显式请求 = 「程序说的是它想说的」,**不新增第五档**) | 证明内容 ≠ 空话:「该汇合点是此处唯一的不确定源」;**范围 = 局部(D10)**——报告面标注「未覆盖观测点」;witness 允许(D9,登记 `EXPLICIT`) | +| `select_any`(不可证等价) | 同 `race` | | +| `select_any`(可证等价)→ deterministic | **① prove** | 依赖引擎的效应等价判定(【待论证】,§1.4) | +| 未证明 ∧ 未标注(隐式竞争) | **④ reject** | 唯一执法点(**并发面**——外部不可见面的同形执法归 S-B5,§4.1 D23);族级 policy `allow_enforce=false` + `strict=true`,决策树固定落 ④(S-A §1.9/§2.2) | + +**(2) enforce 档空白(L8)**:本族**没有** enforce 通道。确定性不可运行时强制(事后太晚 / 事前=改语义)。S-A §1.9 已按此给族级 policy 取 `allow_enforce=false`——**本项已闭合**,不再是待裁决项。**不得**用"插入运行时检查/比较器"充数(那是把义务做成 lint,违反契约 §1.2-2 的精神)。 + +**(3) 标注的语义内容与必填字段**(载体/语法由 S-A 定): + +| 形态 | 必填字段 | +|---|---| +| `deterministic` | 证明引用(结构性 (a)(b) 自动;值的 (c) 由用户提供时记录其引用) | +| `ordered(src)` | src 类型(`state-chain` / `region-order`)+ 定序见证(边集) | +| `race(k)` | k + `Obs(M)` 基数上界(或 `non-enumerable` 标记)+ **若暴露胜者身份则 witness 引用**(D9:显式值边,登记 `EXPLICIT` 不确定源) | +| `any` | 无(+ 若走了 §1.4(i) 的等价升级,记录引擎判定的引用) | + +**(4) π 不可命名(本族的结构性前提,建议 S-A 纳入机制约束)**:图上**不提供**"到达序号"这类输入端口。这不是实现约定,是**语义主张**——它使"隐式竞争"可判定(§1.1 判据 (b) 的论证)。 + +**(5) 承接 S-A §1.9 的硬约束**:其末条要求「在 S-E 完成 `select` 重定之前,`select` **不得**被当作『显式顺序源』用于 prove——否则等于把现有语义缺陷固化为证明前提」。本文**满足并加强了**该约束: +- §3.2 已把 `select` 重定完成(默认形态 = 未标注,判定走 §1.5 决策序); +- §1.2 否定例**永久排除** `select` 与优先级作顺序源(不只是迁移期)——`select` 语义本身**不含**任何定序承诺,重定后亦然; +- 唯一合法的定序来自 L5 白名单(state 边 / region 序),与 `select` 无关——**显式序号域已裁不纳入(D5/Q2)**:白名单首版即终版,不随迁移期结束再扩。 + +### 5.2 给 S-D(`core analyze --determinism` 报告面) + +**provenance 链的形状要求**(本 spec 只给形状,格式归 S-D): + +``` +来源节点 ──(传播)──▶ 汇合点 ──(传播)──▶ 观测点 +boundary #27 → parser → branch → output + ↑ ↑ ↑ ↑ + 外部输入面 数据边/state边 race/any ACTIVE_OP + (D_external) (图传播) 标注点 (可观测) +``` + +即:每条不确定性的报告项 = **从源到可观测点的一条链**,链上必须标出**汇合点**(不确定性被引入/放行的位置)与**形态**。要点: + +1. **汇合点是链上的检查点**,不是链的起点——起点是外部来源(boundary),终点是可观测效应。同一汇合点可出现在多条链上,报告应能按汇合点聚合。 +2. **已证明的汇合不得出现在不确定列表**(§1.5 证明优先);"冗余声明"(标了 `race` 但已证明确定)单独列一类,供用户清理。 +3. 本族**必须**提供的字段(最小面):形态 / 汇合点 id / 输入边集 / 顺序源(`ordered`)/ k 与 `Obs(M)` 基数界(`race`)/ 见证引用 / 分类(已证明 · 已声明 · 冗余声明 · 拒绝)。**落点 = S-D §1.7.1 硬约束第 7 条**(B 线 I3/D20:此前"格式归 S-D"两侧都以为对方承载 ⇒ 现指定为 S-D 输出契约的硬约束);`冗余声明` 类照 §1.5 证明优先规则单列。 +4. `boundary #27` 式的**外部输入面**(`D_external` 轴)由 S-D/S-A 定义,本 spec 只在链中引用它,不定义它。 +5. **边界节点与本族的显式标注并列(D19)**:边界节点(`unsafe` / FFI / 输入 / 测量,清单口径归 S-D O11)本身就是一类**合法的显式不确定源**——主人 = **边界节点 id**,链状态 = `EXPLICIT`,与 S-A 的轴标注登记**并列**,**不因「无标注」降为 `UNKNOWN`**。 +6. **局部 prove 的呈现义务(D10)**:`race` / `select_any` 的 `EXPLICIT` 项**必须**标注「该保证未覆盖观测点」(prove 范围 = 局部)。 + +--- + +## 6. 判据(验收) + +### 6.1 正控 + +| # | 场景 | 期望 | +|---|---|---| +| P1 | **两路 `go`(`IR_SPAWN`)各自纯计算、互不依赖**,产出汇合到可交换组合(整数加法);**或**两个无依赖 region 同形汇合 | `analyze --determinism` 报**已证明**,零诊断;**非空断言(B-N1②)**:该汇合点的 **id 必须出现**在报告条目中(借 S-D §1.7.1 硬约束 7 的字段面)——防「根本不识别汇合点 ⇒ 正控空过为真」 | +| P2 | 汇合的两条入边在同一 state 边链上(副作用序固定) | 报**已证明(顺序源:state 边)** | +| P3 | `select` 各分支体观测等价 | 升级 `merge_deterministic`,**prove**,不进不确定列表 | +| P4 | SG_LOOP 内两迭代产出的汇合(region 迭代序固定) | 报**已证明(顺序源:region 序)** | + +### 6.2 负控 + +| # | 场景 | 期望 | +|---|---|---| +| N1 | **隐式竞争必须 reject**:两处 `go`(`IR_SPAWN`)的输出汇合到同一消费点,组合**非**可交换(如字符串拼接顺序敏感)、无标注、无 state 边定序 | **error**,文案含三条修复通道(声明 `race` / 补顺序源 / 证等价)+ **非空断言(B-N1②)**:诊断**必须含该汇合点的 id**(本族唯一执法点,不得退化为静默 no-op——今日两路 spawn 的产出同属调用者 `SG_FUNC`,靠 region 身份识别会漏,判据已改语义锚) | +| N2 | `race(1)` 显式标注 | **进报告**:race 点 id、输入边集、k=1、`Obs(M)` 基数的界 | +| N3 | 标了 `race` 但分析器证明确定 | **冗余声明 warning**(非 error),报告列为"已证明" | +| N4 | 「优先级 select」(优先级 + 就绪竞速),无别的序 | **不得**判为 `merge_ordered`(白名单外,§1.2 否定例)→ 落 `race` 或 reject | +| N5 | **lowering 引入序**:把非结合组合(浮点加 / 饱和加 / 截断)的 `deterministic` 汇合降低为序敏感实现 | 须被捕获。判据 = 结构单测(降到序敏感发射模式即失败)**或**双调度对拍(同一图、两种调度、`obs` 不同即失败) | +| N6 | **判据 (a) 假阴性**:`IR_CALL_EXTERN` **或任何未证纯的可解析调用**(`print`/`read_file`/`chan_send`…)下游的汇合(**2026-09-11 效应/纯度批后**:这些调用已入 state 链 ⇒ 该汇合有 kind=1 入边 ⇒ **被 (a) 自然排除**;负控仍必须**不得**判 `merge_deterministic`——残余执法面 = 覆盖声明缺失时的保守落档(§1.1 残余限定)) | **不得**仅凭"无 state 边入边"判为 `merge_deterministic`——修复前须按 §1.1 的保守判定落 ③/④(除非另有定序通道);修复后该例的 kind=1 入边本身即排除 (a)。**这是"宁可保守"的执法点**:误判确定 = 语义错误,且报告面看不见 | +| N7 | **过捕获负控(B 线 C3 + B-N1④)**:① 不含并发 / 循环的直线程序的普通二元运算(`a - b`;`s1 + s2 + s3` 的中间节点);② **`SG_IF` 分支交叉**——分支内取值的节点与分支外取值的节点在 merge 后同现(`SG_IF` 覆盖 `[condition, merge)`,`ir_gen.cr:1622`,故前驱分属 `SG_IF` / `SG_FUNC`);③ 同一例但**包在 `SG_LOOP` / `SG_FOR` 内**(迭代根可达 ⇒ 判据 ② 会命中)——预期与 ①② 相同 | 三例均**不得**进入 §1.5 决策序、**不得** reject、**不得**落 ③;其组合函数无需交换 / 结合证明(L1 预筛步 0 拦截)。③ 是**边界探针**:若实测落在 ③/④,须登记为 L1 判据 ② 的过捕获并把「迭代根」限定为「**跨迭代**(同一节点的两个迭代产出)」而非「在迭代内」 | +| N8 | **保序并行门槛(D21)**:`merge_ordered` 并行 lowering 未证「state 边序 = 观测序」即分派 | 该次分派**非法**——不得仅凭依赖门搬运(两道门槛) | + +### 6.3 判据的表述原则(**本族的方法论立场**) + +- 一切判据表述为「**图 + 输入 → 观测**」,**不得**表述为"运行 N 次结果一致"——后者是**概率证据**,不是判据(承契约 §1.2-3「义务不静默」:判据不得靠采样)。 +- **判据必须非空可测(B-N1 新增,规范性)**:正控与负控都**必须**断言「本族识别出的对象(汇合点 id)出现在报告 / 诊断中」——否则判据在「**根本不识别汇合点**」时**空过为真**(正控全绿、负控不触发),执法静默退化为 no-op 而不被察觉。字段面借 S-D §1.7.1 硬约束 7(形态 / 汇合点 id / 输入边集 / …),本 spec 不另立格式。 +- N5 的"双调度对拍"是对**判据"的实证手段**(用于在实施早期捕获 §2 违规),**不是**验收判据本身。两者不得混同——这条要在实施批的测试设计里写明。 + +--- + +## 7. 不承诺 + +逐条对齐共享契约 §1.3 的三类不可消残留(本族落在哪里): + +1. **错误 specification**:本族证明的是"汇合与调度无关",**不是**"用户的 `race` 声明是合理的"。`race(k)` 的内容(此处允许竞争是否满足需求)是**需求层**的事,语言只保证它被显式化并进报告。⇒ 本族不承诺"程序的所有不确定性都是可接受的"。 +2. **恶意 / 失陷外部实体**:外部输入本身的不确定性(`D_external`)**不由本族消除**。本族承诺的是:外部不确定性**进入图之后**不得被放大为隐式竞争,也不得被悄然隐藏(必须出现在 provenance 链上,§5.2)。恶意来源可以通过 boundary 提供的时序影响**哪些** race 结果出现——本族不证明其结果集之外的任何事情。 +3. **需求本身允许危险行为**:**并发竞争本身可能正是需求**(负载均衡器抢单、超时竞速、取消传播)。本族**不禁止竞争**,只要求它**显式**(`race`/`any` 声明 + 报告 + 可枚举义务)。与 §1.3 的对应:本族做的是"把能力与它的代价同时写进图",而不是禁止能力。 + +**本族另外明确不承诺**: + +- **不承诺"Core 程序无数据竞争"**——那是内存模型面(arena/region 隔离、借用检查)的事。本族只管**可观测语义序**,不管字节级并发访问。两者**不互相蕴含**(隔离的内存上仍可有汇合语义的不确定;反之亦然)。 +- **不承诺 `race` 的结果集在跨机器 / 分布式执行下可枚举**——那是 `D_mapping`(跨机器语义确定性)面,与 S-C / S-D 的衔接点。**语义 ≥ 映射**:映射换而集合语义不变,但**集合的具体元素分布**是映射的事。 +- **不承诺代价模型**(并行是否值得)= S-C。 + +--- + +## 8. 不做项(YAGNI)与开放问题 + +### 8.1 不做项 + +| 不做 | 理由 | +|---|---| +| 运行时确定性检查器 / 强制器 | L8:确定性不可运行时强制;且会退化为 lint(契约 §1.2-2 禁止) | +| `select` 的语法实现、关键字表变更、`recv` 语法 | 本 spec 只定**语义**;语法实施属实施批(§3.4 清单) | +| 调度器改动 | §2 只给"不得引入"的判据;调度器实现属部署配置面 | +| 分布式 / 跨机器汇合的语义 | `D_mapping` 面,另议(§7) | +| `recv` / channel 的语法面重定 | 非汇合语义问题(channel FIFO 只在 §1.2 顺序源白名单里被引用) | +| 成本模型 / 并行粒度启发式 | S-C | +| 修改 `docs/dataflow-design.md` / `project-book.md` / `execution-model.md` | 属实施批(§3.4);本 spec 只列清单 | +| 报告面 CLI / 输出格式 | S-D(本 spec 只给链的形状要求,§5.2) | + +### 8.2 裁决记录(原开放问题 Q1–Q5,2026-09-11 用户裁决,**全部已裁**) + +| # | 问题 | 裁决 | 落点(已回填) | +|---|---|---|---| +| **Q1** | `race` 证明范围:局部 vs 到观测点整条路径 | **局部**(只覆盖汇合点本身);**报告面必须标注「该保证未覆盖观测点」**;升档(到观测点)保留为未来候选——只动义务范围,不返工已有判据 | D10;§1.3 / §1.4 / §5.1-(1) / §5.2-6 | +| **Q2** | 顺序源白名单是否纳入显式序号域(order key) | **不纳入**(D5 守零新语法):白名单**首版即终版** = `state-chain` / `region-order` | §1.2 / L5 | +| **Q3** | 未标注 `select` 的默认形态与窗口长度 | **宽松 + 窗口制(分层)**:W1 = **1 个发布周期**(lenient + 诊断 + 自动 `race(1)`);W2 后强制标注(reject);**不设永久 lenient**;**strict 档 / CI 门槛窗口内即 reject** | D3;§3.3 | +| **Q4** | `race` 胜者身份可否作为显式出参(witness) | **允许**:witness = 图上显式值边(非 π 代理),登记为 `EXPLICIT` 不确定源 + 报告义务;`select` 分支体写法可平迁,`load_balancer` 例保留 | D9;§1.3 / §5.1-(3) | +| **Q5** | `merge_ordered` 保序并行是否纳入搬运面 | **纳入**,前置 = **两道门槛都过**(可判定性 + 确定性判定);lowering 须证「state 边序 = 观测序」 | D21;§4.3 / L7 / N8 | + +> 新增开放问题一律另开小节并标注「待论证」——本表**冻结为已裁记录**,不回收未决项(防止实施批二次发明)。 + +--- + +## 附录 A:与共享契约的逐条对齐(自检表) + +| 契约条款 | 本 spec 的落点 | +|---|---| +| §1.1 四档义务 | §1.1/1.2/1.3/1.4 的"义务落档";§5.1 落档表(**与 S-A §1.9 同源**——前两行 = S-A §2.6 行 1,`ordered` ③ 与可证等价行为本 spec 供给的子情形,行形说明见 §5.1-(1);本文不另立档位);L8(enforce 空白 = 族级 `allow_enforce=false`) | +| S-A §1.9 末条(对本族的硬约束) | §5.1-(5) 逐条承接:`select` 重定(§3.2)+ **永久**排除 `select`/优先级作顺序源(§1.2 否定例、§4.1) | +| §1.1 policy = 数据而非语法 | L1(形态 = 图标注);§3.5(不加关键字);§4.4(只加标注) | +| §1.2-1 属性不升格为 primitive | L1;§3.5 | +| §1.2-2 策略面归库 / 禁止 lint 换皮 | §2.2(判据而非检查器);L8(拒绝运行时检查器充数) | +| §1.2-3 义务不静默消失 | §1.5 决策序(第 6 步 reject);§6.3(判据不得靠采样) | +| §1.2-4 判定统一走引擎 | §1.1(c)、§1.4(i) 走 R2 引擎;§1.4 标注"效应等价判定待论证" | +| §1.2-5 只碰声明式上限 | §2(物理面不被本 spec 规定,只规定"不得泄漏");§8.1(不做调度器) | +| §1.4 不确定性 = 显式请求 | 全文论题;§1.5 证明优先于声明 | +| §1.4 时间三值化 | §1.2 否定例(物理时间不作顺序源);§2.2(调度器不得读时间片计数) | +| §1.3 三类残留 | §7 逐条 | +| §2 S-E 行全部交付项 | 四形态语义 §1;每形态落档 §1;`select` 重定与窗口 §3;正/负控 §6;自动并发接口 §4.3 | + +--- + +## 附录 B:术语速查(本族新增,不覆盖契约术语) + +| 术语 | 定义 | +|---|---| +| **汇合点**(merge point) | **跨执行流 / region 汇合**的 DFNode(L1 收窄判据;非普通二元节点) | +| **到达序** π | 输入令牌的物理到达排列;**不是图对象**,不可命名 | +| **π 代理量** | 任何把物理序升格为可读值的图结构(时钟、自旋计数、原子序号、线程 id、优先级抢占痕迹) | +| **观测** `obs` | 值输出 + 效应 + 控制去向;**不含**到达位置/时刻/序 | +| **顺序源** σ | 图上的显式定序对象:state 边链 / region 迭代序 | +| **结果集** `Obs(M)` | `{ obs(F_M(c,π)) : π ∈ Π }`——race 承诺此集合确定 | +| **隐式竞争** | 未证明 ∧ 未标注的汇合 ⇒ reject | +| **物理⇏语义** | lowering / 调度 / 求值顺序不得把物理序泄漏进 `obs` | + +--- + +## 附录 C:收敛批 v3 记录(2026-09-11,B 线 N1 必修闭环) + +| 项 | 变更 | 落点 | +|---|---|---| +| **① 判据改锚** | 汇合点判据从 **region 身份** 改锚到 **语义**(输入前驱间无依赖路径 ∧ 各自可达不确定源根;+ 迭代 / 定序 / 边界三个分支) | L1(§0.3)· §1.0 `M` 行 · §1.5 步 0 · §4.4 | +| **② 判据非空性** | 正控 P1 与负控 N1 各补**非空断言**(汇合点 id 必须出现在报告 / 诊断——借 S-D §1.7.1 硬约束 7 字段面);§6.3 增「判据必须非空可测」原则 | §6.1 P1 · §6.2 N1 · §6.3 | +| **③ 现状断言改正** | `SG_FLOW` = **预留、未产生**(无 `sg_push` 产生点);`SG_FUNC`/`SG_IF`/`SG_LOOP`/`SG_FOR`/`SG_UNSAFE` 五类**今日均产生**(含 `sg_alloc_push` 路径);`IR_PHI` 零发射(只被消费) | §4.2(原「动态汇合容器(已有)」已改)· L1 的「为何改锚」段 | +| **④ 过捕获补控** | N7 增 `SG_IF` 分支交叉用例(`SG_IF` 覆盖 `[condition, merge)`)+ 迭代内交叉探针 | §6.2 N7 · §4.2 `SG_IF` 行 | +| **⑤ 本地编号** | `D-1…D-8` → **`L1…L8`**(与用户裁决 `★D` 消歧;对照表 + 注销登记见路线图 §4) | 全文(34 处) | +| **⑥ D23 补行** | §4.1 补「与 S-B5 的执法分界(D23)」一行(按变更源身份;交集先 S-B5 保守面、本族不重复报)——B 线 I3 的 S-E 侧遗留闭合 | §4.1 | +| **⑦ 纯度批回填** | ~~加「待纯度批落地后回填」占位~~ **已回填(2026-09-11 Task 4)**:§1.1(a) 改为「前提已满足」+ 残余限定三条(链每函数重置 / opcode 清单完备性 / lazy 冻结);§4.1 三后果逐条闭合/限定;N6 负控按新执法面重述 | §1.1(a) · §4.1 · §6.2-N6 | +| **已闭合** | §1.1(a) 的保守面(「等价于所有调用」)已按实测重述(效应/纯度批 `762bd429`+`c9099d73`+`c7ca4251`;证据 = TODO #27) | — | diff --git a/docs/superpowers/specs/2026-09-11-performance-without-commitment-design.md b/docs/superpowers/specs/2026-09-11-performance-without-commitment-design.md new file mode 100644 index 00000000..b9fe05e9 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-performance-without-commitment-design.md @@ -0,0 +1,297 @@ +# 性能无实现承诺 + 可移植(S-C)设计 + +日期:2026-09-11 +状态:**设计起草(2026-09-11 收敛批 v3)**——核心裁决(§0.2 L1-L7)为设计定稿候选 + 2026-09-11 用户裁决(★D3/★D4/★D5/★D7/★D8 等)已回填;成本模型精度/量纲、启发式阈值等**待论证**(§8 开放问题)。**收敛批 v3 变更**:本地裁决编号统一 `L`(原 `D`,与用户裁决 `★D` 撞号——B 线 N2;对照表见路线图 §4)· §6 首切片**靶对象重定**(循环不变的纯节点/region——B 线 N3)· 成本模型返回面与 S-D 对齐(B 线 N5)· 判据 3 可观测面收窄(B 线 N6)· 清单唯一性口径(B 线 N7)· v7 段位表述精确化(B 线 N8) +性质:性能路线 spec(**非语义路线**)——决策对象是「格 → 编码」的**映射选择**,不是图本体;任何优化决策不得改变程序意义(§0.2 L1/L2)。 +本 spec = 路线图 `2026-09-11-semantic-safety-roadmap.md` S-C 行正文;**共享契约逐字遵守**(遵守声明见 §0.5)。 + +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约 + 清单 + 节奏**——本 spec 的术语与边界基准) +- `docs/superpowers/specs/2026-08-23-hw-map-design.md`(设备层)+ `docs/superpowers/specs/2026-09-05-hardware-interface-table.md`(指令/运行层)——**成本模型的数据来源** +- `docs/superpowers/specs/2026-09-10-x86-instance-design.md`(实例化波 1 已实施;**波 2/3 = 本 spec 的合批对象**) +- `docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md`(R1/R2/R3 波序;**R3 映射侧 = 本 spec 的另一名字**) +- `docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md`(G3 可换性判据 = §3 判据来源) +- `docs/regalloc-cache-mapping.md` + `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`(**同族先例**:分配/放置 = 缓存语义映射实例) +- `TODO.md`:性能自动化家族(2026-08-30 记)+「边界判定图 pass」+「控制流自动惰性」(§4 收纳映射的原始条目) +- S-D:`docs/superpowers/specs/2026-09-11-explain-predict-incremental-design.md`(**已落盘**——本线成本模型的**接口以 S-D §1.7.3/§2.6 为准**:`cost_model_query(kind, subject, mapping_desc) -> { cost, band(必填), model_id, model_ver, assumptions[] }`;§5 只写接口需求,不预设其内部设计) + +--- + +## 0. 范围与裁决 + +### 0.0 论题:Performance without implementation commitment + +现状(各语言的通病):为了性能,程序员不断把机器知识写回源码——SIMD intrinsic、CUDA / NPU 各一套方言、硬件换代重写内核。Core 的立场: + +> 程序意义里不含机器形状(承既有定案:宽度移出语言 / 地址 = 映射 / 表示层归处 = 映射参数)。因此**性能决策 = 映射实例的选择,不是程序的一部分**。 + +关键同构(本 spec 的论题展开):单 CPU 上的 SIMD / cache / NUMA / inlining / layout / parallelization 与异构 placement(GPU / NPU / 加速器)是**同一个问题**——在「图 + 目标描述」上选择事件组合与资源绑定。差别只在目标描述的表内容(HIT 运行条目 / hw-map 设备表):**机制同一,无第二条性能路线**。禁止为 SIMD / CUDA / NPU 各开特判通道(与 §1.2.2「禁止内建特判」同构)。 + +### 0.1 本 spec 的决策面 + +- 决策对象:格 → 编码映射的选择/放置/调度(内联、放置、并行分派、事件选择、布局)。 +- 不修改:值流、state edges、region 语义(图本体)——承「执行/副作用/内存的命令式下限不动」(契约 §1.2.5);先例 = 寄存器分配 = 缓存语义映射实例(已定稿)。 +- 判据基调:合法性可判定、收益启发式;**任何变换的缺失都可接受**(直接形态恒合法)。 + +### 0.2 裁决(设计已定 L1-L7) + +| # | 裁决 | 依据 | +|---|---|---| +| L1 | **机器形状不进程序意义**:源码/图零机器名词(无宽度、无寄存器、无 SIMD 宽度、无设备名);性能变化不构成语义变化 | width-out-of-language / 地址 = 映射 / 定长裁决「表示层归处 = 映射参数」 | +| L2 | **优化 = 映射选择,非图修改**:决策作用于「格 → 编码」;图本体不动 | 契约 §1.2.5;regalloc-cache-mapping 先例 | +| L3 | **合法性可判定、收益启发式**(分离原则):变换分两层——合法性前提(图可判定,unknown = 不选)/ 收益估计(成本模型,可错);启发式失效的唯一后果 = 回落语义直接形态 | 本 spec §2.2 | +| L4 | **优化不得削弱义务**:不得移除/绕过 enforcement;检查消除只允许走义务判定(prove 档),不由成本模型决定;MMIO/volatile 不消除/不重排/不合并 | 与 S-A 的边界;hw-map §2 既有条款 | +| L5 | **成本模型 = 数据 + 可替换组件**:成本事实随表走;成本模型(加权/阈值/搜索)是编译器侧可替换件——不进图语义,也不进表的「放置策略」面 | HIT §3.3「优化不走表」 | +| L6 | **标注 = 开放标注/事实来源,不是新 primitive**:不新建属性轴、不新增语言关键字;显式面仅允许「可选性能提示」(可被任何目标忽略 → 退化合法)。**2026-09-11 用户裁决 ★D5 = 守零新语法**:轴 / 上界 / 局部性标注全走**既有插件标签通道**;参数级档位约束落 **policy 数据表**(不新增声明语法) | 契约 §1.2.1 | +| L7 | **决策与缺席都要有理由**(§5)——「不静默」原则在本线的形态(弱承诺:可解释,非拒绝编译) | 与 S-D 的接口 | + +**待论证清单**(不假装已定):成本模型的精度/量纲/校准方法论;启发式阈值(调度开销 vs 收益);PGO 数据的信任形态;决策记录规模与裁剪。逐条见 §8。(局部性显式提示的语法载体已裁 = 零新语法,★D5——见 §8 O2。) + +### 0.3 合批关系(R3 / x86 实例化波 2-3) + +- **同一件事的两个名字**(承 `2026-09-10-language-surface-narrowing-design.md` §3「R3 与实例化波 2 是同一件事(编码层参数化)两个名字」): + - 本 spec(S-C)= **设计侧**:标注面 / 决策流程 / 成本模型接口 / 目标描述形态 / 判据; + - R3 映射侧(hw-map 宽度显式通道 + 表示提示机制 + 实例参数化)+ x86 实例化**波 2**(序列算法参数化、双入口收敛)+ **波 3**(spill/驱逐能力波)= **实施侧**。 +- 实施节奏:与波 2/3 合批;**本 spec 不另开实现线**。合批判据基准 = 边界 C4(行为等价 / 投影正确性方向——非逐字节复刻,x86 实例 spec §4.3)。 +- HIT M2 复启(全 op 合成 / 表模式收口)与本线共享表数据面,但 M2 的实现归属不变(HIT 线)。 + +### 0.4 范围外(与其它 spec 的分界) + +- S-A:属性轴 / 四档义务 / policy 判定 = **定义性输入**,本 spec 消费不重定义。 +- S-D:可解释/可预测/增量——本 spec 只给出**决策记录接口需求**(§5);渲染/CLI/上界证明归 S-D。 +- R2 前端/类型面、后端三轴结构与内核:不动。 + +### 0.5 契约遵守声明(共享契约 §1 逐条) + +| 契约条款 | 本 spec 的遵守形态 | +|---|---| +| §1.1 术语(7 轴 / 义务四档 / policy / declassify / sink-source) | **不重定义**。本 spec 只使用其中 `effect` 轴与 `policy` 定义;性能侧用词(事实、映射选择、成本事实、决策记录)为工作词,不入术语表、不与该表重叠 | +| §1.2.1 属性不升格为 primitive | L6:显式面恒为可选提示/策略数据 | +| §1.2.2 策略面归库、禁止内建特判 | 性能线同构适用:不为 SIMD/CUDA/NPU 开特判通道,全部走表 + 映射(§0.0) | +| §1.2.3 义务不静默消失 | 正交线;本 spec 不弱化它(L4),并给出性能侧对应物 L7(弱承诺) | +| §1.2.4 判定统一走引擎 | 性能合法性前提 = **图事实判定**(复用既有 pass/事实面),不新建判定体系;成本 = 启发式,不属判定体系;义务判定仍全归 R2 引擎 | +| §1.2.5 声明式上限 + 命令式下限 | 本 spec 决策对象 = 映射层(L2);图本体(state edges / region / 调度语义面)不动 | +| §1.3 三类不可消残留 | §7 逐字引用 + 逐条说明本族落点 | +| **§1.4 确定性轴族「不确定性 = 显式请求」+ 时间三值化** | 本线承接 = **模型非证明**(§2.3 契约第 5 条):成本估计只进 `MODELED` 层,**不得**呈现为 WCET 证明 / 时间上界;`band` 必填;与 S-D 结构层 `PROVED` 三层不可混报(§2.3 契约第 6 条;S-D §2.1/§1.7.2) | + +--- + +## 1. 标注面 + +**总原则**: + +1. **自动推导优先**——能从图结构/显式边推导的一律不要求用户写(依赖、纯度/效应、使用计数、索引来源)。 +2. **显式仅在意图不可推导处**——精度(apx)/ 资源约束(意图)类;显式面恒为**可选提示或策略数据**:缺失不改变合法性,被忽略只改变性能(= 无承诺的机制保证)。 + +| 面 | 内容 | 来源 | 形态 | 现状锚点 | +|---|---|---|---|---| +| **依赖** dependency | 数据依赖 + 顺序/副作用序(并行合法性的全部输入) | **自动**(图结构) | 值边 + state edges | 已显式(region 化控制流落地)——自动并行的可判定性地基(TODO auto-parallel 条的机制根据) | +| **纯度/效应** purity-effect | 节点/region 的效应集(无效应 = 纯) | **自动**(`effect` 轴 + 显式效应边)——**已取真纯度事实**(2026-09-11 修复;时点约束见 §2.2 C2) | 推导事实 | S-A §1.1 的 `effect` 轴**只引不扩**;state edges 已存在。**前置缺陷已于 `762bd429`+`c9099d73` 修复(2026-09-11 效应/纯度修正批)**:`fi_ispure` = `compute_all_purity`(`checker.cr:2959`)真计算(保守起点 + 不动点 + 递归/SCC/不可解析保守);入链判据 = 单一真源 `purity_op_effect`(`checker.cr:2912`,含 `IR_CALL_EXTERN`/`IR_SPAWN`/`IR_YIELD`/`IR_HOTPATCH_ROUTE`/`IR_DYN_DISPATCH`/`IR_STORE_PTR`/`IR_AWAIT`)⇒ 一切可解析但不纯的调用(`print`/`read_file`/`chan_send`/`sched_go`…)入 state 链;链**每函数重置**(设计不变——`df_replay_state_chain` 链头重置;跨函数不连,调用节点 = 被调者效应的序代理)。修复前(历史,保留备查):`checker.cr:1107`/`:1142` 值恒 1 ⇒ 上述调用皆被标"纯"、不入链 | +| **局部性** locality | 访问模式 / 复用距离 / 步长 / 嵌套深度 | **自动优先**(图 + 索引来源链) | 推导事实 | 「边界判定图 pass」的 idx/slice 来源链是其数据源;**显式提示 = 走既有通道**(插件标签通道,★D5;判据 = 可忽略性)——不新增语法 | +| **精度需求** precision | apx(精度降级)/ dex 精度意图 | **显式(唯一)** | 既有语言标签 | TODO 定案「明确不自动 apx」——编译器猜不了意图 | +| **资源约束** resource | 内存上界 / 并行度上界 / deadline 类 | **显式(策略数据)** 或目标默认 | policy 数据(契约 §1.1:数据而非语法) | S-D 负责「上界可预测」;本 spec 消费为上界约束(只做满足检查,不重做证明)。**deadline 类 = 仅诊断**(2026-09-11 裁决 ★D8):编译期标注 + 报告,不改运行时语义、不承诺时间上界 | + +注: + +- **效应条款(承 ★D7/★D16——四份 spec 共用同一份清单,本 spec 只引用不复制)**:效应 = 「除引擎判定纯者外的一切 `IR_CALL` / `IR_CALL_EXTERN`」;**已于 `762bd429`+`c9099d73` 修复(2026-09-11)后即其字面定义**——「引擎判定纯者」= `compute_all_purity` 的真值(保守方向:证不纯者恒入清单)。**修复前等价于「所有调用」**(历史快照)。该清单**全仓只允许存在一份**(权威面挂 S-A policy 面 / HIT 投影,安全族收敛定)——**「唯一」指枚举清单**(哪些 API / 事件属外部效应 / 变更源);**公式(上式)在三处逐字引用不算双源**(三处 = S-A §1.7 / 本处 / S-E §1.1,B 线 N7;权威定义仍以 S-A §1.7 为准)。**链覆盖的 opcode 级清单另一份唯一 = `purity_op_effect`(`checker.cr:2912`)**——两清单分工:本清单 = 「哪些 API/事件属外部效应」(policy 面);opcode 清单 = 「哪些 IR opcode 入链」(机械面,新增 IR opcode 必须同步)。§2.2 门禁与 §6 切片按真纯度事实判定(下)。 +- **回填记录(2026-09-11,Task 4 Step 4 = 原「待纯度批落地后回填」marker 的落点)**:修复实测(详证据 = TODO #27):回归 38/38 rc=0 · 语料级旧前端 vs 当前 ELF 逐字节 20/20 同 · canary `95084e7b…d475` IDENTICAL · 自举稳定 · `.ccr` 差异 = 仅 chain(kind=1)边。**§2.2 门禁与 §6 切片的保守面按此放宽**(见该两处的随动修订)。 + +- 局部性/表示类显式提示(如内联容量存储)承 R1 裁决「对非经典范式可退化」——**提示必须是可忽略的**(L6 的落地形态);载体 = **既有插件标签通道**(★D5 守零新语法;不建新语法载体)。 +- 精度轴**只读不写**:优化器不得替用户降精度(不自动 apx);apx 一旦显式给出,本线可消费它做映射选择(如选宽事件/近似事件)——这是「标注 → 映射」的正向消费,反向不许。 +- 事实推导的输出面 = 事实集合(效应集 / 使用计数 / 依赖闭包 / 索引来源 / 生命周期),供 §2 候选门消费。 + +--- + +## 2. 优化器与决策 + +### 2.1 流程(六段) + +``` +① 事实推导(图 pass) 依赖闭包 / 效应集 / 使用计数 / 索引来源 / 生命周期 +② 候选生成(可判定性门) 每条变换附合法性前提清单;前提不可判 → 候选缺席(保守) +③ 成本评分(成本模型接口 ← 表成本事实 + 资源约束) +④ 选择(策略数据:目标描述 + 阈值/约束) +⑤ 记录(决策记录 → §5) +⑥ 发射(实例层:编码 / 布局 / 调度) +``` + +各段失效语义(全部**有向退化**,无静默改义):① unknown → 按最保守事实(有依赖 / 有效应 / 索引未知);② 前提不成立或不可判 → 不选该变换;③ 成本缺失/未知 → 退化用默认形态;④ 无候选或超约束 → 直接形态;⑤⑥ 与语义无关(记录缺失不影响判定)。 + +### 2.2 可判定性与启发式的分界(本 spec 的承重墙) + +| 层 | 判据 | 性质 | 失效后果 | +|---|---|---|---| +| **合法性** soundness | 图前提:无依赖路径(并行)/ 无效应(memo——**按 §1 单一真源**:含任何效应 opcode 或未证纯/不可解析调用即视为有效应;2026-09-11 修复后可消费真纯度,时点见 §2.2 C2)/ 索引仿射可证(向量化)/ 无递归 + 无逃逸(内联)/ 单消费点(惰性下沉)/ **汇合点确定性(并行类变换:S-E §1.5 决策序——可判定性 + 确定性判定两道门槛,★D21/B 线 I2)** | **可判定**(保守:unknown = 否) | 该变换不选——语义不变 | +| **收益** profitability | 成本估计 + 阈值(调度开销 vs 收益;代码膨胀 vs 调用开销) | **启发式**(可错,待论证) | 只影响性能,绝不影响语义 | + +- 承重属性(已定 L3):**任何变换的缺失都可接受**——直接形态恒合法。这与安全线的「义务不静默消失」正交:那是义务,这是可选性。 +- 要求:候选门的判定输出必须**引用图事实**(哪条边 / 哪个效应 / 哪个索引来源)——为 §5 记录与 S-D 解释提供证据面。 +- **效应门纪律(B 线 C2,2026-09-11 修复后修订)**:纯度事实**已可靠**(`compute_all_purity`,`762bd429`+`c9099d73`;见 §1 回填记录)⇒ 首切片与实施批**可以 `fi_ispure` 旗标 / state 链边为真值**——"无效应"结论的判据 = 「无 store 家族 ∧ 无效应 opcode(§1 单一真源清单)∧ 调用全被真纯度证纯(不可解析调用恒计效应)」。**时点约束(硬性)**:该字段在 IR 生成期仍是 checker 的**冻结乐观默认值**(`ir_gen.cr:1590` 的 lazy 判定有意不解冻)⇒ 消费必须发生在 `compute_all_purity` 写回之后(IR 生成 + `df_state_finalize`);生成期消费 = 读到恒 1(即修复前的老行为)。 + +### 2.3 成本模型接口(接 hw-map) + +**设计已定 = 接口形态;待论证 = 精度与量纲。** + +- 输入 ①(**表成本事实**):事件条目 + 运行时条目(调用/存储/系统边界/宽度形状——HIT §3.3)+ hw-map 设备层事实。事实随表走 → 换表换输入。 +- 输入 ②(**形状上下文**):宽度 / 布局 / 嵌套 / 访问模式(来自 §1 事实)。 +- 输入 ③(**约束**):资源约束(§1)——约束由 S-D 判定/证明为上界;本模型只做**满足检查**。 +- 输出:相对成本估计 + **必填四项**:`band`(不确定带——单点数字给人"精确"错觉 = 过度承诺)/ `model_id` / `model_ver` / `assumptions[]`(接口形态**以 S-D §1.7.3 的 `cost_model_query` 为准**,本 spec 不另立);**不输出**任何语义事实。**注(B 线 N5)**:早稿并列的「约束满足判定」**不在** S-D §1.7.3 的返回面——约束满足检查是**消费侧(优化器 ④ 选择段)的内部动作**,其结论经决策记录的 `choice` / `alternatives` / `evidence` 呈现(§5 R1),不属成本模型返回字段;本 spec 不要求模型返回它。 + +契约六条: + +1. 事实/模型分离:成本事实 = 表条目字段(材料属性);「如何用成本事实决策」= 编译器侧可替换件(HIT §3.3「表只描述材料,不描述放置策略」保持)。 +2. 缺失 = 保守退化,**不猜**。 +3. 相对量纲,不承诺绝对值、不承诺最优(§7)。 +4. 模型可替换:多目标/多范式各一套实例,机制不换。 +5. **模型非证明(硬性,承契约 §1.4;B 线 C1/★D24)**:估计值**不得**呈现为 WCET 证明 / 时间上界——`MODELED` 渲染必须明标「模型估计,非证明」(S-D §1.7.2);`band` 必填;模型不可用(未落地 / 预算耗尽)⇒ 消费方按 `UNKNOWN`,**不得**用"经验常数"兜底成 `MODELED`(S-D §1.7.3 禁令)。 +6. **与上界不混报(B 线 C1/★D24)**:成本估计只进 `MODELED` 层;与 S-D 结构层 `PROVED` 数字(分配次数/在飞任务数/通信量)**不得合并/覆盖/相减**——三层不可混报(S-D §2.1);同层内冲突取保守(更宽界)。`phase_ver` 含 `model_ver` ⇒ **模型换代 ⇒ 相关记录与报告自动失效**(S-D §3.3)。 + +### 2.4 与义务/安全性的交互(硬条款,承 L4) + +- 变换不得消除 enforcement(运行时检查/强制通道),除非按四档义务判定升格为 prove 档(消除流程 = 义务判定通道,非成本模型)。 +- volatile / MMIO:不消除、不重排、不合并(hw-map §2 既有条款在本线重申为约束)。 +- 效应序:state edges 是顺序唯一来源;重排/并行只在无依赖证明下允许——这正是自动并发「非依赖猜测」的机制根据(TODO auto-parallel 条:区别于四十年失败史的根源)。 +- **物理 ⇏ 语义:承接 S-E §2.2 的三个检查面(B 线 I2/★D21)**:本线任何搬运/重排/合并(自动并发 lowering、CSE、LICM、惰性下沉等)须同时满足——① **发射层**:lowering 不得引入序依赖(含把非结合算子降低为序敏感实现);② **调度器**:就绪队列/线程 id 不得成为 tie-break 并影响 `obs`;③ **编译器 pass**:不得改变 merge 点的输入到达集合或引入序依赖——**CSE 合并两个 `race` 点 = 改变 `Obs(M)` 基数 ⇒ 禁止**(除非按 S-E §1.4(i) 已证观测等价)。**可陈述不变量:每个变换都保 `obs`**(S-E §2.2)。 +- **自动并发的两道门槛(承 S-E L7;互引 `2026-09-11-merge-semantics-design.md` §4.3)**:依赖门(可判定性)**与**汇合点确定性门(S-E §1.5 决策序)**两道都过才搬运**;只过依赖门即分派 = 该次分派非法。搬运集合含 `merge_ordered` 保序并行(S-E Q5 已裁:纳入),其 lowering 须证明「state 边序 = 观测序」。 + +--- + +## 3. 目标描述 + +### 3.1 形态(数据,非代码) + +编译器持有通用机制 + 实例参数(x86 实例 spec §0 同构原则): + +``` +目标描述 = 三轴组合(架构 × 格式 × OS) + + 表:语义表(事件签名)+ 投影条目(经典编码 / 通道映射 / 连续类演化描述…) + + 运行时条目(call/ret 机制、地址语义、系统边界、宽度/形状) + + 设备/投影表(hw-map:device id → 物理基址) + + 架构特化参数(machine / reloc 等——波 2 机制) + + 成本事实(§2.3 输入①——**载体 = `.ccr` 新段,随 R2 P4 同波**:段 tag 空间可扩展(`2026-09-09-lattice-ir-v7-format.md:44` 的 `7+` **未固定分配**,既有注明用途 = 驱逐标注段 / 证书段,新段名随实施批定);策略随产物分发,★D4 裁决。B 线 N8) +``` + +- **表是唯一允许出现机器名词的地方**(L1 的载体);图的语义完整性不依赖任何表(换表可重建语义)。 +- 表的三种范式形态(编码 / 接通 / 演化)与「无实现:条目缺失 → 编译期拒绝或 unsafe 外部入口」承 HIT §3.2,不重定义。 + +### 3.2 判据:换目标不改语义(可迁移判据) + +1. **观测等价**:同程序 + 不同目标描述 → 值 / 效应序 / rc / 输出一致(判据形式:双目标运行比对,精确 rc;外部效应按登记比对)。 +2. **源码零改动**:换目标不得要求改源码——**这是「无实现承诺」的操作性判据**(要改源码 = 机器形状漏进程序意义,违反 L1)。 +3. **义务判定不变**:换目标后 S-A 的义务档位/判定结果不变(安全不因目标而弱化)。 +4. **决策自由**:映射选择(内联 / 向量化 / 并行 / 布局 / placement)可完全不同——判据**只约束语义,不约束决策**。 +5. **可移植子集**:只用最小核事件的程序 = 任意表可跑(HIT §4 既有条款,本 spec 承接为可移植判据的基线)。 + +反向(泄漏检测,用 lattice-encoding-boundary G3「可换性」判据):a) 换目标需改源码;b) 决策结果被写回图/源码语义;c) 源/图中出现机器名词——三项任一 = 违反,登记为「泄漏待归一」。 + +--- + +## 4. 与既有性能自动化 TODO 的收纳映射 + +**收纳原则**:TODO 家族不再逐条独立立项——统一为「**事实推导 → 决策 → 目标描述**」一条线;只有实现基础设施与前置验证保持独立。 + +| # | TODO 条目(2026-08-30 记) | 归本 spec | 处置 | 保持独立的部分 | +|---|---|---|---|---| +| 1 | **自动并发** auto-parallel | §1(依赖行)+ §2(决策) | 全归本 spec;起步 = 显式 go 保持 + safe 子集自动并行(TODO 原文);粒度成本模型 = **待论证**。**两道门槛**(★D21/B 线 I2):依赖门 + 汇合点确定性门(S-E §1.5)**都过才搬运**;搬运 = lowering,须过 S-E §2.2 三检查面(§2.4) | 多 M 调度验证(TODO 预存 #1)= 其前置,不在本 spec | +| 2 | **自动记忆化** auto-memo | §1.3(纯度/效应)+ §2 | 全归本 spec;与自动惰性共用判定/下沉基础设施(TODO 原文);**§6 首选切片** | 无 | +| 3 | **PGO 自动剖析** pgo | §2(成本模型**校准通道**) | 剖析数据 = 启发式参数来源;本 spec 只定数据入口 + 信任边界(§7) | 插桩/收集/回灌基础设施单独立项实施 | +| 4 | **自动向量化** auto-vectorize | §1(局部性)+ §2 + §3(SIMD 可用性 = 表条目) | 全归本 spec;前置 = hw-map/HIT 编码层落地(TODO 原文);优先级低(保持) | 编码层本身(hw-map/HIT 线) | +| 5 | **自动内联** auto-inline | §2(决策;PGO 配套) | 全归本 spec;`@inline` 显式保留 = §1 显式提示面(显式优先) | 无新增 | +| 6 | **自动软件流水** auto-pipeline | §2(决策) | 归本 spec;标准技术,优先级低(TODO 原文保留) | 无 | +| 7 | **控制流自动惰性**(待实现特性节) | §1.3 + §2(决策) | 同为「纯度 + 使用计数」基础设施(与 memo 同族——TODO 原文「与自动惰性同机制」);纯 + 单次使用 → 下沉唯一消费点 = 可判定前提的既有范例 | 前置 = TODO 已记 use_count 时序缺陷(判定 pass 须在数据流分析产出之后) | +| 8 | **边界判定图 pass**(bounds-inference) | §1(局部性:idx/slice 来源链)+ §2(候选门:恒安全 → 免检查 / 恒越界 → R002 / 不可判 → 保留检查) | 事实来源被本 spec 消费 | **独立 pass 实施**(与指针三 pass 同级);同时是 S-A 义务「prove 档」的图侧判定基础 | +| 9 | **寄存器分配 = 缓存语义映射实例** | §2(**同族先例**) | 不改——其「判定四条 + 分配算法定案」= §2 合法性/收益分离的既有范例 | 实施在飞(regalloc-backend 线) | +| 10 | **hw-map / HIT(表)** | §3(目标描述的数据面) | 不改——本 spec 是**消费者** | 表实现/范式扩展(M2/M3 线) | +| 11 | 明确不自动 **apx** | §7 / §8 | 不做项(精度 = 显式,TODO 定案) | — | +| 12 | 明确不自动 **数据结构自动选择** | §8 | 不做项(依赖意图,太远——TODO 原文) | — | + +收纳后仍独立的事项,仅四项:**插桩基础设施(PGO)**、**边界判定 pass**、**表实现(hw-map/HIT)**、**多 M 调度验证**(并发前置)。 + +--- + +## 5. 可解释性接口(与 S-D) + +前置说明:S-D spec(`2026-09-11-explain-predict-incremental-design.md`)**已落盘**——本节只写接口需求(本 spec 承诺的**产出侧**),不预设其内部设计;成本模型接口与声明面(`band`/`model_id`/`model_ver`/`assumptions[]`、三层不可混报)**以 S-D §1.7.3 / §2.6 / §2.1 为准**;记录表 schema 的**唯一真源 = S-D §1.3**。 + +- **R1 记录 = 数据**(结构化,非文本日志):**字段集 = S-D §1.3**(`id`(内容寻址)/ `kind` / `subject` / `phase` / `choice` / `cause` / `alternatives` / `evidence` / `determinism`);**本线补充一项** = 变换/模型版本(`model_id` / `model_ver`,经 `evidence` 与 `phase_ver` 承载)。本 spec **不另立 schema**(S-D §4.1 明文禁止双源漂移——本项为其境内实例,B 线 M1/★D27 已合流)。 +- **R2 覆盖双向**:已做决策与**未做决策**都有记录——「为何这里没优化」的三类理由之一(缺前提 / 成本不划算 / 无候选)必有落点。决策不静默(L7)。 +- **R3 稳定引用**:图对象引用用编译器稳定身份(节点/条目 id),可与源码↔图↔产物桥接(S-D「图→源码映射」消费)。 +- **R4 可查询粒度**:按函数 / 图对象 / 决策类型查询;默认输出裁剪策略归 S-D(规模 = 开放问题)。 +- **R5 成本面透明**:记录成本估计与所用表条目——可回答「为什么选 A 不选 B」。 +- **R6 开关与降级**:记录通道有开/关(关态零产物差异,沿用现有调试通道惯例);记录缺失不得影响语义。 + +S-D 消费面(归 S-D):explain 输出契约 / 上界预测(内存/任务/通信/deadline)/ 增量失效面。本 spec 只承诺 R1-R6。 + +--- + +## 6. 最小可实施切片 + 判据 + +**切片设计(一条标注 → 一个决策)**: + +- **事实**:纯度(效应集)——**本批(`762bd429`+`c9099d73`)后可直接消费真纯度**:含任何效应 opcode(§1 单一真源 `purity_op_effect`)或不可解析/未证纯调用的节点/region 一律计效应(保守方向)。**时点约束**(同 §2.2 C2):须在 `compute_all_purity` 写回后消费(IR 生成 + `df_state_finalize`),生成期旗标仍是冻结的乐观默认值。零新语法、零用户可见变化。 +- **决策(2026-09-11 收敛批 v3 重定靶对象,B 线 N3)**:**自动记忆化 safe 子集 = 「循环不变的纯节点/region」**——三条前提:① **在循环体内**且**输入不随迭代变化**(定义在循环外,或由图证明为迭代不变量);② 按真纯度判定为纯(保守方向:含任何效应 opcode 或未证纯/不可解析调用即视为有效应——§1 单一真源 `purity_op_effect`;时点见 §2.2 C2);③ 无分配 / 无 arena 交互 + 单线程。选择理由:**收益面真实存在**(每迭代重复求值 ⇒ 上提至循环外/缓存后**求值次数下降**),且合法性前提仍是最小集(无效应边 + 不变性),与自动惰性共用基础设施(TODO 原文的机制重叠);**不接线成本模型**——收益规则用平凡保守启发式,把成本模型留成可替换桩。即:切片先跑通「**合法性可判定**」,把「**收益启发式**」留白(与 §2.2 分界一致)。 +- **为何不用「≥2 次使用」(B 线 N3 实证,硬性)**:① 今日 `IR_LAZY_THUNK` / `IR_LAZY_FORCE`(46/47)在**两条路径都是 eager 直通**(`interp.cr:299-302`「Calls are currently eager, so both wrappers preserve the computed value」;`x86_64/instr.cr:1364-1375` 同义)⇒ **没有"延迟求值"可省**;② 严格 DAG 中一个值的多个消费者**本就只求值一次**(单生产者 + 槽读)⇒「≥2 次使用」的记忆化**无可省收益、判据 3 无见证**(正控会全绿空过)。**备选路线**(把「thunk 真惰性落地」列为切片前置)**不采纳**:理由 = 那会把切片从「一个标注 → 一个决策」变成**执行模型改造**(前置成本远大于收益),且与惰性批(TODO「控制流自动惰性」)重叠——两条路线的收益面不同(惰性 = 避免未使用的求值;本切片 = 避免重复求值),不应互锁。 +- **落点**:新增判定/memo pass(事实读取 + 选择 + §5 记录 stub;时点见前置 1)+ 解释器侧 memo 化消费点(`interp.cr`)。 +- **依赖**:数据流分析的使用计数(§2.1 ① 的事实面)+ **循环归属 / 迭代不变量判定**(region 迭代序 `SG_LOOP` / `SG_FOR` + 输入的 def 位置是否在循环外——B 线 N3 重定靶对象后新增的事实面,仍是"图可判定");S-D1 的记录表 stub(判据 6,建议同批)。 +- **切片前置**(不属切片、但计划须显式列出): + 1. 判定 pass 的时点必须在数据流分析(使用计数)产出之后——TODO 已登记 use_count 时序缺陷(判定 pass 当时读取恒 0;实读点 `ir_gen.cr:1588-1599`(`fi_ispure` :1590 / `g_var_use_count` :1592)vs 数据面 `compute_usage_counts()` `dataflow.cr:405`(调用点 :461);更旧行号 `ir_gen.cr:1106/1108`、`dataflow.cr:325/381` 已漂移,**B 线评审 M3** 校正方向同);本切片在 pass 层消费,规避该缺陷。**注(2026-09-11 效应/纯度批后)**:该批**未动**本前置面(lazy 判定有意冻结 + use_count 时序未修——见 §1 回填记录与 TODO「控制流自动惰性」)⇒ 本项仍是切片前置。 + 2. **求值计数通道不存在**(判据 3 依赖):仓内无任何求值计数设施(`grep eval_count|g_eval` = 0 命中)——切片须自带最小计数(interp-only dev 通道)或退用解释器既有可观测面。 + +**判据**: + +1. **零标注**:源程序不变(无新语法),切片前后源码零改动。 +2. **语义等价**:全回归 + 双路径(interp / ELF)精确 rc + **效应次数不增** + **不变量前提成立**(被记忆化的节点/region:无调用、无效应、**输入不随迭代变化**——三者逐条断言,不靠"看起来纯")。 +3. **生效证据**:决策记录出现记忆化/上提选择(≥1,判据 6 通道)+ **求值规模下降**——判据 3 的**唯一可观测面 = 前置 2 自带的解释器侧最小计数**(interp-only dev 通道);**不采用**「IR 节点数 / 发射指令数下降」作备选(B 线 N6:本切片落点在**解释器侧**,其改写不减少 ELF 路径的指令数——与落点不相容);「生效」必须可观测,不能只看产物没崩。 +4. **退化路径**:事实推导失败 / 使用计数未知 / **不变量不可判(输入随迭代变化)** / 保守条款判为有效应 → 不 memo 且记录理由(无静默)。 +5. **开关**:决策 pass 可关;关态 = 现状行为(产物/rc 与基线一致)。 +6. **记录通道 stub**:§5 R1/R2 的最小记录随切片落地(字段集 = S-D §1.3 + 本线补充;为 S-D 预留消费面)。 + +**切片不做**:成本模型接线(③);并行 / 惰性下沉 / 向量化(后续切片,共用本切片的事实面)。 + +--- + +## 7. 不承诺 + +承共享契约 §1.3(**逐字引用**): + +> - **错误 specification**:Core 只能证明"程序忠实实现了规则",不能证明规则本身正确。 +> - **恶意/失陷外部实体**:供应链、编译机、密钥、硬件后门——用可复现构建/签名/provenance/最小权限**缩小**,不能用语言语义**证明**。 +> - **需求本身允许危险行为**:shell/browser/kernel/JIT 必须执行外来代码——目标不是禁止能力,而是**证明隔离边界**。 + +本族逐条落点: + +- **错误 specification**:优化器不救错算法——「忠实实现规则」包含「忠实地慢」;本线承诺的是「不把机器写回程序」,不是「程序一定快」。 +- **恶意/失陷外部实体**:**表与剖析数据是外部输入**——恶意/损坏的目标描述或 PGO 数据可改变性能甚至改变所选映射;用 provenance / 签名 / 最小权限**缩小**,语言语义**不证明**;信任归治理层(与 M1 定论一致)。 +- **需求本身允许危险行为**:unsafe / extern 边界内手写机器相关代码(HIT extern 事件 / unsafe 外部入口)**不被禁止**——本 spec 不管理它,只保证它不污染其它部分的程序意义(机器相关区域须显式留在边界内)。 + +本线自有不承诺(成本模型 / 启发式): + +- **模型非证明(承契约 §1.4,硬性)**——成本估计是启发式,**不得**呈现为 WCET 证明 / 时间上界;`MODELED` 数字必带 `band` + `model_id`/`model_ver` + `assumptions[]`(§2.3 契约第 5 条),且与 `PROVED` 层不混报(第 6 条);模型不可用 ⇒ `UNKNOWN`,不得用经验常数兜底。 +- **成本模型不保证最优**——选择 = 启发式;最优性证书 = 远期独立项,不在本 spec。 +- **启发式可退化**——退化 = 回落语义直接形态;不承诺任何程序变快,不承诺优化一定发生。 +- **不承诺性能可移植**——语义可移植(§3.2)是承诺;不同目标的性能表现可完全不同(决策自由是其推论)。 +- **不承诺自动推断意图**——精度(apx)等意图类标注永远显式;推断不出的局部性事实不猜。 + +--- + +## 8. 不做项 + 开放问题 + +**不做项**(YAGNI / 已裁): + +- 自动 apx(精度意图——编译器猜不了,显式标签保留——TODO 定案); +- 数据结构自动选择(依赖意图,太远——TODO 定案); +- 最优性证书 / 性能证明(远期独立项); +- 非经典范式的成本模型实例(本 spec 只给接口形态;经典实例先行,范式扩展随 M3); +- 手动性能标注的**语言化**(不新增关键字/属性轴——L6); +- 对 unsafe / extern 内手写机器代码的管理(§7)。 + +**开放问题**: + +1. **成本事实的载体与量纲**:**载体已裁(★D4)**——随 R2 P4 同波进 `.ccr` 新段(段 tag 空间可扩展:v7 `7+` 未固定分配,`2026-09-09-lattice-ir-v7-format.md:44`;策略随产物分发),不另立侧车双源(S-D §4.1);**遗留 = 量纲表达**(相对权重 vs 绝对量)的实测与校准(B 线 T4 / A 线 A1 相交)。 +2. **局部性/表示提示的语法载体**:**已裁(★D5:守零新语法)**——显式提示若需要,只走既有插件标签通道(不新增关键字 / 属性轴),判据 = 可忽略性(L6)。遗留:既有通道覆盖不足的具体场景须先举证。 +3. **并发安全子集的边界**:alloc 下沉 / arena 归属约束(TODO 惰性条已列约束)+ 多 M 未验证(TODO 预存 #1)——auto-parallel 的真实前置面;**另加汇合点确定性门**(★D21;§2.4 的两道门槛)。 +4. **PGO 数据的信任与可复现**:数据 provenance / 签名形态;跨机器剖析数据的有效性。 +5. **成本模型校准方法论**:基准套件;与 S-D 上界的关系——**冲突优先级已裁(★D24)**:三层不可混报(S-D §2.1),估计不覆盖证明、同层取保守(§2.3 契约第 6 条);遗留 = 校准方法、`band` 口径与启发式阈值(与 S-D O10 对齐;T4/T5)。 +6. **决策记录的规模与裁剪**:默认输出裁剪策略归 S-D(§5 R4 已答);遗留 = **规模上界的实测**(**B 线评审 M2** 修法的落地项)。 +7. **优化与义务判定的接口**:prove 档升格后消除 enforcement 的流程归属(§2.4 硬条款的实施细节)。 +8. **「同一问题」的最终验收形态**:单 CPU 优化与异构 placement 用同一机制表达的首个端到端实例(何时/哪个范式实例——如 M3 第二张表)。 diff --git a/docs/superpowers/specs/2026-09-11-policy-authorization-design.md b/docs/superpowers/specs/2026-09-11-policy-authorization-design.md new file mode 100644 index 00000000..f9005888 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-policy-authorization-design.md @@ -0,0 +1,356 @@ +# S-B2 授权族设计:`Can(a,o,r) ⟺ ∃合法 capability path` + +日期:2026-09-11 +状态:**设计起草(2026-09-11 收敛批 v3)**(S-B 族第 2 份;受路线图共享契约约束,未定稿项逐节标注;**A 簇裁决 D2(a)/D3(c)/D13/D14/D22/D29/D30 已随动**,见 §0.3/§0.4/§1.3/§2.1/§2.3/§5.2/§5.3/§8.2;**v3 = A 线 ⚠️3 拆句(§0.3 两类失败分栏)+ A 线 N3 的 `integrity` 不消费行(§3.2)+ A 线 N4 状态行更新(关联栏)**;编号对照表见路线图 §4) +性质:策略族(S-B2)设计 spec——把「越权访问」(broken access control / CWE-284/285/639/862)从业务布尔判断变成**图上不存在合法 authority path**。 + +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**——§1.1 术语 / §1.2 原则 / §1.3 三类不可消残留,本文逐字遵守,不重定义) +- `docs/memory-model-capability-lattice.md` v4(**本族关键前身**:能力成分 / 能力树 / PCM × 布尔格 / **M1 定论**) +- `docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md`(S-A:**本族依赖其四档义务判定流程与义务 API 形态**——**已收敛(2026-09-11 收敛批 v2/v3)**,本文按其 §2.2 / §2.2 附 / §3.1 / §3.3 / §3.6 / §5.2 回填,见 §8.2-7;**A 线 N4 已修**:旧文「并行起草中」作废) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2:双轴接口注册表 + 语义子类型引擎——**判定器**) +- `docs/pointer-model.md`(provenance / `asp` / unsafe 边界)· `docs/memory-model.md`(条目身份 / state edges)· `specs/2026-09-05-hardware-interface-table.md`(效果事件 / 副作用类 / 例外入口) + +--- + +## 0. 范围与裁决 + +### 0.1 论题 + +**授权的语义不是「运行时问一句能不能」,而是图上一条路径是否存在。** 判定式(路线图 §2 S-B2 定义;**带状态版本参数**,2026-09-11 裁决 D13): + +``` +Can(a, o, r, v) ⟺ ∃π : a ⇝ (o, r),π 合法 ∧ v 与路径边的版本见证一致 +``` + +`a` = 主体(subject)· `o` = 对象(object)· `r` = 权限(right)· `v` = **状态版本见证**(D13;κ 粒度 = **对象身份 + 版本见证**)· `π` = capability path。 + +与传统做法的差别是**量的差别变成质的差别**:`if (!has_permission(...)) return error;` 的失败分支是一条**没人写的代码路径**(漏写 = 越权);`∃π` 的失败是一个**类型/图形状事实**(写不出越权,因为该 effect 生成不出来)。本族的全部工作就是让后者成立。 + +### 0.2 与 capability-lattice v4 的 M1 定论对齐(**裁决:不改变,本族是 M1 的兑现**) + +M1 定论(v4 §六):**语义保鲜线不需要「能力」独立概念**;身份 = 图节点身份(已存在)· 无配方 = 标注(已存在)· **授权 = 治理层「可能关系」标注**;能力**不提升一等公民**(可逆、暂不执行)。 + +**本 spec 不申请修订 M1。** 理由逐条: + +| M1 命题 | 本族的落法 | 是否构成「提升」 | +|---|---|---| +| 身份 = 图节点身份 | 主体 `a` = 图节点身份(哪个子图/函数发起)· 对象 `o` = 条目标识 `(产生节点, 输入边)`(memory-model 条款 1,已存在) | 否——零新概念 | +| 授权归一治理层标注 | 授权边 `E_grant` = 图上「可能关系」标注(glossary §六:`A ──可能执行──> B`),与 FUZZY/非确定统一 | 否——统一标注空间加标签 | +| 不提升一等公民 | `Can` 是**导出判定**(§2),不是新的类型/原语;判定走 R2 引擎,不建第二判定体系(契约 §1.2 原则 4) | 否——判定复用 | +| 「可逆」 | §0.4 给出**修订触发条件**(精确判据),当前未触发 | 否——判据在案 | + +**本族对 M1 的两点增量(补充,非修订):** + +1. **不对称性的推广**:M1 说「标注可长,原语不可长」(v4 §六 架构不对称性)。本族把它推广到治理层——**策略可长(数据),判定器不可长(固定为集合包含 + 图上可达性)**。新增安全模型 = 新增授权边数据;判定算法一字不改。这是契约 §1.1 `policy` 定义(数据而非语法)在授权族的具体兑现。 +2. **M1 的证成(而非只是裁决)**:M1 断言「授权不是语义」。本族给出它**为什么不是**——因为 capability path 的语义半边(引用能否到达、生命周期是否包含)**已经在图上了**(provenance / RegionCheck / 借用逃逸,`pointer-model.md`)。缺的只有治理半边。**有引用 ≠ 有授权**;语义层给「能不能到」,治理层给「许不许」。两半分开,正是 M1 的切分点。若把治理半边塞进语义层,就等于让「谁被允许」污染「这程序算什么」——语义保鲜线立刻失守。 + +### 0.3 `unsafe` 与治理层的关系(本族最重要的一条边界裁决) + +`unsafe` 是**例外入口**(`pointer-model.md:205-221`:provenance 断点;capability-lattice §十:`unsafe / alloc_at | 例外入口授权 = 能力创建点`)。裁决: + +> **`unsafe` 消解语义层义务,不消解治理层授权义务。** + +- 在 `unsafe` 块内,provenance 不可追踪 → capability path 的语义半边**不可证** → 该 effect 落 **proof-required 或 reject**(§3.3),**绝不落 prove**。**分类(2026-09-11 收敛批 v3 按 A 线 ⚠️3 拆句)**:这是 **`unknown` 面**——「**不可判定 ≠ 不合法**」(S-A §2.2 点 2)⇒ 走 ③/④,**不得写成「违法」**。 +- 治理层授权义务**独立于 unsafe 继续存在**(**第二条,与上一条分属不同档**):`unsafe` 里读同一对象仍需治理层声明覆盖,否则**硬编译错 = 违法**——缺省拒绝下 `¬Can` **确定成立**,是**判定结论为假**(属"违法",**不属于义务梯档**),不是 ④「无法处置」(S-A §2.2 点 1 分栏,§3.3 表)——出路 = 显式治理层声明(下行)。理由:若 unsafe 能顺带免掉授权检查,`unsafe` 就成了**权限提升后门**——这正是 broken access control 的经典形态,本族存在的意义就是堵它。 +- 唯一合法通道 = **显式治理层声明**(grant 边 / 例外条目),且**派生源必须可追踪**(capability-lattice §七:派生源 = 例外入口授权可追踪,信任链)。这是路线图 §1.1 `declassify` 在授权族的同构对应物:`¬Can → Can` 的唯一合法通道 = 带派生源的显式声明,不是 `unsafe`、不是运行时绕过。 +- **收口到统一 schema(2026-09-11 裁决 D2(a))**:本族条目 = **S-A §3.1「放行 / 声明条目」** 的 `kind = grant|deny`,字段 = 主体/身份 · 目标/范围(`(o,r)`)· 理由 · **派生源** · 义务引用;**必须落台账**(S-A §5.2b)。**形态 = 声明型边集合(`E_grant`/`E_deny`),显式豁免节点形态**——理由:授权归**治理层数据面**(M1),它是**标注/声明**而非「变换一个值」的图节点(与 `declassify` 的语义角色不同);豁免理由 + 审计字段二者缺一即**拒装**(S-A §3.6)。 + +### 0.4 M1 修订触发条件(判据在案,当前未触发) + +按 capability-lattice §十二 #11 判据形态(「图 + 格 + 映射规则无法自然表达」),**若**发生下列任一,本族**申请修订 M1**,并把「能力」提升为一等公民: + +| # | 触发条件 | 当前状态 | +|---|---|---| +| C1 | enforcement 需要运行期**持有的不透明能力对象**(而非图边界一次检查)——如 seL4 式 CSpace 走查 | 未触发:enforce 档复用图边界检查形态(capability-lattice §十:`IR_BOUNDS_CHECK` = 图边界授权检查 = 运行期唯一触点),无新运行时对象 | +| C2 | 撤销(revocation)必须**运行期强制**,且无法表达为图上状态版本 | 未触发:按 v4 §11,Core 参照 seL4 的**结构**不参照其**运行时机制**;撤销落 S-B5 状态版本族(编译期) | +| C3 | 策略表达力需求超出图上正存在边 + 显式 deny(如需要运行期动态策略注入对象) | 未触发:超片段需求按 §3.3 落 proof-required/reject,不扩判定器 | + +C1/C2 的**代价必须写明**(诚实账):不提升 = **无法表达「运行期授予且运行期撤销的能力」**。图是编译期的;`grant` 发生在运行时的场景(能力在程序运行中由外部授予)**不在本族可证范围**——它落 §3.3 的 proof-required 档,由治理层外部机制承担。这条代价不做隐藏。 + +**代价的接受(2026-09-11 裁决 D14)**:**接受**该代价(能力不提升一等公民),代价条款同时写进**路线图 §2 的 S-B2 行**。**延伸路径**(未来若确需运行期授予 / 撤销)= **D13 的能力对象 + 版本见证**(`Can(a,o,r,v)`,§1.3)——「撤销 = 版本推进 ⇒ 旧版本见证失效」,在语言面可表达,不需回溯修订 M1。触发条件仍以 §0.4 的 C1/C2/C3 为判据(当前全未触发)。 + +--- + +## 1. capability path 的形式化 + +### 1.1 路径的三条合取条件(**设计定稿**) + +``` +合法 π ⟺ SemOK(π) ∧ SrcOK(π) ∧ ¬Deny(π) +``` + +| 条件 | 含义 | 来源(**全是既有机制**) | +|---|---|---| +| `SemOK(π)` | 引用**真的能到**:provenance 不断链 + 生命周期包含 + 区域组合合法 | provenance 边(`pointer-model.md`)· RegionCheck(区域逃逸)· 借用逃逸 pass | +| `SrcOK(π)` | 治理层**允许**:路径每条边的授权标注沿路径**单调收窄**(⊑ 细化,不得提权) | 治理层 `E_grant` 标注(**本族新增数据**,非新机制) | +| `¬Deny(π)` | 无显式拒绝边覆盖 | 治理层 `E_deny` 标注 | + +**为什么必须是三条而不是一条**:`SemOK` 是语义层的(M1 已在图上),`SrcOK`/`Deny` 是治理层的(M1 说归治理层)。缺 `SemOK` 的失败 = 「你根本到不了这东西」(UAF/逃逸类);缺 `SrcOK` 的失败 = 「你能到,但你不许」——**后者才是本族**。诊断为二者给出**不同断点**(§2.5),这是「越权」与「野指针」不被混为一谈的形式保证。 + +### 1.2 节点与边(**设计定稿**;内部布局实施批定) + +节点(**全部复用已有身份,零新节点种类**): + +| 角色 | 表示 | 依据 | +|---|---|---| +| 主体 `a` | 图节点身份(子图/函数/`go` 实例)——治理层可绑定角色符号 | M1:身份 = 图节点身份;capability-lattice §七 身份符号 | +| 对象 `o` | 条目标识 = `(产生节点, 输入边)` | memory-model 条款 1——**对象身份本来就在图上** | +| 权限 `r` | 授权集元素(幂集格元素);见 §5.3 枚举 | capability-lattice §九 授权分量 = 布尔格 | + +边(四类,**全部是标注,不是原语**——M1 不对称性): + +| 边类 | 语义 | 复用 | +|---|---|---| +| `E_ref` | 值流/引用边:`a` 持有指向 `o` 的引用 | provenance 边(PointerAnalysis 输出) | +| `E_derive` | 能力树派生:mint / copy(沿树) | capability-lattice §八 能力树;"能力流分析 = PointerAnalysis 推广" | +| `E_delegate` | 传递:`a` 把 `(o,r)` 交给 `b` | 同上 | +| `E_grant` / `E_deny` | 治理层显式授权 / 拒绝声明(**数据**) | 契约 §1.1 `policy`;glossary「可能关系」 | + +**合法边约束 = Kain 约束**(capability-lattice §六:「细化 ⊑ 必须可强制」):沿路径授权集**只许收窄**。这条约束是把「能力派生」与「权限提升漏洞」分开的唯一机制——`copy` 可复制不降权(可复制性,v4 §九),`mint` 必须收窄。 + +### 1.3 判定式(**设计定稿**) + +``` +W(a,o) ≜ ⋃π∈Paths(a,o) SemOK(π) ? ⋂e∈π auth(e) : ∅ -- 沿路径授权交集,跨路径取并 +Can(a,o,r,v) ⟺ r ∈ W(a,o) ∧ ¬DenyCovered(a,o,r) ∧ v 一致(版本见证) +``` + +- **状态版本参数(2026-09-11 裁决 D13)**:`Can` **带状态版本参数** `v`;**κ 粒度 = 对象身份 + 版本见证**(不是裸对象身份)。版本载体 = **S-B5 的 ENT `(var_id, version)`**(v7 既有,**不引入第二套版本机制**)。⇒ 「撤销」在语言面**可表达**(撤销 = 版本推进 ⇒ 旧版本见证失效),与 S-B5 的 check/use 判定同源。§8.2-3 随之关闭。 + +- **路径内取交(⋂ = 幂集格 meet)**:每条边都必须允许。 +- **路径间取并(⋃ = 幂集格 join)**:**存在**一条合法路径即可。 +- 这正是 capability-lattice §九 代数(授权分量 = 幂集格,`∩`/`∪` = meet/join)与保序积总序 `(r₁,a₁) ⊑ (r₂,a₂) ⟺ r₁ ⊑ r₂ ∧ a₁ ⊆ a₂` 的直接消费。 +- **可判定性(好消息)**:`W(a,o)` 是 **格上最宽路径(widest / bottleneck path)问题**。因为授权分量是**分配格**(幂集格 ⇒ 分配律成立),最宽路径良定义且**多项式时间可解**(Dijkstra 变体 / 有限主体集上迭代到不动点 = Datalog 半朴素求值)。递归能力树成环**不破坏可判定性**(格上可达性对环封闭;与 R2 引擎 μ 展开的余归纳 memo 同族处理)。 +- **与 state edges 的接口(正交,**裁决:不耦合**)**:state edges 是「先发生」顺序约束(命令式下限,定案 14 不动);capability path 是**存在性**,不是顺序——两者正交,判定**不消费顺序**。唯一的表面交叉 = **撤销**:撤销前后的能力不同,是**时间**概念 → 归 **S-B5 状态版本族**(图上两个版本),本族**只消费其版本见证**(D13:`Can(…,v)`),不定义版本机制。 +- **与 provenance 的接口(`asp`)**:外部地址空间指针(`asp=1`,`pointer-model.md:221`)**没有 Core provenance** → 其 `SemOK` 恒假 → capability path 必然经例外入口 → 落 §0.3 的 unsafe 裁决。这条把「外部来源引用」自动排除出可证子集,无需特判。 + +--- + +## 2. 判定:`Can` 如何落到引擎 + +### 2.1 两步分离(**设计定稿**——契约 §1.2 原则 4 的兑现) + +| 步 | 做什么 | 归属 | 先例 | +|---|---|---|---| +| **① 导出** | 从 `G + P` 算出 `Cap(o,r)` = 「能对 `o` 行使 `r` 的主体集合」(图上的闭包)**+ 派生链见证** | **图验证 pass**(与 PointerAnalysis / RegionCheck / ProvenanceVerify 同级,定案 1/4)——**「沿 def 链倒推」图面分析的唯一 owner(2026-09-11 裁决 D22)** | 图上稀疏分析 | +| **② 判定** | `Can(a,o,r,v) ≜ ty_sub(τ_subject(a), Cap(o,r))`(+ 版本见证一致,§1.3) | **R2 引擎**(`ty_sub`) | `type-interface-unification-design.md` §3.2 | + +- **唯一 owner 声明(D22,规范性)**:图上「**沿 def 链倒推**」的分析(capability path / 派生链合法性 / 根封闭 / handle 身份溯源)**只在本步实现一次**——**输入** = provenance / RegionCheck / ProvenanceVerify 的**既有结果**(**不新写分析**),**输出** = `Cap` + **派生链见证**(供诊断与下游消费),**`g_*` 表只读**。**S-B4**(其 Q4:派生链合法性落点)与 **S-B5**(其 §3.2:κ 可靠性)**消费本步输出,不各自实现**;三份 spec 的回引已就位(S-B4 §7.2 Q4、S-B5 §3.2)。§6.3 的侦查项据此收窄为「**复用既有 pass 结果是否真的零新分析**」。 +- `τ_subject(a)` = 「主体 `a`」的类型项(**记号更名**:旧稿用 `A_a`,与 S-A §3.3 的**轴取值类型项** `A_a(v)` 记号冲突 —— §3.3 单式语境下同现会读错,故统一改 `τ_subject(a)`;A 线评审 M3)。 +- `Cap(o,r)` = 一个**形状类型项**(主体全集上的子集)。 +- **零新判定规则**:`Can` 不是新的判定体系,是引擎上的一次**包含判定**——与横切接口「满足判定 = 一条包含判定 `T <: 形状`」(R2 §2.2)**完全同构**。「能访问 `(o,r)` 的主体」就是一个形状。 +- 这是 M1「不提升」在判定面的硬证据:**授权不新增判定器,只是引擎的一个新形状**。 + +### 2.2 可判定子集与边界(**设计定稿**;边界项逐条给出落档) + +可判定 ⟺ 三条同时成立:① 图有限(现状恒真)② **主体与对象身份在图上有名** ③ 策略在正片段(正存在边 + 显式 deny)。 + +| 边界 | 处置(按 S-A 四档) | +|---|---| +| 主体身份是**运行时值**(如外部读入的 uid)→ 符号化主体 | 判定到「符号化主体」即停 → **enforce**(图边界一次检查,§3.3) | +| 对象身份是**运行时计算**(如拼接的 pathname) | **不可判定** → 修复面在 S-B4(围栏族:`Path` 让对象可命名);本族据实落 proof-required | +| 策略含**否定以外的表达力**(计数/聚合/时态) | 超片段 → **proof-required**(要求用户提供 contract)或 reject;**不扩判定器**(§0.2 增量 1) | +| 路径枚举**预算超限** | **显式诊断「判定预算耗尽」**——**绝不与「无权限」混淆**(R2 §3.3 同款守卫;契约 §1.2 原则 3 不静默消失) | + +- **预算超限 vs 无权限** 的区分是本族**必须**做对的判定卫生:前者是工具的能力边界,后者是程序的事实。混淆两者 = 误报 = 工具被关掉 = 全盘失效。 +- **默认强度分层(2026-09-11 裁决 D3(c);规范形态见 S-A §2.2 附)**:② **enforce 默认启用**(可 enforce 一律 enforce);无 enforcement 点 ⇒ **③ 登记 + 继续编译**(默认档**不拒编**);**高安全档**关闭 enforce 通道 ⇒ ④;**未声明的边界条目取 `unknown`**(保守判定方向)+ 报告面可见。**CI 门槛 = 严**(strict 档运行,台账非空即失败)。 + +### 2.3 deny 语义(**设计定稿**) + +- **两阶段**:先求 `W(a,o)`(正片段闭包),再查 `E_deny` 覆盖 → **deny 优先**(默认拒绝底色的显式化)。 +- **冲突消解 —— 已裁(D30,2026-09-11):deny 一律优先**。理由:① 「显式拒绝不得被任何 grant 覆盖」= 保守且**可判定**(一次集合查询);② 「最近优先」把**路径次序**引入判定,与 §1.3「存在性判定」的形态冲突(存在性不带序);③ 显式优先级序 = 引入策略语言(违反 D5 零新语法)。§8 开放 4 随之关闭。 + +### 2.4 增量与缓存(**待论证**,依赖 S-D) + +`Cap` 表的失效面 = `ΔG`(图变化)∪ `ΔP`(策略变化)。**2026-09-11 裁决(D29)**:增量 / 报告接口(`Cap` 表失效面、竞态报告链格式、台账格式)**归 S-D 线统一**——本族只写**消费点**,**不设计缓存键、不写格式**。 + +### 2.5 诊断(**设计定稿**) + +失败输出 = **非空反例路径**(同 R2 `ty_witness` 精神:给具体值,不给「不匹配」): + +``` +拒绝:主体 f 对 (o, write) 无合法 authority path + 断点:SrcOK —— 路径 f → g → o 上边 g→o 无 write 授权标注 + 候选:f → h → o 存在 write,但 SemOK 失败(h 的引用已逃逸区域 R) +``` + +- **必须指到断在哪条边、断在 `SemOK` 还是 `SrcOK`**——用户才能分辨「改授权声明」与「改引用结构」。 +- 位置与图→源码映射走 `diag.cr` + LSP 基建(R2 §7 同款)。 + +--- + +## 3. effect 轴耦合 + +### 3.1 哪些 operation 算 effectful(**设计定稿**——不在本族发明,查权威来源) + +effectful op 的定义**不新造**,由既有两个权威面给出: + +1. **HIT 事件条目的「副作用类」字段**(`specs/2026-09-05-hardware-interface-table.md` §3.1:`纯 | effect | volatile`)——**这是运行层的权威声明**。`effect`/`volatile` 类事件 = effectful。最小核里即 `load`(读世界)/ `store`(写世界)(HIT §2)。 +2. **图上 `effect` 轴标注**(路线图 §1.1 七轴之一,开放标注)——声明式上限侧,任何节点可加。 + +两者关系:**图中标注 = 上限(用户/库声明什么算 effectful);HIT 副作用类 = 下限(编码层事实)**。二者不一致 = 编译期诊断(契约 §1.2 原则 3:不静默)。 + +### 3.2 义务(**设计定稿**) + +> **effectful operation 须产出 `⊢ Can(a,o,r)`,否则不能生成该 effect。** + +- **判定在图 pass 完成(声明式上限),义务在 effect 生成点被消费(命令式下限)**——这正是定案 14「两者共用一张图」在授权族的具体形态。 +- 消费点 = **effect 生成点**(IR 合成层 / 事件发射)——比 checker 更低一层。此处**不重新判定**,只**消费**已验证的证明(`g_cap_*` 标注 / gate 标记,`ast.cr` 惯例的 kind 常量,实施批定)。**禁止**在发射层做策略特判(契约 §1.2 原则 2:不把某类漏洞做成内建特判)。 +- 「不能生成该 effect」的落点 = **该 IR op / 该事件发射被拒绝**,不是「生成后再检查」。 +- **本族是否消费 `integrity`(A 线 N3)**:**否**——本族的轴耦合面 = **`effect × authority`**(§3.1 权威面 + S-A §1.8);`integrity`(S-A §1.3 = 可篡改性)在本族**不单列判定面、不承接**,其消费在 **S-B5**(以 **`state-version × integrity`** 形式——S-A §1.3/§1.8 已按此回引)。本行是「轴归属不得单向声明」的落地(A 线 N3 修法)。 + +### 3.3 无证明时的落档(**设计定稿**) + +| 档 | 触发条件 | 本族形态 | 成本 | +|---|---|---|---| +| **prove** | 主体身份 + 对象身份 + 路径全编译期可定,`ty_sub` 通过 | 零运行时残留 | **零**(能力 = 编译期图数据,证明完毕不下发——capability-lattice §十一) | +| **enforce** | 主体身份是**图边界值**(运行时),但路径形状编译期已知 | **图边界一次授权检查** | 与已有 `sub+cmp+jae+ud2` 同级(capability-lattice §十:`IR_BOUNDS_CHECK` = 图边界授权检查 = 运行期唯一触点;**编码措辞待统一(A 线评审 ⚠️5:`cmp+jb+ud2` 与 `sub+cmp+jae+ud2` 是同一比较的对偶写法)**) | +| **proof-required** | 策略依赖运行期外部判定(外部 PMS / 动态策略加载 / 运行期授予,§0.4 C3);或**放行本身**(unsafe / 例外入口 / 治理层声明的正确性)需证(S-A §2.2:放行登记为义务) | 要求用户提供 proof/contract(显式声明来源 + 派生源) | 用户负担,**显式** | +| **④ reject** | **高安全档**下不可处置(如路径形状不可判且无 enforce 通道);或策略超片段且未提供 contract | 拒绝编译 + 反例路径 | 零 | +| **违法**(**不属于义务梯档**——判定结论为假) | 治理层**缺省拒绝**且无声明覆盖(`¬Can` 确定成立,§6.1「缺省拒绝」);或 `SemOK` 确定假(**`asp=1`** = 显式外部地址空间、确定无 Core provenance)。**注**:`unsafe` 内 provenance **追踪中断 / 不可判** = `unknown` ⇒ 走 ③/④,**不是**违法(「不可判定 ≠ 不合法」,S-A §2.2 点 2) | **硬编译错**(任何 policy 档位拒绝)+ 反例路径;**出路 = 显式治理层声明**(S-A §3.1 放行/声明条目 `kind=grant`,登记落台账) | 零 | + +**默认档裁决(2026-09-11 裁决 D3(c) 对齐)**:**② enforce 默认启用**(可 enforce 的一律 enforce);无 enforcement 点 ⇒ **③ 登记 + 继续编译**;**高安全档**(用户显式声明)**关闭 enforce 通道** ⇒ ④ reject。理由:契约 §1.2 原则 3 不静默——enforce 是**显式可见**的(产物里有检查、诊断里有登记),不是「反正运行时查了」的无声漏斗。**enforce 必须留下证据**(诊断登记 + 图边界检查点),否则等同于静默通过。 + +--- + +## 4. 与既有机制 + +| 机制 | 接口裁决 | +|---|---| +| **`.cr` unsafe 边界** | §0.3:消解语义义务,**不消解治理义务**;`unsafe` 内 effectful op 落 proof-required/reject,唯一合法通道 = 带派生源的显式治理层声明。**unsafe ≠ 授权后门**。 | +| **extern** | extern = HIT 运行时条目的「系统边界:extern/入口点约定」(HIT §3.3)。extern 调用 = effectful(副作用类声明)+ **例外入口**(`pointer-model.md`: FFI 返回值无 provenance)→ `SemOK` 不可证 → 合法性**只能来自治理层显式声明**(不可从图内 provenance 推出)。HIT 事件条目的「语义锚:指向规约层的可选引用」(HIT §3.1)即该声明的承载面。 | +| **HIT 事件** | ① 副作用类 = effectful 的**运行层权威**(§3.1)② 表即「编译器与平台的能力协商面」(HIT §4)——**投影缺失 = 编译期拒绝**,与授权拒绝同族(都是「生成不出该 effect」)③ 运行时条目(调用资源/存储资源)是 enforce 档运行时检查的承载面。 | +| **go / sched 并发面** | ① `go` 创建新主体 → 新主体能力集 = **`E_derive` 派生**(从 spawner 出),必须沿能力树**单调不增**(Kain 约束)——**不允许隐式共享**;捕获环境 = 显式传递 = 派生边。② **并发 = 多主体同时活动** → 「可能关系」随时间点变化 → 授权判定**必须带状态版本**才严谨;本族**只声明接口不解决**(→ S-B5)。③ 调度是命令式下限(定案 14 不动),授权判定是声明式上限。④ `go` 捕获可变状态的借用面 = 定案 9 唯一欠账(另轮),本族不碰。 | + +--- + +## 5. 治理层接口 + +### 5.1 策略 = 数据(**设计定稿**,契约 §1.1 逐字遵守) + +`policy` = 一组图上约束(`G + P ⊢ Safe` 里的 `P`),**数据而非语法**——新增授权模型不改 Graph 本体、不加关键字。本族消费三个承载面: + +| 面 | 内容 | 定性 | +|---|---|---| +| **(a) `.cr` 内声明式条目** | 主体/角色符号、`E_grant`/`E_deny`、授权集 | **语义源**(进图、可验证、随子图生命周期)。先例 = `alloc_at`(声明即注册,`pointer-model.md:192`);形态 = **库侧声明**(契约 §1.2 原则 2「策略面归库」),**不新增关键字**(D5),复用现有声明形式,实施批定。**本族条目在 S-A §3.1「放行 / 声明条目」栏的形态(D2(a))**:`kind=grant|deny`,主体 = 主体/角色符号,目标/范围 = `(o,r)`,理由 / **派生源**(例外入口授权链,capability-lattice §七) / 义务引用 = 必填;**形态 = 声明型边集合,显式豁免节点形态**(理由见 §0.3);**每条落台账**(S-A §5.2b)。**栏位边界(B 线 N2)**:**能力创建点(根声明 / `alloc_at` 类例外入口)归 `kind=mint`,是 S-B4 的条目**——本族不复用(「授予**既存**对象/权限」≠「创建新域」) | +| **(b) 外部策略数据** | 同一数据的序列化/配置投影 | **投影,非语义源**(定案 1:图是唯一真相层)——与 S-C 序列化面共裁 | +| **(c) HIT 运行时条目** | extern/入口点约定处的授权声明 | **运行面承载**(§4),与 (a) 数据同源 | + +### 5.2 用户面句柄(**开放问题——引用而非裁决**) + +capability-lattice §十三 开放问题 3:「用户面句柄形态细节:树路径 vs 槽位引用(内部表示)」。 + +**本族立场**:判定层**不依赖**句柄内部表示(`W(a,o)` 只需图上可达性,不需要句柄的编码形态),故**本族不裁决其内部编码**。**表示形态**的裁定权(S-B4 §1.4 交给「S-B5 + 映射层」)**已裁(2026-09-11,D30 采纳 S-B5 §3.2 建议):并列类型**——`Handle` 独立于 `Path`,`bind(p) -> Handle` 为显式转换点(= 唯一的 check/use 边界 = S-B5 C1 的判定对象);`bind` 的原子性来源(fd / dirfd / `openat` 类)由**映射层**确认。本族据此消费 handle 身份(`o` 的对象身份可取自 handle)。**诊断与解释的呈现**(§2.5 反例路径怎么给用户看)依赖它 → 登记为**依赖项**(§8 开放 5),不阻塞最小切片。 + +### 5.3 授权集枚举(**承接 capability-lattice 开放 1**) + +capability-lattice §十三 开放 1:「授权集完整枚举:consult/assume/measure/pass 之外还需哪些(execute?deliver?)」——治理层问题。 + +**本族裁决一处**:**`execute` 必须进**。理由:本族的核心消费面是 **effectful op**(§3),而「执行」是 effect 的原型关系;缺 `execute` 则 §3 的义务无对应授权元素,耦合成空。 + +**枚举面的口径(2026-09-11,D30)**:授权集 = **治理层开放词汇表**(数据)——判定层只消费**集合包含**,**不特判元素符号**(新增元素 = 加数据行,判定器一字不改)。最小切片用 `{execute}`(+ 治理层既有 `consult`/`assume`/`measure`/`pass` 词汇);`deliver` 等其余元素的**闭包**留治理层细化(§8 开放 6 关闭为「开放集 + 加数据行」)。 + +### 5.4 gate(**本族新增的治理层接口,最小**) + +effectful op 节点上挂**义务标注**(§3.2 消费点)——**不新增节点种类、不新增关键字**(标注可长,原语不可长)。gate 是「义务的挂点」,不是「判定的挂点」(判定在图 pass)。 + +--- + +## 6. 最小可实施切片 + 判据 + +### 6.1 切片 S-B2-S1(**小**) + +**一句话**:一张声明表 + 一条判定 + 一个 effectful op 的义务 + 正/负用例。**不含**运行时 enforcement、撤销、动态主体、策略语言(全在 §8)。 + +| 项 | 内容 | +|---|---| +| ① 策略条目 | 最小声明式形态:主体符号 + 对象(图节点身份)+ 授权集(含 `execute`)。**无 deny 表达式**,只有 `E_grant` + 缺省拒绝 | +| ② 导出 | `Cap(o,r)` 计算(`SemOK` 部分复用**既有** provenance/RegionCheck 结果,**不新写分析**;`SrcOK` 部分 = 最小闭包) | +| ③ 判定 | `Can(a,o,r,v) ≜ ty_sub(τ_subject(a), Cap(o,r))`(+ 版本见证,§1.3)走 R2 引擎;自测通道 `corec selftest-policy`(同型 R2 P0 先例 `corec selftest-types`) | +| ④ effectful op | **一个**:`store`(HIT 最小核效果事件,副作用类权威明确,**不需要**主体身份运行时面) | +| ⑤ 落档 | **只实现 prove + 拒绝(含「违法」与 ④ 两类,措辞/码分因)两档**(enforce/proof-required = 后续批)——诚实的小切口 | +| ⑥ 判据 | 见 §6.2 | + +### 6.2 判据(**每项可机械验证**) + +1. **引擎层**:`corec selftest-policy` 用例表全绿——正例(prove)/ 负例(reject)/ 缺省拒绝 / **预算超限 → 显式诊断且与「无权限」措辞分因** / 反例路径非空。 +2. **零回归锚**:**未使用策略条目的程序 → 产物 byte-identical**(R1/R2 同款判据;切片不得扰动既有产物)。 +3. **诊断质量**:负例拒绝信息含**断点边**(明确指出断在 `SemOK` 还是 `SrcOK`)——不可只给「无权限」。 +4. **义务消费**:`store` 的 effect 生成点拒绝路径真实生效(负例下该 IR op **不生成**,不是生成后过滤)。 +5. **自举链**:`corec2/corec3` `cmp` IDENTICAL + N06=0 + 冒烟 rc=42。 +6. **全套件**:按 **R2 §4 每步共同判据行**(`2026-09-10-type-interface-unification-design.md:155`:全套件 + `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 rc=42)执行——**套件计数以该行为唯一真源,本文不重述数字**(A 线评审 M1)。 + +### 6.3 设计 vs 待论证(诚实切分) + +- **设计定稿**:§0(M1 对齐 + unsafe 裁决)· §1(三合取 + 判定式)· §2.1/2.2/2.3/2.5 · §3 · §4 · §5.1/5.3/5.4 · §6。 +- **待论证 / 待定**:§2.4(增量缓存,**已裁归 S-D 线**,D29)· §5.2(句柄**表示形态已裁** = 并列类型;**内部编码**仍不裁,D30)· §6.1 的「复用既有 provenance/RegionCheck 结果」是否真的零新分析——**实施批必须先侦查**(若 `SemOK` 无法从既有 pass 直接取,切片 ① 需缩到「只做 `SrcOK`」)。**范围已收窄(D22)**:def 链倒推的**唯一 owner = 本 spec §2.1 步①**(输入 = 既有 pass 结果,输出 = `Cap` + 派生链见证),S-B4 / S-B5 只消费——侦查项仅剩「既有 pass 结果是否够用」,不再有归属歧义。 +- **2026-09-11 收敛随动**:D2(a)(§0.3/§5.1 放行·声明条目收口)· D3(c)(§2.2/§3.3 默认强度)· D13(§0.1/§1.3 `Can(…,v)`)· D14(§0.4 代价接受)· D22(§2.1 owner)· D29(§2.4)· D30(§2.3 deny 优先 / §5.2 并列类型 / §5.3 授权集开放词汇)· A 线 M3(`A_a` → `τ_subject(a)`)· M5 同款(§3.3 **违法 ≠ ④ reject** 分栏)。 + +--- + +## 7. 不承诺(引路线图 §1.3,逐条说明本族落在哪类) + +### 7.1 错误 specification(**本族首当其冲,必须写透**) + +> Core 只能证明「程序忠实实现了规则」,不能证明规则本身正确。 + +**本族不可能消掉这一类,而且它是本族最大的残留。** 硬例子:策略声明 `E_grant(everyone, account, withdraw)`(「人人可提款」)——Core **会证明通过**。判定式 `Can(a,o,r) ⟺ ∃π` 忠实实现了这个规则;规则错了,`∃π` 依然成立。编译器在此**完全无话可说**。 + +本族真实消除的是:**规则与实现的不一致**——即 CWE-284/285/639 族的主力形态(漏写检查、检查顺序错、路径混淆、TOCTOU 窗口)。形式化陈述: + +``` +保证:G ⊢ Can(a,o,r) 与 P 一致(可判定、反例非空) +不保证:P 本身安全 +``` + +**推论(必须对用户说清)**:本族把「越权」的**发生率**大幅压低(漏写检查变成编译错误),但**不把授权变成安全**——它把授权策略从「散落在代码里的判断」提升为「一处可审计、可 diff、可验证一致性的数据」。审计价值是真实的,安全承诺是没有的。 + +### 7.2 恶意 / 失陷外部实体 + +供应链、编译机、密钥、身份提供者被攻陷——本族**不能用语言语义证明**。缩小手段(本族提供的):**派生源可追踪**(信任链,§0.3)+ **最小权限**(本族给最小权限的**判定机制**,不给威胁证明)。`a` 的身份符号**绑定是否正确**(uid 是不是真 uid)在治理层外,本族不管。 + +### 7.3 需求本身允许危险行为 + +shell/browser/kernel/JIT 必须执行外来代码——本族目标是**证明能力边界**(谁被允许做什么),不是禁止能力。`unsafe`/例外入口的存在是**需求**,不是缺陷;本族对它的贡献是让「例外**必须显式且可追踪**」(§0.3),而非消灭例外。 + +--- + +## 8. 不做项 + 开放问题 + +### 8.1 明确不做(YAGNI / 归他族) + +| 不做项 | 归属 | +|---|---| +| 运行时撤销 / 能力失效 | **S-B5** 状态版本族(图上两版本)——本族只**消费版本见证**(`Can(…,v)`,D13),不定义版本机制(§1.3) | +| 对象身份运行时可命名(`Path` 等) | **S-B4** 围栏族——本族据实落 proof-required(§2.2) | +| `Secret ↛ Public` 流纪律 / `declassify` | **S-B3** 信息流族——本族只对齐「唯一合法通道 = 显式声明」(§0.3) | +| 属性轴 / 四档 / policy 接口的**定义** | **S-A**(地基)——本族消费不重定义 | +| 策略表达力超出正片段(计数/聚合/时态/一阶) | 超片段 → proof-required/reject(§2.2);**不扩判定器** | +| 身份认证、密钥、主体身份的可信绑定 | 治理层外(§7.2 供应链残留) | +| 分级 / 分数授权(`π ∈ (0,1]`) | capability-lattice §九 扩展点,当前 YAGNI | +| 并发共享 / 借用欠账(定案 9 唯一欠账) | 另轮(§4 `go` 面只登记接口) | +| 能力提升一等公民 | **M1**(§0.2 不修订;触发条件在案 §0.4) | + +### 8.2 开放问题(**逐条登记,不阻塞切片**) + +1. **主体身份档位表**(编译期常量 / 图边界读入 / 外部 PMS)——**已裁(D30)**:三档 = `{编译期常量 → prove / 图边界读入 → enforce / 外部 PMS → proof-required}`,与 S-A §2.2 附默认强度对齐;**与图边界(`load` 事件)的接线**留实施批(属 S-B 族横切)。 +2. **`Cap(o,r)` 导出表的规模 / 缓存 / 增量失效面**——**已裁(D29)**:归 **S-D 线统一**(§2.4),本族只写消费点。 +3. **`Can` 是否携带状态版本参数**——**已裁(D13)**:**带**(`Can(a,o,r,v)`,κ = 对象身份 + 版本见证),版本载体 = S-B5 的 ENT `(var_id, version)`(§1.3)。 +4. **deny 冲突消解规则**——**已裁(D30)**:**deny 一律优先**(§2.3),不引入最近优先 / 优先级序。 +5. **用户面句柄形态**——**表示形态已裁(D30)**:**并列类型**(`Handle` 独立,`bind` = 显式转换点);**内部编码**(树路径 vs 槽位引用)仍引 capability-lattice §十三 开放 3,本族不裁,登记为诊断呈现的依赖项(§5.2)。 +6. **授权集完整枚举**——**已裁(D30)**:`execute` 必进(§5.3);枚举面 = **治理层开放词汇表**(加数据行,判定器不改),其余元素留治理层细化。 +7. **S-A 接口回填(原硬依赖)** —— **已完成(2026-09-11)**:S-A **收敛批 v2** 已落盘,本 spec 已按其 §2.2 / §2.2 附 / §3.1 / §3.3 / §3.6 / §5.2 回填(义务挂点 = §3.2 消费点;「义务不静默」流程 = S-A §5.1 状态机)。S-A 若再细化,**以 S-A 为准**。 +8. **enforce 档运行时检查的对齐细则**:capability-lattice §十已给形态(图边界一次检查 = `IR_BOUNDS_CHECK` 同族),但「一次检查」在**多主体 / 多对象**下的复合形态(N 次检查还是一张表)属实施面,**登记待定**;**判定形状不变**(每个检查点 = 一次集合包含判定)。 + +--- + +## 9. 自检记录 + +- **共享契约遵守**:§1.1 术语(属性轴/义务/四档/policy/declassify/sink-source)逐字引用未重定义;§1.2 五条原则逐条落在 §0.2(原则 1)/§3.2(原则 2)/§2.2+§3.3(原则 3)/§2.1(原则 4)/§1.3(原则 5);§1.3 三类不可消残留 = §7 三条逐一对应(错误 specification = 7.1,恶意实体 = 7.2,需求允许危险 = 7.3)。 +- **M1 对齐**:§0.2 明确「不改变 M1」并给出四条逐条理由 + 两点增量(不对称性推广 / M1 证成);§0.4 给出三条修订触发条件与代价(C1/C2/C3,当前全未触发)——**无含糊**。 +- **占位符扫描**:无 TBD/TODO;「实施批定」= 内部布局(非设计缺口,R2 同款惯例);「待论证」三处已在 §6.3 显式切分。 +- **内部一致性**:§1.3 判定式(**含 `v`**)↔ §2.1 `ty_sub` 落法 ↔ §5.3 `execute` 进枚举 ↔ §6.1 切片(`store` 为 effectful op)自洽;§0.3 unsafe 裁决与 §4 `extern` 行同源(都是例外入口);§3.3 四档 + **违法** 行与 S-A §2.2「违法 ≠ ④ reject」分栏同款(A 线 M5 同源修法)。 +- **收敛批 v2 合规(2026-09-11)**:放行/声明条目收口(D2(a):§0.3/§5.1 + 台账);默认强度分层(D3(c):§2.2/§3.3);`Can` 带版本(D13:§0.1/§1.3);M1 代价接受(D14:§0.4);def 链唯一 owner(D22:§2.1);接口格式归 S-D(D29:§2.4);族内技术项(D30:§2.3 / §5.2 / §5.3);记号更名 `τ_subject(a)`(A 线 M3);门禁改引 R2 §4 行(A 线 M1 门禁口径);S-A 回填完成(§8.2-7)。 +- **范围检查**:单一实现计划?**否**——§6 只出最小切片(S-B2-S1),实施计划后立(writing-plans),且切片本身分「通道先行 / 策略条目后随」两小步。 +- **区分设计与否**:§6.3 显式列出定稿面 vs 待论证面(含「复用既有 pass 结果」需实施批侦查的诚实缺口)。 diff --git a/docs/superpowers/specs/2026-09-11-policy-information-flow-design.md b/docs/superpowers/specs/2026-09-11-policy-information-flow-design.md new file mode 100644 index 00000000..60a1075f --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-policy-information-flow-design.md @@ -0,0 +1,420 @@ +# S-B3 信息流族策略设计:`Secret ↛ Public`(除显式 `declassify`) + +日期:2026-09-11 +状态:**设计稿(2026-09-11 收敛批 v3)**——A 簇裁决(★D1(a)/★D2(a)/★D3(c)/★D4(a)/★D12/★D13/★D19/★D30 等)已随动(逐处标注),§8.2 Q1-Q9 全部给裁定(Q7 保留为待实测);实施计划后立(writing-plans),S1 启动受 ★D12 覆盖率门槛约束。**收敛批 v3 变更**:本地裁决编号统一为 `L`(原 `D`;§0.3 表 + 正文引用;编号对照表见路线图 §4——B 线 N2)· §2.1 的 `integrity ↔ S-B5` 回引(A 线 N3) +性质:语义安全路线图 §2 S-B3 行——信息流(机密性 `secrecy` + 不可信方向 **`trust`**)的流纪律判定、`declassify` 语义、隐式流覆盖边界。 + +**依赖注记(必读;2026-09-11 收敛批 v2)**:S-A(`2026-09-11-semantic-safety-obligations-design.md`)**已落盘(收敛批 v2)**——本 spec 已按其实文对齐:§1.1(**`trust` = 可判传播轴,★D1(a)**)、§2.2 + §2.2 附(四档 + 默认强度分层 L3)、§3.1(**放行/声明条目 schema,★D2(a)**)、§3.3.1(C1-C5)、§3.6(装载期校验,★D6)、§5.2(三面可见)。原 `[S-A]` 引用点已逐处校准;**四档义务与属性轴的最终定稿以 S-A 为准**,本 spec 只填 S-B3 范围,不重定义任何共享契约术语。 + +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`——**共享契约(§1.1 术语 / §1.2 原则 / §1.3 三类不可消)逐字遵守**,本 spec 不重定义 +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2——类型项 + 语义子类型引擎;P0/P1/P2a 已实施)= §2.2 的判定落点 +- `docs/memory-model.md` §一(缓存语义七条:值 = 配方 / 边界公理 / 无配方条目)= 信息"如何传播"与"如何存在"的同构面 +- `docs/pointer-model.md`(`unsafe` 边界 / provenance)= §3.3 与 §2.1 R3 的现成机制 +- `docs/superpowers/specs/2026-07-30-probabilistic-pass-design.md`(种子:"数据流图天然记录了所有依赖关系")= §2.4 把它从结论降为**待审计前提** +- `docs/superpowers/specs/2026-08-08-region-cfg-design.md`(region + state edges)= §2.1 守卫规则的载体 + +--- + +## 0. 范围与裁决 + +### 0.1 论题 + +把保密性 / 不可信性(`secrecy` / `trust` 两轴,§1.1)从「编译后再扫描一下」变成 **Semantic IR 本身的一部分**: + +> `Secret ↛ Public`(除非经过显式 `declassify` 节点);对偶地 `Untrusted ↛ Trusted`。 +> 判定不是对源码模式的扫描,而是**图上的一次标注传播 + 一次包含判定**(§2)。 + +`password → hash → comparison` ✓ 与 `password → println` ✗ 的差别,在图上就是前者流到了 `declassify`/契约降密的出口、后者流到了边界 sink。**图已经有了全部依赖结构**(数据边、region 守卫、条目 provenance)——本族要做的是在同一张图上补一条标注轴与一套流纪律,不建第二套分析体系(承路线图 §1.2 原则 1/4 与定案 4「类型检查 = 图验证 pass」)。 + +### 0.2 覆盖三档声明(本 spec 的**边界**,逐条落到 §2.4) + +| 档 | 含义 | 本族处置 | +|---|---|---| +| **覆盖(可判定)** | 有明确判定规则、可静态判、可给反例路径 | §2.1 四条规则覆盖的面:显式数据流、结构化控制守卫(含循环次数外溢到 effect)、经条目(堆)的隐式流、边界 sink | +| **设计但不落地** | 有明确落法,但本切片不含(成本/依赖) | 动态标签的运行时强制(enforce 档,§5.1)、per-offset 标签精度(§2.1 R3) | +| **登记不覆盖** | 本族**不**承诺,且**不静默**——报告中显式列出 | timing / 非终止 / 微架构 / covert channel(§2.4 T 档 + §7) | + +### 0.3 六条裁决(本 spec 自定,供实施批引用;不与共享契约冲突) + +| # | 裁决 | 理由锚点 | +|---|---|---| +| L1 | **标签是数据,纪律是语言**:标注以策略/库为源、以图上标注承载;语言内建的是**传播规则 + 释放节点 + 义务阶梯**,不加关键字、不做名字特判 | 路线图 §1.2 原则 1/2;定案 1(类型 = 图标注) | +| L2 | **格结构是策略参数**:语言不固化格;默认实例 = 二元素布尔格 `Public ⊑ Secret` / `Untrusted ⊑ Trusted`;多级(级别 + 类别)与 principal-indexed 是**格的实例**(数据) | 「规则封闭对象开放」(memory-model-capability-lattice v4);路线图 §1.1「policy = 数据而非语法」 | +| L3 | **传播自动、源与释放显式**:变量级零标注;标注只出现在①边界声明②库的类型化构造器③**可选的**签名契约(契约参与接口注册表,调用点判定) | 定案 12/13(自动推导;显式性最小集);路线图 §1.1「sink/source 模型由领域库提供」 | +| L4 | **两轴默认方向相反、且都不静默放行**(**2026-09-11 按裁决 ★D3(c) 更新**):secrecy 源 = **声明式**;`trust` 方向 = **已声明的**不可信源为 `Untrusted`(纯计算保持——传播不提升);**未声明的边界条目 = `unknown`**(**★D3(c)**:保守判定方向,**不落 `Public`** → 落 ③ + 报告面可见,默认档不拒编 / strict 档拒编) | 违例只由**已声明/已推导出的**高标签触发 → 纪律强度**取决于源侧标注**(库的构造器);"忘标"= **unknown 面(可见、有账)**,不是本族的静默通过(§5.3、§7) | +| L5 | **判定统一走引擎**:标签编码为**类型项**,检查 = `ty_sub(实测, 要求)`;**不做独立的 label 推断体系**,label 随泛型具体化走 R2 §5.2 | 路线图 §1.2 原则 4;R2 §3.2 判定 API | +| L6 | **释放唯一,且 `unsafe` 不洗标**:降标只有 `declassify` 一条通道;`unsafe` 只是**边界标注**(memory-model §一 条款 4),进出 `unsafe` **不改变任何标签** | 路线图 §1.1 `declassify` 定义;§7 第 2 类不可消 | + +### 0.4 为什么这不是「lint 换皮」(路线图 §1.2 原则 2 的反面自查) + +① 无关键字、无函数名匹配——标签与格实例是**数据**,换一种安全模型不改 Graph 本体;② 判定对象是**边**(图上的流),不是文本模式;③ 违例**可以拒绝编译**(四档硬落法),不是警告;④ 与 S-B 其余四族**共用同一传播与义务机制**(注入 = 语法位 sink、授权 = capability path、围栏 = 目标不可构造、TOCTOU = 版本等值)——**是基座而非特判**。 + +--- + +## 1. 标签轴 + +### 1.1 两条轴与格结构 + +| 轴(路线图 §1.1 术语,逐字引用) | 语义 | 默认实例 | 序 | +|---|---|---|---| +| `secrecy`(机密性) | 值的**内容**能否被外部观测 | `Public` / `Secret` | `Public ⊑ Secret`(低 → 高);**释放 = 下降** | +| **`trust`**(可信度;S-A §1.1 轴,本族**消费**) | 值的**内容**能否被不可信来源影响 = 不可信性在程序内的**传播面** | `Trusted` / `Untrusted` | `Untrusted ⊑ Trusted`;**净化 = 上升** | + +- **轴命名收敛(2026-09-11 裁决 ★D1(a))**:本表第二行旧稿写作 `integrity`。按 ★D1(a):**`trust` = 可判的传播轴**(S-A §1.1 为**真源**),本族消费的正是该轴;**`integrity`**(S-A §1.3 = **可篡改性**,与 `trust` 对偶但**不同轴**)在本族**退为对偶表述**——本族**不单列其判定面**(其首消费方 = **S-B2 / S-B5**,见 S-A §1.3)。下文中凡「完整性方向」一律读作 **`trust` 轴方向**(`Untrusted ↛ Trusted`)。 +- **两面对照(不得混同,S-A §1.1 同款)**:`trust` 的**程序内传播**可判(= 本族 R1–R4);**来源真实性的 attestation**(谁生产/签名/背书该来源)**不可判**(路线图 §1.3 第 2 类,只能缩小)。 +- **谁定义 / 谁判定 / 谁是消费者(★D1 共同要求)**:**定义** = **S-A §1.1**;**判定** = 引擎(S-A §3.3 单式 `ty_sub(τ(v) ⊓ A_trust(v), Shape(π))`,三态);**消费者** = **本族传播面(R1–R4)** + S-B1(`syntax_raw` 裸位置判据)+ S-A §6 切片 + S-B4(不可信路径数据)。**本族不定义轴、不新增判定体系**(原则 4)。 + +- **格是参数**(L2):`level × compartment`(Bell-LaPadula 形)与 principal-indexed(`{alice: Secret}` 形)都是默认二元素格的推广实例;多级格的 `⊔` = 级别取 max + 类别取并。语言侧只需要一个 `⊔`(join)与一个 `⊑`(序)的**接口**,实例由策略给定。 +- **join 是唯一的合并算子**:汇合点(PHI / 分支合流 / 参数合并)取 `⊔`,故传播是**单调**的 → 最小不动点存在且唯一,chaotic iteration 收敛(配合 §2.3 的有限高度)。 +- **标签是值的属性,不是类型的替代**:一个值同时有基类型(R2 类型项)与标签;推荐编码见 §2.2。 + +### 1.2 与 `trust` 轴的关系(定案) + +路线图 §1.1 同时列了 `trust`(可信度)与 `integrity`(完整性)。二者分层如下,**不可互相替代**(**2026-09-11 按 ★D1(a) 修正——旧稿把两面混同,已作废**): + +| | `trust` | `integrity` | +|---|---|---| +| 性质 | **可判的传播轴**(S-A §1.1):不可信性沿图**单调传播**——这一**程序内传播面可判** | **可篡改性**(S-A §1.3):值是否可能被非授权方改写——与 `trust` **对偶但不同轴** | +| 能否被语言判定 | **能**——**程序内传播**(= `trust` 轴 = 本族 R1–R4);**不能**——**来源真实性的 attestation**(「谁生产 / 签名 / 背书了这个来源」):属路线图 §1.3 第 2 类「恶意/失陷外部实体」,只能靠可复现构建 / 签名 / provenance **缩小** | 本族**不单列判定面**(首消费方 = S-B2 / S-B5,S-A §1.3) | +| 与谁对接 | **S-A §1.1 轴(本族消费)**;治理层 / 供应链 attestation(**不可判**,只能缩小) | S-B2(授权)· S-B5(check/use 版本) | +| 关系 | `trust` 的**诞生地** = 边界声明("这个入口不可信");其**传播形态** = 本族 R1–R4 流纪律 | 与 `trust` 对偶的表述;本族不重定义 | + +一句话(**替换旧表述**):**`trust` 是轴,本族判的是它的程序内传播面**;**来源真实性的 attestation** 不可判——旧稿「把 `trust` 直接当流纪律用会立刻退化成不可判定」**作废**(它把 attestation 面与传播面混同了;按 ★D1(a),传播面正是可判的、且就归 `trust`)。 + +### 1.3 标注的来源与承载 + +**来源(只有三处,L3)**: + +| 来源 | 形态 | 谁写 | 默认 | +|---|---|---|---| +| ① 边界声明 | 图边界入口(无配方条目:MMIO/FFI/输入/测量,memory-model §一 条款 4;语法层即 `unsafe` 边界)的标注 | 用户/部署策略 | **未声明的边界条目 = `unknown`**(**2026-09-11 裁决 ★D3(c)**,替换旧稿「secrecy 未声明 = `Public` / integrity 未声明 = `Untrusted`」)——**保守判定方向**:落 ③(登记)+ **报告面可见**;**默认档不拒编**、strict 档拒编(§5.2 / §8.2 Q9 已裁)。已声明者:secrecy 按声明;`trust` 方向已声明不可信 ⇒ `Untrusted`(纯计算保持——传播不提升) | +| ② 库的类型化构造器 | `Password::read() -> labeled` 形(构造器即默认出口) | **领域库**(路线图 §1.1 sink/source 模型;§1.2 原则 2 策略面归库) | 由库声明 | +| ③ 签名契约(可选) | 函数的 label 契约(`fn verify(p: labeled, h: labeled) -> labeled`)——契约参与接口注册表,调用点判定 | 库作者/用户 | 缺省 = 由函数体**推导**(保守取体上界) | + +**承载**(图上对象,路线图 §1.1「图对象/边上的开放标注」): + +| 承载位 | 内容 | 实现位(待实施批核对) | +|---|---|---| +| 值标签 | 每个值(IR 变量/边)一个 label 类型项 | 与 `irv_type_term`(R2 §3.4 迁移期双槽)同源;label 是类型项的一部分(§2.2) | +| region 守卫 | 每个 region(`SG_IF`/`LOOP`/`FOR`/`FLOW`/`UNSAFE`)一个 label | 与 `g_df_node_region` 同结构,region 表加一列 | +| 条目标签(堆) | 每个**分配条目**一个 label(`⊔` 所有存入值) | 复用 provenance 的条目标识(memory-model §一 条款 6「地址 = (条目标识, 偏移)」) | +| 释放记录 | `declassify` 节点的四字段(§3.1) | 图上节点 + `.ccr` 段(§6.1 S3) | + +### 1.4 序方向(**最易错点**,实施前必读) + +安全序与**类型序方向相反**: + +| 事实 | 安全叙事 | 类型判定(L5) | +|---|---|---| +| 公开数据可用于机密上下文 | `Public ⊑ Secret`(低可入高) | `labeled <: labeled` ✓ **子类型** | +| 机密数据不可流出到公开上下文 | `Secret ↛ Public`(禁写入低) | `labeled <: labeled` ✗ **非子类型** | +| 汇合取上界 | 分支合流取更机密者 | `label(phi) = ty_join(A, B)` | + +`Secret ↛ Public` 在类型层表现为**子类型关系的缺失**(而非某个"禁止转换"的特判)。实现若把序写反,会把全部负控放行、全部正控拒绝——§6.2 的正/负控表即为此设的对拍锚。 + +--- + +## 2. 流纪律判定 + +### 2.1 四条规则(覆盖档的全部内容) + +设 `guard(ρ)` = region ρ 的守卫标签,`guardᵗ(ρ)` = ρ 及其全部祖先 region 的守卫之 `⊔`。 + +| # | 规则 | 内容 | 图的依据 | +|---|---|---|---| +| **R1 值标签** | 传播 | `label(v) = (⊔ label(每条数据入边)) ⊔ guardᵗ(region(v))` | 数据边(值流)+ region 嵌套 | +| **R2 守卫(隐式流 / pc 标签)** | 控制依赖 | `SG_IF` 的**两个**子 region 守卫均 `⊔` 条件标签;`LOOP`/`FOR` 的体 region 守卫 `⊔` 循环条件(或计数)标签;`FLOW`/`UNSAFE` 不加自身守卫 | region 是**结构化**的 → pc 标签是 **per-region 常量**(可判定的关键,见 §2.4) | +| **R3 条目标签(堆隐式流)** | 存储 | `label(条目 e) = ⊔ label(每个 store 到 e 的节点)`;`label(load e) = label(e) ⊔ label(地址/偏移值)` | provenance 的条目标识;**粒度 = 整条目**(per-offset 精度登记,§8) | +| **R4 sink 观测标签** | 检查点 | 对每个 sink/契约点 `s`:`obs(s) = (⊔ label(实参)) ⊔ guardᵗ(region(s))`,判 `ty_sub(obs(s), required(s))` | sink 声明(§2.1 末);`required(s)` 由库/策略给定 | + +- **R2 覆盖了什么**:`if secret { print("A") } else { print("B") }`——两条 `print` 的实参是常量,R1 什么也看不到;**R2 把条件标签外溢到 region 内的 effect 节点**,两条 print 的 `obs` 均为 `Secret` → 违例。循环次数信道 `for i in 0..secret_n { send(x) }` 同法被覆盖(体 region 守卫 = `Secret`)。 +- **R2 不覆盖什么**:非结构化控制流(若存在 goto/长跳转/exotic 异常)。**假设**:region 覆盖全部结构化控制面——**待审计**(§6.1 前置 3 = **★D12 启动门槛**:覆盖率报告无缺口)。 +- **state edges 的位置**:state edges 表达**顺序**(命令式下限),不是值依赖,**不传播标签**。一个 effect 的"是否发生"由其所在 region 的守卫决定(R2 已覆盖),故无需沿 state edge 传播。反过来,顺序信息本身在保密性方向不是信道(谁先谁后不泄漏内容);**`integrity`(可篡改性)方向由 S-B5 承担**——S-B5 以 **`state-version × integrity`** 的形式消费该轴(S-A §1.3/§1.8 已按此回引;**A 线 N3** 的锚点即本句),本族**不消费、不重定义**该轴(本族消费 = `secrecy` + `trust`,§1.1)。 +- **`required(s)` 从哪来**:① 边界 sink(print/文件/网络/syscall/panic 等跨越图边界的 effect)**默认 `required = Public` / `Trusted`(default-deny:sink 不会"无要求",因此任何已带 `Secret`/`Untrusted` 的到达都被拒)**;② 库内 sink 由库声明(类型化构造器自带 `required`);③ 非边界的内部函数调用**不设** `required`(否则等于给全程序加标注,违反 L3)。default-deny 是**策略数据**,可被策略覆写。 +- **源侧 `unknown` 的入口(★D3(c))**:**未声明的边界条目取 `unknown`**(§1.3①)——由它导出的 `obs(s)` 含 `unknown` ⇒ **不得当通过**:默认档落 ③(登记 + 报告面可见、不拒编),strict 档拒编(§5.2)。这是「漏标源」从**不可见**变成**有账**的机制(§8.2 Q9 已裁)。 +- **纪律强度的诚实边界**:`Secret ↛ Public` 只能拦"**已被标注为 Secret 的流**"。库/用户漏标**已声明面之外的来源**时,本族**不推断秘密性**(没有"秘密性"的静态 oracle),但**不静默**:未声明边界条目 = `unknown` 面(上一条),**必须可见**。这正是路线图 §1.3 第 1 类的落点(规则本身错/oracle 缺失 ≠ 语言语义缺陷),也是 §1.3①「源侧声明」必须由领域库承担的原因。 + +### 2.2 判定如何落到引擎(L5) + +R2 引擎(P0 已实施:`ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited`/`tt_witness`,`g_type_terms` 类型项 DAG)是**唯一判定权威**(路线图 §1.2 原则 4)。标签作为类型项参与: + +- **推荐编码**:`labeled` ≡ `product(label_atom(L), τ)`——标签是 product 的一维,值语义与 R1–R4 的传播都是"同维度 `⊔`"。 +- **引擎义务(交实施批,落点为 R2 类型项扩展)**: + 1. **标签原子 + 序公理**:`label_atom(Public) <: label_atom(Secret)` 等,作为**引擎公理**注册(与 R2 P0「原子类互斥公理」同机制、同用例表形态); + 2. **维度隔离**:标签原子与基类型原子是**不同维度**——`labeled` 与 `labeled` 可同时成立,不得被互斥公理误判不相交; + 3. **join**:`ty_join(A,B)`(PHI 合成用)= 序下的最小上界;若引擎暂不导出 join,则以 `labeled<⊔, τ>` 的显式构造式落地(实施批择一,登记)。 + 4. **非退化性(S-A §3.3.1 **C5**,★D11 同源)**:标签维度必须**非退化**——`label_atom(Trusted) ⊓ τ ≠ ⊥` 对正常基类型成立(**维度隔离义务 2 的正面表述**),且 `Shape` 侧不得退化为 `⊤`(装载期校验,S-A §3.6);否则正控全灭或判据被架空。**「假 / unknown」分因**(假 = 违法、unknown = ③/④),**未定义标签原子 = unknown 不得取真**。 + +- **编码裁定(2026-09-11,★D30 关闭 §8.2 Q3)**:**取 `product(label_atom(L), τ)` 一维**(本节「推荐编码」),**不新增 `AK_LABEL` 原子 kind**。理由:零引擎扩展(product + `ty_join` 已是 P0 面),维度隔离义务(第 2 条)已覆盖其风险。 +- **检查 = 一次 `ty_sub`**:`ty_sub(obs(s), required(s))`;失败给**非空反例路径**(R2 §3.2 `ty_witness` 精神:报出**哪条边**把标签带上来的——图 → 源码映射,R2 §7)。 +- **未判定的诚实落法**:引擎预算耗尽 / 桥接失败 → **不得静默当通过**(路线图 §1.2 原则 3)——`on` 模式登记并告警,`strict` 模式拒编(§5.2)。 +- **不做独立 label 推断**:label 是类型项的一维,泛型函数的 label 多态 = R2 §5.2「泛型 = 编译期接口具体化」的普通实例(实例化时 `ty_sub(实参 label, 约束 label)`)。**不**引入 second-class 的 label 推断/子类型体系。 + +### 2.3 pass 形态 + +与既有三 pass(PointerAnalysis / RegionCheck / ProvenanceVerify)**同级同构**(定案 4:类型检查 = 图验证 pass): + +``` +1. 建标 边界声明 + 库构造器 + 签名契约 → 播种标签 +2. 传播 R1/R2/R3 单调不动点(worklist;格高度有限 → 收敛可证) +3. 检查 R4:每个 sink/契约点一次 ty_sub +4. 落档 四档义务(§5.1):prove / enforce / proof-required / reject +``` + +- 前四步**只读图 + 写标注**,不改 IR 结构 → 不影响代码生成;启用与否由策略控制(§5.2)。 +- 建标与检查都用**同一张图的同一套对象**——这就是"语义保鲜"在安全面上的兑现:`.ccr` 出前端即带标签与释放记录,验证/审计工具**直接消费**,不必重跑前端(R2 §6.4 的同款主张)。 + +### 2.4 隐式流边界表(覆盖到哪一档,逐条) + +| 信道 | 本族归属 | 结论 | +|---|---|---| +| 显式值依赖(数据边) | R1 | ✅ 覆盖(切片 S1) | +| 显式分支 / 条件写(pc 标签) | R2 | ✅ 覆盖(S1;结构化 region → per-region 常量守卫) | +| 循环次数(经 effect 外溢) | R2 | ✅ 覆盖(体 region 守卫) | +| 循环次数(经纯计算影响**时长**) | — | ❌ 登记不覆盖(T1,§6.2)——归 S-C 成本模型/硬件层;**不承诺 constant-time** | +| 经堆(store/load 非结构化) | R3 | ✅ 覆盖(S1;粒度 = 整条目,per-offset 精度登记 §8) | +| 经 `unsafe`/FFI 写 | R4(边界 sink) | ✅ 覆盖为**检查**:`unsafe` 不洗标(L6),写边界 sink 需标签合格 | +| 进程终止(panic/异常/提前返回) | R2(发生受守卫) | ✅ 覆盖其**发生**(`panic` 登记为边界 effect);消息内容 = R1 | +| timing / 非终止 | — | ❌ 登记不覆盖(T2) | +| 微架构(cache / 分支预测 / 功耗 / EM) | — | ❌ 登记不覆盖(T3)——硬件层(hw-map),本族不碰 | +| covert channel(两进程约定 / 系统外部信道) | — | ❌ 登记不覆盖(§7) | +| 动态标签(反射 / `dyn` / 外部返回的标签) | enforce 档 | ⚠️ 设计但不落地(§5.1)——本切片不含运行时影子标签 | +| 非结构化控制流 | — | ⚠️ **假设不存在**——**待审计**(§6.1 前置 3);若不成立,登记为未覆盖 | + +--- + +## 3. `declassify` 语义 + +### 3.1 节点契约(四字段) + +`declassify` 是**图上显式节点**(路线图 §1.1:`Secret → Public` 的唯一合法通道;对偶地 `Untrusted → Trusted` 的净化节点,本 spec **沿用同一个词**,不新造第二个术语): + +| 字段 | 内容 | 强制性 | +|---|---|---| +| **目标标签** | 释放到哪一档(不是隐含的"公开") | 必填 | +| **范围** | 只释放**流经本节点的那个值**——不是"此后该变量全部放行",更不是全程序放行 | 结构强制(节点语义即此) | +| **理由(purpose)** | 审计事实(如 `"password-verify:1bit"`);随图进 `.ccr` | 必填(`strict` 模式下空理由 = 拒编) | +| **授权** | **谁能降密**——本 spec **不判**,作为义务交 S-B2(`authority` 轴 / capability path) | 交 S-B2 [S-A] | + +**本族条目在 S-A §3.1「放行 / 声明条目」栏的形态(2026-09-11 裁决 ★D2(a))**:`kind = declassify`;主体/身份 = **授权字段**(交 S-B2 承载);目标/范围 = **目标标签** + 「只释放流经本节点的那个值」;理由 = `purpose`;**派生源** = 声明它的库 / 净化构造器(§1.3②);**义务引用** = §3.4 / §3.5 的 **proof-required** 义务。**形态 = 图上显式节点(★D2 默认形态,无豁免)**;四字段 + 派生源 + 义务引用**全部进台账**(S-A §5.2b),`strict` 档空理由 = 拒编(S-A §3.6 装载校验同款)。 + +### 3.2 可审计性(本族的核心承诺之一) + +1. **禁止隐式插入**:任何 pass/库/默认行为**不得**自动生成 `declassify`(不做"猜测意图式放行")。 +2. **优化必须保标**:跨越 `declassify` 边界的变换不得改变"释放了什么"(不得把计算提升/下沉穿过释放点导致释放范围扩大)。这是**对优化器的义务**(与 S-C 的接口),与缓存语义的「驱逐不变量」(memory-model §一 条款 2)同形:变换保持可观测语义——此处"可观测"额外包含标签。 +3. **DCE 不留幽灵**:被删除的 `declassify` 节点(其值无消费者)不产生运行期语义,但**释放记录保留在审计通道**(`.ccr`/`explain`,接 S-D)——审计要看到"程序曾声明释放过什么"。 +4. **可计数**:释放点是**有限、可枚举**的图节点——审计 = 枚举全图 `declassify` 节点 + 读四字段;不存在"未记录的降标路径"(前提 = §2.1 的判定完备性,其假设见 §2.4 与 §6.4)。 + +### 3.3 与 `unsafe` 的关系(L6) + +| | `unsafe` | `declassify` | +|---|---|---| +| 语义角色 | **边界标注**:编译器无法追踪 provenance 的入口(memory-model §一 条款 4 / pointer-model「声明是唯一信任点」) | **释放节点**:显式降标 | +| 对标签的作用 | **无**——进出 `unsafe` 不改变任何标签;`unsafe` 内产生的值继承其输入的标签 | 唯一合法的降标动作 | +| 何时是必要的 | 外部地址/FFI/inline asm 等无 provenance 面 | 需要把机密/不可信内容交付给低标签面时 | + +**关键裁决**:`unsafe` **不洗标**(`unsafe` 是标签的诞生地之一,不是释放通道)。`unsafe { raw_write(fd, secret) }` 仍是违例(R4 边界 sink)。若某场景确需在 `unsafe` 内释放(如与外部安全模块交互),走 `declassify` + 证据(§3.4),**不**放宽 `unsafe` 语义。 + +### 3.4 密码学降密 = proof-required 档 + +`encrypt(k, secret)` 的输出**默认仍带 `Secret`**——加密**不是**自动降密器: + +- 正确形态 = `declassify(encrypt_output, 目标=Public, 理由="AEAD:nonce+ciphertext"))`,其正确性(语义安全)**不可由语言层证明** → 落**③ proof-required 档** [S-A]:要求用户/库提供契约或证明工件,节点携带其引用。 +- 这不是过度保守:把"加密即安全"当公理会立刻把整条 IFC 变成可被任意包装绕过的空壳(现实漏洞的常见形态)。 +- 同理,**哈希/编码/截断/启发式扫描都不是降密/净化**(§4.3)。 + +### 3.5 不可信方向的放行面(`trust` 轴的对偶面) + +`trust` 轴的 `declassify`(净化)与机密轴的**差别只在证据种类**:机密轴要"**为什么要释放**"(purpose + 可选密码学证明);`trust` 轴要"**依什么规则判定为安全**"(validation rule:解析/白名单/转义/参数化的具体规则)。**净化构造器的形态与领域库接口约定归 S-B1**(`trust` 轴的消费方);本 spec 只定它在流纪律中的语义 = **降标节点 + proof-required 证据 + 唯一通道**(`kind=declassify`,§3.1)。 + +--- + +## 4. 不可信方向(`trust` 轴) + +> 节名收敛(★D1(a)):本方向判的是 **`trust` 轴**(S-A §1.1);`integrity`(可篡改性)不在此单列判定面(§1.1 命名收敛)。 + +### 4.1 对偶规则 + +本方向 = 同一个传播机制、相反的释放方向(`trust` 与 `secrecy` 对偶): + +| | secrecy | **`trust`**(不可信方向) | +|---|---|---| +| 违例 | `Secret → Public`(内容外泄) | `Untrusted → Trusted`(污染内容进入受信位置) | +| 默认源 | 声明式(§1.3 ①);**未声明边界条目 = `unknown`**(★D3(c)) | **已声明的**不可信源 = `Untrusted`;**未声明边界条目 = `unknown`**(★D3(c)) | +| 出口 | `declassify`(释放) | 净化构造器(validate/parse/escape/参数化) | +| 义务档 | 多为 prove(静态判定);密码学 → proof-required | 净化规则 → **proof-required**("这条规则真的够吗"不可由语言证明) | +| 典型 sink | 边界输出(print/网络/文件/syscall) | 解释器语法位(S-B1)、特权操作、持久化受信位置 | + +### 4.2 与 S-B1(注入族)的分界 + +| | S-B3(`trust` 轴传播面) | S-B1 注入族 | +|---|---|---| +| 说什么 | **标签怎么流**:不可信性沿任意计算传播(拼接/取子串/哈希都洗不掉) | **端口怎么配**:哪些输入端口是"解释器语法位",哪些是"值位" | +| 判定形态 | 图上的包含判定(`ty_sub(obs, required)`) | 构造器端口/形状纪律(`SQL` 等类型化构造器)+ 非法边 | +| 失败例 | `untrusted → concat → SQL.exec(...)` 里"untrusted 仍是 untrusted" | `SQL.exec("... " + input)` vs `SQL.query(sql, input)` 的**端口**差别 | +| 关系(**2026-09-11 按 ★D1(a) 改写**) | **供给面**:本族供给 `trust` 轴的**传播**(R1–R4)+ 语法位 sink 的 `required` **消费口径** | **消费面**:S-B1 的"非法边" = `trust` 轴上不可信标签到达一个 `required = Trusted` 的语法位端口——**S-B1 的裸位置判据消费 `trust` 轴(S-A 供给)**;S-B1 不重复本族的传播规则,本族不重复 S-B1 的领域构造器约定 | + +> **旧表述作废**:本节旧稿写「S-B1 是 S-B3 **完整性轴**在『解释器端口』上的**实例化**」,与 S-B1 §4.6「不同轴、本文不定义 `integrity`」正面冲突(A 线评审 C1)——按 **★D1(a)** 改写为「**消费 `trust` 轴(S-A 供给)**」;`integrity`(可篡改性)不是本族的判定面(§1.1)。 + +分界一句话:**S-B3 保证"脏的没洗干净";S-B1 保证"洗的方式对不对(值位 vs 语法位)"**。二者相接处 = 调用 `sanitize`/参数化构造器时产生一个 proof-required 义务(`required(s)` 由库声明)。 + +### 4.3 「洗白」的非法手段(负控的语义来源) + +以下操作**不降低** `trust` 标签(即不构成净化):字符串拼接/切片、哈希、编码(base64/URL/HTML 实体——**编码不是转义上下文感知的**)、长度/格式启发式扫描(denylist/regex)、`dyn` 转换、序列化/反序列化(除非构造器声明为净化点并带规则)。它们只能作为 `declassify` 的**证据素材**,不能自动成立。 + +--- + +## 5. 义务落档 + +### 5.1 四档映射 [S-A](术语逐字用路线图 §1.1) + +| 判定面 | 档 | 落法 | +|---|---|---| +| `ty_sub(obs, required)` 判定为真 | ① **prove** | 零成本通过,无产物变化 | +| 标签为**动态**(反射/`dyn`/外部返回标签/策略运行期决定)且存在强制点 | ② **enforce** | 运行时标签检查(fail-closed:不过则中止,不静默放行)——**设计但不落地**(本切片不含;登记为 enforce 档设计落点) | +| 释放/净化的**规则正确性**(密码学/验证规则)不可由语言证明 | ③ **proof-required** | 要求契约/证明工件;`declassify` 节点携带其引用;缺失 = 不通过 | +| 引擎预算耗尽 / 桥接失败 / 未覆盖面在 `strict` 模式被触发 | ④ **reject** | 拒编(高安全模式),错误码段 `EC_IF_*`(段号实施批定,参照 R2 §7 码段做法) | +| **未声明的边界条目**(源侧 `unknown`,§1.3①/★D3(c)) | **③ 登记 + 报告面可见**(默认档**不拒编**);strict 档 ⇒ ④ | **不得当通过**;台账条目 + 未覆盖面清单(§5.3) | +| `ty_sub(obs, required)` 判定为**假**(确定违例,obs 明确超出 required) | **违法**(**不属于义务梯档**——判定结论为假) | **硬编译错**(任何档位拒绝);唯一放行 = `declassify` 节点 / 净化构造器(放行本身登记为义务,§3.2/§3.4);**不要写成「reject」**(S-A §2.2 点 1 分栏) | + +**要点**:①②③ 的**边界**不是"能不能判",而是"**证据从哪来**"——prove 靠引擎、enforce 靠运行时、proof-required 靠人/工具。三者都不许"静默当通过"(路线图 §1.2 原则 3)。 + +### 5.2 模式(策略数据,L1) + +| 模式 | 行为 | 用途 | +|---|---|---| +| `off`(默认) | 不做流验证(不建标、不检查、零产物变化) | 未启用策略的项目;**零回归锚**(§6.3) | +| `on` | 全量 R1–R4 + 边界 sink default-deny;未判定面**登记并告警** | 常规启用 | +| `strict` | `on` + 未判定/未覆盖面 → **拒编**;`declassify` 必填理由与授权引用 | 高保证构建(对齐四档 ④) | + +默认 `off` 是诚实的工程选择:纪律面随策略启用(路线图「policy = 数据」),语言本体不加关键字、不改既有程序语义。 + +**默认强度分层(2026-09-11 裁决 ★D3(c);规范形态见 S-A §2.2 附)**:**本地 = 登记 + 继续编译**(③ = `open`,不是通过);**strict 档拒编**(④);**CI 门槛 = 严**(以 strict 档运行);**未声明的边界条目取 `unknown`**(§1.3①)——默认档不拒编但**报告面可见**,strict 档拒编。**不承诺永久 lenient**(`select` 迁移窗口 W1 = 1 个发布周期,归共享契约 §1.4 / S-E,本族不涉)。 + +### 5.3 义务不静默消失(可承诺的强 claim) + +- 判定不通过 → 必落 ①②③④ 之一,**绝不**当通过; +- **未覆盖面**(§2.4 ❌ 行)在每次 `on`/`strict` 构建的报告中**显式列出**"本构建未覆盖:timing/终止/微架构/covert"——不谎报覆盖,也不把未覆盖混同于"已证明安全"; +- 与 R2 §3.3 的**预算守卫**同款:预算耗尽 ≠ 类型不兼容,此处亦然 ≠ 泄漏;三类结论(通过 / 违例 / 未判定)**分因、分措辞、分码**。 + +--- + +## 6. 最小可实施切片 + 判据 + +### 6.1 切片定义(小、可独立验收、每片一个计划) + +| 片 | 内容 | 依赖 | 判据 | +|---|---|---|---| +| **S1 传播** | 建标(①边界声明 ②库构造器标注位)→ R1/R2/R3 不动点 → 图转储可见标签与守卫 | 无(纯图分析,零语言面改动) | 图转储用例表:正控 P5–P6(§6.2)标注正确;`off` 模式零产物变化 | +| **S2 检查与释放** | R4 边界 sink default-deny + `declassify` 节点四字段 + `EC_IF_*` 诊断 | S1 + S-A 定稿(四档用语) | 正控 P1–P4 通过、负控 N1–N4 拒编且诊断指向**边路径** | +| **S3 载体与审计** | 标签/释放记录进 `.ccr`(依附 R2 P4 载体批次;段布局随批定) | R2 P4 | 出前端即带标签与释放记录;`explain`/审计通道可枚举释放点(接 S-D) | + +**前置**(三件,全部解掉才开工 S1): + +1. **S-A 定稿**——**已满足(2026-09-11)**:S-A **收敛批 v2** 已落盘(含 ★D1(a)/★D2(a)/★D3(c)/★D11 随动);本 spec 的 `[S-A]` 引用点已按其 §1.1 / §2.2 附 / §3.1 / §3.3.1 / §3.6 校准。 +2. **R2 P2b 接线**——判定统一入口(`iface_ops`/`res_type_node` 合一)就位后,标签的 `ty_sub` 检查才不是第二套判定; +3. **依赖完备性审计(定点;★D12 裁决——升级为 S1 的启动门槛)**——把 §2.4 的**前提**从"结论"降回"待验证":**独立侦查项**逐 op 核对"该 op 的语义依赖是否都表达为图上的边/region"(尤其:内存读写的地址/偏移是否为数据边;`dyn`/间接调用/间接写是否表达;有无非结构化控制),**输出「边 / region 覆盖率报告 + 缺口清单」**。**启动条件(规范性,★D12):覆盖率报告「无缺口」才启动 S1**——有缺口 ⇒ 不启动(不是"带着缺口开工");审计不完备的 op 一律列入未覆盖面(种子文档的"图上已有全部依赖"是本族的**待验证前提**,不是公理)。S-B1 §1.2 的流敏感性论证与 S-B4 §1.3 的派生链判定同挂此门槛。 + +### 6.2 正控 / 负控 / 登记控(语义判据表) + +**正控(须通过)** + +| # | 样例 | 通过的理由 | +|---|---|---| +| P1 | `pw → hash → verify(lib 契约: → labeled) → if ok { grant() }` | **契约级降密**:1 bit 泄漏是需求本身要的(§7 第 3 类),显式在库契约里 | +| P2 | `secret → declassify(目标=Public, 理由="audit-export") → println` | 唯一合法通道,释放留痕 | +| P3 | `secret → hash → declassify(...) → println` | `hash` 不是释放,`declassify` 才是(§3.4) | +| P4 | `argv → parse → validate(规则 R) → 受信路径` | 净化构造器 + proof-required 证据(S-B1 交界) | +| P5 | `public → 机密上下文(sink requires Secret)` | 上行合法(§1.4 第一行) | +| P6 | `secret → 计算 → declassify → 再计算 → 公开 sink` | 释放**只**解除流经该值的那部分标签 | +| P7 | 未启用策略的既有程序 | **零产物变化**(byte-identical,同 R1/R2 判据) | + +**负控(须拒编)** + +| # | 样例 | 违例规则 | +|---|---|---| +| N1 | `pw → println` | R4:边界 sink `required=Public` | +| N2 | `if secret { print("A") } else { print("B") }` | R2:守卫外溢(实参是常量,R1 看不见) | +| N3 | `arr[0] = secret; ... println(arr[0])` | R3:条目标签 | +| N4 | `secret → unsafe { raw_write(fd, p) }` | R4 + L6:`unsafe` 不洗标 | +| N5(**联合用例**,2026-09-11 收敛 M8 + L2) | `untrusted → concat → SQL 语法位` | `trust` 轴传播(本族侧)+ 端口违例(S-B1 侧)。**语法位 sink 的 `required` 由 S-B1 声明(policy 数据)、本族只消费** ⇒ 断言**「恰一条诊断」**——两族**不得各报一条**(这是 ★D2 统一 schema 的边界后果:同一场景 = 同一 schema 条目下的一次判定) | +| N6 | `secret → encrypt → println(ciphertext)` | 加密非自动降密(§3.4);缺 `declassify` + 证据 | +| N7 | `if secret { panic() } else { ok() }` | R2:终止的发生受守卫(`panic` 登记为边界 effect) | + +**登记控(须"未判定"且显式登记,不得静默通过、不得谎报为已覆盖)** + +| # | 样例 | 落法 | +|---|---|---| +| T1 | `if secret { slow_public() } else { fast_public() }`(无标签 sink,时长差) | ❌ timing:`on` 模式登记;`strict` 拒编 | +| T2 | `while secret_cond { }`(无 effect 的非终止差) | ❌ 非终止:同上 | +| T3 | 依赖 cache 行为的密钥表查找 | ❌ 微架构:归硬件层/密码库,本族不判 | + +### 6.3 判据 + +1. **用例表全绿**:P1–P7 / N1–N7(**含 N5 的「恰一条诊断」断言**)/ T1–T3 语义判据(含诊断**指向边路径**与分因码); +2. **零回归锚**:`off` 模式下既有语料产物 **byte-identical**(同 R1/R2 判据形态);`on` 模式下未使用标签的语料同样 byte-identical; +3. **套件**:按 **R2 §4 每步共同判据行**(`2026-09-10-type-interface-unification-design.md:155`:全套件 + `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 rc=42)执行——**套件计数以该行为唯一真源,本文不重述数字**(A 线评审 M1); +4. **序方向对拍**:§1.4 的表逐行有用例(防"序写反"这类全放行/全拒绝故障); +5. **诚实性判据**:`on`/`strict` 构建报告**必含**未覆盖面清单(T 档 + §2.4 ❌ 行)——报告缺该节即视为未完成。 + +--- + +## 7. 不承诺(引用路线图 §1.3,逐条说明本族落在哪类) + +| 路线图 §1.3 | 本族的落点 | +|---|---| +| **错误 specification** | 策略说"这些数据是机密"是**规则**;Core 证明的是"程序忠实执行了该规则"(`Secret ↛ Public`),**不证明**分类本身正确(误标为公开的医疗数据、写错 `required` 的库契约照旧泄漏)。 | +| **恶意/失陷外部实体** | **密钥被偷、硬件后门、编译机失陷、供应链投毒、侧信道测量**——本族**不**消除,只在"程序内部传播"这一小段缩小:把本该显式的释放变成**显式且可在 `.ccr` 审计的**释放点。机密不被盗的前提在此类之外(可复现构建/签名/provenance/最小权限,均非语言语义)。 | +| **需求本身允许危险行为** | **需求本身要打印密码**(管理员导出、调试输出、`verify` 的 1 bit 结果):本族**不禁止**,只要求它是**显式 `declassify` + 留痕 + 可选授权**(P1/P2)。同理,crypto 库必须做危险事(计时、处理密钥)——目标是**证明隔离边界**,不是取消能力。 | + +**本族专有的不承诺**(另立,避免读者把"流纪律"误当"侧信道安全"): + +1. **不承诺 constant-time / 无计时信道**——R2/R4 只判"值流向",不判"执行时长";常量时间需密码库契约 + S-C 成本模型 + 硬件层。 +2. **不承诺无终止信道**(`while secret {}` 类)。 +3. **不承诺无 covert channel**(两进程约定、外部系统信道)——经典 IFC 可判定性边界之外。 +4. **不承诺微架构面**(cache/分支预测/功耗/EM)。 +5. **不承诺**"秘密不被读"(confidentiality ≠ 不可访问;那是 S-B2 授权族与 S-B4 围栏族的范围)。 + +--- + +## 8. 不做项 + 开放问题 + +### 8.1 不做(YAGNI / 后置登记) + +- **不做**运行时影子标签全量 IFC 落地(enforce 档保留为设计落点,本切片不含)。 +- **不做** per-offset / 每字节标签精度(切片 = **整条目**粒度:一个数组里放一个秘密会"污染整个数组"——安全但粗;细分精度登记,先例 = provenance 的 (条目, 偏移) 已具备结构)。 +- **不做**独立的 label 推断/子类型体系(label 是类型项的一维,走 R2 引擎与泛型具体化;L5)。 +- **不做**常量时间/侧信道缓解(归密码库 + S-C + hw-map)。 +- **不做** covert channel 检测。 +- **不做**非结构化控制流的流纪律(若 IR 存在此类形态,先登记,不进本切片)。 +- **不做**动态标签的自动推理(`dyn`/反射/外部返回标签 → enforce 档或拒编,不猜)。 + +### 8.2 开放问题(**待裁决/待审计**,非本 spec 可单方定) + +| # | 问题 | 影响 | 裁定(2026-09-11) | +|---|---|---|---| +| Q1 | **依赖完备性**:图的边/region 是否真的覆盖全部语义依赖(内存读写地址是否入边、间接调用/间接写如何表达、有无非结构化控制) | §2.4 的覆盖声明与 §6.1 前置 3 | **已裁(★D12)**:**独立侦查项**(产出「边 / region 覆盖率报告 + 缺口清单」);**S1 启动条件 = 报告无缺口**(§6.1 前置 3) | +| Q2 | 默认二元素格是否够用,还是应直接给 `level × compartment` 默认实例 | §1.1;切片 S1 的格接口面 | **已裁(★D30)**:**默认 = 二元素布尔格**(最小且可判);`level × compartment` 与 principal-indexed = **格的推广实例**(数据),**不设默认多级** | +| Q3 | 标签编码:`product` 一维(推荐)vs 新增原子 kind `AK_LABEL` | §2.2 引擎义务 | **已裁(★D30)**:**`product` 一维**(§2.2 编码裁定),**不新增 `AK_LABEL`** | +| Q4 | `declassify` 的粒度:值级(本 spec 立场)vs 通道级 vs endorsement 形;授权引用与 S-B2 的具体 seam | §3.1;跨 S-B2 | **已裁(★D2(a) 同源)**:**值级**(保持本 spec 立场);通道级 / endorsement 形**不采用**——唯一放行 = S-A §3.1 的 `kind=declassify` **显式节点**(§3.1 形态栏)。与 S-B2 的 seam = **授权字段**(交其 `authority` 判定) | +| Q5 | 标签是否随**版本**变化(S-B5 交叉:v0 为 Public 的条目 v1 存入 Secret 后再读)——与 `state-version` 轴的标签维度 | §2.1 R3;跨 S-B5 | **已裁(★D13)**:**标签随版本**——`label(条目, v)`,读 v1 得 v1 的标签(R3 的 store-join 在**版本上**求值,v0/v1 不混算);κ 粒度 = 对象身份 + 版本见证 | +| Q6 | 标签/释放记录的 `.ccr` 承载:并入 R2 TYPE 段扩展 vs 独立 LABEL 段 | §6.1 S3;载体批次 | **已裁(★D4(a))**:**独立段**(`LABEL` / `POLICY`),随 **R2 P4** 同波(v7 `7+` 段位已预留) | +| Q7 | 守卫传播的**精度代价**:结构化控制在真实语料上会产生多少"保守拒绝"(尤其中断/早退/多出口函数) | §2.1 R2 | **保留为待实测**(登记,不裁)——需语料数据(同 R2 P1 影子期做法),切片 S1 上线前给出实测结论;**不得以估计值代替** | +| Q8 | 边界 sink 的 `required` 默认表(哪些内建 effect 算边界 sink)——需要一份**清单与理由**,而不是逐处特判 | §2.1 末;与 HIT/IR op 面清单同法 | **已裁(★D19 合并 + ★D7 约束)**:① 表 = **一份**(含理由,定点审计产出),与 **HIT / IR op 面清单**同法;② **边界节点 = 一类合法的显式不确定源**(主人 = 边界节点 id;`determinism` 族侧链取值 `EXPLICIT`,★D19/S-D4 判据②);③ **外部效应 / 变更源清单全局唯一**(挂 S-A §1.7),本表引用不重写(★D7) | +| Q9 | `strict` 模式是否把**未声明的边界条目**当作 `unknown`(而非 `Public`)从而在边界 sink 上拒编(catch 漏标源,代价 = 误报多、必须逐源声明) | §1.3①/§2.1 末;默认 vs 严格的分界线 | **已裁(★D3(c))**:**未声明的边界条目一律取 `unknown`**(**不是** `Public`)——**默认档不拒编**(落 ③ + 报告面可见),**strict 档拒编**。§1.3①/§2.1 末/§5.2 已随动 | + +--- + +## 自检记录 + +- **共享契约**:§1.1 术语(属性轴/义务/四档/policy/`declassify`/sink·source 模型)**逐字引用未重定义**;§1.2 五条原则逐条有落点(1→L1/L2、2→§4.2/§8.2、3→§5.3、4→§2.2、5→§2.1 state edges 注);§1.3 三类在 §7 逐条说明。 +- **依赖注记**:S-A **已落盘(收敛批 v2)** → 题注与各引用点已按其 §1.1 / §2.2 附 / §3.1 / §3.3.1 / §3.6 / §5.2 逐处校准,无悬空引用。 +- **隐式流边界**:§2.4 表逐信道给"覆盖/设计但不落地/登记不覆盖"三态;§6.2 第三类"登记控"保证未覆盖面**不静默**且**不谎报**。 +- **占位符扫描**:无 TBD/TODO;未定项一律落 §8.2 开放问题(Q1–Q9)并标注裁决归属。 +- **默认值自洽(2026-09-11 按 ★D3(c) 更新)**:**未声明的边界条目 = `unknown`**(§1.3①/L4)↔ 由它导出的 `obs(s)` 含 `unknown` ⇒ 落 ③ + 报告面(§2.1 末)↔ 边界 sink `required = Public`/`Trusted` 的 default-deny(§2.1)↔ "纪律强度取决于源侧标注"的诚实交代(§7 第 1 类)四处口径一致;**漏标源不再不可见**(Q9 已裁 = unknown + 报告面),不在正文暗设。 +- **切片**:S1/S2/S3 三片,最小片(S1)无语言面改动、零依赖、可独立验收;三件前置(§6.1)显式列出,不藏依赖。 +- **范围检查**:单一实现计划?**否**——S1/S2/S3 各自独立成计划(writing-plans 逐片立)。 +- **歧义检查**:序方向(§1.4 专设表);`unsafe` vs `declassify`(§3.3 专设表);`trust` vs `integrity`(§1.2 专设表,**含旧表述作废声明**);S-B3 `trust` 轴 vs S-B1(§4.2 专设表,**供给/消费面改写**)——四处最易混同点均以对照表消歧。 +- **收敛批 v3 记录(2026-09-11,第二轮闭环)**:本地裁决 `D1–D6` → **`L1–L6`**(§0.3 表 + 正文全部引用;`★D` 保持为用户裁决写法;对照表 + 注销登记见路线图 §4);**A 线 N3** → §2.1「state edges 的位置」行补 `integrity ↔ S-B5` 的消费面回引(`state-version × integrity`)。 +- **收敛批 v2 记录(2026-09-11)**:**A 线 C1 → ★D1(a)**(§1.1 命名收敛 + 对表 / §1.2 重写 / §4 节名与两表 / §4.2 实例化表述改写);**A 线 C2 → ★D2(a)**(§3.1 形态栏);**★D3(c)**(§0.3 L4 / §1.3① / §2.1 末 / §5.2 / §8.2 Q9);**★D12**(§6.1 前置 3 = 启动门槛);**★D13**(§8.2 Q5);**★D4(a)**(§8.2 Q6);**★D19/★D7**(§8.2 Q8);**★D30**(§2.2 编码裁定 = product / Q2 默认格 / Q3 编码 / Q4 粒度);**M8**(§6.2 N5 联合用例);**A 线 M1 门禁**(§6.3-3);**Q7 保留为待实测**(不裁)。 diff --git a/docs/superpowers/specs/2026-09-11-policy-injection-design.md b/docs/superpowers/specs/2026-09-11-policy-injection-design.md new file mode 100644 index 00000000..04a0118c --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-policy-injection-design.md @@ -0,0 +1,455 @@ +# 语义安全 S-B1:注入族策略设计(injection) + +日期:2026-09-11 +状态:**设计稿(2026-09-11 收敛批 v3)**(**v3 = §5 的 ④ 按 A 线 N1 限定为「已声明域内声明不完整 ⇒ unknown」**——与 §0.2 裁决 9「无声明 = 无义务」并存不冲突;编号对照表见路线图 §4)——已按 S-A 收敛批 v2(`2026-09-11-semantic-safety-obligations-design.md`)对齐:S-A §1 七轴 / §2 四档决策树 + **§2.2 附默认强度分层(D3)** / §3.1 policy 条目 + **放行·声明条目 schema(D2)** / §3.3 判定接法(`ty_sub(τ ⊓ A_a(v), Shape)`)/ **§3.3.1 C1-C5** / §3.6 装载期校验(D6)/ §4.3 enforcement 失败语义 / §5.2 三面可见;A 簇 ★ 裁决(D1(a)/D2(a)/D3(c)/D4(a)/D5(a)/D6(a)/D11/D12)已随动(§8.2 逐条标注)。 +性质:策略族设计(S-B 之一)。只定义「**不可信数据 → 解释器语法位**」这一族非法边的判据、领域库接口约定、净化语义、义务落档与最小切片。**不**定义属性轴/四档义务本体(归 S-A)、**不**改 Graph 本体、**不**加关键字、**不**新增判定体系。 + +关联(逐字遵守路线图 §1 共享契约,本文不重定义其中任何术语): +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**:§1.1 术语 / §1.2 五条原则 / §1.3 三类不可消残留;§2 的 S-B1 行 = 本族范围) +- `docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md`(S-A:属性轴 + 四档义务 + policy 接口——**本族是它的第一个消费者**) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2:类型项 τ / 形状类型项 / `ty_sub`·`ty_witness` / 判定缓存与预算守卫——**判定器地基**) +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(定案 1 类型 = 图标注;定案 2 接口统一;定案 14 声明式边界) +- `docs/memory-model-capability-lattice.md` v4(M1:授权归治理层——与本族边界见 §4.7) + +--- + +## §0 范围与裁决 + +### 0.1 论题 + +把「不可信数据 → 解释器语法位」变成**非法边**: + +> **值位(value position)合法;语法位(syntax position)非法;参数化通道(结构由库的类型化构造器给出、数据走值位)合法。** + +判据的第一性来源是**构造器封闭**(库声明的形状项只有库自己的构造器能产生),**不是污点传播**——这是本族不退化成 lint、也不退化成全局污点分析的原因(§1.3)。污点(属性轴)只在「未包装 API 的裸位置」这一退路里承担判据(§2.4),且作为 S-A 轴基础设施的消费者出现,不是本族自建的第二套体系。 + +### 0.2 本族裁决(在共享契约内取定;承自路线图的另行标注) + +| # | 议题 | 取定 | 来源 | +|---|---|---|---| +| 1 | 判据形态 | **形状项包含判定**(`ty_sub`)为主判据;属性判据仅用于 `syntax_raw` 裸位置。两者统一表达为 S-A §3.3 的单式:`ty_sub(τ(v) ⊓ A_trust(v), Shape(π))` | 本族(承原则 4 + S-A §3.3) | +| 2 | 位置种类的来源 | **领域库声明**(下游解释器性质,编译器不可见) | 本族(承原则 2) | +| 3 | 语法位在类型上的表示 | 位置的**声明类型 = 形状类型项 φ**(`SQL` 类);裸位置用策略数据显式声明 | 本族 | +| 4 | 净化(sanitize)的语义 | **位置化**:把不可信值放进「解释器当数据消费」的位置;产出新类型项,不是「清洗字符串」 | 本族 | +| 5 | `declassify` 的后果 | 产生**义务**(净化正确性由库承担,可落 VC),不是免费通行证 | 本族(承原则 3) | +| 6 | 运行时转义 | **不作为默认落法**——转义是库的构造器策略,不是语言 enforcement;本族 enforce 档默认 = 「校验失败 ⇒ abort」(S-A §4.3 默认档),`degrade` **仅当** policy 显式声明且降级动作 = 库的净化构造器(§5 末) | 本族(回应路线图 §0「而非 runtime escaping」+ S-A §4.3) | +| 7 | 内建特判 | **禁止**任何漏洞类别/关键字/内建 sink 表;sink/source 一律由库的策略数据给出 | 承原则 2(硬性) | +| 8 | 属性来源 | `trust` 标注**推导不手写**,`string` 现状零标注不变 | 本族(承 §1.2 原则 1 + 定案 1) | +| 9 | 义务产生条件 | **无声明 = 无义务**(库未声明语法位 ⇒ 不产生判定,既有代码零影响) | 本族(§4.2 渐进) | +| 10 | 切片边界 | 首切片只做形状项判据 + 常量档,**不含**属性推导与参数化形态判定 | 本族(§6) | + +### 0.3 术语用法声明 + +本文使用路线图 §1.1 的术语:**属性轴** / **义务** / **四档义务**(prove · enforce · proof-required · reject)/ **policy** / **declassify** / **sink / source 模型**——**不重定义**。本族新引入的局部词汇(仅本文使用,不外溢): + +- **位置(position)π**:一次调用实参槽(含方法 `self` 槽)、或等价的「值进入解释器的入口」。位置种类 `kind(π) ∈ {value, syntax(D,φ), syntax_raw(D)}`。 +- **领域 D**:下游解释器标识(`sql` / `html` / `sh` / `fmt` …)——**开放的字符串标识,不是语言枚举**(新领域不改编译器)。 +- **位置形状项 φ**:该位置允许的值的类型项(R2 §1.2 τ),由库给出。 +- **裸位置(raw position)**:库无法用类型表达、只能由策略数据声明为语法位的位置(典型:C 库/未包装 API,`system(char*)`)。 + +--- + +## §1 非法边判据 + +### §1.1 位置种类的操作性定义(为什么它必须由库声明) + +> **语法位判据**:位置 π 是领域 D 的**语法位**,当且仅当**下游解释器 D 对 π 处的字节做分隔符/结构再扫描(re-scan)**——即这些字节中若出现 D 的元字符(或分隔约定),会改变 D 对结构的判定。否则 π 是**值位**。 + +判定测试(对分类的交叉检验,也是库自我审查的检查表): + +| 位置 | 再扫描? | 种类 | +|---|---|---| +| `execve` 的 argv 元素 | 否(内核按 NUL 边界取整段,不再扫描内容) | 值位 | +| `sh -c ` 的 line | 是(`;` `\|` `$()` 空白分词) | 语法位(`sh`) | +| SQL 绑定参数(prepared bind) | 否(协议按长度传值给引擎的**值**槽) | 值位 | +| SQL 查询文本 | 是(`;` `'` `--`) | 语法位(`sql`) | +| HTML 元素体文本节点 | 否(解析器到此已是文本节点) | 值位 | +| HTML 属性值 / URL / 内联 JS | 视上下文(引号/`&`/`:`/JS 元字符) | 语法位(`html` 的对应子形态) | +| HTTP 头的**值**字段 | **是**(CRLF 是分隔符) | 语法位(`http`) | +| 长度前缀协议的载荷 | 否 | 值位 | + +该定义**只能由库声明**、不能由编译器推断:它是对**下游解释器**(DB 引擎、浏览器、shell、内核接口)性质的陈述,编译器看不到下游。这正是路线图原则 2「策略面归库」在本族的落点——**语言不内建任何领域的元字符表**(原则 2 的禁令:任何「SQL 字符串字面量检查」式特判都是 lint 换皮)。 + +### §1.2 形式化:源属性 × 目标位的合法性关系 + +记号(全部复用既有机制,无新判定体系): + +- `τ(v)`:值 v 的**类型项**(R2 §3.1 `g_type_terms`;图上标注,迁移期双槽)。 +- `trust(v)`:v 的**可信度**标注(S-A 的属性轴之一;**推导产生,用户不写**)。 +- `φ`:位置 π 声明的**形状类型项**(R2 §2.2 横切轴 / §2.3 用户轴条目)。 +- 位置约束项 `κ`:附着于 φ 的**档位约束**(常量档 / 符号档 / 动态档,F11 三档,R2 §5.1 长度约束项的先例形式)。 + +**合法性判据**: + +``` +Legal(v, π) ⟺ + kind(π) = value → ⊤ (值位,无义务) + ∨ kind(π) = syntax(D, φ) → ty_sub(τ(v), φ) = 真 (类型判据 · 主) + ∧ 档位约束 κ 满足 (常量档等附加约束) + ∨ kind(π) = syntax_raw(D) → ⊢ Trusted(v) (属性判据 · 仅裸位置) +``` + +- **非法边** = `¬Legal(v, π)`。在图上表现为:**不存在可建的合法 semantic edge**——拒绝是**图不完备**(无合法边),不是「边上加运行期守卫」(§0 裁决 6)。这与路线图的「非法边」措辞严格一致,且更弱:不需要先说「边存在再检测到非法」。 +- 判定走引擎(原则 4):`ty_sub` / `iface_satisfies`(形状满足,R2 §2.5)/ `ty_witness`(失败给**具体反例值** → 诊断「值 X(来自 `io.read_file`,`test.cr:12`)不是 `SQL`」)。 +- **与 S-A §3.3 的接法(已定)**:上式三种 `kind` 统一写成 S-A 的单式 + + ``` + 判定 = ty_sub( τ(v) ⊓ A_trust(v) , Shape(π) ) ⊓ = 引擎 INTER + ``` + + ——`Shape(π)` 由 policy 数据给出:值位 = `⊤`(恒真);`syntax(D,φ)` = `φ`(+档位约束 κ);`syntax_raw(D)` = 该策略的 `Trusted` 形状项。**不新增判定算子、不新增规范化规则**(S-A §3.3 同一要求)。 +- **陷阱注记(本族对 S-A §3.3 的补充,实施批必读)**:单式里的 `⊓` 与 `⊥` 有一个会**架空判据**的退化路径——若 `A_trust(v)` 以**否定形式**展开(如不可信 ↦ `¬Trusted`),而左侧值项本身已含该标注(如库构造出的 `SQL` 值被标 `untrusted`),则左式 = `SQL ⊓ ¬Trusted = ⊥`,而 `⊥ ⊑ 任意 φ` **恒真** ⇒ 判定放行一个不可信值 ⇒ **静默通过**(违反原则 3)。**规范约定**:`A_trust` 只做**加约束式单调展开**(可信 ↦ `Trusted`,不可信 ↦ `⊤`=不加约束),属性要求一律写在**右侧** `Shape(π)`;实施批须配负控(N5,§6.1)锁死该约定。**并且右项与正例也要锁**(S-A §3.3.1 **C5**,2026-09-11 裁决 D11)——本式的**全部鉴别力在右项**(`trust` 不可信 ↦ `⊤` 不加约束),因此:① `Shape(π)` 在 `syntax` / `syntax_raw` 位置**不得为 `⊤`**(policy 装载期校验,违者**拒装**);② 每条轴取值须有**非退化正例**(`Trusted ⊓ string ≠ ⊥`,否则正控全灭、P1-P3 不成立);③「假 / unknown」**分因**(假 = 违法、unknown = ③/④);④ 未定义原子**取 unknown 不得取真**。四项的规范性来源 = **S-A §3.3.1 C1-C5**,本文只引用。 +- **未判定不静默**(原则 3 + R2 §3.3 预算守卫):`ty_sub` 返回 unknown(预算耗尽 / 未覆盖面)时,**不得当通过**,按 S-A §2.2 决策树落 ③/④ 并登记(§5)。 + +**为什么不需要流敏感分析**:判据只读 `τ(v)` 与 `trust(v)` 两处标注,与 v 的**定义路径、别名、中转(laundering)无关**。把不可信字符串存进结构体字段、经函数返回、经容器搬运,都不会改变它到不了语法位这一事实——因为**唯一的入口是库的构造器**。这是形状项判据相对污点分析的结构性优势,也是 §3「if-校验旁路」不成立的根因。 + +- **该论证的前提(登记,D12)**:`trust(v)` 的传播面(S-B3 的 R1-R4 同族)与形状判据都要求「**图的边 / region 覆盖全部语义依赖**」——这是**待审计前提**,不是公理。按 **D12**,依赖完备性 = **独立侦查项**(输出「边 / region 覆盖率报告 + 缺口清单」);**覆盖率报告无缺口前**,本节论证与 S-B3 S1 一律不启动(S-B3 §6.1 前置 3 同门槛)。 + +### §1.3 构造器封闭 = 非法边的第一性来源 + +`φ` 是**库声明的**形状类型项,其 inhabited 值只能由**声明返回 φ 的构造器**产生: + +- **类型层封闭(自动获得)**:任何 `fn f(...) -> φ` 的函数体若返回非 φ 的值,被普通返回类型检查拒绝;因此「凭空造一个 `SQL`」在类型层做不到。 +- **数值层封闭(须策略给出)**:`φ` 的某个构造器若同时接受「不可信来源可满足的类型」(如 `string`),则它是**开口**——策略必须二选一:① 给它附**档位约束**(解析式位置 = 常量档,动态实参不满足,§2.2),或 ② 把它登记为净化节点(§3,带后置条件义务)。 +- **封闭性的现实缺口(已裁,2026-09-11)**:Core 今天**不强制** `pub`(`parser.cr:662` 声明 / `:697` 只写 `is_pub`,**全仓无消费**——代码级缺陷 5)⇒ 库外代码若被允许构造 `φ`,封闭性只能靠 TCB 维持。**裁决(D6(a))**:形状项 / 根声明**只在受控命名空间内有效** + **装载期校验**(S-A §3.6);「谁有权声明形状项 / 根」记入 policy 面、**对装载器可见**;**不启用 `pub` 强制**(选项 (b) 不实施)。⇒ 库外伪造 = **装载期拒绝**(不再是运行期靠约定)。 + +一句话:**「不可信数据不能到达语法位」之所以是非法边,不是因为我们追踪了它,而是因为语法位的类型里没有一条从 `string` 出发的边**——唯一存在的边(构造器)由库把守,库的边要么带常量档,要么是一处显式、可审计的净化节点。 + +### §1.4 与 `string` 的关系(零标注) + +`string` **不变**:仍是原生八员之一(R2 §1.2),不拆成 `Trusted` / `Untrusted`,既有签名零改动。不可信性不是类型,而是: + +1. 一处**推导得到的标注** `trust(v)`(S-A 轴;仅在裸位置判据里被读); +2. 一条**形状项判据**——`string ⊄ SQL`(`ty_sub` 直接给「否」)。 + +因此 §4 的渐进引入不需要动任何既有代码。 + +--- + +## §2 领域库接口约定 + +**声明总原则**:库只声明三件事——**① 未包装的语法位(若有)② 不可信来源函数集 ③ 净化节点及其后置条件**。**构造器不需要在策略里声明**:构造器由**类型**给出(返回 `φ` 的函数即构造器),这是「策略面最小」的关键(data, not syntax;路线图 §1.1 `policy` 定义)。 + +**与 S-A §3.1 的关系**:本族的这些声明就是 S-A policy 条目里 `` / `` / `` 三栏的**填法**——S-A 定栏目,本族定填法(含「sink 的语法位/值位之分」这一 S-A §3.1 已点名的 S-B1 专属字段);`decl`(净化行)在本族对应 S-A §3.1「**放行 / 声明条目**」栏的 `kind=declassify`(D2(a),填法随 S-B3 §3.1)。条目的注册走 S-A §3.5 的插件标签通道(`register_plugin_tag`),**语言零语法改动**。 + +**声明面裁决(2026-09-11,D5(a):零新语法)**:以上声明 = **policy 数据表**(S-A §3.1 条目)+ 既有插件标签通道(S-A §3.5),**不新增声明语法**、`interface` 语法**不改**。「**参数级档位约束**」(如 `sql_text(lit)` 的 `lit` 带 `κ=const`)同样落 **policy 数据**(键 = `(ns, policy_name, fn, slot)`),**不在声明位新开语法**(§6.3 前置 2)。 + +示意的声明记法(**语义示意 = 数据表字段**,非语法提案;**R2 §2.3 的 `interface` 不承载参数级约束声明**——见 §6.3): +`shape` = 形状类型项声明;`ctor` = 返回该形状项的构造器;`src` = 不可信来源;`pos` = 位置声明(含**参数级档位约束**槽)。 + +### §2.1 共同结构 + +``` +shape D # Ph = 阶段/上下文参数(本族的设计形态;参数化判定能力见 §6 依赖) +ctor ... -> D # 构造器(库函数,返回形状项即成立) +src fn_name # 返回不可信值的函数(来源) +pos fn:slot ... # 语法位声明(仅未包装 API;已包装者由参数类型承担) +decl fn -> Trusted [+ post] # 净化/declassify 节点(§3) +``` + +### §2.2 例一:SQL + +- **下游**:DB 引擎的 SQL 解析器;元字符 `;` `'` `--` `/*` 改变结构。 +- **形状项**:`SQL`,`Ph ∈ {Text, Structure, Frag, Param}`。 +- **解析式表示(文本形态)**:`sql_text(lit: string) -> SQL`,其中 `lit` 带**常量档约束项 κ=const**——即查询文本必须是编译期已知的常量(源码字面量)。动态字符串**不满足** κ ⇒ 拒绝(不是运行时验证)。对应「查询文本 = 代码,不是数据」的直觉,且复用 R2 §5.1 F11 常量档机制。 +- **拼接式表示(结构形态)**: + +``` +sql_select(cols: [SQLFrag], from: TableRef, where: Cond) -> SQL +sqlfrag(x: SQL) : SQL + SQL -> SQL +sql_param[T](v: T) -> SQL # 值位通道:不可信值从这里进 +SQL + SQL -> SQL +SQL + SQL -> SQL # 仅结构 + 结构 +``` + +- **sink**:`sql_exec(q: SQL, binds: [SQL])`。`q` 是语法位,其**声明类型即形状项** ⇒ 判据自动成立;`binds` 是值位(prepared bind,引擎按值消费)⇒ 不可信数据合法搭载。 +- **危险逃生口**:`db_raw_exec(text: string)` 这类「整串文本执行」API **不由库提供**。若某库坚持提供,则策略**必须**把该槽声明为 `pos syntax_raw(sql)`,判据退到属性面(§2.4)。 +- **来源**:`io.read_file` / `cli.argv` / `net.recv` / `os.get_env`。 +- **正/负控**:`sql_exec(sql_text("SELECT ..."), [sql_param(u)])` 合法(`u` 不可信亦可);`db_raw_exec(read_file("q.sql"))` 非法边。 + +### §2.3 例二:HTML + +- **下游**:HTML 解析器;`<` `>` `&` 引号;且**上下文敏感**(元素体 / 属性值 / URL / 内联 JS 规则各不相同)。 +- **形状项**:`HTML`,`Ctx ∈ {Text, AttrValue, URL, Elem}`(JS 上下文单列为高危子形态,默认不进白名单)。 +- **值位 = 文本节点 / 属性值**(这就是「参数化通道」的形态): + +``` +html_text(s: string) -> HTML # 转义 = 位置化(§3.1) +html_attr_value(s: string) -> HTML +html_elem(tag: Tag, attrs: [Attr], children: [HTML]) -> HTML # Tag = 闭枚举白名单 +html_parse(lit: string /*κ=const*/) -> HTML # 解析式:模板须为字面量 +``` + +- **负控**:`html_elem(div, [], [read_file("u.txt")])` ⇒ `children` 槽要求 `HTML`,实参 `string` ⇒ **非法边**。而 `html_text(read_file("u.txt"))` 合法——注入的非法性完全由「不存在 `string → HTML` 的边」表达,**唯一的边就是被声明为净化的构造器**(`html_text` 是 `string → HTML` 的唯一映射)。 +- **形态细分粒度不足的残留**:`HTML` 对 URL 上下文不安全(`javascript:` 是合法的属性值文本),需要库把 `href` 另立 `HTML` 并给出 URL 校验构造器——**粒度不足 = 库的规格错误**(§7-①)。 + +### §2.4 例三:shell(同时展示两条通道) + +- **下游**:`/bin/sh -c ` 的整行;元字符 `;` `|` `&` `$()` `` ` `` 空白分词。 +- **通道 A —— argv 形态(值位,安全)**: + +``` +sh_run(prog: Sh, args: [Sh]) # 对应 execve(2):内核不再扫描参数内容 +sh_arg(s: string) -> Sh # 值位通道,不可信值合法搭载 +``` + + 负控:`sh_run(sh_prog("ls"), [sh_arg(read_file("p.txt"))])` **合法**——这是设计意图,不是漏洞(内核不解析参数)。 + **诚实边界**:argv 值位只保证**不改变结构**,不保证**语义安全**——`sh_run(sh_prog("rm"), [sh_arg(untrusted)])` 的选项注入(`-rf /`)在本族判据**之外**(§7-附加)。 +- **通道 B —— 文本形态(语法位)**: + +``` +sh_text(lit: string /*κ=const*/) -> Sh # 解析式:整行命令须为源码字面量 +sh_quote(s: string) -> Sh # 净化节点:单引号包裹 + ' 转义 +sh_run_text(c: Sh) # 接受常量文本或已引用片段;不接受 string +Sh + Sh -> Sh # 组合仍只接引用片段 +``` + + `Sh` 与 `Sh` 的**区分**是防止「把未引用片段拼进来」的机制——形态参数在这里承担了「片段合法性」的判据。 +- **裸位置(本族唯一必须退到属性判据的地方)**:libc `system(cmd)` / `popen(cmd)`(参数是 `string`,库无法重新定型)。策略: + +``` +pos syntax_raw(sh) system:0 +pos syntax_raw(sh) popen:0 +``` + + 判据退到 `⊢ Trusted(v)`:`system("ls -l")`(常量 ⇒ `Trusted` 可证)**通过**;`system(read_file("x.sh"))` **拒绝**。这是属性轴在本族的唯一落点,也是「未包装 API 规模问题」的入口(§8-开放问题 8)。 + **现状事实**:本仓库已存在该 sink 的真实实例——`src/stdlib/os.cr` 的 `system(cmd: string) -> int`(`fork()` + `execve("/bin/sh", ["/bin/sh","-c",cmd])`),这正是 S-A §6.1 选定的首切片对象;本族切片 1 与它**互补不重叠**(§6.1)。 + +### §2.5 同一判据的第 N 个领域(非特判证据) + +`fmt`(格式串)、`ld`(路径段,与 S-B4 边界重叠,§4.8)、`http`(头值字段,CRLF 再扫描)、`json`(键名/字符串值)、`regex`(模式文本)、`ld.so`(`LD_PRELOAD`)——**同一个判据,只是 D 与 φ 不同**。编译器侧代码路径**零增长**:新增领域 = 库新增一份策略数据 + 形状项声明。这是「不做成特判」的可检验表述(§6 判据 L4)。 + +--- + +## §3 净化(sanitize)与 declassify 的语义 + +### §3.1 净化 = 位置化,不是「清洗」 + +库的净化构造器**不做**「从字符串里删掉危险子串」的语义,而是做**位置化**:把不可信值放进下游解释器**当数据消费**的位置,并把这个事实写进**返回类型项**。 + +- `html_text(u) -> HTML`:不是「u 被清洗了」,而是「u 被放进了元素体文本节点这个值位」; +- `sh_quote(u) -> Sh`:不是「u 里的 `;` 被删了」,而是「u 被放进单引号片段,shell 分词器到此不再切分」; +- `sql_param(u) -> SQL`:不是「u 被转义」,而是「u 走 bind 协议进引擎的值槽」。 + +这个重述有实质后果:**净化的正确性条件 = 位置声明的正确性**(§1.1 的再扫描判据),而不是「正则写得对不对」。HTML 的例子说明它并未消除全部风险:位置声明的**粒度**决定精度(属性值 vs URL vs JS)。 + +### §3.2 declassify 节点:显式、可审计、产生义务 + +`declassify` 是共享契约定义的唯一合法通道(`Secret → Public` 同构地 `Untrusted → Trusted`)。本族取定三条约束: + +1. **必须显式成节点**:只存在两类合法通道——(a) 类型变化的位置化构造器(§3.1);(b) 同型放行的 `declassify`(如 `sql_ident_checked(s: string) -> Trusted`,库声明后置条件 `is_ident(s)`)。二者都在图上是一个**节点**,不是一处标注、不是一个 pragma。 +2. **可审计**:节点进 S-A §5.2 的**三面可见**(诊断面 / 义务台账面 / 产物面)+ `core explain` 决策通道(S-D);「这个程序的不可信数据从哪进、经哪放行、到哪止步」可被一条命令答出。净化节点在台账中的记录字段 = S-A §5.2b 的义务项(轴 / 源 / 汇 / 落档 / 状态 / 图→源码映射)。 +3. **产生义务而不是免单**:`declassify` 声明的是**库的后置条件**,义务是「证明该后置条件成立」——落 proof-required(VC 由验证切片消费;库体本身可作 VC 目标)。**绝不**因为「它是库给的」而当通过(原则 3;「库自己写错 sanitize」是 §7-② 的不可消残留——本族能做的是把它变成一笔**显式记在账上的债**,不是消灭它)。 + +### §3.3 不允许隐式 declassify(if-校验旁路) + +**控制流上的校验不构成净化**: + +``` +if is_valid(s) { sh_run_text(sh_text(s)) } // s 仍是 string,不是 Sh ⇒ 仍拒 +``` + +这正是本族相对经典污点分析的**结构性差异**:判据在类型项上,不在控制流上,因此不存在「校验函数没被建模 / 校验的是另一个东西 / 校验被短路」这一类旁路。若库希望让某个校验器真正放行,唯一途径是把它声明为**净化节点**(显式、可审计、带后置条件义务),而校验器若是**可能证不出**的谓词(如「完整 SQL 语法正确」),义务落 proof-required 而非 prove——**这正是义务阶梯的正确使用**,不是本族的失败。 + +### §3.4 净化节点与位置种类的判定边界 + +净化节点产出的是**新类型项**,不是新属性:`html_text: string → HTML` 成立的原因是**返回类型**,与 `trust` 标注无关(哪怕输入 `Trusted`,也仍是 `HTML`)。属性判据(裸位置)里没有构造器可用,因此那条路径上**没有**「位置化」的出口——只有 `declassify` 或改用已包装 API。这条不对称是刻意的:**类型判据优先,属性判据是退路**(§0 裁决 1)。 + +--- + +## §4 与既有机制的关系 + +### 4.1 与类型系统(R2):同一个引擎,无第二套判定 + +- 位置形状项 `φ` = 类型项(R2 §1.2 `k(τ…)` / `AK_NAMED` 参数化命名);判据 = `ty_sub`(R2 §3.2); +- 形状满足 = R2 §2.5 `iface_satisfies` 同入口; +- 失败诊断 = `ty_witness` 给反例值(R2 §3.2/§7); +- 常量档 κ = R2 §5.1 F11 三档约束项(先例:数组长度约束,P2a 已实施)。 +- **不新增判定体系**(原则 4):本族的全部判定是「把策略编译成形状项/约束,送进引擎」。 + +### 4.2 `string` 现状零标注 → 渐进引入(不破坏既有代码) + +| 档 | 状态 | 判据 | +|---|---|---| +| 0 · 现状 | 无策略、无声明 ⇒ **无义务**(§0 裁决 9);本族代码路径不进入 | 产物 **byte-identical**(同 R1/R2 「零回归锚」) | +| 1 · 审计 | 加载库策略;只报不拒(`--policy-audit` 式通道,默认关,两态产物逐字节同——**沿用审计通道先例**:现行载体 = `--verify-named-dedup` / `--verify-evp-nodes`(默认关的校验通道);迁移期 `--type-shadow` 已随 R2 P5 Task 5 下线——R2 P6 Task 5 勘误) | 审计清单产出;产物同 | +| 2 · 库级强制 | 策略声明语法位即拒绝;默认模式 | 正控绿 + 负控拒绝 + 回归全绿 | +| 3 · 高安全 | unknown/证不出 = 拒绝;`unsafe`/FFI 面须显式证明 | 未覆盖面登记为零静默 | + +`string` 本身、既有函数签名、既有库调用**在四档中均不变**——变的是「是否存在声明的语法位」与「未处置义务的落法」。 + +### 4.3 与 S-A(属性轴 + 四档) + +本族是 S-A 的**第一个消费者**,只消费不定义:用 `trust` 轴(裸位置判据)、四档决策树(§5)、policy 条目(§2.1)、诊断/台账通道(§3.2)。对齐锚点:S-A §1.1(`trust`:`untrusted ⊑ trusted`、来源 = 库声明 + 自动推导 + 净化节点)、§2.2(决策树与「违法 ≠ ④ reject」)、§3.1/§3.3(policy 条目与 `ty_sub(τ ⊓ A_a(v), Shape)`)、§4.3(enforce 失败语义)、§5.2(三面可见)。**S-A 收敛批 v2 已落盘(2026-09-11)**:接口不再暂行——本族已按其 §1.1 / §2.2 + 附 / §3.1 / §3.3.1 / §3.6 / §5.2 逐处对齐(随动记录见 §8.2-9)。 + +### 4.4 与 where 三档(F11) + +| F11 档 | 本族用法 | +|---|---| +| 常量档 | **解析式语法位**:文本形态(`sql_text`/`html_parse`/`sh_text`)要求实参编译期已知;恒假 ⇒ 编译错 | +| 符号档 | 位置相关的符号约束(如「表名来自白名单变量」)⇒ VC 义务(proof-required) | +| 动态档 | 允许落 enforce 的唯一场合:库提供**全函数校验器**(如标识符字符集 + 长度),校验失败 ⇒ **拒绝执行**(§5) | + +### 4.5 与判定引擎的 unknown/预算 + +`ty_sub` 的 unknown(R2 §3.3 预算守卫 / P0 未覆盖面)在本族**不得当通过**:按 S-A §2.2 决策树落 ③ proof-required 或 ④ reject(strict),并**显式登记未覆盖面**。这与「不与类型不兼容混淆」一致——诊断要区分「确定不满足 ⇒ **违法**」(真非法边)与「未判定 ⇒ 未处置」(证据不足)。 + +### 4.6 与 S-B3(信息流族)的轴区分(**2026-09-11 裁决 D1(a) 收敛**) + +本族消费 **`trust`**(S-A §1.1:不可信性 / 传播轴,**可判**);S-B3 消费 **`secrecy`**(机密性方向)**并供给 `trust` 的传播面**(R1-R4)。**两者是不同轴**(一个防「坏结构进来」,一个防「秘密出去」),判据方向相反、残留不同(S-B3 的隐式流在这里没有对应物:本族对控制流免疫,§3.3)。 + +**谁定义 / 谁判定 / 谁是消费者(D1 共同要求,与 S-A §1.1 / S-B3 §1.1 同表)**:**定义** = S-A §1.1;**判定** = 引擎(S-A §3.3 单式,三态);**消费者** = 本族 `syntax_raw` 裸位置判据(§2.4)+ S-A §6 切片 + S-B3 传播面 + S-B4(不可信路径数据)。 + +- **原稿的错误表述已修(A 线 C1 ⇒ D1(a))**:本文旧版称「本族消费 `trust`(不可信度,**完整性**方向)」且「本文不定义 `integrity` 轴用法,留给 S-B3/S-B4」——**`integrity`(S-A §1.3 = 可篡改性)与 `trust` 对偶但不同轴**,其**首消费方是 S-B2 / S-B5**(S-A §1.3),不是本文。本文与 S-B3 判的都是 **`trust` 轴**(S-B3 判其传播,本文判其到达 `required = Trusted` 的端口),**不是两套判定面**。 +- S-B3 §4.2 的「S-B1 = 完整性轴实例化」表述已随本裁决改为「**S-B1 的裸位置判据消费 `trust` 轴(S-A 供给)**」——两侧口径现已一致。 + +### 4.7 与 S-B2(授权族)的边界 + +「能不能执行 SQL」= 授权(S-B2,能力路径判定);「文本能不能进 SQL 语法位」= 本族。二者正交:一次 `sql_exec` 可以「授权完备但非法边」(不可信文本 + 非法结构),也可以「文本合法但未授权」。**不得**用本族判据替代授权,反之亦然。 + +### 4.8 与 S-B4(围栏族)的边界 + +路径段既是**语法位**(`../` 改变路径结构)又是**围栏**(逃出 root)。本族判据说「不可信字节不能拼进路径语法位」;S-B4 说「无法构造指向 root 外的 capability」。`Path` 与 `φ` 的机制同族(形状项),边界 = 谁声明:路径**结构**的语法正确性归本族形态,**根约束**归 S-B4。重叠点登记为 S-B4 的输入(§8-开放问题 7)。 + +### 4.9 与载体(.ccr v7)的关系 + +形状项与位置声明**随库分发**才有意义(否则跨模块/预编译库丢失策略)。v7 段表已预留 `7+`(驱逐标注/证书段)——**是否新增 `POLICY` 段**、策略随 `.ccr` 走还是随源文件走,是实施批议题(§8-开放问题 5);本族只要求「策略是数据、可随库携带、可被审计」。 + +--- + +## §5 义务落档 + +**本族主要在 ① prove / 违法**(对应路线图 S-B1 行「可证通过 / 拒绝」的主判);unknown 面落 ③/④(S-A §2.2 决策树);enforce 是受限退路;proof-required 只在净化后置条件与符号档处出现。 + +**术语前置(S-A §2.2 的两点关键区分,逐字沿用)**:「**违法**」= 引擎判定为**假**(已判定为不合法 ⇒ 编译错误,**任何 policy 档位都拒绝**);「**④ reject**」= **无法处置**(判不出 / 不可强制 / 又不满足 strict policy)。本文表格严格按此分栏——**不要把「违法」写成「reject」**。 + +**默认强度分层(2026-09-11 裁决 D3(c),规范形态见 S-A §2.2 附)**:① **② enforce 默认启用**(本族 = 库提供全函数校验器的场合,可 enforce 一律 enforce);② **高安全档关闭 enforce 通道** ⇒ 该场合落 ④;③ **unknown 面**(引擎预算 / 未覆盖面)默认 **③ 登记 + 继续编译**(**不拒编**)、strict 档拒编;④ **已声明域内声明不完整**取 **`unknown`**(**2026-09-11 收敛批 v3 按 A 线 N1 限定**):仅当策略**已声明**该 sink / 该位置(进入本族声明域)而**声明缺项**时(如声明了语法位但未给 `Shape` / 未给档位约束 κ),取保守判定方向 `unknown`——**不是"没有义务"**,并**在报告面可见**;strict 档拒编。**未进入任何策略声明域的位置仍不产生判定**——§0.2 裁决 9「**无声明 = 无义务**」对**声明域之外**的语法位 / 值位**不变**(既有程序零影响、CI 门槛可达);本项**不得**读作「策略一装载 ⇒ 所有未声明槽即产生 ③ 登记」(那会与裁决 9 冲突,且与 §5 表「值位实参 → 无义务(`Shape=⊤`)」自相矛盾)。**源侧口径保留**:未声明的**边界条目** = `unknown`(S-B3 §1.3① / S-B2 §2.2 同款,D3(c) 原文覆盖面)。⑤ **CI 门槛 = 严**(strict 档运行)。 + +| 情形 | 落档 | 依据 / 落法 | +|---|---|---| +| 值位实参 | — | 无义务(`Shape(π)=⊤`,恒真) | +| `ty_sub(τ(v) ⊓ A_trust(v), φ)` = 真 | **① prove** | 零成本,不插入任何检查 | +| 常量档解析位 + 实参为编译期常量 | **① prove** | 档位判定恒真 | +| 常量档解析位 + 实参非常量 | **违法** | 档位判定恒假(不是运行时验证) | +| 语法位 + 实参项不满足 φ(引擎判**假**) | **违法** | 本族主判(非法边);任何档位都拒绝;唯一放行 = §3 的净化节点(放行本身登记为义务) | +| 语法位 + 引擎 **unknown**(预算/未覆盖面) | **③ proof-required**(policy 允许)/ **④ reject**(strict) | **不得当通过**(原则 3);登记未覆盖面(S-A §5.1 状态机) | +| 裸位置 + `⊢ Trusted(v)` 不可证(unknown) | **③ proof-required**(policy 允许)/ **④ reject**(strict);若库提供全函数校验器且 policy 允许 ⇒ **② enforce** | enforce 失败语义见下 | +| 净化节点(`declassify` / 位置化构造器)的后置条件 | **③ proof-required** | VC 由验证切片消费;库体可作 VC 目标;证不出不静默(§3.2) | +| 符号档位置约束 | **③ proof-required** | 同 F11 符号档(S-A §2.3) | +| 库提供全函数校验器(标识符字符集、长度、白名单成员) | **② enforce** | 插入检查节点;失败语义按 S-A §4.3(默认 **abort** = 硬陷阱) | + +**enforce 的失败语义(与 S-A §4.3 对齐后的精确表述)**:默认 = **abort**(硬陷阱,`IR_BOUNDS_CHECK` 先例);S-A 允许 policy 显式声明 `degrade` / `report`,但本族**收紧一格**: + +- `degrade` 在本族**只允许**把降级动作指向**库的净化构造器**(§3.1 位置化),即「校验失败 ⇒ 改走 `html_text`/`sh_quote` 这类声明过的节点」。理由:转义的正确性是**库的规格**(§7-①②);若允许应用侧自写转义,等于把策略面偷偷搬回语言面(原则 2),并制造一个**没有台账条目的隐式放行**(违反原则 3)。 +- 因此本族 enforce **不是**「变形器」,而是**闸门**或**闸门 + 库声明的净化节点**;`degrade` 目标须是策略 `declassify 通道`栏里的具名节点,且**记入台账**。 + +--- + +## §6 最小可实施切片 + +**切片原则**:切到「一个领域、一个形状项、三个构造器、一条判据、一组正负控」——不引入属性推导、不引入参数化判定、不引入 VC。 + +### 6.1 切片 1(本族首切片,判据完备可验收) + +| 项 | 内容 | +|---|---| +| 领域 | `sql`(单领域) | +| 形状项 | 单一形态 `SQL`(**非参数化**,规避 R2 P3 参数化变型规则的未实施面) | +| 构造器 | `sql_text(lit /*κ=const*/) -> SQL`、`sql_param[T](v: T) -> SQL`、`SQL + SQL -> SQL` | +| sink | `sql_exec(q: SQL, binds: [SQL])`(槽 0 = 语法位,由**声明类型**承担) | +| 来源 | `io.read_file` / `cli.argv`(策略数据声明;本切片只需登记,供诊断文案与台账) | +| 判据 | `ty_sub(τ(实参), SQL)` + 常量档 κ;**仅此**(无属性判据) | +| 挂点 | 调用实参槽(checker 调用检查处)→ 引擎判定 → 诊断/台账 | +| 诊断码 | 建议新段 `EC_SF_*`(**段号与命名归 S-A 统一分配**);至少区分:非法边(真不满足)/ 常量档未满足 / 未判定(证据不足,保守拒绝) | +| 依赖 | R2 P0 引擎(**已就位**);形状项建表(`AK_NAMED` 具名项);**常量档通用化两步(§6.3,含参数级档位约束声明面 = policy 数据,D5)**;S-A 的 policy 条目注册 / 诊断台账通道 / 装载期校验(S-A §3.1/§3.5/§3.6/§5.2——**共用,不另立**);**依赖完备性审计报告「无缺口」(D12)** | +| **不依赖** | 属性标注 / 属性传播(= S-A §6 切片所交付的面;本切片只走形状项判据)、参数化原子判定(R2 P3)、VC(验证切片)、**R2 P2b 接线**(`iface_ops`/`res_type_node` 合一——只用 P0 已实施的引擎 API)。**注**:「不依赖 P2b」≠「无硬前置」——硬前置见 §6.3 的**显式阻断项**(A 线评审 I5) | + +**与 S-A §6 切片的关系(互补不重叠)**:S-A 首切片 = `untrusted ↛ system(cmd)` 的**裸位置 + 属性判据**路线(`os.cr` 的 `system`),交付的是轴标注/传播/义务流程;本切片 = **形状项 + 常量档判据**路线(`SQL` 类型化构造器),交付的是领域库接口与构造器封闭。二者判据互补(属性判据 vs 类型判据),切片 1 **即使 S-A 的属性传播未就绪也可独立验收**;按路线图 §3.2 节奏,S-A 切片先行并落地共用通道,本族切片 1 随后挂上。 + +**正控(须通过)**: +- P1 `sql_exec(sql_text("SELECT 1"), [])` —— 常量档满足。 +- P2 `sql_exec(sql_text("SELECT * FROM t WHERE x = ?"), [sql_param(read_file("u"))])` —— 不可信值走值位(bind),**这正是要保护的合法路径**。 +- P3 `sql_exec(sql_text("..."), [sql_param(42)])` —— 类型参数实例化。 + +**负控(须判「违法」= 编译错误,且诊断为「语法位非法边」而非普通类型错)**: +- N1 `sql_exec(read_file("q.sql"), [])` —— 不可信 `string` 直落语法位。 +- N2 `sql_exec(sql_text(read_file("q.sql")), [])` —— 常量档未满足(动态文本伪装成查询文本)。 +- N3 `sql_exec(sql_concat(sql_text("SELECT * FROM "), some_runtime_string), [])` —— 拼接路径试图引入 `string`(`+` 无 `SQL + string` 重载 ⇒ 类型层无合法边)。 +- N4 `sql_exec` 的实参是 unknown 判定(触发预算/未覆盖面)⇒ 落 ③/④ 并登记(**不得通过**)。 +- N5 **属性项展开陷阱负控 + C5 两控**(§1.2 注记;S-A §3.3.1 C1-C5):① 构造一个「不可信值 + 值项 ⊓ 否定属性项」的组合,断言判定**不**因 `⊥ ⊑ φ` 而放行;② **右项 ⊤ 负控**:把 `Shape(π)` 声明为 `⊤` ⇒ 断言**装载期拒装**;③ **非退化正控**:`Trusted ⊓ string ≠ ⊥` 的正例必须通过(否则 P1-P3 全灭)。 +- **联合用例(2026-09-11 收敛 M8)**:`untrusted → concat → SQL 语法位` 的**单条诊断**断言由 **S-B3 N5** 承载(语法位 sink 的 `required` **由本族声明、S-B3 只消费**)——两族**不得各报一条**;本族侧只需保证 `required` 数据存在且可被消费。 + +**判据(验收门;编号 `L1`–`L5` = 本 spec 本地判据号——2026-09-11 收敛批 v3 由 `D1`–`D5` 改名,避免与用户裁决 `★D` 撞号,见路线图 §4)**: +- **L1 零回归锚**:未加载策略时,全部产物 **byte-identical**(对齐 R1/R2 判据)。 +- **L2 负控零漏**:N1–N4 全部拒绝,且错误码与文案落在语法位判据段(不是「类型不匹配」误报)。 +- **L3 正控零误报**:P1–P3 全部通过;P2 的不可信值不产生任何附加检查代码。 +- **L4 无特判**:编译器侧**不存在** `sql`/`html`/`sh` 标识符(grep 判据);新增第二领域只需新增策略数据 + 形状项声明,代码零增长。 +- **L5 链路**:按 **R2 §4 每步共同判据行**(`2026-09-10-type-interface-unification-design.md:155` = 全套件 + `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 rc=42)执行——**套件计数以该行为唯一真源,本文不重述数字**(A 线评审 M1)。 + +### 6.2 切片 2(登记,不在本族首切片) + +按依赖顺序:① 属性轴 + `trust` 推导(S-A)⇒ 解锁 `syntax_raw` 裸位置(`system`/`popen`);② F11 常量档通用化 ⇒ 解锁解析式位置的一般形态;③ R2 P3 参数化变型 ⇒ 解锁 `SQL`/`HTML` 形态细分;④ 验证切片 ⇒ 解锁净化后置条件的 proof-required。**切片 1 的判据不因切片 2 而修改**(累加不替换)。 + +### 6.3 切片 1 的已知前置缺口(诚实登记) + +- **常量档通用化 —— 拆两步 + 显式阻断项(2026-09-11 A 线评审 I5 + 裁决 D5(a))**: + 1. **判定侧 = F11 通用化**:F11 三档目前只在数组长度上实施(R2 §5.1 / P2a Task 2,`type_compat_strict` 显式补检);把它推广为「**任意参数**携带档位约束项」的**判定面**是本切片的硬前置。 + 2. **载体侧 = 参数级档位约束声明面**:「在 `sql_text(lit: string)` 上怎么标 `κ=const`」——**不新增声明语法**(**D5(a)**):落 **policy 数据表**(键 = `(ns, policy_name, fn, slot)`;S-A §3.1 条目的 sink 声明集 / 形状项数据),由**装载器校验**(S-A §3.6)。R2 §2.3 的 `interface` 语法**不承载**参数级约束(那是类型面契约,不是档位数据)——旧稿把它推给「interface 重定义」是**错认承载面**。 + - **显式阻断项(规范性)**:上述两步(**尤其 2**)未就位前,**判据 L2 的 N2(常量档未满足)不可验收、切片 1 不得宣布完成**;实施计划必须把该前置列为**显式阻断项**,不得以「实施批定」带过。 +- **形状项建表**:`AK_NAMED` 今天**不展开**(R2 P0 未覆盖面 ②),同形判等可用——切片 1 正是靠这一点(`string ⊄ SQL` 稳定成立、`SQL ⊑ SQL` 同形成立);**不得**在切片 1 依赖参数化项之间的包含关系。 +- **库内可见性 / 形状项声明权(已裁,D6(a))**:`pub` 不强制(§1.3);切片 1 的封闭性 = 「类型层封闭 + **装载期校验**(受控命名空间 + 声明权登记,S-A §3.6)」,须在实施批按 S-A §3.6 记录(§8.2-1 已闭合)。 + +--- + +## §7 不承诺 + +路线图 §1.3 的三类**不可消**残留,逐条说明本族落在哪类: + +1. **错误 specification**——Core 只能证明「程序忠实实现了规则」,不能证明规则本身正确。本族中**规则** = 库声明的「语法位集合 + 形状项 + 常量档 + 净化后置条件」。库可以把一个值位误声明为语法位(过度拒绝),也可以把语法位漏声明为值位(**漏保护**),或者把 `HTML` 用在不安全的上下文中(§2.3 粒度不足)。**本族不可能消除错误 specification**:它是策略面的残留,不是语言能力不足。缓解 = 声明随库分发、可审计、可对照 §1.1 再扫描判据逐条自查。 +2. **恶意/失陷外部实体**——供应链、编译机、密钥、硬件后门;用可复现构建/签名/provenance/最小权限**缩小**,不能用语言语义**证明**。本族中对应的具体形态是**库的实现**(TCB):DB 驱动、HTML 转义函数、`sh_quote`、FFI 边界之后的代码。**本族不可能消除「库自己写错 sanitize」**——本族能做的是:① 让每一处放行**显式成节点**(可清点、可审计、进台账);② 把净化正确性变成一笔**记在账上的义务**(§3.2,证明方式可后置);③ 不把「库说了算」伪装成「语言证明了」。 +3. **需求本身允许危险行为**——shell/browser/kernel/JIT 必须执行外来代码;目标不是禁止能力,而是**证明隔离边界**。本族中:程序**必须**接受不可信输入并**必须**把它交给解释器(否则程序无用);本族证明的正是那条边界——「不可信值以**数据**身份到达解释器,且结构只能由**库的构造器**产生」。这与「shell 安全」不是一回事(§2.4 选项注入)。 + +**附加诚实边界(本族特有,不属上述三类但必须写在这里)**: + +- **值位 ≠ 语义安全**:argv 值位只保证不改变结构,不保证参数语义无害(`rm -rf` 的选项注入;`--` 终结符约定)。本族判据**不覆盖**参数语义混淆。 +- **编码层混淆不在判据内**:非法 UTF-8 序列、Unicode 规范化/同形字、大小写折叠(某些下游解释器不区分大小写)——这些改变的是**字节解释**,不是结构再扫描;归库的构造器(规范化后置条件)与 S-B3 之外的编码层议题。 +- **未覆盖面**:引擎 unknown/预算耗尽会产生「证不出」;本族的处置是保守拒绝并登记(§5),**不承诺**「所有程序都能判定」。 +- **本族的判定不替代任何下游安全机制**(授权、围栏、信息流、密码学):见 §4.6–§4.8。 + +--- + +## §8 不做项 + 开放问题 + +### 8.1 明确不做(YAGNI) + +1. **不做语言内建的 sink/source/领域关键字或漏洞类别特判**(原则 2 硬性;判据 L4 可检验)。编译器不知道 `sql`/`html`/`sh` 这些字符串是领域标识。 +2. **不做完整污点分析作为主判据**:主判据是形状项包含(§1.3);属性推导只在裸位置路径上,且属 S-A 的轴基础设施。 +3. **不做转义正确性证明**:库的事(§3.1/§7-②)。 +4. **不做流敏感/别名分析**:不需要(§1.2 末)。 +5. **不做各语言语法模型内建**(元字符表、SQL 文法、HTML 上下文规则)——全部归库。 +6. **不做运行时 escaping 作为默认落法**(§5 末)。 +7. **不在本族定义 `integrity`/`secrecy`/`authority` 轴的用法**(归 S-B2/S-B3/S-B4)。 +8. **不改 Graph 本体、不加关键字、不动命令式下限**(原则 1/5、定案 14)。 + +### 8.2 开放问题 + +1. **构造器封闭性的强制手段** —— **已裁(D6(a),2026-09-11)**:装载期校验 + policy 授权(受控命名空间 + 声明权登记,S-A §3.6);「类型层封闭 + 库实现为 TCB」仍成立但**不再是唯一防线**;选项 (b)(`pub` 强制 / 库内可见性)**不实施**。见 §1.3。 +2. **属性项的展开约定** —— **已闭合(2026-09-11)**:S-A §3.3.1 已交付**规范性约定 C1-C5**(含 C5 非退化性,D11:右项非 ⊤ / 正例非 ⊥ / 假·unknown 分因 / 未定义原子取 unknown)与负控要求;本族 N5 沿用为对应控(已扩为三控,§6.1)。**勿二次设计**。 +3. **F11 常量档的通用化范围** —— **载体侧已裁(D5(a))**:参数级档位约束声明面 = **policy 数据表**(零新语法,§2/§6.3);**剩余 = 判定侧的改动面与判据**(切片 1 硬前置,§6.3 显式阻断项)。 +4. **隐式 declassify 的口子** —— **维持「不允许」(D2(a) 口径,2026-09-11)**:唯一放行 = S-A §3.1「放行 / 声明条目」的 `kind=declassify` 条目(**默认形态 = 显式节点**)。若将来要以 predicate-refinement(`where` 通道 + VC)充当放行,它必须作为该条目的**另一种实现形态显式登记**(豁免 + 理由 / 派生源 / 义务引用 + 台账),**不得**作为隐式旁路存在(§3.3 不变)。 +5. **策略的携带形态** —— **已裁(D4(a),2026-09-11)**:**随 `.ccr` 的 `POLICY` 段**(v7 `7+` 段位已预留),**随 R2 P4 载体批同波**;**策略随产物分发**(跨预编译库分发 = 策略随库);「随源 / 独立策略文件」不作为目标形态(过渡期可作编译输入)。段布局实施批定,S-A §3.4 为准。 +6. **跨领域组合语义**:一个领域的输出成为另一个领域的输入(`sql_result → html_text`、`html → sql_param`)——位置化结果的类型项如何参与后续判据?是否存在「中转即洗白」的错觉?(预判:类型项携带形态参数即可表达,但需形态细分的配合。) +7. **与 S-B4 的路径重叠**:路径段既是语法位又是围栏;`Path` 与 `φ` 共用形状项机制,需要一条统一的分工判据(归 S-B4 输入)。 +8. **未包装 API 的规模问题**:C 库/AOT/动态链接场景下裸位置数量大,策略维护成本高;是否允许(在审计档下)自动从符号/类型推断候选裸位置并生成清单供人工裁决——这仍属「策略数据生成」,不违反原则 2,但需要论证不产生「自动特判」。 +9. **S-A 对齐余项**:本文已按 S-A **收敛批 v2 → v3** 对齐(状态行 + §1.2/§2.1/§3.2/§4.3/§4.6/§5/§6.3 锚点;D1(a)/D2(a)/D5(a)/D6(a)/D11 已随动;**v3 = §5 的 ④ 按 A 线 N1 限定为「已声明域内声明不完整 ⇒ unknown」,与 §0.2 裁决 9「无声明 = 无义务」并存不冲突;编号对照表见路线图 §4**)。**仍待定(归实施批 / S-A 统一分配)**:`Trusted` 判定入口名、policy 装载器实现面(S-A §3.6 已定语义)、义务台账**格式**(归 S-D 线,D29)、错误码段分配(本文 §6.1 建议的 `EC_SF_*` 段号待 S-A 统一)。 + +--- + +## §9 自检记录 + +- **共享契约合规**:§1.1 术语(属性轴/义务/四档/policy/declassify/sink-source)逐字沿用且未重定义;§1.2 五原则逐条落到 §0 裁决表(原则 1 → 裁决 8;原则 2 → 裁决 2/7 + §2 + D4;原则 3 → 裁决 5 + §5「不得当通过」;原则 4 → 裁决 1 + §4.1;原则 5 → §8.1-8);§1.3 三类残留逐条对应 §7-1/2/3。 +- **S-A 对齐(收敛批 v2,2026-09-11)**:锚点——§1.2 判定接法(S-A §3.3 单式 + **C1-C5**:⊥ 陷阱 **+ 右项非退化 / 正例非 ⊥ / 假·unknown 分因**)/ §2.1 policy 条目三栏填法(S-A §3.1、§3.5)+ **放行·声明条目 schema(D2)** / §3.2 三面可见(S-A §5.2)/ §4.6 轴对表(**D1(a)**)/ §5 「违法 ≠ ④ reject」+ **默认强度分层(D3)** + enforce 失败语义(S-A §2.2、§2.2 附、§4.3)/ §6.1 与 S-A 首切片互补不重叠。**跨 spec 风险已闭合**:`τ ⊓ A_a(v)` 的否定展开架空洞 → S-A §3.3.1 C1-C5(本族 N5 为对应控,§8.2-2 标记已闭合)。**新登记前提**:流敏感性论证依赖**依赖完备性审计**(D12,§1.2 末)。 +- **禁令自检**:无任何漏洞类别内建特判(§2 的三例全部由库声明;D4 给出可检验判据);无关键字新增;无 Graph 本体改动;未修改本文之外的任何文件。 +- **设计与待论证分离**:设计取定见 §0 裁决表;待论证项集中在 §8.2(封闭性强制的论证、属性项展开约定、常量档通用化、载体形态、S-A 五问随动)。 +- **切片小**:切片 1 = 单领域 + 非参数化形状项 + 3 构造器 + 1 sink + 9 条正负控;显式列出**不依赖**项(属性推导/参数化判定/VC)与硬前置(常量档通用化)。 +- **占位符扫描**:无 TBD/TODO;「暂行/待论证/归实施批」均已在 §8.2 登记为开放问题,非设计缺口。 diff --git a/docs/superpowers/specs/2026-09-11-policy-resource-confinement-design.md b/docs/superpowers/specs/2026-09-11-policy-resource-confinement-design.md new file mode 100644 index 00000000..bd1157cc --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-policy-resource-confinement-design.md @@ -0,0 +1,318 @@ +# S-B4:路径/资源围栏族(resource confinement) + +日期:2026-09-11 +状态:**设计起草(2026-09-11 收敛批 v3)**——论题/裁决/判定形状/切片定稿;**D6(a)/D12/D22/D30 已随动**,§7.2 Q1-Q9 全部给裁定;实现计划后立(承路线图 §3 节奏:S-B 各族在 S-A 定稿后逐族出实施计划)。**收敛批 v3 变更**:本地裁决编号 `D-1…D-7` → **`L1…L7`**、**层名 `L-1`/`L-2` → `LV-1`/`LV-2`**(消除与本地裁决号的同形混淆——B 线 N2;对照表见路线图 §4)· §3 的 S-B5 状态行更新(A 线 N4)· Q9 栏位改 `kind=mint`(B 线 N2) +性质:路线图 §2 S-B4 行的展开。本文只填本族范围,**不重定义**共享契约的 §1.1 术语 / §1.2 原则 / §1.3 不承诺(逐字遵守)。 +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**——术语/原则/不承诺的唯一真源) +- `docs/pointer-model.md`(provenance / asp / 条目标识+偏移;本族与之同构) +- `docs/memory-model-capability-lattice.md` v4(能力成分 ⟨身份符号, 授权集, 域约束, 派生源⟩ / 能力树 / M1 定论 / 无格承诺) +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(定案 1/4/5/8/14) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2 引擎 = 判定器地基:`ty_sub`/`ty_equiv`/类型项表/接口注册表) +- **依赖 S-A**:`2026-09-11-semantic-safety-obligations-design.md`(属性轴 + 四档义务 + policy 接口)——**已落盘(2026-09-11 收敛批 v2)**。本文已按其 §2.2 附(默认强度 ★D3)/ §3.1(**放行·声明条目 schema,D2**)/ §3.6(**装载期校验 + 声明权,D6**)/ §4.3 对齐;§7.2 的依赖登记随之收敛(Q2 已裁)。 +- **依赖 S-B5**:`2026-09-11-policy-state-version-toctou-design.md`(**收敛批 v2/v3,已对齐**——A 线 N4 修正)——衔接面见 §3。 + +--- + +## 0. 范围与裁决 + +### 0.1 论题 + +路径遍历(`../../etc/passwd`)在既有语言里是**危险字符串**:安全性依赖"每个程序员在每个调用点都记得检查"。本族的论题(承路线图 §0 的机制): + +> 把目标资源从"字符串可达"改为"**无法构造目标 capability**"——路径值携带 provenance/capability,派生只能落在 capability 定义的 namespace 内。 + +判据形态:不是"我们拒绝了 `..`",而是"**`..` 没有对应的构造**"——攻击面从"检查是否遗漏"变成"类型/图上是否存在这条边"。 + +### 0.2 本族覆盖 / 不覆盖 + +| | 范围 | +|---|---| +| **覆盖** | 路径值的**定域构造**(根身份 + 派生)、namespace 成员性判定、围栏的义务落档、与 handle 化/状态版本族的接力点 | +| **不覆盖** | TOCTOU / check-use 分离(**S-B5**)、注入语法位(**S-B1**)、`Secret ↛ Public`(**S-B3**)、`Can(a,o,r)` 授权判定本体(**S-B2**——本族只**供给** capability 与派生链) | + +### 0.3 裁决清单(本族的定案) + +- **L1 围栏 = 库类型 + 图派生链**,不加关键字、不改 Graph 本体、不建第二套数据结构(承契约 §1.2-1/-2、定案 1/4)。用户面 = 类型化构造器(同 SQL/HTML/shell 的领域库纪律);**不做字符串消毒/黑名单筛查**(那是 lint 换皮,契约 §1.2-2 明文禁止)。 +- **L2 `..` 在派生处一律非法,不做消解尝试**。理由 = **局部性**:单组件判定不需要知道路径前缀、不需要文件系统状态、不引入规范化算法(编码/多次编码/尾随字符是经典绕过面)。备选 L2b(允许 `..` 但要求消解后仍在根内)显式登记、**不采用**,见 §2.3。 +- **L3 围栏的语义内容 = (根身份, 组件序列)**,与底层路径字符串表示无关(同 pointer-model「地址 = 映射」:字节地址从来不是语义对象)。 +- **L4 唯一信任点 = 根声明入口**(声明式进图,同构 `alloc_at` / `unsafe` 边界);`string → Path` **无任何公开转换**。宿主绝对路径只允许出现在根声明处,且全程序可枚举(可审计面)。**声明权(2026-09-11 裁决 D6(a))**:根声明只在**受控命名空间内有效** + **装载期校验**;「谁有权声明根」记入 policy 面、对装载器可见(S-A §3.6)——不是空白(§7.2 Q2 已裁)。 +- **L5 语义层只判 LV-1(构造合法性);LV-2(解析身份:symlink/mount/hardlink)显式登记为映射层义务**,只能由 handle 化强制(接 §3)。**LV-1 通过不蕴含 LV-2 安全**——本族不得含糊带过此条。 +- **L6 类型参数 = 根身份(稳定,不随派生变化);细化(收窄到子树)走图上的派生链,不走类型子类型**。判定归 S-B2 的 capability path(本族只提供证据),从而**不依赖** R2 P3 的参数化变型规则,也不与 S-B2 重复判定。 +- **L7 消费的属性轴 = `authority`(授权,与 S-B2 边界)+ `trust`(不可信名字来源,与 S-B1 边界);本族不新增属性轴**(轴的定义是 S-A 的事)。本族的 policy 数据 = **根声明表**(哪些根存在、各自命名空间算法)——数据而非语法(契约 §1.1 policy 条)。 + +--- + +## 1. 围栏对象 + +### 1.1 类型表示 + +``` +Path // Root = 根身份符号(不透明、用户不可伪造) +``` + +- `Root` 是**身份符号**,不是字符串、不是路径前缀的字面量类型。它的还原面承 M1 定论:身份 = 图节点身份(根声明节点),无独立身份符号;派生源 = 沿图可追踪(能力成分 ⟨身份符号, 授权集, 域约束, 派生源⟩ 的 **身份 + 派生源** 两成分,v4 定位)。 +- **域约束**(能力成分第三项)= 该根声明的命名空间算法 + 名字集合——即"这个 capability 的适用范围",语义边界而非字节长度(与 lattice §七 一致)。 +- `Path` 的值 = (根身份, 组件序列)。**组件序列是偏移域的对应物**:不透明、不可算术、只能沿派生增长(§2.2)。无 `parent()` 派生——"向上"是 `..` 的另一种拼写,同样不可构造。 + +### 1.2 与 `string` 的关系 + +| 面 | 规则 | +|---|---| +| `string` 的角色 | 名字**原材料**(字节序列);未定域、无 capability、可在任意调用点被消费 | +| `string → Path` | **无公开转换**(无隐式转换、无 `from_string`、无 `cast`)。唯一通道 = 根声明(信任点)+ `child` 派生(L4) | +| `Path → string` | **仅诊断渲染**(`display()` 类,单向);渲染结果拿去重建 = 重新走 `child`,不恢复 capability | +| 合法性判定 | 在**命名空间的名字集合**上做成员性判定,**不做字符串重写/规范化/过滤**(L1)。"名字不合法" ≠ "字符串被清洗成合法" | + +**L1 的边界说明(区分设计与待论证)**:本族与"输入校验库"的差别不在实现而在**判定对象**——校验库判"这个字符串危险吗"(可被绕过、需枚举攻击面);本族判"这个名字在命名空间的名字集合里吗"(定义即判据,无枚举)。因此本族**不承诺**任何字符串层面的安全性,只承诺构造层面无非法 capability。 + +### 1.3 与 provenance / asp 的复用面(同构表) + +| `docs/pointer-model.md` | 本族 | +|---|---| +| 条目标识(entry identity) | 根身份符号 | +| 偏移(偏移域,与物理地址无关) | 组件序列(派生链) | +| 边界判定 `0 ≤ off ∧ off+width ≤ alloc_size` | 组件合法性 ∧ 根封闭(§2) | +| `ALLOC` / `ALLOC_AT`(声明式进图) | 根声明入口(唯一信任点) | +| `asp=1` 外部地址空间(无 provenance) | 未围栏 `string` 名字 | +| `unsafe` 边界(外部地址的解引门槛) | 根声明入口(`string` 只在此处被信任) | +| ProvenanceVerify 沿 def 链倒推到 ALLOC | 派生链倒推到根声明节点 | +| 判定域 = 条目标识 + 偏移,**与字节地址表示无关** | 判定域 = 根身份 + 组件序列,**与路径字符串表示无关** | + +**复用声明(不是类比,是同一判定形状)**: +0. **唯一 owner(2026-09-11 裁决 D22)**:图上「沿 def 链倒推」的判定**只在 S-B2 §2.1 步① 实现一次**(输入 = provenance / RegionCheck / ProvenanceVerify 的既有结果,输出 = `Cap` + 派生链见证,`g_*` 只读)。**本族只供给派生链证据**(§3 接口约定 ②),**不另写分析、不重复判定**——其 Q4(派生链合法性落点)据此关闭。 +1. 两者都是**沿 def 链倒推的图稀疏分析**,都不新增 pass——本族判定 = R2 引擎的类型项判定(身份判等,定案 1/4 的图验证层级)+ 图上派生链判定。**共同前提(D12)**:仓库的边 / region 必须**覆盖全部语义依赖**——**覆盖率报告无缺口**前,本族的派生链判定与 S-B3 S1 一样**不启动**(独立侦查项,S-B3 §6.1 前置 3)。 +2. 两者都把"外部/未验证入口"收敛到**唯一显式信任点**(`unsafe` / 根声明),且进信任点后重获追踪权(`alloc_at` 先例:"声明是唯一信任点,之后编译器重新获得追踪权")。 +3. **能力三件套的边界成分**(lattice §六表:边界 → ProvenanceVerify 已覆盖,对应物 = 域约束)在本族 = 命名空间边界;两族的"边界"都**不是**字节长度("排除:基址/长度/字节 R/W/X——经典映射实例")。 + +**差异(须诚实标出)**:pointer-model 的边界判定在**已存在的分配**上做(`alloc_size` 是图数据);本族的边界判定在**名字集合**上做,而名字集合的成员性在经典映射下可能依赖运行时文件系统状态(名字存在与否)——**"名字合法性"与"名字存在"是两件事**,本族只判前者(判据见 §2.2)。 + +### 1.4 handle 的类型表示(**登记**,本族不裁定) + +handle(身份绑定载体)出现在本族值的什么位置,只有两种结构可行,**本 spec 不在此裁定**: + +| 形态 | 表示 | 代价 | +|---|---|---| +| **内嵌槽** | `Path` 值 = (根身份, 组件序列, **可选 handle**)——未绑定 = 空槽,①→② 档在消费点填充 | 值类型随绑定状态变化 → 需判定"空槽"档位(引入状态依赖) | +| **并列类型** | `Handle` 独立于 `Path`;`bind(p: Path) -> Handle` | 引入一次显式转换点 = check/use 边界(**正是 S-B5 的判定对象**) | + +裁定归属:**S-B5 + 映射层**(表示形态取决于"绑定原子性"如何判定)。本族的既定立场(§3 共享不变量):无论哪种表示,**一旦绑定身份就不再按名字重查**;且本族切片 v0 **不含 handle 槽**(§5.1)。 + +--- + +## 2. namespace 判定 + +### 2.1 命名空间 = 根 + 名字集合 + 派生运算 + +- **语义层固定的是判定形状**:一个根定义了一个**名字集合** `N(R)` 与一个**派生运算** `child : (R, name) ⇀ R'`(部分函数——不合法即无定义)。 +- **具体命名空间算法是映射参数**(结构同 lattice §4.6 无格承诺):POSIX 目录树、对象存储前缀、URL 路径、配置键空间各自实例化自己的 `N` 与 `child`;语义层不固定哪一个,也不按范式枚举。 +- 与"无格承诺"的同构:**用普适构造定义层,不用情形分析**——本族只要求 `child` 是部分函数且**保根封闭**(§2.4),不要求它是字符串拼接,也不要求名字集合可枚举。 + +### 2.2 `child` 派生规则 + +``` +child(self: Path, name: string) -> Path // 失败语义见下 +``` + +**合法名字(本族切片的最小名字集合)**:单个非空组件,且 + +1. 不含命名空间分隔符(经典映射下 = `/`); +2. 不是 `.`; +3. **不是 `..`**(L2); +4. 非绝对(无前导分隔符)。 + +**判定档位**(义务落档的详细信息见 §4): + +- 名字是**编译期常量** → 编译期判定。合法 → ① **prove**,零运行时成本;非法 → **硬编译错**(判定失败,不是"运行时再说")。 +- 名字是**动态值** → ② **enforce**:运行时成员性检查(O(名字长度) 单遍比较,无分配),失败**响亮拒绝**。 + +**失败语义的硬性要求(安全性载体,与失败形态解耦)**: + +> 非法派生**绝不产出**落在其它资源上的 capability;**绝不回退**(不截断、不回落父路径、不静默取根、不"尽力而为")。 + +失败形态(panic / `Option`/`Result` 类错误值)是**策略选择**,不改变上述安全性 claim——推荐切片 v0 用响亮拒绝(panic,零新类型),优雅错误返回登记为生产形态(§7 Q1)。 + +### 2.3 `..` 的语义:消解失败即非法(L2) + +**精确表述**:`..` 不在 `N(R)` 中——命名空间里**没有它的目标**(根的父不存在于该 capability 的域内)。因此 `child("..")` 不是"被检查后拒绝",而是**无对应派生**(unconstructible)。这正是 §0.1 论题的落地:危险从"检查点"移出,成为"构造的不存在"。 + +**诚实的边界(不得含糊)**:对**非根**路径,`..` 在经典文件系统里**技术上可消解**(`images/../css` 是合法资源)。本族裁决 L2 仍**一律非法**,理由: + +- **局部性**:单组件判定无需规范化算法——避免"判定一个名字是否安全"退化为"实现一个正确的路径规范化器 + 证明它"(编码、多次编码、尾随点/空格、大小写、Unicode 规范化都是经典绕过面); +- **可强制**(lattice §六引 Kain 1986 约束:「细化 ⊑ 必须可强制」):局部规则天然可强制,全局消解规则的强制依赖映射层正确性。 + +**备选 L2b(登记,不采用)**:允许 `..` 但要求**消解后仍在根内**。代价 = 规范化算法 + 其正确性论证 + 与符号链接面交互后**不可判定**(§2.5):值级消解"仍在根内"在 symlink 参与下不再蕴含解析后仍在根内 → 该备选的 LV-1 判定不闭合。裁决保留在 §7 Q5。 + +### 2.3b 绝对路径 + +**语义**:绝对路径的含义 = **重绑搜索原点**("从文件系统根开始"),与 capability 的定义(原点固定 = 该 capability 的根)**直接矛盾**。因此前导分隔符不在 `N(R)` 中——不是"检查后拒绝",而是**无对应派生**(同 §2.3 的 `..`)。 + +**两个推论**: + +1. **`child` 的实现不得是字符串拼接**。若实现为"根字符串 + 名字",则拼接本身不是错误来源(前导 `/` 已在判定处排除)——但**判定与消费必须绑定同一个名字集合**,否则两处规则漂移。这是 §1.2"判定对象 = 名字集合成员性"的实现后果,登记为实施期检查项。 +2. **宿主绝对路径只允许出现在根声明处**(L4)。这使"程序能触达的宿主路径集合"**静态可枚举**——围栏的可审计面由此而来(§6-3)。 + +**与 §2.5 的关系**:排除绝对路径只解决 LV-1——`child("images")` 仍可能解析到根外(symlink)。绝对路径面**不减少** LV-2 的登记义务。 + +### 2.4 根封闭(传递性) + +`child` 保根封闭:`Path --child--> Path`(L6:类型参数稳定)。故派生链上任一点都在根域内,**归纳成立**,判定无需重算整条链。 + +**细化(收窄到子树)不在类型上**:`Path` 不携带"我只被允许访问 `images/`"这一信息——**细化是派生链的性质(图数据),判定归 S-B2 的 capability path**(`Can(a,o,r) ⟺ ∃合法 capability path`),本族只保证派生链**忠实记录且不可伪造**。此裁决同时避免了与 S-B2 的判定重复、以及对 R2 P3 参数化变型规则的依赖(§7 Q3)。 + +### 2.5 符号链接面(**显式登记,不承诺**) + +**判据(本族最重要的不承诺条目)**: + +> **LV-1 通过 ⊉ LV-2 安全。** 构造合法性(纯值级)**不蕴含**解析身份安全性(文件系统级)。 + +| 层 | 内容 | 可判定性 | 归属 | +|---|---|---|---| +| **LV-1 构造合法性** | 组件名字成员性 + 根封闭 | 纯值级:常量档编译期,动态档运行时单遍检查;零文件系统依赖 | **本族(本 spec 的判定面)** | +| **LV-2 解析身份** | symlink 跳根外 / bind mount 把外部挂进根内 / hardlink 同 inode 两名 | **值级不可判**(依赖运行时文件系统状态与挂载表) | **映射层义务 + 治理面**(§3 handle 化) | + +三条独立性(登记,逐条给出归属): + +1. **symlink**:组件 `images` 是合法名字,但它可能是指向 `/etc` 的符号链接 → 构造合法而目标越界。关掉方式 = 逐组件 `O_NOFOLLOW` + dirfd 链,或 `openat2(RESOLVE_BENEATH|RESOLVE_NO_SYMLINKS)`——**映射层实现选择,不进语义层**。 +2. **mount**:bind mount 可把根外资源挂进根内路径下——namespace 面**不可判**(不是名字问题),归治理/挂载面("唯一信任点"外的第二个信任面,必须登记而非默认安全)。 +3. **hardlink**:同一 inode 两个名字,其中一个在根内——同样不可判,归治理面。 + +**同构推广(登记,不在本批范围)**:HTTP 重定向 ≡ symlink(命名空间外的第二次解析);对象存储前缀命名空间**无原子 handle** → LV-2 不可强制 → 高安全档落 ④(§4)。此推广说明本族的判定形状跨命名空间成立,而**LV-2 的可强制性是映射层属性**——正是"无格承诺"的结构(层不承诺具体算法)。 + +--- + +## 3. 与 S-B5(状态版本 / TOCTOU)的衔接 + +**接力点**:本族保证**名字 → capability 的构造**单步且不可回退(无"先检查字符串、再用字符串");S-B5 保证 **capability → 资源身份的绑定**是原子的(check/use 同一版本)。 + +**共享不变量(两族交接的核心,写入本族)**: + +> **一旦绑定身份,不再按名字重查。** 检查与使用必须是**同一个对象**,而不是"同一个名字的两次解析"。 + +两条路径(把 §2.5 的 LV-2 归属讲清): + +| 形态 | 构造语义 | TOCTOU 面 | +|---|---|---| +| **已 handle 化**(fd/dirfd) | `child` = `openat(dirfd, name, …)` → 新 handle = 新 capability。**构造即绑定身份** | 无重查 → 无 TOCTOU;本族产出即 S-B5 的**版本载体** | +| **未 handle 化**(纯值路径:配置值 / 对象存储前缀) | 值级派生,无资源绑定 | 值级无版本变化 → 无 TOCTOU;**"值 → 资源身份"的转换点**才有 TOCTOU 面 → 那是 S-B5 的判定对象 | + +**接口约定(本族交给下游的三样东西)**:① 根身份(capability 身份);② 派生链(授权细化证据,供 S-B2 `Can` 判定);③ 可选的 handle 槽(身份绑定载体,S-B5 消费)。 + +**边界**:本族**不实现** handle 化——切片 v0 为纯值级(§5),故 v0 的安全 claim **只覆盖 LV-1**(§2.5 判据在此重申)。两族的合并 claim(LV-1 ∧ LV-2)需要 v0 + handle 化映射同时到位。 + +**S-B5 的状态(2026-09-11 收敛批 v3 按 A 线 N4 更新)**:S-B5 = **收敛批 v2/v3,已与本 spec 对齐**(原「并行起草中 / 未定稿」表述**作废**)——其 §3.2 已回接本节 §1.4 / §3 / Q7,并**给出 handle 类型表示裁定 = 并列类型**(本节 Q7 据此关闭)。本 spec 只固定上述接力点与共享不变量;若两族后续形态冲突,以**两族共同复核**为准。 + +--- + +## 4. 义务落档(四档 + 判定失败) + +档位定义逐字取路线图 §1.1(prove / enforce / proof-required / reject);贯穿原则 = 契约 §1.2-3「义务不静默消失」。 + +| 判定 | 档 | 落法 | +|---|---|---| +| 常量合法名(`child("images")`) | ① **prove** | 编译期消解,零运行时成本 | +| 名字来源自带约束(枚举/白名单类型,或 where 常量档 —— 定案 5 三档) | ① **prove** | 构造者可静态证合法;**这是可证性的正当来源,不是白名单筛查** | +| 动态名 | ② **enforce** | 运行时成员性检查(单遍比较,无分配);失败响亮拒绝、绝不回退 | +| 派生链身份/根封闭/身份判等 | ① **prove** | R2 引擎 `ty_equiv`(参数化原子同形判等)+ 图派生链判定,零运行时 | +| 需要"证明名字属于受限集合"但来源无静态约束 | ③ **proof-required** | 要求用户提供 contract/proof(形态由 S-A 定);**本族暂不消费**(登记) | +| 动态名 + **策略禁止运行时检查**(如常量时间要求) | ④ **reject** | 高安全档拒编——**本族 ④ 仅在此边界出现** | +| LV-2(symlink/mount/hardlink)且映射层**无 handle 化能力** | ④ **reject** | 高安全档拒编(有 handle 化 → 落 ② 由映射层强制) | +| **判定失败**(常量非法名) | **硬编译错** | 不属于义务梯档——是判定结论为假(非"义务待处置") | + +**默认强度分层(2026-09-11 裁决 D3(c);规范形态见 S-A §2.2 附)**:本族 **② enforce 默认启用**(动态名 = 运行时成员性检查);**④ 仅高安全档**(策略禁止运行时检查 / 映射层无 handle 化);**③ = 登记 + 继续编译**(默认档不拒编);**CI 门槛 = 严**。 + +**两条结构性观察(区分设计与待论证)**: + +1. **本族的 ④ 面很窄**:因为 enforcement(运行时名字成员性检查)**总是可实现**,④ 只在"策略禁止运行时检查"或"映射层无 handle 化"时出现。这与 S-B1 注入族(参数化通道之外无 enforcement)形态不同——登记为族间差异,供 S-A 的义务分类消费。 +2. **③ 的位置**:本族 ③ 是"把动态名升级为可证"的正当通道(契约第 14 条声明式上限),**不是**"证不出来就要求用户写证明"的兜底——后者会退化成把责任推回程序员(违反本族论题)。 + +--- + +## 5. 最小可实施切片 + 判据 + +### 5.1 切片 v0(纯值级 LV-1,**不接 syscall、不做 handle**) + +1. 领域库位落 `Path` 类型 + 根声明入口(常量绝对路径,声明式进图,唯一信任点)+ `child` 构造器; +2. 常量档判定接编译器(实施细节 = S-A §3.6 装载面 + R2 P2b 字面量面接线——**S-A 已落盘(收敛批 v2)**,剩实施批;**D12 覆盖率门槛**同样适用于本切片的派生链判定,§1.3); +3. 动态档运行时检查(成员性单遍比较,无分配); +4. 无 `string → Path` 公开转换(唯一通道判据); +5. 判据落 `tests/selfhost/` 用例(编译错断言 + 运行时拒绝断言)。 + +**明确不在切片内**(登记):handle/fd 化、syscall 接线、symlink/mount/hardlink 面、跨命名空间映射(HTTP/对象存储)、L2b 消解备选、细化的 capability path 判定(归 S-B2)。 + +### 5.2 判据(正控 / 负控) + +``` +// 正控 ①:常量合法派生链 +r := root("/var/www"); // 唯一信任点(宿主绝对路径只在此处出现) +i := r.child("images"); // ✓ 编译期 prove +f := i.child("logo.png"); // ✓ 根封闭传递 + +// 负控 ②:常量非法名 → 硬编译错(逐例断言诊断) +r.child("..") // ✗ +r.child("../../etc/passwd") // ✗ +r.child("/etc/passwd") // ✗ +r.child("a/b") // ✗ +r.child("") // ✗ + +// 负控 ③:动态名 → 运行时响亮拒绝,且不产出 capability +n := get_env("UNTRUSTED"); // n = "../../etc" +r.child(n) // ✗ 运行时拒绝(不截断、不回落、不静默取根) + // 正控对照:n = "images" → ✓ + +// 负控 ④:身份判等 + 唯一通道 +root("/srv").child("x") // ✗ 与 Path 不等价(类型错) +cast>(some_string) // ✗ 无此转换(编译错) +``` + +**验收判据**: + +| # | 判据 | 方式 | +|---|---|---| +| 1 | 正控链通过(含二级派生) | 正向编译/运行 | +| 2 | 负控 ② 五例逐一硬编译错(措辞分因,不得并成一条) | 逐例断言 | +| 3 | 负控 ③ 运行时响亮拒绝 + **不产出可用 capability**(拒绝后无对象可用) | 双路径(解释器 / ELF)rc 精确 | +| 4 | **无旁路**:`string → Path` 公开转换不存在(类型面 + 转换面核查) | 类型错误断言 + 面核查 | +| 5 | 正控对照 ③:`"images"` 动态名通过(证明 ②③ 是判定而非"一律拒绝") | 正向运行 | +| 6 | 根封闭传递(判据 1 的第二级派生) | 同 1 | +| 7 | **零成本面**(登记):常量正控无新增运行时指令;动态档为 O(名字长度) 单遍比较 | 产物对比(实施期) | + +**判据的诚实说明**:判据 1-6 全部在 LV-1 面——**切片 v0 不证明任何文件系统安全性**(§2.5 判据)。把 v0 描述为"解决了路径遍历"是夸大;v0 的 claim = "非法 capability 不可构造"。 + +--- + +## 6. 不承诺(逐条落路线图 §1.3 三类) + +1. **错误 specification**:Core 只证明"程序忠实实现了围栏规则",不证明规则本身正确。本族的规则输入 = **根声明的选择**(把哪个宿主路径定为根)、**命名空间算法的选择**——声明错(例如根定为 `/`)则围栏忠实实现了一个无用规则。**"唯一信任点"就是这条边界的名字**:它把 spec 正确性的责任集中到一处可审计的声明,而不是消除它。 +2. **恶意/失陷外部实体**:编译机、供应链、以及**谁能执行根声明**(治理面)不在语义可证范围。围栏缩小权限面(能力显式、可枚举、可审计),不证明声明者可信。硬件后门/内核被攻陷同属此类。 +3. **需求本身允许危险行为**:程序若**确实要**读 `/etc/passwd`,本族不禁止——它要求程序**显式持有那个 capability**(对该根的一次显式声明)。即:围栏把能力从**隐含**(任何字符串可达)= 变为**显式**(可枚举、可治理、可审计),而不是禁止能力。这正是路线图"目标不是禁止能力,而是证明隔离边界"的本族实例。 + +**另加一条本族特有(源自 §2.5,非 §1.3 三类)**:切片 v0 **不承诺** LV-2(解析身份)安全——symlink/mount/hardlink 面显式登记在映射层与治理面。 + +--- + +## 7. 不做项 + 开放问题 + +### 7.1 不做项 + +1. 不做字符串消毒/黑名单/规范化筛查(L1,契约 §1.2-2); +2. 不加关键字、不做内建特判、不改 Graph 本体、不动命令式下限(契约 §1.2-1/-5、定案 14); +3. 不在本族实现 handle/fd 化与 syscall 接线(§3,归映射层 + S-B5); +4. 不承诺 symlink/mount/hardlink 的语义层判定(§2.5); +5. 不在类型参数上做细化子类型(L6,归 S-B2 的 capability path); +6. 本批不做跨命名空间映射(HTTP/对象存储)——只保判定形状可实例化(§2.1); +7. 不重定义属性轴/义务档/不承诺(共享契约 §1.1/§1.3 逐字遵守)。 + +### 7.2 开放问题 + +| # | 问题 | 关联 | 裁定(2026-09-11,D30 族内逐条) | +|---|---|---|---| +| Q1 | `child` 失败语义载体(panic vs `Option`/`Result` 类错误值) | §2.2 | **已裁**:切片 v0 = **panic(响亮拒绝,零新类型)**;`Option`/`Result` 类优雅形态登记为**生产形态**(后续批)。**失败语义与安全 claim 解耦**(§2.2 硬性要求不变) | +| Q2 | **根声明的治理面**:谁有权声明根?是否需要审计/签名/静态可枚举清单 | §1.1、§6-2;S-B2 | **已裁(D6(a))**:**装载期校验 + policy 面授权**——根声明只在**受控命名空间内有效**;「谁有权声明根」记入 policy 面、**对装载器可见**;**不启用 `pub` 强制**(S-A §3.6)。静态可枚举面已由 §2.3b 推论 2 给出(宿主绝对路径只出现在根声明处)。签名/provenance 跨库面 = 路线图 §1.3 第 2 类(登记不判) | +| Q3 | R2 引擎"参数化原子同形判等"是否严格等于**符号身份判等**;细化子类型是否依赖 P3 变型 | `ty_equiv`;R2 P0/P3 | **已裁**:切片 v0 **只依赖同形判等**(P0 已实施),**不作符号身份判等的承诺**;**L6 已刻意避开参数化变型** ⇒ 无隐藏依赖(实施批复核为验证项,不改设计) | +| Q4 | 派生链合法性判定的落点;需与 S-B2 capability path 判定**去重** | §1.3、§2.4;S-B2 | **已裁(D22)**:**唯一 owner = S-B2 §2.1 步①**(本族只供给派生链证据,§1.3 复用声明 0);本族**不另写图面分析** | +| Q5 | L2 vs L2b(`..` 一律非法 vs 允许根内消解)的最终裁决 | §2.3 | **已裁**:**维持 L2(`..` 一律非法,不做消解尝试)**;**L2b 不采用**(其代价:规范化算法 + 正确性论证 + symlink 交互后不闭合,§2.3 备选条目保留为登记) | +| Q6 | 命名空间算法的**映射参数化**形态(`N(R)`/`child` 如何随实例选择;非经典命名空间的实例化证据) | §2.1;lattice §4.6、R3 | **已裁(与 ★D5 一致)**:`N(R)`/`child` = **映射参数 = policy 数据**(**零新语法**);非经典实例化证据(对象存储前缀 / URL 路径)登记为 **v0 之后的验证项**(不阻塞 v0) | +| Q7 | 与 S-B5 的 handle 接口契约最终形态(类型表示、绑定原子性的判定面) | §3;S-B5 | **已裁**:handle 表示 = **并列类型**(`Handle` 独立,`bind(p) -> Handle` 为显式转换点——S-B5 §3.2 建议,2026-09-11 采纳 D30);`bind` 原子性来源(fd / dirfd / `openat` 类)= **映射层确认项** | +| Q8 | 名字**规范化/等价**(NFC/NFD、大小写不敏感、尾随字符)——归语义层还是映射层;可强制性如何登记 | §2.2、§2.5 | **已裁**:**归映射层**(值级判定只判**成员性**,不做规范化);可强制性 = 「**判定与消费绑定同一个名字集合**」(§2.3b 推论 1,实施期检查项);**与 LV-2 同档**——无映射层保障时高安全档落 ④(§4) | +| Q9 | 依赖登记:S-A 尚未落盘 → §4 档位语义 / ③ 的 proof 形态 / policy 接口承接点需逐条复核 | 全文 | **已闭合(2026-09-11,收敛批 v3 按 B 线 N2 修正栏位)**:S-A **收敛批 v2/v3** 已落盘,本族 §4 已按其 §2.2 附(默认强度)+ §3.1(放行·声明条目)+ §3.6(装载校验)复核;**本族条目在 schema 中的形态 = `kind=mint`(能力创建点 = 根声明——S-A §3.1 的 `kind` 闭集已含 `mint`)**,字段填法随 S-B2 §5.1 的**条目字段面**(`主体/身份 · 目标/范围 · 理由 · 派生源 · 义务引用`),但**不复用其 `grant` 栏位**(「授予既存对象权限」≠「创建新域」) | diff --git a/docs/superpowers/specs/2026-09-11-policy-state-version-toctou-design.md b/docs/superpowers/specs/2026-09-11-policy-state-version-toctou-design.md new file mode 100644 index 00000000..011c875a --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-policy-state-version-toctou-design.md @@ -0,0 +1,256 @@ +# 状态版本族(S-B5):check/use 版本分离 + handle 身份绑定 + +日期:2026-09-11 +状态:**设计起草(2026-09-11 收敛批 v3)**(待用户/评审裁决;**D2(a)/D3(c)/D4(a)/D7(c)/D13/D22/D23/D30 已随动**,§5.3 与 §7.2 开放问题全部给裁定或降为待实测;族内实施计划后立)。**v3 变更**:§0 补 `integrity` 消费声明(A 线 N3)· 层名 `L-1`/`L-2` → **`LV-1`/`LV-2`**(消同形混淆;S-B4 同步)· §7.2 Q6 的「S-E 侧 D23 一行」遗留**已闭合**(S-E §4.1 已补)· §1.2 补纯度批回填指引 +性质:语义安全线 S-B×5 之一(路线图 §2 S-B5 行)——`state-version` 属性轴的 TOCTOU 判据 + handle 身份绑定设计。不含代码、不含字节格式、**不新增载体机制**。 + +关联: +- `docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(**共享契约**——术语 §1.1 / 原则 §1.2 / 不承诺 §1.3 逐字遵守,本文不重定义) +- `docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md`(S-A:属性轴 / 四档义务 / policy 接口)——**本族的依赖项**;**已落盘(2026-09-11 收敛批 v2)**,衔接点(§2.1 执法表 / §2.2 附默认强度 / §2.3 形状项 / §3.1 `kind=stability` 条目 / §4 违法分栏)已对表(§7.2 Q6) +- `docs/superpowers/specs/2026-09-11-policy-resource-confinement-design.md`(S-B4:`Path` 围栏)——**衔接面 = §3.2**(该 spec **并行起草 / 待评审**(2026-09-11 A 线评审 M6 修正:非「已定稿」):§3 为 S-B5 专设接力点,并把 handle 类型表示裁定权交给「S-B5 + 映射层」;本族 §3.2 回接其 §1.4 / §3 / Q7,并**给出裁定 = 并列类型**) +- 路线图 §1.4(2026-09-11 追加裁决:确定性轴族 + 「不确定性 = 显式请求」)——本族轴与 `determinism` 族相邻(`D_external` / `D_effect`),执法形态同构(§2.1);汇合语义本体归 **S-E**,不入本族 +- `docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md` + `2026-09-09-lattice-ir-v7-format.md`(**既有机制**:ENT 条目版本 / 存在区间 / EDG kind=1 state 边——本族全部复用,零重造) +- `docs/memory-model.md` §一(条款 4 边界公理 / 4b 图内不可重算 / 5 赋值 = 版本化) +- `docs/memory-model-capability-lattice.md` v4 §六(身份 → 图节点身份,无独立身份符号;授权归治理层) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2:判定走引擎 `ty_sub`/`ty_equiv`;§6.1 TYPE/IFACE 段 = 注解若需持久化的既有去向) +- 源码事实底座(**2026-09-11 行号复核修正**):`src/compiler/dataflow.cr:160-177`、`src/compiler/checker.cr:1107/1142`(原引 `:1103`/`:1138` 错)、`src/compiler/checker.cr:2021`(注释)/`:2024-2025`(返回点)(原引 `:2020-2021` 错)、`src/stdlib/io.cr:27/72`、`src/arch/x86_64/instr.cr:681` + +--- + +## 0. 范围与裁决 + +**论题**:`check(x); …; use(x)` 中间外部状态可变化 → 图上应是**两个分离的状态版本**;更合理的 API = `open → verified handle → operation`(**handle 绑定身份**,而非按 pathname 重查)。 + +**本族的轴**:拥有 `state-version`(路线图 §1.1);借用 `effect`(变更事件)与 `authority`(S-B2)——只消费,不重定义。 + +**本族是否消费 `integrity`(A 线 N3,显式声明)**:**消费**——形式 = **`state-version × integrity`**(S-A §1.8 的耦合行):**版本 = 完整性的时间证据**(check 与 use 必须同版本)⇒ 本族 §2.1 的 C1/C2 判据即该消费面。这是 `integrity` 目前**唯一经对侧确认**的消费关系(S-A §1.3 首消费方行已按此改回引);S-B2 侧明示不消费(其对侧行已加)。 + +**与确定性轴族的关系(路线图 §1.4,2026-09-11 追加)**:本族 = `determinism` 族 `D_effect` / `D_external` 两轴在**外部不可见面**的具体化。§1.4「不确定性必须可归因到显式语义源」在本族读作:**外部世界的版本推进不得静默变成程序里的陈旧检查**——与「隐式竞争 → reject」同一条执法线(§2.1 对表)。 + +**族内裁决**: + +- **决 1(版本载体)**:状态版本 = ENT `(var_id, version)`(v7 格式 §3.5)——本族**不引入第二套版本机制**;存在区间 = `live_start/live_end`,无配方 = `flags bit0`,序 = EDG `kind=1`。重造即偏离既有定案。 +- **决 2(外部对象进图)**:外部对象 = **无配方条目**(memory-model 条款 4/4b 统一规则)——外部状态**不需要新通道**,它已经以边界条目身份在图里。 +- **决 3(判据性质)**:图上只有**图版本**;**世界版本按定义不进图**。因此判据不是"版本号相等"本身,而是「图版本同版本支配 + 窗口内世界版本无变更」(§2),且后者**永不静默**(§4)。 + +**范围外**:并发线程/flow 间竞争(§7 开放问题 4);OS 内核内部 TOCTOU(S-B4/平台面);不新增关键字、不加语言 primitive(路线图 §1.2 原则 1)。 + +--- + +## 1. 状态版本模型 + +### 1.1 图版本(既有,逐字复用,零新增字段) + +| 面 | 既有载体(v7) | 本族用法 | +|---|---|---| +| 版本身份 | ENT `(var_id, version)`(1-based,每次定值 +1) | 同一性判定的原子单位 | +| 定值点 | ENT `def_nod` | 版本推进 = `def_nod` 换新(memory-model 条款 5:x₁ 创建、x₀ 失效) | +| 存在区间 | ENT `[live_start, live_end)`(NOD id 坐标,半开) | 版本在图上的存活窗口 | +| 无配方 | ENT `flags bit0`(图内不可重算 / 边界) | **外部对象(观察结果)的标注面** | +| 序 | EDG `kind=1`(state 边 = 副作用序约束集) | 变更与观察的顺序约束 | + +本族声明:**上表字段一个不改、一个不加**。§5 切片不加任何格式字段。 + +### 1.2 外部状态进入图的四条通道(三条既有 + 一条注解) + +| 通道 | 载体 | 状态 | +|---|---|---| +| ① 观察 | 边界节点(extern / `unsafe` / syscall / MMIO)产出条目,`flags bit0`;语义 = 符号常量(条款 4) | **既有** | +| ② 身份 | handle 值 = 条目;身份 = **产生它的图节点身份**(capability-lattice v4 §六:无独立身份符号) | **既有**(本族只规定用法) | +| ③ 变更 | 图内变更 = STORE 族节点(已在 state 链);**图外变更 = 无节点**——这正是 TOCTOU 的本体 | 变更**声明** = 注解(policy 层,路线图 §1.2 原则 1) | +| ④ 序 | EDG `kind=1` state 边 | **既有** | + +- ③ 的注解面:外部调用节点可带 `mutates(κ)`(κ = 身份键);**未标注的 extern / 未知调用保守视为「变更一切外部身份」**——沿用既有先例(`dataflow.cr:153-159`:未知/外部函数保守视为有副作用——现按 `purity_op_effect` 单一真源 + `fi_ispure` 真值判定)。 +- **外部效应事实底座(2026-09-11 **已修复**;修复前快照保留备查)**:修复前纯度标注乐观——`checker.cr:1107`「optimistic: all functions are pure」+ `:1142`「optimistic: extern functions are pure」(**值恒 1**),且 `df_connect_state` 分类表**不含 `IR_CALL_EXTERN`**,链每函数重置 ⇒ **外部效应无保证的序通道**(对 TOCTOU 恰好是最坏情形)。**已于效应/纯度批修复**(`762bd429` 真纯度计算 + `c9099d73` 分类表补全/单一真源):`df_connect_state`(`dataflow.cr:146`)按 `purity_op_effect`(`checker.cr:2912`)入链——extern / 间接调用(`IR_DYN_DISPATCH`)/ spawn / yield / hotpatch / 裸指针写 / await 全入链,且一切未证纯的可解析调用入链;`fi_ispure` = `compute_all_purity`(`checker.cr:2959`)真值。**⇒ 外部效应(含 extern 调用节点)已有函数内保证的序通道**;**残余面**:链每函数重置(设计不变——图内变更源在判据域内的序已覆盖;**图外变更无节点 = TOCTOU 本体,不受本修复影响**)+ lazy 判定冻结(`ir_gen.cr:1590`,不涉链)。 +- **修链归属(2026-09-11 裁决 D7(c) + A 线评审 I4 收敛;**已闭合**)**:修链(`df_connect_state` 覆盖 `IR_CALL_EXTERN` + 更正 `fi_ispure` 写入点)= 原「独立里程碑单独立项」,**已作为 P0 插队批落地**(`762bd429`+`c9099d73`+`c7ca4251`;落地登记 = TODO #27)。**门禁口径不变**:本族切片**不以其为前置**(修链落地前后同此);S-E §4.1 的「判给 S-B5 切片」表述仍**不成立**(两处口径已对齐)。判据 = 本批实测(全量回归 + `.ccr` 边集语义断言 `test_ccr_v7` 24→27 + ELF canary 逐字节 + 自举稳定)。 +- **清单唯一约束(硬性)**:**外部效应 / 变更源清单只允许存在一份**(挂 S-A §1.7 的 policy 面);本族切片自带的最小面 = **对该清单的消费**(`mutates(κ)` 注解 + 未标注 extern = 变更一切 κ,§1.2 ③),**不另写一份**(否则必然漂移)。负控(§5.4)建立在该面 + 该清单之上。 +- **回填记录(2026-09-11,Task 4 Step 4 = 原「待纯度批落地后回填」marker 的落点)**:上两条的「纯度标注乐观 / 外部效应无保证的序通道」**已按实测重述**为修复后状态;链覆盖范围 = 函数内 store 家族 + `purity_op_effect` 全清单 + 一切不可解析/未证纯的可解析调用(跨函数不连)。实测证据 = TODO #27(回归 38/38 rc=0 · 语料级旧前端 vs 当前 ELF 逐字节 20/20 同 · canary `95084e7b…d475` IDENTICAL · 自举稳定 · `.ccr` 差异仅 chain(kind=1)边)。 +- 今日语言面事实:`syscall3/4` 是特判内置(`checker.cr:2021` 注释 / `:2024-2025` 返回点;`instr.cr:681` 内联发射),stdlib `read_file(path)` / `write_file(path, …)`(`io.cr:27/72`)是**按 pathname 重查**的形态(P3,§2.4)。 + +### 1.3 图版本 vs 世界版本(判据的诚实核心) + +- **图版本** `v_g`:ENT 里那个版本号——图知道的状态。 +- **世界版本** `v_w(κ, t)`:身份 κ 在运行时刻的真实状态——**按定义不进图**(无配方条目 = 符号常量,条款 4/4b)。 +- 推进 `v_w` 的三类来源: + - **(a) 图内变更节点**——图上可见(STORE 族;③)。 + - **(b) 外部代理**(另一进程 / 线程 / 硬件 / 系统调用内部副作用)——图上**不可见**。 + - **(c) 时间本身**——不推进版本;只有变更推进。(显式排除,免得判据退化成"时间过去了"这一无信息量条件。) +- **本族要立的不变式**:`v_w(κ)|use = v_w(κ)|check`(检查窗口内世界版本未推进)。 +- 图上**能**证的是 (a) 的缺席 + ② 的身份不可重解析;**不能**证的是 (b)。这条边界就是 §4 四档的分界来源,也是 §6 引用路线图 §1.3「恶意/失陷外部实体」的落点。 + +--- + +## 2. TOCTOU 判据 + +### 2.1 判据形态(C1 ∧ C2 ∧ C3) + +**记号**: +- check 节点 `C`:验证谓词 `P` 于身份 `κ` 的版本 `v(C)`。 +- use 节点 `U`:作用于身份 `κ` 的版本 `v(U)`。 +- state 可达 `→*`:EDG `kind=1` 的传递闭包。图语义 = 约束集 ⇒ 偏序可达(v7 载体设计 §1.3);今日生产形态 = 链(`g_last_state_node`)⇒ 判定退化为区间包含(实施快路径)。 +- mutator:图内变更(③ a)或带 `mutates(κ)` 的边界节点;未标注 extern = 变更一切 κ。 + +**TOCTOU 判据**:`U` 合法 ⟺ ∃C 使 C1 ∧ C2 ∧ C3: + +- **C1(同版本支配)**:`v(C) ≡ v(U)` **且** C 支配 U——C 经数据边可达 U,或 U 直接消费 C 的版本见证(条目)。 +- **C2(窗口无变更)**:状态路径 `C →* U` 上不存在 κ 的 mutator,**且 U 本身不是对 κ 的重观察**(2.2)。 +- **C3(稳定性声明)**:κ 在图存在区间内被声明为"外部代理不推进它"(独占 / 不可变)。**形态与登记(2026-09-11 裁决 D2(a))**:C3 = **S-A §3.1「放行 / 声明条目」**的 `kind = stability` 条目——形态 = **治理层注解**(**显式豁免节点形态**:它是关于世界的事实声明,无可变换之值),但**必须补齐审计字段**(主体/范围 = κ 与存在区间 · 理由 · **派生源** · 义务引用)并**落台账**(S-A §5.2b);豁免理由或审计字段缺失 ⇒ **拒装**(S-A §3.6)。非语义本体(capability-lattice v4:授权归治理层)。 + +**与路线图 §1.4 执法表的对表**(两者是同一形状的两个入口):§1.4「依赖/状态边足证汇合与调度无关 → prove」↔ 本族 C1∧C2∧C3;§1.4「语义明文允许(`race`/`select_any`)→ 显式不确定、必须进报告面」↔ 本族 C3 声明的登记面;§1.4 **「既未证明也未标注(隐式竞争)→ reject ← 真正的执法点」** ↔ 本族 ④(P3/C2 缺口)。 + +- **可判定分界(2026-09-11 裁决 D23;A 线评审 I3 收敛)**:两者是同一执法点,**不得两处各判一次**,分界**按变更源身份**判定—— + - `mutator ∈ 图节点集合`(图内可见的变更/并发主体)⇒ **S-E**(并发面); + - `mutator ∈ 边界条目 / 未知 extern`(图上不可见的外部推进)⇒ **本族**(外部不可见面); + - **交集非空**(同一 `go` 起的节点既是图节点又是并发主体)⇒ **先本族的保守面**,**S-E 不重复报**(只出一条诊断)。 + - 对应负控 = **交叉负控**(§5.4 新增:同一场景断言**恰一条**诊断)。S-E 侧的同款一行归其自身文本(B 线)。 + +### 2.2 版本分离的检测(本族要检什么) + +- **重观察 = 新版本**:`use` 若重新读同一 pathname,则产生新边界节点 → 新条目 → 新 `def_nod` ⇒ `v(U) ≠ v(C)`,C1 不成立。**这就是 `check(x); use(x)` 的图形态:两次独立观察 = 两个分离的状态版本**(论题的直接兑现)。 +- 版本分离**本身不是错误**(合法:写后重读);**没有新 check 的 use 才是**——即分离出的版本上不存在同版本的 check。 +- **pathname 重查识别**:边界节点以 pathname 形值(string)为输入、产出**新**条目,而作用域内已有同 κ 的活验证版本 ⇒ 重查模式(不必然非法,但**不继承既有验证**)。 +- 判据落法:C1 走引擎(§2.3);C2/C3 走图分析 + 注解——**不另建判定体系**(路线图 §1.2 原则 4)。 + +### 2.3 版本等价判定走引擎 + +- 版本身份编码为形状类型项(S-A 横切轴形态),如 `Observed` / `Verified`。**形状项定义属 S-A**;本族只规定判定 = `ty_sub(实际项, 要求项)`(R2 §3.2),要求项来源 = 操作的契约(领域库给出:`op` 要求 `Verified Handle[κ] // 边界节点:一次身份解析,产出条目(flags bit0) +verify(h, P) -> Verified[P, Handle[κ], v] // check 节点:把断言 P 绑到 (κ, v) +op(vh, …) -> … // 操作只对 Verified 定义 +``` + +- **关键性质**:`op` 不接收 pathname,只接收 handle ⇒ **重解析这个动作不存在**(② 身份 = 节点身份,v4 §六)⇒ C3 的身份部分**不靠证明、靠构造**。 +- `verify` 携带版本见证 `v`(= 该条目 ENT `version`)⇒ C1 的判定输入。 +- **谓词分流**:身份谓词(存在 / 类型 / 属主 / 路径)与内容谓词(权限位 / 内容摘要 / 大小)分开——前者 C3 由构造消解,后者 C3 不可消解 ⇒ 走 P2 值绑定或 enforce。 + +### 3.2 与 S-B4 围栏的衔接(回接其 §1.4 / §3 / Q7) + +- **接力点(S-B4 §3 原文立场)**:S-B4 保证**名字 → capability 的构造**单步且不可回退(无"先检查字符串、再用字符串");S-B5 保证 **capability → 资源身份的绑定**是原子的(check/use 同一版本)。分工 = 命名空间判定(S-B4)∥ 时间判定(本族);**两族互不重定义**:S-B4 不定义版本,本族不定义命名空间。 +- **S-B4 交给下游的三样东西**(① 根身份 ② 派生链 ③ 可选的 handle 槽)中,本族消费 ③;① ② 归 S-B2 的 `Can` 判定。**唯一 owner(2026-09-11 裁决 D22)**:①② 的图面分析(沿 def 链倒推 / 根封闭 / 派生链合法性)**只在 S-B2 §2.1 步① 实现一次**(输出 = `Cap` + 派生链见证,`g_*` 只读)——本族**消费其输出**(κ 的可靠性上限 = 映射层 handle 化能力,见下),**不另写分析**。S-B4 只供给派生链证据(其 Q4 据此关闭)。 +- **LV-1 / LV-2 分工(S-B4 L5 / §2.5)**:S-B4 语义层只判 LV-1(构造合法性);**LV-2(symlink / mount / hardlink = 解析身份)值级不可判,显式登记为映射层义务**。本族随之划界:**κ 的可靠性上限 = 映射层 handle 化的能力**——无 handle 化时 ④ reject(**高安全档**;S-B4 §4 同款——该行的 ④ 只在「策略禁止运行时检查 / 映射层无 handle 化」时出现,不是默认档行为),有 handle 化时 LV-2 由映射层强制(本族 C3 的身份部分据此才成立)。 +- **handle 类型表示的裁定(S-B4 §1.4 把裁定权交给「S-B5 + 映射层」)——本族裁定(2026-09-11,D30 采纳本节建议):并列类型**。理由:本族需要 handle 携带版本见证 `(κ, v)`(§3.1),而"内嵌槽"方案使 `Path` 值类型随绑定状态变化(状态依赖类型,与路线图 §1.2 原则 1「属性不升格 primitive」张力更大),且要求值级也带版本(S-B4 已裁定**纯值路径无版本变化**)。故取 `Handle` 独立 + `bind(p: Path) -> Handle`:**转换点 = 唯一的 check/use 边界 = 本族 C1 的判定对象**(与 S-B4 §3 表述一致)。映射层确认项 = `bind` 的原子性由谁提供(fd / dirfd / `openat` 类;S-B4 §3「已 handle 化」行)。**S-B4 §1.4 的两种形态已由本裁定收敛为「并列类型」**(其 Q7 据此关闭)。 +- **两侧的 TOCTOU 分工(S-B4 §3 表格)**:已 handle 化 ⇒ 构造即绑定身份、**无重查 ⇒ 无 TOCTOU**(S-B4 产出即本族**版本载体**);未 handle 化(纯值路径)⇒ 值级无版本变化、无 TOCTOU,**"值 → 资源身份"的转换点才产生 TOCTOU 面 = 本族判定对象**(= 本族 P3 重查模式的另一投影)。 +- **切片依赖(实测)**:S-B4 切片 v0 = 纯值级、**不含 handle 槽** ⇒ 本族切片(§5)的 handle 只能从边界 `open` 起(衔接退化为并列,不阻塞任何一方)。若本族最终形态与 S-B4 §3 冲突,按 S-B4 Q7「两族共同复核」处理。 + +### 3.3 与既有外部操作的关系(事实面) + +- `syscall3/4` 是特判内置(`checker.cr:2021` 注释 / `:2024-2025` 返回点、`instr.cr:681`),`read_file` / `write_file`(`io.cr:27/72`)是 P3 形态——**切片不改 stdlib**(§5 只立判据面);stdlib 的 handle 化 = 切片之后的独立批(§7)。 +- 不引入新的外部操作原语(路线图 §1.2 原则 2:策略面归库):`open` / `verify` 是**领域库**的类型化构造器形态,语言只给流纪律与义务阶梯。 + +--- + +## 4. 义务落档(四档,路线图 §1.1 逐字) + +> ① **prove**(静态可证 → 零成本通过)② **enforce**(不可证但可运行时强制 → 插入 enforcement)③ **proof-required**(不可证也不可强制 → 要求用户提供 proof/contract)④ **reject**(高安全模式下仍无法处置 → 拒绝编译) + +| 档 | 本族条件 | 处置 | +|---|---|---| +| ① prove | C1 ∧ C2 ∧ C3 | 零成本通过;绑定构造性的(P1/P6)不问 C3 | +| ② enforce | C1 成立或可重建,C2/C3 不可证 | 插入**版本重校验**:use 前紧邻一次同身份观察 + 见证比对;不等 = **失败封闭(fail-closed)**。若身份无可观察的版本见证 ⇒ 退化为「观察与使用**原子化**为同一步」(= P1/P2 的唯一化) | +| ③ proof-required | 需要「κ 独占 / 不可变」而引擎不可判 | 要求用户 / 领域库提供 contract(C3 声明面) | +| ④ reject | 高安全模式下:C1 ✗ 且无可执行的 enforce 通道(如 P3 且存在外部变更可能) | 拒绝编译 | + +- **不可证 → 不静默通过**(路线图 §1.2 原则 3):每个判定结果必须落到 ①②③④ 之一,无第五种"算了"。 +- **enforce 的语义纪律**:版本重校验**必须紧邻 use 且 fail-closed**——"检查后再用一个可能已变的东西"不是 enforcement,是 TOCTOU 复现。 +- 与 where 三档的关系(**2026-09-11 按 A 线 M5 修正**):四档(**处置分类**)与 where 三档(**约束形态分类**)是**不同分类轴**(S-A §0.3 T2 明裁),**不得读作「三档在本族的展开形态」**;本族的对应只是**形态上的相邻**:C1 常量/结构可判 → 编译期;C3 符号 → contract(proof-required);C2 动态 → 运行时(enforce)。 +- **判定为假 ≠ 义务档(同 A 线 M5 修正)**:`verify` 的谓词判定为**假**(且与策略冲突)= **硬编译错**(= **违法**,与 S-B4 §4 表末行同款),**不属于义务梯档**——是**判定结论为假**,**不是 ④ reject**(S-A §2.2 点 1 分栏;本族 ④ 只在「高安全档且无可执行 enforce 通道」时出现,§4 表内已列)。 + +--- + +## 5. 最小可实施切片 + 判据 + +### 5.1 切片 S-B5-0(注解 + 判定,零载体改动) + +1. **身份键 κ 与 `mutates(κ)` 注解面**(最小:handle 派生面 + 保守 extern 面)。切片**不落盘**;**持久化去向(2026-09-11 裁决 D4(a))**= **`.ccr` POLICY / LABEL 段,随 R2 P4 载体批同波**(v7 `7+` 段位已预留)——不是 R2 §6.1 的 TYPE/IFACE 段扩展;v7 格式 §6 开放点 1 的 EDG `kind 2+` 保留位**不动**。 +2. **版本等价判定** = `ty_equiv` 调用(R2 引擎)+ 离线小用例表。 +3. **判定 pass(一个文件)**:对每个 use 求 C1(同版本支配)、C2(state 边可达性 + mutator 集)、C3(取注解,**形态 = S-A §3.1 `kind=stability` 条目**,§2.1)→ 输出四档分类 + 反例。 +4. **判据(见 §5.4)** + 不变式:**零产物变化**(注解未启用 = 今日程序不受影响——策略面归库,路线图 §1.2 原则 2);全套件 + 自举判据按 **R2 §4 每步共同判据行**(`2026-09-10-type-interface-unification-design.md:155`)执行,落地细节入实施计划。 + +### 5.2 切片边界(明确不做) + +不改 stdlib、不改 extern 序通道(§1.2 事实底座)、不加载体字段、不做跨过程(过程间 κ 传播 = 切片后续)。 + +### 5.3 待论证(2026-09-11 收敛:三项中两项已裁) + +- **κ 的粒度** —— **已裁(D13)**:**对象身份 + 版本见证**(不是裸对象身份,也不是纯类级);**实例 / 类 / 全外部**的取舍退化为**策略数据**(粒度键由领域库给出),精度上界仍待实测(§7.2 Q1)。 +- **C3 的声明形态** —— **已裁(D2(a))**:**S-A §3.1 的 `kind=stability` 条目**(治理层注解形态,**显式豁免节点形态** + 审计字段 + 落台账;§2.1)。 +- **C3 的 contract 面是否并入 S-A policy 接口** —— **已裁**:**并入**——**承载与登记** = S-A §3.1 policy 面;**撰写方**可以是领域库(原则 2:策略面归库),二者不矛盾(库写条目,S-A 管 schema / 装载校验 / 台账)。§7.2 Q3 据此关闭。 +- 切片仍只要求「最小可用」。 + +### 5.4 正控 / 负控 / 精度控 + +- **正控 A(同 handle 复用 ✓)**:`h = open(p); v = verify(h, P); op(v)` → **prove**,零 enforcement 插入(P1)。 +- **正控 B(值绑定 ✓)**:`v = observe(x); verify(v, P); use(v)` → **prove**(P2 / P6)。 +- **负控 A(pathname 重查 + 中间 external mutation ✗)**:`check("p"); extern_mutate("p"); use("p")` → **必须非 prove**(enforce 或 reject),且反例指明两个分离版本。 +- **负控 B(同 handle 但窗口内 mutator ✗)**:P4 形态 → C2 失败 → 非 prove。 +- **精度控(不误杀)**:`verify(h, P)` 与 `op(h)` 之间夹一个**不同 κ** 的 extern 变更 → 仍 **prove**——证明判据不是一刀切拒绝。 +- **交叉负控(2026-09-11 裁决 D23;与 S-E 的执法分界)**:构造「同一汇合点 / 同一身份的两次访问、无序边、无声明」两例——① 变更源 = **图节点**(并发面)、② 变更源 = **边界条目 / 未知 extern**(外部面)→ 断言**恰出一条诊断**:① 归 S-E、② 归本族;**交集场景**(同一 `go` 起的节点)**先本族保守面、S-E 不重复报**。 + +--- + +## 6. 不承诺 + +(逐条落路线图 §1.3 三类不可消残留) + +1. **恶意 / 失陷外部实体**(本族主落点):外部代理可在 C 与 U 之间推进世界版本,**语言语义不能证明其不发生**——`v_w` 按定义不进图(§1.3)。本族只做三件事:使分离**可见**、使义务**有主**、使好 API **可表达**。**本族不可能消掉"外部实体恶意"本身**——这是路线图 §1.3 第二条的正面承认,不是实现缺口。§1.4 读法:世界版本推进是**物理不确定性**,本族要求它**不得静默变成语义不确定性**(陈旧检查);但"外部代理不推进"这一物理事实永远只能被**声明 / 缩小**,不能被证明——同一条界线。 +2. **错误 specification**:`verify(h, P)` 的 P 本身写错(检了错的属性)——语言只保证"检查与使用同版本",不保证 P 正确。 +3. **需求本身允许危险行为**:CLI 按名字 open 之类的语义有其正当性——目标不是禁止能力,而是**把风险压到一个显式义务上**(证明隔离边界)。 +4. **不承诺**检测所有 TOCTOU:只覆盖图上可表达、policy 覆盖的那些;未覆盖面要么按保守面处理(策略启用时),要么明确不在义务范围——**无第三种"看起来安全"**。 + +--- + +## 7. 不做项 + 开放问题 + +### 7.1 不做项(YAGNI / 边界) + +- 不新增语言 primitive / 关键字;不把 TOCTOU 做成内建特判(路线图 §1.2 原则 1/2——那是 lint 换皮)。 +- **不重造版本机制**:ENT + state 边是唯一版本 / 序载体(决 1)。 +- 不做并发线程 / flow 间竞争判定(声明边界,见开放问题 4)。 +- 不做 OS 内核内部 TOCTOU、不做平台 syscall 语义正确性(S-B4 / 平台面)。 +- 不做 stdlib handle 化(切片后独立批)。 +- 不锁形状项语法(S-A 定稿后对表)。 + +### 7.2 开放问题 + +1. **κ 粒度与可靠性的张力** —— **已裁(D13)**:κ = **对象身份 + 版本见证**;可靠性上限 = **映射层 handle 化能力**(S-B4 LV-2:symlink / mount / hardlink 值级不可判)。**精度上界保留为待实测**(精度控 §5.4 只给下界),登记不裁。 +2. **无版本见证的外部对象** —— **已裁(D30)**:**需要第三形态**——采用**独占声明**(= C3 的 `kind=stability` 条目,**治理层声明形态**,§2.1/§5.3),不新增 enforcement 机制;**运行期锁属库**(不在语言面)。既无版本见证又无独占声明 ⇒ ② 退化为「观察与使用原子化」或 ④(§4 表)。 +3. **C3 归谁** —— **已裁(D2(a))**:**承载与登记 = S-A §3.1 policy 面**(`kind=stability`);**撰写方可为领域库**(原则 2)——二者不矛盾(§5.3)。S-A **已落盘**(收敛批 v2),对表完成。 +4. **与并发模型的关系** —— **已裁(D23)**:**可判定分界按变更源身份**——`mutator ∈ 图节点` ⇒ S-E;`mutator ∈ 边界条目 / 未知 extern` ⇒ 本族;**交集时先本族保守面、S-E 不重复报**(§2.1);交叉负控见 §5.4。本族仍只碰外部不可见面。 +5. **与条款 4b 写回规则的耦合** —— **已裁(D30)**:外部对象条目 = **无配方**(4b)⇒ **按定义图内不可重算** ⇒ 「驱逐必写回」**不适用于只读外部对象**(写回义务的前提 = 有配方可重算);与 memory-model / M4 的对表登记为**实施期核对项**(不改本 spec 判据)。 +6. **依赖对表** —— **已闭合(2026-09-11)**:S-B4 = **并行起草 / 待评审**(原「已定稿」表述已按 A 线 M6 修正),本族 §3.2 已按其 §1.4 / §3 / Q7 回接并**给出 handle 裁定 = 并列类型**(映射层确认 `bind` 原子性来源);S-A **已落盘**(收敛批 v2),衔接点对表完成:§2.1(§1.4 执法表 + D23 分界)、§2.3(形状项)、§3.2(`bind`→Handle)、§4 末(四档 ↔ where 三档,**已按 A 线 M5 修正为不同分类轴**)、§1.2(D7 修链归属)。**遗留已闭合(2026-09-11 收敛批 v3)**:S-E 侧的 D23 同款一行已由其 §4.1 补入(按变更源身份分界 + 交集先本族保守面 + 恰一条诊断),交叉负控两侧文本齐备(本 spec §5.4 + S-E §4.1)。 + +--- + +## 合规自检(共享契约逐字遵守) + +- 术语:只使用路线图 §1.1 定义(属性轴 / 义务 / 四档 / policy / declassify / sink-source 未被重定义);新增的族内记号(图版本 / 世界版本 / κ / C1-C3)**不进入**共享术语表语义。 +- 原则:① 属性不升格 primitive(§5.1 注解面 = policy,不改语言)② 策略面归库(§3.3)③ 义务不静默(§4)④ 判定统一走引擎(§2.3)⑤ 只碰声明式上限(§0 范围外:state 链 / region / 调度不动)。 +- 不承诺:§6 逐条落 §1.3 三类残留。 +- 路线图 §1.4(追加裁决):轴族相邻性已声明(§0),执法点同形已对表(§2.1),**未稀释汇合语义本体**(§7.2 Q4 归 S-E)。 +- 复用而非重造:§1.1 零新增字段;§5.1 明确不动 EDG 保留位;无第二套版本机制。 +- 设计与待论证分离:§5.3 待论证、§7.2 开放问题 1-6。 +- **收敛批 v2 记录(2026-09-11)**:**A 线 C2 → D2(a)**(§2.1 C3 `kind=stability` + 审计字段 + 落台账);**D3(c)**(§4 默认强度已由 S-A §2.2 附供给,本族高安全档限定);**D4(a)**(§5.1-1 载体 = `.ccr` POLICY/LABEL 段随 P4);**D7(c)+A 线 I4**(§1.2 修链 = 单独立项 + 清单唯一);**D13**(§5.3 κ = 对象身份 + 版本见证 / §7.2 Q1);**D22**(§3.2 def 链 owner = S-B2 §2.1 步①);**D23**(§2.1 可判定分界 + §5.4 交叉负控);**D30**(§3.2 handle = 并列类型 / §7.2 Q2 独占声明 / Q5 4b 不适用只读外部对象);**A 线 M2**(file:line 修正 `1107/1142`、`2021`/`2024-2025`);**A 线 M5**(§4 末四档 ≠ where 三档展开 + 判定为假 = 违法);**A 线 M6**(§关联「并行起草/待评审」+ 高安全档限定词);**A 线 M1 门禁**(§5.1-4 引 R2 §4 行)。 diff --git a/docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md b/docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md new file mode 100644 index 00000000..2dc4d1c6 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-semantic-safety-obligations-design.md @@ -0,0 +1,595 @@ +# S-A:安全属性轴 + 义务阶梯(四档) + +日期:2026-09-11 +状态:**设计稿(2026-09-11 收敛批 v3)**(**v3 = B 线 N2 对照表挂路线图 §4 + A 线 N1-N5 与 B 线 N2/N4/N8 的接口面收口**:§3.1 放行条目补**第四族(S-B4,`kind=mint`)**与 `declassify 通道` 栏的投影关系(A 线 N2/⚠️2)· §1.3 首消费方改为「暂无对侧确认(登记)」+ S-B5 的 `state-version × integrity` 回引(A 线 N3)· §2.2 附补族级例外限定(A 线 N5)· §3.4 段位表述精确化(B 线 N8)· §1.7 清单唯一口径(B 线 N7)· 纯度事实留回填指引)。**正文面**:轴(§1,含共享契约 §1.4 追加裁决的 `determinism` 轴族)/ 档(§2)/ policy 接口(§3)/ enforcement 通道(§4)/ 诊断面(§5)五面定形;§9 五问已裁 4、余 1 待论证;**A 簇 ★ 裁决(D1-D3/D5-D7/D9-D11)与设计者可裁项已落实,索引见 §0.4**。实施计划后立(writing-plans),实施排在 **R2 收线之后**(共享契约 §3.3)。 +性质:语义安全线(S 族)**地基 spec**——其余 8 份(S-B×5 / S-C / S-D / S-E)的机制与术语基准。**本 spec 是设计文档,不写代码、不改产物。** +共享契约:`docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md` §1.1 术语 / §1.2 原则 / §1.3 三类不可消——**逐字遵守,不重定义**。 + +关联: +- 共享契约:`docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md`(§0 论题 = 属性轴 + 义务阶梯同一机制;**§1.4 = 用户 2026-09-11 追加裁决:`determinism` 轴族 + 「不确定性 = 显式请求」**——本 spec §1.9 承接其轴族与义务落档) +- 汇合语义:`docs/superpowers/specs/2026-09-11-merge-semantics-design.md`(S-E——本 spec 的 `determinism` 族引用其语义结果,**不定义**其语义;边界见 §1.9) +- 判定器地基:`docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2——P0 引擎已实施:`ty_sub`/`ty_equiv`/`ty_disjoint`/`ty_inhabited`/`tt_witness`/`ty_exhaustive`,**三态判定** + 预算守卫) +- where 三档前身:`docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(定案 1 图本体 / 定案 5 where 三档 / 定案 12 用法驱动 / 定案 14 声明式边界) +- 能力前身与 M1 定论:`docs/memory-model-capability-lattice.md` v4(§4.5 规则封闭对象开放 / §4.6 无格承诺 / §六 M1 定论 / §七 能力成分 / §11.5 共存关系 / §十一「证明完毕不下发」) +- 实施底座(**现状机制,file:line 供实施批引用**): + - 标注语法载体:`src/compiler/parser.cr:689-719`(声明标签位:`mut`/`pub`/`apx` + **插件注册标签** + 未知标签 TA08)+ `src/compiler/globals.cr:317-327`(`register_plugin_tag(ns_ni, name_ni, data)`,24B/条)+ `:307`(`find_plugin_entry`) + - 插件钩子通道:`src/compiler/ext_mgr.cr`(`ext_reg` / `ext_dispatch` / `EXT_HOOK_ARRAY_ACCESS=1` / `CORE_SAFE` 门控) + - **enforcement 先例**:`src/compiler/ast.cr:559`(`IR_BOUNDS_CHECK=30`)+ `src/compiler/ext_safety.cr:27,33`(发射点)+ `src/compiler/interp.cr:289`(解释器侧)+ `docs/ir-op-semantics.md:196`(语义 `if ρ(s1) ≥ᵤ s2 → ud2`;ELF 编码 cmp+jb+ud2) + - 图机制:`src/compiler/dataflow.cr:129,157`(state edges 副作用链)/ `:428-432`(SG_IF/SG_LOOP/SG_FOR/SG_FLOW/SG_UNSAFE region)+ `src/compiler/provenance_verify.cr`(ProvenanceVerify) +- `TODO.md`:「类型系统方向定案」(:382)/「验证·工具自动化」(:476)/「边界判定图 pass」(bounds-inference——与 §4.2 发射门控同构)/「性能自动化」(auto-memo 纯度判定——effect 轴消费方) + +--- + +## 0. 裁决与范围 + +### 0.1 本 spec 定什么(六项) + +| # | 定项 | 落点 | +|---|---|---| +| 1 | 属性轴的**形式公设 A1-A4** + 七轴逐轴四字段(语义定义 / 载面 / 来源 / 既有机制接口)+ **`determinism` 族七轴**(共享契约 §1.4)与「不确定性 = 显式请求」的执法映射 | §1(+ §1.9) | +| 2 | 义务四档的**判定决策树**(从义务到落档)+ 与 where 三档的映射 + 每档成本与可判定性边界 + **`determinism` 族落档规则**(§1.4 的实例化) | §2(+ §2.6) | +| 3 | **policy = 数据**的条目形态 + 挂法(与 R2 双轴注册表并列)+ **放行 / 声明条目统一 schema(D2)** + 判定走引擎(不另建判定体系)+ **判定形态健全性约定 C1-C5**(`A_a` 只做加约束式单调展开 / ⊥ 非真 / **非退化性**) | §3(+ §3.3.1 / §3.6) | +| 4 | **enforcement 通道**(动态档落地):插入面 / 发射门控 / 失败语义 / 成本纪律 / 与定案 14 的边界 | §4 | +| 5 | 未证明义务的**三面可见**(诊断 / 台账 / 产物)+「不静默」的可测判据 | §5 | +| 6 | **最小可实施切片 A**(trust 轴 + 一条策略 + 正/负控 + 零产物变化判据)+ **候选切片 B**(`determinism` 族隐式竞争 reject——与 A 并列,二选一或两者) | §6(+ §6.5) | + +### 0.2 本 spec 不定什么(划界,交接对象) + +- **各族的流纪律语义**(注入 / 授权 / 信息流 / 围栏 / 状态版本)→ S-B1…S-B5 各自定;本 spec 只给机制、档位与接口。 +- **判定算法本体**(DNF 规范化 / memo / 预算 / 反例)→ R2 P0 已定已实施;本 spec 只**消费** API,不改引擎。 +- **隐式流完整判定**(branch/timing/cache 面)→ S-B3 的边界;本 spec 只在 §2.3 登记「不可强制」类并说明其落档。 +- **where / requires / ensures 的语法实现** → TODO「.cr 规约语法实现」(:440)线;本 spec 的 §2.3 映射停在语义层(现状:`where` 三档**仅设计态**,parser 无 `where` 终结符——见 §2.3 注)。 +- **性能无实现承诺 / 成本模型** → S-C;**可解释与语义增量** → S-D。 +- **汇合语义本体**(`merge_deterministic` / `merge_ordered` / `race` / `select_any`)→ **S-E**;**确定性报告面**(`core analyze --determinism` + 不确定性 provenance 链)→ **S-D**(共享契约 §1.4 边界,逐字)。 +- **图本体(Graph)改动**:无——本 spec 零新 opcode、零新关键字、零 Graph 结构变更(契约 §1.2 原则 1/2)。 +- **.ccr 段布局** → R2 P4 载体批(本 spec 只登记 policy 段的存在,§3.4)。 + +### 0.3 与既有定案的四处张力(**明确指出,不静默改写**) + +| # | 张力 | 处置 | +|---|---|---| +| T1 | **轴 vs「类型 = 图标注」/ R2 双轴注册表**:轴若做成注册表条目(如横切接口),会与「单一概念树(定案 2)」及「规则封闭、对象开放」(v4 §4.5)相悖——每加一轴 = 加条目 + 加判定规则 = 枚举维护 | **轴不进注册表**(§1.0):横切接口问「这个值**长什么样**」(结构形状,单值包含判定);轴问「这个值**从哪来 / 能去哪**」(来源与流向纪律)。轴的价值在判定时**编译为形状项**交给引擎(§3.3),轴本身是标注空间维度(v4「图标注空间加标签——加标签不是新机制」)。**残余风险登记为 §9 开放问题 5** | +| T2 | **四档 vs where 三档(定案 5)**:三档隐含「动态档 → 运行时检查」是动态约束的固定归宿(兜底永远可用);四档显式否定——存在**不可强制**的义务(③) | 共享契约已裁「补成四档」,**以四档为准**。三档保留为**约束形态分类**(常量/符号/动态),四档是**处置分类**——两者是不同分类轴,非竞争关系。关键差异:四档**把运行时检查从「归宿」降格为「允许的兜底之一」**(§2.3) | +| T3 | **authority 轴 vs M1 定论**(能力不提升一等公民;授权归治理层) | **不冲突**。轴 = 标注(开放),非一等公民;且 authority 轴的**载面 = 治理层「可能关系」标注**(M1 的还原结论,v4 §六/§七),不是值流边——这是七轴中唯一载面不同的轴,是 M1 的必然后果(§1.4)。S-B2 的 `Can(a,o,r)` 只做治理层判定 | +| T4 | **enforce 档 vs 定案 14 声明式边界**(本路线图只碰声明式上限;执行/副作用/内存的命令式下限不动) | 检查节点 = **命令式下限的既有形态复用**(图构造,同 `IR_BOUNDS_CHECK`),**不进语义本体**;证明完毕不下发(v4 §十一同款)。语义层不承诺检查存在——prove 档零字节(§4.2/§4.4) | + +**已定 vs 待论证(全局标注约定)**:本文中「定」= 本 spec 的裁决(可实施);「现状事实」= 已核对的代码/文档实况(file:line);「待论证」= 需要证明或用户裁决(§9 汇总)。 + +### 0.4 用户裁决落实索引(2026-09-11,A 簇;★ 项全部照「推荐」列) + +| 裁决 | 内容(一句话) | 本 spec 落点 | A 簇随动文件 | +|---|---|---|---| +| **D1(a)** | `trust` = 可判传播轴(S-A §1.1 为真源);来源真实性的 attestation 不可判 | §1.1(对表)/ §1.3 注 | S-B1 §4.6、S-B3 §1.1/§1.2/§4.2 | +| **D2(a)** | 放行/声明条目统一 schema(**`kind` 五枚 = `declassify|grant|deny|mint|stability`,四族填法**——`mint` = 能力创建点,B 线 N2 补);**默认节点形态**,偏离须显式豁免 + 审计字段 | §3.1 / §3.6 | S-B2 §0.3/§5.1、S-B3 §3.1、**S-B4 Q9(`mint`)**、S-B5 §2.1 C3 | +| **D3(c)** | 默认强度分层:本地登记+继续 / strict 拒编 / CI 从严 / 未声明边界取 unknown / `select` 窗口 W1 = 1 发布周期 | §2.2 附 / §9.2-1 | 路线图 §1.4、S-B1 §5、S-B2 §3.3、S-B3 §5.2/§8.2 Q9 | +| **D4(a)** | 载体随 R2 P4 同波进 `.ccr` 新段(策略随产物分发) | §3.4 / §9.2-4 | S-B1 §8.2-5、S-B3 §6.1 S3、S-B5 §5.1 | +| **D5(a)** | 零新语法:轴标注走插件标签通道;参数级档位约束落 **policy 数据表** | §3.5 / §9.1 / §9.2-2 | S-B1 §2/§6.3、S-B5 §2.3/§5.3 | +| **D6(a)** | 装载期校验 + policy 授权声明根 / 形状项(**不启用 `pub` 强制**) | §3.6 | S-B1 §1.3/§8.2-1、S-B4 Q2 | +| **D7(c)** | 外部效应:先修缺陷止血(**清单唯一**)+ 修链**单独立项** | §1.7 注记 | S-B5 §1.2/§5.2 | +| **D9(a)** | `race` 胜者身份(witness)允许,登记为 `EXPLICIT` 不确定源 + 报告义务 | §1.9 需求 5 | (语义实现归 S-E) | +| **D10(a)** | `race` prove 范围 = **局部**;prove **当且仅当**「声明-实现一致性」证明成立,否则落 ③ | §1.9 需求 1、§2.6 行 3 | (S-E 供给证明结论) | +| **D11** | 增设 **C5 非退化性**(右项非 ⊤ + 正例非 ⊥ + 假/unknown 分因 + 未定义原子取 unknown) | §3.3.1 / §3.6 / §7-#8 | S-B1 §1.2 注记 / N5 | +| **D12** | 依赖完备性审计 = **独立侦查项**;S-B3 S1 启动条件 = 覆盖率报告**无缺口** | §0.4(登记,本 spec 不消费) | S-B3 §6.1 前置 3 / §8.2 Q1、S-B1 §1.2 末、S-B4 §1.3 | +| **D13** | `Can` 带 `state_ver`;κ 粒度 = 对象身份 + 版本见证(标签随版本) | (S-B2/S-B5 落点) | S-B2 §1.3/§8.2-3、S-B5 §5.3/Q1、S-B3 §8.2 Q5 | +| **D14** | M1 代价**接受**(无法表达运行期授予/撤销;延伸走能力对象 + 版本见证) | (S-B2 §0.4 + 路线图 §2 行) | S-B2 §0.4 | +| **D15** | policy 档位粒度 = 全局默认 + **每策略覆盖**(键 `(ns, policy_name)`;每轴留扩展位) | §3.1 档位行、§9.2-3 | — | +| **D19** | 边界节点 = **合法的显式不确定源**(主人 = 边界节点 id;链取值 `EXPLICIT`) | §1.9 接口面登记 | S-B3 §8.2 Q8 | +| **D22** | 「沿 def 链倒推」唯一 owner = **S-B2 §2.1 步①** | (S-B2 落点) | S-B2 §2.1/§6.3、S-B4 Q4、S-B5 §3.2 | +| **D23** | 外部不可见面 vs 并发面 = 按**变更源身份**可判定分界 | (S-B5 落点) | S-B5 §2.1/§5.4 | +| **D29** | S-D 增量 / 报告接口归 **S-D 线统一**;各族只写消费点不写格式 | §5.2b 注 | S-B2 §2.4 | +| **D30** | 族内技术项逐条按各族建议裁(句柄形态 = 并列类型 / `child` 失败 = panic(v0)/ L2 维持 / 授权集开放词汇 / 标签编码 product / 默认二元素格 …) | — | S-B1 §8.2、S-B2 §5.2/§5.3、S-B3 §8.2、S-B4 Q1-Q8、S-B5 §5.3/§7.2 | + +(D8 / D16 / D17 / D18 / D20 / D21 / D24–D28 的落点在 B 簇文件(S-C / S-D / S-E),本索引不重复;A 侧只保接口面一致。) + +--- + +## 1. 属性轴(基础七轴 + `determinism` 族) + +### 1.0 轴的形式公设(**本 spec 的裁决**) + +> 术语「属性轴(attribute axis)」按共享契约 §1.1 逐字使用:**图对象/边上的开放标注维度**,七轴 = `trust`(可信度)/ `secrecy`(机密性)/ `integrity`(完整性)/ `authority`(授权)/ `lifetime`(生命周期)/ `state-version`(状态版本)/ `effect`(副作用)。 + +- **A1 值域不固定**:**本 spec 不为任何轴固定值域、偏序或格代数**——三者均为 **policy 数据**。理由 = v4「无格承诺」(§4.6):判定与定理只在关系层面陈述;任何具体格 = 签名 + 恒等式 = 封顶。 + - 各轴**必须**具备三件结构(形式要求,非具体取值):① 值域(有限或无限)② 偏序(关系层面,用于「谁强于谁」)③ **传播规则**(沿值流边如何派生)。 + - 承 A1 的直接后果:新增轴 = 在标注空间加一个维度(v4「加标签不是新机制」),**不动 Graph 本体、不加关键字**(契约 §1.1 policy 定义)。 +- **A2 不升格 primitive**:轴 = 开放 semantic annotation / relation;**只有义务可能拒绝编译**(契约 §1.2 原则 1,承 M1 定论与定案 1/12)。 +- **A3 传播单调**:派生不提升信任、不降低机密性、不提升完整性、不延长生命周期(各轴方向由 policy 定,但**单调性本身是形式要求**)。唯一例外 = 显式**净化/降密节点**。 +- **A4 唯一放行通道 = declassify**(共享契约 §1.1:`Secret → Public` 的唯一合法通道;同构地 `Untrusted → Trusted` 的净化节点)。且 declassify 节点**自身带 authority 义务**(降密需要授权)——这是轴间耦合的第一实例(§1.8)。 + +### 1.1 `trust`(可信度) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 值是否由受控方产生:`untrusted ⊑ trusted`(**信任只降不升**——派生不提高信任;唯一提升 = 净化节点) | +| 载面 | **节点标注(source 节点)+ 沿值流边的传播**(与 provenance 同族——出生节点记录来源,传播 = 图上可达性) | +| 来源 | **领域库声明**(不可信来源:`read_file`/`get_env`/argv/网络)+ **自动推导**(传播)+ **显式标注**(净化节点 = 库提供的类型化构造器,契约 §1.1 sink/source 模型) | +| 既有机制接口 | provenance(`docs/pointer-model.md`:出生节点即来源,**同一条边**,零新图机制)+ `ext_safety.cr` 钩子(enforcement 落点)+ parser 标签位/`g_plugin_tags`(标注载体,§3.5) | +| 首消费方 | **S-A 切片(§6)**、S-B1(注入——含 `syntax_raw` 裸位置判据)、**S-B3(对偶方向 `Untrusted ↛ Trusted` 的传播面)**、S-B4(围栏——不可信路径数据) | + +**三份消费方对表(D1(a) 共同要求;2026-09-11 裁决)**:**谁定义** = 本表(S-A §1.1,术语唯一真源);**谁判定** = 判定引擎(§3.3 单式 `ty_sub(τ_value ⊓ A_trust(v), Shape(π))`,三态,不另建体系);**谁是消费者** = S-A §6 切片(裸位置)、S-B1(`syntax_raw` 裸位置)、S-B3(`Untrusted ↛ Trusted` 传播面)、S-B4(不可信路径数据)。**两面对照(不得混同)**:`trust` 的**程序内传播**可判(本轴,就是上表);**来源真实性的 attestation**(谁生产/签名/背书该来源)**不可判**——属 §1.3 第 2 类,只能用可复现构建 / 签名 / provenance **缩小**(§8 第 2 行)。 + +### 1.2 `secrecy`(机密性) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 值是否允许流向公开观察面:`secret ⊑ public`;**降密 = declassify(唯一通道,A4)** | +| 载面 | 节点标注(secret 产生点:密钥 / 私钥文件 / 密码缓冲)+ 沿值流边传播 + sink 标注(公开面:打印 / 网络 / 日志 / 诊断消息) | +| 来源 | 领域库声明(机密来源)+ 自动推导(传播) | +| 既有机制接口 | 与 trust **同构对偶**(`Secret ↛ Public` ⟺ `Untrusted ↛ Trusted`)——同一机制、两套轴数据 | +| 首消费方 | S-B3(信息流;含隐式流登记面) | + +### 1.3 `integrity`(完整性) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 值是否可能被非授权方改写(数据完整性;与 trust 对偶但不同轴——trust 问来源,integrity 问可篡改性) | +| 载面 | 节点标注(外部输入默认低完整性)+ 沿值流边传播 | +| 来源 | 领域库声明(外部输入面)+ 自动推导(传播) | +| 既有机制接口 | provenance / 出生节点;**与 `state-version` 轴耦合**(版本 = 完整性的时间证据,§1.8) | +| 首消费方 | **经 2026-09-11 复核(A 线 N3)修正为:「本 spec 暂无经对侧确认的消费方(登记)」**——候选 = S-B2 / S-B5,但 `integrity` 在 S-B2 / S-B5 / S-B4 三份正文中**零次出现**,**由本 spec 单方指派的消费关系不成立**(与 A 线 C1 同类:轴归属的单向声明)。**已落地的对侧确认只有一处**:S-B5 以 **`state-version × integrity`** 形式消费(其 C2/C3 判据 = 版本 = 完整性的时间证据,§1.8 行 + S-B5 §2.1;该行已回引)。S-B2 侧**明示不消费**(其耦合面 = `effect × authority`,A 线 N3 修法)。**注**:S-B3 的 `Untrusted ↛ Trusted` 方向消费的是 `trust` 轴(§1.1,D1(a)),**不消费本轴**——本轴不设流纪律判定面 | + +### 1.4 `authority`(授权) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 「什么操作被允许」的标注——承 **M1 定论**:授权归**治理层**(v4 §六第 3 步还原),不是值属性 | +| 载面 | **治理层的关系标注**(A ──可能执行──> B,v4 §六/§七「可能关系」)——**不是值流边**。基础七轴中唯一载面不同的轴(T3) | +| 来源 | **声明式进图**(`alloc_at` 先例:声明即注册,v4 §八/§十)+ 领域库/治理声明 | +| 既有机制接口 | capability-lattice v4:能力成分 ⟨身份符号, 授权集, 域约束, 派生源⟩ → v4 还原(身份 = 图节点身份 / 授权集 = 治理层可能关系);`unsafe`/`alloc_at` = 例外入口授权(能力创建点) | +| 首消费方 | S-B2(`Can(a,o,r) ⟺ ∃合法 capability path`);与 `effect` 轴耦合 | + +### 1.5 `lifetime`(生命周期) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 值/条目的存在区间——何时存在、何时失效(v4 §11.5「存在区间 = 图活性」) | +| 载面 | **region**(图锚定区域 `SG_IF`/`SG_LOOP`/`SG_FOR`/`SG_FLOW`/`SG_UNSAFE` 的存在区间,`dataflow.cr:428-432`)+ 条目(IR 变量版本) | +| 来源 | **自动推导**(region 推断 / 逃逸 pass——用户零标注,定案 8/9)+ 领域库声明边界(arena 归属) | +| 既有机制接口 | `RegionCheck`(逃逸 pass)+ 缓存语义条目/共存关系(v4 §11.5:存在区间相交 = 共存)+ 寄存器分配(映射实例) | +| 首消费方 | S-B4(围栏)、S-B5(版本)——**本轴载面已实现**(基础七轴中唯一) | + +### 1.6 `state-version`(状态版本) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 值绑定 = **版本化**(`docs/memory-model.md` 条款 5:赋值 = 创建新版本 / 旧版本失效 / 绑定移动);轴 = 「这个值观测的是哪个版本」 | +| 载面 | **条目 / IR 变量版本**(图上已存在)+ 产生节点(产生节点-版本的「版本对」) | +| 来源 | **自动推导**(版本编号由图构造生成——零标注) | +| 既有机制接口 | **state edges**(顺序约束 = 版本序的落地形态,`dataflow.cr:129,157`)+ 条目与配方(条款 1)+ 缓存语义(条款 5) | +| 首消费方 | S-B5(check/use 分离版本 = 图上两版本;TOCTOU 判定 = 版本等价判定) | + +### 1.7 `effect`(副作用) + +| 字段 | 内容 | +|---|---| +| 语义定义 | 节点/值是否产生副作用(IO / FFI / unsafe / volatile / 写内存) | +| 载面 | **节点标注** + **state edges**(副作用链——顺序表达**已有**;缺的是副作用**类别**的标注面) | +| 来源 | **自动推导**(ir_gen 判定:写内存 / 不纯调用 / unsafe → state edge 链;`docs/lazy.md` 判定表「有副作用 → 永远 eager」已有)+ 领域库声明(FFI 效应集) | +| 既有机制接口 | state edges + 惰性/记忆化判定(TODO auto-memo「图显式纯度判定」)+ `unsafe` 边界 | +| 首消费方 | S-B2(effectful op)、S-C(纯度/效应标注)、S-D(增量失效面——副作用 = 不可缓存边界) | + +**外来注记(2026-09-11;D7 裁决 + **本批修复**)**:本轴的**保守面**为——**效应 = 除「引擎判定纯者」外的一切 `IR_CALL` / `IR_CALL_EXTERN`**;**已于效应/纯度修正批退化为其字面定义**(`762bd429` Task 1 真纯度计算 + `c9099d73` Task 2 分类表补全/单一真源;落地登记 = TODO #27)。**事实底座(修复后)**:`fi_ispure` 由 `compute_all_purity`(`checker.cr:2959`)在 IR 生成后按全程序 IR 面写回真值(保守起点「全不纯」+ 调用图不动点升纯 + 递归/SCC 保守 + 不可解析调用保守 + `unsafe` 区不纯 + 泛型实例/源合取);`df_connect_state`(`dataflow.cr:146`)的入链判据收敛为单一真源 `purity_op_effect`(`checker.cr:2912`:store 家族 + `IR_CALL_EXTERN`/`IR_SPAWN`/`IR_YIELD`/`IR_HOTPATCH_ROUTE`/`IR_DYN_DISPATCH`/`IR_STORE_PTR`/`IR_AWAIT`)⇒ `print` / `read_file` / `chan_send` / `sched_go` 等可解析但不纯的调用**全部入 state 链**。**修复前状态(历史快照,保留备查)**:两写入点值恒 1(`checker.cr:1107`「optimistic: all functions are pure」/ `:1142` extern)+ 分类表不含 `IR_CALL_EXTERN` ⇒ 上述调用均不入链。 +**回填记录(2026-09-11,Task 4 Step 4 = 原「待纯度批落地后回填」marker 的落点)**:本批实测(详证据 = TODO #27):全量回归 38/38 rc=0 · 语料级「旧前端 vs 当前全链」ELF 逐字节 20/20 同 · canary `tests/suite/ptr_arith.cr` = `95084e7b…d475` IDENTICAL · 自举稳定(两连建产物一致 + `corec2b`/`corec3b` cmp IDENTICAL + N06=0 + 冒烟 42);`.ccr` 差异 = 仅 chain(kind=1)边增删(NOD 语义字段零差异、STR/SYM/ENT/REG 逐字节不变)。**残余面(限定,非本轴裁决)**:lazy 判定有意冻结(`ir_gen.cr:1590` 生成期仍读乐观默认值;其 use_count 时序缺陷独立批——TODO「控制流自动惰性」);链仍**每函数重置**(跨函数不连——判据域内调用节点 = 被调者效应的序代理)。S-C §1 / S-E §1.1 / S-B5 §1.2 / S-D §1.6 的对应措辞同批重述。 +**关键约束(硬性,未变)**:**外部效应 / 变更源清单只允许存在一份**(挂本 spec 的 policy 面),S-B3/S-B5/S-E 一律**引用**,不得各自成表(否则必然漂移)。**口径(B 线 N7,防误读为"措辞也不得重复")**:「唯一」= **枚举清单唯一**(哪些 API / 事件属外部效应 / 变更源);**公式**(「效应 = 除引擎判定纯者外的一切 `IR_CALL` / `IR_CALL_EXTERN`」)**在三处逐字出现不算双源**(三处 = 本处 / S-C §1 注 / S-E §1.1),**权威定义仍以本处为准**。链覆盖的 opcode 级清单**另一份唯一**(`purity_op_effect`,`checker.cr:2912`——D7 的机械保证:新增 IR opcode 必须同步该处)。 + +### 1.8 轴间耦合(登记,不展开) + +| 耦合 | 内容 | 归属 | +|---|---|---| +| `trust` × `integrity` | 对偶但不同轴:来源可信 ≠ 不可篡改(外部可信通道仍可被中间人改写) | S-B1 / S-B2 | +| `secrecy` × `authority` | **declassify 自身带 authority 义务**(谁能降密)——A4 的落地 | S-B3 + S-B2 | +| `state-version` × `integrity` | 版本 = 完整性的时间证据(check 与 use 必须同版本)。**这是 `integrity` 目前唯一经对侧确认的消费面**(S-B5 以本形式消费,S-B5 §2.1 回引;A 线 N3) | S-B5 | +| `effect` × `authority` | effectful op 须产出证明(授权面耦合) | S-B2 | +| `lifetime` × `effect` | 副作用链的存活范围 = region 边界(arena reset 面) | S-B4 | + +**本节的整体判断(诚实评估)**:七轴中 `lifetime`/`state-version`/`effect` 的**载体已在图上跑**(region / 条目版本 / state edges),`trust` 的传播与 provenance 同边,`authority` 载面是治理层既有形态。**S-A 的工作 = 把「标注维度」显式化 + 给判定与落档流程,不新建图机制**——这是本 spec 可小步落地(§6)的结构性理由。§1.9 的 `determinism` 族同理(`D_effect`/`D_memory` 直接复用 state edges / region)。 + +### 1.9 `determinism` 族(`D_*`,七轴——共享契约 §1.4 追加裁决,2026-09-11) + +> §1.4 已定:轴族**并入 §1.1 轴表**;**轴族/义务落档属 S-A**;报告面属 S-D;汇合语义本体属 S-E。本节只做轴的四字段 + 执法映射,**不重定义 §1.4 的任何内容**(不重定义、不稀释)。 + +**定位(不可混淆)**:基础七轴(§1.1-§1.7)是**正交属性**;`determinism` 是**族**——七个正交轴(`D_*`)**各自独立标注、独立落档**,与基础七轴并列构成轴表,**禁止合并成单一 `determinism` 标注维度**(合并即丢失「时间三值化」「跨机器语义确定性」等分轴判据,且违反 §1.4「轴族」措辞)。 + +| 轴 | 语义定义(§1.4 逐字) | 载面 | 来源 | 既有机制接口 | +|---|---|---|---|---| +| `D_value` | 值/结果确定性 | 节点(纯值流) | 自动推导(§1.4 现状「强」) | 值流边 + 引擎判定(无副作用边即纯) | +| `D_effect` | 副作用顺序确定性 | **节点 + state edges**(副作用链) | 自动推导(§1.4「有基础(state edges 显式)」) | state edges(`dataflow.cr:129,157`);与 `effect` 轴(§1.7)**同载体、不同问题**(`effect` 问「有无副作用」,`D_effect` 问「顺序是否被固定」) | +| `D_memory` | 内存行为/生命周期确定性 | region + 条目 | 自动推导(§1.4「强(region 锚定 + bump/reset)」) | region / arena / 共存关系(v4 §11.5);与 `lifetime` 轴(§1.5)同载体 | +| `D_timing` | 时间 / WCET | 节点/region 上界标注 | **三值**(§1.4 硬性) | `PROVED`(机器无关上界:分配次数/字节、在飞任务数、通信量——可从 region/图证明;接 bounds-inference)|`MODELED`(机器相关 = **成本模型估计,明确标注「模型非证明」**,复用 S-C 成本模型)|`UNKNOWN`。**禁止把 MODELED 呈现为 WCET 证明**(→ §5.2a 措辞/码可区分) | +| `D_resource` | 资源上界确定性 | 节点/region 的静态 bound | 自动推导 + 声明(§1.4「有基础(需静态 bound)」) | bounds-inference(TODO);接 S-C 成本模型 | +| `D_external` | 外部输入依赖确定性 | **边界节点**(无配方条目:MMIO/FFI/输入/测量) | 领域库/部署声明(§1.4「边界节点可归档(图上有边界)」) | 边界公理(memory-model 条款 4)+ 图内不可重算条款(v4 §5.3);与 `trust` 轴(§1.1)**同边界点、不同问题** | +| `D_mapping` | **跨机器语义确定性**(Core 特有轴) | **语义层性质**(「语义 ≥ 映射」——不是某个图对象的标注) | 自动(结构性质) | 三层映射(图→格→编码)+ v4「证明完毕不下发」;承载 = 映射实例的语义保持定理面。**唯一的非标注型轴**——本 spec 只登记,不建机制 | + +**执法 = 四档义务(§1.4 表 → §2.2 决策树的实例化)**: + +| §1.4 情形 | 本 spec 落档 | 说明 | +|---|---|---| +| 依赖/状态边足证「汇合与调度无关」 | **① prove**(零成本) | 引擎真(判定输入 = 图上显式依赖/顺序源) | +| 顺序由显式顺序源固定(`merge_ordered`) | 落到确定实现 | 义务已由图上显式顺序源处置——**不新增档**(四档约定不破) | +| 语义明文允许(`race` / `select_any`)**且「声明-实现一致性」证明成立** | **① prove + 报告义务**(**不是第五档**) | 显式请求 + 声明与实现一致 ⇒ 义务已被「程序说的是它想说的」处置;报告面登记归 S-D。**证明范围 = 局部**(只覆盖该汇合点本身,**不覆盖观测点**)——该未覆盖面必须在报告面**显式标注**(D10(a))。**证明不成立 ⇒ 落 ③ 登记**(不是 prove)。§1.4 明言「与 §1.1 四档同构」,故**不引入第五档** | +| 既未证明也未标注(**隐式竞争**) | **④ reject** | 该族 policy 取值 = `allow_enforce=false` + `require_proof=false` + `strict=true` → §2.2 决策树**固定落到 ④**(**无需改树**——档位是 policy 数据,§3.1)。§1.4「真正的执法点」 | + +- **判定侧硬约束(本 spec 给出)**:在 S-E 完成 `select` 重定之前,`select` **不得**被当作「显式顺序源」用于 prove——否则等于把现有语义缺陷(§1.4:现 `select` = 「最早到达的令牌」,把物理调度顺序泄进语义)**固化为证明前提**。迁移路径与兼容窗口归 S-E;**窗口 W1 = 默认 1 个发布周期**(§1.4,D3):窗口内本禁令不解除,窗口后强制标注。 +- **S-A ↔ S-E 接口需求**(本 spec 只提**需求与供给**,不定义其语义——§0.2 边界): + 1. **需求(S-A 消费面)**:S-E 为四形态各给出**图上可判的显式标记**(判定输入)——`merge_deterministic`(汇合无关)/ `merge_ordered`(显式顺序源)/ `race` / `select_any`(显式不确定请求)。S-A 的落档规则(§2.6)**消费两面**:① **标记存在性**(不读其内部语义);② **该标记的「声明-实现一致性」证明结论**(S-E §1.3 / §1.5 第 5 步;2026-09-11 裁决 D10(a))。**`race` / `select_any` 的 ① prove 当且仅当 ② 成立**——② 不成立 ⇒ **落 ③ 登记**(不是 prove)。② 的证明**范围 = 局部**(只覆盖汇合点本身):报告面**必须**标注「该保证**未覆盖观测点**」(不覆盖性由 S-D 的报告面承担)。 + 2. **需求(解除条件)**:S-E 的 `select` 重定须使「显式顺序源」不再含物理调度泄漏——即本条硬约束的**解除条件**(窗口 W1 见上)。 + 3. **供给(S-A 面)**:四档落档规则(§2.6)+ 档位 policy 数据(`allow_enforce` / `require_proof` / `strict`,§3.1)+ 义务台账与诊断接口(§5)——S-E 的四形态可**直接对表落档**,不必自建义务机制。 + 4. **冲突检查点**:`race` / `select_any` 的「显式不确定 = prove + 报告义务」须由 S-E 确认**不引入第五档**(§1.4 明言「与 §1.1 四档同构」)。 + 5. **供给/登记(D9,2026-09-11 裁决)**:**`race` 的胜者身份允许作为显式出参(witness)**——本 spec 侧无需新机制:witness = **显式语义源**,登记为 `EXPLICIT` 不确定源 + 报告义务(= 本表行 3 的实例;可见性义务由 S-E 的既有报告机制覆盖)。语义形态归 S-E。 +- `MODELED` 的诚实纪律与 §5.2a / §7-#7 同一判据:**模型非证明**必须可在诊断与报告面区分(不得把 MODELED 呈现为 PROVED)。 + +--- + +## 2. 义务四档 + +> 术语「义务(obligation)」按共享契约 §1.1 逐字使用:**由属性轴导出的、必须在编译/验证期被处置的判定要求**。四档 = ① **prove**(静态可证 → 零成本通过)② **enforce**(不可证但可运行时强制 → 插入 enforcement)③ **proof-required**(不可证也不可强制 → 要求用户提供 proof/contract)④ **reject**(高安全模式下仍无法处置 → 拒绝编译)。 + +### 2.1 义务的形态 + +``` +义务 O =(轴 a,源标注 S,汇要求 R,落档策略 policy) +判定输入 = 图上由 S 到 R 的「值可达性 / 形状包含性」判定(判定形态见 §3.3) +义务状态 = open → {proved | enforced | proof-provided | rejected};**不存在 open → passed**(§5.1 禁令) +``` + +### 2.2 从义务到落档:判定决策树(**本 spec 的裁决**) + +``` +义务 O(轴 a,源标注 S,汇要求 R) + │ + ├─ 引擎判定(ty_sub 三态;常量档 = 判定输入为编译期常量) + │ ├─ 真 ⇒ ① prove(零成本通过;不下发任何字节) + │ └─ 假 ⇒ **违法**(编译错误;任何 policy 档位都拒绝) + │ 唯一放行 = 显式例外入口(declassify / 净化节点 / unsafe 边界 / + │ **授权族的 grant 声明**——均入 §3.1 放行·声明条目并落台账,D2), + │ 且放行本身**登记为义务**(不静默——v4「例外入口授权可追踪」) + │ + └─ unknown(三态第三态:判定预算耗尽 / 桥接失败 / 名称不展开) + ├─ **存在 enforcement 点**(检查语义定义良好 + 图上有插入面) + │ ├─ policy 允许 ⇒ ② enforce(插检查节点;失败语义按策略,默认硬陷阱) + │ └─ policy 档位 strict ⇒ ④ reject(或要求 proof 覆盖该检查点) + └─ **不存在 enforcement 点**(不可强制:隐式流 / 信任假设 / 规范正确性) + ├─ policy 允许 ⇒ ③ proof-required(登记义务 + 要求 proof/contract; + │ 未提供时 = 显式诊断 + 台账条目,**不是通过**) + └─ policy strict 且未提供 ⇒ ④ reject +``` + +三点关键区分(防误读): + +1. **「违法」与「④ reject」不同**:违法 = **已判定为不合法**(引擎返回假),任何档位下都拒绝;④ = **无法处置**(判不出、不可强制、又不满足 policy),是「不知道」的结果。前者措辞 = 违反(含反例值,复用 `tt_witness`);后者措辞 = 未处置(含出路建议)。 +2. **三态是决策树的地基**:R2 P0 引擎的三态(真 / 假 / unknown——unknown 含预算耗尽与未覆盖面)使「证不出」不是异常而是**第一等输入**。这与 R2 §3.3 的「预算超限 ≠ 类型不兼容」同构:**不可判定 ≠ 不合法**。 +3. **档位选择是 policy 数据**:同一条义务在不同 policy 档位下落不同档——「同一程序、不同 policy、不同档」是特性而非缺陷,即「安全 = 相对 policy」的诚实面(§8 第 1 类)。 + +**附:默认强度分层(2026-09-11 用户裁决 D3(c),规范性)**: + +| 面 | 默认(本地 / 开发) | 严档(strict / CI / 高安全) | 依据 | +|---|---|---|---| +| 档 ③ | **登记 + 继续编译**(③ = `open`,**不是通过**) | **拒编**(④) | §5.1 状态机;§9.2-1 已裁 | +| 档 ② | **默认启用**(可 enforce 的一律 enforce) | **关闭 enforce 通道** ⇒ ④ | 档位 = policy 数据(§3.1) | +| 未声明的**边界条目** | 取 **`unknown`**(保守判定方向)——**不拒编**,落 ③ + **报告面可见** | **拒编** | 不得把「不知道」渲染成「确定 / 公开」(S-B3 §8.2 Q9 随此裁) | +| CI 门槛 | — | **从严**(strict 档 = CI 断言义务台账为空,§5.2b) | 「本地不阻塞、CI 守门」分成两个面 | +| `select` 迁移 | 窗口 **W1 = 1 个发布周期**(窗口内 lenient + 具名诊断) | 窗口后**强制标注** | 共享契约 §1.4;实现归 S-E;**不承诺永久 lenient** | + +- 本表是**规范形态**:各族的「默认 / strict」两栏取值一律引用本表,**不得各自发明**(档位取值见 §3.1)。**族级例外由 S-A 的族级裁决给出**(如 §2.6 / §1.9 给 `determinism` 族取 `allow_enforce=false`——见 §3.1 的每策略覆盖,D15)——**各族只引用,不自设数值**;本句限定并存(A 线 N5)。 + +### 2.3 四档 × where 三档映射(定案 5 的补完) + +| where 三档(定案 5) | 四档落位 | 说明 | +|---|---|---| +| **常量档**(约束输入 = 编译期常量)→ 编译错误/通过 | 真 → ① prove;假 → **违法** | 与三档原语义一致 | +| **符号档**(约束输入 = 符号项)→ VC 义务 | 真 → ①;unknown → **③ proof-required**(VC 义务,验证管线消费);假 → 违法 | 三档的 VC 面 = 四档 ③ 的一个**来源** | +| **动态档**(约束输入 = 运行期值)→ 运行时检查 | **② enforce**(插运行时检查);strict → ④ | **四档的收紧点**:三档把「运行时检查」当动态档的固定归宿;四档把它**降格为允许的兜底之一**(§0.3 T2) | +| (三档无对应)**不可强制义务** | **③ / ④** | 四档的**新增面**:隐式流(branch/timing)、信任假设(供应链)、规范正确性——三档结构上未覆盖。**实例**:确定性族的隐式竞争 → ④(该族 policy 固定,§1.9) | + +- 三档自带的「防『证明不了也不静默』」措辞(定案 5 原文)与契约 §1.2 原则 3 同向——四档把它补完为**档位谱 + 拒编**。 +- **现状注**:`where` 三档当前**仅设计态**——parser 无 `where`/`requires`/`ensures` 终结符(TODO :440 挂账,语法实现未落地)。故 §2.3 映射停在**语义层**:四档流程可先于 where 语法实施(§6 切片不依赖 where)。 + +### 2.4 每档的成本与可判定性边界 + +| 档 | 经典路径成本 | 可判定性边界 | 失败语义 | +|---|---|---|---| +| ① **prove** | **零新增字节**(证明完毕不下发——与 bounds-inference、缓存语义 v4 §十一同款) | 引擎可判定子集内且预算内返回真 | n/a | +| ② **enforce** | 检查节点字节(先例 `IR_BOUNDS_CHECK` ≈ cmp+jb+ud2);最坏 = 每 sink 一次 | **不可判定但检查语义可定义**(检查点存在) | 默认**硬陷阱/中止**(与先例一致:「中止而非未定义值(安全优先)」,`ir-op-semantics.md:196`);库可声明其他(§4.3) | +| ③ **proof-required** | 零运行期成本;成本 = 用户/领域库的 proof/contract 撰写 + 验证管线时间 | **不可判定且不可强制**(隐式流 / 信任假设 / 规范正确性) | 默认:诊断 + 台账条目(登记,**不是通过**);strict:拒绝 | +| ④ **reject** | n/a | 策略档位的后果(非判定结果) | 编译错误(响亮 + 出路建议,§5.4) | + +- 预算边界(**诚实项**):引擎有判定预算守卫(R2 P0)。预算耗尽 → unknown → 按本决策树落 ②/③/④,**绝不当通过**;且诊断必须区分「预算耗尽」与「判定为假」(R2 §3.3 同款)。 + +### 2.5 判定的可靠性责任(**待论证,登记**) + +本 spec 定的判定**入口** = 引擎(契约 §1.2 原则 4);「判定为真 ⇒ 该族的流纪律成立」这一**可靠性**是**各族的证明义务**,不是本 spec 的承诺: + +- 判定目标形态 = 单值判定:`ty_sub(τ_value ⊓ A_a(v), Shape(sink))`(§3.3)。 +- **待论证的前提**:轴传播单调(A3)+ 单值判定的忠实编码 ⇒ 图上「源到汇」的路径性质(不可达性)。若某族该前提不成立(典型:隐式流——值未流动但信息流动),该族落 ③(不可强制)或另立图可达性 pass,**不得**用单值判定冒充。 +- 登记项:各族在自身 spec 中给出「判定真 ⇒ 纪律成立」的论证或反例边界。 + +### 2.6 `determinism` 族的落档规则(§1.4 的实例化——**规范形态**) + +> 本族**不新增档**:§1.4 逐字「执法 = 四档义务(与 §1.1 四档同构)」。轴表与载面见 §1.9;本节给**规范形态**(实施批以此为准)。 + +| 判定输入(图上事实) | 落档 | 依据(§1.4 对表) | +|---|---|---| +| 依赖 / 状态边**足证**「汇合与调度无关」 | **① prove**(零成本) | §1.4 行 1 | +| **显式顺序源**存在(`merge_ordered`) | 落到确定实现(义务已由显式顺序源处置——**不新增档**) | §1.4 行 2 | +| **显式不确定请求**(`race` / `select_any`)**且「声明-实现一致性」证明成立** | **① prove + 报告义务**(**非第五档**;报告面登记归 S-D) | §1.4 行 3——「语义明文允许 ⇒ 程序说的是它想说的 ⇒ 义务已处置」;**证明范围 = 局部**(报告面须标注「未覆盖观测点」);**证明不成立 ⇒ ③ 登记**(D10(a)) | +| **既未证明也未标注**(隐式竞争) | **④ reject**(§1.4「真正的执法点」) | §1.4 行 4——该族 policy 取值 `allow_enforce=false` + `require_proof=false` + `strict=true`(§3.1 数据)→ §2.2 决策树**固定落 ④**,**无需改树** | + +两条硬约束: + +1. **④ 的直接性来自 policy 取值,不是判定结果**:隐式竞争在引擎侧是 **unknown**(判不出「汇合无关」,也不存在显式请求);其 ④ 是**该族 policy 禁止 ②/③ 兜底**的结果。若未来放宽(例如「要求提供汇合证明」→ ③),改的是 **policy 数据**,决策树与判定器不动。 +2. **`select` 判定侧禁令**(§1.9):S-E 重定前,`select` 不得作为「显式顺序源」输入 prove——否则把现有语义缺陷固化为证明前提。 + +--- + +## 3. policy 接口 + +> 术语「policy」按共享契约 §1.1 逐字使用:一组图上约束(`G + P ⊢ Safe` 里的 `P`)——**数据而非语法**——新增安全模型不改 Graph 本体、不加关键字。 + +### 3.1 policy 条目形态(**设计已定**,字段为建议集,实施批可增补) + +| 字段 | 内容 | +|---|---| +| `name` / `ns` | 策略名 + 命名空间(沿用 `register_plugin_tag` 的 `(ns_ni, name_ni)` 冲突规则:同名不可重复注册、命名空间不遮蔽无限定条目——`globals.cr:317-327`) | +| 涉及轴 | 该策略消费的轴(基础七轴或 `determinism` 族之一或其组合,§1.8/§1.9) | +| **source 声明集** | 哪些节点是来源(领域库声明的类型化构造器,契约 §1.1 sink/source 模型) | +| **sink 声明集** | 哪些汇是危险落入点;每个 sink 需区分**语法位**与**值位**(S-B1 的参数化通道) | +| 流纪律规则 | 非法边的形状(例如「untrusted 源头沿值流可达 sink 的语法位」) | +| **落档规则** | 本策略对 ②/③/④ 的取舍(`allow_enforce` / `require_proof` / `strict`)——§2.2 决策树的 policy 输入 | +| declassify 通道 | 本策略认可的净化/降密节点(A4 的唯一放行通道)。**与下行「放行 / 声明条目」的关系(A 线 ⚠️2,防实施批建两张表)**:本栏**不是**第二张表——它是下行条目栏的 **`kind=declassify` 投影**(同一存储面、同一 schema,只是按策略视角列出"本策略认可哪些")。**实现只建一张表**(放行 / 声明条目);本栏 = 该表按 `kind` + 策略名的查询视图 | +| **放行 / 声明条目**(**D2(a) 统一 schema**) | **任何绕过义务的通道(`¬义务 → 义务`)只经本栏条目,且必须落台账**(§5.2b)。条目字段 = `kind{declassify|grant|deny|mint|stability} · 主体/身份 · 目标/范围 · 理由 · **派生源** · 义务引用`。**`kind` 语义**:`declassify` = 显式降密/净化放行;`grant`/`deny` = 对**既存**对象/权限的授予/拒绝声明;**`mint` = 能力创建点**(根声明 / `alloc_at` 类例外入口授权——**声明的目标本身即新建的域**,与 `grant`「授予既存对象的权限」语义不同;B 线 N2 修法采纳「增一枚」支);`stability` = 治理层稳定性注解。**形态默认 = 图上显式节点**(可枚举、可审计);**偏离节点形态者必须在本栏显式豁免并给出理由**,且一律**补齐审计字段**(理由 / 派生源 / 义务引用),**不得沉默**。**四族填法**(各写一行「本族条目在本栏的形态」):S-B3 §3.1(`kind=declassify`,节点)/ S-B2 §0.3·§5.1(`kind=grant|deny`,声明型边集合)/ **S-B4 Q9(`kind=mint`,根声明条目——声明即注册,能力创建点)** / S-B5 §2.1 C3(`kind=stability`,治理层注解) | +| 档位粒度(**D15**) | 两级:**全局默认 + 每策略覆盖**(键 = `(ns, policy_name)`);**每轴粒度留扩展位**(键形式预留,不实施)。默认值取自 §2.2 附的强度分层表 | +| 诊断码 / 消息模板 | 沿用 `error[XXXX]` 体系(§5.2a) | + +### 3.2 挂法:与 R2 双轴注册表**并列**,判定**共用入口** + +- **并列表**(不复用 `g_ifaces`/`g_impl_for`),三条理由: + 1. **语义不同**:策略不是类型接口——混表 = 把漏洞类型做成类型特判(契约 §1.2 原则 2 禁止「lint 换皮」)。 + 2. **布局遗产**:`g_ifaces`(1432B/条、16 方法 × 88B 上限)+ `g_impl_for` 的名字拼接遗留是 R2 §2.4 的**升级对象**;策略表另立 → 两者互不阻塞。 + 3. **增量性**:策略新增 = 加数据行(契约:新增安全模型不改 Graph 本体)。 +- **共用入口**:查询 API 与 R2 `iface_*` 家族**同风格**(`policy_*`),判定**唯一**走引擎(契约 §1.2 原则 4:「不另建判定体系」)。 +- **现状事实**:`g_plugin_tags`(24B/条 `ns/name/data`)已是「插件可注册的开放表」原型——策略表是其**语义升格**(同一形态,语义从「标签位」升为「策略条目」),非新机制。 + +### 3.3 策略 → 图上的形状项/约束(**判定走引擎**) + +``` +A_a(v) :轴 a 的取值 v → 类型项(**policy 数据**,各族自定;A1 不固定值域) +Shape(sink) :sink 的形状项(policy 数据;语法位 / 值位的区分在此编码) +判定 :ty_sub( τ_value ⊓ A_a(v) , Shape(sink) ) ← ⊓ = 引擎 INTER(不新增判定算子) +``` + +- 引擎返回三态 → 直接进 §2.2 决策树。 +- 反例值:判定失败时用 `tt_witness` 给**具体值示例**(R2 §3.2 已有),诊断质量直接受益。 +- **不新增判定算子、不新增规范化规则、不新增 memo 语义**——策略侧只提供数据(§0.3 T1)。 +- **诚实边界**:`A_a(v)` 的声明责任在 policy(领域库)——声明错(漏 sink / 错形状)属「错误 specification」类,本族**不承诺**(§8 第 1 类)。 + +#### 3.3.1 健全性约定:`A_a` 只做加约束式单调展开(**规范性**;与 S-B1 约定/N5 互为对偶) + +**风险(跨 spec 健全性洞,S-B1 提出)**:若 `A_a` 以**否定形式**展开(如 `untrusted ↦ ¬Trusted`),左侧求交可能塌为 `⊥`——而 `⊥ ⊑ 任意 φ` **恒真** ⇒ 判定对**所有** sink 放行 ⇒ **静默通过**(违反契约 §1.2 原则 3)。R2 类型项语言含 `¬`(`NOT` 构造子),故此洞是**判定形态的结构性风险**,不是表达力问题。 + +**两条腿都要堵(2026-09-11 A 线评审 C3 + 裁决 D11)**:C1/C3 堵住**左腿**(否定展开 ⇒ 左项塌 `⊥`);**右腿(右项 `Shape`)与正例侧原先无控**——单式 `ty_sub(τ_value ⊓ A_a(v), Shape(π))` 在裸位置判据下**全部鉴别力都在右项**:若 `Shape` 被误声明为 `⊤`、或引擎对未定义的原子(如 `Trusted`)取宽松解释(无公理的原子当可满足一切),判定**恒真 ⇒ 静默通过**(同一类洞,换了腿);反之若 `Trusted` 被声明为与 `string` **互斥**的原子类,则正例左项 `Trusted ⊓ string = ⊥` ⇒ C3 判失败 ⇒ **正控全灭**(§6.4-1「净化路径编译通过」不成立)。**C5 因此是必需的**,不是补充项。 + +| # | 约定(C) | 内容 | +|---|---|---| +| **C1** | **左侧只加约束(单调)** | `A_a(v)` 必须是**加约束式单调展开**(纯合取式的**正**编码,如 `Trusted ⊓ string` / 原子类约束);**禁止**用否定形式编码属性(`untrusted ↦ ¬Trusted` 之类) | +| **C2** | **要求一律写右侧** | 属性**要求**(sink 需要什么)一律写在 `Shape(φ)` 侧(右侧),**不写**在左侧值项上 | +| **C3** | **⊥ 非真(vacuous truth 禁令)** | 判定入口显式守卫:`ty_inhabited(τ_value ⊓ A_a(v)) == 0`(左侧为 ⊥ / 标注不一致)时**不得**判为通过——落 §2.2 的 unknown 分支(诊断「标注不一致 / 义务不可判」),按该族 policy 处置。**⊥ 视为判定失败,不视为真** | +| **C4** | **负控 / 正控(测试)** | ① 构造一条**以否定形式展开的 `A_a`** → 断言**不得放行**(落 ④ 或具名诊断);②「左侧塌 ⊥」探针用例 → 断言按失败处理(C3);③ **非退化正控**(C5②):`Trusted ⊓ string ≠ ⊥` 的正例必须通过;④ **右项 ⊤ 负控**(C5①):`Shape(π)` 为 `⊤` 的策略 → 断言**装载期拒装**(§3.6)。四项进 §7-#8 | +| **C5** | **非退化性(2026-09-11 裁决 D11;堵右腿 + 锁正例)** | ① **右项非退化**:`Shape(π)` 在 `syntax` / `syntax_raw` 位置**不得为 `⊤`**——**policy 装载期校验**,违者诊断**拒装**(§3.6);② **正例非 ⊥**:每条轴取值 `v` 必须有**非退化正例**(`τ ⊓ A_a(v) ≠ ⊥`,典型 `Trusted ⊓ string ≠ ⊥`),以 C4③ 的正控锁死;③ **假 / unknown 分因 + 未定义原子规则**:`ty_sub(τ, Shape)` 的「假」= **违法**、「unknown」= **③/④**(§2.2 点 1/2 同款),并明写「**未定义原子的解释规则 = unknown,不得取真**」——否则同一份 policy 数据下诊断档位会在 ③/④ 与硬错之间摆动 | + +- 落地接口:C3 用 P0 引擎**已有** API(`ty_inhabited`,R2 §3.2 已实施);C5① 是**装载期数据校验**(§3.6),C5② 是**正控**,C5③ 是**诊断分因要求**——**三者都不新增判定算子**(§3.3 第三条不变)。 +- 与 A3 的关系:C1 = A3(传播单调)在**判定形态**上的对偶——轴在传播上单调,在判定编码上也必须单调;C5 = 「轴值是**真标注**(非 ⊤ 非 ⊥)」在判定形态上的对偶——标注必须**有信息量**,否则判据被架空(右项 ⊤)或正控被误杀(左项 ⊥)。 +- 跨 spec 关系:S-B1 在其文中已写同向约定 + 负控 N5(其 §8.2-2 已闭合,勿二次设计);**本 spec = 规范性来源**(S-B1 引用;两处措辞不一致时以本节 **C1-C5** 为准)。 + +### 3.4 载体(**裁决已定(D4(a)):随 R2 P4 同波进 `.ccr` 新段**) + +- **裁决(2026-09-11,D4(a))**:policy / 标签 / 释放记录 / 义务台账 / 决策记录 / 成本事实**各立段**(`POLICY` / `LABEL` / … 段名与布局实施批定),随 **R2 P4 载体批**(TYPE/IFACE 段)**同波**进 `.ccr`——**段 tag 空间可扩展**:v7 的 `7+` **未固定分配**(`2026-09-09-lattice-ir-v7-format.md:44` 的既有注明用途 = 驱逐标注段 / 证书段,与本批新段**并列取号**,不是"新段用旧预留名"——B 线 N8;版本 bump 见 `:80/121`);**策略随产物分发**(跨预编译库分发 = 策略随库)。“随源先行、载体后置”**不采用**(逐字节判据无法用侧车承载)。 +- 段号 / 条目布局 / 版本 bump 策略仍在实施批定,**本 spec 只登记存在性与时序(随 P4)**。在载体就位前,policy 数据随编译输入(库源码/项目配置)进入前端(过渡态,非目标形态)。 + +### 3.5 轴标注的语法载体(**现状事实 + 设计**) + +- **现状事实**:`parser.cr:689-719` 的声明标签位已支持三类标签——内建(`mut`/`pub`/`apx`)、**插件注册标签**(查 `g_plugin_tags`)、未知标签 → `TA08`(`EC_TA_UNKNOWN_TAG=4008`)。`apx` = **变量级标签、零新语法**(`numeric-types-design.md:29`「与 mut/pub 同族,零新语法」)。 +- **设计**:轴标注走**既有插件标签通道**——领域库注册轴标签(`register_plugin_tag(ns, name, data)`),`data` 承载轴 id / 轴值;**语言本体零语法改动、零新关键字**(契约 policy 定义)。 +- **现状缺口(诚实注记)**:parser 现仅把命中的插件标签按 `data != 0 → is_mut = 1` 消费(`parser.cr:703-704`)——**标签→轴标注**的通用化属实施批(§6 T1)。 +- **不加宽语法面**:本 spec **不引入**轴专用语法(如 `x : string, untrusted` 的专用语义)——若实施中发现插件标签通道不够(例如需要标注**表达式/调用点**而非仅声明),该缺口上报为开放问题(§9)而非本 spec 私自加语法。 +- **裁决(2026-09-11,D5(a):零新语法)**:轴 / 汇合 / 上界 / 局部性标注**全部落既有插件标签通道**;**参数级档位约束**(如「`sql_text(lit)` 的 `lit` 带 κ=const」)落 **policy 数据表**(§3.1 条目的 sink 声明集 / 形状项数据,键 = `(ns, policy_name, fn, slot)`),**不新增声明语法**,`interface` 语法不改(S-B1 §6.3 的阻断项随之可解)。(`merge_ordered` 白名单是否纳入「显式序号域」= S-E 面,不在本 spec。) + +### 3.6 装载期校验(policy loader;D6 + D11①,**规范性**) + +- **受控命名空间(D6(a))**:**形状项 / 根声明**只在**受控命名空间内有效**——**装载期校验**;「**谁有权声明形状项 / 根**」记入 policy 面,**对装载器可见**(审计面)。**不启用 `pub` 强制**(`pub` 今日全仓无消费:`parser.cr:662/697`,见「代码级缺陷 5」);**选项 (b)「库内可见性 / 构造器封闭」不实施**。理由:根声明权与形状项声明权本是**治理层数据**,塞进语言权限模型会与 §1.4 的判定边界打架。 +- **C5① 落地**:`Shape(π)` 在 `syntax` / `syntax_raw` 位置为 `⊤` ⇒ **拒装 + 具名诊断**(不等到运行期/切片验收才发现)。 +- **条目 schema 完整性**:§3.1「放行 / 声明条目」六字段齐备(缺**派生源**或**义务引用**者**拒装**——审计字段不是可选项)。 +- **边界**:本节是**数据校验**(policy 装载面),不是新判定体系(原则 4 不破);校验失败的诊断码与 §5.2a 同段分配。 + +--- + +## 4. enforcement 通道(动态档落地) + +### 4.1 先例(**现状事实——enforce 档的原型已经在跑**) + +`IR_BOUNDS_CHECK`(`ast.cr:559`,op 30):语义 = `if ρ(s1) ≥ᵤ s2 → ud2`(`ir-op-semantics.md:196`);发射 = `ext_safety.cr:27,33`(**插件 + 钩子**形态,不是语言特判);解释器侧已实现(`interp.cr:289`);ELF 侧编码 = cmp + jb + ud2。失败语义 = **中止而非未定义值(安全优先)**;负索引由无符号比较天然捕获。 + +**结论**:enforce 档**不需要新机制**——它就是「插件钩子 + 图侧检查节点 + 硬陷阱」的推广。 + +### 4.2 插入面与发射门控 + +- **插入面**:pass 层(与 `ProvenanceVerify` / `RegionCheck` / `ext_safety` 同级),检查节点 = 图上节点(**不新增语义**——检查是映射实例层的保护带)。 +- **发射门控**(与 TODO「边界判定图 pass」**同构**): + - 证明恒安全(引擎真)→ **不发射**(prove 档,零字节); + - 证明恒违法(引擎假)→ **编译期拒绝**(§2.2 违法分支); + - 判不出(unknown)→ 保留检查 + **图推导必要性标注**(证明失败的原因随节点落盘,兼作验证证据)。 +- 需要新 opcode 时,成本面 = `ast.cr` 分配 + 解释器 + x86 编码 + 判据(先例:`IR_BOUNDS_CHECK` 全链);**本 spec 的切片不新增 opcode**(§6.2)。 + +### 4.3 失败语义(**本 spec 的裁决**) + +| 语义 | 内容 | 适用 | +|---|---|---| +| **abort(默认)** | 硬陷阱(ud2/SIGILL),与 `IR_BOUNDS_CHECK` 先例一致 | 安全类 sink 的默认 | +| degrade | 净化降级后继续(例如转义/白名单) | 库声明(policy 数据) | +| report | 报告并返回错误值/错误码 | 库声明(policy 数据) | + +- 默认 = **abort**:**中止优于未定义行为**(先例已立此判据)。 +- degrade 必须由 policy **显式**声明(否则等于静默放行——违反契约 §1.2 原则 3)。 + +### 4.4 成本纪律 + +- 仅在该 sink 发射检查;**无轴使用的程序零产物变化**(判据沿用仓库既有形态:byte-identical)。 +- 语义层不承诺检查存在(T4)——「是否发射」是映射/策略决策,不是语义承诺。 + +### 4.5 与定案 14 声明式边界的处理 + +检查节点落在**命令式下限**(执行面),但它是**图构造**(与 state edges / region 同层),不是新的顺序语义或语义本体条目。承 v4 §十一「证明完毕不下发」:证明产物(含检查的必要性标注)不下发到运行期,只有检查本身按 §4.2 门控发射。 + +### 4.6 双路径一致性(判据要求) + +enforce 档一旦落地,检查语义必须**解释器 / ELF 双路径一致**——沿用 R1 的判据形态(同一用例双路径精确 rc,`test_global_init.py` 先例)。切片 1 不含运行期检查(§6.2),该项列为切片 2 判据。 + +--- + +## 5. 未证明义务的落法(诊断面) + +### 5.1 义务状态机与禁令 + +``` +open ──prove(真)──→ proved(零成本) +open ──enforce────→ enforced(检查节点已发射) +open ──proof 提供──→ proof-provided(proof/contract 归档,验证管线可复核) +open ──strict 未满足──→ rejected(编译拒绝) +open ─────────────────→ **passed 不存在**(禁令:不静默) +``` + +**禁令(契约 §1.2 原则 3 的可实施形态)**:证不出 → 必落 ②/③/④ 之一,**绝不静默当通过**。③ 的「登记」状态是 **open**(未处置),**不是** passed——台账中它是待办,不是已通过项。 + +### 5.2 三面可见(缺一不可) + +| 面 | 内容 | 现状接口 | +|---|---|---| +| **a) 诊断面** | `error[XXXX]` / `warning` 体系,新增「安全义务」码段。必须区分三类语义:**不可判定 ≠ 不合法**(R2 §3.3 同款)/ **未处置 ≠ 通过** / **预算耗尽 ≠ 判定为假** | `diag.cr` + `EC_*` 段(`ast.cr`)+ R2 §7 的分段策略 | +| **b) 台账面** | **义务清单**(obligations manifest):轴 / 源 / 汇 / 落档 / 状态 / 图→源码位置映射。是人类与 CI 的门槛通道(strict 档 = CI 断言清单为空)。**§3.1「放行 / 声明条目」的每一条(含豁免形态者)必须在此出现**(D2:放行必须落台账) | 落点实施批定(编译输出过渡态先行;`.ccr` 段随 §3.4 = R2 P4)。**格式归 S-D 线统一(D29)**——各族只写消费点,不写格式 | +| **c) 产物面** | 义务清单 = 图数据的投影(语义保鲜兑现,R2 §6.4 同构)——验证工具直接消费,不必重跑前端 | `.csr`/TagNode 图验证线(coreir-schema「图即验证」) | + +### 5.3 「不静默」的可测判据(把承诺变成断言) + +- **负控**:故意留一条未处置义务 → 编译输出**必须**含具名诊断(轴 + 源位置 + 汇位置)+ 台账条目;测试断言二者均存在。 +- **strict 负控**:同一条义务在 strict 档 → 编译**必须**拒绝(非零退出 + 错误码)。 +- **正控**:净化后 → 通过,且台账中该义务状态 = proved,无诊断。 +- 「静默通过」的**反判据**:任何「义务存在但输出无诊断且台账无条目」的场景 = 本 spec 的**违约**,测试必须覆盖。 + +### 5.4 档 ④ 的措辞要求 + +拒绝必须给**出路**(否则等于死墙):补 proof/contract / 加 declassify / 显式例外入口(`unsafe` 边界——**放行记入台账**,承 v4「例外入口授权可追踪(信任链)」)。 + +--- + +## 6. 最小可实施切片(S-A 第一批) + +### 6.1 选型与理由 + +- **轴 = `trust`**:唯一同时具备 ① 现状 source(`read_file`/`get_env`/argv)② 现状 sink(`src/stdlib/os.cr:30` `system(cmd)` = **fork + execve("/bin/sh","-c",cmd)**,教科书级命令注入点)③ 净化先例(declassify 同构)④ **不需要新 opcode**(静态纪律)的轴。 +- **策略 = 一条**:`untrusted ↛ system 的 cmd 参数(语法位)`——S-B1(注入族)的**前身**,但本切片**只做流纪律与档位流程,不做领域库**(SQL/HTML 留给 S-B1)。 +- **正控 / 负控**:负控 = `get_env("X")` 直达 `system(...)`;正控 = 经净化节点(库提供的 sanitize 内建/显式 declassify)后通过。 +- **启动条件(已满足)**:切片 A 的判据基础 = `trust` 的**可判传播**语义——2026-09-11 裁决 **D1(a)** 保住 S-A §1.1 为真源(§1.1 对表),**该阻断项已解除**,切片 A 可开工;前置依赖 = §6.2 的「不依赖」清单 + R2 引擎已就位。 + +### 6.2 切片边界(明确的「不做」) + +- **不新增 opcode**、不做 enforcement 实施(② 档**只留接口位** + 复用 `IR_BOUNDS_CHECK` 先例登记,实施归切片 2); +- 不做 `.ccr` POLICY 段(台账先落编译输出); +- 不依赖 `where` 语法(§2.3 现状注); +- **不依赖 R2 P2b**(`iface_ops` 查表 / `res_type_node` 两表合一)——只用 P0 已实施的引擎 API; +- 不碰隐式流(③ 档在本切片只以**登记**形态出现,不判定)。 + +### 6.3 组分(三项;**按三批排期**:T1 → T2 → T3,各自带判据;2026-09-11 A 线评审 I5 修法) + +| 任务 | 内容 | +|---|---| +| **T1 轴标注 + 传播** | source 标注(库注册标签通道,§3.5)+ 沿 def-use 边传播(复用 provenance 追踪);**无 source 标注时零产物变化** | +| **T2 义务 + 判定 + 落档** | 义务项构造 → 引擎 `ty_sub` 三态 → §2.2 决策树落档 → 违法 = 诊断(含源/汇位置 + 反例) | +| **T3 台账 + 诊断 + 正/负控** | 义务清单输出 + 码段分配 + §6.4 全部判据的测试 | + +### 6.4 切片判据(可验收) + +1. **正控**:净化路径编译通过;产物与「无轴标注版本」**byte-identical**(prove 档零成本)。 +2. **负控**:untrusted 直达 sink → 编译拒绝;诊断含轴名 / 源位置 / 汇位置。 +3. **不静默**:unknown 面 → 台账条目存在(测试断言,§5.3)。 +4. **零回归**:不用该轴的既有语料 → 按 **R2 §4 每步共同判据行**(`2026-09-10-type-interface-unification-design.md:155` = 全套件 + `corec2/corec3` cmp IDENTICAL + N06=0 + 冒烟 rc=42)执行;**套件计数以该行为唯一真源,本文不重述数字**(A 线评审 M1)。 +5. **双路径**:本切片不含运行期检查 → **n/a**(留作切片 2 判据,§4.6)。 + +### 6.5 候选切片 B:`determinism` 族的「隐式竞争 reject」执法面(**与切片 A 并列,二选一或两者**) + +- **范围(以负控侧为主)**:图上「共享条目被两个并发 region 触碰 + 两 region 间无 state edge 顺序源 + 无显式请求标注」→ **reject**(§2.6 行 4)。**正控侧**(显式 `race` / `select_any` 通过 + 报告条目)**依赖 S-E 四形态语义**——若 S-E 未定稿,切片 B **先只做负控**(隐式竞争必须 reject),正控登记为 S-E 就位后的增量。 +- **为什么小**:判定输入 = **图上已有事实**(region `dataflow.cr:428-432` + state edges `:129,157` + 共享条目 / 共存关系——零新图机制);执法 = 诊断 + 拒绝(无 enforcement 节点、无新 opcode、无新 op)。 +- **判据**: + 1. **负控**:隐式竞争用例 → 编译拒绝;诊断指出**共同条目 + 两个 region 位置**(图→源码映射)。 + 2. **正控(S-E 就位后)**:显式 `race` / `select_any` → 通过,且报告面条目存在(S-D 接口)。 + 3. **零回归**:无并发构造的既有语料 → **零产物变化**(byte-identical)+ 全套件 / 自举关卡同切片 A(§6.4-4)。 + 4. **不静默**:④ 的诊断必须给**出路**(显式请求标注 / 补顺序源 / 提供汇合证明——按该族 policy 当前可用者)。 +- **与切片 A 的关系**:两者**共用**同一套机制(轴标注 / 义务 / 决策树 / 台账 / 诊断),差别只在判定输入与 policy 档位取值——可独立成批,也可同批。**建议**:先 A 后 B(A 验证机制本身;B 额外验证「policy 档位可把 unknown 固定推向 ④」这一新面)。 +- **越界声明**:切片 B **不定义** `merge_*` / `race` / `select_any` 的语义(属 S-E,§0.2);只消费其**图上显式标记的存在性**(§1.9 接口需求 1)。 + +--- + +## 7. 判据(本 spec 落地后的验收面) + +| # | 判据 | 检查方式 | +|---|---|---| +| 1 | **术语合规**:本 spec 与 8 份后续 spec 对 §1.1 术语 / §1.2 原则 / §1.3 三类的使用无重定义、无漂移 | 交叉审阅(9 份 spec 两两对照) | +| 2 | **流程完备**:每条义务必落一档(无 open 悬挂、无 passed 旁路) | 状态机断言 + 台账完整性 | +| 3 | **判定统一**:引擎是唯一判定入口;策略侧零新增判定算子/规范化/memo | 代码面清点(`ty_*` 是唯一判定 API) | +| 4 | **不静默**:§5.3 正/负控全绿(含 strict 负控) | 测试 | +| 5 | **零成本**:prove 档与无轴程序 **byte-identical** | 产物哈希比对 | +| 6 | **可扩展**:演示「纯数据新增一条策略」——**零关键字、零 Graph 本体改动、零判定器改动** | 演示 + diff 检查 | +| 7 | **诚实**:判定为 unknown / 预算耗尽 / `D_timing` 的 `MODELED`(模型非证明)时,诊断与「判定为假 / `PROVED`」**措辞与错误码可区分** | 诊断用例 | +| 8 | **⊥ 非真 + 非退化(vacuous truth 禁令 / 右项与正例控制)**:① `A_a` 否定形式展开的负控**不得放行**;② 左侧项塌 `⊥` 的探针按判定失败处置;③ `Shape` 为 `⊤` 的策略**装载期拒装**;④ 每轴取值有**非退化正例**(`Trusted ⊓ string ≠ ⊥`)**必须通过**;⑤ 未定义原子**取 unknown 不取真**(§3.3.1 C1-C5) | 负控 + 正控 + 装载期用例(§3.3.1 C4/C5、§3.6) | + +--- + +## 8. 不承诺 + +> 逐条引用共享契约 §1.3 三类**不可消**的残留,说明本族落在哪类。 + +| # | 契约 §1.3 类别 | 本族(S-A + 属性轴/义务阶梯)的落点 | +|---|---|---| +| 1 | **错误 specification** | 本族只能证明「**程序忠实实现了 policy**」,**不能**证明 policy 本身正确:policy 漏声明的 sink / 错形状的 source / 错误的 `A_a(v)` 映射 → 义务阶梯**结构上无法覆盖**它。故本族**不承诺**「程序安全」,只承诺「相对 policy 的忠实性 + 义务不静默」。这是第 1 类的**纯落点** | +| 2 | **恶意/失陷外部实体** | `trust` 轴提供**声明与检查**通道(untrusted 标记 + 净化节点),但**不能**证明「被声明为 trusted 的组件真的可信」——供应链 / 编译机 / 密钥 / 硬件后门不可用语言语义证明。本族贡献 = 把信任假设**显式化**(可审计、可追踪、最小权限,承 v4「可追踪信任链」),**不是消除它**。`determinism` 族的 `D_external`/`D_timing` 同落此类:外部世界的时间与环境**不可证明**,只能**归因 + 三值标注**(`PROVED`/`MODELED`/`UNKNOWN`)——不承诺消除物理不确定性,只承诺**不许撒谎**(§1.9) | +| 3 | **需求本身允许危险行为** | shell / browser / kernel / JIT 必须执行外来代码——`system(cmd)` 本身就是需求(§6.1 的 sink 不是 bug)。本族目标 = 证明**隔离边界**(形如「不可信数据不能到达解释器语法位」的流纪律),**不是禁止能力**;`declassify` / `unsafe` = 危险行为的**显式授权点**(登记可追踪) | + +**全局禁令(原文遵守)**:**不承诺**「能自动证明所有程序安全」——判定引擎是**三态 + 预算守卫**的,unknown 是设计内的第一等结果(Rice/halting 边界,契约 §1.2 原则 3)。 + +--- + +## 9. 不做项(YAGNI)与开放问题 + +### 9.1 不做项 + +- **不改 Graph 本体**:零新 opcode(切片 1)、零新关键字、零新图结构(契约 §1.1 policy 定义)。 +- **不建第二判定体系**:判定一律走引擎(契约 §1.2 原则 4);不新增判定算子。 +- **不做自动证明搜索**:归 TODO「证明搜索自动」(proof-search);本 spec 只做**档位与流程**。 +- **不做隐式流完整判定**(timing/cache 面):登记为「不可强制」类(§2.3),边界归 S-B3。 +- **不做运行期标签**:轴标注 = 编译期图数据,证明完毕不下发(与能力同理,v4 §十一)。 +- **不做 policy DSL**:policy = 条目数据,不是策略语言(新语法 = 违反契约;需要表达力时用 `A_a(v)` 映射数据)。 +- **不做「安全开关」语义**:policy 档位(strict/宽松)是**策略数据**,不是语言模式;不存在「关掉安全 = 语义不同」的开关(同 CORE_SAFE 的调试语义需在实施批单独裁决)。 + +### 9.2 开放问题(**5 条:4 条已裁(2026-09-11)+ 1 条待论证**) + +| # | 问题 | 影响面 | 裁定(2026-09-11) | +|---|---|---|---| +| 1 | **档 ③ 的默认行为**(宽松档):诊断 + 台账**继续编译**,还是默认拒绝、需显式开关降级? | 「不静默」的强度 vs 上手成本 | **已裁(D3(c))**:默认 = **登记 + 继续编译**(③ 是**登记**不是通过);**strict 档拒编**;**CI 门槛从严**。规范形态见 §2.2 附 | +| 2 | **轴标注的语法载体**:走既有插件标签通道(`g_plugin_tags`,零新语法),还是给轴**独立语法**? | 语言面改动面(零关键字 vs 新语法) | **已裁(D5(a))**:**零新语法**——走**插件标签通道**(§3.5);参数级档位约束落 **policy 数据表**;缺口上报而非私自加语法 | +| 3 | **policy 档位粒度**:全局 / 每策略 / 每轴 / 每模块? | 策略表的键设计(§3.1) | **已裁(D15)**:**两级**——全局默认 + **每策略覆盖**(键 = `(ns, policy_name)`);**每轴粒度留扩展位**(不实施) | +| 4 | **义务台账载体**:编译输出先行,还是直接规划 `.ccr` POLICY 段(与 R2 P4 载体批合批)? | 实施批次划分 | **已裁(D4(a))**:**随 R2 P4 同波进 `.ccr` 新段**(政策随产物分发);载体就位前 = 编译输出过渡态(非目标形态) | +| 5 | **轴与横切接口的边界复核**(**本 spec 最大理论风险**):`trust`/`integrity` 是否可被正确表达为「横切形状类型项」(R2 §2.2 同入口)从而**根本不需要轴机制**? | 若成立,S-A 的价值收缩为「流程 + 落档」,标注机制并入 R2 注册表 | 倾向:不成立(§0.3 T1——横切形状是**单值结构判定**,轴是**来源/流向**性质,跨节点路径性质无法由单值形状表达);但**该论证未形式化 = 待论证**,须在切片 1 的语言实践中检验 | + +--- + +## 自检记录 + +- **共享契约合规逐条**:§1.1 术语(属性轴/义务/四档/policy/declassify/sink·source 模型)逐字引用未重定义;§1.2 五条原则逐条落点(原则 1 → A2/§0.3 T3;原则 2 → §3.2 理由 1;原则 3 → §2.2/§5.1/§5.3;原则 4 → §3.3;原则 5 → §0.3 T4/§4.5);**§1.4(2026-09-11 追加裁决)→ §1.9 承接轴族与义务落档**(含「显式不确定 = prove + 报告义务,**不是第五档**」与 `select` 的判定侧硬约束),报告面(S-D)/汇合语义(S-E)边界逐字引用;§1.3 三类 → §8 逐条引用落点。 +- **占位符扫描**:无 TBD/TODO;「待论证」出现于 §0.3 T1、§2.5、§9.2 #5(**刻意保留**——性质 = 需证明或需用户裁决,非缺稿)。 +- **内部一致性**:§2.2 决策树 ↔ §2.3 映射表 ↔ §2.4 成本表三表同源(同一落档语义);§5.1 状态机 ↔ §2.4 失败语义 ↔ §5.2 三面同源。 +- **范围检查**:单一实施计划?**否**——本 spec 是机制与判据定形,§6 切片 = 唯一的实施计划对象(一批);S-B×5/S-C/S-D 各自立计划。 +- **歧义检查**:「违法」与「④ reject」已分(§2.2 点 1);「unknown」与「假」已分(§2.2 点 2);「登记(open)」与「passed」已分(§5.1 禁令);「轴」与「横切接口」已分(§0.3 T1,残余风险登记 §9.2 #5);**「族」与「轴」已分**(§1.9 定位段——`determinism` 族七轴独立标注/独立落档,禁止合并为单一标注维度)。 +- **跨 spec 健全性(§3.3.1)**:S-B1 提出的「`A_a` 否定形式展开 ⇒ 左侧塌 ⊥ ⇒ `⊥ ⊑ 任意 φ` 恒真 ⇒ 静默放行」已落为**规范性约定 C1-C5**(左侧只加约束 / 要求写右侧 / ⊥ 非真 / 负控+正控四用例 / **非退化性:右项非 ⊤ + 正例非 ⊥ + 假·unknown 分因 + 未定义原子取 unknown**),并声明**本 spec 为规范性来源**(S-B1 引用,冲突以本节为准);C3 只用 P0 已有 `ty_inhabited`、C5① 走装载期数据校验,**未新增判定算子**。 +- **A 簇收敛记录(2026-09-11)**:本批落实 —— A 线 C1(D1(a):§1.1 对表 + §1.3 注)、A 线 C2(D2(a):§3.1 放行/声明条目 + §3.6 装载校验 + §5.2b 台账)、A 线 C3→**C5**(D11:§3.3.1 + §3.6 + §7-#8)、A 线 I1(D10(a):§1.9 需求 1 + §2.6 行 3)、A 线 I5(§6.1 启动条件 + §6.3 三批)、A 线 M1(门禁改引 R2 §4 行)。D5/D6/D7/D9/D12-D15/D19/D22/D23/D29/D30 的裁定与落点见 §0.4 索引。 +- **收敛批 v3 记录(2026-09-11 第二轮闭环)**:**B 线 N2** → 编号统一(本地裁决改 `L`,见路线图 §4 对照表 + 注销登记)· **A 线 N2** → §3.1 `kind` 补 `mint` + 四族填法(S-B4 入 schema)· **A 线 N3** → §1.3 首消费方改「暂无对侧确认(登记)」+ S-B5 的 `state-version × integrity` 为唯一已回引面(§1.8 行同步)· **A 线 N5** → §2.2 附补族级例外限定 · **A 线 ⚠️2** → §3.1 `declassify 通道` 栏写明 = `kind=declassify` 投影(只建一张表)· **B 线 N7/N8** → §1.7 清单唯一口径 + §3.4 段位表述。**纯度事实**(§1.7 外来注记)留回填指引(在飞批 Task 4 Step 4)。 +- **§1.4 并入核对(4 点)**:① 族/轴分离定位(§1.9 开头);② 规范落档规则含 `select` 判定侧硬约束(§2.6);③ 候选切片 B = 隐式竞争 reject(§6.5,与切片 A 并列、含 S-E 依赖注记与「先 A 后 B」建议);④ S-A↔S-E 接口需求 4 条(§1.9——只提需求与供给,不定义执行模型)。 +- **冲突声明**:与既有定案的四处张力(T1-T4)**全部显式列出**,无静默改写;T2 的裁决依据 = 共享契约 §0 明裁「补成四档」。 +- **现状事实核对**(file:line 均已读码验证):`parser.cr:689-719`、`globals.cr:307/317-327`、`ast.cr:559/394`、`ext_safety.cr:27,33`、`interp.cr:289`、`os.cr:30`(`system` = fork+execve /bin/sh -c)、`io.cr:27,72`、`dataflow.cr:129,157,428-432`。 diff --git a/docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md b/docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md new file mode 100644 index 00000000..6582c2bc --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-semantic-safety-roadmap.md @@ -0,0 +1,162 @@ +# 语义安全路线图与九份设计 spec 的共享契约 + +日期:2026-09-11 +状态:**路线图定稿(用户裁决:全部起草)+ 收敛批 v3 补 §4 编号对照表(2026-09-11)**——本文件不是设计本身,而是 9 份设计 spec 的**共享契约 + 清单 + 起草/实施节奏 + 编号对照**。 +性质:战略层路线文档(用户与外部 AI 讨论收敛后的方向确认)+ 后续各 spec 的术语与边界基准。 + +关联: +- `docs/superpowers/specs/2026-08-30-type-system-direction-design.md`(14 条定案:类型 = 图标注 / 接口 = 契约 / where 三档) +- `docs/superpowers/specs/2026-09-10-type-interface-unification-design.md`(R2:双轴接口注册表 + 语义子类型引擎——本路线图的**判定器地基**) +- `docs/memory-model-capability-lattice.md` v4(能力 = ⟨身份符号, 授权集, 域约束, 派生源⟩;**M1 定论:能力不提升一等公民,授权归治理层**) +- `docs/memory-model.md`(缓存语义七条 / 条款 4b 图内不可重算)/ `docs/pointer-model.md`(provenance / asp) +- `docs/superpowers/specs/2026-08-23-hw-map-design.md`(设备层)+ `specs/2026-09-05-hardware-interface-table.md`(指令/运行层) +- `TODO.md` 性能自动化 / 验证与工具自动化两族(2026-08-30 记) + +--- + +## 0. 论题(为什么安全线不是新方向) + +两条表面无关的线索(① CWE/OWASP 里内存安全之外仍站着一大批类别;② C++/SO 调查里依赖管理/构建时间/并发复杂度比 UAF 更痛的工程问题)在 Core 上收敛到**同一个机制**: + +> **图上的正交属性轴 + 义务阶梯**——把尽可能多的漏洞/风险类别,从「程序员规范」变成「非法语义或不可静默消失的义务」。 + +- **义务阶梯**已有半成品:定案 5 的 where 三档(常量 → 编译错 / 符号 → VC / 动态 → 运行时检查);本路线图把它补成**四档**(+ 拒编)。 +- **属性轴**是定案 1「类型 = 图标注」的直接推广。 +- **判定器**在 R2 落地(P0 引擎 + P2a 判定替换:引擎已成为判定权威)。 + +## 1. 共享契约(9 份 spec 必须逐字遵守) + +### 1.1 术语 + +| 术语 | 定义 | +|---|---| +| **属性轴**(attribute axis) | 图对象/边上的**开放标注**维度:`trust`(可信度)/ `secrecy`(机密性)/ `integrity`(完整性)/ `authority`(授权)/ `lifetime`(生命周期)/ `state-version`(状态版本)/ `effect`(副作用) | +| **义务**(obligation) | 由属性轴导出的、必须在编译/验证期被处置的判定要求 | +| **四档义务** | ① **prove**(静态可证 → 零成本通过)② **enforce**(不可证但可运行时强制 → 插入 enforcement)③ **proof-required**(不可证也不可强制 → 要求用户提供 proof/contract)④ **reject**(高安全模式下仍无法处置 → 拒绝编译) | +| **policy** | 一组图上约束(`G + P ⊢ Safe` 里的 `P`):**数据而非语法**——新增安全模型不改 Graph 本体、不加关键字 | +| **declassify** | 显式降密/放行节点:`Secret → Public` 的唯一合法通道(同构地:`Untrusted → Trusted` 的净化节点) | +| **sink / source 模型** | 领域侧的「危险落入点 / 不可信来源」声明——**由领域库提供**(类型化构造器),语言只给流纪律与义务阶梯 | + +**轴语义真源(2026-09-11 用户裁决 D1(a))**:`trust` = **可判的传播轴**(来源 = 库声明 + 自动推导 + 净化节点);**来源真实性的 attestation 不可判**(属 §1.3 第 2 类),但**程序内传播可判**——该传播面即 `trust` 轴。`integrity`(= 可篡改性,与 `trust` 对偶但**不同轴**)不因「不可判」改写;S-B3 的 `Secret ↛ Public` 对偶方向(`Untrusted ↛ Trusted`)**消费 `trust` 轴**,不单列第二判定面。三份消费方(S-A / S-B1 / S-B3)各写「谁定义 / 谁判定 / 谁是消费者」对表(承 D1 共同要求)。 + +**放行 / 声明条目(2026-09-11 用户裁决 D2(a))**:任何**绕过义务的通道**(`¬义务 → 义务`)**只**经 S-A §3.1 的「放行 / 声明条目」栏(`kind{declassify|grant|deny|mint|stability} · 主体/身份 · 目标/范围 · 理由 · 派生源 · 义务引用`),且**必须落台账**。**`kind` 闭集五枚**:`declassify`(显式降密/净化)· `grant`/`deny`(**既存**对象/权限的授予/拒绝)· **`mint`(能力创建点 = 根声明 / `alloc_at` 类例外入口授权——声明的目标即新建域)** · `stability`(治理层稳定性注解)——**四族填法** = S-B3(`declassify`)· S-B2(`grant|deny`)· **S-B4(`mint`)** · S-B5(`stability`)(B 线 N2)。**形态默认 = 图上显式节点**;偏离节点形态者(`grant`/`deny` 边集合、`mint` 的声明型根条目、`stability` 注解)须在 S-A **显式豁免并补审计字段**——不得沉默。 + +### 1.2 原则(硬性) + +1. **属性不升格为语言 primitive**(承 M1 定论与定案 1/12):属性 = 开放 semantic annotation / relation;只有**义务**可能拒绝编译。 +2. **策略面归库,纪律面归语言**:SQL/HTML/shell 的语法正确性由**领域库**用类型化构造器表达(`SQL` 等);语言只负责「不可信数据不能到达解释器语法位」这类**流纪律**与义务阶梯。**禁止**把某类漏洞做成内建特判(那是 lint 换皮)。 +3. **义务不静默消失**(可承诺的强 claim):证不出 → 落 ②/③/④ 之一,**绝不静默当通过**。**不承诺**「能自动证明所有程序安全」(Rice/halting 边界)。 +4. **判定统一走引擎**:义务判定 = R2 引擎的类型项判定(`ty_sub`/`ty_equiv` 等),策略 = 附加的形状项/约束,不另建判定体系。 +5. **声明式上限 + 命令式下限**(定案 14):本路线图只碰声明式上限;执行/副作用/内存的命令式下限(state edges / region / 调度)不动。 + +### 1.4 确定性轴与「不确定性 = 显式请求」(用户 2026-09-11 追加裁决) + +**论题**:目标**不是**「Core 是最确定的语言」(标量式 claim 必然在局部维度被 Esterel/Lustre/SPARK-Ravenscar 打败),而是—— + +> **每条可观测的不确定性,必须可归因到「显式语义源」或「已证明的汇合无关性」。** +> 即:`物理不确定性 ⇏ 语义不确定性`,除非源码语义明确允许。(claim 形态:「Core = 不确定性最难偷偷混进程序的语言」) + +**轴族**(并入 §1.1 的轴表,作为 `determinism` 族): + +| 轴 | 含义 | Core 现状 | +|---|---|---| +| `D_value` | 值/结果确定性 | 强(纯函数语言层面) | +| `D_effect` | 副作用顺序确定性 | 有基础(state edges 显式) | +| `D_memory` | 内存行为/生命周期确定性 | 强(region 锚定 + bump/reset) | +| `D_timing` | 时间/WCET | **三值化**(见下)——不许撒谎 | +| `D_resource` | 资源上界确定性 | 有基础(需静态 bound;接 bounds-inference) | +| `D_external` | 外部输入依赖确定性 | 边界节点可归档(图上有边界) | +| `D_mapping` | **跨机器语义确定性** | **Core 特有轴**(语义 ≥ 映射;映射换而语义不变)——最强的差异化面 | + +**执法 = 四档义务**(与 §1.1 四档同构): + +| 情形 | 落档 | +|---|---| +| 依赖/状态边足证「汇合与调度无关」 | **prove**(零成本) | +| 顺序由显式顺序源固定(`merge_ordered`) | 落到确定实现 | +| 语义明文允许(`race` / `select_any`) | **显式不确定**——必须进报告面,不得偷偷发生 | +| 既未证明也未标注(**隐式竞争**) | **reject** ← 真正的执法点 | + +**迁移窗口 W1(2026-09-11 用户裁决 D3)**:`select` 重定给出**显式迁移窗口**——默认 **1 个发布周期**:窗口内 lenient(保留现语义 + 具名诊断),窗口后**强制标注**(未标注即按隐式竞争处理)。**不承诺永久 lenient**;窗口的语义实现归 **S-E**。窗口内 `select` 仍受 S-A §2.6 的判定侧禁令(不得作为「显式顺序源」输入 prove)。 + +**胜者身份(witness,2026-09-11 用户裁决 D9)**:`race` 的胜者身份**允许**作为显式出参(witness)——落在既有机制内:witness = 显式语义源,登记为 `EXPLICIT` 不确定源 + 报告义务(本表行 3 的实例),不新增语义。 + +**默认强度分层(2026-09-11 用户裁决 D3,总口径)**:本地开发 = 登记 + 继续编译;**strict 档 / CI 门槛 = 严(拒编)**;② **enforce 默认启用**(可 enforce 的一律 enforce,高安全档关闭);**未声明的边界条目取 `unknown`(保守判定方向)但不拒编**,报告面可见。细则落 S-A §2.2 附。 + +**时间三值化(`D_timing`,硬性)**:`PROVED`(机器无关上界:分配次数/字节、在飞任务数、通信量——可从 region/图证明)|`MODELED`(机器相关时间 = **成本模型估计,明确标注"模型非证明"**,复用 S-C 成本模型)|`UNKNOWN`。**禁止**把 MODELED 呈现为 WCET 证明。 + +**现有语义缺陷(2026-09-11 用户定性 = 待修)**:`select` 现语义 = **「最早到达的令牌」**(`docs/dataflow-design.md:102`;`docs/project-book.md:241` 同款)——**把物理调度顺序泄进了语义**,而 `docs/execution-model.md:15` 同时宣称「DAG ⟹ 确定性」。两句互相拆台 → 按本节的「不确定性 = 显式请求」重定(迁移路径与兼容窗口见 S-E)。 + +**先例(本原则已在仓内成立)**:`apx` = **显式**放宽数值精度(opt-in);把同一模式推广到并发/时间/外部输入即本节主张。 + +**边界**:报告面(`core analyze --determinism` + 不确定性 provenance 链)属 **S-D**;轴族/义务落档属 **S-A**;**汇合语义本身(`merge_deterministic`/`merge_ordered`/`race`/`select_any`)属 S-E**——它改的是执行模型,不得稀释进通用机器。 + +### 1.3 三类**不可消**的残留(每份 spec 的「不承诺」节必须引用并逐条说明本族落在哪类) + +- **错误 specification**:Core 只能证明"程序忠实实现了规则",不能证明规则本身正确。 +- **恶意/失陷外部实体**:供应链、编译机、密钥、硬件后门——用可复现构建/签名/provenance/最小权限**缩小**,不能用语言语义**证明**。 +- **需求本身允许危险行为**:shell/browser/kernel/JIT 必须执行外来代码——目标不是禁止能力,而是**证明隔离边界**。 + +## 2. 清单(9 份设计 spec) + +| # | 文件 | 范围 | 依赖 | 关键判据/交付 | +|---|---|---|---|---| +| **S-A** | `2026-09-11-semantic-safety-obligations-design.md` | 属性轴(7 轴,开放标注)+ 四档义务 + policy 接口(挂 R2 接口注册表旁)+ **放行/声明条目统一 schema(D2)** + 未证明义务的落法(诊断/拒绝/强制通道)+ 与 where 三档的关系 | **R2 引擎(已就位)** | 轴/档/接口的定稿 + 「义务不静默」的判定流程 + 最小可实施切片(**A 簇裁决索引见 S-A §0.4**) | +| **S-B1** | `2026-09-11-policy-injection-design.md` | 注入族:`untrusted → interpreter 语法位` 为非法边;参数化通道(值位 vs 语法位) | S-A | 非法边判据(**裸位置判据消费 `trust` 轴,D1(a)**)+ 领域库接口约定(SQL/HTML/shell 三例)+ 正/负控语义 + **切片 1 硬前置拆解(D5/I5)** | +| **S-B2** | `2026-09-11-policy-authorization-design.md` | 授权族:`Can(a,o,r,v) ⟺ ∃合法 capability path`(**带状态版本参数**,D13);effectful op 须产出证明 | S-A + **capability-lattice v4** | capability path 判定(**唯一 owner = S-B2 §2.1 步①**,D22;def 链倒推不二处实现)+ 与 M1「授权归治理层」的边界(**代价已接受,D14**:无运行期授予/撤销 ⇒ 延伸走能力对象 + 版本见证)+ 与 effect 轴的耦合 | +| **S-B3** | `2026-09-11-policy-information-flow-design.md` | 信息流族:`Secret ↛ Public`,除 `declassify`;含隐式流(branch/timing 面登记) | S-A(**`trust` 轴消费面**,D1(a):对偶方向 `Untrusted ↛ Trusted` 判的就是 `trust` 的传播) | 流纪律判定 + declassify 语义(**S-A §3.1 放行/声明条目 `kind=declassify`**,D2)+ 隐式流边界(可判定子集) | +| **S-B4** | `2026-09-11-policy-resource-confinement-design.md` | 围栏族:`Path` / capability-scoped namespace;`../..` = 无法构造目标 capability | S-A + provenance/asp | namespace 判定 + 与 pointer-model 的 reuse + TOCTOU 衔接(**根声明治理 = 装载期校验 + policy 授权,D6**;派生链判定的 owner 归 S-B2,D22) | +| **S-B5** | `2026-09-11-policy-state-version-toctou-design.md` | 状态版本族:check/use 分离版本 = 图上两版本;handle 绑定身份而非 pathname 重查 | S-A | 版本等价判定(κ 粒度 = **对象身份 + 版本见证**,D13)+ 与 state edges/条目的 reuse + 模式清单 + **与 S-E 的执法分界(D23:按变更源身份可判定)** | +| **S-C** | `2026-09-11-performance-without-commitment-design.md` | 性能无实现承诺 + 可移植:纯度/效应/依赖/局部性/精度标注 → 优化器 → 目标描述(**成本模型**接 hw-map);收纳 TODO 性能自动化家族 | hw-map + **R3/实例化波 2-3 合批** | 标注面 + 成本模型接口 + 「不把机器写进程序意义」的可迁移判据 | +| **S-D** | `2026-09-11-explain-predict-incremental-design.md` | 可解释(`core explain` 决策通道:图→源码 + 映射决策)+ 可预测(内存/任务/通信/deadline 上界)+ 语义增量(ΔSource→ΔGraph→ΔProof→ΔBinary)**+ §1.4 的报告面(`core analyze --determinism` + 不确定性 provenance 链)** | engine + LSP 基建 + 现有 cir cache | 三者各自的可实施切片 + explain 输出契约 + 增量失效面(含 TODO #5 缓存键家族) | +| **S-E** | `2026-09-11-merge-semantics-design.md` | **汇合语义定稿**:`merge_deterministic` / `merge_ordered` / `race` / `select_any` 四形态的**图级**定义 + **物理⇏语义降级义务**(lowering/调度不得引入未声明的不确定)+ `select` 现语义(最早到达)的**迁移路径**(用户定性=待修)+ 与「自动并发」TODO(可判定性)的接口 | **S-A(轴/四档);被 S-A 的 determinism 轴族引用** | 四形态语义 + 每形态的义务落档 + `select` 重定与兼容窗口 + 正/负控判据(隐式竞争必须 reject) | + +## 3. 起草与实施节奏(用户裁决:全部起草;S-B 一族一份) + +1. **起草阶段(本次)**:9 份 spec 全部起草(本路线图 = 共享契约,各 spec 只填自己的范围,**不得**重定义 §1.1 术语或 §1.3 不承诺)。 +2. **实施顺序(建议)**: + - **S-A 先定稿并实施最小切片**(属性轴 + 四档 + 义务判定走引擎)——它是其余 8 份的地基,没有它每族都会退化成特判。 + - **S-B×5**:S-A 定稿后**逐族**出实施计划(每族独立验收;不并行开工,避免语义争议交叉)。 + - **S-C**:与 x86 实例化波 2/3、R3 映射侧**合批**(同一件事的两个名字)。 + - **S-D**:可解释面可先做(用户可见收益直接);语义增量依赖缓存键修复(TODO #5 家族)。 +3. **与在飞工作线的关系**:本路线图**不打断** R2 剩余批(P2b:`iface_ops` 查表接线 + `res_type_node` 两表合一;R3:映射侧/载体)——S-A 的实施排在 R2 收线之后。 + +--- + +## 4. 编号对照表(2026-09-11 收敛批 v3 新增;**规范性**) + +**背景(B 线 N2)**:十份文件里并存 **五套编号**,其中两套(用户裁决 `D` 与各 spec 本地 `D`)**在同一文件内撞义**——实例:S-D 的本地 `D8`(记录随缓存条目存取)vs `D8`(deadline 仅诊断,用户裁决);S-C 的本地 `D4`/`D5`/`D7` 同理。本节给唯一对照,并**注销无定义表的编号**。**此后约定**:本地裁决一律写 `L`;用户/收敛裁决写 `D`(首次出现可用 `★D` 强调);评审条目一律带线别(「A 线 M1」/「B 线 C2」)。 + +### 4.1 五个编号空间 + +| 编号空间 | 写法 | 定义位置(唯一真源) | 范围 | 说明 | +|---|---|---|---|---| +| **用户/收敛裁决** | `D`(★ 强调形同义) | 全量 = `.superpowers/sdd/decisions-sheet.md`;A 簇索引 = **S-A §0.4** | D1–D30 | D1–D7 / D9–D15 / D19 / D22 / D23 / D29 / D30 落点在 A 簇 spec;**D8 / D16 / D17 / D18 / D20 / D21 / D24–D28 落点在 B 簇 spec**(S-C / S-D / S-E——S-A §0.4 尾注已声明,本表为补齐) | +| **本地编号** | `L`(**收敛批 v3 统一改名**,原 `D` / `D-n` / `D-1…D-8`) | **裁决表**:S-C §0.2(L1–L7)· S-D §0.3(L1–L15)· S-E §0.3(L1–L8)· S-B3 §0.3(L1–L6)· S-B4 §0.3(L1–L7);**判据编号**:S-B1 §6.1(L1–L5 = 验收门)。**层名**:S-B4/S-B5 的构造合法性 / 解析身份两层 = `LV-1` / `LV-2`(原 `L-1`/`L-2`,收敛批 v3 改名以消同形混淆) | 各 spec 内 | 只在本 spec 内引用;与 `D` **不得混用**(N2 的根因)。无本地编号表的文件:roadmap / S-A / S-B2 / S-B5(其 §0.3 用「决 1/2/3」) | +| **A 线评审编号** | 「A 线 C/I/M/N」 | `.superpowers/sdd/safety-review-a.md`(正文小节) | C1–C3 · I1–I5 · M1–M8 · N1–N5 | 可直接定位 | +| **B 线评审编号** | 「B 线 C/I/M/N」 | `.superpowers/sdd/safety-review-b.md`(正文小节) | C1–C4 · I1–I5 · M1–M6 · N1–N8 | 可直接定位。**警告**:A/B 两线的 `C1`/`M1` 等编号**互不相同**,引用必须带线别(同一文件内同时出现两线编号时,后加线别的写法为准) | +| **张力 / 待实测编号** | `Tn` | A 线 `T1–T4` = **S-A §0.3 四处张力**;B 线 `T1–T5` = `safety-review-b.md` §4.3「待实测」 | 见左 | 跨文件引用须带线别 | + +### 4.2 注销登记(**无定义表 ⇒ 不得再引用**) + +| 编号 | 判定 | 依据 | +|---|---|---| +| B 线 `B1` / `B2` / `B5` / `B9` / `B10` / `B11` / `B13` | **注销**(评审单轮内部编号,无定义表、不可追溯) | 全仓检索无定义;正文引用处已按 §4.3 改写为可定位编号 | +| B 线 `T6` / `T7` / `T8` / `T9` / `T10` / `T11` / `T12` | **注销**(同上;**`T10–T12` 两份评审 + 十份 spec 全仓 0 命中**,评审原文亦未定义) | 同上;B 线实际存在的待实测编号只有 `T1–T5` | + +### 4.3 已改写的引用(收敛批 v3 闭环) + +| 原引用 | 位置 | 改写为 | 依据 | +|---|---|---|---| +| `B1/T8` | S-D §5 S-D3 判据⑤ | **A 线 M1**(门禁口径一律引 R2 §4 该行) | 该处语义 = A 线 M1 的修法 | +| `M2/B9` | S-C §8 O6 | **B 线 M2**(O6 改「规模上界实测」) | 该处语义 = B 线 M2 的修法 | +| `M3/B10` | S-C §6 切片前置 1 | **B 线 M3**(`ir_gen.cr:1590-1593` 行号校正) | 该处语义 = B 线 M3 | +| `M4/B11` | S-E §5.1-(1) 行形说明 | **A 线 M4**(落档表行形) | 该处语义 = A 线 M4 | +| `M6/B13` | S-D §5 S-D3 范围行 | **B 线 M6**(`det` 进不做清单) | 该处语义 = B 线 M6 | +| `T9` | S-B2 §3.3 enforce 行 | **A 线 ⚠️5**(`cmp+jb+ud2` 与 `sub+cmp+jae+ud2` 同义统一) | **按语义对应认定**(非引用)——该行所说的"编码措辞统一"即 A 线 ⚠️5 | + +### 4.4 两类编号**不再新增**(防复发) + +- 新增本地裁决 / 判据**必须**用 `L` 并登记在本 spec 的表;**不得**再用裸 `D`。 +- 评审条目引用**必须**带线别(「A 线 M1」);**不得**引用 §4.2 的注销编号。 +- **既存同形编号的引用纪律**(不改名,只要求带出处):S-B2 §0.4 的 `C1`–`C3`(M1 修订触发条件)· S-B1 §0.2 的 `1`–`10`(本族裁决序号)· S-B5 §0.3 的「决 1/2/3」——引用时**必须带出处**(如「S-B2 §0.4 C2」),裸引用与 A/B 线评审的 `C` 视为歧义。 diff --git a/grammar/core.ebnf b/grammar/core.ebnf index 6d35962a..24fec29d 100644 --- a/grammar/core.ebnf +++ b/grammar/core.ebnf @@ -55,8 +55,9 @@ ProjectAccess = '@' IDENT IDENT '::' IDENT ; ArgList = Expr { ',' Expr } ; (* 注:DEX_LIT(`3.14` 十进制小数,词法令牌名 T_DEX,原 T_FLOAT)默认产生 dex(精确 - 定点小数,数值迁移 Task 4);f32/f64 后缀 = apx CPU 位宽标注(见数值类型设计, - 2026-08-16;lexer 消费后缀后仅发 T_DEX,宽度透传为已知缺口) *) + 定点小数,数值迁移 Task 4);f32/f64 后缀已退役(2026-09-10 语言面收窄 §2)——词法 + 对数字字面量的字母后缀一律响亮报错,宽度 = 机器形状,归映射层(数值类型设计, + 2026-08-16) *) Literal = INT_LIT | DEX_LIT | STRING_LIT | CHAR_LIT | BOOL_LIT | UNIT_LIT | 'None' | 'Some' '(' Expr ')' ; BlockExpr = Block ; IfExpr = 'if' Expr Block [ 'else' ( Block | IfExpr ) ] ; diff --git a/grammar/corespec.ebnf b/grammar/corespec.ebnf index 5419fd7c..823846ec 100644 --- a/grammar/corespec.ebnf +++ b/grammar/corespec.ebnf @@ -1,3 +1,11 @@ +(* grammar/corespec.ebnf —— 2026-09-06 退役 *) + +本文件(独立 .corespec 规约语言语法)已退役——与 .crasm 退役同一逻辑: +规约(requires/ensures/old/result/where)是 Core 语言语法的一部分,只有一种表达 = +.cr(编译进图 = TagNode,见 coreir-schema「图即验证」),无第二套文件/语言。 +- 规约语法并入 grammar/core.ebnf(迁移事项,见 TODO「规约语法并入 .cr」) +- 本文件内容保留为迁移源,不再演进;SpecImplies 产生式随迁 + (*** 顶层 ***) SpecCompilationUnit = { ImportDecl } { SpecItem } ; @@ -16,7 +24,11 @@ RequiresClause = 'requires' SpecExpr ';' ; EnsuresClause = 'ensures' SpecExpr ';' ; (*** 规约表达式 ***) -SpecExpr = SpecLogicalOr ; +(* 蕴含 ==>:2026-09-06 评审 F2 补(regalloc-consistency.corespec 使用)。 + 优先级低于 ||(最低层);右结合——右侧递归展开(A ==> B ==> C = A ==> + (B ==> C))。量词箭头 '=>'(SpecQuantifier 内)为不同词法形态,不冲突。 *) +SpecExpr = SpecImplies ; +SpecImplies = SpecLogicalOr [ '==>' SpecImplies ] ; SpecLogicalOr = SpecLogicalAnd { '||' SpecLogicalAnd } ; SpecLogicalAnd = SpecEquality { '&&' SpecEquality } ; SpecEquality = SpecComparison { ('==' | '!=') SpecComparison } ; diff --git a/grammar/tokens.ebnf b/grammar/tokens.ebnf index ba71f514..837c4ba3 100644 --- a/grammar/tokens.ebnf +++ b/grammar/tokens.ebnf @@ -19,16 +19,15 @@ KEYWORD = 'auto' | 'fileid' | 'move' | 'in' | 'None' | 'Some' | 'unit' ; (* 字面量 *) -(* 数字字面量不支持 '_' 分隔符(lexer.cr:数字扫描仅收 DIGIT;'_' 属 is_alpha(95), - 会被消费为后缀,产生无效 T_INT(-1);bootstrap lexer 支持 '_',两实现有分歧—— - 以实现为准取不支持) *) -INT_LIT = DIGIT { DIGIT } [ INT_SUFFIX ] ; -INT_SUFFIX = 'i8' | 'i16' | 'i32' | 'i64' | 'u8' | 'u16' | 'u32' | 'u64' ; +(* 数字字面量不支持 '_' 分隔符、不支持任何宽度后缀(2026-09-10 语言面收窄 §2 用户裁决: + 两实现统一响亮报错——修复前 self-hosted 把 '_' 当后缀静默消费成取值 0、bootstrap + 静默接受;宽度后缀 = 机器形状,归映射层)。 *) +INT_LIT = DIGIT { DIGIT } ; (* 3.14 十进制小数 → DEX_LIT(数值迁移:T_FLOAT → T_DEX,见 core.ebnf Literal 注)。 定点缩放精确解析(int_val = 缩放整数),lexeme 槽保留原数字串(apx 场景由 - str_to_f64_bits 还原 binary64 位模式)。f32/f64 后缀 = apx CPU 位宽标注 - (lexer 消费后缀后统一发 T_DEX——宽度透传为已知缺口,见 numeric-migration-inventory 争议点 7) *) -DEX_LIT = ( DIGIT { DIGIT } [ '.' DIGIT { DIGIT } ] | '.' DIGIT { DIGIT } ) [ 'f32' | 'f64' ] ; + str_to_f64_bits 还原 binary64 位模式)。f32/f64 宽度后缀已退役(2026-09-10 语言面 + 收窄 §2)——lexer 对字母后缀一律响亮报错;宽度 = 机器形状,归映射层。 *) +DEX_LIT = ( DIGIT { DIGIT } [ '.' DIGIT { DIGIT } ] | '.' DIGIT { DIGIT } ) ; STRING_LIT = '"' { ESCAPED_CHAR } '"' ; CHAR_LIT = '\'' ( ESCAPED_CHAR | CHAR - '\'' ) '\'' ; ESCAPED_CHAR = '\' ( 'n' | 't' | 'r' | '0' | '\' | '"' | '\'' ) | '\x' HEX HEX ; diff --git a/spec/compiler/checker.corespec b/spec/compiler/checker.corespec index e69de29b..6c9d84e2 100644 --- a/spec/compiler/checker.corespec +++ b/spec/compiler/checker.corespec @@ -0,0 +1,7 @@ +(* === checker.corespec —— 2026-09-06 退役 === +.corespec 独立格式取消(与 .crasm 同构):规约只有一种表达 = Core 语言 +(.cr 内 requires/ensures/where + 编译进图 TagNode)。本文件内容作为 +组件行为契约迁移至对应实现注释/.cr 契约文档(先例:src/compiler/ +regalloc-consistency.cr),不再演进。 +*) + diff --git a/spec/compiler/parser.corespec b/spec/compiler/parser.corespec index e69de29b..59f52465 100644 --- a/spec/compiler/parser.corespec +++ b/spec/compiler/parser.corespec @@ -0,0 +1,7 @@ +(* === parser.corespec —— 2026-09-06 退役 === +.corespec 独立格式取消(与 .crasm 同构):规约只有一种表达 = Core 语言 +(.cr 内 requires/ensures/where + 编译进图 TagNode)。本文件内容作为 +组件行为契约迁移至对应实现注释/.cr 契约文档(先例:src/compiler/ +regalloc-consistency.cr),不再演进。 +*) + diff --git a/spec/stdlib/collections.corespec b/spec/stdlib/collections.corespec index e69de29b..835f34a7 100644 --- a/spec/stdlib/collections.corespec +++ b/spec/stdlib/collections.corespec @@ -0,0 +1,7 @@ +(* === collections.corespec —— 2026-09-06 退役 === +.corespec 独立格式取消(与 .crasm 同构):规约只有一种表达 = Core 语言 +(.cr 内 requires/ensures/where + 编译进图 TagNode)。本文件内容作为 +组件行为契约迁移至对应实现注释/.cr 契约文档(先例:src/compiler/ +regalloc-consistency.cr),不再演进。 +*) + diff --git a/spec/stdlib/io.corespec b/spec/stdlib/io.corespec index e69de29b..23549dd9 100644 --- a/spec/stdlib/io.corespec +++ b/spec/stdlib/io.corespec @@ -0,0 +1,7 @@ +(* === io.corespec —— 2026-09-06 退役 === +.corespec 独立格式取消(与 .crasm 同构):规约只有一种表达 = Core 语言 +(.cr 内 requires/ensures/where + 编译进图 TagNode)。本文件内容作为 +组件行为契约迁移至对应实现注释/.cr 契约文档(先例:src/compiler/ +regalloc-consistency.cr),不再演进。 +*) + diff --git a/src/arch/hit/hit.cr b/src/arch/hit/hit.cr new file mode 100644 index 00000000..a4533ff7 --- /dev/null +++ b/src/arch/hit/hit.cr @@ -0,0 +1,1166 @@ +// === src/arch/hit/hit.cr === +// 硬件接口表(HIT):core-x86.toml → 内存表 加载器(M1 Task 1 + M2a Task 1 schema v2)。 +// 表数据归架构轴(src/arch/x86_64/core-x86.toml,x86 实例化波 1 Task 1 迁入)—— +// 本文件 = 引擎(留 src/arch/hit/,未来实例可复用);路径由 --table 传入。 +// 设计:docs/superpowers/specs/2026-09-05-hardware-interface-table.md +// 格式:docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md「表文件格式」节 +// + docs/superpowers/specs/2026-09-07-hit-m2-design.md §3(schema v2) +// 表 = corearch 运行的唯一平台依赖:load_hit_table 把 toml 表文件读成 +// 事件/投影/步 三块紧凑 buffer 表 + 运行时条目,表驱动编码器直读。 +// +// schema v2(M2a Task 1):步模板从 M1 的 5 字段扩展为完整形态描述 +// prefix/rex/opcode/modrm{role|digit}/SIB/disp/imm/rel/cond;每事件可多 proj +// 条目(多形态);[[event.proj.step]] 步小节表达多步序列;[runtime.x86] 小节 +// 表达运行时条目。M1 表形态(字段直置 proj 层、单 proj、无 runtime) +// = v2 子集,加载不变。字段值域/关联规则校验错误全部拒绝加载。 +// +// 本文件仅进 corearch 构建清单(build_selfhost_native.py corearch concat), +// corec 不需要。import 行仅为独立 `corec check src/arch/hit/hit.cr` 解析 +// (flat concat 构建会剥离 import 行;io/toml/fmt 均在 corearch 闭包内)。 +import io +import toml +import fmt + +// ════════════════════════════════════════════════════════════════ +// 内存表布局(直读;LE 存取用 hit_w32/hit_r32) +// ════════════════════════════════════════════════════════════════ + +// g_hit_events:每事件 40B = 10 × i32 +HIT_EVENT_REC : int = 40; +HIT_EV_OFF_ID : int = 0; // 最小核事件号(1-4 数据 + 5-9 控制/调用/系统) +HIT_EV_OFF_NAME : int = 4; // 名字串下标(g_hit_names 表) +HIT_EV_OFF_INPUTS : int = 8; // 事件签名(无宽度/寄存器/寻址——最小核抽象) +HIT_EV_OFF_OUTPUTS : int = 12; +HIT_EV_OFF_SIDE : int = 16; // 0 = pure,1 = effect +// 20/24/28 = proj0(M1 兼容视区):M1 消费方(emit_instr_tabled → hit_ev_step_of +// → hit_proj_step(es, 0, st))只读首投影视图,故把 proj0 的 isa/步数/首步偏移 +// 镜像于此,多 proj 事件的其余形态走 32/36 字段。旧路径消费面布局不变。 +HIT_EV_OFF_ISA : int = 20; // proj0 ISA 码(1 = x86-64) +HIT_EV_OFF_STEP_COUNT : int = 24; // proj0 步数 +HIT_EV_OFF_STEP_OFF : int = 28; // proj0 首步在 g_hit_steps 的字节偏移 +HIT_EV_OFF_PROJ_OFF : int = 32; // 首投影记录下标(g_hit_projs,12B/条) +HIT_EV_OFF_PROJ_COUNT : int = 36; // 投影形态数(M1 恒 1) + +// g_hit_projs:每投影 12B = 3 × i32(事件 32/36 字段索引本表) +HIT_PROJ_REC : int = 12; +HIT_PJ_OFF_ISA : int = 0; // 投影 ISA 码 +HIT_PJ_OFF_STEP_COUNT : int = 4; // 该形态步数 +HIT_PJ_OFF_STEP_OFF : int = 8; // 该形态首步在 g_hit_steps 的字节偏移 + +// g_hit_steps:每步 108B。前 20B = M1 布局视区(OP0/OP1/REG_ROLE/RM_ROLE/ +// RM_MODE——偏移与旧布局一致,旧消费方原样可读);20B 起 = v2 规范字段。 +// 模板字节 = prefix → REX → opcode 三段固定拼接序(不含 ModRM/SIB/disp/imm/ +// rel——那些由槽位/角色回填,非模板字节)。 +HIT_STEP_REC : int = 108; +HIT_ST_OFF_OP0 : int = 0; // 字节流首字节(prefix/REX/opcode 拼接序) +HIT_ST_OFF_OP1 : int = 4; // 字节流第二字节(0 = 无) +HIT_ST_OFF_REG_ROLE : int = 8; // modrm.reg 侧角色码(角色 1-6 / 寄存器字面量 16+regno;0 = 无/digit 形) +HIT_ST_OFF_RM_ROLE : int = 12; // modrm.rm 角色码(同上;0 = 无) +HIT_ST_OFF_RM_MODE : int = 16; // 0 = rm 为寄存器;1 = rm 为 base+disp(缺省 base = rbp);2 = SIB +// ── v2 规范字段(20 起;未用恒 0)── +HIT_ST_OFF_PFX0 : int = 20; // 前缀字节 0(66/0F…) +HIT_ST_OFF_PFX1 : int = 24; // 前缀字节 1 +HIT_ST_OFF_REX : int = 28; // REX 字节(0x40|w<<3|r<<2|x<<1|b;无 REX = 0) +HIT_ST_OFF_OPB0 : int = 32; // opcode 字节 0 +HIT_ST_OFF_OPB1 : int = 36; // opcode 字节 1 +HIT_ST_OFF_OPB2 : int = 40; // opcode 字节 2 +HIT_ST_OFF_OPN : int = 44; // opcode 字节数(1..3) +HIT_ST_OFF_REG_IS_DIGIT : int = 48; // reg 侧为 /digit(1)而非角色 +HIT_ST_OFF_REG_DIGIT : int = 52; // digit 值(0..7) +HIT_ST_OFF_BASE_ROLE : int = 56; // base 角色码(rm_mode 1 = modrm base,缺省 rbp→0; + // rm_mode 2 = SIB base;rip = 32 池/rodata/全局形) +HIT_ST_OFF_SIB_SCALE : int = 60; // SIB scale(0..3;非 rm_mode 2 = 0) +HIT_ST_OFF_SIB_INDEX : int = 64; // SIB index 角色码 +HIT_ST_OFF_DISP_SIZE : int = 68; // disp 宽度:0 = auto(槽回填按幅度选 1|4);1/4 字面 +HIT_ST_OFF_DISP_SRC : int = 72; // disp 源角色码(0 = 无/槽隐含) +HIT_ST_OFF_IMM_SIZE : int = 76; // imm 宽度(1/2/4/8;0 = 无) +HIT_ST_OFF_IMM_ROLE : int = 80; // imm 操作数角色码(字面量 lit = 100) +HIT_ST_OFF_IMM_KIND : int = 84; // imm 回填 kind(0 = 无;1 = fnaddr) +HIT_ST_OFF_IMM_LIT : int = 88; // imm 字面量值(imm_role = lit 时) +HIT_ST_OFF_REL_ROLE : int = 92; // rel 回填操作数角色码(0 = 无) +HIT_ST_OFF_REL_KIND : int = 96; // rel 回填表:1 = 事件流位置;2 = 函数;3 = 外部符号 +HIT_ST_OFF_REL_SIZE : int = 100; // rel 宽度(8/32) +HIT_ST_OFF_COND_ROLE : int = 104; // branch 条件源槽角色码(cmp/test 序列操作数) + +// 角色/效应/ISA 码 +HIT_ROLE_DST : int = 1; +HIT_ROLE_SRC1 : int = 2; +HIT_ROLE_SRC2 : int = 3; +HIT_ROLE_ADDR : int = 4; +HIT_ROLE_VAL : int = 5; +HIT_ROLE_COND : int = 6; // branch 条件源(v2) +HIT_SIDE_PURE : int = 0; +HIT_SIDE_EFFECT : int = 1; +HIT_ISA_X86 : int = 1; +// 操作数码空间补充(与角色码不相交) +HIT_REG_BASE : int = 16; // 寄存器字面量 = 16 + regno(rax=0..r15=15) +HIT_CODE_RIP : int = 32; // rip 基(仅 base 键合法) +HIT_CODE_LIT : int = 100; // imm 字面量(imm_role = "lit") +// rel kind 码 +HIT_REL_EVENT : int = 1; +HIT_REL_FUNC : int = 2; +HIT_REL_EXTERN : int = 3; +// imm kind 码 +HIT_IMM_FNADDR : int = 1; +// [runtime.x86] 条目槽(每槽 4B;见 hit_parse_runtime_chunk) +HIT_RT_CALL_MECHANISM : int = 0; +HIT_RT_CALL_RET_ADDR : int = 1; +HIT_RT_CALL_STACK_GROW : int = 2; +HIT_RT_PARAM_MODE : int = 3; +HIT_RT_PARAM_MASK : int = 4; +HIT_RT_SAVE_MASK : int = 5; +HIT_RT_EXTERN_ENTRY : int = 6; +HIT_RT_EXTERN_RESOLVE : int = 7; + +// 表加载全局(hit.cr 仅进 corearch 构建 → 不污染 corec/corelsp) +g_hit_events : string, mut; g_hit_event_count : int, mut; g_hit_event_cap : int, mut; +g_hit_steps : string, mut; g_hit_step_count : int, mut; g_hit_step_cap : int, mut; +g_hit_projs : string, mut; g_hit_proj_count : int, mut; g_hit_proj_cap : int, mut; +g_hit_names : string, mut; g_hit_name_count : int, mut; g_hit_name_cap : int, mut; +g_hit_runtime : string, mut; g_hit_runtime_present : int, mut; + +// ════════════════════════════════════════════════════════════════ +// buffer 表小工具(自持:仅用运行时内建 load8/store8/alloc) +// ════════════════════════════════════════════════════════════════ + +fn hit_copy_bytes(src: string, n: int, dst: string) { + i : int, mut = 0; + loop { + if i >= n { break; } + store8(dst, i, load8(src, i)); + i = i + 1; } +} + +// 4B 小端写/读(仅存非负值——id/字节/码 均非负;字节拆取仿 w32 的 % 256 +// 惯例——按位 & 不进 HIT 表值域:本文件仅 hit_bit 用倍增) +fn hit_w32(buf: string, pos: int, v: int) { + store8(buf, pos + 0, v % 256); + store8(buf, pos + 1, (v / 256) % 256); + store8(buf, pos + 2, (v / 65536) % 256); + store8(buf, pos + 3, (v / 16777216) % 256); } + +fn hit_r32(buf: string, pos: int) -> int { + return load8(buf, pos) + load8(buf, pos + 1) * 256 + + load8(buf, pos + 2) * 65536 + load8(buf, pos + 3) * 16777216; } + +fn hit_zero_bytes(buf: string, from: int, n: int) { + i : int, mut = 0; + loop { + if i >= n { break; } + store8(buf, from + i, 0); + i = i + 1; } +} + +fn hit_grow_events(needed: int) { + if needed < g_hit_event_cap { return; } + nc : int, mut = g_hit_event_cap * 2; + if nc < 16 { nc = 16; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * HIT_EVENT_REC); + hit_copy_bytes(g_hit_events, g_hit_event_cap * HIT_EVENT_REC, nb); + g_hit_events = nb; + g_hit_event_cap = nc; } + +fn hit_grow_steps(needed: int) { + if needed < g_hit_step_cap { return; } + nc : int, mut = g_hit_step_cap * 2; + if nc < 16 { nc = 16; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * HIT_STEP_REC); + hit_copy_bytes(g_hit_steps, g_hit_step_cap * HIT_STEP_REC, nb); + g_hit_steps = nb; + g_hit_step_cap = nc; } + +fn hit_grow_projs(needed: int) { + if needed < g_hit_proj_cap { return; } + nc : int, mut = g_hit_proj_cap * 2; + if nc < 16 { nc = 16; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * HIT_PROJ_REC); + hit_copy_bytes(g_hit_projs, g_hit_proj_cap * HIT_PROJ_REC, nb); + g_hit_projs = nb; + g_hit_proj_cap = nc; } + +fn hit_grow_names(needed: int) { + if needed < g_hit_name_cap { return; } + nc : int, mut = g_hit_name_cap * 2; + if nc < 16 { nc = 16; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 8); + hit_copy_bytes(g_hit_names, g_hit_name_cap * 8, nb); + g_hit_names = nb; + g_hit_name_cap = nc; } + +// ════════════════════════════════════════════════════════════════ +// toml 文本扫描(表文件结构层;标量值解析复用 stdlib toml.cr) +// ════════════════════════════════════════════════════════════════ + +// 从 pos 起逐行扫描,返回第一个 tag 匹配的行首偏移;无 → -1。 +// 匹配规则:行首(允许前导空白)字节与 tag 完全相等,且 tag 之后只允许 +// 行尾 / 空白 / '\r'(CRLF)/ '#'(行尾注释)——容忍手写表的 +// "[[event.proj]] # 注释" 形式;同时避免 "[[event]]" 前缀误配 +// "[[event.proj]]"(tag 后必须紧跟界定符)。 +fn hit_find_tag(content: string, pos: int, tag: string) -> int { + slen := str_len(content); + tlen := str_len(tag); + p : int, mut = pos; + loop { + if p >= slen { break; } + s : int, mut = p; + loop { + if s >= slen { break; } + c := load8(content, s); + if c == 32 || c == 9 { s = s + 1; } else { break; } + } + e : int, mut = s; + loop { + if e >= slen { break; } + if load8(content, e) == 10 { break; } + e = e + 1; } + if s + tlen <= e { + m : int, mut = 0; + ok : int, mut = 1; + loop { + if m >= tlen { break; } + if load8(content, s + m) != load8(tag, m) { ok = 0; break; } + m = m + 1; } + if ok == 1 { + if s + tlen == e { return s; } // tag 到行尾(\n 或 EOF) + c2 := load8(content, s + tlen); + if c2 == 13 || c2 == 32 || c2 == 9 || c2 == 35 { return s; } + } + } + p = e + 1; } + return -1; } + +// pos 所在行的行尾后偏移(下一行首;EOF = 文长) +fn hit_line_after(content: string, pos: int) -> int { + slen := str_len(content); + i : int, mut = pos; + loop { + if i >= slen { break; } + if load8(content, i) == 10 { return i + 1; } + i = i + 1; } + return slen; } + +// 行首 token(键名)判定:text 中是否存在以 key 为整 token 的行 +// (token 终止符 = 空白 / '=' / 行尾;'#' 注释行跳过)。 +fn hit_has_key(text: string, key: string) -> int { + slen := str_len(text); + klen := str_len(key); + p : int, mut = 0; + loop { + if p >= slen { break; } + s : int, mut = p; + loop { + if s >= slen { break; } + c := load8(text, s); + if c == 32 || c == 9 { s = s + 1; } else { break; } } + e : int, mut = s; + loop { + if e >= slen { break; } + if load8(text, e) == 10 { break; } + e = e + 1; } + if s < e && load8(text, s) == 35 { p = e + 1; continue; } // '#' 注释行 + t : int, mut = s; + loop { + if t >= e { break; } + c := load8(text, t); + if c == 32 || c == 9 || c == 61 { break; } + t = t + 1; } + if t - s == klen { + m : int, mut = 0; + ok : int, mut = 1; + loop { + if m >= klen { break; } + if load8(text, s + m) != load8(key, m) { ok = 0; break; } + m = m + 1; } + if ok == 1 { return 1; } + } + p = e + 1; } + return 0; } + +// allowed(空格分隔键名表)中是否含 key(整 token 匹配) +fn hit_key_allowed(allowed: string, key: string) -> int { + alen := str_len(allowed); + klen := str_len(key); + p : int, mut = 0; + loop { + if p >= alen { break; } + loop { + if p >= alen { break; } + if load8(allowed, p) == 32 { p = p + 1; } else { break; } } + if p >= alen { break; } + e : int, mut = p; + loop { + if e >= alen { break; } + if load8(allowed, e) == 32 { break; } + e = e + 1; } + if e - p == klen { + m : int, mut = 0; + ok : int, mut = 1; + loop { + if m >= klen { break; } + if load8(allowed, p + m) != load8(key, m) { ok = 0; break; } + m = m + 1; } + if ok == 1 { return 1; } + } + p = e; } + return 0; } + +// 本层键名白名单扫描:任一非 '#' 行以不在 allowed 中的键开头 → 报错 +// "unknown key"。Returns 0 = 干净;1 = 已报未知键。 +fn hit_scan_keys_ok(text: string, allowed: string) -> int { + slen := str_len(text); + p : int, mut = 0; + loop { + if p >= slen { break; } + s : int, mut = p; + loop { + if s >= slen { break; } + c := load8(text, s); + if c == 32 || c == 9 { s = s + 1; } else { break; } } + e : int, mut = s; + loop { + if e >= slen { break; } + if load8(text, e) == 10 { break; } + e = e + 1; } + if s < e && load8(text, s) != 35 { // 非注释行 + t : int, mut = s; + loop { + if t >= e { break; } + c := load8(text, t); + if c == 32 || c == 9 || c == 61 { break; } + t = t + 1; } + if t > s { + tok := str_sub(text, s, t - s); + if hit_key_allowed(allowed, tok) == 0 { + print("error: HIT table: unknown key '"); print(tok); println("'"); + return 1; } + } + } + p = e + 1; } + return 0; } + +// 原始值串(key = 之后到行尾注释前,去首尾空白;返回原样含引号) +fn hit_raw_str(text: string, key: string) -> string { + slen := str_len(text); + klen := str_len(key); + p : int, mut = 0; + loop { + if p >= slen { return ""; } + s : int, mut = p; + loop { + if s >= slen { break; } + c := load8(text, s); + if c == 32 || c == 9 { s = s + 1; } else { break; } } + e : int, mut = s; + loop { + if e >= slen { break; } + if load8(text, e) == 10 { break; } + e = e + 1; } + if s < e && load8(text, s) != 35 && s + klen <= e { + m : int, mut = 0; + ok : int, mut = 1; + loop { + if m >= klen { break; } + if load8(text, s + m) != load8(key, m) { ok = 0; break; } + m = m + 1; } + if ok == 1 { + q : int, mut = s + klen; + loop { + if q >= e { break; } + if load8(text, q) == 32 || load8(text, q) == 9 { q = q + 1; } else { break; } } + if q < e && load8(text, q) == 61 { + v0 : int, mut = q + 1; + loop { + if v0 >= e { break; } + if load8(text, v0) == 32 || load8(text, v0) == 9 { v0 = v0 + 1; } else { break; } } + v1 : int, mut = v0; + loop { + if v1 >= e { break; } + if load8(text, v1) == 35 { break; } // '#' 行尾注释 + v1 = v1 + 1; } + loop { + if v1 <= v0 { break; } + c := load8(text, v1 - 1); + if c == 32 || c == 9 || c == 13 { v1 = v1 - 1; } else { break; } } + return str_sub(text, v0, v1 - v0); + } + } + } + p = e + 1; } + return ""; } + +// 事件名 → 角色码(1..6;0 = 未知/缺失) +fn hit_role_code(name: string) -> int { + if str_eq(name, "dst") != 0 { return HIT_ROLE_DST; } + if str_eq(name, "src1") != 0 { return HIT_ROLE_SRC1; } + if str_eq(name, "src2") != 0 { return HIT_ROLE_SRC2; } + if str_eq(name, "addr") != 0 { return HIT_ROLE_ADDR; } + if str_eq(name, "val") != 0 { return HIT_ROLE_VAL; } + if str_eq(name, "cond") != 0 { return HIT_ROLE_COND; } + return 0; } + +// 寄存器名 → regno(0..15);非寄存器名 → -1 +fn hit_reg_code(name: string) -> int { + if str_eq(name, "rax") != 0 { return 0; } + if str_eq(name, "rcx") != 0 { return 1; } + if str_eq(name, "rdx") != 0 { return 2; } + if str_eq(name, "rbx") != 0 { return 3; } + if str_eq(name, "rsp") != 0 { return 4; } + if str_eq(name, "rbp") != 0 { return 5; } + if str_eq(name, "rsi") != 0 { return 6; } + if str_eq(name, "rdi") != 0 { return 7; } + if str_eq(name, "r8") != 0 { return 8; } + if str_eq(name, "r9") != 0 { return 9; } + if str_eq(name, "r10") != 0 { return 10; } + if str_eq(name, "r11") != 0 { return 11; } + if str_eq(name, "r12") != 0 { return 12; } + if str_eq(name, "r13") != 0 { return 13; } + if str_eq(name, "r14") != 0 { return 14; } + if str_eq(name, "r15") != 0 { return 15; } + return -1; } + +// 操作数码:角色 1..6 或寄存器字面量 16+regno;0 = 未知 +fn hit_operand_code(name: string) -> int { + rc := hit_role_code(name); + if rc != 0 { return rc; } + rn := hit_reg_code(name); + if rn >= 0 { return HIT_REG_BASE + rn; } + return 0; } + +// base 键码:操作数码或 rip(32);0 = 未知 +fn hit_base_code(name: string) -> int { + oc := hit_operand_code(name); + if oc != 0 { return oc; } + if str_eq(name, "rip") != 0 { return HIT_CODE_RIP; } + return 0; } + +// 2^n(按位或不存在,倍增) +fn hit_bit(n: int) -> int { + b : int, mut = 1; + i : int, mut = 0; + loop { + if i >= n { break; } + b = b * 2; + i = i + 1; } + return b; } + +fn hit_err_ev(ev_id: int) { + print("error: HIT table: event "); print_i(ev_id); print(": "); } + +fn hit_err_step(ev_id: int, step_no: int) { + print("error: HIT table: event "); print_i(ev_id); print(" step "); print_i(step_no); print(": "); } + +// ════════════════════════════════════════════════════════════════ +// schema v2 步字段解析(模板语言核心) +// ════════════════════════════════════════════════════════════════ +// text = 一个 [[event.proj.step]] 小节全文(或 v1 平铺投影的 proj 层文本—— +// isa_ok = 1 时允许 isa 键混于其中)。解析成功 → 追加一条 108B 步记录。 +// Returns 0 = 成功;1 = 失败(错误已打印)。 +fn hit_parse_step(text: string, ev_id: int, step_no: int, isa_ok: int) -> int { + if isa_ok == 1 { + if hit_scan_keys_ok(text, "isa prefix opcode rex_w rex_r rex_x rex_b modrm_reg_role modrm_reg_digit modrm_rm_role rm_mode modrm_base_role sib_scale sib_index_role sib_base_role disp_size disp_src imm_size imm_role imm_lit imm_kind rel_role rel_kind rel_size cond_role") != 0 { return 1; } + } else { + if hit_scan_keys_ok(text, "prefix opcode rex_w rex_r rex_x rex_b modrm_reg_role modrm_reg_digit modrm_rm_role rm_mode modrm_base_role sib_scale sib_index_role sib_base_role disp_size disp_src imm_size imm_role imm_lit imm_kind rel_role rel_kind rel_size cond_role") != 0 { return 1; } + } + // ── 输出记录字段(函数顶给默认值;各组按需覆盖)── + op0 : int, mut = 0; op1 : int, mut = 0; + reg_code : int, mut = 0; is_digit : int, mut = 0; digit_val : int, mut = 0; + rm_code : int, mut = 0; rm_mode : int, mut = 0; + base_code : int, mut = 0; scl_val : int, mut = 0; idx_code : int, mut = 0; + dsz_code : int, mut = 0; dsrc_code : int, mut = 0; + isz_val : int, mut = 0; iro_code : int, mut = 0; ik_val : int, mut = 0; ilit_val : int, mut = 0; + rro_code : int, mut = 0; rk_val : int, mut = 0; rs_val : int, mut = 0; + cond_code : int, mut = 0; + rex_byte : int, mut = 0; npf : int, mut = 0; + pfx0 : int, mut = 0; pfx1 : int, mut = 0; + // ── rex 位(出现则须 0/1;任一出现且值 1 才产生 REX 字节)── + rwp := hit_has_key(text, "rex_w"); + rrp := hit_has_key(text, "rex_r"); + rxp := hit_has_key(text, "rex_x"); + rbp := hit_has_key(text, "rex_b"); + rw : int, mut = 0; rr : int, mut = 0; rx : int, mut = 0; rb : int, mut = 0; + if rwp == 1 { rw = toml_get_int(text, "rex_w"); } + if rrp == 1 { rr = toml_get_int(text, "rex_r"); } + if rxp == 1 { rx = toml_get_int(text, "rex_x"); } + if rbp == 1 { rb = toml_get_int(text, "rex_b"); } + if rwp == 1 && (rw < 0 || rw > 1) { hit_err_step(ev_id, step_no); println("bad rex bit"); return 1; } + if rrp == 1 && (rr < 0 || rr > 1) { hit_err_step(ev_id, step_no); println("bad rex bit"); return 1; } + if rxp == 1 && (rx < 0 || rx > 1) { hit_err_step(ev_id, step_no); println("bad rex bit"); return 1; } + if rbp == 1 && (rb < 0 || rb > 1) { hit_err_step(ev_id, step_no); println("bad rex bit"); return 1; } + if (rwp == 1 && rw == 1) || (rrp == 1 && rr == 1) || + (rxp == 1 && rx == 1) || (rbp == 1 && rb == 1) { + rex_byte = 0x40; + if rwp == 1 && rw == 1 { rex_byte = rex_byte + 8; } + if rrp == 1 && rr == 1 { rex_byte = rex_byte + 4; } + if rxp == 1 && rx == 1 { rex_byte = rex_byte + 2; } + if rbp == 1 && rb == 1 { rex_byte = rex_byte + 1; } + } + // ── prefix(≤2 字节;每字节 ≤255)── + if hit_has_key(text, "prefix") == 1 { + pfb := alloc(16); + npf = toml_get_int_list(text, "prefix", pfb, 3); + if npf < 0 { + hit_err_step(ev_id, step_no); println("prefix missing/malformed"); + return 1; } + if npf > 2 { + hit_err_step(ev_id, step_no); println("prefix >2 bytes"); + return 1; } + j : int, mut = 0; + loop { + if j >= npf { break; } + if hit_r32(pfb, j * 4) > 255 { + hit_err_step(ev_id, step_no); println("prefix byte >255 (not a byte)"); + return 1; } + j = j + 1; } + if npf >= 1 { pfx0 = hit_r32(pfb, 0); } + if npf >= 2 { pfx1 = hit_r32(pfb, 4); } + } + // ── opcode(必选,1..3 字节;每字节 ≤255)── + opc_p := hit_has_key(text, "opcode"); + opb := alloc(16); + nop := toml_get_int_list(text, "opcode", opb, 4); + if opc_p == 0 || nop < 1 { + hit_err_step(ev_id, step_no); println("opcode missing/malformed"); + return 1; } + if nop > 3 { + hit_err_step(ev_id, step_no); println("opcode >3 bytes"); + return 1; } + j : int, mut = 0; + loop { + if j >= nop { break; } + if hit_r32(opb, j * 4) > 255 { + hit_err_step(ev_id, step_no); println("opcode byte >255 (not a byte)"); + return 1; } + j = j + 1; } + // ── modrm 族关联校验(键出现才算族;各组字段按需自校验)── + h_role := hit_has_key(text, "modrm_reg_role"); + h_digit := hit_has_key(text, "modrm_reg_digit"); + h_rm := hit_has_key(text, "modrm_rm_role"); + h_mode := hit_has_key(text, "rm_mode"); + h_base := hit_has_key(text, "modrm_base_role"); + h_scl := hit_has_key(text, "sib_scale"); + h_sidx := hit_has_key(text, "sib_index_role"); + h_sbas := hit_has_key(text, "sib_base_role"); + h_dsz := hit_has_key(text, "disp_size"); + h_dsrc := hit_has_key(text, "disp_src"); + grp := h_role + h_digit + h_rm + h_mode + h_base + h_scl + h_sidx + h_sbas + h_dsz + h_dsrc; + if grp > 0 { + if h_role == 1 && h_digit == 1 { + hit_err_step(ev_id, step_no); println("modrm reg role and digit both present"); + return 1; } + if h_role == 0 && h_digit == 0 { + hit_err_step(ev_id, step_no); println("modrm reg role/digit missing"); + return 1; } + if h_role == 1 { + reg_code = hit_operand_code(toml_get_str(text, "modrm_reg_role")); + if reg_code == 0 { + hit_err_step(ev_id, step_no); println("unknown modrm role"); + return 1; } + } else { + digit_val = toml_get_int(text, "modrm_reg_digit"); + if digit_val < 0 || digit_val > 7 { + hit_err_step(ev_id, step_no); println("bad reg digit"); + return 1; } + is_digit = 1; + } + if h_rm == 0 { + hit_err_step(ev_id, step_no); println("modrm rm role missing"); + return 1; } + rm_code = hit_operand_code(toml_get_str(text, "modrm_rm_role")); + if rm_code == 0 { + hit_err_step(ev_id, step_no); println("unknown modrm role"); + return 1; } + if h_mode == 1 { + rm_mode = toml_get_int(text, "rm_mode"); + if rm_mode < 0 || rm_mode > 2 { + hit_err_step(ev_id, step_no); println("bad rm_mode"); + return 1; } + } + if h_scl + h_sidx + h_sbas > 0 && rm_mode != 2 { + hit_err_step(ev_id, step_no); println("sib fields need rm_mode 2"); + return 1; } + if rm_mode == 2 && (h_scl == 0 || h_sbas == 0) { + hit_err_step(ev_id, step_no); println("rm_mode 2 needs sib_scale and sib_base_role"); + return 1; } + if h_base == 1 && rm_mode == 0 { + hit_err_step(ev_id, step_no); println("modrm_base_role needs rm_mode 1/2"); + return 1; } + if h_base == 1 && rm_mode == 1 { + base_code = hit_base_code(toml_get_str(text, "modrm_base_role")); + if base_code == 0 { + hit_err_step(ev_id, step_no); println("bad modrm_base_role"); + return 1; } + } + if (h_dsz + h_dsrc > 0) && rm_mode == 0 { + hit_err_step(ev_id, step_no); println("disp needs rm_mode 1/2"); + return 1; } + if rm_mode == 2 { + scl_val = toml_get_int(text, "sib_scale"); + if scl_val < 0 || scl_val > 3 { + hit_err_step(ev_id, step_no); println("bad sib_scale"); + return 1; } + if h_sidx == 1 { + idx_code = hit_operand_code(toml_get_str(text, "sib_index_role")); + if idx_code == 0 { + hit_err_step(ev_id, step_no); println("bad sib_index_role"); + return 1; } + } + base_code = hit_base_code(toml_get_str(text, "sib_base_role")); + if base_code == 0 { + hit_err_step(ev_id, step_no); println("bad sib_base_role"); + return 1; } + } + if h_dsz == 1 { + dv := hit_raw_str(text, "disp_size"); + if str_len(dv) >= 2 && load8(dv, 0) == 34 && load8(dv, str_len(dv) - 1) == 34 { + dv = str_sub(dv, 1, str_len(dv) - 2); } + dv_ok : int, mut = 0; + if str_eq(dv, "auto") != 0 { dsz_code = 0; dv_ok = 1; } + if str_eq(dv, "1") != 0 { dsz_code = 1; dv_ok = 1; } + if str_eq(dv, "4") != 0 { dsz_code = 4; dv_ok = 1; } + if dv_ok == 0 { + hit_err_step(ev_id, step_no); println("bad disp_size"); + return 1; } + } + if h_dsrc == 1 { + dsrc_code = hit_operand_code(toml_get_str(text, "disp_src")); + if dsrc_code == 0 { + hit_err_step(ev_id, step_no); println("bad disp_src"); + return 1; } + } + } + // ── imm 族(与 modrm 族无关:字面量/回填载体)── + h_isz := hit_has_key(text, "imm_size"); + h_iro := hit_has_key(text, "imm_role"); + h_ik := hit_has_key(text, "imm_kind"); + h_ilit := hit_has_key(text, "imm_lit"); + if h_isz == 1 { + isz_val = toml_get_int(text, "imm_size"); + if isz_val != 1 && isz_val != 2 && isz_val != 4 && isz_val != 8 { + hit_err_step(ev_id, step_no); println("bad imm_size"); + return 1; } + if h_iro == 0 && h_ik == 0 { + hit_err_step(ev_id, step_no); println("imm_size without imm_role/imm_kind"); + return 1; } + } + if h_iro == 1 { + inm := toml_get_str(text, "imm_role"); + if str_eq(inm, "lit") != 0 { iro_code = HIT_CODE_LIT; } + else { + iro_code = hit_operand_code(inm); + if iro_code == 0 { + hit_err_step(ev_id, step_no); println("bad imm_role"); + return 1; } + } + } + if h_ik == 1 { + if str_eq(toml_get_str(text, "imm_kind"), "fnaddr") == 0 { + hit_err_step(ev_id, step_no); println("bad imm_kind"); + return 1; } + if h_isz == 0 || isz_val != 8 { + hit_err_step(ev_id, step_no); println("imm_kind fnaddr needs imm_size 8"); + return 1; } + ik_val = HIT_IMM_FNADDR; + } + if h_ilit == 1 { + ilit_val = toml_get_int(text, "imm_lit"); + if ilit_val < 0 { + hit_err_step(ev_id, step_no); println("bad imm_lit (negative unsupported)"); + return 1; } + } + // ── rel 族(三字段同现;kind = event/func/extern;size = 8/32)── + h_rro := hit_has_key(text, "rel_role"); + h_rk := hit_has_key(text, "rel_kind"); + h_rs := hit_has_key(text, "rel_size"); + if h_rro + h_rk + h_rs > 0 { + if h_rro == 0 || h_rk == 0 || h_rs == 0 { + hit_err_step(ev_id, step_no); println("rel fields incomplete (role/kind/size)"); + return 1; } + rkn := toml_get_str(text, "rel_kind"); + if str_eq(rkn, "event") == 0 && str_eq(rkn, "func") == 0 && str_eq(rkn, "extern") == 0 { + hit_err_step(ev_id, step_no); println("bad rel kind"); + return 1; } + if str_eq(rkn, "event") != 0 { rk_val = HIT_REL_EVENT; } + if str_eq(rkn, "func") != 0 { rk_val = HIT_REL_FUNC; } + if str_eq(rkn, "extern") != 0 { rk_val = HIT_REL_EXTERN; } + rs_val = toml_get_int(text, "rel_size"); + if rs_val != 8 && rs_val != 32 { + hit_err_step(ev_id, step_no); println("bad rel size"); + return 1; } + rro_code = hit_operand_code(toml_get_str(text, "rel_role")); + if rro_code == 0 { + hit_err_step(ev_id, step_no); println("bad rel role"); + return 1; } + } + // ── cond 键(branch 条件源槽)── + if hit_has_key(text, "cond_role") == 1 { + cond_code = hit_operand_code(toml_get_str(text, "cond_role")); + if cond_code == 0 { + hit_err_step(ev_id, step_no); println("bad cond_role"); + return 1; } + } + // ── 字节流组装(prefix → REX → opcode)+ 追加 108B 步记录 ── + stb := alloc(8); + nst : int, mut = 0; + if npf >= 1 { store8(stb, nst, pfx0); nst = nst + 1; } + if npf >= 2 { store8(stb, nst, pfx1); nst = nst + 1; } + if rex_byte != 0 { store8(stb, nst, rex_byte); nst = nst + 1; } + j = 0; + loop { + if j >= nop { break; } + store8(stb, nst, hit_r32(opb, j * 4)); + nst = nst + 1; + j = j + 1; } + if nst > 0 { op0 = load8(stb, 0); } + if nst > 1 { op1 = load8(stb, 1); } + hit_grow_steps(g_hit_step_count + 1); + sb := g_hit_step_count * HIT_STEP_REC; + g_hit_step_count = g_hit_step_count + 1; + hit_zero_bytes(g_hit_steps, sb, HIT_STEP_REC); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OP0, op0); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OP1, op1); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REG_ROLE, reg_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_RM_ROLE, rm_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_RM_MODE, rm_mode); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_PFX0, pfx0); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_PFX1, pfx1); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REX, rex_byte); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OPB0, hit_r32(opb, 0)); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OPB1, hit_r32(opb, 4)); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OPB2, hit_r32(opb, 8)); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_OPN, nop); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REG_IS_DIGIT, is_digit); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REG_DIGIT, digit_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_BASE_ROLE, base_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_SIB_SCALE, scl_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_SIB_INDEX, idx_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_DISP_SIZE, dsz_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_DISP_SRC, dsrc_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_IMM_SIZE, isz_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_IMM_ROLE, iro_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_IMM_KIND, ik_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_IMM_LIT, ilit_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REL_ROLE, rro_code); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REL_KIND, rk_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_REL_SIZE, rs_val); + hit_w32(g_hit_steps, sb + HIT_ST_OFF_COND_ROLE, cond_code); + return 0; } + +// ════════════════════════════════════════════════════════════════ +// [[event.proj]] 投影小节解析(chunk 内 ph 处起;多步/平铺两形) +// ════════════════════════════════════════════════════════════════ +// Returns 0 = 成功;1 = 失败(错误已打印)。 +fn hit_parse_proj_section(chunk: string, ph: int, ev_id: int) -> int { + pl := hit_line_after(chunk, ph); + // 本投影文本终点:平铺 = 下一 [[event.proj]](或块尾);步小节形 = 首步头 + //(步头位于下一 proj 头之前才算本投影的步小节——防跨 proj 误配) + nxt_pr := hit_find_tag(chunk, pl, "[[event.proj]]"); + st_hdr := hit_find_tag(chunk, pl, "[[event.proj.step]]"); + sectioned : int, mut = 0; + pend : int, mut = str_len(chunk); + if st_hdr >= 0 && (nxt_pr < 0 || st_hdr < nxt_pr) { + sectioned = 1; + pend = st_hdr; } + else if nxt_pr >= 0 { + pend = nxt_pr; } + pfields := str_sub(chunk, pl, pend - pl); + // isa 必选且须为 x86-64(层级无关:平铺时 isa 混于步字段) + if str_eq(toml_get_str(pfields, "isa"), "x86-64") == 0 { + hit_err_ev(ev_id); println("unknown isa"); + return 1; } + first_step := g_hit_step_count; + scnt : int, mut = 0; + if sectioned == 0 { + // 平铺单步(v1 子集:isa 之外的字段 = 单步字段;白名单扫 = 步层全表) + if hit_parse_step(pfields, ev_id, 0, 1) != 0 { return 1; } + scnt = 1; } + else { + if hit_scan_keys_ok(pfields, "isa") != 0 { return 1; } + // 步小节逐个解析(步文本终点 = 下一 step 头 / 下一 proj 头 / 块尾) + sp : int, mut = st_hdr; + loop { + if sp < 0 { break; } + sl := hit_line_after(chunk, sp); + ns := hit_find_tag(chunk, sl, "[[event.proj.step]]"); + np2 := hit_find_tag(chunk, sl, "[[event.proj]]"); + send : int, mut = str_len(chunk); + if ns >= 0 && ns < send { send = ns; } + if np2 >= 0 && np2 < send { send = np2; } + stext := str_sub(chunk, sl, send - sl); + if hit_parse_step(stext, ev_id, scnt, 0) != 0 { return 1; } + scnt = scnt + 1; + // 续行边界(Task 1 评审 fix):ns 在下一 proj 头之后 = 属于后续 + // [[event.proj]] 的步小节——不得续入本投影(np2 只截步文本、不 + // 终止循环;旧缺陷 = 静默吸收后续投影步:本投影步数膨胀 + 步 + // 记录重复入库,多 proj 步小节事件加载成功但表已错) + if ns >= 0 && (np2 < 0 || ns < np2) { sp = ns; } + else { sp = -1; } } + if scnt == 0 { + hit_err_ev(ev_id); println("projection has no steps"); + return 1; } + } + // 追加投影记录(isa/步数/首步字节偏移) + hit_grow_projs(g_hit_proj_count + 1); + ps := g_hit_proj_count; + g_hit_proj_count = ps + 1; + hit_w32(g_hit_projs, ps * HIT_PROJ_REC + HIT_PJ_OFF_ISA, HIT_ISA_X86); + hit_w32(g_hit_projs, ps * HIT_PROJ_REC + HIT_PJ_OFF_STEP_COUNT, scnt); + hit_w32(g_hit_projs, ps * HIT_PROJ_REC + HIT_PJ_OFF_STEP_OFF, first_step * HIT_STEP_REC); + return 0; } + +// ════════════════════════════════════════════════════════════════ +// [[event]] 事件小节解析(含其全部投影小节) +// ════════════════════════════════════════════════════════════════ +// chunk = 自 "[[event]]" 行起、到下个节头前的文本(不含下一节)。 +// Returns 0 = 成功;1 = 失败(错误已打印)。 +fn hit_parse_event_chunk(chunk: string) -> int { + // 事件字段区 = 头行后、首个 proj/step 小节前(step 在 proj 前 = 结构错) + fe := hit_line_after(chunk, 0); + p_off := hit_find_tag(chunk, fe, "[[event.proj]]"); + s_off := hit_find_tag(chunk, fe, "[[event.proj.step]]"); + if s_off >= 0 && (p_off < 0 || s_off < p_off) { + println("error: HIT table: stray [[event.proj.step]] before [[event.proj]]"); + return 1; } + fend : int, mut = str_len(chunk); + if p_off >= 0 && p_off < fend { fend = p_off; } + if s_off >= 0 && s_off < fend { fend = s_off; } + ev_text := str_sub(chunk, fe, fend - fe); + if hit_scan_keys_ok(ev_text, "id name arith inputs outputs side_effect") != 0 { return 1; } + id := toml_get_int(ev_text, "id"); + if id <= 0 { + println("error: HIT table: event missing or invalid id"); + return 1; } + // 重复 id 检测 + k : int, mut = 0; + loop { + if k >= g_hit_event_count { break; } + if hit_r32(g_hit_events, k * HIT_EVENT_REC + HIT_EV_OFF_ID) == id { + print("error: HIT table: duplicate event id "); print_i(id); println(""); + return 1; } + k = k + 1; } + name := toml_get_str(ev_text, "name"); + if str_len(name) == 0 { + hit_err_ev(id); println("missing name"); + return 1; } + inputs := toml_get_int(ev_text, "inputs"); + outputs := toml_get_int(ev_text, "outputs"); + se := toml_get_str(ev_text, "side_effect"); + side : int, mut = -1; + if str_eq(se, "pure") != 0 { side = HIT_SIDE_PURE; } + if str_eq(se, "effect") != 0 { side = HIT_SIDE_EFFECT; } + if side < 0 { + hit_err_ev(id); println("bad side_effect"); + return 1; } + // 名字入名字表(8B/项存串指针;str_sub 副本 arena 存活) + hit_grow_names(g_hit_name_count + 1); + name_ni : int, mut = g_hit_name_count; + g_hit_name_count = name_ni + 1; + store_str_ptr(g_hit_names, name_ni * 8, name); + // 事件行追加(proj 范围字段在投影解析后回填) + hit_grow_events(g_hit_event_count + 1); + slot := g_hit_event_count; + g_hit_event_count = slot + 1; + hit_zero_bytes(g_hit_events, slot * HIT_EVENT_REC, HIT_EVENT_REC); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_ID, id); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_NAME, name_ni); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_INPUTS, inputs); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_OUTPUTS, outputs); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_SIDE, side); + // 投影小节(多 proj:事件 32/36 记范围;20/24/28 = proj0 镜像) + first_proj : int, mut = g_hit_proj_count; + pcount : int, mut = 0; + hpos : int, mut = fend; + loop { + ph := hit_find_tag(chunk, hpos, "[[event.proj]]"); + if ph < 0 { break; } + pcount = pcount + 1; + if hit_parse_proj_section(chunk, ph, id) != 0 { return 1; } + hpos = ph + 1; } + if pcount == 0 { + hit_err_ev(id); println("missing [[event.proj]]"); + return 1; } + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_OFF, first_proj); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_COUNT, pcount); + // proj0 镜像(M1 消费方视区:isa/步数/首步字节偏移) + p0 := first_proj * HIT_PROJ_REC; + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_ISA, + hit_r32(g_hit_projs, p0 + HIT_PJ_OFF_ISA)); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_STEP_COUNT, + hit_r32(g_hit_projs, p0 + HIT_PJ_OFF_STEP_COUNT)); + hit_w32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_STEP_OFF, + hit_r32(g_hit_projs, p0 + HIT_PJ_OFF_STEP_OFF)); + return 0; } + +// ════════════════════════════════════════════════════════════════ +// [runtime.x86] 运行时小节(spec §6 最小描述;可选小节,出现则 8 键齐备) +// ════════════════════════════════════════════════════════════════ +// chunk = 自 "[runtime.x86]" 行起、到下个节头前的文本(不含下一节)。 +// Returns 0 = 成功;1 = 失败(错误已打印)。 +fn hit_parse_runtime_chunk(chunk: string) -> int { + fe := hit_line_after(chunk, 0); + text := str_sub(chunk, fe, str_len(chunk) - fe); + if hit_scan_keys_ok(text, "call_mechanism call_ret_addr call_stack_grow param_mode param_regs save_regs extern_entry extern_resolve") != 0 { return 1; } + // 8 键齐备 + if hit_has_key(text, "call_mechanism") == 0 { println("error: HIT table: runtime section missing call_mechanism"); return 1; } + if hit_has_key(text, "call_ret_addr") == 0 { println("error: HIT table: runtime section missing call_ret_addr"); return 1; } + if hit_has_key(text, "call_stack_grow") == 0 { println("error: HIT table: runtime section missing call_stack_grow"); return 1; } + if hit_has_key(text, "param_mode") == 0 { println("error: HIT table: runtime section missing param_mode"); return 1; } + if hit_has_key(text, "param_regs") == 0 { println("error: HIT table: runtime section missing param_regs"); return 1; } + if hit_has_key(text, "save_regs") == 0 { println("error: HIT table: runtime section missing save_regs"); return 1; } + if hit_has_key(text, "extern_entry") == 0 { println("error: HIT table: runtime section missing extern_entry"); return 1; } + if hit_has_key(text, "extern_resolve") == 0 { println("error: HIT table: runtime section missing extern_resolve"); return 1; } + rt := alloc(32); + // 机制枚举值(非 stack/pushed/down/regs/call_rel32/ext_rel → 拒) + if str_eq(toml_get_str(text, "call_mechanism"), "stack") == 0 { + println("error: HIT table: bad call_mechanism"); + return 1; } + hit_w32(rt, HIT_RT_CALL_MECHANISM * 4, 1); + if str_eq(toml_get_str(text, "call_ret_addr"), "pushed") == 0 { + println("error: HIT table: bad call_ret_addr"); + return 1; } + hit_w32(rt, HIT_RT_CALL_RET_ADDR * 4, 1); + if str_eq(toml_get_str(text, "call_stack_grow"), "down") == 0 { + println("error: HIT table: bad call_stack_grow"); + return 1; } + hit_w32(rt, HIT_RT_CALL_STACK_GROW * 4, 1); + pm := toml_get_str(text, "param_mode"); + if str_eq(pm, "regs") == 0 && str_eq(pm, "stack") == 0 { + println("error: HIT table: bad param_mode"); + return 1; } + if str_eq(pm, "regs") != 0 { hit_w32(rt, HIT_RT_PARAM_MODE * 4, 1); } + if str_eq(pm, "stack") != 0 { hit_w32(rt, HIT_RT_PARAM_MODE * 4, 2); } + if str_eq(toml_get_str(text, "extern_entry"), "call_rel32") == 0 { + println("error: HIT table: bad extern_entry"); + return 1; } + hit_w32(rt, HIT_RT_EXTERN_ENTRY * 4, 1); + if str_eq(toml_get_str(text, "extern_resolve"), "ext_rel") == 0 { + println("error: HIT table: bad extern_resolve"); + return 1; } + hit_w32(rt, HIT_RT_EXTERN_RESOLVE * 4, 1); + // 寄存器掩码(空格分隔寄存器名 → regno 位集) + if hit_parse_reg_mask(toml_get_str(text, "param_regs"), rt, HIT_RT_PARAM_MASK * 4) != 0 { return 1; } + if hit_parse_reg_mask(toml_get_str(text, "save_regs"), rt, HIT_RT_SAVE_MASK * 4) != 0 { return 1; } + g_hit_runtime = rt; + g_hit_runtime_present = 1; + return 0; } + +// 寄存器掩码解析(空格分隔名列表 → 位掩码写 rt+pos);0 = 成功;1 = 未知名(已打印) +fn hit_parse_reg_mask(raw: string, rt: string, pos: int) -> int { + rlen := str_len(raw); + mask : int, mut = 0; + p : int, mut = 0; + loop { + if p >= rlen { break; } + loop { + if p >= rlen { break; } + if load8(raw, p) == 32 { p = p + 1; } else { break; } } + if p >= rlen { break; } + e : int, mut = p; + loop { + if e >= rlen { break; } + if load8(raw, e) == 32 { break; } + e = e + 1; } + rn := hit_reg_code(str_sub(raw, p, e - p)); + if rn < 0 { + print("error: HIT table: runtime unknown register '"); + print(str_sub(raw, p, e - p)); println("'"); + return 1; } + mask = mask + hit_bit(rn); + p = e; } + hit_w32(rt, pos, mask); + return 0; } + +// ════════════════════════════════════════════════════════════════ +// 公共接口(M1 Task 1 契约 + M2a Task 1 扩展) +// ════════════════════════════════════════════════════════════════ + +// 加载 HIT 表文件并填充 g_hit_events/g_hit_projs/g_hit_steps(计数式重置—— +// 重复调用覆盖旧表)。[[event]] 与 [runtime.x86] 小节按出现序任意交错解析 +//(runtime 可夹在事件间,尾部追加事件亦可)。Returns 0 = 成功;1 = 失败 +//(错误已打印,表不可用)。 +fn load_hit_table(path: string) -> int { + content := read_file(path); + if str_len(content) == 0 { + print("error: cannot open HIT table: "); println(path); + return 1; } + g_hit_event_count = 0; + g_hit_step_count = 0; + g_hit_proj_count = 0; + g_hit_name_count = 0; + g_hit_runtime_present = 0; + expected := toml_get_int(content, "events"); // [table] events(缺省 0 → 尾部校验兜底) + pos : int, mut = 0; + loop { + ev := hit_find_tag(content, pos, "[[event]]"); + rt := hit_find_tag(content, pos, "[runtime.x86]"); + if ev < 0 && rt < 0 { break; } + if ev < 0 || (rt >= 0 && rt < ev) { + // [runtime.x86] 小节:到下一 [[event]] / 再一 runtime 头 / EOF 为止 + end : int, mut = str_len(content); + e2 := hit_find_tag(content, rt + 1, "[[event]]"); + r2 := hit_find_tag(content, rt + 1, "[runtime.x86]"); + if e2 >= 0 && e2 < end { end = e2; } + if r2 >= 0 && r2 < end { end = r2; } + chunk := str_sub(content, rt, end - rt); + if hit_parse_runtime_chunk(chunk) != 0 { return 1; } + pos = end; } + else { + end : int, mut = str_len(content); + e2 := hit_find_tag(content, ev + 1, "[[event]]"); + r2 := hit_find_tag(content, ev + 1, "[runtime.x86]"); + if e2 >= 0 && e2 < end { end = e2; } + if r2 >= 0 && r2 < end { end = r2; } + chunk := str_sub(content, ev, end - ev); + if hit_parse_event_chunk(chunk) != 0 { return 1; } + pos = end; } } + if g_hit_event_count == 0 { + println("error: HIT table: no [[event]] sections"); + return 1; } + // 注(M2a Task 1 评审记录):spec §2「运行时核事件 id 1-9 每表必具」与验证项 + // 「缺事件…拒绝」的加载器强制在场检查暂缓——事件绑定 = Task 4 落地(届时 + // 消费方按 id 查找自然暴露缺失),此处仅拦零事件与 [table] events 计数不符。 + if g_hit_event_count != expected { + print("error: HIT table: [table] events="); print_i(expected); + print(" parsed="); print_i(g_hit_event_count); println(" mismatch"); + return 1; } + return 0; } + +// 表模式激活态:加载成功(g_hit_event_count > 0)= 发射循环启用表优先 +fn hit_table_active() -> int { + if g_hit_event_count > 0 { return 1; } + return 0; } + +// 事件 id → 事件槽(记录下标);-1 = 无 +fn hit_event_lookup(id: int) -> int { + i : int, mut = 0; + loop { + if i >= g_hit_event_count { break; } + if hit_r32(g_hit_events, i * HIT_EVENT_REC + HIT_EV_OFF_ID) == id { return i; } + i = i + 1; } + return -1; } + +// 事件名(诊断用;g_hit_names 表取串) +fn hit_event_name(slot: int) -> string { + if slot < 0 || slot >= g_hit_event_count { return ""; } + ni := hit_r32(g_hit_events, slot * HIT_EVENT_REC + HIT_EV_OFF_NAME); + if ni < 0 || ni >= g_hit_name_count { return ""; } + return load_str_ptr(g_hit_names, ni * 8); } + +// 事件投影形态数;-1 = 事件槽非法 +fn hit_proj_count(event_slot: int) -> int { + if event_slot < 0 || event_slot >= g_hit_event_count { return -1; } + return hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_COUNT); } + +// 事件第 proj_i 投影步数;-1 = 越界 +fn hit_proj_step_count(event_slot: int, proj_i: int) -> int { + if event_slot < 0 || event_slot >= g_hit_event_count { return -1; } + po := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_OFF); + pc := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_COUNT); + if proj_i < 0 || proj_i >= pc { return -1; } + return hit_r32(g_hit_projs, (po + proj_i) * HIT_PROJ_REC + HIT_PJ_OFF_STEP_COUNT); } + +// 取事件第 proj_i 投影第 step_i 步:把该步 108B v2 记录原样复制到 out +//(字段布局 = 上方 HIT_ST_OFF_* 常量序;调用方保证 ≥108B)。 +// Returns 0 = 成功;-1 = 事件槽/proj/步号非法。 +fn hit_proj_step_get(event_slot: int, proj_i: int, step_i: int, out: string) -> int { + if event_slot < 0 || event_slot >= g_hit_event_count { return -1; } + po := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_OFF); + pc := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_PROJ_COUNT); + if proj_i < 0 || proj_i >= pc { return -1; } + ps := (po + proj_i) * HIT_PROJ_REC; + sc := hit_r32(g_hit_projs, ps + HIT_PJ_OFF_STEP_COUNT); + if step_i < 0 || step_i >= sc { return -1; } + off := hit_r32(g_hit_projs, ps + HIT_PJ_OFF_STEP_OFF); + j : int, mut = 0; + loop { + if j >= HIT_STEP_REC { break; } + store8(out, j, load8(g_hit_steps, off + step_i * HIT_STEP_REC + j)); + j = j + 1; } + return 0; } + +// 取事件投影步(M1 契约 = proj0 视图):把该步记录原样复制到 out(调用方 +// 保证 ≥HIT_STEP_REC;M1 消费方只读前 20B 视区 = 布局未变。事件 24/28 字段 +// = proj0 镜像,语义与 M1 记录等价——单投影事件的 proj0 = 唯一投影)。 +// Returns 0 = 成功;-1 = 事件槽/步号非法。 +fn hit_proj_step(event_slot: int, step_i: int, out: string) -> int { + if event_slot < 0 || event_slot >= g_hit_event_count { return -1; } + sc := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_STEP_COUNT); + if step_i < 0 || step_i >= sc { return -1; } + off := hit_r32(g_hit_events, event_slot * HIT_EVENT_REC + HIT_EV_OFF_STEP_OFF); + j : int, mut = 0; + loop { + if j >= HIT_STEP_REC { break; } + store8(out, j, load8(g_hit_steps, off + step_i * HIT_STEP_REC + j)); + j = j + 1; } + return 0; } + +// 运行时条目查询:key = 8 键名之一(call_mechanism/call_ret_addr/ +// call_stack_grow/param_mode/param_regs/save_regs/extern_entry/extern_resolve)。 +// 值 = 4B 码或位掩码写入 out。Returns 0 = 已写;-1 = 无 runtime 小节/未知键。 +fn hit_runtime_get(key: string, out: string) -> int { + if g_hit_runtime_present == 0 { return -1; } + slot : int, mut = -1; + if str_eq(key, "call_mechanism") != 0 { slot = HIT_RT_CALL_MECHANISM; } + if str_eq(key, "call_ret_addr") != 0 { slot = HIT_RT_CALL_RET_ADDR; } + if str_eq(key, "call_stack_grow") != 0 { slot = HIT_RT_CALL_STACK_GROW; } + if str_eq(key, "param_mode") != 0 { slot = HIT_RT_PARAM_MODE; } + if str_eq(key, "param_regs") != 0 { slot = HIT_RT_PARAM_MASK; } + if str_eq(key, "save_regs") != 0 { slot = HIT_RT_SAVE_MASK; } + if str_eq(key, "extern_entry") != 0 { slot = HIT_RT_EXTERN_ENTRY; } + if str_eq(key, "extern_resolve") != 0 { slot = HIT_RT_EXTERN_RESOLVE; } + if slot < 0 { return -1; } + hit_w32(out, 0, hit_r32(g_hit_runtime, slot * 4)); + return 0; } + +// ── --dump-table 读回通道(corearch 加载后调用;只读表状态,零写侧影响)── +// 评审项 1 锁(多 proj 步小节归属)与评审项 4(v2 字段区读回覆盖)共用: +// 每事件一行 proj 表记录回读(hit_proj_count/hit_proj_step_count——步数吞并 +// 在此失真);每步一行 v2 字段区探针(hit_proj_step_get 整条 108B 复制后读 +// opcode 区 OPB0 = 偏移 32——写读一致的实证);runtime 槽位掩码读回。 +fn hit_dump_table_state() { + // 事件 → 逐 proj 步数(proj0 镜像/M1 视区也随 p0 步数一起验证) + ei : int, mut = 0; + loop { + if ei >= g_hit_event_count { break; } + print("hit proj: ev="); + print_i(hit_r32(g_hit_events, ei * HIT_EVENT_REC + HIT_EV_OFF_ID)); + pc := hit_proj_count(ei); + print(" projs="); print_i(pc); + pi : int, mut = 0; + loop { + if pi >= pc { break; } + print(" p"); print_i(pi); print("="); + print_i(hit_proj_step_count(ei, pi)); + pi = pi + 1; } + println(""); + ei = ei + 1; } + // 逐步 opcode 首字节探针(v2 字段区 = 步记录偏移 32 起) + st := alloc(HIT_STEP_REC); + ej : int, mut = 0; + loop { + if ej >= g_hit_event_count { break; } + idj := hit_r32(g_hit_events, ej * HIT_EVENT_REC + HIT_EV_OFF_ID); + pj : int, mut = 0; + loop { + if pj >= hit_proj_count(ej) { break; } + sj : int, mut = 0; + loop { + if sj >= hit_proj_step_count(ej, pj) { break; } + if hit_proj_step_get(ej, pj, sj, st) == 0 { + print("hit step: ev="); print_i(idj); + print(" p="); print_i(pj); print(" s="); print_i(sj); + print(" opb="); print_i(hit_r32(st, HIT_ST_OFF_OPB0)); + println(""); } + sj = sj + 1; } + pj = pj + 1; } + ej = ej + 1; } + // runtime 槽读回(掩码十进制 = 测试期望锚:param = rdi rsi rdx rcx r8 r9; + // save = rbx r12-15) + if hit_runtime_get("param_regs", st) == 0 { + print("hit rt: param_regs="); print_i(hit_r32(st, 0)); println(""); } + if hit_runtime_get("save_regs", st) == 0 { + print("hit rt: save_regs="); print_i(hit_r32(st, 0)); println(""); } +} diff --git a/src/arch/hit/lower_to_core.cr b/src/arch/hit/lower_to_core.cr new file mode 100644 index 00000000..f39ee3a1 --- /dev/null +++ b/src/arch/hit/lower_to_core.cr @@ -0,0 +1,657 @@ +// === src/arch/hit/lower_to_core.cr === +// 合成层(HIT M1 Task 3):IR 直线子集 → 4 核事件流 + 常量池。 +// 设计:docs/superpowers/plans/2026-09-05-hit-minimal-core-m1.md Task 3。 +// 管线(表模式下 corearch 内部):IR →(本文件降低)→ 事件流 →(表投影)→ 字节。 +// +// 事件流记录(g_hit_ev_stream,每条 20B = 5 × i32): +// [ev_id i32, dst i32, src1 i32, src2 i32, flags i32] +// ev_id 最小核事件号(sub=1 nand=2 load=3 store=4——语义锚,表加载侧校验存在) +// dst 结果变量(store = 0 未用);src1/src2 = 变量或常量池槽(flags 区分) +// flags bit0 = src1 为常量池槽(否则变量),bit1 = src2 为常量池槽 +// 未用操作数字段恒存 0(勿存 -1:hit_w32 字节拆取仅定义非负值——自持 x86 截断 +// 除法与 Python 解释地板除读回分裂(255 vs 0xFFFFFFFF),M2-2a 改存 0)。 +// 降低规则(M1 直线子集:const/store/load/add/sub): +// IR_CONST n → load 事件(addr = 池槽;池值发射时写 ELF rodata) +// IR_BINARY OP_SUB → sub 事件直通 +// IR_BINARY OP_ADD → sub 反减 sub(a, sub(0, b)):事件 E1 sub(d, 池0, b)、 +// E2 sub(d, a, d)——中间值驻 d(编码器先读后写,读先于写); +// dest-fresh 前提 d != a(违反 → 0 事件落旧路径,M2-2c 防御) +// IR_LOAD/IR_STORE → load/store 事件直通(槽寻址;全局/形态不符由编码器预检落旧路径) +// 超子集 IR_BINARY 子操作 → 报错「needs more events」(exit 1) +// 未映射指令(RET/ALLOC/ARENA_*/…)→ 无事件 → 旧路径(M1 混合模式)。 +// +// 常量池(g_hit_pool,每槽 8B,按值去重):值于发射尾随字符串区写入 .rodata; +// 事件对池槽的引用由编码器记 g_hit_pool_patch(instr.cr 记、elf.cr 发射后回填 rip 位移)。 +// +// 本文件进两份构建:corearch concat 清单(build_selfhost_native.py 的 +// hit_engine_files 段,位于 common 之后、内核/架构轴之前)与组合根 +// project-mode 单元(src/targets/x86_64-linux/ +// _import.cr 的 `import lower_to_core`,2026-09-10 补——该单元原先漏此导入, +// 致 15×N06+16×N01 静默未定义)。import 行供独立 `corec check`/project-mode +// 解析(concat 构建剥离 import 行;io/ast/globals/dyn_arr/hit 均在闭包内; +// dyn_arr 的 iri_* 等访问器与 w32/r32/r64 即编译器 IR 访问面)。 + +import io +import fmt +import ast +import globals +import dyn_arr +import hit + +// ════════════════════════════════════════════════════════════════ +// 事件流 / 常量池 布局与状态 +// ════════════════════════════════════════════════════════════════ + +// 最小核事件号契约(core-x86.toml 的 id 同此——表加载侧按 id 匹配,缺则旧路径) +HIT_EV_SUB : int = 1; +HIT_EV_NAND : int = 2; +HIT_EV_LOAD : int = 3; +HIT_EV_STORE : int = 4; +HIT_EV_JUMP : int = 5; // M2a:jump(rel32 事件流位置回填——发射 = instr.cr Task 2) +HIT_EV_BRANCH: int = 6; // M2a:branch/call/ret/extern 模板已声明——发射 = Task 3 起 +HIT_EV_CALL : int = 7; +HIT_EV_RET : int = 8; +HIT_EV_EXTERN: int = 9; + +HIT_ES_REC : int = 20; // 5 × i32 +HIT_ES_OFF_ID : int = 0; +HIT_ES_OFF_DST : int = 4; +HIT_ES_OFF_S1 : int = 8; +HIT_ES_OFF_S2 : int = 12; +HIT_ES_OFF_FLAGS : int = 16; +HIT_ES_S1_POOL : int = 1; // src1 = 常量池槽(否则 = 变量号) +HIT_ES_S2_POOL : int = 2; // src2 = 常量池槽(否则 = 变量号) + +g_hit_ev_stream : string, mut; g_hit_ev_count : int, mut; g_hit_ev_cap : int, mut; +g_hit_ev_map : string, mut; g_hit_ev_map_size : int, mut; // 每 IR 指令 8B {start u32, count u32} +g_hit_pool : string, mut; g_hit_pool_count : int, mut; g_hit_pool_cap : int, mut; +g_hit_pool_patch : string, mut; g_hit_pool_patch_count : int, mut; g_hit_pool_patch_cap : int, mut; // 16B/条 {pos i64, k i64} + +// ── M2a Task 2:rel 回填表 + 事件发射位置表 + 注入通道激活态 ── +// g_hit_rel:三张回填表合一(kind 0 = 事件流位置、1 = 函数起点、2 = 外部符号序), +// 24B/条 {kind i64, pos i64, target i64}(pos = rel 字段位置)。登记 = instr.cr +// 发射器(hit_rel_add);回填 = elf.cr 发射后循环(本任务 kind 0 = 事件序目标)。 +// g_hit_ev_pos:事件序 → 发射字节位置(8B/序;发射器边发边记——jump 回填消费)。 +// g_hit_inject_active:--hit-events-file 注入通道激活(测试专用——预检拒绝升级为 +// 大声错误 + 事件计数不符 = exit 1,防静默落旧路径掩盖注入文件错误)。 +g_hit_rel : string, mut; g_hit_rel_count : int, mut; g_hit_rel_cap : int, mut; +g_hit_ev_pos : string, mut; g_hit_ev_pos_count : int, mut; g_hit_ev_pos_cap : int, mut; +g_hit_inject_active : int, mut; + +fn hit_grow_ev(needed: int) { + if needed < g_hit_ev_cap { return; } + nc : ., mut = g_hit_ev_cap * 2; + if nc < 64 { nc = 64; } + if nc < needed { nc = needed + 8; } + nb := alloc(nc * HIT_ES_REC); + hit_copy_bytes(g_hit_ev_stream, g_hit_ev_cap * HIT_ES_REC, nb); + g_hit_ev_stream = nb; + g_hit_ev_cap = nc; } + +fn hit_grow_pool(needed: int) { + if needed < g_hit_pool_cap { return; } + nc : ., mut = g_hit_pool_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 8); + hit_copy_bytes(g_hit_pool, g_hit_pool_cap * 8, nb); + g_hit_pool = nb; + g_hit_pool_cap = nc; } + +fn hit_grow_pool_patch(needed: int) { + if needed < g_hit_pool_patch_cap { return; } + nc : ., mut = g_hit_pool_patch_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 16); + hit_copy_bytes(g_hit_pool_patch, g_hit_pool_patch_cap * 16, nb); + g_hit_pool_patch = nb; + g_hit_pool_patch_cap = nc; } + +fn hit_grow_rel(needed: int) { + if needed < g_hit_rel_cap { return; } + nc : ., mut = g_hit_rel_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 24); + hit_copy_bytes(g_hit_rel, g_hit_rel_cap * 24, nb); + g_hit_rel = nb; + g_hit_rel_cap = nc; } + +fn hit_grow_ev_pos(needed: int) { + if needed < g_hit_ev_pos_cap { return; } + nc : ., mut = g_hit_ev_pos_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 8); + hit_copy_bytes(g_hit_ev_pos, g_hit_ev_pos_cap * 8, nb); + g_hit_ev_pos = nb; + g_hit_ev_pos_cap = nc; } + +// rel 回填登记(M2a Task 2 接口——instr.cr 发射器调用;elf.cr 发射后回填)。 +// kind: 0 = 事件流位置(jump/branch——事件序目标,本任务落地)、 +// 1 = 函数起点、2 = 外部符号序(Task 4/5 消费——登记接口先行,回填拒接)。 +fn hit_rel_add(kind: int, pos: int, target: int) { + hit_grow_rel(g_hit_rel_count + 1); + slot : ., mut = g_hit_rel_count; + g_hit_rel_count = slot + 1; + w64(g_hit_rel, slot * 24, kind); + w64(g_hit_rel, slot * 24 + 8, pos); + w64(g_hit_rel, slot * 24 + 16, target); } + +// 事件序 → 发射位置记录(8B;发射器在事件字节落位前写入——位置 = 绝对 buf 偏移) +fn hit_ev_pos_set(ord: int, pos: int) { + hit_grow_ev_pos(ord + 1); + w64(g_hit_ev_pos, ord * 8, pos); + if ord + 1 > g_hit_ev_pos_count { g_hit_ev_pos_count = ord + 1; } } + +// 8B 补码小端写(借位链,两补正确——负值池槽可表达;仿 dyn_arr w32 链扩至 8 字节) +fn hit_pool_w64(buf: string, pos: int, v: int) { + b0 : ., mut = v % 256; t1 : ., mut = v / 256; + b1 : ., mut = t1 % 256; t2 : ., mut = t1 / 256; + b2 : ., mut = t2 % 256; t3 : ., mut = t2 / 256; + b3 : ., mut = t3 % 256; t4 : ., mut = t3 / 256; + b4 : ., mut = t4 % 256; t5 : ., mut = t4 / 256; + b5 : ., mut = t5 % 256; t6 : ., mut = t5 / 256; + b6 : ., mut = t6 % 256; t7 : ., mut = t6 / 256; + b7 : ., mut = t7 % 256; + if b0 < 0 { b0 = b0 + 256; b1 = b1 - 1; } + if b1 < 0 { b1 = b1 + 256; b2 = b2 - 1; } + if b2 < 0 { b2 = b2 + 256; b3 = b3 - 1; } + if b3 < 0 { b3 = b3 + 256; b4 = b4 - 1; } + if b4 < 0 { b4 = b4 + 256; b5 = b5 - 1; } + if b5 < 0 { b5 = b5 + 256; b6 = b6 - 1; } + if b6 < 0 { b6 = b6 + 256; b7 = b7 - 1; } + if b7 < 0 { b7 = b7 + 256; } + store8(buf, pos + 0, b0); store8(buf, pos + 1, b1); + store8(buf, pos + 2, b2); store8(buf, pos + 3, b3); + store8(buf, pos + 4, b4); store8(buf, pos + 5, b5); + store8(buf, pos + 6, b6); store8(buf, pos + 7, b7); } + +fn hit_lower_reset() { + g_hit_ev_count = 0; + g_hit_pool_count = 0; + g_hit_pool_patch_count = 0; + // M2a Task 2:rel 回填表 + 事件位置表逐次发射清零 + g_hit_rel_count = 0; + g_hit_ev_pos_count = 0; } + +fn hit_ev_map_init(n: int) { + g_hit_ev_map_size = n; + g_hit_ev_map = alloc(n * 8 + 8); } + +fn hit_ev_map_set(i: int, start: int, cnt: int) { + w32(g_hit_ev_map, i * 8, start); + w32(g_hit_ev_map, i * 8 + 4, cnt); } + +fn hit_ev_map_cnt(i: int) -> int { + if i < 0 || i >= g_hit_ev_map_size { return 0; } + return r32(g_hit_ev_map, i * 8 + 4); } + +fn hit_ev_map_start(i: int) -> int { + if i < 0 || i >= g_hit_ev_map_size { return 0; } + return r32(g_hit_ev_map, i * 8); } + +// 追加一条事件(20B);未用操作数 = 0(勿存 -1:hit_w32 非负约定——M2-2a) +fn hit_ev_append(ev_id: int, d: int, s1: int, s2: int, flags: int) { + hit_grow_ev(g_hit_ev_count + 1); + slot : ., mut = g_hit_ev_count; + g_hit_ev_count = slot + 1; + hit_w32(g_hit_ev_stream, slot * HIT_ES_REC + HIT_ES_OFF_ID, ev_id); + hit_w32(g_hit_ev_stream, slot * HIT_ES_REC + HIT_ES_OFF_DST, d); + hit_w32(g_hit_ev_stream, slot * HIT_ES_REC + HIT_ES_OFF_S1, s1); + hit_w32(g_hit_ev_stream, slot * HIT_ES_REC + HIT_ES_OFF_S2, s2); + hit_w32(g_hit_ev_stream, slot * HIT_ES_REC + HIT_ES_OFF_FLAGS, flags); } + +// 事件流访问器(dump/编码器消费;i 为流内下标) +fn hit_ev_id(i: int) -> int { return hit_r32(g_hit_ev_stream, i * HIT_ES_REC + HIT_ES_OFF_ID); } +fn hit_ev_dst(i: int) -> int { return hit_r32(g_hit_ev_stream, i * HIT_ES_REC + HIT_ES_OFF_DST); } +fn hit_ev_s1(i: int) -> int { return hit_r32(g_hit_ev_stream, i * HIT_ES_REC + HIT_ES_OFF_S1); } +fn hit_ev_s2(i: int) -> int { return hit_r32(g_hit_ev_stream, i * HIT_ES_REC + HIT_ES_OFF_S2); } +fn hit_ev_flags(i: int) -> int { return hit_r32(g_hit_ev_stream, i * HIT_ES_REC + HIT_ES_OFF_FLAGS); } +fn hit_ev_total() -> int { return g_hit_ev_count; } + +// 常量池:按值去重 intern;返回槽号 +fn hit_pool_intern(v: int) -> int { + k : ., mut = 0; + loop { + if k >= g_hit_pool_count { break; } + if r64(g_hit_pool, k * 8) == v { return k; } + k = k + 1; } + hit_grow_pool(g_hit_pool_count + 1); + pk : ., mut = g_hit_pool_count; + g_hit_pool_count = pk + 1; + hit_pool_w64(g_hit_pool, pk * 8, v); + return pk; } + +fn hit_pool_total() -> int { return g_hit_pool_count; } +fn hit_pool_val(k: int) -> int { return r64(g_hit_pool, k * 8); } + +// 池引用 patch 记录(编码器在发射池值 mov 时调用;elf.cr 于 rodata 定稿后回填) +fn hit_pool_patch_add(pos: int, k: int) { + hit_grow_pool_patch(g_hit_pool_patch_count + 1); + slot : ., mut = g_hit_pool_patch_count; + g_hit_pool_patch_count = slot + 1; + w64(g_hit_pool_patch, slot * 16, pos); + w64(g_hit_pool_patch, slot * 16 + 8, k); } + +// ════════════════════════════════════════════════════════════════ +// 降低规则(IR 直线子集 → 事件流) +// ════════════════════════════════════════════════════════════════ + +// 降低单条 IR 指令并追加事件。Returns 0 = 成功(可能无事件 = 旧路径直通); +// 1 = 超子集 op(错误已打印)——调用方中止(exit 1)。 +fn hit_lower_instr(j: int) -> int { + op := iri_op(j); + d := iri_dest(j); + s1 := iri_s1(j); + s2 := iri_s2(j); + s3 := iri_s3(j); + ti := iri_tk(j); + if op == IR_CONST { + // 字符串常量走旧路径(g2_str_off/rodataref 字符串机制); + // 其余常量 → 池槽 load 事件(8B 槽值发射时写 rodata) + if ti == TI_STR { return 0; } + if d < 0 { return 0; } + k := hit_pool_intern(s1); + hit_ev_append(HIT_EV_LOAD, d, k, 0, HIT_ES_S1_POOL); // load 事件:addr = 池槽(s2 = 0 未用) + return 0; } + if op == IR_BINARY { + if ti == TI_DEX { return 0; } // binary64 运算走旧 SSE 路径(M1 不移表) + if s3 == OP_SUB { + if d < 0 || s1 < 0 || s2 < 0 { return 0; } + hit_ev_append(HIT_EV_SUB, d, s1, s2, 0); + return 0; } + if s3 == OP_ADD { + if d < 0 || s1 < 0 || s2 < 0 { return 0; } + // dest-fresh 前提(M2-2c 防御):E1 先把中间值 (0−b) 写入 d 槽; + // d == a 时 E2 读 src1(a) 读到的已是中间值 → a − (0−b) 语义崩。 + // 落 0 事件 = 旧路径(旧路径编码器逐事件先读后写,无此前提)。 + if d == s1 { return 0; } + k0 := hit_pool_intern(0); // 合成 0(最小核无 const 事件) + // sub 反减 add(a,b) := sub(a, sub(0,b))——中间值驻 d: + // E1: d ← 0 − b;E2: d ← a − d(编码器先读后写,读先于写) + hit_ev_append(HIT_EV_SUB, d, k0, s2, HIT_ES_S1_POOL); + hit_ev_append(HIT_EV_SUB, d, s1, d, 0); + return 0; } + // 其余子操作(mul/div/mod/移位/位与/或/…)= 超出 M1 直线子集 + print("error: HIT lower: instr "); print_i(j); + print(" op="); print_i(op); print(" sub="); print_i(s3); + println(" needs more events (M1 straight-line subset: const/load/store/add/sub)"); + return 1; } + if op == IR_LOAD { + if d >= 0 && s1 >= 0 { + // 槽读 → 槽写(全局/形态不符由编码器预检 → 整条落旧路径); + // s2 = 0 未用(勿存 -1:hit_w32 非负约定,M2-2a) + hit_ev_append(HIT_EV_LOAD, d, s1, 0, 0); + } + return 0; } + if op == IR_STORE { + if s1 >= 0 && s2 >= 0 { + // 槽写:addr = s1 槽、val = s2;dst = 0 未用(M2-2a) + hit_ev_append(HIT_EV_STORE, 0, s1, s2, 0); + } + return 0; } + return 0; } + +// 全程序降低(corearch 表模式下、发射前调用)。 +// M1 直线子集契约 = 入口函数 main 的指令流;.ccr 随带的标准库辅助函数 +// (fmt/io 闭包,src/stdlib/_import.cr 无条件引入)含比较/分支/除法等超子集 +// 运算——超出 M1 语义范围,记 0 事件(发射时落旧路径,混合模式)。main 内 +// 超子集 op(mod/移位/…)→ 'needs more events' 报错(exit 1)。 +// map 全量清零(未降低指令 0 事件);仅降低 main。 +// Returns 0 = 成功;1 = 超子集 op(错误已打印,调用方 exit 1)。 +fn hit_lower_program() -> int { + hit_lower_reset(); + hit_ev_map_init(g_ir_instr_count); + mi : ., mut = 0; + loop { + if mi >= g_ir_instr_count { break; } + hit_ev_map_set(mi, 0, 0); + mi = mi + 1; } + entry : ., mut = -1; + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + if str_eq(istr_get(r64(g_ir_func_name_idx, fi * 8)), "main") != 0 { entry = fi; break; } + fi = fi + 1; } + if entry < 0 { return 0; } // 无 main = M1 载体外(不降低,全旧路径) + ist := r64(g_ir_func_instr_start, entry * 8); + ic := r64(g_ir_func_instr_count, entry * 8); + ii : ., mut = 0; + loop { + if ii >= ic { break; } + j := ist + ii; + base : ., mut = g_hit_ev_count; + if hit_lower_instr(j) != 0 { return 1; } + hit_ev_map_set(j, base, g_hit_ev_count - base); + ii = ii + 1; } + return 0; } + +// ════════════════════════════════════════════════════════════════ +// ════════════════════════════════════════════════════════════════ +// 事件流注入测试通道(M2a Task 2)——hit_lower_program 的注入替身 +// ════════════════════════════════════════════════════════════════ +// corearch 隐藏 flag --hit-events-file (测试专用;真实构建路径不启用): +// 文本事件流文件(--dump-events 输出的逆格式)→ 构造 g_hit_ev_stream/g_hit_pool +// + 每指令事件映射,跳过降低直接 emit——模板对照与降低器解耦(Task 3/6 的 +// 对照与 dump 断言全部经此通道,无需降低器先支持)。 +// +// 文件格式('#' 注释行可夹;行序 = 事件流序 = 指令序): +// ev [dst=N] [s1=[pool]N] [s2=[pool]N] 每事件一行(name = 表内事件名) +// pool ... 池值行(按序入槽——可多行) +// 事件操作数域缺省 = 取所附着 IR 指令的同名操作数(dst→iri_dest、s1→iri_s1、 +// s2→iri_s2;指令域为负 → 0)——测试作者无需知道 .ccr 全局变量号;需要改写 +// 的域显式给(jump 的 s1 = 目标事件序;池引用写 s1=poolN)。 +// +// 附着规则(= M1 降低的指令↔事件对应;未列事件指令 = 0 事件 → 旧路径—— +// 逐字节对照面因此可含 const/分支等任意旧路径指令): +// 事件 1 sub ← IR_BINARY(OP_SUB) 事件 4 store ← IR_STORE +// 事件 3 load ← IR_LOAD(var 操作数)/ IR_CONST(非串、pool 操作数——池载入) +// 事件 5 jump ← IR_JUMP(s1 须显式 = 目标事件序) +// 事件 11 cst ← IR_CONST(非串、imm 直载 const 事件——cst 夹具表测试载体, +// imm 值 = 事件 s1 原值;核心表未声明 id 11 时行解析即拒 'unknown event') +// 其余事件(6-9 等)→ 无指令期望 → 行无法附着 → 拒绝('no instruction +// expects'——branch/call/ret/extern Task 3 前保持拒绝的大声面)。 +// 解析/附着错误全部 return 1(corearch exit 1)。本文件与注入表均测试专用。 +// +// 注入事件行暂存(解析期不知目标指令——两遍:先解析行,后走指令附着): +// 每行 20B = {id i32, flags i32, dst i32, s1 i32, s2 i32}(flags 位见下) +g_hit_inj : string, mut; g_hit_inj_count : int, mut; g_hit_inj_cap : int, mut; + +HIT_INJ_HAS_DST : int = 1; // dst 域显式(否则 = 指令 dest) +HIT_INJ_HAS_S1 : int = 2; // s1 域显式 +HIT_INJ_HAS_S2 : int = 4; // s2 域显式 +HIT_INJ_S1_POOL : int = 8; // s1 = 池槽引用 +HIT_INJ_S2_POOL : int = 16; // s2 = 池槽引用 + +fn hit_grow_inj(needed: int) { + if needed < g_hit_inj_cap { return; } + nc : ., mut = g_hit_inj_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 4; } + nb := alloc(nc * 20); + hit_copy_bytes(g_hit_inj, g_hit_inj_cap * 20, nb); + g_hit_inj = nb; + g_hit_inj_cap = nc; } + +fn hit_inj_line_id(i: int) -> int { return hit_r32(g_hit_inj, i * 20 + 0); } +fn hit_inj_line_flags(i: int) -> int { return hit_r32(g_hit_inj, i * 20 + 4); } +fn hit_inj_line_dst(i: int) -> int { return hit_r32(g_hit_inj, i * 20 + 8); } +fn hit_inj_line_s1(i: int) -> int { return hit_r32(g_hit_inj, i * 20 + 12); } +fn hit_inj_line_s2(i: int) -> int { return hit_r32(g_hit_inj, i * 20 + 16); } + +// 事件名 → 事件槽(反向查名——dump 打印名、注入文件写名;未知 → -1) +fn hit_inj_event_by_name(name: string) -> int { + ei : ., mut = 0; + loop { + if ei >= g_hit_event_count { break; } + if str_eq(hit_event_name(ei), name) != 0 { return ei; } + ei = ei + 1; } + return -1; } + +// 注入行附着期望:指令 (op, subop, ti) 是否期望事件行 (l_id, l_pool)。 +// 未列事件(6-9 等)= 无期望 → 行不得附着(拒绝面)。 +fn hit_inj_line_expected(op: int, subop: int, ti: int, l_id: int, l_pool: int) -> int { + if l_id == HIT_EV_JUMP { if op == IR_JUMP && l_pool == 0 { return 1; } return 0; } + if l_id == HIT_EV_SUB { + if op == IR_BINARY && subop == OP_SUB { return 1; } + return 0; } + if l_id == HIT_EV_STORE { if op == IR_STORE { return 1; } return 0; } + if l_id == HIT_EV_LOAD { + if op == IR_LOAD && l_pool == 0 { return 1; } + if op == IR_CONST && ti != TI_STR && l_pool == 1 { return 1; } + return 0; } + if l_id == 11 { // cst 夹具事件(imm 直载 const)——注入测试载体,见上注 + if op == IR_CONST && ti != TI_STR && l_pool == 0 { return 1; } + return 0; } + return 0; } + +// 行分派:pool 值行 / ev 事件行 / 坏行。0 = 行已处理;1 = 错误(已打印)。 +fn hit_inj_parse_line(line: string) -> int { + llen := str_len(line); + if llen >= 4 && load8(line, 0) == 112 && load8(line, 1) == 111 && + load8(line, 2) == 111 && load8(line, 3) == 108 && + (llen == 4 || load8(line, 4) == 32) { + return hit_inj_parse_pool_line(line); } + if llen >= 2 && load8(line, 0) == 101 && load8(line, 1) == 118 && + (llen == 2 || load8(line, 2) == 32) { + return hit_inj_parse_ev_line(line); } + print("error: HIT events: bad event line: "); println(line); + return 1; } + +// pool 行:空格分隔值按序入池槽(可多行)。0 = 成功;1 = 错误(已打印)。 +fn hit_inj_parse_pool_line(line: string) -> int { + llen := str_len(line); + vp : ., mut = 4; + loop { + loop { + if vp >= llen { break; } + if load8(line, vp) == 32 { vp = vp + 1; } else { break; } } + if vp >= llen { break; } + vv := hit_inj_parse_int(line, vp); + if vv < 0 { + print("error: HIT events: bad pool value at line: "); + println(line); + return 1; } + hit_grow_pool(g_hit_pool_count + 1); + hit_pool_w64(g_hit_pool, g_hit_pool_count * 8, vv); + g_hit_pool_count = g_hit_pool_count + 1; + loop { // 越过该值全部数字('-' 含首字符——已在 parse 校验) + if vp >= llen { break; } + c := load8(line, vp); + if c >= 48 && c <= 57 || c == 45 { vp = vp + 1; } else { break; } } } + return 0; } + +// ev 行:`ev [dst=N] [s1=[pool]N] [s2=[pool]N]`——操作数域缺省 = 附着时 +// 取指令同名操作数(附着规则见 hit_inj_line_expected)。行暂存 20B 待遍 2 附着。 +// 0 = 成功;1 = 错误(已打印)。 +fn hit_inj_parse_ev_line(line: string) -> int { + llen := str_len(line); + np : ., mut = 2; + loop { + if np >= llen { break; } + if load8(line, np) == 32 { np = np + 1; } else { break; } } + ne : ., mut = np; + loop { + if ne >= llen { break; } + if load8(line, ne) == 32 { break; } + ne = ne + 1; } + if ne <= np { + print("error: HIT events: bad event line: "); println(line); + return 1; } + es := hit_inj_event_by_name(str_sub(line, np, ne - np)); + if es < 0 { + print("error: HIT events: unknown event '"); + print(str_sub(line, np, ne - np)); println("'"); + return 1; } + ev_id := hit_r32(g_hit_events, es * HIT_EVENT_REC + HIT_EV_OFF_ID); + fl : ., mut = 0; + dv : ., mut = 0; + s1v : ., mut = 0; + s2v : ., mut = 0; + dpos := hit_inj_field_of(line, ne, "dst", 0); + if dpos == -2 { + print("error: HIT events: bad dst at line: "); println(line); + return 1; } + if dpos >= 0 { + dv = hit_inj_parse_int(line, dpos); + if dv < 0 { + print("error: HIT events: bad dst at line: "); println(line); + return 1; } + fl = fl + HIT_INJ_HAS_DST; } + s1pos := hit_inj_field_of(line, ne, "s1", 1); + if s1pos == -2 { + print("error: HIT events: bad s1 at line: "); println(line); + return 1; } + if s1pos >= 0 { + if load8(line, s1pos) == 112 { // poolN + s1v = str_int(str_sub(line, s1pos + 4, str_len(line) - s1pos - 4)); + fl = fl + HIT_INJ_HAS_S1 + HIT_INJ_S1_POOL; } + else { + s1v = hit_inj_parse_int(line, s1pos); + if s1v < 0 { + print("error: HIT events: bad s1 at line: "); println(line); + return 1; } + fl = fl + HIT_INJ_HAS_S1; } } + s2pos := hit_inj_field_of(line, ne, "s2", 1); + if s2pos == -2 { + print("error: HIT events: bad s2 at line: "); println(line); + return 1; } + if s2pos >= 0 { + if load8(line, s2pos) == 112 { + s2v = str_int(str_sub(line, s2pos + 4, str_len(line) - s2pos - 4)); + fl = fl + HIT_INJ_HAS_S2 + HIT_INJ_S2_POOL; } + else { + s2v = hit_inj_parse_int(line, s2pos); + if s2v < 0 { + print("error: HIT events: bad s2 at line: "); println(line); + return 1; } + fl = fl + HIT_INJ_HAS_S2; } } + hit_grow_inj(g_hit_inj_count + 1); + hit_w32(g_hit_inj, g_hit_inj_count * 20 + 0, ev_id); + hit_w32(g_hit_inj, g_hit_inj_count * 20 + 4, fl); + hit_w32(g_hit_inj, g_hit_inj_count * 20 + 8, dv); + hit_w32(g_hit_inj, g_hit_inj_count * 20 + 12, s1v); + hit_w32(g_hit_inj, g_hit_inj_count * 20 + 16, s2v); + g_hit_inj_count = g_hit_inj_count + 1; + return 0; } + +// 注入文件解析 + 指令附着(入口函数 = main——与降低同定位)。0 = 成功;1 = 错 +//(错误已打印——corearch 侧 exit 1)。 +fn hit_inject_events(path: string) -> int { + content := read_file(path); + if str_len(content) == 0 { + print("error: cannot open HIT events file: "); println(path); + return 1; } + hit_lower_reset(); + g_hit_inj_count = 0; + // ── 遍 1:逐行解析(pool 值直入池;ev 行暂存待附着)── + slen := str_len(content); + ln : ., mut = 0; + loop { + if ln >= slen { break; } + s : ., mut = ln; + loop { + if s >= slen { break; } + c := load8(content, s); + if c == 32 || c == 9 { s = s + 1; } else { break; } } + e : ., mut = s; + loop { + if e >= slen { break; } + if load8(content, e) == 10 { break; } + e = e + 1; } + if s < e && load8(content, s) != 35 { // 非注释非空行 + line := str_sub(content, s, e - s); + if hit_inj_parse_line(line) != 0 { return 1; } + } + ln = e + 1; } + // ── 遍 2:入口函数(main)指令序附着(行序 = 指令序——未匹配行留待下条)── + entry : ., mut = -1; + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + if str_eq(istr_get(r64(g_ir_func_name_idx, fi * 8)), "main") != 0 { entry = fi; break; } + fi = fi + 1; } + if entry < 0 { + println("error: HIT events: no main function to attach events to"); + return 1; } + hit_ev_map_init(g_ir_instr_count); + mi : ., mut = 0; + loop { + if mi >= g_ir_instr_count { break; } + hit_ev_map_set(mi, 0, 0); + mi = mi + 1; } + ist := r64(g_ir_func_instr_start, entry * 8); + ic := r64(g_ir_func_instr_count, entry * 8); + cur : ., mut = 0; // 下一条未附着行 + ii : ., mut = 0; + loop { + if ii >= ic { break; } + j := ist + ii; + if cur < g_hit_inj_count { + op := iri_op(j); + ti := iri_tk(j); + subop : ., mut = -1; + if op == IR_BINARY { subop = iri_s3(j); } + l_id := hit_inj_line_id(cur); + l_fl := hit_inj_line_flags(cur); + l_pool : ., mut = 0; + if l_fl / 8 % 2 == 1 { l_pool = 1; } + if hit_inj_line_expected(op, subop, ti, l_id, l_pool) == 1 { + if l_id == HIT_EV_JUMP && l_fl / 2 % 2 == 0 { + println("error: HIT events: jump event needs explicit s1 (target event ordinal)"); + return 1; } + // 附着:事件记录 = 行值(缺省取指令同名操作数) + d : ., mut = hit_inj_line_dst(cur); + if l_fl % 2 == 0 { d = iri_dest(j); if d < 0 { d = 0; } } + s1 : ., mut = hit_inj_line_s1(cur); + if l_fl / 2 % 2 == 0 { s1 = iri_s1(j); if s1 < 0 { s1 = 0; } } + s2 : ., mut = hit_inj_line_s2(cur); + if l_fl / 4 % 2 == 0 { s2 = iri_s2(j); if s2 < 0 { s2 = 0; } } + evfl : ., mut = 0; + if l_fl / 8 % 2 == 1 { evfl = evfl + HIT_ES_S1_POOL; } + if l_fl / 16 % 2 == 1 { evfl = evfl + HIT_ES_S2_POOL; } + hit_ev_append(l_id, d, s1, s2, evfl); + hit_ev_map_set(j, g_hit_ev_count - 1, 1); + cur = cur + 1; } } + ii = ii + 1; } + // ── 遍 3:尾校验(未附着行 = 作者错——事件不支持/与指令序不符)── + if cur < g_hit_inj_count { + print("error: HIT events: no instruction expects event "); + print(hit_event_name(hit_event_lookup(hit_inj_line_id(cur)))); + print(" (line "); print_i(cur + 1); println(")"); + return 1; } + if g_hit_inj_count == 0 { + println("error: HIT events: file has no ev lines"); + return 1; } + // jump 事件目标 = 事件序——范围校验(0 ≤ 目标 < 总事件数) + ji : ., mut = 0; + loop { + if ji >= g_hit_ev_count { break; } + if hit_ev_id(ji) == HIT_EV_JUMP { + t := hit_ev_s1(ji); + if t < 0 || t >= g_hit_ev_count { + print("error: HIT events: jump target event ordinal "); + print_i(t); print(" out of range (0.."); + print_i(g_hit_ev_count - 1); println(")"); + return 1; } } + ji = ji + 1; } + return 0; } + +// 行内数值 token 解析(val 起为值首;返回 值;-1 = 非数) +fn hit_inj_parse_int(content: string, val: int) -> int { + slen := str_len(content); + p : ., mut = val; + if p < slen && load8(content, p) == 45 { p = p + 1; } // '-'(池负值) + if p >= slen { return -1; } + if load8(content, p) < 48 || load8(content, p) > 57 { return -1; } + return str_int(str_sub(content, val, str_len(content) - val)); } + +// k=v 字段定位:从 fpos 起找 token "k=";命中返回 v 起偏移(v = 数或 "pool")。 +// 返回:≥0 = 值起;-1 = 键未出现;-2 = 键出现但值非数/非 pool(坏值——调用方报错)。 +fn hit_inj_field_of(content: string, fpos: int, key: string, pool_ok: int) -> int { + slen := str_len(content); + klen := str_len(key); + p : ., mut = fpos; + loop { + if p + klen >= slen { return -1; } + if load8(content, p + klen) == 61 && (p == fpos || load8(content, p - 1) == 32) { + m : ., mut = 0; + ok : ., mut = 1; + loop { + if m >= klen { break; } + if load8(content, p + m) != load8(key, m) { ok = 0; break; } + m = m + 1; } + if ok == 1 { + v0 : ., mut = p + klen + 1; + if v0 >= slen { return -2; } + c := load8(content, v0); + if pool_ok == 1 && c == 112 && v0 + 4 <= slen && + load8(content, v0 + 1) == 111 && load8(content, v0 + 2) == 111 && + load8(content, v0 + 3) == 108 { return v0; } + if c >= 48 && c <= 57 || c == 45 { return v0; } + return -2; } + } + p = p + 1; } + return -1; } diff --git a/src/arch/linux/ld/_import.cr b/src/arch/linux/ld/_import.cr deleted file mode 100644 index 3e95a101..00000000 --- a/src/arch/linux/ld/_import.cr +++ /dev/null @@ -1,15 +0,0 @@ -// _import.cr — imports for the corearch backend project -import cli -import fmt -import io -import toml -import ast -import globals -import dyn_arr -import monomorph -import ccr_io -import sizes -import instr -import resolve -import elf -import ld diff --git a/src/arch/x86_64/core-x86.toml b/src/arch/x86_64/core-x86.toml new file mode 100644 index 00000000..c052f4ef --- /dev/null +++ b/src/arch/x86_64/core-x86.toml @@ -0,0 +1,213 @@ +# === core-x86.toml === +# 硬件接口表(HIT):corearch 表驱动编码器的 x86-64 档(M1 Task 1 建立, +# M2a Task 1 = schema v2 迁移)。 +# schema v2(docs/superpowers/specs/2026-09-07-hit-m2-design.md §3): +# - 步模板字段完整化:prefix/REX(rex_w/r/x/b 逐位)/opcode/modrm 角色与 +# /digit/SIB/disp/imm/rel/cond——模板字节 = prefix → REX → opcode 三段 +# 拼接序,legacy 视区(OP0/OP1)与之逐字节等价(M1 消费面不变) +# - [[event.proj.step]] 步小节表达多步序列;事件可多 proj 形态 +# - [runtime.x86] 运行时条目小节(spec §6 最小描述) +# M1 事件 1-4(sub/nand/load/store)迁为结构性 REX 字段后,legacy 字节视区 +# 与 M1 模板逐字节相同:4D 29 / 4D 21 / 4C 8B / 4C 89(walker 测试断言)。 +# 事件 5-9 = 控制/调用/系统核:jump/branch/call/ret/extern 真实 x86 投影 +# 声明(发射器消费 = M2b 起,先以「加载通过」落地,见 plan 2026-09-07-hit-m2)。 + +[table] +name = "core-x86-min" +events = 9 # 运行时核:1-4 数据 + 5-9 控制/调用/系统(M2a) + +# ════════════════════════════════════════════════════════════════ +# 事件 1-4:数据面(M1 直通事件,v2 = 结构性字段;M1 字节视区不变) +# ════════════════════════════════════════════════════════════════ + +# ── 1 sub:sub r/m64, r64(REX.W+R+B;M1 模板 0x4D 0x29 等价拆解)── +[[event]] +id = 1 +name = "sub" +arith = "sub" # 语义锚:规约层引用(加载不消费) +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +rex_w = 1 +rex_r = 1 +rex_b = 1 +opcode = [0x29] +modrm_reg_role = "src2" +modrm_rm_role = "dst" +rm_mode = 0 + +# ── 2 nand:and r/m64, r64(REX.W+R+B;M1 模板 0x4D 0x21——nand 单步占位, +# M1 语义不变:and 结果 + 降低层 neg 合成)── +[[event]] +id = 2 +name = "nand" +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +rex_w = 1 +rex_r = 1 +rex_b = 1 +opcode = [0x21] +modrm_reg_role = "src2" +modrm_rm_role = "dst" +rm_mode = 0 + +# ── 3 load:mov r64, r/m64(REX.W+R;M1 模板 0x4C 0x8B——rm = rbp+disp32 +# 槽寻址;rm_mode 1 + 无 base_role = 缺省 rbp;disp 由槽回填生成)── +[[event]] +id = 3 +name = "load" +inputs = 1 +outputs = 1 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +rex_w = 1 +rex_r = 1 +opcode = [0x8B] +modrm_reg_role = "dst" +modrm_rm_role = "addr" +rm_mode = 1 + +# ── 4 store:mov r/m64, r64(REX.W+R;M1 模板 0x4C 0x89——写 [rbp+disp32] +# ← val)── +[[event]] +id = 4 +name = "store" +inputs = 2 +outputs = 0 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +rex_w = 1 +rex_r = 1 +opcode = [0x89] +modrm_reg_role = "val" +modrm_rm_role = "addr" +rm_mode = 1 + +# ════════════════════════════════════════════════════════════════ +# 事件 5-9:控制/调用/系统核(v2 语法;语义绑定 = Task 4 降低对照定稿) +# ════════════════════════════════════════════════════════════════ + +# ── 5 jump:E9 rel32(无条件转移;rel kind = event → 事件流位置回填表)── +[[event]] +id = 5 +name = "jump" +inputs = 1 # 目标 = 事件流位置(IR_JUMP s1 → 标签映射) +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0xE9] +rel_role = "src1" +rel_kind = "event" +rel_size = 32 + +# ── 6 branch:cmp + jne 两步序列 ── +# 第一步 cmp r10(=条件载入), 0 → ZF=(s1==0):83 /7 ib 带 REX.W+B(rm = r10 +# 需 B 位;/7 digit 组不得带 REX.R)+ imm 字面量 0;条件源槽 cond_role = src1 +#(判定语义 Task 4 对照定稿)。第二步 jne rel32(ZF=0 → 条件≠0 → 真目标 s2); +# 假目标 = 事件流下一条(相邻跳过选型 = 降低层 Task 4 定稿——此处仅模板声明)。 +[[event]] +id = 6 +name = "branch" +inputs = 3 # 条件 s1(≠0 真)、真目标 s2、假目标 s3 +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +rex_w = 1 # REX.W+B(rm = r10 需 B 位) +rex_b = 1 +opcode = [0x83] # 83 /7 ib = cmp r/m64, imm8 +modrm_reg_digit = 7 +modrm_rm_role = "dst" +rm_mode = 0 +imm_size = 1 +imm_role = "lit" +imm_lit = 0 +cond_role = "src1" # 条件源槽(判定语义 Task 4 对照定稿) + +[[event.proj.step]] +opcode = [0x0F, 0x85] # jne rel32(ZF=0 → 真目标) +rel_role = "src2" +rel_kind = "event" +rel_size = 32 + +# ── 7 call:E8 rel32(rel kind = func → 函数体事件流位置回填表)── +[[event]] +id = 7 +name = "call" +inputs = 1 # 目标 = 函数索引(.ccr SYM func 行序) +outputs = 0 # 值返回 = 调用点序列槽协议(R2/R7 算法保留代码,Task 4) +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0xE8] +rel_role = "src1" +rel_kind = "func" +rel_size = 32 + +# ── 8 ret:C3(调用栈机制 = [runtime.x86] 条目;返回序列 = R7 算法保留代码)── +[[event]] +id = 8 +name = "ret" +inputs = 0 +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0xC3] + +# ── 9 extern:E8 rel32 + 外部重定位标记(rel kind = extern → ext_rel 同族 +# 回填)── +[[event]] +id = 9 +name = "extern" +inputs = 1 # 外部符号 +outputs = 0 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0xE8] +rel_role = "src1" +rel_kind = "extern" +rel_size = 32 + +# ════════════════════════════════════════════════════════════════ +# [runtime.x86]:运行时条目最小描述(spec §6;M2a Task 1 声明,消费方 = +# M2b+)。现行约定 = SysV 寄存器传参(rdi/rsi/rdx/rcx/r8/r9——elf.cr R2 +# 参数拷贝:callee 帧槽保存,与分配器一致);save 集 = callee-saved +# rbx/r12-15;extern = call rel32 + ext_rel 符号解析。 +# ════════════════════════════════════════════════════════════════ +[runtime.x86] +call_mechanism = "stack" +call_ret_addr = "pushed" +call_stack_grow = "down" +param_mode = "regs" +param_regs = "rdi rsi rdx rcx r8 r9" +save_regs = "rbx r12 r13 r14 r15" +extern_entry = "call_rel32" +extern_resolve = "ext_rel" diff --git a/src/arch/x86_64/frame.cr b/src/arch/x86_64/frame.cr new file mode 100644 index 00000000..aff540a3 --- /dev/null +++ b/src/arch/x86_64/frame.cr @@ -0,0 +1,289 @@ +// ══════════════════════════════════════════════════════════════ +// src/arch/x86_64/frame.cr —— 架构轴:x86-64 函数帧 +// +// x86 实例化波 1 Task 3 抽取(帧公式双源合流——H2 波 1 最大风险之一): +// - 帧尺寸/帧相关字节核算自 src/format/elf/elf.cr(Phase 2 dry-run 核算 + +// Phase 3 sub rsp 立即数)与 src/arch/x86_64/instr.cr(g2_tag_off / +// mw_frame_size——H3 归属裁决:帧布局面归 frame,tag2l 依赖 frame)迁入; +// - 序言/尾声/函数尾附加块(jo 慢路径块 + 2L 操作数块)自 elf.cr Phase 3 +// 抽为本文件 pf_prologue/pf_epilogue(发射序逐字节 verbatim——波 1 判据 +// = stage 链 byte-identical)。 +// +// **H2 帧公式单源**(三处入口,各自唯一): +// ① 帧总字节 = pf_frame_size(vc) —— Phase 2 dry-run 与 Phase 3 +// sub rsp 立即数两处**调用同一函数**(此前双源 = 唯一风险面; +// test_mw_task1.py 的 sub rsp 立即数断言 = 帧专项判据); +// ② 帧字节总数 = pf_frame_overhead(ss) —— dry-run 只核算,须与 ③ 的 +// 实际发射逐字节一致(尺寸原语 sz_* 由 sizes.cr 单源); +// ③ 实际发射 = pf_prologue/pf_epilogue —— 帧相关段的唯一发射器 +// (elf.cr 零残留帧尺寸计算/帧发射序)。 +// tag 区(int 多字 M1):tag 字节位于槽区之下,偏移 -(vc*8+1+k)——公式与 +// 识别规则见 tag2l.cr mw_setup_tags 注释块(波 1 Task 4 起 tag 表 owner 在 +// tag2l.cr;本文件 g2_tag_off = 读侧 seam);tag 数为 0 时帧公式与旧布局 +// 逐字节一致(快路径零变化硬约束)。 +// +// 抽取纪律(H1):cp/pos/buf 三参数显式传递(无环境态假设);调用点 = elf.cr +// Phase 2 dry-run 与 Phase 3 函数发射序。 +// +// Depends on: x86_64/instr.cr(e2_*/emit_rex/emit_modrm/get_reg_for_var/ +// w8/w32/r64)、x86_64/tag2l.cr(e2_mw_slow_block/ +// e2_mw_opnd_block——pf_epilogue 函数尾块;同轴双向引用见 +// tag2l.cr 头注:本文件反向提供 g2_tag_off 给 tag2l)、 +// x86_64/sizes.cr(sz_* 编码尺寸单源)、共享全局(g_opt_level/ +// g_current_func_var_start/g_x86_sub_rsp_pos/g_x86_mw_* 站点表)。 +// 跨文件引用经 concat 扁平单元(build_selfhost_native.py arch_x86_64_files) +// / module.cr 三轴回退链(组合根 _import.cr `import frame`)解析。 +// ══════════════════════════════════════════════════════════════ + +// ── tag 表读取(H3 自 instr.cr 迁入;表由 tag2l.cr mw_setup_tags 按函数填充 +// ——波 1 Task 4 起 tag 表 owner 在 tag2l.cr)── +fn g2_tag_off(v: int) -> int { + // var v(当前函数内)的 tag 字节偏移(相对 rbp,恒负);-1 = 非 tagged。 + // 仅当前函数内有效(表由 mw_setup_tags 按函数填充)。 + if v < 0 { return -1; } + lv := v - g_current_func_var_start; + if lv < 0 { return -1; } + if str_len(g_x86_mw_tag_off) <= lv * 8 { return -1; } + return r64(g_x86_mw_tag_off, lv * 8); +} + +// ── 帧总字节(H2 ①:Phase 2 dry-run 与 Phase 3 共用)── +fn pf_frame_size(vc: int) -> int { + // 帧总字节 = var 槽 vc*8 + tag 区(g_x86_mw_tag_count 字节)再按现 SysV + // 规则补 16 对齐:opt≥1(6 pushes)帧 ≡ 8 (mod 16);opt<1(1 push)≡ 0。 + // tag 数为 0 时对任意 vc 的结果与旧公式逐字节一致(快路径零变化)。 + sz : ., mut = vc * 8 + g_x86_mw_tag_count; + r := sz % 16; + if g_opt_level >= 1 { + if r != 8 { + pad := 8 - r; + if pad < 0 { pad = pad + 16; } + sz = sz + pad; + } + } else { + if r != 0 { sz = sz + (16 - r); } + } + return sz; +} + +// ── Phase 2 dry-run:帧相关字节总数(序言 + 尾声;不含形参落槽与函数体)── +// H2 ②:dry-run 核算(本函数)与 H2 ③ 实际发射(pf_prologue/pf_epilogue)必须 +// 一致——尺寸原语 sz_* 单源 sizes.cr;本函数 = 帧的核算侧唯一入口。 +// callee-saved 计入 = sz_callee_saved_push/pop(波 1 Task 4 自硬编码 18/9 提出 +// ——数值与提取前逐字节相同,波 1 零变化硬约束)。 +// ⚠️ 已知预存偏差(波 1 Task 4 评审登记,波 2 清):opt≥1 时本函数多计一个 +// sz_callee_saved_pop(9B/函数——push/pop 对已在 sz_callee_saved_push+pop 内, +// 见下方函数尾再计点)。Phase 2 total_code 为临时值(Phase 3 由实际 cp 重算 +// rodata/bss/函数偏移——elf.cr:1380/1422/1088),**无发射字节影响**、偏保守。 +fn pf_frame_overhead(ss: int) -> int { + sz : ., mut = sz_push_rbp() + sz_mov_rbp_rsp(); + if g_opt_level >= 1 { sz = sz + sz_callee_saved_push() + sz_callee_saved_pop(); } // push rbx,r12-r15(9) + pop r15-r12,rbx(9) + sz = sz + sz_sub_rsp(ss); + sz = sz + sz_add_rsp(ss) + sz_pop_rbp() + sz_ret(); + if g_opt_level >= 1 { sz = sz + sz_callee_saved_pop(); } // pop r15,r14,r13,r12,rbx + return sz; +} + +// ── Phase 3 序言(帧 setup + 形参落槽 + 寄存器参数装载)── +// 发射序(opt≥1 六寄存器保存 → push rbp/mov rbp,rsp → sub rsp 占位(立即数 +// 由 pf_epilogue 回填实际帧字节)→ 形参落槽(XMM/int/栈参 + tag 卫生③参数行 +// 清 tag)→ 寄存器分配参数装载);返回新 cp。g_x86_sub_rsp_pos = sub rsp +// 立即数字段位置(供 pf_epilogue 回填);帧尺寸 ss 显式传入(= pf_frame_size +// 结果——H2 单源,本函数不再自行计算帧尺寸)。 +fn pf_prologue(vs: int, pc: int, ss: int, buf: string, pos: int) -> int { + cp : ., mut = pos; + // Save callee-saved registers (pushed before rbp setup → at [rbp+8..48]) + if g_opt_level >= 1 { + w8(buf, cp, 83); cp = cp + 1; // push rbx + w8(buf, cp, 65); w8(buf, cp+1, 84); cp = cp + 2; // push r12 + w8(buf, cp, 65); w8(buf, cp+1, 85); cp = cp + 2; // push r13 + w8(buf, cp, 65); w8(buf, cp+1, 86); cp = cp + 2; // push r14 + w8(buf, cp, 65); w8(buf, cp+1, 87); cp = cp + 2; // push r15 + } + // frame + w8(buf, cp, 85); cp = cp + 1; // push rbp + w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 229); cp = cp + 3; // mov rbp, rsp + g_x86_sub_rsp_pos = cp; + if ss > 0 { + if ss > 127 { + w8(buf, cp, 72); w8(buf, cp+1, 129); w8(buf, cp+2, 236); + e2_w32(buf, cp+3, 0); cp = cp + 7; + } else { + w8(buf, cp, 72); w8(buf, cp+1, 131); w8(buf, cp+2, 236); w8(buf, cp+3, 0); cp = cp + 4; + } + } + // Save register and caller-stack params into this function's slots. + pi := 0; loop { if pi >= pc { break; } + po2 := -(vs + pi + 1 - g_current_func_var_start) * 8; // force stack slot, ignore reg alloc + pty := irv_type(vs + pi); + if pty == TI_DEX && pi < 6 { + // float 参数在 XMM:movsd [rbp+po2], xmm{frn}(SysV) + // frn = 第 pi 个参数前的 float 参数数 + frn : ., mut = 0; + fj : ., mut = 0; + loop { if fj >= pi { break; } + if irv_type(vs + fj) == TI_DEX { frn = frn + 1; } + fj = fj + 1; } + if frn < 8 { + // movsd [rbp+po2], xmm{frn} — F2 0F 11 /rn(mod=01, rm=5) + w8(buf, cp, 242); w8(buf, cp+1, 15); w8(buf, cp+2, 17); + w8(buf, cp+3, 64 + frn * 8 + 5); w8(buf, cp+4, po2); cp = cp + 5; + } else { + // 9+ binary64 参数在栈上(边缘场景,位置布局简化处理) + caller_off := 16 + (pi - 6) * 8; + if g_opt_level >= 1 { caller_off = caller_off + 40; } + cp = cp + e2_sd_load(buf, cp, caller_off); + cp = cp + e2_sd_store(buf, cp, po2); + } + } else if pi >= 6 { + caller_off := 16 + (pi - 6) * 8; + if g_opt_level >= 1 { caller_off = caller_off + 40; } + if pty == TI_DEX { + cp = cp + e2_sd_load(buf, cp, caller_off); + cp = cp + e2_sd_store(buf, cp, po2); + } else { + cp = cp + e2_ld(buf, cp, 10, caller_off); + cp = cp + e2_st(buf, cp, 10, po2); + } + } else { + if pi == 0 { cp = cp + e2_st(buf, cp, 7, po2); } + if pi == 1 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 117); w8(buf, cp+3, po2); cp = cp + 4; } + if pi == 2 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 85); w8(buf, cp+3, po2); cp = cp + 4; } + if pi == 3 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 77); w8(buf, cp+3, po2); cp = cp + 4; } + if pi == 4 { w8(buf, cp, 76); w8(buf, cp+1, 137); w8(buf, cp+2, 69); w8(buf, cp+3, po2); cp = cp + 4; } + if pi == 5 { w8(buf, cp, 76); w8(buf, cp+1, 137); w8(buf, cp+2, 77); w8(buf, cp+3, po2); cp = cp + 4; } + } + // int 多字 M1(Task 4)tag 卫生③:tagged 参数行的 prologue 定值 + // 清 tag——参数在函数内首次定值前可能被消费者读取(tag 闭包可含 + // 参数行:p = p + k 的 B' 定值拷贝),无本清则读到上次调用/垃圾 + // tag。参数行按强制栈槽保存(po2),tag 字节同在帧内。 + if pty == TI_INT { + ptg := g2_tag_off(vs + pi); + if ptg != -1 { + if ptg >= -128 && ptg <= 127 { + w8(buf, cp, 198); w8(buf, cp+1, 69); w8(buf, cp+2, ptg); w8(buf, cp+3, 0); cp = cp + 4; + } else { + w8(buf, cp, 198); w8(buf, cp+1, 133); e2_w32(buf, cp+2, ptg); w8(buf, cp+6, 0); cp = cp + 7; + } + } + } + pi = pi + 1; } + + // Load register-allocated parameters from stack to callee-saved regs + if g_opt_level >= 1 { + pi2 : ., mut = 0; + loop { if pi2 >= pc || pi2 >= 6 { break; } + pri := get_reg_for_var(vs + pi2); + if pri >= 0 { + pso2 := -(vs + pi2 + 1 - g_current_func_var_start) * 8; // force stack slot + // mov reg, [rbp+offset] — load from stack to allocated register + cp = cp + emit_rex(buf, cp, 1, pri/8, 0, 0); + e2_w8(buf, cp, 139); cp = cp + 1; // 0x8B MOV r64, r/m64 + cp = cp + emit_modrm(buf, cp, 1, pri%8, 5); // [rbp+disp8] + e2_w8(buf, cp, pso2); cp = cp + 1; + } + pi2 = pi2 + 1; } + } + return cp; +} + + +// ── Phase 3 尾声 + 函数尾附加块 —— +// RETURN patch(回填至 epilogue 起点)→ sub rsp 立即数回填(实际帧字节)→ +// epilogue 发射(add rsp / opt≥1 六寄存器逆序 pop / pop rbp / ret)→ 函数尾 +// 附加块(jo 慢路径块 + 2L 操作数块;逐站点独立块,冷区)。返回新 cp。 +// ss 显式传入(= 序言所用帧尺寸;H2 单源)。 +fn pf_epilogue(ss: int, buf: string, pos: int) -> int { + cp : ., mut = pos; + // patch RETURNs to epilogue + epi_pos := cp; + print(" rets: "); print(int_str(g_x86_ret_patch_count)); println(""); + rpi := 0; loop { if rpi >= g_x86_ret_patch_count { break; } + jmp_pos := r64(g_x86_ret_patch_pos, rpi * 8); + rel := epi_pos - (jmp_pos + 5); + w32(buf, jmp_pos + 1, rel); + rpi = rpi + 1; } + g_x86_ret_patch_count = 0; + + // Patch prologue sub rsp with actual stack size + if ss > 0 { + ss3 := ss; + if ss3 > 127 { + e2_w32(buf, g_x86_sub_rsp_pos + 3, ss3); + } else { + w8(buf, g_x86_sub_rsp_pos + 3, ss3); + } + } + + // epilogue (emit with correct stack size, no placeholder) + if ss > 0 { + if ss > 127 { + w8(buf, cp, 72); w8(buf, cp+1, 129); w8(buf, cp+2, 196); + e2_w32(buf, cp+3, ss); cp = cp + 7; + } else { + w8(buf, cp, 72); w8(buf, cp+1, 131); w8(buf, cp+2, 196); w8(buf, cp+3, ss); cp = cp + 4; + } + } + if g_opt_level >= 1 { + // Pops in REVERSE order: rbp, r15, r14, r13, r12, rbx + w8(buf, cp, 93); cp = cp + 1; // pop rbp + w8(buf, cp, 65); w8(buf, cp+1, 95); cp = cp + 2; // pop r15 + w8(buf, cp, 65); w8(buf, cp+1, 94); cp = cp + 2; // pop r14 + w8(buf, cp, 65); w8(buf, cp+1, 93); cp = cp + 2; // pop r13 + w8(buf, cp, 65); w8(buf, cp+1, 92); cp = cp + 2; // pop r12 + w8(buf, cp, 91); cp = cp + 1; // pop rbx + } else { + w8(buf, cp, 93); cp = cp + 1; // pop rbp + } + w8(buf, cp, 195); cp = cp + 1; // ret + + // ── int 多字 M1(Task 3):慢路径块(函数尾附加)+ jo rel32 回填 ── + // 每 jo 站点独立块(块形状决策:jo 现场 = dest 槽/回跳点逐站点而异, + // 共享块需逐站分发 = 复杂度不值;块 ≈ 45B × 站点数,函数尾冷区,M1 + // 保守 tagged 集代价的一部分——与 Task 2 已付的每站 jo 6B 同族)。 + // 块 = 真实 2-limb 修正代码(e2_mw_slow_block,tag2l.cr): + // 修正 128 位值 → alloc(16)(alloc_patch 注册——计数逐函数不重置, + // 与函数体 alloc 调用同批回填)→ 写 2-limb → 值槽存指针(含 + // O1/O2 reg 形态——g2_slot 于当前函数上下文现算,与函数体发射 + // 同源同值)→ tag 置位 → jmp 回该站点快路径 store 之后。 + // jo 记录(pos/dest/is_sub/resume——g2_init 清零逐函数段)此处按站 + // 点点序发射;无 jo 的函数不发块、零字节影响(untagged 快路径零变化)。 + if g_x86_mw_jo_count > 0 { + mw_ji : ., mut = 0; + loop { if mw_ji >= g_x86_mw_jo_count { break; } + jo_pos := r64(g_x86_mw_jo_pos, mw_ji * 8); + jo_d := r64(g_x86_mw_jo_dest, mw_ji * 8); + jo_sub := r64(g_x86_mw_jo_is_sub, mw_ji * 8); + jo_res := r64(g_x86_mw_jo_resume, mw_ji * 8); + mw_blk := cp; + w32(buf, jo_pos + 2, mw_blk - (jo_pos + 6)); + cp = cp + e2_mw_slow_block(buf, cp, jo_d, jo_sub, jo_res); + mw_ji = mw_ji + 1; } + g_x86_mw_jo_count = 0; + } + // ── int 多字 M1(Task 4):2L 操作数块(函数尾附加,jo 块之后)── + // 每消费者站点独立块(快路径 tag 检查 jne → 块首;块 = 128 位算术 + // 或比较 + 公共落值——e2_mw_opnd_block,tag2l.cr)。块形状按站点 + // 静态参数化(操作数行 tagged 与否决定分派形态),含 alloc(16) 的 + // 块与函数体同批回填(alloc_patch 计数逐函数不重置)。jne rel32 + // 位置在 oc 记录(pos1/pos2,-1 = 无第二个检查),块位置 = 发射时 + // cp。无消费者站点的函数零字节(untagged 快路径零变化保持)。 + if g_x86_mw_oc_count > 0 { + mw_oi : ., mut = 0; + loop { if mw_oi >= g_x86_mw_oc_count { break; } + oc_p1 := r64(g_x86_mw_oc_pos1, mw_oi * 8); + oc_p2 := r64(g_x86_mw_oc_pos2, mw_oi * 8); + oc_s1 := r64(g_x86_mw_oc_s1, mw_oi * 8); + oc_s2 := r64(g_x86_mw_oc_s2, mw_oi * 8); + oc_d := r64(g_x86_mw_oc_dest, mw_oi * 8); + oc_op := r64(g_x86_mw_oc_op, mw_oi * 8); + oc_res := r64(g_x86_mw_oc_resume, mw_oi * 8); + oc_blk := cp; + if oc_p1 >= 0 { w32(buf, oc_p1 + 2, oc_blk - (oc_p1 + 6)); } + if oc_p2 >= 0 { w32(buf, oc_p2 + 2, oc_blk - (oc_p2 + 6)); } + cp = cp + e2_mw_opnd_block(buf, cp, oc_s1, oc_s2, oc_d, oc_op, oc_res); + mw_oi = mw_oi + 1; } + g_x86_mw_oc_count = 0; + } + return cp; +} diff --git a/src/arch/linux/ld/instr.cr b/src/arch/x86_64/instr.cr similarity index 67% rename from src/arch/linux/ld/instr.cr rename to src/arch/x86_64/instr.cr index 002fb1fe..58d54a55 100644 --- a/src/arch/linux/ld/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -6,6 +6,7 @@ g_x86_rodata_base : int, mut; g_x86_func_frame_start : int, mut; // abs buf pos of current function body (after frame) g_current_func_var_start : int, mut; // var_start of current function, set before emit +g_hit_tabled_count : int, mut; // 表驱动实际发射事件条数(诊断:corearch 表模式总结) E2_REG_SLOT_BASE : int = 1000000000; @@ -14,14 +15,21 @@ fn g2_init() { g_x86_emit_var_count = 0; g_x86_emit_stack_size = 0; g_x86_ret_patch_count = 0; + // g_x86_mw_jo_count: reset per function — jo rel32s are patched to the + // function-tail slow-path block right after each function's epilogue. + g_x86_mw_jo_count = 0; + // g_x86_mw_oc_count (Task 4): reset per function — 2L 操作数检查 + // (jne 0F 85)同款回填到函数尾 2L 块。 + g_x86_mw_oc_count = 0; // g_x86_alloc_patch_count NOT reset: alloc calls are patched after all funcs. // g_x86_ext_rel_count NOT reset: extern relocations span all functions. // g_x86_rip_patch_count NOT reset } // ── Optimization metadata: register assignment lookup ── -// Reads g_opt_meta (saved in .ccr v3+) to find register for a variable. -// Returns -1 if no register assigned. +// Reads g_opt_meta to find register for a variable. regalloc 移后端(2026-09-07) +// 后 g_opt_meta 由 corearch O2 自算填充(load 后 alloc_registers,不再经 .ccr +// 传输);O0/O1 恒空 → 全栈发射。Returns -1 if no register assigned. fn get_reg_for_var(var_idx: int) -> int { mi : ., mut = 0; @@ -83,6 +91,24 @@ fn g2_rodata_sz() -> int { return o; } +// int 多字 M1(tagged 槽)/ tag 表 / 2L 编码族——**全章移至 +// src/arch/x86_64/tag2l.cr**(值表示、识别规则 A/B/B'、M1 边界、tag 卫生、 +// 慢路径块/2L 块、tag 字节助手;x86 实例化波 1 Task 4 整函数搬迁,发射序 +// 逐字节 verbatim,无行为变化)。本文件保留通用编码原语(e2_*/emit_*)与 +// emit_instr;mw 族消费点见 IR_BINARY/IR_LOAD/IR_STORE/IR_RETURN 分支的 +// tag 检查 / jo / tag 卫生调用(tag 表读取 = frame.cr g2_tag_off)。 +// +// 调用序列——**已移至 src/os/linux/callseq.cr**(x86 实例化波 1 Task 5 抽取; +// OS 轴 = SysV AMD64 调用约定):寄存器参数分派(cs_args_dispatch——IR_CALL/ +// IR_CALL_EXTERN/IR_SPAWN 三处同源合流)/栈参压栈(cs_arg_on_stack/ +// cs_stack_count/cs_stack_args)/栈清理(cs_stack_cleanup)/返回值 +// (cs_ret_value——IR_RETURN 值序列 + tag 读路径 A)/通用调用 +// (cs_call_direct)。本文件保留 e2_* 编码原语、emit_instr 分派与内置体 +// (load8/store8/…、get_arg、_dyncpy、goroutine_wrapper_addr——内置体直通属 +// 波 2 参数化面)。**syscall 约定(syscall3/4 内置体)已移 +// src/os/linux/syscall.cr**(x86 实例化波 1 Task 6 抽取——sys_syscall3_stub/ +// sys_syscall4_stub;rax 号 + rdi/rsi/rdx(/r10) 参数序 + 0F 05 + 回存)。 + // ── Byte encoding helpers ── fn e2_w8(buf: string, pos: int, val: int) { store8(buf, pos, val % 256); } fn e2_w16(buf: string, off: int, val: int) { w32(buf, off, val); } @@ -465,6 +491,22 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { g_x86_rodataref_count = g_x86_rodataref_count + 1; cp = cp + e2_lr(buf, pos+cp, 0); // placeholder, patched later cp = cp + e2_st(buf, pos+cp, 10, do2); + } else if r64(g_x86_is_global, d * 8) != 0 { + // R2 P4 Task 5:**全局行**直写(g2_slot 对全局行返回帧外伪偏移 ⇒ 修复前 + // 本分支落 `e2_li` 即写栈垃圾 = 静默丢写;解释器同 IR 直写 g_ir_vals ⇒ + // **两路径分歧**)。与 IR_LOAD/IR_STORE 的全局分派同款:常量入 r10 + + // RIP 相对 store(e2_lrb 取址、patch 记录与 IR_STORE 同构)。 + // 注:TI_STR 分支(rodata 指针)对全局行同样未分派——当前零发射点 + // (字符串常量入全局走 .data/_init_globals),登记不改。 + e2_w8(buf, pos+cp, 73); e2_w8(buf, pos+cp+1, 186); // movabs r10, imm64(REX.W + B8+rd) + e2_w64(buf, pos+cp+2, s1); cp = cp + 10; + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, d); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + cp = cp + e2_lrb(buf, pos+cp, 0); + // mov [r11], r10 — REX.WRB + 0x89 + cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 11/8); e2_w8(buf, pos+cp, 137); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 11%8); } else { cp = cp + e2_li(buf, pos+cp, do2, s1); } @@ -512,8 +554,36 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { } cp = cp + e2_load_var(buf, pos+cp, 10, s1); cp = cp + e2_load_var(buf, pos+cp, 11, s2); - if s3 == OP_ADD { cp = cp + e2_alu(buf, pos+cp, 1); } - else if s3 == OP_SUB { cp = cp + e2_alu(buf, pos+cp, 41); } + // int 多字 M1(Task 4):消费者站点 tag 检查——操作数行 tagged 且站点 + // 属 2L 消费面(add/sub dest tagged = jo 同条件;比较任一操作数 + // tagged)→ 快路径前查 tag:任一操作数 2L(tag=1)→ 函数尾 2L 块 + // (128 位算术/比较,e2_mw_opnd_block)。untagged 行/站点零字节。 + // (tag 检查在操作数装载后——检查只写标志,不扰 r10/r11。) + mw_oc : ., mut = -1; + if (s3 == OP_ADD || s3 == OP_SUB) && g2_tag_off(d) != -1 || (s3 >= OP_EQ && s3 <= OP_GE) { + if g2_tag_off(s1) != -1 { + if mw_oc == -1 { mw_oc = mw_oc_new(s1, s2, d, s3); } + cp = cp + e2_mw_oc_check(buf, pos+cp, mw_oc, 0, s1); + } + if s1 != s2 && g2_tag_off(s2) != -1 { + if mw_oc == -1 { mw_oc = mw_oc_new(s1, s2, d, s3); } + cp = cp + e2_mw_oc_check(buf, pos+cp, mw_oc, 1, s2); + } + } + if s3 == OP_ADD { + cp = cp + e2_alu(buf, pos+cp, 1); + // int 多字 M1(Task 2/3):tagged dest 的 add/sub 后紧跟溢出跳 + // (e2_alu 的 OF 其后无任何写标志指令可再被读)→ 函数尾慢路径 + // 块。慢路径在快路径 store 之前跳走(块内自回存 dest 值 + 置 tag + // 并跳回 store 之后继续——e2_mw_slow_block,tag2l.cr;块发射 = + // frame.cr pf_epilogue 函数尾按站点)。站点记录 dest 与 op(is_sub + // ——高 limb 修正规则因 op 异)。 + if g2_tag_off(d) != -1 { cp = cp + e2_mw_jo(buf, pos+cp, d, 0); } + } + else if s3 == OP_SUB { + cp = cp + e2_alu(buf, pos+cp, 41); + if g2_tag_off(d) != -1 { cp = cp + e2_mw_jo(buf, pos+cp, d, 1); } + } else if s3 == OP_MUL { // imul r10, r11 — 2-byte opcode 0x0F 0xAF cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 11/8); @@ -540,7 +610,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { cp = cp + emit_modrm(buf, pos+cp, 3, 5, 10%8); } else if s3 == OP_PTR_ADD { // p + n: scale n by element size (8), add to p - // imul r11, 8, r11 — REX.WB + 0x6B + ModRM(3, r11, r11) + imm8 + // imul r11, 8, r11 — REX.WRB + 0x6B + ModRM(3, r11, r11) + imm8 + //(0x4D:reg 字段与 rm 同为 r11——R(reg)与 B(rm)都需置位) cp = cp + emit_rex(buf, pos+cp, 1, 11/8, 0, 11/8); e2_w8(buf, pos+cp, 107); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 3, 11%8, 11%8); @@ -579,12 +650,18 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { sop := 148; if s3 == OP_NE { sop = 149; } else if s3 == OP_LT { sop = 156; } else if s3 == OP_GT { sop = 159; } else if s3 == OP_LE { sop = 158; } else if s3 == OP_GE { sop = 157; } // SETcc al — 2-byte opcode 0x0F 0x9x e2_w8(buf, pos+cp, 15); cp = cp + 1; e2_w8(buf, pos+cp, sop); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 3, 0, 0); - // movzx r10, al — REX.RB + 0x0FB6 + // movzx r10d, al — REX.R + 0x0FB6(rm=al 无需 B——R 只因 reg=r10) cp = cp + emit_rex(buf, pos+cp, 0, 10/8, 0, 0); e2_w8(buf, pos+cp, 15); cp = cp + 1; e2_w8(buf, pos+cp, 182); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 3, 10%8, 0); } else if s3 == OP_AND { cp = cp + e2_alu(buf, pos+cp, 33); } else if s3 == OP_OR { cp = cp + e2_alu(buf, pos+cp, 9); } cp = cp + e2_st(buf, pos+cp, 10, do2); + // int 多字 M1(Task 4)tag 卫生①:快路径 64 位定值点 tag 清 0—— + // 快值落 tagged 槽后 tag 必须为 0(否则回边再执行时 stale tag=1 会 + // 把快值当 2-limb 指针读)。jo/2L 块跳 resume 在此之后——慢路径自 + // 写 tag=1,不被本条清除(resume 位于本条之后)。dest untagged → + // 零字节(mul/shl/…/非 add-sub 的 tagged dest 不存在——防御兜底)。 + cp = cp + e2_mw_tag_clr(buf, pos+cp, d); return cp; } @@ -609,76 +686,22 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { if op == IR_CALL { fa := s1; ac := s2; - // SysV AMD64 参数分派:int 用 ir(0-5 → rdi,rsi,rdx,rcx,r8,r9), - // binary64 参数用 fr(0-7 → xmm0-7),各自独立编号(标准答案) + // SysV AMD64 参数分派 + 栈超限压栈 = OS 轴序列(callseq.cr,波 1 + // Task 5 抽取——三处同源合流/差异核对见该文件头注)。 // 第一遍:寄存器参数(位置顺序,左到右) - ir_cnt : ., mut = 0; fr_cnt : ., mut = 0; - ai := 0; - loop { if ai >= ac { break; } - pt := irv_type(fa + ai); - if pt == TI_DEX { - if fr_cnt < 8 { - cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + ai), fr_cnt); - fr_cnt = fr_cnt + 1; - } - } else { - if ir_cnt < 6 { - r := -1; - if ir_cnt == 0 { r = 7; } if ir_cnt == 1 { r = 6; } if ir_cnt == 2 { r = 2; } - if ir_cnt == 3 { r = 1; } if ir_cnt == 4 { r = 8; } if ir_cnt == 5 { r = 9; } - cp = cp + e2_load_var(buf, pos+cp, r, fa + ai); - ir_cnt = ir_cnt + 1; - } - } - ai = ai + 1; } + cp = cs_args_dispatch(buf, pos, cp, fa, ac); // 第二遍:栈参数(右到左压,第 7 个 int / 第 9 个 binary64 超限才压) - stack_total : ., mut = 0; - stack_ai : ., mut = ac - 1; - loop { - if stack_ai < 0 { break; } - ic2 : ., mut = 0; fc2 : ., mut = 0; - j2 : ., mut = 0; - loop { if j2 >= stack_ai { break; } - if irv_type(fa + j2) == TI_DEX { fc2 = fc2 + 1; } else { ic2 = ic2 + 1; } - j2 = j2 + 1; } - if irv_type(fa + stack_ai) == TI_DEX { - if fc2 >= 8 { - cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + stack_ai), 0); - cp = cp + e2_push_xmm0(buf, pos+cp); - stack_total = stack_total + 1; - } - } else { - if ic2 >= 6 { - cp = cp + e2_load_var(buf, pos+cp, 10, fa + stack_ai); - e2_w8(buf, pos+cp, 65); e2_w8(buf, pos+cp+1, 82); cp = cp + 2; // push r10 - stack_total = stack_total + 1; - } - } - stack_ai = stack_ai - 1; - } + stack_total := cs_stack_count(fa, ac); + cp = cs_stack_args(buf, pos, cp, fa, ac); // Match builtins by interned string index (integer compare, no str_eq) if s3 == g_ni_syscall3 { - cp = cp + e2_mov(buf, pos+cp, 0, 7); - cp = cp + e2_mov(buf, pos+cp, 7, 6); - cp = cp + e2_mov(buf, pos+cp, 6, 2); - cp = cp + e2_mov(buf, pos+cp, 2, 1); - // syscall: 2-byte 0x0F 0x05 - e2_w8(buf, pos+cp, 15); e2_w8(buf, pos+cp+1, 5); cp = cp + 2; - if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + // Linux syscall 约定发射序 = OS 轴序列(syscall.cr,波 1 Task 6 + // 抽取——rax 号 + rdi/rsi/rdx 参数序 + 0F 05 + rax 回存)。 + cp = sys_syscall3_stub(buf, pos, cp, d); } else if s3 == g_ni_syscall4 { - // syscall4(num, a, b, c, d)——第 4 参 d 经 r10 传递(x86-64 syscall - // 约定:第 4 参在 r10;rcx 被 syscall 指令用作返回地址)。 - // I-2:wait4 的 rusage 此前无第 4 参通道——通用装载器把第 4 参放 - // rcx(ir_cnt=3)、第 5 参放 r8(ir_cnt=4),syscall 读 r10 = 残留 - // 垃圾 → 内核 EFAULT 或写错地址 → 退出码传播不可靠。 - cp = cp + e2_mov(buf, pos+cp, 0, 7); // rax = rdi — syscall number - cp = cp + e2_mov(buf, pos+cp, 7, 6); // rdi = rsi — arg1 - cp = cp + e2_mov(buf, pos+cp, 6, 2); // rsi = rdx — arg2 - cp = cp + e2_mov(buf, pos+cp, 2, 1); // rdx = rcx — arg3 - cp = cp + e2_mov(buf, pos+cp, 10, 8); // r10 = r8 — arg4 - // syscall: 2-byte 0x0F 0x05 - e2_w8(buf, pos+cp, 15); e2_w8(buf, pos+cp+1, 5); cp = cp + 2; - if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + // Linux syscall 约定发射序 = OS 轴序列(syscall.cr,波 1 Task 6 + // 抽取——第 4 参经 r10 的约定与 I-2 修复史见该文件内注)。 + cp = sys_syscall4_stub(buf, pos, cp, d); } else if s3 == g_ni_load8 { // movzx rax, byte [rdi+rsi] — REX.W + 0x0FB6 + SIB cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 0); e2_w8(buf, pos+cp, 15); cp = cp + 1; e2_w8(buf, pos+cp, 182); cp = cp + 1; @@ -800,41 +823,16 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { cp = cp + 10; cp = cp + e2_st(buf, pos+cp, 10, do2); } else if s3 >= 0 { - fn2 := istr_get(s3); - to := -1; tf := 0; - loop { if tf >= g_x86_func_off_count { break; } if str_eq(istr_get(r64(g_x86_func_offsets, tf*16)), fn2) != 0 { to = r64(g_x86_func_offsets, tf*16+8); break; } tf = tf + 1; } - // Record call position for post-emission patching - grow_call_patch(g_x86_call_patch_count + 1); - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp); - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern(fn2)); - g_x86_call_patch_count = g_x86_call_patch_count + 1; - if to >= 0 { - cp = cp + e2_call(buf, pos+cp, (176 + to) - (pos + cp + 5)); - } else { - // Unknown function: emit external relocation (for dynamic linking) - grow_ext_rel(g_x86_ext_rel_count + 1); - w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp + 1); - w64(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, s3); - g_x86_ext_rel_count = g_x86_ext_rel_count + 1; - cp = cp + e2_call(buf, pos+cp, 0); - } - if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + // 通用调用路径 = callseq.cr cs_call_direct(查名 → e2_call / + // 外部位 + 返回值存;波 1 Task 5 抽取) + cp = cs_call_direct(buf, pos, cp, s3, d); } else { // xor eax, eax e2_w8(buf, pos+cp, 49); e2_w8(buf, pos+cp+1, 192); cp = cp + 2; if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } } - stack_count := stack_total; // 实际压栈数(int 超 6 + float 超 8) - if stack_count > 0 { - stack_bytes := stack_count * 8; - if stack_bytes <= 127 { - e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 131); - e2_w8(buf, pos+cp+2, 196); e2_w8(buf, pos+cp+3, stack_bytes); cp = cp + 4; - } else { - e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 129); e2_w8(buf, pos+cp+2, 196); - e2_w32(buf, pos+cp+3, stack_bytes); cp = cp + 7; - } - } + // 栈清理(call 后恢复 rsp)= callseq.cr cs_stack_cleanup(波 1 Task 5 抽取) + cp = cs_stack_cleanup(buf, pos, cp, stack_total); return cp; } @@ -843,27 +841,10 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { name_ni := s1; // F16①:装载参数(s2=首参、s3=参数个数,SysV AMD64 约定: // int rdi,rsi,rdx,rcx,r8,r9;float xmm0-7)——修复前 call 前无任何装载。 - fa2 := s2; ac2 := s3; - ir_cnt2 : ., mut = 0; fr_cnt2 : ., mut = 0; - ai2 : ., mut = 0; - loop { if ai2 >= ac2 { break; } - pt2 := irv_type(fa2 + ai2); - if pt2 == TI_DEX { - if fr_cnt2 < 8 { - cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa2 + ai2), fr_cnt2); - fr_cnt2 = fr_cnt2 + 1; - } - } else { - if ir_cnt2 < 6 { - r2 := -1; - if ir_cnt2 == 0 { r2 = 7; } if ir_cnt2 == 1 { r2 = 6; } if ir_cnt2 == 2 { r2 = 2; } - if ir_cnt2 == 3 { r2 = 1; } if ir_cnt2 == 4 { r2 = 8; } if ir_cnt2 == 5 { r2 = 9; } - cp = cp + e2_load_var(buf, pos+cp, r2, fa2 + ai2); - ir_cnt2 = ir_cnt2 + 1; - } - } - ai2 = ai2 + 1; - } + // 分派段 = callseq.cr cs_args_dispatch(波 1 Task 5 三处同源合流)。 + // **注**:extern 路径只做寄存器分派——无栈参压栈/栈清理(预存行为, + // 逐字节保持;差异核对见 callseq.cr 头注)。 + cp = cs_args_dispatch(buf, pos, cp, s2, s3); // Record external relocation grow_ext_rel(g_x86_ext_rel_count + 1); w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp); @@ -896,70 +877,22 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { // 函数名查表 → 补丁失败 → call rel32=0 → 栈不平衡 → SIGSEGV 139。) name_ni := s3; // F4:补参数装载——与 IR_CALL 完全相同的 SysV AMD64 分派 - // (int: rdi,rsi,rdx,rcx,r8,r9 + 栈超限;float: xmm0-7)。 + // (int: rdi,rsi,rdx,rcx,r8,r9 + 栈超限;float: xmm0-7)—— + // = callseq.cr cs_args_dispatch(波 1 Task 5 三处同源合流)。 fa := s1; ac := s2; - ir_cnt : ., mut = 0; fr_cnt : ., mut = 0; - ai := 0; - loop { if ai >= ac { break; } - pt := irv_type(fa + ai); - if pt == TI_DEX { - if fr_cnt < 8 { - cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + ai), fr_cnt); - fr_cnt = fr_cnt + 1; - } - } else { - if ir_cnt < 6 { - r := -1; - if ir_cnt == 0 { r = 7; } if ir_cnt == 1 { r = 6; } if ir_cnt == 2 { r = 2; } - if ir_cnt == 3 { r = 1; } if ir_cnt == 4 { r = 8; } if ir_cnt == 5 { r = 9; } - cp = cp + e2_load_var(buf, pos+cp, r, fa + ai); - ir_cnt = ir_cnt + 1; - } - } - ai = ai + 1; } + cp = cs_args_dispatch(buf, pos, cp, fa, ac); // 栈参数(右到左压,第 7 个 int / 第 9 个 float 超限才压)——同 IR_CALL - stack_total : ., mut = 0; - stack_ai : ., mut = ac - 1; - loop { - if stack_ai < 0 { break; } - ic2 : ., mut = 0; fc2 : ., mut = 0; - j2 : ., mut = 0; - loop { if j2 >= stack_ai { break; } - if irv_type(fa + j2) == TI_DEX { fc2 = fc2 + 1; } else { ic2 = ic2 + 1; } - j2 = j2 + 1; } - if irv_type(fa + stack_ai) == TI_DEX { - if fc2 >= 8 { - cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + stack_ai), 0); - cp = cp + e2_push_xmm0(buf, pos+cp); - stack_total = stack_total + 1; - } - } else { - if ic2 >= 6 { - cp = cp + e2_load_var(buf, pos+cp, 10, fa + stack_ai); - e2_w8(buf, pos+cp, 65); e2_w8(buf, pos+cp+1, 82); cp = cp + 2; // push r10 - stack_total = stack_total + 1; - } - } - stack_ai = stack_ai - 1; - } + // (= callseq.cr cs_stack_args/cs_stack_count;波 1 Task 5 抽取) + stack_total := cs_stack_count(fa, ac); + cp = cs_stack_args(buf, pos, cp, fa, ac); // Emit call to function + store result (single-threaded approximation) grow_call_patch(g_x86_call_patch_count + 1); w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp); w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_ni); g_x86_call_patch_count = g_x86_call_patch_count + 1; e2_w8(buf, pos+cp, 232); e2_w32(buf, pos+cp+1, 0); cp = cp + 5; - // 栈清理(call 后恢复 rsp)——同 IR_CALL - stack_count := stack_total; - if stack_count > 0 { - stack_bytes := stack_count * 8; - if stack_bytes <= 127 { - e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 131); - e2_w8(buf, pos+cp+2, 196); e2_w8(buf, pos+cp+3, stack_bytes); cp = cp + 4; - } else { - e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 129); e2_w8(buf, pos+cp+2, 196); - e2_w32(buf, pos+cp+3, stack_bytes); cp = cp + 7; - } - } + // 栈清理(call 后恢复 rsp)——同 IR_CALL(= callseq.cr cs_stack_cleanup) + cp = cs_stack_cleanup(buf, pos, cp, stack_total); if d >= 0 && irv_type(d) == TI_DEX { cp = cp + e2_sd_store(buf, pos+cp, do2); } else { @@ -989,25 +922,9 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { } if op == IR_RETURN { - if s1 >= 0 { - if irv_type(s1) == TI_DEX { - // binary64 返回:movsd xmm0, [slot](SysV 返回值在 XMM0,apx 快路径) - cp = cp + e2_sd_load(buf, pos+cp, g2_slot(s1)); - } else if r64(g_x86_is_global, s1 * 8) != 0 { - // Global: load via RIP-relative into rax - grow_rip_patch(g_x86_rip_patch_count + 1); - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3); - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1); - g_x86_rip_patch_count = g_x86_rip_patch_count + 1; - cp = cp + e2_lr(buf, pos+cp, 0); // lea r10, [rip+0] - // mov rax, [r10] — REX.WB + 0x8B - cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 0, 10%8); - } else { cp = cp + e2_ld(buf, pos+cp, 0, g2_slot(s1)); } - } - // record position for caller to patch jmp → epilogue - grow_ret_patch(g_x86_ret_patch_count + 1); w64(g_x86_ret_patch_pos, g_x86_ret_patch_count * 8, pos + cp); - g_x86_ret_patch_count = g_x86_ret_patch_count + 1; - cp = cp + e2_jmp(buf, pos+cp, 0); + // 值序列 + ret_patch 登记 + jmp 占位 = callseq.cr cs_ret_value + // (波 1 Task 5 抽取;tag 读路径 A 见该文件注释) + cp = cs_ret_value(buf, pos, cp, s1); return cp; } @@ -1110,8 +1027,18 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { // mov r10, [r10] — REX.WRB + 0x8B cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 10%8); cp = cp + e2_st(buf, pos+cp, 10, do2); - } else { cp = cp + e2_load_var(buf, pos+cp, 10, s1); cp = cp + e2_st(buf, pos+cp, 10, do2); } + // 全局源(恒 64 位值)→ d tagged 时防御性清 tag + cp = cp + e2_mw_tag_clr(buf, pos+cp, d); + } else { + cp = cp + e2_load_var(buf, pos+cp, 10, s1); cp = cp + e2_st(buf, pos+cp, 10, do2); + // 拷贝定值 tag 传播(源 tagged → 运行时 tag 拷贝;源 untagged + // → 清 0)——d 为 as 转换/拷贝链行(规则 B 闭包成员) + cp = cp + e2_mw_tag_cpy(buf, pos+cp, d, s1); + } } else { cp = cp + e2_load_var(buf, pos+cp, 10, s1); cp = cp + e2_st(buf, pos+cp, 10, do2); } + // s1<0 尾分支(域外行——现 IR 形态防御兜底):mw_setup_tags 规则 B 只扫 + // 函数域 [vs, ve)(s1 ∈ 域 且 tagged 才标 d)→ d 恒 untagged——快值原样 + // 直拷,无 e2_mw_tag_cpy/清 0 字节(tag 拷贝只对有 tag 字节的域内行发)。 return cp; } @@ -1127,15 +1054,27 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { cp = cp + e2_lrb(buf, pos+cp, 0); // mov [r11], r10 — REX.WRB + 0x89 cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 11/8); e2_w8(buf, pos+cp, 137); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 11%8); - } else { cp = cp + e2_load_var(buf, pos+cp, 10, s2); cp = cp + e2_st(buf, pos+cp, 10, o1); } + } else { + cp = cp + e2_load_var(buf, pos+cp, 10, s2); cp = cp + e2_st(buf, pos+cp, 10, o1); + // 拷贝定值 tag 传播(规则 B' 闭包成员:s2 tagged → 运行时 tag + // 拷贝;untagged/全局源 → 清 0)——变量行真定值载体 + cp = cp + e2_mw_tag_cpy(buf, pos+cp, s1, s2); + } } else { cp = cp + e2_load_var(buf, pos+cp, 10, s2); cp = cp + e2_st(buf, pos+cp, 10, o1); } + // s1<0 尾分支(域外目标行——现 IR 形态防御兜底):规则 B' 只标函数域内 + // [vs, ve) 的 s1(且 s2 tagged)→ 域外 s1 恒 untagged——快值原样直拷, + // 无 tag 拷贝/清 0(g2_tag_off 域外恒 -1,拷贝源 s2 的 tag 无需落地)。 return cp; } if op == IR_LOAD_FIELD && d >= 0 { - o1 := g2_slot(s1); do2 := g2_slot(d); fi2 := s3; + do2 := g2_slot(d); fi2 := s3; fo : ., mut = fi2 * 8; - cp = cp + e2_ld(buf, pos+cp, 10, o1); + // 基址加载走 e2_load_var(局部 = [rbp+slot];全局 = RIP 相对 lea, + // 与 IR_LOAD_INDEX/IR_ADDR_INDEX 的「handles local and global」同规约)。 + // 修复前为 g2_slot(s1)+e2_ld:全局行落在本函数帧之外,g2_slot 返回正的 + // 伪 rbp 偏移 → 读栈垃圾(全局结构体字段读静默错值)。 + cp = cp + e2_load_var(buf, pos+cp, 10, s1); // mov r10, [r10 + disp32] — REX.WRB + 0x8B cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 2, 10%8, 10%8); cp = cp + e2_w32(buf, pos+cp, fo); @@ -1144,9 +1083,9 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { } if op == IR_STORE_FIELD { - o1 := g2_slot(s1); o2 := g2_slot(s2); fi2 := s3; + fi2 := s3; fo : ., mut = fi2 * 8; - cp = cp + e2_ld(buf, pos+cp, 10, o1); cp = cp + e2_ld(buf, pos+cp, 11, o2); + cp = cp + e2_load_var(buf, pos+cp, 10, s1); cp = cp + e2_load_var(buf, pos+cp, 11, s2); // mov [r10 + disp32], r11 — REX.WRB + 0x89 cp = cp + emit_rex(buf, pos+cp, 1, 11/8, 0, 10/8); e2_w8(buf, pos+cp, 137); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 2, 11%8, 10%8); cp = cp + e2_w32(buf, pos+cp, fo); @@ -1275,7 +1214,12 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { if op == IR_LOAD_ENUM_TAG && d >= 0 { o1 := g2_slot(s1); do2 := g2_slot(d); - cp = cp + e2_ld(buf, pos+cp, 10, o1); + // R2 P4 Task 5(与 IR_CONST 全局行分支同族):**tag 读取源走 e2_load_var** + // (局部 = [rbp+slot];全局 = RIP 相对 lea)。修复前 `e2_ld(o1)` 对全局行读 + // 帧外伪偏移 ⇒ tag 读垃圾 ⇒ 全局可选槽的 match **静默走空臂**(实测: + // `g : int? = Some(5)` + Some 臂 ELF=0 / 解释器=5 = 双路径分歧,基线同病)。 + // 注:目的槽仍按帧槽(全局目的行的 RIP 相对 store 未接——当前零发射点)。 + cp = cp + e2_load_var(buf, pos+cp, 10, s1); // mov r10, [r10 + disp32] — tag at offset 0 // mov r10, [r10 + 0] (enum tag) cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; @@ -1564,3 +1508,409 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int { return 0; } + +// ══════════════════════════════════════════════════════════════ +// HIT 表驱动发射(M1 Task 2/3)——事件流编码器(emit_instr 的并行路径) +// ══════════════════════════════════════════════════════════════ +// 表模式下 corearch 内部 = IR →(lower_to_core.cr 降低)→ 事件流 →(本编码器 +// 表投影)→ 字节。数据 = core-x86.toml(表数据归架构轴 src/arch/x86_64/, +// load_hit_table 已载入 +// g_hit_events/steps)。降低在发射前一次性完成(hit_lower_program, +// lower_to_core.cr:IR 直线子集 → 事件流 + 常量池);每条 IR 指令在 +// g_hit_ev_map 记 [事件流起点, 条数](0 条 = 非子集 → 落旧路径)。 +// 调用方(elf.cr 发射循环):hit_table_active() 时先调 emit_instr_tabled, +// 返回 -1 = 无事件/形态不支持 → 落旧路径 emit_instr(M1 混合模式)。 +// 正确性契约:事件字节 = 模板投影(opcode 与 modrm 角色取自表数据——表 = 数据, +// 错在数据不在代码)+ 操作数装载/回存 glue(复用 e2_* 槽机制,与旧路径同语义; +// 逐字节对照仅 Task 2 直通形严格,Task 3 起 const → 池 load 字节有意不同)。 +// 事件记录布局/访问器/常量池:lower_to_core.cr(构建清单中位于本文件之前); +// 布局常量 hit.cr(HIT_*)。 + +// 角色 → M1 寄存器对低 3 位(modrm 字段编码;r10=2 主累加 / r11=3 第二操作数) +fn hit_role_reg_low(role: int) -> int { + if role == HIT_ROLE_DST { return 2; } + if role == HIT_ROLE_SRC1 { return 2; } + if role == HIT_ROLE_SRC2 { return 3; } + if role == HIT_ROLE_VAL { return 2; } + return -1; } // addr/未知:无寄存器惯例(M1 addr 走 rbp+disp 形态) + +// 角色 → 绝对寄存器号(M1 惯例 r10/r11:低 3 位 + REX.R 位含在表 opcode 内) +fn hit_role_reg(role: int) -> int { + low := hit_role_reg_low(role); + if low < 0 { return -1; } + return 8 + low; } + +// 槽/池寻址形态的 addr 变量可用性:须为当前函数真内存槽变量 +// (表模板 = rbp+disp32:寄存器分配变量/全局走旧路径——e2 族已有其语义) +fn hit_ev_slot_addr_ok(v: int) -> int { + if v < 0 { return 0; } + if v >= g_ir_var_count { return 0; } + if r64(g_x86_is_global, v * 8) != 0 { return 0; } + if v < g_current_func_var_start { return 0; } + o := g2_slot(v); + if o >= E2_REG_SLOT_BASE { return 0; } + return 1; } + +// 取事件模板步(proj0 视图整条 108B 记录复制入 st;本文件 M1 消费面只读 +// 前 20B 视区——schema v2 记录加宽后偏移 0-16 语义未变,见 hit.cr 布局注释); +// -1 = 表无此事件/步非法 +fn hit_ev_step_of(ev_id: int, st: string) -> int { + es := hit_event_lookup(ev_id); + if es < 0 { return -1; } + if hit_proj_step(es, 0, st) != 0 { return -1; } + return 0; } + +// ── v2 步模板发射/预检(M2a Task 2——模板解释器字段发射)── +// 形态族字段(hit.cr HIT_ST_OFF_* v2 区)按解释器固定序组装:prefix → rex → +// opcode → modrm → disp → imm → rel(= loader 字节视区拼接序同源)。本任务 +// 消费面:modrm 双角色(rm_mode 0 寄存器对 = 清单 A 族 / rm_mode 1 rbp+disp = +// C 族)/digit 操作码扩展(C7 系)/disp auto|1|4(auto = 槽偏移幅度选 disp8/32 +// ——与旧路径 e2_ld/e2_st 同规则 ④)/imm 字面量与操作数原值/rel32 回填登记 +// (kind 0 = 事件流位置)。SIB(rm_mode 2)/rip 基/寄存器字面量角色/imm_kind/ +// rel8/cond/多步序列 = 后续批与 Task 3/6 消费——预检拒(保守落旧路径)。 + +// 事件语义类(运行时核事件 id = 语义锚:粘合序列 = 保留代码,模板字节 = 表数据): +// ALU(sub=1/nand=2):rm_mode 0 双寄存器累加形(rm = dst r10、reg = src2 r11) +// MEM(load=3/store=4 槽寻址):rm_mode 1 [rbp+disp](reg = dst|val r10) +// POOL:load 事件 s1 = 池槽——池 mov([rip+disp] 硬编码辅助——M1 通道,非模板) +// JUMP(5):无 modrm rel32 步(E9——目标 = 事件序) +// IMM(11 cst——注入夹具):/digit + [rbp+disp] + imm(imm 值 = 事件 s1 原值) +HIT_EV_CLS_ALU : int = 0; +HIT_EV_CLS_MEM : int = 1; +HIT_EV_CLS_POOL : int = 2; +HIT_EV_CLS_JUMP : int = 3; +HIT_EV_CLS_IMM : int = 4; + +fn hit_ev_class(ev_id: int, fl: int) -> int { + if ev_id == HIT_EV_SUB || ev_id == HIT_EV_NAND { return HIT_EV_CLS_ALU; } + if ev_id == HIT_EV_LOAD && fl % 2 == 1 { return HIT_EV_CLS_POOL; } + if ev_id == HIT_EV_LOAD || ev_id == HIT_EV_STORE { return HIT_EV_CLS_MEM; } + if ev_id == HIT_EV_JUMP { return HIT_EV_CLS_JUMP; } + if ev_id == 11 { return HIT_EV_CLS_IMM; } // cst 夹具(注入测试载体) + return -1; } // 事件 6-9 = Task 3 前拒绝(无语义类 → 落旧路径) + +// 形态(proj)选择:类按所需形态逐 proj 选首个匹配(条目序 = 优先序—— +// 槽寻址 = rm_mode 1/2 形态、寄存器对 = 0;jump 取首个投影)。st = 选中 +// proj 的步 0 记录(108B)。返回 proj 下标;-1 = 无匹配形态('no projection +// for shape'——预检落旧路径)。 +fn hit_ev_proj_pick(es: int, cls: int, st: string) -> int { + pc := hit_proj_count(es); + pi : ., mut = 0; + loop { + if pi >= pc { break; } + if hit_proj_step_get(es, pi, 0, st) != 0 { return -1; } + rm_mode := hit_r32(st, HIT_ST_OFF_RM_MODE); + if cls == HIT_EV_CLS_ALU && rm_mode == 0 { return pi; } + if cls == HIT_EV_CLS_MEM && (rm_mode == 1 || rm_mode == 2) { return pi; } + if cls == HIT_EV_CLS_IMM && rm_mode == 1 { return pi; } + if cls == HIT_EV_CLS_JUMP { return pi; } + pi = pi + 1; } + return -1; } + +// 模板字节头:prefix → rex → opcode(v2 字段区逐段;st = 108B 步记录)。 +// 返回字节数。 +fn hit_st_head(st: string, buf: string, pos: int) -> int { + cp : ., mut = 0; + p0 := hit_r32(st, HIT_ST_OFF_PFX0); + if p0 != 0 { e2_w8(buf, pos + cp, p0); cp = cp + 1; } + p1 := hit_r32(st, HIT_ST_OFF_PFX1); + if p1 != 0 { e2_w8(buf, pos + cp, p1); cp = cp + 1; } + rx := hit_r32(st, HIT_ST_OFF_REX); + if rx != 0 { e2_w8(buf, pos + cp, rx); cp = cp + 1; } + nop := hit_r32(st, HIT_ST_OFF_OPN); + j : ., mut = 0; + loop { + if j >= nop { break; } + if j == 0 { e2_w8(buf, pos + cp, hit_r32(st, HIT_ST_OFF_OPB0)); } + if j == 1 { e2_w8(buf, pos + cp, hit_r32(st, HIT_ST_OFF_OPB1)); } + if j == 2 { e2_w8(buf, pos + cp, hit_r32(st, HIT_ST_OFF_OPB2)); } + cp = cp + 1; + j = j + 1; } + return cp; } + +// rm_mode 1(base = rbp)的 modrm + disp 发射:宽度按步 disp_size 字段 +//(0 = auto——槽偏移幅度选 disp8|32,与旧路径 e2_ld/e2_st 同规则 ④)。 +// 返回字节数;宽度不符(disp8 越界)= -1。 +fn hit_st_modrm_disp(st: string, buf: string, pos: int, reg_lo: int, disp_val: int) -> int { + dsz := hit_r32(st, HIT_ST_OFF_DISP_SIZE); + cp : ., mut = 0; + if dsz == 1 { + if disp_val < -128 || disp_val > 127 { return -1; } + cp = cp + emit_modrm(buf, pos + cp, 1, reg_lo, 5); + e2_w8(buf, pos + cp, disp_val); + cp = cp + 1; } + else if dsz == 4 { + cp = cp + emit_modrm(buf, pos + cp, 2, reg_lo, 5); + cp = cp + e2_w32(buf, pos + cp, disp_val); } + else { + if disp_val >= -128 && disp_val <= 127 { + cp = cp + emit_modrm(buf, pos + cp, 1, reg_lo, 5); + e2_w8(buf, pos + cp, disp_val); + cp = cp + 1; } + else { + cp = cp + emit_modrm(buf, pos + cp, 2, reg_lo, 5); + cp = cp + e2_w32(buf, pos + cp, disp_val); } } + return cp; } + +// 步 imm 发射(imm_size 1/2/4/8;值由调用方解析——lit 取模板字面量、操作数 +// 角色取事件域原值)。返回字节数。 +fn hit_st_imm(st: string, buf: string, pos: int, imm_val: int) -> int { + isz := hit_r32(st, HIT_ST_OFF_IMM_SIZE); + if isz == 0 { return 0; } + if isz == 1 { e2_w8(buf, pos, imm_val); return 1; } + if isz == 2 { e2_w8(buf, pos, imm_val); e2_w8(buf, pos + 1, imm_val / 256); return 2; } + if isz == 4 { e2_w32(buf, pos, imm_val); return 4; } + e2_w64(buf, pos, imm_val); + return 8; } + +// 步 rel 发射 + 回填登记(kind 0 = 事件流位置——pos = rel 字段偏移;宽 = 32)。 +// 返回字节数;kind/宽度非本任务面 = -1(预检已拦——防御)。 +fn hit_st_rel_emit(st: string, buf: string, pos: int, target: int) -> int { + rk := hit_r32(st, HIT_ST_OFF_REL_KIND); + if rk == 0 { return 0; } + rs := hit_r32(st, HIT_ST_OFF_REL_SIZE); + if rk == HIT_REL_EVENT && rs == 32 { + e2_w32(buf, pos, 0); // 占位——elf.cr 发射后按事件位置表回填 + hit_rel_add(0, pos, target); + return 4; } + return -1; } + +// 单条事件预检(发射前整条指令全过才落字节;任一不支持 → 整条落旧路径, +// 不产生半写)。Returns 0 = 可发射;1 = 不支持。 +fn hit_ev_preflight_ok(ev_i: int) -> int { + ev_id := hit_ev_id(ev_i); + d := hit_ev_dst(ev_i); + s1 := hit_ev_s1(ev_i); + s2 := hit_ev_s2(ev_i); + fl := hit_ev_flags(ev_i); + es := hit_event_lookup(ev_id); + if es < 0 { return 1; } // 表在但事件缺 → 保守落旧路径 + cls := hit_ev_class(ev_id, fl); + if cls < 0 { return 1; } // 事件 6-9 等 = 无语义类 → 拒绝(Task 3 前保持) + if cls == HIT_EV_CLS_POOL { + // 池载入:load 模板 proj0 视区(= hit_ev_emit_pool_mov 同源形态) + if d < 0 { return 1; } + if s1 < 0 || s1 >= g_hit_pool_count { return 1; } + stx := alloc(HIT_STEP_REC); + if hit_ev_step_of(ev_id, stx) != 0 { return 1; } + rm_mode := hit_r32(stx, HIT_ST_OFF_RM_MODE); + rr := hit_r32(stx, HIT_ST_OFF_REG_ROLE); + mr := hit_r32(stx, HIT_ST_OFF_RM_ROLE); + if rm_mode != 1 || rr != HIT_ROLE_DST || mr != HIT_ROLE_ADDR { return 1; } + if hit_role_reg_low(rr) < 0 { return 1; } + return 0; } + st := alloc(HIT_STEP_REC); + pj := hit_ev_proj_pick(es, cls, st); + if pj < 0 { return 1; } // 'no projection for shape' + if hit_proj_step_count(es, pj) != 1 { return 1; } // 多步序列 = Task 3/6 + rm_mode := hit_r32(st, HIT_ST_OFF_RM_MODE); + rr := hit_r32(st, HIT_ST_OFF_REG_ROLE); + mr := hit_r32(st, HIT_ST_OFF_RM_ROLE); + // ── 步字段支持门(本任务发射面外 → 保守落旧路径)── + if hit_r32(st, HIT_ST_OFF_OPN) < 1 { return 1; } // opcode 必选(loader 已验——防御) + if hit_r32(st, HIT_ST_OFF_SIB_SCALE) != 0 || rm_mode == 2 { return 1; } // SIB = M2c 批 3 + if hit_r32(st, HIT_ST_OFF_BASE_ROLE) != 0 { return 1; } // 非 rbp 基(rip/槽值寄存器 = 批 4/3) + if hit_r32(st, HIT_ST_OFF_REG_ROLE) >= HIT_REG_BASE { return 1; } // 寄存器字面量角色 = M2c + if hit_r32(st, HIT_ST_OFF_RM_ROLE) >= HIT_REG_BASE { return 1; } + if hit_r32(st, HIT_ST_OFF_IMM_KIND) != 0 { return 1; } // imm64 fnaddr 回填 = M2c 批 2 + if hit_r32(st, HIT_ST_OFF_REL_SIZE) == 8 { return 1; } // rel8 = Task 12(批 2 G2) + if hit_r32(st, HIT_ST_OFF_COND_ROLE) != 0 { return 1; } // cond 发射 = Task 3 + if hit_r32(st, HIT_ST_OFF_REL_KIND) == HIT_REL_FUNC || + hit_r32(st, HIT_ST_OFF_REL_KIND) == HIT_REL_EXTERN { return 1; } // Task 4/5 + if cls == HIT_EV_CLS_JUMP { + // 无条件转移:E9 rel32——目标 = 事件序(s1);dst/s2 未用(恒 0) + if d != 0 || s2 != 0 { return 1; } + if fl != 0 { return 1; } + if s1 < 0 || s1 >= g_hit_ev_count { return 1; } + if rr != 0 || mr != 0 { return 1; } // 无 modrm 形态 + if hit_r32(st, HIT_ST_OFF_REL_KIND) != HIT_REL_EVENT { return 1; } + return 0; } + if cls == HIT_EV_CLS_ALU { + // 双寄存器累加形(sub/nand…):rm = dst 累加(r10)、reg = src2(r11) + if mr != HIT_ROLE_DST { return 1; } + if rr != HIT_ROLE_SRC2 { return 1; } + if hit_role_reg_low(rr) < 0 || hit_role_reg_low(mr) < 0 { return 1; } + if d < 0 { return 1; } + if fl % 2 == 0 && s1 < 0 { return 1; } // var 操作数须非负 + if fl / 2 % 2 == 0 && s2 < 0 { return 1; } + if fl / 2 % 2 == 1 { return 1; } // M1:仅首操作数可为池 + return 0; } + if cls == HIT_EV_CLS_IMM { + // cst(注入夹具):/digit + [rbp+disp] + imm——rm = dst 槽、值 = s1 原值 + if d < 0 || s1 < 0 { return 1; } + if fl != 0 { return 1; } + if hit_r32(st, HIT_ST_OFF_REG_IS_DIGIT) != 1 { return 1; } + if hit_r32(st, HIT_ST_OFF_REG_DIGIT) > 7 { return 1; } + if mr != HIT_ROLE_DST { return 1; } + if hit_r32(st, HIT_ST_OFF_IMM_SIZE) == 0 { return 1; } + if hit_r32(st, HIT_ST_OFF_REL_KIND) != 0 { return 1; } + return 0; } + // MEM(load/store 槽寻址):reg 角色 = dst(load)或 val(store)——rm 侧 = addr + if mr != HIT_ROLE_ADDR { return 1; } + if hit_role_reg_low(rr) < 0 { return 1; } + if rr == HIT_ROLE_DST { // load:读 [addr槽] → dst 槽 + if d < 0 { return 1; } + if fl % 2 == 1 { return 1; } // 池 load = C_POOL(上已走) + if hit_ev_slot_addr_ok(s1) == 0 { return 1; } + return 0; } + if rr == HIT_ROLE_VAL { // store:写 [addr槽] ← val + if fl % 2 == 1 { return 1; } // 写池 = 无意义(池只读)——保守 + if hit_ev_slot_addr_ok(s1) == 0 { return 1; } + if s2 < 0 { return 1; } + return 0; } + return 1; } + +// 池值 → 寄存器:mov r64, [rip+disp32]——load 事件模板字节 + mod=00(rm=101 = +// rip 相对;disp 由 elf.cr 于 rodata 定稿后按池槽回填)。reg 低 3 位 = 目标。 +// 返回写入字节数(恒 7);load 事件形态不符 = -1(调用方整条落旧路径)。 +fn hit_ev_emit_pool_mov(buf: string, pos: int, reg_low: int, k: int) -> int { + st := alloc(HIT_STEP_REC); + if hit_ev_step_of(HIT_EV_LOAD, st) != 0 { return -1; } + rm_mode := hit_r32(st, HIT_ST_OFF_RM_MODE); + rr := hit_r32(st, HIT_ST_OFF_REG_ROLE); + mr := hit_r32(st, HIT_ST_OFF_RM_ROLE); + if rm_mode != 1 || rr != HIT_ROLE_DST || mr != HIT_ROLE_ADDR { return -1; } + rl := hit_role_reg_low(rr); + if rl != reg_low { return -1; } // 目标寄存器须与 load 模板 reg 角色一致 + cp : ., mut = pos; + e2_w8(buf, cp, hit_r32(st, HIT_ST_OFF_OP0)); cp = cp + 1; + op1 := hit_r32(st, HIT_ST_OFF_OP1); + if op1 != 0 { e2_w8(buf, cp, op1); cp = cp + 1; } + cp = cp + emit_modrm(buf, cp, 0, reg_low, 5); + e2_w32(buf, cp, 0); cp = cp + 4; + hit_pool_patch_add(pos, k); + return cp - pos; } + +// 编码单条事件 → 字节(预检已过;此处仍防御性 -1)。 +fn hit_ev_emit_one(ev_i: int, buf: string, pos: int) -> int { + ev_id := hit_ev_id(ev_i); + d := hit_ev_dst(ev_i); + s1 := hit_ev_s1(ev_i); + s2 := hit_ev_s2(ev_i); + fl := hit_ev_flags(ev_i); + es := hit_event_lookup(ev_id); + if es < 0 { return -1; } + cls := hit_ev_class(ev_id, fl); + if cls < 0 { return -1; } + st := alloc(HIT_STEP_REC); + cp : ., mut = 0; + if cls == HIT_EV_CLS_POOL { + // 池载入:mov r10, [rip+池槽](load 模板字节 + rip 回填——M1 通道) + n := hit_ev_emit_pool_mov(buf, pos, hit_role_reg_low(HIT_ROLE_DST), s1); + if n < 0 { return -1; } + cp = cp + n; + cp = cp + e2_st(buf, pos + cp, 10, g2_slot(d)); + return cp; } + if hit_ev_proj_pick(es, cls, st) < 0 { return -1; } + rm_mode := hit_r32(st, HIT_ST_OFF_RM_MODE); + rr := hit_r32(st, HIT_ST_OFF_REG_ROLE); + mr := hit_r32(st, HIT_ST_OFF_RM_ROLE); + if cls == HIT_EV_CLS_JUMP { + // E9 rel32(模板头)+ rel 登记(目标 = 事件序 s1——elf.cr 按位置表回填) + hd := hit_st_head(st, buf, pos + cp); + if hd < 0 { return -1; } + cp = cp + hd; + n2 := hit_st_rel_emit(st, buf, pos + cp, s1); + if n2 < 0 { return -1; } + cp = cp + n2; + return cp; } + if cls == HIT_EV_CLS_ALU { + // 双寄存器累加形:rm = dst 累加器(首输入驻)、reg = src2 + r_acc := hit_role_reg(mr); // dst → 10 + r_sec := hit_role_reg(rr); // src2 → 11 + if r_acc < 0 || r_sec < 0 { return -1; } + if fl % 2 == 1 { + // 首输入 = 池:mov r10, [rip+池槽](load 模板字节;预检已验形态) + n3 := hit_ev_emit_pool_mov(buf, pos, hit_role_reg_low(mr), s1); + if n3 < 0 { return -1; } + cp = cp + n3; } + else { + cp = cp + e2_load_var(buf, pos + cp, r_acc, s1); } + cp = cp + e2_load_var(buf, pos + cp, r_sec, s2); + // 指令字节:prefix → rex → opcode 三段(v2 字段区——legacy 视区同源) + hd2 := hit_st_head(st, buf, pos + cp); + if hd2 < 0 { return -1; } + cp = cp + hd2; + // modrm:mod=3(寄存器);reg/rm 低 3 位来自角色(src2→r11=3、dst→r10=2) + cp = cp + emit_modrm(buf, pos + cp, 3, hit_role_reg_low(rr), hit_role_reg_low(mr)); + // 结果回存(先读后写——dst 兼作操作数(add 反减中间值)时读先于写) + cp = cp + e2_st(buf, pos + cp, r_acc, g2_slot(d)); + return cp; } + if cls == HIT_EV_CLS_IMM { + // cst:/digit + [rbp+disp(auto)] + imm——rm = dst 槽、imm 值 = s1 原值 + hd3 := hit_st_head(st, buf, pos + cp); + if hd3 < 0 { return -1; } + cp = cp + hd3; + n4 := hit_st_modrm_disp(st, buf, pos + cp, + hit_r32(st, HIT_ST_OFF_REG_DIGIT), g2_slot(d)); + if n4 < 0 { return -1; } + cp = cp + n4; + iv : ., mut = s1; + if hit_r32(st, HIT_ST_OFF_IMM_ROLE) == HIT_CODE_LIT { + iv = hit_r32(st, HIT_ST_OFF_IMM_LIT); } + n5 := hit_st_imm(st, buf, pos + cp, iv); + if n5 < 0 { return -1; } + cp = cp + n5; + return cp; } + // MEM:reg 角色(dst/val)→ r10(glue 寄存器对同 M1);rm = [rbp+disp 槽] + reg_lo := hit_role_reg_low(rr); + if reg_lo < 0 { return -1; } + r_val := hit_role_reg(rr); + if r_val < 0 { return -1; } + if rr == HIT_ROLE_DST { + // load:读 [addr槽] → dst(disp = addr 槽偏移——auto/1/4 按步字段) + hd4 := hit_st_head(st, buf, pos + cp); + if hd4 < 0 { return -1; } + cp = cp + hd4; + n6 := hit_st_modrm_disp(st, buf, pos + cp, reg_lo, g2_slot(s1)); + if n6 < 0 { return -1; } + cp = cp + n6; + cp = cp + e2_st(buf, pos + cp, r_val, g2_slot(d)); + return cp; } + if rr == HIT_ROLE_VAL { + // store:写 [addr槽] ← val(val 槽值先载入角色寄存器) + cp = cp + e2_load_var(buf, pos + cp, r_val, s2); + hd5 := hit_st_head(st, buf, pos + cp); + if hd5 < 0 { return -1; } + cp = cp + hd5; + n7 := hit_st_modrm_disp(st, buf, pos + cp, reg_lo, g2_slot(s1)); + if n7 < 0 { return -1; } + cp = cp + n7; + return cp; } + return -1; } + +// 表驱动单指令发射。与 emit_instr 同签名;返回写入字节数;-1 = 无事件/不支持。 +// 消费 lower_to_core.cr 的事件流:0 事件 = 非子集指令(旧路径); +// 有事件 = 预检整条 → 逐事件表投影发射(g_hit_tabled_count 计事件条数)。 +fn emit_instr_tabled(instr_idx: int, buf: string, pos: int) -> int { + cnt := hit_ev_map_cnt(instr_idx); + if cnt <= 0 { return -1; } + start := hit_ev_map_start(instr_idx); + e : ., mut = 0; + loop { + if e >= cnt { break; } + if hit_ev_preflight_ok(start + e) != 0 { + // 注入通道(测试专用):预检拒绝 = 文件事件无法经表发射——大声错误 + // (corearch 于发射后按事件计数不符 exit 1——不静默落旧路径掩盖); + // 真实降低路径 = 整条落旧路径(M1 混合模式,不半写)。 + if g_hit_inject_active != 0 { + print("error: HIT events: event "); + print_i(start + e); + print(" ("); print(hit_event_name(hit_event_lookup(hit_ev_id(start + e)))); + println(") not emittable (shape/operands)"); + } + return -1; } + e = e + 1; } + cp : ., mut = 0; + e2 : ., mut = 0; + loop { + if e2 >= cnt { break; } + // 事件序 → 字节位置(M2a Task 2:rel 回填表 kind 0 的目标解析表—— + // 边发射边记;位置 = 事件首字节的绝对 buf 偏移) + hit_ev_pos_set(start + e2, pos + cp); + n := hit_ev_emit_one(start + e2, buf, pos + cp); + if n < 0 { return -1; } // 预检后不可达(防御) + cp = cp + n; + g_hit_tabled_count = g_hit_tabled_count + 1; + e2 = e2 + 1; } + return cp; } diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr new file mode 100644 index 00000000..4a820403 --- /dev/null +++ b/src/arch/x86_64/regalloc.cr @@ -0,0 +1,881 @@ +// === regalloc.cr(架构轴 src/arch/x86_64/——x86 实例化波 1 Task 1 自实例目录迁入)=== +// 编码层资源决策(corearch 侧):寄存器分配数据面(存在区间/版本条目/共存) +// + CAG 分配(alloc_registers)+ 一致性判定(verify/注入钩子)。 +// 2026-09-07 自 opt.cr 按层拆分迁入(迁移段于 corec 侧 opt.cr 切换提交删除; +// 本文件保真搬移——实现与核心注释未改,表述清理见 regalloc-backend 计划 Task 5)。 +// 归位后同进程消费:g_opt_meta 内存态自算 + g_ir_entries 自 corearch 自建 +// (.ccr ENT/opt_meta 不再传输——D-1=Y 拍板,见计划 Task 1 盘点)。 + + +// +// 2026-09-10 内核抽取 Task 1:存在区间/条目数据面 + 共存 + 规则①② 判定 + +// 判定诊断已随判定搬入 src/lattice/ent_kernel.cr(范式无关内核,格层)—— +// 本文件余下 = x86 实例机器侧:CAG alloc_registers(调用内核 +// compute_live_ranges)+ g_opt_meta 写入 + 注入钩子(cir debug 测试通道)。 + + +// GC-1 测试探针(cir --inject-coexist-oob 载体;真实构建路径永不调用): +// entries_coexist 上界防御——e1/e2 = 首/次个越界条目索引(≥ g_entry_count)。 +// 守卫缺失时 accessor 对槽后区域越界读:条目表容量 ≥ 计数、alloc 零初始化 +// → 全零槽 [ls=0, le=0] 与任何区间相交 → 误判共存返回 1(读穿缓冲则崩溃)。 +// 输出 "coexist-oob-guard: <0|1>",r != 0 → 退出码 1(测试断言 rc + 数值)。 +fn inject_coexist_oob() -> int { + e1 := g_entry_count; + e2 := g_entry_count + 1; + r := entries_coexist(0, e1, e2); + print("coexist-oob-guard: "); println(int_str(r)); + if r != 0 { return 1; } + return 0; +} + +// ===== Task 5 测试钩子(cir --check-regalloc 载体专用注入;真实构建路径 +// 永不调用——注入只存在于 cir debug 分支,不触碰生产分配器行为)===== +// 各注入在全部函数范围找目标(不假设 func 0 = 被测 main——按 cwd 解析差异, +// 标准库函数可能并入 IR 使 func 序移位;找「首个满足形状的函数」保证确定性)。 + +// 注入 ①-home 组冲突:取两条不同 var 的共存条目 (e1,e2),先撤销两 var 的 +// 寄存器驻留(meta_remove_var——真实分配已覆盖时 append 伪造对会被既有对遮蔽, +// 撤销使位置判定退化到 home/栈 seam),再置 home = 同一槽 7 → 规则 ① home 组 +// 违反。撤销的 var 无 home 条目(home=-1)不参与 sweep,无侧伤。 +fn try_inject_home_conflict(func_i: int) -> int { + es := entry_start(func_i); + ec := entry_count(func_i); + if ec < 2 { return 0; } + e1i : ., mut = 0; + loop { + if e1i >= ec { break; } + e1 := es + e1i; + v1 := ent_var(e1); + if v1 < 0 { e1i = e1i + 1; continue; } + e2i : ., mut = e1i + 1; + loop { + if e2i >= ec { break; } + e2 := es + e2i; + v2 := ent_var(e2); + if v1 != v2 && entries_coexist(func_i, e1, e2) != 0 { + meta_remove_var(v1); + meta_remove_var(v2); + w32(g_ir_entries, e1 * ESZ_ENTRY + OFF_ENTRY_HOME, 7); + w32(g_ir_entries, e2 * ESZ_ENTRY + OFF_ENTRY_HOME, 7); + return 1; + } + e2i = e2i + 1; + } + e1i = e1i + 1; + } + return 0; +} + +fn inject_home_conflict() -> int { + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + if try_inject_home_conflict(fi) != 0 { return 1; } + fi = fi + 1; + } + return 0; +} + +// ── 元数据写侧辅助(CAG 真实分配后注入以改写真实输出为手段—— +// append 首匹配语义会被既有对遮蔽,注入须原位改写/移除)── + +// 返回 var 的 reg 字段偏移(g_opt_meta 内;-1 = 无对)。与 instr.cr +// get_reg_for_var 同布局(随迁后同进程——见判定区头注)。 +fn meta_reg_pair_off(var_idx: int) -> int { + mi : ., mut = 0; + loop { + if mi >= g_opt_meta_count { break; } + mo := mi * OPT_META_STRIDE; + if r32(g_opt_meta, mo) == OPT_KEY_REG_ASSIGN { + cnt := r32(g_opt_meta, mo + 8); + di : ., mut = 0; + loop { + if di >= cnt { break; } + if r32(g_opt_meta, mo + 12 + di * 8) == var_idx { + return mo + 16 + di * 8; + } + di = di + 1; + } + } + mi = mi + 1; + } + return -1; +} + +// ── 登记表配对辅助(双份同步纪律——内核完备 Task 3)── +// 判定面输入 = 位置登记表(kern_loc_assign/clear/of,ent_kernel.cr);本文件 +// 每 g_opt_meta 写点成对调 kern API——var 级分配/撤销 → 该 var 全部版本条目 +// 逐条登记/清除(与判定侧 meta_reg_for_var「任一已登记条目 = 驻留」投影语义 +// 等价)。配对点全集 = meta_set_reg/meta_remove_var(注入钩子只经此二函数 +// 改写 meta——全部注入路径覆盖,红路径测试 = test_live_ranges +// check_regalloc_violations/read_gap_nonfunc0 经登记通道仍红)+ alloc_registers +// phase 5(原语直写不经辅助函数——单独配对,见 alloc 区尾注记)。 +// 条目的所属函数未知(var_idx = 全局 var id)→ 全表扫描(注入 = cir debug +// 通道,低频小表;生产路径 = phase 5 段界扫描不经本辅助)。 +fn reg_assign_var_entries(var_idx: int, loc: int) { + e : ., mut = 0; + loop { + if e >= g_entry_count { break; } + if ent_var(e) == var_idx { kern_loc_assign(e, loc); } + e = e + 1; + } +} + +fn reg_clear_var_entries(var_idx: int) { + e : ., mut = 0; + loop { + if e >= g_entry_count { break; } + if ent_var(e) == var_idx { kern_loc_clear(e); } + e = e + 1; + } +} + +// 强制 var → reg(已有对原位改写;无对追加——append 语义 = 首匹配生效, +// 无对时追加必然生效) +fn meta_set_reg(var_idx: int, reg: int) { + po := meta_reg_pair_off(var_idx); + if po >= 0 { w32(g_opt_meta, po, reg); } + else { meta_append_reg_assign(var_idx, reg); } + reg_assign_var_entries(var_idx, reg); // 登记表配对(判定面同步) +} + +// 移除 var 的分配对(块内左移收拢;空块整体移除)——「撤销寄存器驻留」= +// 位置判定退化为 home/栈(entries home=-1 → 不参与规则① sweep,无侧伤) +fn meta_remove_var(var_idx: int) { + mi : ., mut = 0; + loop { + if mi >= g_opt_meta_count { break; } + mo := mi * OPT_META_STRIDE; + if r32(g_opt_meta, mo) == OPT_KEY_REG_ASSIGN { + cnt := r32(g_opt_meta, mo + 8); + di : ., mut = 0; + loop { + if di >= cnt { break; } + if r32(g_opt_meta, mo + 12 + di * 8) == var_idx { + // 移除第 di 对:后续对左移 8B(数据区 = count@+8 + 对@+12) + sh : ., mut = di; + loop { if sh + 1 >= cnt { break; } + w32(g_opt_meta, mo + 12 + sh * 8, r32(g_opt_meta, mo + 12 + (sh + 1) * 8)); + w32(g_opt_meta, mo + 16 + sh * 8, r32(g_opt_meta, mo + 16 + (sh + 1) * 8)); + sh = sh + 1; } + cnt = cnt - 1; + w32(g_opt_meta, mo + 8, cnt); + w32(g_opt_meta, mo + 4, 4 + cnt * 8); + if cnt == 0 { + // 空块整体移除:后续块左移一个 OPT_META_STRIDE + mj : ., mut = mi; + loop { if mj + 1 >= g_opt_meta_count { break; } + so := (mj + 1) * OPT_META_STRIDE; + kk : ., mut = 0; + loop { if kk >= OPT_META_STRIDE / 8 { break; } + w64(g_opt_meta, mj * OPT_META_STRIDE + kk * 8, r64(g_opt_meta, so + kk * 8)); + kk = kk + 1; } + mj = mj + 1; } + g_opt_meta_count = g_opt_meta_count - 1; + } + reg_clear_var_entries(var_idx); // 登记表配对(判定面同步) + return; + } + di = di + 1; + } + } + mi = mi + 1; + } +} + +// REG_ASSIGN 对总数(--check-regalloc 看门狗行;rc>0 = 寄存器真实分配实证) +fn meta_reg_assign_total() -> int { + tot : ., mut = 0; + mi : ., mut = 0; + loop { + if mi >= g_opt_meta_count { break; } + mo := mi * OPT_META_STRIDE; + if r32(g_opt_meta, mo) == OPT_KEY_REG_ASSIGN { tot = tot + r32(g_opt_meta, mo + 8); } + mi = mi + 1; + } + return tot; +} + +// 追加一条 OPT_KEY_REG_ASSIGN 记录(格式同 alloc_registers 写侧;首个匹配即 +// 生效——故伪造目标须是真实分配未覆盖的 var,后端 get_reg_for_var 同语义) +fn meta_append_reg_assign(var_idx: int, reg: int) { + grow_opt_meta(g_opt_meta_count + 1); + eo := g_opt_meta_count * OPT_META_STRIDE; + store8(g_opt_meta, eo, 0); store8(g_opt_meta, eo + 1, 0); + store8(g_opt_meta, eo + 2, 0); store8(g_opt_meta, eo + 3, 0); // OPT_KEY_REG_ASSIGN=0 + dl : ., mut = 4 + 8; + store8(g_opt_meta, eo + 4, dl % 256); store8(g_opt_meta, eo + 5, (dl / 256) % 256); + store8(g_opt_meta, eo + 6, (dl / 65536) % 256); store8(g_opt_meta, eo + 7, (dl / 16777216) % 256); + store8(g_opt_meta, eo + 8, 1); store8(g_opt_meta, eo + 9, 0); + store8(g_opt_meta, eo + 10, 0); store8(g_opt_meta, eo + 11, 0); // count = 1 + store8(g_opt_meta, eo + 12, var_idx % 256); store8(g_opt_meta, eo + 13, (var_idx / 256) % 256); + store8(g_opt_meta, eo + 14, (var_idx / 65536) % 256); store8(g_opt_meta, eo + 15, (var_idx / 16777216) % 256); + store8(g_opt_meta, eo + 16, reg % 256); store8(g_opt_meta, eo + 17, (reg / 256) % 256); + store8(g_opt_meta, eo + 18, (reg / 65536) % 256); store8(g_opt_meta, eo + 19, (reg / 16777216) % 256); + g_opt_meta_count = g_opt_meta_count + 1; +} + +// 注入 ①-寄存器组冲突:找一对不同 var 的共存条目 (e1, e2),把两 var 的 +// REG_ASSIGN 原位改写为同一 reg 3(已有对改写 / 无对追加——真实分配覆盖与否 +// 均确定生效;共存条目同组同寄存器 → 规则 ① 寄存器组违反)。 +// 注入仅存在于 cir debug 分支(真实构建路径永不注入)。 +fn try_inject_reg_conflict(func_i: int) -> int { + es := entry_start(func_i); + ec := entry_count(func_i); + if ec < 2 { return 0; } + e1i : ., mut = 0; + loop { + if e1i >= ec { break; } + e1 := es + e1i; + v1 := ent_var(e1); + if v1 < 0 { e1i = e1i + 1; continue; } + e2i : ., mut = e1i + 1; + loop { + if e2i >= ec { break; } + e2 := es + e2i; + v2 := ent_var(e2); + if v1 != v2 && entries_coexist(func_i, e1, e2) != 0 { + meta_set_reg(v1, 3); + meta_set_reg(v2, 3); + return 1; + } + e2i = e2i + 1; + } + e1i = e1i + 1; + } + return 0; +} + +fn inject_reg_conflict() -> int { + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + if try_inject_reg_conflict(fi) != 0 { return 1; } + fi = fi + 1; + } + return 0; +} + +// 注入 ②-读点空洞:找 var——其最后一个版本条目(def ≥ 0 中 def 最大) +// live_end > def(定值后仍有读)——把该 var 置为寄存器驻留并把该版本 live_end +// 截断为 def → 其后的读点无活跃版本覆盖 → 规则 ② 违反。 +// 真实分配下:目标 var 已被分配 → 保持其原 reg(规则① 零扰动)直接截断即红; +// 未分配(rc=0 时代/栈驻留 var)→ 强制 reg 3——先把与目标共存且已分配的 var +// 全部撤销(防强制 reg 引发规则① 侧伤;撤销 var 无位置参与 sweep,无新违反)。 +fn try_inject_read_gap(func_i: int) -> int { + vc := r64(g_ir_func_var_count, func_i * 8); + vs := r64(g_ir_func_var_start, func_i * 8); + es := entry_start(func_i); + ec := entry_count(func_i); + lv : ., mut = 0; + loop { + if lv >= vc { break; } + gv := vs + lv; + // 该 var 最后一条 def ≥ 0 条目(表内定值点升序 → 末条即 def 最大) + last : ., mut = -1; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_var(e) == gv && ent_def(e) >= 0 { last = e; } + ei = ei + 1; + } + if last >= 0 && ent_live_end(last) > ent_def(last) { + // 探针 = 读 meta_reg_for_var(内核完备 Task 3 后 = 登记表读——探针 + // 与 verify 读同一判定输入通道——单真源选择,理由注记:registry 由 + // 实例配对写点(alloc phase 5 + meta_set_reg/meta_remove_var)保持 + // 与 g_opt_meta 同步——本注入的撤销/强制驻留经配对辅助,探针读 = + // g_opt_meta 的一致视图,无需实例侧私有直扫) + if meta_reg_for_var(gv) < 0 { + // 强制驻留路径:撤销与 gv 任何版本条目共存的已分配 var + ej : ., mut = 0; + loop { + if ej >= ec { break; } + e2 := es + ej; + v2 := ent_var(e2); + if v2 != gv && meta_reg_for_var(v2) >= 0 { + ek : ., mut = 0; + loop { + if ek >= ec { break; } + if ent_var(es + ek) == gv && + entries_coexist(func_i, es + ek, e2) != 0 { + meta_remove_var(v2); + break; + } + ek = ek + 1; + } + } + ej = ej + 1; + } + meta_set_reg(gv, 3); + } + w32(g_ir_entries, last * ESZ_ENTRY + OFF_ENTRY_LE, ent_def(last)); + return 1; + } + lv = lv + 1; + } + return 0; +} + +fn inject_read_gap() -> int { + // 函数迭代序 = 1..n-1 再 0:优先注入非 func 0 目标(F1 回归前提——规则 ② 的 + // 坐标失明面只在 ist > 0 的函数上显形)。GC 批 2 后 _arena 等每函数首条 + // def 使 func 0 恒可注入——若仍从 0 扫起,注入恒落 main,非 func 0 面失守。 + fi : ., mut = 1; + loop { + if fi >= g_ir_func_count { break; } + if try_inject_read_gap(fi) != 0 { return 1; } + fi = fi + 1; + } + return try_inject_read_gap(0); +} + +// ------------------------------------------------------------------ +// Register allocation — CAG 上下文贪心(docs/regalloc-cache-mapping.md §五 定案) +// ------------------------------------------------------------------ +// 语义:寄存器文件 = 缓存层、栈槽 = home 的映射实例(缓存语义条款 7)。 +// 本实现 = 存在结构上 First Fit(指令序扫描 = CFG 退化实例)+ 上下文修正: +// 共存(窗口互斥)、region 生命周期(回边携带值整函数窗口)、调用点 +// (callee-saved 专用——ABI 保留契约,判定 ④ 平凡满足)、使用次数/门控。 +// 结果写 g_opt_meta 的 OPT_KEY_REG_ASSIGN 对(var_idx → 物理 reg);同进程 +// instr.cr g2_slot/get_reg_for_var 在发射时按对把 var 落寄存器(E2_REG_SLOT_BASE +// 正哨兵)——判定侧解析同布局同源消费(regalloc 移后端后无跨进程镜像)。 +// 旧「负编码改写 IR 操作数」是过时设计(见判定区头注记),现 seam = meta 表 +// + 后端查询。 +// +// 分配粒度决策(var 级):条目表(compute_entries)是版本级,但消费 seam 是 +// var 级单位置——meta 每 var 一条对、后端按 var 无指令上下文查询(迁移前 +// .ccr 同构传输,现 corearch 进程内自算)。版本级分配(同 var 不同版本不同 +// 位置)在该 seam 不可表达(需逐指令 +// 操作数改写 + 后端带指令上下文的槽解析,为未来升级点)。故分配粒度 = var +// 全窗口 [first_ref,last_ref]——该 var 各版本条目存在区间之并(版本按定值点 +// 无缝切割,见 compute_entries);判定按 var 投影到版本条目(衔接决策 b), +// 分配器将来升版本级时判定零改动。 +// +// rc=0 缺陷根因(评审披露,本重写修复):旧实现 free 遍(每次指令迭代前 +// last_ref < ii 复位)+ reg_idx 单调不复用 + 循环末只把「末指令仍活跃」var 写 +// meta → 实测 g_opt_meta 恒无 REG_ASSIGN 对 → 后端全栈发射(正确但零加速)。 +// 本实现:活跃集合按窗口终点正确归还(末引用之后释放、同位置先 free 后分配, +// 允许寄存器复用)、全部已分配对函数末一次性落 meta。 +// +// 门控(发射器路径审计——寄存器驻留 var 的每次读写必须走 BASE-aware 装载 +// e2_ld/e2_st/e2_load_var;直接内存操作数/槽地址/双槽偏移发射在 reg 哨兵槽上 +// 编码垃圾): +// - 类型门:TI_DEX / TI_DEX_S(XMM 内存路径 e2_sd_*/e2_sd_cvt)、TI_DYN +// (16B 双槽 value+tag,e2_ld(o±8) 偏移错位)、TI_UNIT 不分配; +// - IR_CONST 的 dest(e2_li 无 reg 槽分支); +// - IR_REF 的 s1(e2_lb 取 home 槽地址——地址泄漏 = 别名入口,必须留 home); +// - IR_I2F 的 s1(e2_sd_cvt 内存源操作数); +// - ti==TI_DEX/S 指令的引用 var(防御,类型门已覆盖)。 +// 排除 var = 全窗口栈驻留(frame slot home 保留)。寄存器不足(>5 共存)时 +// 静态放置失败 = 不放置(无动态驱逐代码、无 spill 指令):无配方条目条款 +// (memory-model-capability-lattice §5.3「必须有 home」)因从未放置而平凡满足 +// ——判定 ③ 驱逐配对无事件、④ 调用点契约 = callee-saved 保留平凡成立。 +// caller-saved 解锁(调用点失效 + 跨调用点 spill/重载)留注记下批(§五 谱系 +// 调用点上下文项):现仅用 callee-saved,调用点判定平凡真。 +// +// region 生命周期上下文(回边携带值):朴素文字窗口 [first_ref,last_ref] 在 +// 含回边函数上不安全——循环携带值(读点文字序先于其重定值,如 cond 读 i 在 +// i=i+1 前)跨回边存活,窗口止于末引用会把 reg 让给循环尾文字区间不交的 var +// → 定值污染(test_live_ranges LOOP_CARRY_SRC 语义锚)。判定:对每个回边 +// span [label_pos, branch_pos],var 在 span 内首个引用为读(非定值形式)→ +// 携带 → 窗口扩至整函数。 +// 条件定值健全化(复审 Critical,2026-09-06 修复):def-form 首引用 ≠ 每轮 +// 必刷新——「首引用为定值 ⇒ 不携带」只在定值无条件每轮执行时成立。定值点 d +// 位于 span 内条件区域(∃ BRANCH b ∈ [t0, d),目标 label t ∈ (d, b0]——跳过 +// d 后仍在 span 内继续循环)时,跳过路径上旧值跨回边存活(下轮读污染值, +// v_noz/conddef_repro3 确定性误编译,O2 专属)。此类定值按携带处理(窗口 +// 扩至整函数)。另:dest/STORE-s1 定值若同指令自读(s1/s2 == 目标,读先于 +// 写)→ 该读本身就是跨回边读 → 亦按携带(防御性统一,现 IR 未见此形态—— +// x=x+1 的读在独立 BINARY 指令上,首引用已是读形式)。 +// 残余近似:非 span 首引用读点 + 跨 span 复杂路径需真 CFG 活性(RegionCheck +// 图层 = docs/regalloc-cache-mapping.md §三 定案升级点,未接线;判定与分配器 +// 同文字区间模型——条件定值携带语义的判定侧同步 = 该升级点前的一贯挂账, +// 回归语义锚 = test_live_ranges COND_DEF_CARRY 源,见报告 cag-report.md)。 + +fn label_pos_of(id: int, lab_id: string, lab_ps: string, n: int) -> int { + i : ., mut = 0; + loop { + if i >= n { break; } + if r64(lab_id, i * 8) == id { return r64(lab_ps, i * 8); } + i = i + 1; + } + return -1; +} + +fn alloc_registers() { + if g_opt_level < 1 { return; } + // v6 数据基础:存在区间表先行(尾部重建版本条目表——判定消费同源数据) + compute_live_ranges(); + MAX_REGS : int = 5; + // callee-saved 物理寄存器(rbx, r12-r15;x86 枚举号,e2_mov/e2_ld REX 编码) + reg_phys : string, mut = alloc(5 * 8); + w64(reg_phys, 0, 3); w64(reg_phys, 8, 12); + w64(reg_phys, 16, 13); w64(reg_phys, 24, 14); w64(reg_phys, 32, 15); + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + ic := r64(g_ir_func_instr_count, fi * 8); + ist := r64(g_ir_func_instr_start, fi * 8); + vc := r64(g_ir_func_var_count, fi * 8); + vs := r64(g_ir_func_var_start, fi * 8); + pc := r64(g_ir_func_param_count, fi * 8); + if vc <= 0 || ic <= 0 { fi = fi + 1; continue; } + + // ── 阶段 1:门控扫描(并行 hazard 标志)── + haz_const : string, mut = alloc(vc * 8); // IR_CONST dest(e2_li 写路径) + haz_ref : string, mut = alloc(vc * 8); // IR_REF s1(槽地址泄漏) + haz_i2f : string, mut = alloc(vc * 8); // IR_I2F s1(sd_cvt 内存源) + haz_dyn : string, mut = alloc(vc * 8); // IR_DYN_*(16B 双槽布局) + haz_dex : string, mut = alloc(vc * 8); // ti==TI_DEX/S 指令引用 + iz : ., mut = 0; + loop { + if iz >= vc { break; } + w64(haz_const, iz * 8, 0); w64(haz_ref, iz * 8, 0); w64(haz_i2f, iz * 8, 0); + w64(haz_dyn, iz * 8, 0); w64(haz_dex, iz * 8, 0); + iz = iz + 1; + } + ii : ., mut = 0; + loop { + if ii >= ic { break; } + inst := ist + ii; + op := iri_op(inst); + d := iri_dest(inst); s1 := iri_s1(inst); s2 := iri_s2(inst); + if d >= vs && d < vs + vc { + dl := d - vs; + if op == IR_CONST { w64(haz_const, dl * 8, 1); } + if op == IR_DYN_PACK || op == IR_DYN_TAG || op == IR_DYN_VAL || + op == IR_DYN_DISPATCH { w64(haz_dyn, dl * 8, 1); } + } + if op == IR_REF && s1 >= vs && s1 < vs + vc { w64(haz_ref, (s1 - vs) * 8, 1); } + if op == IR_I2F && s1 >= vs && s1 < vs + vc { w64(haz_i2f, (s1 - vs) * 8, 1); } + if op == IR_DYN_PACK || op == IR_DYN_TAG || op == IR_DYN_VAL || + op == IR_DYN_DISPATCH { + if s1 >= vs && s1 < vs + vc { w64(haz_dyn, (s1 - vs) * 8, 1); } + if s2 >= vs && s2 < vs + vc { w64(haz_dyn, (s2 - vs) * 8, 1); } + } + tk := iri_tk(inst); + if tk == TI_DEX || tk == TI_DEX_S { + if d >= vs && d < vs + vc { w64(haz_dex, (d - vs) * 8, 1); } + if s1 >= vs && s1 < vs + vc { w64(haz_dex, (s1 - vs) * 8, 1); } + if s2 >= vs && s2 < vs + vc { w64(haz_dex, (s2 - vs) * 8, 1); } + } + ii = ii + 1; + } + + // ── 阶段 2:回边探测 + 携带值标记(region 生命周期上下文)── + ln : ., mut = 0; + ii = 0; + loop { if ii >= ic { break; } + if iri_op(ist + ii) == IR_LABEL { if iri_s1(ist + ii) >= 0 { ln = ln + 1; } } + ii = ii + 1; + } + lab_id : string, mut = alloc(ln * 8); + lab_ps : string, mut = alloc(ln * 8); + li : ., mut = 0; + ii = 0; + loop { + if ii >= ic { break; } + if iri_op(ist + ii) == IR_LABEL && iri_s1(ist + ii) >= 0 { + w64(lab_id, li * 8, iri_s1(ist + ii)); + w64(lab_ps, li * 8, ii); + li = li + 1; + } + ii = ii + 1; + } + // 回边 span 收集(16B {label_pos, branch_pos};上限 = 指令数) + bt : string, mut = alloc(ic * 16); + bn : ., mut = 0; + has_loop : ., mut = 0; + ii = 0; + loop { + if ii >= ic { break; } + inst := ist + ii; + op := iri_op(inst); + if op == IR_JUMP { + tp := label_pos_of(iri_s1(inst), lab_id, lab_ps, ln); + if tp >= 0 && tp < ii { + w64(bt, bn * 16, tp); w64(bt, bn * 16 + 8, ii); + bn = bn + 1; has_loop = 1; + } + } else if op == IR_BRANCH { + tp := label_pos_of(iri_s2(inst), lab_id, lab_ps, ln); + if tp >= 0 && tp < ii { + w64(bt, bn * 16, tp); w64(bt, bn * 16 + 8, ii); + bn = bn + 1; has_loop = 1; + } + tp = label_pos_of(iri_s3(inst), lab_id, lab_ps, ln); + if tp >= 0 && tp < ii { + w64(bt, bn * 16, tp); w64(bt, bn * 16 + 8, ii); + bn = bn + 1; has_loop = 1; + } + } + ii = ii + 1; + } + car : string, mut = alloc(vc * 8); + iz = 0; + loop { if iz >= vc { break; } w64(car, iz * 8, 0); iz = iz + 1; } + if has_loop != 0 { + // span 内逐指令扫:var 的 span 内首引用若为读形式 → 值跨回边存活 + // (携带);定值形式(dest / STORE-s1)首引用且每轮必执行 → 每轮 + // 刷新,不携带。状态:0 未见 / 1 首引用 = 定值 / 2 首引用 = 读。 + // 条件定值健全化(评审 Critical):首引用为定值 d 但 d 可被 span 内 + // 条件分支跳过(仍在 span 继续循环)→ 未必每轮刷新 → 同读形式按 + // 携带处理(差分标记 + 前缀累计见下)。 + st : string, mut = alloc(vc * 8); + st_def : string, mut = alloc(vc * 8); // state-1 var 的定值位置 + skp : string, mut = alloc((ic + 2) * 8); // 可跳过位置差分(span 局部序) + bi : ., mut = 0; + loop { + if bi >= bn { break; } + t0 := r64(bt, bi * 16); + b0 := r64(bt, bi * 16 + 8); + iz = 0; + loop { + if iz >= vc { break; } + w64(st, iz * 8, 0); + w64(st_def, iz * 8, -1); + iz = iz + 1; + } + pp : ., mut = t0; + loop { + if pp > b0 { break; } + inst := ist + pp; + op := iri_op(inst); + d := iri_dest(inst); s1 := iri_s1(inst); s2 := iri_s2(inst); + if d >= vs && d < vs + vc { + lvd := d - vs; + if r64(st, lvd * 8) == 0 { + // dest 定值同指令自读(s1/s2 == d):读先于写,该读 + // 可能取上一轮值 → 按读首引用(携带)处理 + if s1 == d || s2 == d { + w64(st, lvd * 8, 2); + w64(car, lvd * 8, 1); + } else { + w64(st, lvd * 8, 1); + w64(st_def, lvd * 8, pp); + } + } + } + if op == IR_STORE && s1 >= vs && s1 < vs + vc { + lvs := s1 - vs; + if r64(st, lvs * 8) == 0 { + if s2 == s1 { + w64(st, lvs * 8, 2); + w64(car, lvs * 8, 1); + } else { + w64(st, lvs * 8, 1); + w64(st_def, lvs * 8, pp); + } + } + } + if s1 >= vs && s1 < vs + vc && op != IR_STORE { + lv1 := s1 - vs; + if r64(st, lv1 * 8) == 0 { + w64(st, lv1 * 8, 2); + w64(car, lv1 * 8, 1); + } + } + if s2 >= vs && s2 < vs + vc { + lv2 := s2 - vs; + if r64(st, lv2 * 8) == 0 { + w64(st, lv2 * 8, 2); + w64(car, lv2 * 8, 1); + } + } + pp = pp + 1; + } + // 条件定值健全化——差分标记:BRANCH(pp)的 forward 目标 + // t ∈ (pp, b0] ⇒ 位置 (pp, t) 可被跳过且循环仍继续(区间 + // [pp+1, t−1] 覆盖 +1,终点 t 处 −1);前缀累计 > 0 ⟺ 该位置 + // 落在某可跳过区域内。回跳/外跳目标(≤ pp 或 > b0)不构成 + // 「跳过仍继续循环」,不计。 + // 第二轮健全化(复审 Critical 残留):if-else 布局 + // BRANCH c, L_then, L_else; L_then: A; JUMP L_merge; + // L_else: B; JUMP L_merge; + // 中 else 体 B 被 then 尾无条件 JUMP 结构性跳过——B 内定值 + // 正落在 BRANCH 自身 else 目标的标号处(区间 [pp+1, t−1] + // 不含终点 t),首轮只扫 IR_BRANCH 漏 IR_JUMP → B 内 def 判 + // 「每轮必执行」→ 同寄存器污染。对 span 内 forward IR_JUMP + // (pp→t) 同样生成 [pp+1, t−1] 差分:跳过仍继续循环的路径 + // 等价(continue 前向形态同理;回跳 continue/break 外跳因 + // 目标 ≤ pp 或 > b0 不计)。over-mark 只损利用率、方向安全。 + zz : ., mut = 0; + spn := b0 - t0 + 1; + loop { if zz >= spn { break; } w64(skp, zz * 8, 0); zz = zz + 1; } + pp = t0; + loop { + if pp > b0 { break; } + if iri_op(ist + pp) == IR_BRANCH { + t2 := label_pos_of(iri_s2(ist + pp), lab_id, lab_ps, ln); + t3 := label_pos_of(iri_s3(ist + pp), lab_id, lab_ps, ln); + if t2 > pp && t2 <= b0 { + w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1); + w64(skp, (t2 - t0) * 8, r64(skp, (t2 - t0) * 8) - 1); + } + if t3 > pp && t3 <= b0 { + w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1); + w64(skp, (t3 - t0) * 8, r64(skp, (t3 - t0) * 8) - 1); + } + } else if iri_op(ist + pp) == IR_JUMP { + t1 := label_pos_of(iri_s1(ist + pp), lab_id, lab_ps, ln); + if t1 > pp && t1 <= b0 { + w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1); + w64(skp, (t1 - t0) * 8, r64(skp, (t1 - t0) * 8) - 1); + } + } + pp = pp + 1; + } + // 前缀累计:skp[p−t0] > 0 ⟺ 位置 p 可被某条件分支跳过 + acc : ., mut = 0; + pp = t0; + loop { + if pp > b0 { break; } + acc = acc + r64(skp, (pp - t0) * 8); + if acc != 0 { w64(skp, (pp - t0) * 8, 1); } + else { w64(skp, (pp - t0) * 8, 0); } + pp = pp + 1; + } + // state-1(首引用 = 定值)var:定值点可被跳过 → 条件定值, + // 每轮未必刷新 → 旧值跨回边存活 → 携带(窗口扩至整函数) + iz = 0; + loop { + if iz >= vc { break; } + if r64(st, iz * 8) == 1 { + dd := r64(st_def, iz * 8); + if dd >= t0 && r64(skp, (dd - t0) * 8) != 0 { + w64(car, iz * 8, 1); + } + } + iz = iz + 1; + } + bi = bi + 1; + } + } + + // ── 阶段 3:候选构建(窗口 = [first_ref,last_ref] + 上下文修正)── + var_reg : string, mut = alloc(vc * 8); // 活跃集:函数内 var → reg 序 (0..4) + var_end : string, mut = alloc(vc * 8); // 活跃集窗口终点(free 判据—— + // 携带值扩窗后 ≠ live_last,须存) + var_asg : string, mut = alloc(vc * 8); // 终分配:函数内 var → 物理 reg + reg_occ : string, mut = alloc(MAX_REGS * 8); + cand_st : string, mut = alloc(vc * 8); + cand_en : string, mut = alloc(vc * 8); + cand_lv : string, mut = alloc(vc * 8); + lv : ., mut = 0; + loop { if lv >= vc { break; } + w64(var_reg, lv * 8, -1); w64(var_end, lv * 8, 0); w64(var_asg, lv * 8, -1); + lv = lv + 1; + } + lv = 0; + loop { if lv >= MAX_REGS { break; } w64(reg_occ, lv * 8, -1); lv = lv + 1; } + cc : ., mut = 0; + lv = 0; + loop { + if lv >= vc { break; } + gv := vs + lv; + f := live_first(fi, gv); + l := live_last(fi, gv); + if f < 0 || l <= f { lv = lv + 1; continue; } // 未用 / 单指令引用 + ty := irv_type(gv); + if ty == TI_DEX || ty == TI_DEX_S || ty == TI_DYN || ty == TI_UNIT { + lv = lv + 1; continue; + } + if r64(haz_const, lv * 8) != 0 || r64(haz_ref, lv * 8) != 0 || + r64(haz_i2f, lv * 8) != 0 || r64(haz_dyn, lv * 8) != 0 || + r64(haz_dex, lv * 8) != 0 { + lv = lv + 1; continue; + } + st : ., mut = f; + en : ., mut = l; + if lv < pc { st = 0; } // 参数入口装载 → 窗口自函数头 + if r64(car, lv * 8) != 0 { st = 0; en = ic - 1; } // 携带值 → 整函数 + w64(cand_st, cc * 8, st); + w64(cand_en, cc * 8, en); + w64(cand_lv, cc * 8, lv); + cc = cc + 1; + lv = lv + 1; + } + // 候选按 (start, lv) 稳定升序(插入排序;起点 = 指令位置,C ≤ vc 小集) + ci2 : ., mut = 1; + loop { + if ci2 >= cc { break; } + ks := r64(cand_st, ci2 * 8); + kl := r64(cand_lv, ci2 * 8); + ke := r64(cand_en, ci2 * 8); + jj : ., mut = ci2; + loop { + if jj <= 0 { break; } + pj := jj - 1; + ps := r64(cand_st, pj * 8); + pl := r64(cand_lv, pj * 8); + if ps < ks || (ps == ks && pl < kl) { break; } + w64(cand_st, jj * 8, ps); + w64(cand_en, jj * 8, r64(cand_en, pj * 8)); + w64(cand_lv, jj * 8, pl); + jj = jj - 1; + } + w64(cand_st, jj * 8, ks); + w64(cand_en, jj * 8, ke); + w64(cand_lv, jj * 8, kl); + ci2 = ci2 + 1; + } + + // ── 阶段 4:位置扫描 First Fit(同位置先 free 后分配)── + ci : ., mut = 0; + p : ., mut = 0; + loop { + if p >= ic { break; } + // free:窗口终点(分配时记入 var_end;携带值扩窗 ≠ live_last)已过 → + // 归还寄存器。rc=0 根因修复点:free 发生在末引用之后、寄存器按窗口 + // 终点正确复用(旧实现 free 后 meta 收集丢失 + reg 序单调不复用)。 + lv2 : ., mut = 0; + loop { + if lv2 >= vc { break; } + rr := r64(var_reg, lv2 * 8); + if rr >= 0 && r64(var_end, lv2 * 8) < p { + w64(var_reg, lv2 * 8, -1); + w64(reg_occ, rr * 8, -1); + } + lv2 = lv2 + 1; + } + // assign:起点 == p 的候选(窗口终点 = p−1 的 var 已先 free → 可复用 + // 同寄存器;终点恰 = p 的 var 仍活跃于 p,不释放——同指令共存条目 + // 不得同寄存器) + loop { + if ci >= cc { break; } + if r64(cand_st, ci * 8) != p { break; } + lvv := r64(cand_lv, ci * 8); + eend := r64(cand_en, ci * 8); + ci = ci + 1; + rj : ., mut = 0; + tk : ., mut = -1; + loop { + if rj >= MAX_REGS { break; } + if r64(reg_occ, rj * 8) < 0 { tk = rj; break; } + rj = rj + 1; + } + if tk >= 0 { + w64(var_reg, lvv * 8, tk); + w64(var_end, lvv * 8, eend); + w64(reg_occ, tk * 8, lvv); + w64(var_asg, lvv * 8, r64(reg_phys, tk * 8)); + } + } + p = p + 1; + } + + // ── 阶段 5:终分配写 g_opt_meta ── + // 块格式(ast.cr OPT_META_STRIDE=64 = 头 8B + 数据 ≤56B):每块 ≤5 对 + // (data_len = 4 + n×8 ≤ 44;ccr 加载器边界校验 md_len ≤ STRIDE−8)—— + // 分配对数超过 5 时按 5 切块多块拼接(reader/get_reg_for_var 全块扫描, + // 首匹配生效;同 var 恒在同一块内 1 对,无跨块遮蔽问题)。 + // 修复前只收「末指令仍活跃」var → 恒空(rc=0);现写全部已分配对。 + rc : ., mut = 0; + lv = 0; + loop { if lv >= vc { break; } + if r64(var_asg, lv * 8) >= 0 { rc = rc + 1; } + lv = lv + 1; + } + if rc > 0 { + // 预收集已分配 var(函数内下标)——切块时按序取 + asg_lv : string, mut = alloc(rc * 8); + ai : ., mut = 0; + lv = 0; + loop { if lv >= vc { break; } + if r64(var_asg, lv * 8) >= 0 { + w64(asg_lv, ai * 8, lv); + ai = ai + 1; + } + lv = lv + 1; + } + pb : ., mut = 0; + loop { + if pb >= rc { break; } + n_in : ., mut = rc - pb; + if n_in > 5 { n_in = 5; } + ei : ., mut = g_opt_meta_count; + grow_opt_meta(ei + 1); + // Write key(u32) + data_len(u32) header using store8 + eo := ei * OPT_META_STRIDE; + store8(g_opt_meta, eo, 0); store8(g_opt_meta, eo+1, 0); + store8(g_opt_meta, eo+2, 0); store8(g_opt_meta, eo+3, 0); // OPT_KEY_REG_ASSIGN=0 + dl : ., mut = 4 + n_in * 8; + store8(g_opt_meta, eo+4, dl%256); store8(g_opt_meta, eo+5, (dl/256)%256); + store8(g_opt_meta, eo+6, (dl/65536)%256); store8(g_opt_meta, eo+7, (dl/16777216)%256); + // Write count + store8(g_opt_meta, eo+8, n_in%256); store8(g_opt_meta, eo+9, (n_in/256)%256); + store8(g_opt_meta, eo+10, (n_in/65536)%256); store8(g_opt_meta, eo+11, (n_in/16777216)%256); + // Write pairs: [var_idx(u32), reg(u32)]...(对 @+12 起,8B 步进) + di : ., mut = 12; + pi : ., mut = 0; + loop { + if pi >= n_in { break; } + lvp := r64(asg_lv, (pb + pi) * 8); + vw := vs + lvp; + rn := r64(var_asg, lvp * 8); + store8(g_opt_meta, eo+di, vw%256); store8(g_opt_meta, eo+di+1, (vw/256)%256); + store8(g_opt_meta, eo+di+2, (vw/65536)%256); store8(g_opt_meta, eo+di+3, (vw/16777216)%256); + store8(g_opt_meta, eo+di+4, rn%256); store8(g_opt_meta, eo+di+5, (rn/256)%256); + store8(g_opt_meta, eo+di+6, (rn/65536)%256); store8(g_opt_meta, eo+di+7, (rn/16777216)%256); + di = di + 8; + pi = pi + 1; + } + g_opt_meta_count = ei + 1; + pb = pb + n_in; + } + // ── 阶段 5 尾:登记表配对(双份同步纪律——内核完备 Task 3)── + // g_opt_meta = emit 面私有结构(instr.cr get_reg_for_var 消费);判定 + // 面输入 = 位置登记表(kern_loc_assign——每 g_opt_meta 写点成对登记, + // 实例责任)。分配粒度 = var 级单位置(见上分配粒度决策)→ 同 var + // 全部版本条目登记同 loc:对每个已分配 var vw,对 e ∈ [entry_start(fi), + // +entry_count(fi)) 且 ent_var(e) == vw 逐一 kern_loc_assign(e, rn)—— + // 与判定侧 meta_reg_for_var 投影语义精确等价的前提(var 分配 ⇔ 全 + // 条目登记;分配器将来升条目级时按条目登记,判定零改动)。 + es := entry_start(fi); + ec := entry_count(fi); + ai = 0; + loop { + if ai >= rc { break; } + lvp := r64(asg_lv, ai * 8); + vw := vs + lvp; + rn := r64(var_asg, lvp * 8); + ej : ., mut = 0; + loop { + if ej >= ec { break; } + e := es + ej; + if ent_var(e) == vw { kern_loc_assign(e, rn); } + ej = ej + 1; + } + ai = ai + 1; + } + } + fi = fi + 1; + } +} + +// ===== 语义对象模型(内核完备 Task 1):调度重建 = 实例事务 ===== +// NOD→g_ir_instrs 线性重建自 ccr_io.cr load_ccr 移出(原 NOD 段重建循环函数 +// 体纯搬移——零改动):从内核对象缓冲(nod_* 访问器读 g_v7_nod_sem——28B +// 语义字段载入镜像 + nod_edge_first/count 读邻接域)顺序直出 48B 线性流 +// (g_ir_instrs + g_ir_instr_count)。重建产物与移出前逐字节一致(判据 = +// backend_bootstrap stage 链 byte-identical——纯搬移证明)。REG 展开/函数 +// 边界回填(root span → g_ir_func_instr_start/count)留在 loader(GC-3 守卫 +// 消费——Task 0 确认零线性流依赖)。调用方:corearch.cr / 组合根 +// src/targets/x86_64-linux/main.cr(自举 stage 链入口)——load_ccr 成功后、 +// 分派/发射前调用一次。 +fn build_linear_schedule() { + grow_ir_instrs(g_v7_nod_count); + ni : ., mut = 0; + loop { + if ni >= g_v7_nod_count { break; } + iri_set_op(ni, nod_op(ni)); + iri_set_dest(ni, nod_dest(ni)); + iri_set_s1(ni, nod_s1(ni)); + iri_set_s2(ni, nod_s2(ni)); + iri_set_s3(ni, nod_s3(ni)); + iri_set_tk(ni, nod_tk(ni)); + g_ir_instr_count = ni + 1; + ni = ni + 1; + } +} diff --git a/src/arch/linux/ld/sizes.cr b/src/arch/x86_64/sizes.cr similarity index 78% rename from src/arch/linux/ld/sizes.cr rename to src/arch/x86_64/sizes.cr index e6d9ee00..7905dedd 100644 --- a/src/arch/linux/ld/sizes.cr +++ b/src/arch/x86_64/sizes.cr @@ -6,7 +6,12 @@ // - Phase 2 prologue/epilogue uses these helpers // - emit_instr() returns sizes via e2_* return values (must match) // -// NEVER hardcode byte counts in elf.cr, instr.cr, or resolve.cr. +// NEVER hardcode byte counts in elf.cr, instr.cr, tag2l.cr, resolve.cr, frame.cr, +// or src/os/linux/entry.cr (_start 体与 sz_start_body/sz_start_argv_save 必须同步 +// ——H2 类双源守护;frame.cr 的 pf_frame_overhead/pf_prologue/pf_epilogue 分居 +// 核算侧与发射侧,尺寸必须经本文件 sz_* 单源;tag2l.cr mw 族尺寸**值一致但尚未 +// 同源化**(评审修正——e2_mw_jo 现返回字面量 6, sz_jo 无引用:改 sz_jo 不会传播, +// 波 2 同源化或保持注记)。 // Change a size here → all phases automatically agree. // ══════════════════════════════════════════════════════════════ @@ -29,6 +34,7 @@ fn sz_call() -> int { return 5; } fn sz_jmp() -> int { return 5; } fn sz_je() -> int { return 6; } fn sz_alu() -> int { return 3; } +fn sz_jo() -> int { return 6; } // jo rel32 (0F 80 cd)——慢路径块附函数尾,rel8 ±127 在 >127B 函数体上不可达 fn sz_syscall() -> int { return 2; } // ── Prologue / epilogue sizes ── @@ -42,6 +48,11 @@ fn sz_sub_rsp(ss: int) -> int { fn sz_add_rsp(ss: int) -> int { return sz_sub_rsp(ss); } fn sz_pop_rbp() -> int { return 1; } fn sz_ret() -> int { return 1; } +// callee-saved 保存/恢复(opt≥1):push rbx(1)+r12..r15(4×2)=9 / pop r15..r12(4×2) +// +rbx(1)=9——frame.cr pf_frame_overhead 核算与 pf_prologue/pf_epilogue 发射同源 +// (波 1 Task 4 起自 pf_frame_overhead 的硬编码 18/9 提到本文件——值逐字节不变)。 +fn sz_callee_saved_push() -> int { return 9; } +fn sz_callee_saved_pop() -> int { return 9; } // Each register param save uses func-relative offsets (always disp8) fn sz_save_param() -> int { return 4; } diff --git a/src/arch/x86_64/tag2l.cr b/src/arch/x86_64/tag2l.cr new file mode 100644 index 00000000..e4200f79 --- /dev/null +++ b/src/arch/x86_64/tag2l.cr @@ -0,0 +1,547 @@ +// ══════════════════════════════════════════════════════════════ +// src/arch/x86_64/tag2l.cr —— 架构轴:int 多字 M1 的 tag 表 + 2L 编码族 +// +// x86 实例化波 1 Task 4 整搬(自 src/arch/x86_64/instr.cr 整函数搬迁——发射序 +// 逐字节 verbatim;波 1 判据 = stage 链 byte-identical): +// - **tag 表 owner = 本文件**:mw_setup_tags(规则 A/B/B' 不动点扫描 → tag +// 字节分配——per 函数,由 elf.cr Phase 2 dry-run 与 Phase 3 各调一次)+ +// 消费接口 g2_tag_off(定义在 frame.cr——H3 帧布局面,见下双向引用); +// - mw 族纯编码:jo 快路径溢出跳(e2_mw_jo)、函数尾慢路径块 +// (e2_mw_slow_block)、tag 卫生字节助手(e2_mw_t8/b8/ld8/st8/tag_clr/ +// tag_cpy)、消费者站点(mw_oc_new/e2_mw_oc_check)、2L 装载与 2L 块 +// (e2_mw_ld2/e2_mw_sext/e2_mw_opnd_block); +// - 发射门 mw_int_arith_jo_needed(HIT 表路径排除门——elf.cr 汇合点调用)。 +// 通用编码原语(e2_w8/e2_mov/e2_ld/e2_st/e2_alu/emit_rex/…)留 instr.cr—— +// 本文件是它们的消费者;本段原为 instr.cr 的 mw 章节(自 mw_setup_tags 起)。 +// +// **frame ↔ tag2l 同轴双向引用**(波 1 Task 4 评审裁定②:可接受——同轴、 +// 两构建路径均容忍:concat 扁平单元 + 组合根清单): +// 本文件 → frame.cr:g2_tag_off(tag 表读取;表 owner 在本文件); +// frame.cr → 本文件:pf_epilogue 函数尾按站点发射慢路径块/2L 块 +// (e2_mw_slow_block/e2_mw_opnd_block);pf_prologue 的 tag 卫生③读 +// g2_tag_off(表由本文件 mw_setup_tags 填充)。 +// 解析 = concat(build_selfhost_native.py arch_x86_64_files)+ module.cr 三轴 +// 回退链(组合根 _import.cr `import tag2l`);函数可见性不受段内顺序约束, +// 顺序按「被依赖者先」惯例:regalloc → sizes → instr → tag2l → frame。 +// ══════════════════════════════════════════════════════════════ + +// ══════════════════════════════════════════════════════════════ +// int 多字 M1 tagged 槽(D1a)——潜在多字变量识别 + tag 区(Task 1) +// +// 值表示(plan 拍板):快路径 int 变量槽仍 64 位(快值或 2-limb 堆指针); +// 潜在多字变量(运行时可持有 |v| ≥ 2^63 的值)在栈帧尾附加 1 字节 tag。 +// var 槽偏移(g2_slot)不变;tag 区位于槽区之下:k-th tagged(变量索引升序) +// 的 tag 字节偏移 = -(vc*8 + 1 + k)(相对 rbp)。无 tagged 变量的函数 tag 区 +// 为空 → 帧公式与现状逐字节一致(快路径零变化硬约束)。 +// +// 识别规则(保守最小正确集——M1 无区间证明,D1c 静态免 tag = M2 后置): +// 函数局部值流闭包(per 函数 [vs, vs+vc) 的 TI_INT 变量行——含表达式临时行, +// ir_gen 的赋值形态 = 临时行算值 + IR_STORE 定值拷贝落变量行): +// A. IR_BINARY(OP_ADD/OP_SUB) 的 dest —— add/sub 溢出是 M1 中唯一的 +// >64 位值生产者(mul/div 溢出链 = M2;>64 字面量 lexer 拒 = D4 推迟 M2); +// B. IR_LOAD 值拷贝 d ← s1:s1 已 tagged(TI_INT)→ d tagged —— as 转换等 +// 拷贝形态(后端 IR_LOAD = e2_load_var 直拷槽值,无解引用); +// B'. IR_STORE 定值拷贝 ρ(s1) := ρ(s2)(ir_gen 赋值形态;后端 = 槽值直拷): +// s2 已 tagged → s1 tagged —— 2-limb 值经拷贝进入消费槽(return/比较读 +// 的是 s1 槽,只标 add 临时行不够——变量行才是跨语句/跨迭代的携带者)。 +// 闭包 = 不动点扫描(见 mw_setup_tags):tag 态可沿循环回边携带(拷贝点线性 +// 先于其 tag 源的定值时,迭代 ≥2 拷贝的是 2-limb 值)——定义先于使用的线性 +// 论证只对无环成立;标记单调递增 ⇒ 至多 vc+1 遍收敛。 +// 保守性:集合外变量行运行时不可能是 2-limb 态(唯一生产者 = A 的溢出慢路径落 +// dest 槽与 B/B' 的槽拷贝);A 无溢出只是运行时事实(付 1 字节栈 + 之后任务的 +// tag 初值写)。 +// +// M1 边界(tag 不做传播,后续任务/文档挂账): +// - 跨函数:2-limb 值经调用实参/返回值传递(含 goroutine 通道)——无协议; +// - 出逃:IR_STORE 的全局 s1 / STORE_PTR/STORE_INDEX 把 tagged 值写入 +// 全局/堆/数组(STORE 的局部 s1 = 函数内定值拷贝,属 B' 闭包); +// - globals/BSS:dest 为全局的算术(现 IR 中函数体 dest 均为局部)。 +// 上述场景若 tagged 值真的超 64 = M1 已知边界(正确性由测试套件规避)。 +// +// 消费方接口(Task 2-5): +// - mw_setup_tags(fi, vs, vc):per 函数识别 + 填表(elf.cr 两阶段各一次); +// - g_x86_mw_tag_count:tagged 数 = tag 区字节数; +// - g2_tag_off(var_idx):var 的 tag 字节偏移(-1 = 非 tagged)——慢路径写 +// tag(Task 3)、消费者读 tag(Task 4)均走它;**x86 实例化波 1 Task 3 +// 起定义迁 src/arch/x86_64/frame.cr(H3 裁决:帧布局面归 frame——本文件 +// 依赖 frame;instr.cr/frame.cr 的 mw 族消费点经扁平单元解析不变)**; +// - pf_frame_size(vc):含 tag 区的帧总字节(已按 SysV 16 对齐规则取整)—— +// 同迁 frame.cr(H2 单源:Phase 2 dry-run 与 Phase 3 sub rsp 立即数共用 +// ——此前名字 mw_frame_size)。 +// Task 5(D6「tagged 只栈」)定案:**不排除寄存器分配**——plan 原案的排除 +// 动机(多字值不可驻单寄存器)被本表示消解:槽/寄存器驻留的是 64 位快值或 +// 2-limb 堆对象指针,128 位载荷在堆上,tag 在帧字节——reg 形态状态完整。 +// 全部值读写单 seam g2_slot(含 jo 慢路径回存 e2_mov(dst_reg,rax) 与 2L 块 +// 装载),tag 读写单 seam g2_tag_off,与值形态无关;O2 下 tagged 变量确实 +// 被 CAG 分配寄存器(含运行时 2L 态)且行为全绿——test_mw_task5.py 元断言 +// (oracle tagged ∩ REG_ASSIGN ≠ ∅ @O2 + O1 全栈)+ 16B 通道 = 裁决的实证 +// 形态。排除零收益(无独立 reg/栈发射路径可删)有代价(tagged = 最常见 add +// 循环行,O2 性能)。若将来引入 reg 形态不完整的机制(动态 spill 等)再议。 +// ══════════════════════════════════════════════════════════════ + +fn mw_setup_tags(fi: int, vs: int, vc: int) { + // 识别函数 fi(var 域 [vs, vs+vc))的潜在多字变量并建立 tag 字节偏移表。 + // 纯 IR 函数——输入即 .ccr 载入后的最终 IR(发射所见),无格式/无跨进程态。 + // 卫生锚:tag 读写模型与识别规则必须同域——tagged 行定值点全集 = ①add/sub + // 快路径落值清 0 ②IR_STORE/IR_LOAD 拷贝定值传播/清 0 ③prologue 参数保存清 0 + // ④2L 慢路径块写 tag=1(四类由发射侧逐一维护 ⇒ 无帧入口清零——论证全文见 + // 本文件 e2_mw_opnd_block 前「Task 4:tag 卫生」注释块;规则 A/B/B' 只标 + // 这四类会写的行——闭包 ⊇ 2L 可能态由唯一生产者论证闭合)。 + grow_mw_tag_off(vc); + z : ., mut = 0; + loop { if z >= vc { break; } w64(g_x86_mw_tag_off, z * 8, -1); z = z + 1; } + g_x86_mw_tag_count = 0; + if vc <= 0 { return; } + ic := r64(g_ir_func_instr_count, fi * 8); + ist := r64(g_ir_func_instr_start, fi * 8); + ve := vs + vc; + // Pass 1:不动点扫描(规则 A/B/B' 单调,重复全扫至某遍无新标收敛; + // ≤ vc+1 遍——回边携带:循环体内拷贝点可线性先于其 tag 源的定值, + // 单遍前向(定义先于使用)论证只对无环成立)。dest/目标在函数域内且 + // 为 TI_INT 时: + // A. IR_BINARY(ADD/SUB) → 标 dest; + // B. IR_LOAD d←s1(as 转换拷贝)且 s1 已标 → 标 d; + // B'. IR_STORE ρ(s1):=ρ(s2)(赋值定值拷贝)且 s2 已标 → 标 s1。 + chg : ., mut = 1; + ii : ., mut = 0; + loop { + if chg == 0 { break; } + chg = 0; + ii = 0; + loop { + if ii >= ic { break; } + ino := ist + ii; + op := iri_op(ino); + if op == IR_BINARY { + d := iri_dest(ino); + if d >= vs && d < ve && irv_type(d) == TI_INT { + s3 := iri_s3(ino); + if (s3 == OP_ADD || s3 == OP_SUB) && r64(g_x86_mw_tag_off, (d - vs) * 8) == -1 { + w64(g_x86_mw_tag_off, (d - vs) * 8, 0); // 0 = 已标记(占位) + chg = 1; + } + } + } else if op == IR_LOAD { + d := iri_dest(ino); + if d >= vs && d < ve && irv_type(d) == TI_INT { + s1 := iri_s1(ino); + if s1 >= vs && s1 < ve && irv_type(s1) == TI_INT && r64(g_x86_mw_tag_off, (s1 - vs) * 8) != -1 && r64(g_x86_mw_tag_off, (d - vs) * 8) == -1 { + w64(g_x86_mw_tag_off, (d - vs) * 8, 0); + chg = 1; + } + } + } else if op == IR_STORE { + s1 := iri_s1(ino); + if s1 >= vs && s1 < ve && irv_type(s1) == TI_INT { + s2 := iri_s2(ino); + if s2 >= vs && s2 < ve && irv_type(s2) == TI_INT && r64(g_x86_mw_tag_off, (s2 - vs) * 8) != -1 && r64(g_x86_mw_tag_off, (s1 - vs) * 8) == -1 { + w64(g_x86_mw_tag_off, (s1 - vs) * 8, 0); + chg = 1; + } + } + } + ii = ii + 1; + } + } + // Pass 2:变量索引升序分配 tag 字节(k-th → rbp - (vc*8 + k))。 + cnt : ., mut = 0; + k : ., mut = 0; + loop { + if k >= vc { break; } + if r64(g_x86_mw_tag_off, k * 8) != -1 { + cnt = cnt + 1; + w64(g_x86_mw_tag_off, k * 8, -(vc * 8 + cnt)); + } + k = k + 1; + } + g_x86_mw_tag_count = cnt; +} + +// ── Task 2:快路径溢出检测发射(jo → 函数尾慢路径块)── +// 发射条件 = 规则 A 的 add/sub 指令(emit 侧与 mw_setup_tags 同源判定): +// IR_BINARY、dest 为当前函数 tagged 变量(g2_tag_off ≠ -1——域外/非 int 恒 +// -1,天然过滤 globals/其他函数/dest=-1 与拷贝行)即溢出可能 → jo。untagged +// 的 int 算术(cmp/shl/mul/div…)与 dex(TI_DEX SSE 先行分支)恒不发射—— +// 真快路径对 untagged 代码零字节影响。 +// Task 4 扩展(本函数 = 表路径排除门——需旧路径(tag 检查/2L 块)的站点 +// 整条落旧路径):比较(OP_EQ..OP_GE)且任一操作数行 tagged → 同样需旧路 +// 径(2L 比较读 tag——表路径不知 tag)。add/sub 的 tagged 操作数已由 dest +// tagged 覆盖(规则 A 保证操作数 tagged ⟹ dest tagged)。 +fn mw_int_arith_jo_needed(instr_idx: int) -> int { + if iri_op(instr_idx) != IR_BINARY { return 0; } + s3 := iri_s3(instr_idx); + if s3 >= OP_EQ && s3 <= OP_GE { + s1 := iri_s1(instr_idx); + if g2_tag_off(s1) != -1 { return 1; } + s2 := iri_s2(instr_idx); + if s1 != s2 && g2_tag_off(s2) != -1 { return 1; } + return 0; + } + if s3 != OP_ADD && s3 != OP_SUB { return 0; } + if g2_tag_off(iri_dest(instr_idx)) == -1 { return 0; } + return 1; } + +// jo rel32(0F 80 cd,6B)→ 慢路径块,位置后知:记录站点现场到 +// g_x86_mw_jo_{pos,dest,is_sub}(g2_init 清零、frame.cr pf_epilogue 函数尾统一 +// 发射块并回填;resume 由 elf.cr 于该指令发射完后按序补写)。 +// rel32(非 rel8):块附函数尾——函数体 >127B 时 rel8 不可达(编译器自身 +// 的大函数在自举回归里必然命中;Task 2 用 t3 大函数用例锁定该编码)。 +fn e2_mw_jo(b: string, p: int, d: int, is_sub: int) -> int { + grow_mw_jo_patch(g_x86_mw_jo_count + 1); + w64(g_x86_mw_jo_pos, g_x86_mw_jo_count * 8, p); + w64(g_x86_mw_jo_dest, g_x86_mw_jo_count * 8, d); + w64(g_x86_mw_jo_is_sub, g_x86_mw_jo_count * 8, is_sub); + g_x86_mw_jo_count = g_x86_mw_jo_count + 1; + w8(b, p, 15); w8(b, p + 1, 128); // 0F 80 = jo rel32 + e2_w32(b, p + 2, 0); // 占位——frame.cr pf_epilogue 块位置已知后回填 + return 6; +} + +// ── Task 3:慢路径块(每站点独立块,附于函数尾 epilogue 之后)── +// 站点状态(jo 到达块时): +// - r10 = 快路径 add/sub 的环绕 64 位结果(低 limb); +// - OF = 1(jo 前提);CF 仍为 e2_alu 所置(jo 不改标志——块内首条指令 +// 前无任何写标志指令可再被读); +// - rsp ≡ 0 (mod 16)(jo 站点位于 IR_BINARY 发射内——帧级 rsp,无 call +// 栈参推送段在途;opt≥1 帧 ≡ 8 与 opt<1 帧 ≡ 0 的对齐规则保证)。 +// 其余现场:O1/O2 寄存器分配只用 callee-saved(rbx/r12-r15,opt.cr +// CAG)——块只碰 caller-saved(rax/rcx/rdx/rdi/r10/r11 等)+ push/pop +// 自平衡,callee-saved 原样穿越(“块内 call 自平衡”约束)。 +// +// 128 位修正(数学推演——溢出下真值 = 环绕 L + 高 limb·2^64,高 limb ∈ +// {0, −1} 恒成立,因 add 真值 S ∈ [−2^64, 2^64−2]、sub 真值 S ∈ +// [−2^64+1, 2^64−1]): +// add:S = a+b 溢出 ⇔ a、b 同号。S ≥ 2^63(正溢出)→ 两操作数 ≥ 0 → +// 无符号和 = S < 2^64 → CF=0 → 高 limb = 0;S < 0(负溢出,S ∈ +// [−2^64, −2^63−1])→ 两操作数 < 0 → 无符号和 = S+2^65 ≥ 2^64 → +// CF=1 → 高 limb = −1。∴ add 溢出高 limb = CF ? −1 : 0。 +// sub:S = a−b 溢出。S ≥ 2^63(正溢出)→ a ≥ 0 > b → a_u < b_u → +// 借位 CF=1 → 高 limb = 0;S ≤ −2^63−1(负溢出)→ a < 0 ≤ b → +// a_u > b_u → CF=0 → 高 limb = −1(混合符号无借位——a=−2^63、b=1 +// 即 S=−2^63−1、CF=0)。∴ sub 溢出高 limb = CF ? 0 : −1。 +// 实现:CF → r11:sbb r11, r11 = −CF(0 或 −1——add 直接可用); +// sub 再 not r11(~(−CF) = CF ? 0 : −1)。低 limb = r10 环绕值照存。 +// 两者合一叙述(溢出 = 符号翻转):高 limb = −1 ⇔ 环绕结果符号位 = 0 +// (r10 ≥ 0)——add/sub 同式,CF 式只是其标志实现。 +// 边界核对(brute-force 全样本验证 + 逐用例,见 mw-m1-task-3 报告): +// a=b=−2^63(add,S=−2^64 恰在 128 域内):L=0、CF=1 → [lo=0, hi=−1] ✓。 +fn e2_mw_slow_block(b: string, p: int, d: int, is_sub: int, resume: int) -> int { + cp : ., mut = 0; + // 1. 高 limb → r11(add:sbb r11,r11 = −CF;sub:+not → CF?0:−1)。 + // 须为块内首操作——CF 其后随时可能被写。 + w8(b, p + cp, 77); w8(b, p + cp + 1, 25); w8(b, p + cp + 2, 219); cp = cp + 3; // sbb r11, r11 (4D 19 DB) + if is_sub != 0 { + w8(b, p + cp, 73); w8(b, p + cp + 1, 247); w8(b, p + cp + 2, 211); cp = cp + 3; // not r11 (49 F7 D3—REX.WB: rm=r11 需 B 位) + } + // 2. 保存 lo/hi(alloc 调用会摧毁 caller-saved r10/r11):两 push 后 + // rsp ≡ 0 (mod 16)——满足 SysV call 对齐(进入块时 rsp ≡ 0)。 + w8(b, p + cp, 65); w8(b, p + cp + 1, 82); cp = cp + 2; // push r10 (41 52) + w8(b, p + cp, 65); w8(b, p + cp + 1, 83); cp = cp + 2; // push r11 (41 53) + // 3. alloc(16)——16B 2-limb 对象;与 IR_ALLOC_* 同款补丁表注册 + // (g_x86_alloc_patch_count 逐函数不重置——函数尾块与函数体同批回填)。 + // mov edi, 16(BF imm32——16 为合法 imm32) + w8(b, p + cp, 191); e2_w32(b, p + cp + 1, 16); cp = cp + 5; + grow_alloc_patch(g_x86_alloc_patch_count + 1); + w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, p + cp); + g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1; + w8(b, p + cp, 232); e2_w32(b, p + cp + 1, 0); cp = cp + 5; // call alloc(占位回填) + // 4. 恢复 lo/hi + w8(b, p + cp, 65); w8(b, p + cp + 1, 91); cp = cp + 2; // pop r11 (41 5B) + w8(b, p + cp, 65); w8(b, p + cp + 1, 90); cp = cp + 2; // pop r10 (41 5A) + // 5. 写 2-limb 对象(rax = alloc 返回的数据指针):[+0] = lo(环绕值)、 + // [+8] = hi(符号扩展修正)。注意 REX:rm=rax 无需 B 位——仅 R(源 + // r10/r11)→ 4C(对照 elf.cr alloc 内 mov [rax], r9 = 4C 89 08)。 + w8(b, p + cp, 76); w8(b, p + cp + 1, 137); w8(b, p + cp + 2, 16); cp = cp + 3; // mov [rax], r10 (4C 89 10) + w8(b, p + cp, 76); w8(b, p + cp + 1, 137); w8(b, p + cp + 2, 88); w8(b, p + cp + 3, 8); cp = cp + 4; // mov [rax+8], r11 (4C 89 58 08) + // 6. 值槽存指针(跳过快路径 e2_st——本块自回存 dest;g2_slot 含 + // O1/O2 reg 形态:E2_REG_SLOT_BASE 编码 → e2_mov reg, rax) + cp = cp + e2_st(b, p + cp, 0, g2_slot(d)); + // 7. tag 字节置位(g2_tag_off(d) ≠ −1 由 jo 发射条件保证):槽内现为 + // 2-limb 指针 ⇔ tag = 1(tag 不变量慢路径侧)。 + tago := g2_tag_off(d); + if tago >= -128 && tago <= 127 { + w8(b, p + cp, 198); w8(b, p + cp + 1, 69); w8(b, p + cp + 2, tago); w8(b, p + cp + 3, 1); cp = cp + 4; + } else { + w8(b, p + cp, 198); w8(b, p + cp + 1, 133); e2_w32(b, p + cp + 2, tago); + w8(b, p + cp + 6, 1); cp = cp + 7; + } + // 8. 跳回快路径 store 之后(resume = 该站点指令尾,elf.cr 填) + w8(b, p + cp, 233); // E9 jmp rel32 + e2_w32(b, p + cp + 1, resume - (p + cp + 5)); + return cp + 5; +} + +// ── Task 4:tag 卫生(定值点写 tag)+ 消费者读路径 ── +// 值表示:槽 64 位 = 快值 或 2-limb 堆对象指针;旁路 tag 字节 0/1。 +// +// tag 卫生策略 = 定值点写 tag(无帧入口清零——论证:tagged 行的每个定值点 +// 都属于以下集合之一,消费者只读「本调用内已执行的定值」之后的 tag): +// ① add/sub 快路径落值(emit_instr IR_BINARY 尾 e2_st 后 tag 清 0); +// ② 拷贝定值 IR_STORE/IR_LOAD(源 tagged → 运行时 tag 拷贝;源 untagged +// → 清 0——e2_mw_tag_copy); +// ③ 本函数 prologue 参数保存(frame.cr pf_prologue——tagged 参数行在函数内首次定值 +// 前的读取需要确定性 tag=0); +// ④ 2-limb 定值 = 慢路径块/2L 块写指针 + tag=1(Task 3/本任务)。 +// 行模型事实(.ccr 实证):用户变量行只经 IR_STORE 得到真定值(常量/算术 +// 都经临时行 + STORE);IR_CONST/IR_UNARY/IR_CALL 等的 dest 恒为一次性 +// 临时行(tag 闭包外)——因此定值点全集 = 上述四类,无遗漏。 +// +// 消费者读路径(tag=1 → 槽 = 指针 → 解 2-limb): +// A. IR_RETURN:解指针取低 limb → rax(exit 低 8 位 = 数学值低字节); +// B. int 比较(IR_BINARY OP_EQ..OP_GE,任一操作数 tagged):128 位比较 +// ——高 limb 带符号、同则低 limb 无符号(两补语义);结果 0/1 快值; +// C. add/sub 链(dest tagged,任一操作数 tagged):128 位混合算术 +// (快操作数符号扩展 + 低+低进位链 + 高+高/借位链),结果可装 64 位 → +// 降级快值 + tag 0(保持「2L ⟹ |v| ≥ 2^63 ⟹ 非零」不变量——IR_BRANCH +// 对 2L 指针的真值测试因此天然正确,无需改动);否则新 2-limb 对象 +// + tag 1。结果界:|加数| ≤ 2^64−1 + |2L| ≤ 2^65 → |和| < 2^66 ≪ +// 2^127——128 位码域永不越界(无码域错误路径;链长 ~2^63 步才可能 +// 触界 = 理论,M2 动态增长)。 +// D. M2 边界(保持 Task 1 报告边界):2L 操作数进入 MUL/DIV/SHL/一元 +// neg/移位等未实现站点、出逃写(STORE 全局/堆/数组)、跨函数—— +// 本任务不改,测试规避。 +// ══════════════════════════════════════════════════════════════ + +// 字节级小助手(rbp 相对偏移,disp8/disp32 自动): +// test byte [rbp+off], 0xFF——F6 /0(ZF ⇔ 字节==0;只写标志) +fn e2_mw_t8(b: string, p: int, off: int) -> int { + if off >= -128 && off <= 127 { + w8(b, p, 246); w8(b, p+1, 69); w8(b, p+2, off); w8(b, p+3, 255); return 4; + } + w8(b, p, 246); w8(b, p+1, 133); e2_w32(b, p+2, off); w8(b, p+6, 255); return 7; +} +// mov byte [rbp+off], imm——C6 /0 +fn e2_mw_b8(b: string, p: int, off: int, imm: int) -> int { + if off >= -128 && off <= 127 { + w8(b, p, 198); w8(b, p+1, 69); w8(b, p+2, off); w8(b, p+3, imm); return 4; + } + w8(b, p, 198); w8(b, p+1, 133); e2_w32(b, p+2, off); w8(b, p+6, imm); return 7; +} +// mov al, [rbp+off]——8A /0(rax 低字节 scratch——发射点 rax 无 live) +fn e2_mw_ld8(b: string, p: int, off: int) -> int { + if off >= -128 && off <= 127 { + w8(b, p, 138); w8(b, p+1, 69); w8(b, p+2, off); return 3; + } + w8(b, p, 138); w8(b, p+1, 133); e2_w32(b, p+2, off); return 6; +} +// mov [rbp+off], al——88 /0 +fn e2_mw_st8(b: string, p: int, off: int) -> int { + if off >= -128 && off <= 127 { + w8(b, p, 136); w8(b, p+1, 69); w8(b, p+2, off); return 3; + } + w8(b, p, 136); w8(b, p+1, 133); e2_w32(b, p+2, off); return 6; +} + +// 定值点 tag 清 0:变量 v 的槽刚被 64 位快值定值(v tagged 时发 mov byte 0)。 +fn e2_mw_tag_clr(b: string, p: int, v: int) -> int { + tg := g2_tag_off(v); + if tg == -1 { return 0; } + return e2_mw_b8(b, p, tg, 0); +} + +// 拷贝定值 tag 传播:dst 的槽 ← src 的值(拷贝后 dst 的 tag = src 的 tag; +// src 无 tag 字节(恒快)→ dst 清 0)。dst untagged → 零字节。 +fn e2_mw_tag_cpy(b: string, p: int, dst: int, src: int) -> int { + dtg := g2_tag_off(dst); + if dtg == -1 { return 0; } + stg := g2_tag_off(src); + if stg == -1 { return e2_mw_b8(b, p, dtg, 0); } + c : ., mut = e2_mw_ld8(b, p, stg); + c = c + e2_mw_st8(b, p + c, dtg); + return c; +} + +// ── 消费者站点记录(oc = operand-check):位置后知(函数尾块),jo 同款 ── +fn mw_oc_new(s1: int, s2: int, d: int, op: int) -> int { + grow_mw_oc_patch(g_x86_mw_oc_count + 1); + w64(g_x86_mw_oc_pos1, g_x86_mw_oc_count * 8, -1); + w64(g_x86_mw_oc_pos2, g_x86_mw_oc_count * 8, -1); + w64(g_x86_mw_oc_s1, g_x86_mw_oc_count * 8, s1); + w64(g_x86_mw_oc_s2, g_x86_mw_oc_count * 8, s2); + w64(g_x86_mw_oc_dest, g_x86_mw_oc_count * 8, d); + w64(g_x86_mw_oc_op, g_x86_mw_oc_count * 8, op); + g_x86_mw_oc_count = g_x86_mw_oc_count + 1; + return g_x86_mw_oc_count - 1; +} + +// 站点检查:test tag(v), 0xFF;jne rel32(0F 85)→ 该站点函数尾 2L 块。 +// rel32 恒选:块附函数尾,函数体 >127B 时 rel8 不可达(jo 同理由)。chk = +// 0/1 → pos1/pos2(站点至多 2 个检查——s1、s2 各行一个,s1==s2 只发 1 个)。 +// 记录位置 = jne 起始(p+c——test 前缀 4/7B 后;rel32 字段在 jne 起 +2—— +// 若误记 test 起,回填会覆写 test 的 disp/imm 字节)。 +fn e2_mw_oc_check(b: string, p: int, oc: int, chk: int, v: int) -> int { + c : ., mut = e2_mw_t8(b, p, g2_tag_off(v)); + if chk == 0 { w64(g_x86_mw_oc_pos1, oc * 8, p + c); } + else { w64(g_x86_mw_oc_pos2, oc * 8, p + c); } + w8(b, p + c, 15); w8(b, p + c + 1, 133); e2_w32(b, p + c + 2, 0); + return c + 6; +} + +// ── 2L 操作数装载(解 2-limb 对象到寄存器对)── +// regs: A = (r8=lo, r9=hi),B = (r10=lo, r11=hi);rax = 指针 scratch; +// 快值 → hi = sar 63 符号扩展(两补 128 语义)。快值载入 r8/r10;2L = 槽值 +// (指针)入 rax 后解 [rax+0]/[rax+8](对象布局 [+0]=lo [+8]=hi)。 +// mov r8, [rax] 4C 8B 00 mov r9, [rax+8] 4C 8B 48 08 +// mov r10, [rax] 4C 8B 10 mov r11, [rax+8] 4C 8B 58 08 +fn e2_mw_ld2(b: string, p: int, lo_r: int, hi_r: int) -> int { + w8(b, p, 76); w8(b, p+1, 139); w8(b, p+2, (lo_r % 8) * 8); // mov r{lo}, [rax] + w8(b, p+3, 76); w8(b, p+4, 139); + w8(b, p+5, 64 + (hi_r % 8) * 8); w8(b, p+6, 8); // mov r{hi}, [rax+8] + return 7; +} +// mov r{hi}, r{lo}; sar r{hi}, 63——快值 64→128 符号扩展(hi = sext) +fn e2_mw_sext(b: string, p: int, lo_r: int, hi_r: int) -> int { + c : ., mut = e2_mov(b, p, hi_r, lo_r); + w8(b, p+c, 73); w8(b, p+c+1, 193); // REX.WB + C1 /7 + w8(b, p+c+2, 248 + (hi_r % 8)); // modrm 11 111 rm(/7 = SAR) + w8(b, p+c+3, 63); + return c + 4; +} + +// ── 2L 操作数块(每消费者站点独立,附于函数尾 jo 块之后)── +// 入口:≥1 操作数 2L(快路径 tag 检查 jne rel32 → 块首)。现场:无 live +// 值(检查在操作数装载后、运算前——块内重载全部操作数,r10/r11 装载值 +// 废弃);rsp ≡ 0 (mod 16)(站点帧级 rsp——同 jo 块论证;块内唯一 call +// (alloc)由双 push/pop 自平衡)。只碰 caller-saved(rax/rdx/r8-r11)+ +// push/pop;callee-saved(O1/O2 reg 分配 rbx/r12-15)原样穿越。 +// 静态参数化:操作数行 untagged(恒快)→ 不发该行 tag 测试/2L 装载—— +// 块入口条件(≥1 2L)在单 tagged 站点下唯一确定装载路径;双 tagged 站点 +// 内部分派(测试 s1 → A 快则 B 必 2L;A 2L 再测 s2)。s1==s2 时两测试同 +// 字节(单检查站点:入口即 2L → A 2L → 同字节测试恒取 L_both,自洽)。 +// 块内 jcc/jmp 全部 rel8(块全长最坏 ~160B——双 tagged + sub + 2L 落值尾; +// 但 rel8 只跨发射点后的局部子区,最远前向 = je → L_fit 越过 2L 存储尾 +// ≤ ~60B——±127 恒足;跨块长距跳恒 rel32:tag 检查 jne → 块首、块尾 jmp → +// resume)。rel8 目标在块内顺序发射中即时回填(目标 = 已到达的后续位置)。 +fn e2_mw_opnd_block(b: string, p: int, s1: int, s2: int, d: int, op: int, resume: int) -> int { + cp : ., mut = 0; + t1 := g2_tag_off(s1); + t2 : ., mut = t1; + if s1 != s2 { t2 = g2_tag_off(s2); } + is_cmp : ., mut = 0; + if op >= OP_EQ && op <= OP_GE { is_cmp = 1; } + is_sub : ., mut = 0; + if is_cmp == 0 && op == OP_SUB { is_sub = 1; } + + if t1 != -1 && t2 != -1 { + // ── 双 tagged 分派:测 t1——0 → A 快 + B 必 2L;1 → A 2L 再测 t2 ── + cp = cp + e2_mw_t8(b, p+cp, t1); + j_a2l : ., mut = cp; w8(b, p+cp, 117); w8(b, p+cp+1, 0); cp = cp + 2; // jne L_a2l + // A 快 + cp = cp + e2_load_var(b, p+cp, 8, s1); + cp = cp + e2_mw_sext(b, p+cp, 8, 9); + // B 必 2L + cp = cp + e2_load_var(b, p+cp, 0, s2); + cp = cp + e2_mw_ld2(b, p+cp, 10, 11); + j_op1 : ., mut = cp; w8(b, p+cp, 235); w8(b, p+cp+1, 0); cp = cp + 2; // jmp L_op + // L_a2l:A 2L + w8(b, p + j_a2l + 1, cp - (j_a2l + 2)); + cp = cp + e2_load_var(b, p+cp, 0, s1); + cp = cp + e2_mw_ld2(b, p+cp, 8, 9); + cp = cp + e2_mw_t8(b, p+cp, t2); + j_both : ., mut = cp; w8(b, p+cp, 117); w8(b, p+cp+1, 0); cp = cp + 2; // jne L_both + // B 快 + cp = cp + e2_load_var(b, p+cp, 10, s2); + cp = cp + e2_mw_sext(b, p+cp, 10, 11); + j_op2 : ., mut = cp; w8(b, p+cp, 235); w8(b, p+cp+1, 0); cp = cp + 2; // jmp L_op + // L_both:B 2L + w8(b, p + j_both + 1, cp - (j_both + 2)); + cp = cp + e2_load_var(b, p+cp, 0, s2); + cp = cp + e2_mw_ld2(b, p+cp, 10, 11); + // L_op = 当前 cp——回填两个 jmp + w8(b, p + j_op1 + 1, cp - (j_op1 + 2)); + w8(b, p + j_op2 + 1, cp - (j_op2 + 2)); + } else if t1 != -1 { + // 仅 s1 tagged:入口 ⟹ s1 2L → A 2L;B 恒快 + cp = cp + e2_load_var(b, p+cp, 0, s1); + cp = cp + e2_mw_ld2(b, p+cp, 8, 9); + cp = cp + e2_load_var(b, p+cp, 10, s2); + cp = cp + e2_mw_sext(b, p+cp, 10, 11); + } else { + // 仅 s2 tagged:入口 ⟹ s2 2L → A 恒快;B 2L + cp = cp + e2_load_var(b, p+cp, 8, s1); + cp = cp + e2_mw_sext(b, p+cp, 8, 9); + cp = cp + e2_load_var(b, p+cp, 0, s2); + cp = cp + e2_mw_ld2(b, p+cp, 10, 11); + } + + // ── L_op:128 位运算(A=(r8,r9) B=(r10,r11))── + if is_cmp == 0 { + // 算术:结果 (r10 = lo, r11 = hi) + if is_sub == 0 { + w8(b, p+cp, 77); w8(b, p+cp+1, 1); w8(b, p+cp+2, 194); cp = cp + 3; // add r10, r8(lo + CF) + w8(b, p+cp, 77); w8(b, p+cp+1, 17); w8(b, p+cp+2, 203); cp = cp + 3; // adc r11, r9(hi 进位链) + } else { + w8(b, p+cp, 77); w8(b, p+cp+1, 41); w8(b, p+cp+2, 208); cp = cp + 3; // sub r8, r10(lo,CF=借位) + w8(b, p+cp, 76); w8(b, p+cp+1, 137); w8(b, p+cp+2, 216); cp = cp + 3; // mov rax, r11(暂存 bh) + w8(b, p+cp, 77); w8(b, p+cp+1, 137); w8(b, p+cp+2, 203); cp = cp + 3; // mov r11, r9(ah) + w8(b, p+cp, 73); w8(b, p+cp+1, 25); w8(b, p+cp+2, 195); cp = cp + 3; // sbb r11, rax(ah−bh−CF) + w8(b, p+cp, 77); w8(b, p+cp+1, 137); w8(b, p+cp+2, 194); cp = cp + 3; // mov r10, r8(lo) + } + // 公共落值:fits-64 ⇔ hi == signmask(lo) → 降级快值;否则 2L 对象 + // (结果界:|加数| ≤ 2^64−1 + 2^65 → |和| < 2^66 ≪ 2^127——M1 码域 + // 内恒可表示,无需码域错误路径——见块注释)。 + cp = cp + e2_mov(b, p+cp, 2, 10); // mov rdx, r10 + w8(b, p+cp, 72); w8(b, p+cp+1, 193); w8(b, p+cp+2, 250); w8(b, p+cp+3, 63); cp = cp + 4; // sar rdx, 63(signmask) + w8(b, p+cp, 73); w8(b, p+cp+1, 57); w8(b, p+cp+2, 211); cp = cp + 3; // cmp r11, rdx(0x49 39 D3:REX.W+B——reg=rdx;错编 0x4D = reg r10 = cmp r11,r10——假 fit 截断,d1/d2 判别) + j_fit : ., mut = cp; w8(b, p+cp, 116); w8(b, p+cp+1, 0); cp = cp + 2; // je L_fit + // 2L 存储:alloc(16) + 2-limb 写 + 槽存指针 + tag=1(同 Task 3 块) + w8(b, p+cp, 65); w8(b, p+cp+1, 82); cp = cp + 2; // push r10 + w8(b, p+cp, 65); w8(b, p+cp+1, 83); cp = cp + 2; // push r11 + w8(b, p+cp, 191); e2_w32(b, p+cp+1, 16); cp = cp + 5; // mov edi, 16 + grow_alloc_patch(g_x86_alloc_patch_count + 1); + w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, p + cp); + g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1; + w8(b, p+cp, 232); e2_w32(b, p+cp+1, 0); cp = cp + 5; // call alloc + w8(b, p+cp, 65); w8(b, p+cp+1, 91); cp = cp + 2; // pop r11 + w8(b, p+cp, 65); w8(b, p+cp+1, 90); cp = cp + 2; // pop r10 + w8(b, p+cp, 76); w8(b, p+cp+1, 137); w8(b, p+cp+2, 16); cp = cp + 3; // mov [rax], r10 + w8(b, p+cp, 76); w8(b, p+cp+1, 137); w8(b, p+cp+2, 88); w8(b, p+cp+3, 8); cp = cp + 4; // mov [rax+8], r11 + cp = cp + e2_st(b, p+cp, 0, g2_slot(d)); // 值槽存指针 + cp = cp + e2_mw_b8(b, p+cp, g2_tag_off(d), 1); // tag = 1 + w8(b, p+cp, 233); e2_w32(b, p+cp+1, resume - (p+cp+5)); cp = cp + 5; // jmp rel32 → resume + l_fit := cp; + w8(b, p + j_fit + 1, l_fit - (j_fit + 2)); // 回填 je → L_fit + cp = cp + e2_st(b, p+cp, 10, g2_slot(d)); // 降级:快值落槽 + cp = cp + e2_mw_tag_clr(b, p+cp, d); // tag = 0 + w8(b, p+cp, 233); e2_w32(b, p+cp+1, resume - (p+cp+5)); cp = cp + 5; // jmp rel32 → resume + return cp; + } + + // ── 比较:结果 0/1(128 位语义——高 limb 带符号、同则低 limb 无符号)── + if op == OP_EQ || op == OP_NE { + w8(b, p+cp, 77); w8(b, p+cp+1, 49); w8(b, p+cp+2, 208); cp = cp + 3; // xor r8, r10(al^bl) + w8(b, p+cp, 77); w8(b, p+cp+1, 49); w8(b, p+cp+2, 217); cp = cp + 3; // xor r9, r11(ah^bh) + w8(b, p+cp, 77); w8(b, p+cp+1, 9); w8(b, p+cp+2, 193); cp = cp + 3; // or r9, r8(ZF ⇔ 全等) + if op == OP_EQ { w8(b, p+cp, 15); w8(b, p+cp+1, 148); w8(b, p+cp+2, 192); cp = cp + 3; } // sete al + else { w8(b, p+cp, 15); w8(b, p+cp+1, 149); w8(b, p+cp+2, 192); cp = cp + 3; } // setne al + } else { + // LT/GT/LE/GE:eax 缺省 0(假);决定性 jcc → L_t(真)/L_d(假, + // 前向);hi 同 → lo 无符号再判。布局: + // xor eax; cmphi; j_ht L_t; j_hf L_d; cmplo; j_lf L_d; + // L_t: mov eax,1; L_d: 落值(j_ht 落 L_t、其余落 L_d——全前向) + w8(b, p+cp, 49); w8(b, p+cp+1, 192); cp = cp + 2; // xor eax, eax + w8(b, p+cp, 77); w8(b, p+cp+1, 57); w8(b, p+cp+2, 217); cp = cp + 3; // cmp r9, r11(hi 符号) + hi_t : ., mut = 124; hi_f : ., mut = 127; // 缺省 LT/LE:jl → 真、jg → 假 + if op == OP_GT || op == OP_GE { hi_t = 127; hi_f = 124; } // GT/GE:jg → 真、jl → 假 + j_ht : ., mut = cp; w8(b, p+cp, hi_t); w8(b, p+cp+1, 0); cp = cp + 2; + j_hf : ., mut = cp; w8(b, p+cp, hi_f); w8(b, p+cp+1, 0); cp = cp + 2; + w8(b, p+cp, 77); w8(b, p+cp+1, 57); w8(b, p+cp+2, 208); cp = cp + 3; // cmp r8, r10(lo 无符号) + lo_f : ., mut = 115; // 缺省 LT:jae(lo ≥u → 假) + if op == OP_LE { lo_f = 119; } else if op == OP_GT { lo_f = 118; } else if op == OP_GE { lo_f = 114; } + // LE: ja / GT: jbe / GE: jb(lo 不满足 → 假——L_d 前向) + j_lf : ., mut = cp; w8(b, p+cp, lo_f); w8(b, p+cp+1, 0); cp = cp + 2; + // L_t: mov eax, 1(落入 L_d 共享落值——全前向布局) + l_t := cp; w8(b, p+cp, 184); e2_w32(b, p+cp+1, 1); cp = cp + 5; + // L_d = 当前 cp——回填(j_ht → L_t;j_hf/j_lf → L_d) + w8(b, p + j_ht + 1, l_t - (j_ht + 2)); + w8(b, p + j_hf + 1, cp - (j_hf + 2)); + w8(b, p + j_lf + 1, cp - (j_lf + 2)); + } + // L_d:movzx + 落值(0/1 恒快;dest 若静态 tagged 防御性清 0——结果行 + // 实际恒 untagged,见 mw_setup_tags 规则域) + w8(b, p+cp, 68); w8(b, p+cp+1, 15); w8(b, p+cp+2, 182); w8(b, p+cp+3, 208); cp = cp + 4; // movzx r10d, al + cp = cp + e2_st(b, p+cp, 10, g2_slot(d)); + cp = cp + e2_mw_tag_clr(b, p+cp, d); + w8(b, p+cp, 233); e2_w32(b, p+cp+1, resume - (p+cp+5)); cp = cp + 5; // jmp rel32 → resume + return cp; +} diff --git a/src/ci/run.sh b/src/ci/run.sh index e011f529..d9efecbf 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -36,6 +36,11 @@ check_compiler_sources() { run_suite() { for f in tests/suite/*.cr; do case "$f" in + # mini* 语料整体 SKIP(历史遗留:其中 at_test_mini4/6 为**函数体内嵌套 fn 声明**, + # TODO #16 —— 该构造不属语言面(grammar/core.ebnf:Statement 不含 FunctionDecl), + # 修复前编译 rc=139 段错误;现由 corec 以 error[P21] 定位诊断拒绝(rc=1), + # 属**负例**而非可运行正例,故仍不进正例套件;回归见 tests/selfhost/test_nested_fn.py + # (同族扁平正例 at_test_mini5.cr / at_test.cr 已在套件内)。 *_mini*.cr) continue ;; esac if [ ! -s "$f" ]; then @@ -62,10 +67,51 @@ case "$CI_JOB_NAME" in selfhost-tests) build_selfhost + # ─── 判定面回归网(R2 P5 Task 5 起;**影子通道已下线**,不得引用其计数/摘要/站点直方图)─── + # 影子对拍(R2 P1 的迁移期仪器:`--type-shadow` / `[type-shadow]` 摘要 / 差异转储 / 站点直方图) + # 已随 R2 P5 Task 5 整体删除(对照物 `type_equal_legacy` 在 T4 删除后对拍停摆,全语料 + # `decisions=0`)。判定面回归网 = ① **冻结基线同源对拍**:冻结基线(**由 + # `tools/baseline/rebuild.sh` 从 pinned revision 重建**——配方 + 三 sha 白名单见 + # `tools/baseline/REBUILD.md`;R2 P6 Task 1 起可复现,二进制仍不入库)× 当前源 vs + # 当前二进制 × 当前源,72 档语料(runner `tools/baseline/parity_run.sh`,逐档 clean-cache) + # `check` rc + 日志逐档 diff(手工判据,本 job 内不跑——CI 为浅检出且无 jj);② **行为探针**: + # 下列套件(test_named_face / test_named_dedup / test_type_engine / test_optional / + # test_match_exhaust …)+ **入仓探针语料 `tests/probes/`(29 档;runner + # `tools/baseline/probes_run.sh`;变异态/影子态不可复跑面见该目录 README)**; + # ③ **突变控制**(改坏派生面 ⇒ 探针/对拍转红);④ **三态纪律**(引擎 -1 ⇒ ICE04 硬错, + # 见 main.cr 硬名单)。新批次新增判定面行为**必须**同时给这三类证据之一,不得回退到 + # 「与旧版逐字节同」(TODO #26 口径)。 python3 tests/selfhost/test_compile.py + python3 tests/selfhost/test_type_engine.py # 类型项引擎自测通道(selftest-types **415 例**——P5 收官 404 + R2 P6 Task 2 增 11(`ts_t2_neg_run`:¬ 面同类原子身份判据的邻域覆盖——同身份 / μ 字面 / ⊤ₖ 双向 / 跨枚举同名 / 同枚举异变体 / 令牌字面 / AK_NAMED / 空链与链元素域外 / 参数化面 / REF mut 标记面;三例 `t3.*_over_claim` 为**重钉**(0→1,不增减);P0..P6 T2 一路漏挂) + python3 tests/selfhost/test_iface_ops.py # R2 P2b Task 4/5/6:接口查表接线(算术/逻辑/条件三门 + 索引兜底门 + TY→TI 单表合一;正控/负控/登记面/站点域 76 例 + R2 P6 Task 4a 增 2(方法调用站点 never 透传 + 泛型方法登记钉)= 78 例) + python3 tests/selfhost/test_purity.py # 效应/纯度批判据(真纯度 13 例 + 链语义 4 例的自测通道驱动) + python3 tests/selfhost/test_ccr_v7.py # 效应/纯度批 Task 3:.ccr 层三条边集语义断言(取代「与旧版逐字节同」) + python3 tests/selfhost/test_ccr_types.py # R2 P4 Task 1/2/3/4 + P5 Task 2:段机制(版本 8 + loader 三闸/必备集 + 旧 v7 拒收)8 例 + TYPE 内容面(行表/项 DAG 序列化 + 确定性装填 + corearch 读回对拍 + loader 负分支)10 例 + IFACE 内容面(五小节/条目扩列 16/形状命名化/签名项化/impl 边与方法表/corearch 读回/跨段引用域/确定性)10 例 + DFNode.TK 项槽(逐节点规则 + dex 语料 + NOD 36B/同序 + 冷/暖快照对称 + dump 零产物影响 + .cir 布局未变)6 例 = 34 例 + P5 T2 单槽化 6 例(复合行走辅码 + 互斥不变量 + F1 hotpatch 无面 + 复合行冷/暖对称 + 复合行 NOD 同序 + 快照盘面派生码保真)= 40 例 + R2 P6 Task 3 项索引 8 例(版本闸 v8 拒收 / NOD 40B 字段序 / 项索引落盘读回对拍(非退化)/ 两通道面一致 / 辅码面与复合行保真 / 项索引越界与不一致 ⇒ 拒绝 / 段界检 / D18 纯度静态)= 48 例 python3 tests/selfhost/test_impl.py python3 tests/selfhost/test_borrow.py python3 tests/selfhost/test_pointer_safety.py + python3 tests/selfhost/test_params_limit.py # TODO #8 形参上限/≥18 形参静默误编译回归 + python3 tests/selfhost/test_tuple_slots.py + python3 tests/selfhost/test_agg_slots.py # TODO #25 姊妹条目:struct/struct 模式/数组字面量槽位(同 F5 契约) + python3 tests/selfhost/test_agg_checks.py # TODO #29 聚合字面量「名/型/同质性」三校验(名字绑定 + TS01-04/TK02 硬错误) + python3 tests/selfhost/test_nested_fn.py # TODO #16 嵌套 fn 声明段错误 → 定位诊断回归 + python3 tests/selfhost/test_interp_parity.py # TODO #11 解释器 callee 内联 ≡ 主循环 ≡ ELF + python3 tests/selfhost/test_cache_identity.py # TODO #5 cir 缓存编译器身份(跨重建失效) + python3 tests/selfhost/test_cir_warm_path.py # R2 P5 Task 1:.cir 暖态 SIGSEGV 根因修复回归——冷/暖 rc 双 0 + ELF 逐字节同 + .ccr 段级契约(STR 冷≠暖为预存口径)+ 暖态真命中(条目 size/mtime 不变)+ 变体(多串/0 串/大串/多函数)+ 可选面零条目 + **累计装载复现例**(400 条目补零至 3MB ⇒ Σ=1.2GB > 1GB 堆,暖态 rc=0 + 峰值 RSS < 512MB;修复前 rc=139 红态实测)= 19 例(COREC_WARM_SELF=1 另开 2 例自源语料,~3 分钟) + python3 tests/selfhost/test_match_exhaust.py # R2 P3 Task 3:match 穷尽性(补集空性 + 具体变体反例;TM03 硬门 + TM04 软面) + python3 tests/selfhost/test_tf01_fallthrough.py # TF01 收口:函数体落空分析(无 break 的 loop 收尾不再误报)+ lits_copy 返回型(真·类型洗白);正控/负控/端到端 19 例 + R2 P6 Task 4a 增 3(never 调用返回位/传播位 + 死分支端到端)= 22 例 + python3 tests/selfhost/test_optional.py # R2 P3 Task 4:联合/可选(T? = T ∪ null;Some/None 解析 + ? 结构解包 + Option 名退役 + 载荷类型节点列)12 例 + R2 P4 Task 5:解包侧判表示(裁决 5)——B.7「裸值 + Some 臂」双路径 SIGSEGV 闭合 + 表示位运行期分派(写点置位/条件写/两形态改写)+ 返回/形参信道 + `?` 解包装箱值双路径分歧闭合 + 未覆盖面登记 = 34 例 + **容量批 T2(裁-CAP-1 (a) 存储边界规范化)**:聚合面(结构体字段/数组·切片元素/元组元素/枚举载荷/全局槽)写点装箱——A 类 8 写点逐点用例(字段赋值/字面量 · 数组字面量下标/动态下标/字面量元素 · 全局赋值/**初值** · match 结果条件写 · 枚举载荷 · 元组元素)+ 负控 + 原未覆盖面 4 例**重钉**(-11 → 精确值;死亡证据 = 本批能力变更)= **48 例** + python3 tests/selfhost/test_generic_constr.py # R2 P3 Task 5:泛型约束(结构/枚举约束保留 + 实例化点 ty_sub 判定 + 反例文本;用户接口面 P3b Task 0 起真判定;F4 形参链;实例键类型项化) + python3 tests/selfhost/test_iface_satisfies.py # R2 P3b Task 0:iface_satisfies 契约(轴分派/三态/与 check_iface 同源;实例化点 T: I 判定 + 登记面 17 例) + python3 tests/selfhost/test_xcut_iface.py # R2 P3b Task 2:横切接口接线(索引兜底门 → 可索引形状 / range 分支 → 序列形状 + 固定性位;行为保持 12 例:三路同证正例 + TK01 软诊断负例 + F2/F11 硬错误钉子) + python3 tests/selfhost/test_impl_iface.py # R2 P3b Task 6:impl 契约覆盖集(签名类型项化 + 形状项逐成员判定 + mangling 退役 + 上限钉子;20 例:正向三路/两路同证 + 反向签名五面 + 错误例 + 登记面 + 硬错误) + python3 tests/selfhost/test_enum_limit.py # R2 P4 Task 6(TODO #35)旧态 13 例(16 边界 + 17 定位硬错 P022/P023)→ **容量批 T3 重钉为 15 例**:字段/变体/载荷**上限解除**(侧表)⇒ 16/17/40/64/70 一律编译+运行值正确(旧 17-拒绝面转正,死亡证据 = 本批能力变更)· 跨记录完整性(宽记录 + 邻记录)· 覆盖位无界(≥63 变体穷尽性正确)· 真错仍拒(TM03 缺臂 / TM04 冗余臂) + python3 tests/selfhost/test_named_face.py # R2 P5 Task 3:命名面判定化(身份链:同链 1 / 链异 0 / 域外 -1)行为覆盖集——受 10 例(命名互赋 / 泛型应用两实例化 / 嵌套应用 / 实参含命名 / 容器元组 / 递归 *Node / 形参 T / 别名透明 / 函数边界)+ 拒 8 例(异名同形 / 异实参应用 / 命名 vs 原生 / 命名 vs 应用 / 可选异名 / T 赋 int / 不变槽残留 ×2);**不依赖影子通道**(影子通道已于 P5 T5 下线 ⇒ 本套件 = 下线后的判定面行为网主力);18 例 + python3 tests/selfhost/test_named_dedup.py # R2 P2a Task 3(C-4):侧表 ↔ res_type_node 管线内断言(`--verify-named-dedup`;**非影子通道**——判据 = 真实流水线上三组一致性,影子期亦无耦合) + python3 tests/selfhost/test_let_check.py # R2 P5 Task 6(TODO #32):`EXPR_LET` 站点值/注解兼容判定——TA02 定位硬错(值非兼容 + 数组长度档)+ 无产物 + 前端拒绝(不进入 lower/写 .ccr/ELF)+ 全局声明面(check_global_let 同款)+ 批量/丢弃名 + 级联抑制(TI_NEVER 错误标记只发原诊断)+ 正控(无注解/auto/dyn/泛型 T 与 [T;N]/可选注入/无初值);23 例〔**注释陈旧自纠**:T4a 前实读 = 24(23 为 P5 期旧值,差 1 未同步)〕+ R2 P6 Task 4a 增 5(never 调用声明位 / near-miss 负控 / 强负控调用返回型不符仍拒 / 语句位宽面 / 赋值位登记钉)= **29 例** + python3 tests/selfhost/test_diag_gate.py # FC 批 T2:fail-closed 闸门(默认阻断 + 豁免登记表 9 条)——正控 9(表内 6 个 build 面码仍放行 + scope=check 3 条 check rc=1 不变)+ 负控 6(语法面 P21 / 类型面 TA02·R02·TM03·TK05 / 安全检查面 TU03:仍阻断 + **零产物**)+ 零产物 3(前端失败无半成品 / corearch 失败删本次 .ccr [stub 仿真] / 旧哨兵原样仍在)= **17 例** + python3 tests/selfhost/test_warm_cache_gate.py # #60 批 T2+T3:暖缓存两态回归(**CI 暖态最小面**;广度层 = tools/baseline/warm_leg.sh 手工判据)——缓存命中跳过 `ir_gen_func` ⇒ ir_gen 期 `alloc_type` 行不重建(T1 实锤:warm 缺 `TYP_PTR extra=1` ⇒ `provenance_verify.cr:66` TU03 静默失效)。修法 = 生成期对「快照不载的共享面」有副作用 ⇒ 该条目**不可写**(下次必 miss 重放副作用);判据 ① `as *int` 解引用 load/store **冷/暖同**(都 rc=1 + TU03)② 机制钉:副作用函数 `::main.cir` **无条目** ③ 正控:普通程序条目在 + 二跑真命中(size/mtime 不变)④ TK01 冷/暖同 ⑤ `ccr` 面同判;**同路径重复编译 = 缺陷真触发场景(定路径是本设计的要点)**= **6 例** + python3 tests/selfhost/test_tc02_branch.py # TC02 收口:`if` 分支相容判定的**发散豁免**(P3 不对称——else 支发散 ⇒ 不报;then 支发散 ⇒ 仍报真信号;谓词 `stmt_diverges`,checker.cr,只服务本判定点)+ 既有 NEVER 豁免(loop{} 收尾)零扰动 + TF01/TA02/TB01 面钉子(TF01 仍报 / 落空豁免不变 / 声明位 TA02 不变 / TB01 真错负控 ×2)+ 端到端 build+run(`test_native_float` 两源同形)= **15 例** ;; suite) diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index e3f88e85..a6c140e7 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -8,10 +8,31 @@ import dyn_arr import lexer import parser import checker +import type_terms +import type_engine +// R2 P2b Task 1:本质条目表 + iface_* 查询 API(静态数据表;置于引擎之后、桥接层之前 +// ——Task 2 起桥接层(ty_shadow.cr)委托本层,故顺序必须是 引擎 → 注册表 → 桥接层。 +// 注:ty_shadow.cr 的文件名是历史遗留(其影子对拍挂点已随 R2 P5 Task 5 下线)。 +import iface_registry +// R2 P4 Task 0:引擎核纯化拆分——iface 满足判定簇 + `iface_kind_of` 移入 corec-only 的 +// iface_axis.cr(type_engine.cr 留纯核、iface_registry.cr 除该函数)。**project-mode 面必须 +// 同列本行**:src/compiler 目录的文件集 = 本清单(无目录自动发现,仅 import 收编)—— +// 漏列 ⇒ corec2/corec3 自举链上 iface_kind_of/iface_satisfies 全族 `error[N06]` 静默未定义 +// (rc=0 + 产物照出,B.6 同族)。corearch 清单(src/targets/x86_64-linux/_import.cr)**不列** +// 本文件与引擎簇(本任务不动该文件)。 +import iface_axis +import ty_shadow +// R2 P4 Task 2:TYPE(7) 段内容构造(D18 corec-only——装填引用桥接层 sh_term_of_ti; +// project-mode 面必须同列本行:src/compiler 目录的文件集 = 本清单,漏列 ⇒ 自举链上 +// ccr_type_prepare_save 族全 N06 静默未定义(B.6 同族)。corearch 清单不列本文件。) +import ccr_types +import type_selftest +import purity_selftest import diag import ir_gen import dataflow import ccr_io +import ent_kernel import module import toml import project diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index a836170e..4acad224 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -81,17 +81,9 @@ T_MINUS_EQ : int = 74; T_STAR_EQ : int = 75; T_SLASH_EQ : int = 76; -// Suffixed integer/float token kinds (lexer emits these when suffix like _i32, _u64, _f32 is found) -T_INT_I8 : int = 77; -T_INT_I16 : int = 78; -T_INT_I32 : int = 79; -T_INT_I64 : int = 80; -T_INT_U8 : int = 81; -T_INT_U16 : int = 82; -T_INT_U32 : int = 83; -T_INT_U64 : int = 84; -T_FLOAT_F32 : int = 85; -T_FLOAT_F64 : int = 86; +// 77-86 原为宽度后缀 token kind(T_INT_I8..T_INT_U64 / T_FLOAT_F32 / T_FLOAT_F64)。 +// 2026-09-10 语言面收窄 §2 删除:lexer 从不发射这些 kind(宽度后缀已退役,改响亮报错), +// parser 侧引用同步移除。**勿重编号**——编号空间稳定(先例:T_FLOAT_TYPE 91 已删除)。 T_NONE : int = 87; T_SOME : int = 88; T_LET : int = 89; @@ -106,17 +98,8 @@ T_REF : int = 96; T_DYN : int = 99; // dynamic type T_EXTERN : int = 100; // extern "C" / foreign function declaration -// Width constants (stored in EXPR_INT/EXPR_DEX data field) -W_I8 : int = 1; -W_I16 : int = 2; -W_I32 : int = 3; -W_I64 : int = 4; -W_U8 : int = 5; -W_U16 : int = 6; -W_U32 : int = 7; -W_U64 : int = 8; -W_F32 : int = 9; -W_F64 : int = 10; +// W_I8..W_F64(1..10)原为「宽度标注」值域,仅供宽度后缀 token 分支使用。 +// 2026-09-10 语言面收窄 §2 随死分支一并删除。**勿重编号**。 // Type constants TY_INT : int = 0; @@ -130,9 +113,9 @@ TY_GENERIC_PARAM : int = 7; // special sentinel for generic type params TY_DEX_S : int = 8; // dex 定点形式(TI_DEX_S)的类型表占位 data(终审 M1:占住表项 // 下标 8,用户类型从 9 起;占位项永不参与解析/运算) MAX_GENERICS : int = 4; // max generic params per declaration (language limit) -MAX_STRUCT_FIELDS : int = 16; // max fields per struct (struct info size limit) -MAX_ENUM_VARIANTS : int = 16; // max variants per enum (enum info size limit) -MAX_VARIANT_TYPES : int = 16; // max payload types per variant +// 容量批 T3(裁-CAP-2 (a)):`MAX_STRUCT_FIELDS` / `MAX_ENUM_VARIANTS` / `MAX_VARIANT_TYPES` +// **已退役**(记录布局迁侧表 ⇒ 三面上限解除,P022/P023 硬错随之停发)。数值 16 不再具有 +// 语义(旧定长内嵌槽区的容量);布局真源 = `dyn_arr.cr` 的 OFF_SI_*/OFF_EI_* 与侧表访问器。 // Token struct struct Token { @@ -143,44 +126,10 @@ struct Token { col: int, } -// Function signature (for call resolution) -struct FuncInfo { - name: string, - param_count: int, - param_types: [int; 64], - return_type: int, - ast_node: int, // index into ast array for the fn body - generic_names: [string; 16], - generic_count: int, -} - -// Struct layout -struct StructInfo { - name: string, - field_names: [string; 64], - field_types: [int; 64], - field_type_nodes: [int; 64], // original type node indices (for generic resolution) - field_count: int, - generic_names: [string; 16], - generic_count: int, -} - -// Enum variant description -struct EnumVariant { - name: string, - types: [int; 64], // TY_* for each field - type_count: int, -} - -// Enum layout -struct EnumInfo { - name: string, - variants: [EnumVariant; 16], - variant_count: int, - generic_names: [string; 16], - generic_count: int, -} - +// ── 记录镜像声明(旧):容量批 T3 起**删除**——`FuncInfo`/`StructInfo`/`EnumInfo`/ +// `EnumVariant` 三个镜像结构在全仓**零类型使用点**(仅注释命中),且其定长槽区(`[.;64]` +// 等)与运行期布局早已不一致(旧布局 = 内存字节缓冲 + 定长内嵌槽区,T3 起字段/变体迁 +// 侧表)。布局真源 = `dyn_arr.cr`(ESZ_*/OFF_* 常量 + 侧表访问器)。 // Loop context (for break/continue) struct LoopInfo { start_label: string, @@ -215,6 +164,11 @@ EXPR_UNARY : int = 6; // a=operand, c=opcode EXPR_CALL : int = 7; // a=func, b=first arg idx, c=arg count, type_val=CALL_FLAG_* CALL_FLAG_MODULE : int = 1; CALL_FLAG_INLINE : int = 2; +// R2 P3b Task 6(Step 3,mangling 退役):接口方法调用(泛型形参接收者,`fn f[T: I]` 体内 +// `x.m()`)——data = **泛型形参名 ni**(不是合成的 "T.m" 串);实例化时由 monomorph 按 +// 具体类型查方法表(`iface_find_method`)解析为真实函数名。旧态 = 名字拼接 + 克隆期文本替换, +// 实例体内调用目标悬空(产物运行 rc=139,实测)。 +CALL_FLAG_IFACE_METHOD : int = 4; EXPR_BLOCK : int = 8; // a=g_block_stmts start, b=stmt count EXPR_IF : int = 9; // a=cond, b=then, c=else (-1 if none) EXPR_LOOP : int = 10; // a=body @@ -223,9 +177,14 @@ EXPR_RETURN : int = 12; // a=value expr (-1 if none) EXPR_FIELD : int = 13; // a=object, int_val=field name idx EXPR_INDEX : int = 14; // a=object, b=index EXPR_ASSIGN : int = 15; // a=target, b=value -EXPR_STRUCT : int = 16; // a=type name idx, b=first field, c=field count +EXPR_STRUCT : int = 16; // a=type name idx, b=first wrapper(g_ast 中连续;wrapper kind=EXPR_NONE 且 a=字段值节点、b=字段名 idx(TODO #29 ①;-1 = 无名字信息,按位序回落)), c=field count (struct literal) EXPR_FN : int = 17; // a=name idx, b=first param, c=param count, d=body, data=return_type EXPR_PARAM : int = 18; // a=name idx, int_val=type +// 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 的类型构造器身份已退役—— +// 语义归处 = product(N 元聚合)/ 序列接口 + 长度 where(N 长序列)/ F11 图(长度事实, +// 可表达依赖长度)。本语法保留为「内联容量存储」表示提示(映射参数层,与 hw-map 同层; +// 随实例选择生效或退化,非经典范式映射可忽略)。见 +// docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 EXPR_ARRAY : int = 19; // a=first elem, b=elem count EXPR_BREAK : int = 20; EXPR_CONTINUE : int = 21; @@ -242,7 +201,7 @@ EXPR_MOVE : int = 32; // a=expr being moved EXPR_ENUM_CONSTRUCTOR : int = 37; // a=name idx, b=first arg, c=arg count EXPR_REFTYPE : int = 38; // a=inner type node, data=mut flag (for &T / &mut T in type position) EXPR_GENERIC_APPLY : int = 39; // a=base name idx, b=first arg type node, c=arg count -EXPR_TUPLE : int = 40; // a=first elem, b=elem count (tuple literal) +EXPR_TUPLE : int = 40; // a=first wrapper(g_ast 中连续;wrapper kind=EXPR_NONE 且 a=元素值节点), b=elem count (tuple literal) EXPR_ARG : int = 41; // a=expr, b=next arg node or -1 (argument linked list) EXPR_GO : int = 42; // go expr: a=-1, b=body; go var start..end expr: a=-1, b=body, c=iter_ni, data=range_node EXPR_FLOW : int = 43; // flow fn — a=fn_name_ni, b=param_count, c=first_param, data=body @@ -253,10 +212,14 @@ EXPR_AT : int = 46; // @builtin: a=name_ni, b=args_node, c=0, iv=0, // Desugared constructs EXPR_TRY : int = 33; // a=expr being tried (? operator) EXPR_UNSAFE : int = 34; // a=block body -EXPR_STRUCTPAT : int = 35; // struct pattern: a=name ni, b=first field pat, c=field count +EXPR_STRUCTPAT : int = 35; // struct pattern: a=name ni, b=first wrapper(连续;wrapper kind=EXPR_NONE 且 a=子模式节点), c=field count EXPR_AS : int = 36; // a=expr, b=type node (cast: expr as Type) EXPR_PTRTYPE : int = 47; // a=inner_type (for *T in type position) EXPR_EXTERN : int = 48; // a=name_ni, b=first_param, c=param_count, data=ffi_lang_ni +// R2 P3 Task 4:`T?` 的目标形态(退役「T? → EXPR_GENERIC_APPLY(Option, T)」的**名字依赖**: +// 旧形态要求内建 Option 名注册在符号表里,且 §5.4 的「T? = T ∪ null」无法表达)。 +// a = 内层类型节点。-1 内层 / 非法形态 → res_type_node 落 TI_UNIT(照各类型节点分支同款)。 +EXPR_OPTIONAL : int = 49; // a=inner type node (T? 类型位置) // Field representation in struct literal: two consecutive AST nodes // (name_idx, value_idx, line=line, col=col) @@ -313,14 +276,23 @@ TI_DEX_S : int = 8; // Type table entry kinds TYP_BASE : int = 0; // data = TY_* constant TYP_NAMED : int = 1; // data = name string index +// 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 退役为「内联容量存储」表示提示(语义归处 = product / 序列+长度约束 / F11)——完整注记见本文件 EXPR_ARRAY 常量处,spec 见 docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 TYP_ARRAY : int = 2; // data = element type idx, extra = size TYP_REF : int = 3; // data = inner type idx, extra = mut flag TYP_PTR : int = 4; // data=pointee_type, extra=address_space (0=tracked, 1=external) TYP_GENERIC_PARAM : int = 7; // data = name string index (unresolved generic param) TYP_GENERIC_APPLY : int = 8; // data = base type idx, extra = arg list start in g_gen_apply_data +// 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 退役为「内联容量存储」表示提示(语义归处 = product / 序列+长度约束 / F11)——完整注记见本文件 EXPR_ARRAY 常量处,spec 见 docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 TYP_SLICE : int = 9; // data = element type idx (dynamic-length view into array) TYP_TUPLE : int = 10; // data = element_count, extra = elem types start in g_gen_apply_data TYP_DYN : int = 11; // data = type set bitmap (0 = single known type) +// R2 P3 Task 4(联合/可选):`T?` = `T ∪ null`(spec §5.4)——不再是「内建 Option[T] 命名类型」。 +// data = 内层类型行;桥接侧译作 union(内层项, null 原子项)(ty_shadow.cr 的 TYP_OPTIONAL 分支)。 +// 引擎面 = 联合项(非原子类)⇒ `iface_kind_of` 对之行回 -1(不得按单一原子处理)。 +TYP_OPTIONAL : int = 12; // data = inner type idx(T? = T ∪ null) +// `null` 的类型(`None` 值的类型;spec §5.4 的「T ∪ null」里的 null)。值域单点 ⇒ 引擎侧 +// AK_NULL 原子(原生第九员);行不带参数(None 无载荷)。与 AK_UNIT/AK_NEVER 均不相交。 +TYP_NULL : int = 13; // 无字段(data/extra 恒 0) // Error codes: category * 1000 + number, matching docs/error-codes.md // Category 0 = unclassified (000-) @@ -361,6 +333,10 @@ EC_P_IMPORT_PATH : int = 1016; // P016 Invalid import path EC_P_FILEID : int = 1017; // P017 Invalid fileid declaration EC_P_VAR_DECL : int = 1018; // P018 Invalid var declaration EC_P_LIT_OVERFLOW : int = 1019; // P019 Numeric literal overflow +EC_P_TOO_MANY_PARAMS : int = 1020; // P020 Too many function parameters (FuncInfo 参数槽区容量) +EC_P_NESTED_FN : int = 1021; // P021 Nested function declaration (函数声明仅限顶层) +EC_P_ENUM_LIMIT : int = 1022; // P022 **已退役**(容量批 T3:变体/载荷上限解除,零 raise) +EC_P_STRUCT_LIMIT : int = 1023; // P023 **已退役**(容量批 T3:字段上限解除,零 raise) // N0xx — Name Resolution EC_N_UNDEFINED : int = 2001; // N001 Undefined name @@ -505,6 +481,10 @@ EC_E_CCR_OPEN : int = 15004; // E004 Cannot open CCR EC_ICE_UNEXPECTED : int = 16001; // ICE01 Unexpected EC_ICE_OVERFLOW : int = 16002; // ICE02 Buffer overflow EC_ICE_UNSUPPORTED : int = 16003; // ICE03 Unsupported +// R2 P5 Task 4(P-A):类型判定不可判(引擎 -1 = 未覆盖面/预算耗尽,或桥接缺口 = 该行译不成 +// 类型项)。三态纪律:**未知不得当 0/1** ⇒ 硬错(legacy 回落面已删,D24)。反例(两侧类型项 +// 文本)随诊断输出;判定点无 AST 位置 ⇒ line/col = 0。 +EC_ICE_TY_INDET : int = 16004; // ICE04 Type judgment indeterminate // Diagnostic entry struct Diag { diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index e788b266..641a321d 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -2,31 +2,180 @@ // .ccr binary serialization — the interface between corec (frontend) // and corearch (backend). // -// Format (all integers little-endian): -// [magic: "CCR1" = 4 bytes] -// [version: u32 = 5] -// [func_count, instr_count, var_count, str_count, str_const_count, struct_count, enum_count: u32 ×7] -// [strings: str_count × [len: u32] [data: len bytes]] -// [func_meta: func_count × [name_idx, param_count, ret_type, instr_start, instr_count, var_start, var_count: u32 ×7]] -// [instrs: instr_count × [opcode, dest, src1, src2, src3, type_kind: i32 ×6]] -// [vars: var_count × [name_idx, id, type_kind: u32 ×3]] -// [str_consts: str_const_count × [str_idx: u32]] -// [structs: struct_count × [name_idx: u32] [field_count: u32] fields[field_count]×[name_idx, type: u32 ×2]] -// [enums: enum_count × [name_idx: u32] [variant_count: u32] variants[variant_count]×[name_idx: u32] [field_count: u32] fields[field_count]×[type: u32]] -// [globals: global_count × [name_idx: u32] [var_idx: u32] [init_val: i64]] (16B each; init_val added in v4) -// [opt_meta: opt_count × [key: u32] [len: u32] [data: len bytes]] (v3+) -// [sgs: sg_count × [kind, enter, exit, parent, nstart, ncount: i32 ×6]] (v5+) +// v8 format(serialization v5;v8-only——load 校验 version==8,无 v7 兼容/转换; +// 旧 v7 文件由 version 拒收,无转换工具。v8 = v7 段表架构的**加法扩展** +// (R2 P4 Task 1,D9/D10):+ TYPE(7)/IFACE(8) 两段(内容面归 Task 2/3,本 +// 任务落空壳)+ CCR_VERSION 7→8(留 7 会让旧 6 段文件被静默读成「两段缺席 = +// 空表」——正是三态纪律要消灭的静默类)。规范序闸 tg == ri+1 与段体连续闸 +// 形状不变(D9 取 7/8 而非 9/10 的直接收益:加段成本 = 闸界 6→8 一处)) +// 字节真相 = docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md(设计定稿 +// ——在 v6 段表架构上扩展:NOD 36B 邻接 + EDG 段必落 + ENT 实记录(Task 2: +// corec 产时重建——直调内核 compute_live_ranges(ent_kernel.cr 单源化,见 save_ccr)) +// + coreir-schema.md 家风格(Task 6 并入 schema)。本文件头注释 = 实现权威 +// (v6 目标形状落地:SYM 归并 spec §3.2——vars 表并入函数记录声明区/globals; +// REG 坐标化 spec §3.5——kind/parent/enter/exit/first_ent/last_ent,nstart/ +// ncount 由 enter/exit 派生)。与 spec 表格的编码层差异(实现决策,Task 6 +// schema 同步按本注释落笔): +// (a) 局部变量 name/type 不丢:ENT 28B 定长记录只携带 var_id(NOD 同命名 +// space = 内存行序),无法容纳声明元数据——「存在即声明」落为:变量的 +// 名称/类型 = 函数记录内嵌声明区(位置数组,行序即创建序、前 param_count +// 个 = 参数;非符号命名空间——SYM 无 vars 小节);全局 type 占 v5 全局 +// 记录 var_idx 槽(var_idx 恒 == 行序,冗余)——记录仍 16B。 +// (b) SYM 子节序 = globals → funcs → str_consts → structs → enums → +// opt_meta(globals 前置 = loader 单遍流式重建 var 行序 [全局行][函数块])。 +// (c) func 记录的 instr/var 范围不再落盘——instr 范围由 root_region(REG 行 +// id)span 派生(根 region = SG_FUNC,span = 函数节点范围,与 v5 func_meta +// instr_start/count 恒等);var_start = 声明区行序游标(globals 行 + 前缀 +// 函数块)。 +// (d) REG 由「可缺」升为必备(load 拒绝无 REG 的文件——函数指令边界唯一 +// 真源);ENT 仍可缺(v5 精神:旧段缺失 = 空)。 +// 全整数 LE;offset 相对文件头: +// [header 16B]: magic u32 = "CCR1" | version u32 = 8 | seg_count u32 = 8 | +// reserved u32 = 0 +// [seg table 8×12B]: {tag u32, offset u32, size u32}——规范序(tag = 行号 1..8, +// offset = 上一段尾,段体紧随段表连续排列) +// [seg bodies](按段表寻址): +// STR(1) 字符串表: [str_count u32] [× {len u32, data}](同 v5) +// SYM(2) 符号面(spec §3.2 归并形状——每小节自带计数): +// [global_count][global_count×16B {name u32, type u32, init_val i64}] +// (v5 var_idx 槽 → type:var 行序 = 全局记录序 0..G-1, +// 行名/型随本记录携带——var_idx 恒等行序故删除) +// [func_count][func_count×{name u32, param_count u32, ret_type u32, +// root_region i32(本函数 SG_FUNC 的 REG 行 id), +// first_ent i32, last_ent i32(本函数条目文件范围; +// -1 = 无条目)} = 24B +// + param_ents[param_count]×i32(参数变量的「入参」条目 = +// 该参数 def_nod=-1 条目 id;函数内被重定值/从未引用 → -1) +// + var_count u32 +// + var_decls[var_count]×{name u32, type u32}](v5 vars 表 +// 并入——行序 = 创建序,前 param_count 个 = 参数;变量命名 +// space = globals 行 + 函数块行序相接,ENT/NOD var_id 指此) +// [str_const_count][str_const_count×4B] +// [struct_count][struct_count×{name u32, field_count u32, +// fields[field_count]×{name u32, type u32}}] +// [enum_count][enum_count×{name u32, variant_count u32, +// variants[variant_count]×{name u32, type_count u32, +// types[type_count]×u32}}] +// [opt_count][opt_count×{key u32, len u32, data lenB}] +// NOD(3) 节点表: [nod_count u32] [×36B {op i32, dest i32, src1 i64, +// src2 i32, src3 i32, tk i32, first_edge u32, +// edge_count u32}]——28B v5 语义字段不变 + 邻接索引 +// (v7 spec §3.3;NOD id = 文件序索引 0..nod_count-1 +// = 图坐标 D1;ENT 区间/def 即指此坐标) +// EDG(6) 边表(v7 必落):[edg_count u32] [×8B {to_nod u32, kind u32}] +// ——节点 i 出边连续段 [first_edge, first_edge+edge_count), +// first_edge = 前缀累计(节点 i+1 first_edge = 前节点 +// first_edge+edge_count);每边 to_nod > 所属节点(v7 +// spec §4 拓扑不变量 1——数据/state 边前向);kind 0=数据 +// (def-use)、1=state(副作用序)。落盘源 = g_df_edges +// (内存 = 头插链表,dataflow.cr)——save 前按节点序 +// 单遍收集(见 ccr_collect_edges);v7 校验规则 +// edg_count == Σ edge_count(规则 3)。 +// ENT(4) 条目表: [ent_count u32] [×28B {var_id i32, version u32, +// def_nod i32, live_start u32, live_end u32(半开: +// 最后使用点+1), home i32, flags u32}] +// v7 Task 2 起实记录(corec 产时重建 = 直调内核 +// compute_live_ranges/compute_entries(ent_kernel.cr—— +// 内核抽取 Task 2 写侧单源化,镜像消除;文件 ENT = 校验面 +// + 语义消费通道数据源);内存表 24B/条(闭区间、无 +// version,内核与写侧共用)→ 落盘: +// version = 同 var 组内定值升序序数(1-based), +// live_end_disk = live_end_mem + 1;盘上 7 字段 = 28B。 +// home 恒 -1(实例注记——分配决策不写回格式,字节 +// spec §3.5);flags 恒 0(无配方/参数/全局/驱逐位 +// 零实例——位语义保留) +// REG(5) region 表: [sg_count u32] [×24B {kind u32, parent i32, +// enter_nod u32, exit_nod u32(v5 enter/exit 指令号 = +// NOD 坐标,语义不变), first_ent i32, last_ent i32}] +// v5 的 nstart/ncount 不再落盘——nstart ≡ enter、 +// ncount = exit − enter(sg_push/sg_pop 不变式), +// load 内存态重建与 v5 记录逐字节一致。 +// first_ent/last_ent(区内条目范围)语义:条目按其 +// def_nod ∈ [enter_nod, exit_nod) 归属 region(定值点 +// 升序 → 文件条目序连续一段);根 region(kind=SG_FUNC) +// = 整个函数条目块(含 def=-1 参数条目);无条目 = -1。 +// TYPE(7) 类型面(R2 P4 Task 2 内容面;D12 两小节 = 类型行表 24B/条 +// {kind,data,extra}(i64×3——与内存 g_types 槽逐位同源) +// + 类型项 DAG 40B/条 {tag,a..d}(i64×5),哈希不落盘 +// (加载侧 tt_hash5 重算)→ load 重建 g_types/ +// g_type_terms/g_tt_index,失败 = 整体拒绝(**不得** +// 留空表)。段体构造 = corec-only 的 ccr_types.cr(D18: +// 装填引用桥接层 sh_term_of_ti,corearch 无此层);本文件 +// 只搬运 g_ccr_type_seg 缓冲(保存侧)+ 解析重建(加载侧)。 +// loader 逐条校验(违规 = 拒绝):两小节计数/长度自洽 + +// 段体无尾随字节;tag ∈ 0..10(TT_*);a..d ≥ -1;子项 +// 引用 < 自身行号(标注槽不查——字段表见 ccr_type_term_ref_ok)。 +// 段内容确定性 = ccr_types.cr 的 D13 装填(类型表/接口表 +// 的纯函数,与判定历史无关)。**段序 7/8 的数值权威 = +// 本文件段序**(D9;format spec §2 的 `9+` 预留顺移驱逐 +// 标注段/证书段——两段代码零实现,仅注释冲突) +// IFACE(8) 接口面(R2 P4 Task 1 空壳 = [native_count u32 = 0];内容面归 +// Task 3:D14 五小节 = 原生条目/横切形状/用户接口签名 +// 项/impl 边/方法表) +// Task 1 空壳纪律:段体恒 4B count = 0;loader 对 +// count != 0 或段体非 4B **拒绝**(内容面落地前不接受 +// 外部半成品——不得静默当空表,三态纪律 C.5-3)。 +// 两段必备(D11:have7/have8 == 0 → 拒绝)——可选段 = +// 两种 .ccr 在野 = 静默降级面。 +// 载荷约定(corec → corearch):NOD 段 = v5 instrs 坐标化(字段同布局)—— +// corearch 消费路径不变(硬约束);ENT 由 corearch 加载校验,发射不依赖。 +// 内存态重建(load 后 = 本文件字节的投影,ELF 发射语义零变化):g_ir_vars 行 +// (+ TYPE 段:g_types 行表 + g_type_terms 项 DAG + g_tt_index 索引,见 TYPE 段注) +// {name,id,type}(id = 行序)、g_ir_globals {name,var_idx,init_val}(var_idx = +// 行序)、g_ir_func_* 七数组(instr/var 范围派生,见 (c))、g_sgs(nstart/ +// ncount 派生)、g_ir_entries 24B 表 + func 条目块(块界按 SYM func first/last +// 校验)。 +// 与 v5 差异:固定 36B 头 + 定序段 → Header + 段表;entries 段新增; +// v5 参考:magic/version=5/7 计数在固定偏移,本文件旧注释已废弃。 // --- Byte buffer helpers --- // No bitwise ops in Core — use arithmetic instead. CCR_MAGIC : int = 827474755; // "CCR1" (0x31524343) +CCR_VERSION : int = 9; // v9-only(load 校验 ==9;拒 version≠9——D10 先例:v8 及更早整类拒收,**不得**静默当「段/字段缺席 = 空表」)。 +// R2 P6 Task 3(β):v8→v9 = NOD 记录 36→40B(+28 项索引 i32、邻接域顺移 +32/+36)——旧文件在版本闸整类拒收 +// (cache miss 语义 = 使用者重编;.cir 快照面零改动 ⇒ CIR_CACHE_VER 保持 17) +CCR_SEG_COUNT : int = 8; // STR SYM NOD ENT REG EDG TYPE IFACE(规范序 tag 1..8;预留 9+ 不占空间——D9:原 7/8 预留段顺移) +CCR_SEG_TYPE : int = 7; // TYPE 段 tag(数值权威 = 段序;D9) +CCR_SEG_IFACE : int = 8; // IFACE 段 tag(D9) -// On-disk SG record size: 6 × i32 = 24 bytes (kind/enter/exit/parent/nstart/ncount). +// On-disk NOD record size: 32B 语义区(8 字段)+ 邻接 2 × u32 = 40 bytes — v7 spec §3.3 +// {op, dest, s1(i64), src2, src3, tk, item, first_edge, edge_count}(v5 原 28B 语义 +// 字段逐字节不变 + R2 P6 Task 3 的 4B 项索引 = 32B 语义区;first_edge/edge_count = +// 节点出边在 EDG 段的连续段索引)。`tk` = **派生码**(sh_dfn_code_of_slots, +// 语义/字节与 v8 逐字节同——辅码面节点即靠它);`item` = **TYPE 段文件空间** +// 项索引(i32,-1 = 无项;写侧装填 = ccr_types.cr:ccr_nod_item_populate, +// 读侧 = load_ccr 的 TYPE 段后一致性硬校验——见该段注)。内存对象记录 = 本记录 +// 前 32B(ESZ_NOD_SEM,剥离邻接)——见 ent_kernel.cr 语义对象节。 +ESZ_NOD_DISK : int = 40; + +// On-disk EDG record size: 2 × u32 = 8 bytes — v7 spec §3.4 +// {to_nod u32, kind u32}(from = 所属节点,邻接隐含——文件序节点 i 的出边 +// 在 [first_edge, first_edge+edge_count) 连续段内) +ESZ_EDGE_DISK : int = 8; + +// On-disk REG record size: 6 × i32 = 24 bytes — v6 坐标化字段序 +// {kind, parent, enter_nod, exit_nod, first_ent, last_ent}(v5 的 nstart/ncount +// 不落盘——load 由 enter/exit 派生:nstart = enter, ncount = exit − enter)。 // NOTE: the in-memory SG entry is ESZ_SG (48 bytes, u64 fields) — that is NOT // the wire format. Always use ESZ_SG_DISK for .ccr size math, never ESZ_SG. ESZ_SG_DISK : int = 24; +// SYM 落盘记录尺寸: +// 全局记录 16B {name u32, type u32, init_val i64}(v5 var_idx 槽 → type) +// 函数记录 = 24B 定长头 {name, param_count, ret_type, root_region, +// first_ent, last_ent} + param_ents[i32 × param_count] +// + {var_count u32} + var_decls[8B × var_count] +ESZ_GLOBAL_DISK : int = 16; +ESZ_FUNC_HEAD_DISK : int = 24; +ESZ_VARDECL_DISK : int = 8; + +// On-disk ENT record: 7 × 4B = 28 bytes (var_id/version/def_nod/live_start/ +// live_end/home/flags; version + 半开 live_end 由内存 24B 表转换,见文件头注释). +// The in-memory entry table is ESZ_ENTRY (24 bytes, no version field — the +// per-var ordinal is derivable from group order); disk record ≠ memory record. +ESZ_ENTRY_DISK : int = 28; + fn bw_byte(val: int, shift: int) -> int { if shift == 0 { return val % 256; } if shift >= 24 { return (val / 16777216) % 256; } @@ -110,6 +259,8 @@ fn ccr_validate_i32_fields() -> int { ccr_i32_fits(iri_s3(ii)) == 0 { return 0; } ii = ii + 1; } + // REG 落盘字段 = kind/parent/enter/exit(nstart/ncount 由 enter/exit 派生, + // 不再落盘——v5 校验过的内存 nstart/ncount 字段随之免除) si : ., mut = 0; loop { if si >= g_sg_count { break; } @@ -117,63 +268,84 @@ fn ccr_validate_i32_fields() -> int { if ccr_i32_fits(r64(g_sgs, f + OFF_SG_KIND)) == 0 || ccr_i32_fits(r64(g_sgs, f + OFF_SG_ENTER)) == 0 || ccr_i32_fits(r64(g_sgs, f + OFF_SG_EXIT)) == 0 || - ccr_i32_fits(r64(g_sgs, f + OFF_SG_PARENT)) == 0 || - ccr_i32_fits(r64(g_sgs, f + OFF_SG_NSTART)) == 0 || - ccr_i32_fits(r64(g_sgs, f + OFF_SG_NCOUNT)) == 0 { return 0; } + ccr_i32_fits(r64(g_sgs, f + OFF_SG_PARENT)) == 0 { return 0; } si = si + 1; } return 1; } -// --- Size calculation --- +// 函数 fi 的根 region = REG 行序第 fi 个 SG_FUNC 行(压栈序:每个已编译函数 +// df_begin_func 推 SG_FUNC,嵌套行紧随其根行——根行按函数序 1:1)。 +// 返回行 id;-1 = 越界/结构不一致。 +fn ccr_func_root_sg(func_i: int) -> int { + if func_i < 0 { return -1; } + k : ., mut = 0; + si : ., mut = 0; + loop { + if si >= g_sg_count { break; } + if r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_FUNC { + if k == func_i { return si; } + k = k + 1; + } + si = si + 1; + } + return -1; +} -fn calc_ccr_size() -> int { - sz : ., mut = 36; // header + counts +// --- Segment size calculation(段体大小;Header+段表 = 16 + 12×6 = 88)--- +// 每段自带计数 u32;写侧与 calc 侧逐字节一致(v7 测试 walk 校验 end==fsize)。 - // strings +fn ccr_str_seg_size() -> int { + sz : ., mut = 4; // str_count si : ., mut = 0; loop { if si >= g_str_count { break; } - sl := istr_len(si); - sz = sz + 4 + sl; + sz = sz + 4 + istr_len(si); si = si + 1; } + return sz; +} - sz = sz + g_ir_func_count * 28; // func meta - sz = sz + g_ir_instr_count * 28; // instrs(s1 为 64 位) - sz = sz + g_ir_var_count * 12; // vars - sz = sz + g_ir_str_const_count * 4; // str_consts - - // structs +fn ccr_sym_seg_size() -> int { + // 布局 = ccr_sym_seg_size/save_ccr/load_ccr 三方逐字节一致(见头注释): + // globals(16B) → funcs(24B 头 + param_ents + var_count + var_decls 8B) + // → str_consts → structs → enums → opt_meta + sz : ., mut = 4 + g_ir_global_count * ESZ_GLOBAL_DISK; // global_count + globals + sz = sz + 4; // func_count + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + pc := r64(g_ir_func_param_count, fi * 8); + vc := r64(g_ir_func_var_count, fi * 8); + sz = sz + ESZ_FUNC_HEAD_DISK + pc * 4 + 4 + vc * ESZ_VARDECL_DISK; + fi = fi + 1; + } + sz = sz + 4 + g_ir_str_const_count * 4; // str_const_count + str_consts + // structs: struct_count + {name, field_count, fields×8B} + sz = sz + 4; sti : ., mut = 0; loop { if sti >= g_struct_count { break; } - fc := si_field_count(sti); - sz = sz + 8 + fc * 8; + sz = sz + 8 + si_field_count(sti) * 8; sti = sti + 1; } - - // enums + // enums: enum_count + {name, variant_count, variants×{name, tc, types×4B}} + sz = sz + 4; ei : ., mut = 0; loop { if ei >= g_enum_count { break; } vc := ei_variant_count(ei); - sz = sz + 8; // name_idx + variant_count + sz = sz + 8; // name + variant_count vi : ., mut = 0; loop { if vi >= vc { break; } tc := ei_variant_type_count(ei, vi); - sz = sz + 8; // variant_name_idx + field_count - sz = sz + tc * 4; + sz = sz + 8 + tc * 4; // variant name + tc + types vi = vi + 1; } ei = ei + 1; } - - // globals: count + triples of (name_idx, var_idx, init_val) = 16 bytes each - sz = sz + 4 + g_ir_global_count * 16; - - // optimization metadata: count + [key, data_len, data] + // opt_meta: opt_count + {key, len, data} sz = sz + 4; mi : ., mut = 0; loop { @@ -181,37 +353,256 @@ fn calc_ccr_size() -> int { sz = sz + 8 + r32(g_opt_meta, mi * OPT_META_STRIDE + 4); mi = mi + 1; } + return sz; +} - // v5: SG (region) section — count + sg_count × 24B records (6×i32) - sz = sz + 4 + g_sg_count * ESZ_SG_DISK; +fn ccr_nod_seg_size() -> int { + return 4 + g_ir_instr_count * ESZ_NOD_DISK; +} +fn ccr_edg_seg_size(edge_total: int) -> int { + return 4 + edge_total * ESZ_EDGE_DISK; +} + +fn ccr_ent_seg_size() -> int { + return 4 + g_entry_count * ESZ_ENTRY_DISK; +} + +fn ccr_reg_seg_size() -> int { + return 4 + g_sg_count * ESZ_SG_DISK; +} + +// R2 P4 Task 2:TYPE(7) 段体大小 = 段体缓冲长度(D18:内容构造 = corec-only +// ccr_types.cr;本文件保持共享层纯度——ccr_io.cr 同时在 corearch 清单内,任何 +// 前端符号引用 = corearch N06 静默未定义)。缓冲**含首字段 row_count** ⇒ 段体 +// 大小 ≡ 缓冲长度(单源;T1 空壳期注释写的「4 + 段体缓冲长度」是缓冲不含计数的 +// 方案,实施取「缓冲 = 完整段体」,偏差登记见 Task 2 报告)。 +fn ccr_type_seg_size() -> int { + return g_ccr_type_seg_len; +} + +// R2 P4 Task 3:IFACE(8) 段体大小(D14 五小节;D18 解耦——内容构造 = corec-only +// ccr_types.cr 的 ccr_iface_populate/ccr_iface_seg_build;本文件(corearch 也链接) +// 只按段表搬运/解析)。缓冲**含首字段 native_count** ⇒ 段体大小 ≡ 缓冲长度(单源)。 +fn ccr_iface_seg_size() -> int { + return g_ccr_iface_seg_len; +} + +// --- Size calculation(v8:16B header + 8×12B seg table + 各段体)--- +// edge_total = EDG 记录总数(save_ccr 先行收集,见 ccr_collect_edges)。 + +fn calc_ccr_size(edge_total: int) -> int { + sz : ., mut = 16 + CCR_SEG_COUNT * 12; + sz = sz + ccr_str_seg_size(); + sz = sz + ccr_sym_seg_size(); + sz = sz + ccr_nod_seg_size(); + sz = sz + ccr_ent_seg_size(); + sz = sz + ccr_reg_seg_size(); + sz = sz + ccr_edg_seg_size(edge_total); + sz = sz + ccr_type_seg_size(); + sz = sz + ccr_iface_seg_size(); return sz; } -// --- Save --- +// --- EDG 内容收集(写侧;v7 spec §3.4/§4)--- +// g_df_edges = 每节点头插出边链表(dataflow.cr df_add_edge_kind,OFF_DFE_NEXT +// 链)。落盘前按节点序单遍走查 → 每节点出边连续段(first_edge = 前缀累计) +// + 扁平 {to, kind} 值缓冲(8B/边,to + kind×2^32 打包)。写侧守卫与 loader +// 同域(v7 守卫面强度保持):链表归属错乱/后向边(to_nod ≤ 所属节点,自环 +// 含)/kind > 1 → 拒绝落盘(内存图 = 文件 EDG 的唯一源,loader 永不拒绝 +// 自己 writer 的产物)。 +fn ccr_collect_edges(edge_counts: string, edge_offs: string) -> int { + total : ., mut = 0; + ni : ., mut = 0; + loop { + if ni >= g_ir_instr_count { break; } + w64(edge_offs, ni * 8, total); + cnt : ., mut = 0; + eid : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE); + loop { + if eid < 0 { break; } + if eid >= g_df_edge_count { return -1; } // 链表越界(缓存恢复错乱防御) + if r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_FROM) != ni { return -1; } + eto := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO); + if eto <= ni { return -1; } // v7 §4 规则 1:to_nod > 所属节点 + if r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND) > 1 { return -1; } + cnt = cnt + 1; + eid = r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT); + } + w64(edge_counts, ni * 8, cnt); + total = total + cnt; + ni = ni + 1; + } + return total; +} + +// 第二遍:按节点序把出边写入扁平缓冲(slot = edge_offs[i] 起连续) +fn ccr_fill_edge_buf(buf: string, edge_offs: string) { + ni : ., mut = 0; + loop { + if ni >= g_ir_instr_count { break; } + slot : ., mut = r64(edge_offs, ni * 8); + eid : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE); + loop { + if eid < 0 { break; } + eto := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO); + ekind := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND); + w64(buf, slot * 8, eto + ekind * 4294967296); + slot = slot + 1; + eid = r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT); + } + ni = ni + 1; + } +} + +// --- v7 读侧载入缓冲(EDG 校验后保留——图边语义对象:内核完备 Task 1 起经 +// 对象面 v7_edge_to/v7_edge_kind 消费(ent_kernel.cr——节点出边遍历 = 配方 +// 输入集);g_df_edges 在 corearch 编译物中存在但 dataflow.cr 不在其内—— +// 独立缓冲)--- +g_v7_edges : string, mut; // EDG 扁平记录(8B/条,文件序 = 节点运行序) +g_v7_edge_count : int, mut; + +// --- Entry-table accessors(内存 24B 表读——写侧/loader 本地别名;内核 +// ent_* 等价访问器在 ent_kernel.cr(双 concat 共享,2026-09-10 Task 2 起), +// 本组保留不收敛(按实际引用删原则)。buf_read_i32 带符号扩展同内核。)--- +fn ccr_ent_var(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_VAR); } +fn ccr_ent_def(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_DEF); } +fn ccr_ent_ls(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_LS); } +fn ccr_ent_le(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_LE); } +fn ccr_ent_home(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_HOME); } +fn ccr_ent_flags(e: int) -> int { return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_FLAGS); } + +// --- Entry 表载体 grow 注记(内核抽取 Task 2:写侧单源化)--- +// compute_entries_v7/ccr_grow_* 镜像已删——corec 写侧直调内核 +// compute_live_ranges()(尾部逐函数内调 compute_entries;grow = 内核 +// grow_entries/grow_func_entry_meta,ent_kernel.cr 双 concat 共享单实例)。 +// loader(corearch 进程)ENT 载入同样直调内核 grow(函数体 = 原本地副本 +// 逐字节同源——语义零变化)。 + +// 函数条目块 [es, es+ec) 内 var 的 def=-1 条目 id(SYM func param_ents 回填 +// 用——参数「入参」条目 = 该参数 def=-1 条目;被重定值/从未引用 → 无 = -1; +// def=-1 补丁每 var 至多一条)。写侧守卫:返回 id 恒 ≥ es 且在块内。 +fn ccr_param_entry_id(var_id: int, es: int, ec: int) -> int { + e : ., mut = 0; + loop { + if e >= ec { break; } + ei := es + e; + if ccr_ent_var(ei) == var_id && ccr_ent_def(ei) < 0 { return ei; } + e = e + 1; + } + return -1; +} + +// --- 写侧对象镜像(内核完备 Task 2——A 通道中立化写侧载体裁决 2026-09-10)--- +// corec 写侧不经 loader——内核 compute 系(ent_kernel.cr)现读 NOD 对象面 +// nod_*(compute_live_ranges/compute_entries/… 的 iri_* 4 使用点已中立化): +// 本函数在 compute 前把线性流 g_ir_instrs 单遍镜像到内核对象缓冲 +// g_v7_nod_sem(28B 语义字段——字段序/宽度与 loader 解析逐字节对称:op u32 +// @0/dest i32 @4/s1 i64 @8/s2 i32 @16/s3 i32 @20/tk u32 @24,布局 = 盘 36B +// 记录剥离邻接——OFF_NS_*/ESZ_NOD_SEM 见 ent_kernel.cr 语义对象节)。数值 +// 与本函数下方 NOD 段写盘同源(同 g_ir_instrs 直读、save_ccr 头已过 +// ccr_validate_i32_fields 形状守卫)→ loader 载入文件产生的镜像与本函数产物 +// 逐字节同 —— compute 尾随逐函数 compute_entries,ENT 数据面双进程同值、 +// 产物 byte-identical(行为零变化判据)。 +fn populate_nod_objects() { + g_v7_nod_sem = alloc((g_ir_instr_count + 8) * ESZ_NOD_SEM); + g_v7_nod_count = g_ir_instr_count; + ii : ., mut = 0; + loop { + if ii >= g_ir_instr_count { break; } + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, iri_op(ii)); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, iri_dest(ii)); + w64(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, iri_s1(ii)); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, iri_s2(ii)); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, iri_s3(ii)); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, iri_tk(ii)); + // R2 P6 Task 3(β):项索引(TYPE 段文件空间;-1 = 无项)。缓冲由 corec-only + // 的 ccr_types.cr:ccr_nod_item_populate 装填(D18:本文件在 corearch 清单内, + // 此处**不得**引桥接层符号),本函数纯搬运;缓冲短于节点数(不经 + // ccr_seg_prepare_save 的诊断路径)⇒ 落 -1(响亮面由 load 侧一致性校验承担)。 + it0 : ., mut = -1; + if ii < g_ccr_nod_item_count { it0 = buf_read_i32(g_ccr_nod_item, ii * 4); } + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it0); + ii = ii + 1; + } +} + +// --- Save(写侧与 calc 侧一致;段表规范序、段体连续)--- fn save_ccr(path: string) -> int { - // The v5 wire format stores these fields as signed i32. Refuse to emit a - // lossy file instead of letting w32 silently keep only the low bits. + // The v7 wire format stores these fields as signed i32 — 编码层文件格式 + // 限制(字段形状 = 文件布局域,hw-map/经典投影实例;与 int 语义无涉, + // int-unbounded-semantics 定稿 §三)。Refuse to emit a lossy file instead + // of letting w32 silently keep only the low bits. if ccr_validate_i32_fields() == 0 { return -1; } - tsz := calc_ccr_size(); + + // v7 Task 2(ENT 主干化)+ 内核抽取 Task 2(写侧单源化):ENT 数据面先行 + // ——corec 写侧直调内核 compute_live_ranges()(尾部逐函数内调 compute_entries, + // 填 g_ir_entries 内存 24B 闭区间表 / g_entry_count + 函数块界 + // g_ir_func_entry_start/count)。镜像 compute_entries_v7 已消除——双转录 + // R5 收敛;corearch 判定/分配自算 = 同一内核(双进程同源)。文件 ENT = 校验 + // 面 + 语义消费通道数据源,行为零变化(半开转换 live_end+1 仍在下方写点)。 + // 内核完备 Task 2(A 通道中立化——写侧载体裁决 2026-09-10):compute 系 + // 已改读 NOD 对象面(nod_*——ent_kernel.cr iri_* 4 使用点中立化),corec + // 写侧不经 loader → 本进程 compute 前先 populate(g_ir_instrs → + // g_v7_nod_sem 镜像,与 loader 载入对称);同值 → ENT 产物与直读时代 + // byte-identical(双进程同实现,无第二套 compute)。 + populate_nod_objects(); + compute_live_ranges(); + + // v7:EDG 内容先收集(NOD 邻接域 + 段尺寸先决)——g_df_edges 内存 = + // 头插链表,按节点序走查 → 每节点出边连续段(first_edge = 前缀累计) + edge_counts := alloc((g_ir_instr_count + 8) * 8); + edge_offs := alloc((g_ir_instr_count + 8) * 8); + edge_total := ccr_collect_edges(edge_counts, edge_offs); + if edge_total < 0 { return -1; } + edge_buf : string, mut = ""; + if edge_total > 0 { + edge_buf = alloc(edge_total * 8); + ccr_fill_edge_buf(edge_buf, edge_offs); + } + + tsz := calc_ccr_size(edge_total); buf := alloc(tsz); pos : ., mut = 0; - // Magic + version (v5 = serialization v2: SG region section appended) + // Header(16B) buf_write_u32(buf, pos, CCR_MAGIC); pos = pos + 4; - buf_write_u32(buf, pos, 5); pos = pos + 4; + buf_write_u32(buf, pos, CCR_VERSION); pos = pos + 4; + buf_write_u32(buf, pos, CCR_SEG_COUNT); pos = pos + 4; + buf_write_u32(buf, pos, 0); pos = pos + 4; // reserved - // Counts - buf_write_u32(buf, pos, g_ir_func_count); pos = pos + 4; - buf_write_u32(buf, pos, g_ir_instr_count); pos = pos + 4; - buf_write_u32(buf, pos, g_ir_var_count); pos = pos + 4; - buf_write_u32(buf, pos, g_str_count); pos = pos + 4; - buf_write_u32(buf, pos, g_ir_str_const_count); pos = pos + 4; - buf_write_u32(buf, pos, g_struct_count); pos = pos + 4; - buf_write_u32(buf, pos, g_enum_count); pos = pos + 4; + // 段体大小(与 calc_ccr_size 同一来源逐段一致) + s1 := ccr_str_seg_size(); + s2 := ccr_sym_seg_size(); + s3 := ccr_nod_seg_size(); + s4 := ccr_ent_seg_size(); + s5 := ccr_reg_seg_size(); + s6 := ccr_edg_seg_size(edge_total); + s7 := ccr_type_seg_size(); // R2 P4 Task 2:内容面(缓冲长度) + s8 := ccr_iface_seg_size(); // R2 P4 Task 3:内容面(缓冲长度) + + // Seg table(8 × 12B;offset = 前段尾,从段表后起;规范序 tag 1..8) + o1 : ., mut = 16 + CCR_SEG_COUNT * 12; + o2 : ., mut = o1 + s1; + o3 : ., mut = o2 + s2; + o4 : ., mut = o3 + s3; + o5 : ., mut = o4 + s4; + o6 : ., mut = o5 + s5; + o7 : ., mut = o6 + s6; + o8 : ., mut = o7 + s7; + + buf_write_u32(buf, pos, 1); buf_write_u32(buf, pos + 4, o1); buf_write_u32(buf, pos + 8, s1); pos = pos + 12; + buf_write_u32(buf, pos, 2); buf_write_u32(buf, pos + 4, o2); buf_write_u32(buf, pos + 8, s2); pos = pos + 12; + buf_write_u32(buf, pos, 3); buf_write_u32(buf, pos + 4, o3); buf_write_u32(buf, pos + 8, s3); pos = pos + 12; + buf_write_u32(buf, pos, 4); buf_write_u32(buf, pos + 4, o4); buf_write_u32(buf, pos + 8, s4); pos = pos + 12; + buf_write_u32(buf, pos, 5); buf_write_u32(buf, pos + 4, o5); buf_write_u32(buf, pos + 8, s5); pos = pos + 12; + buf_write_u32(buf, pos, 6); buf_write_u32(buf, pos + 4, o6); buf_write_u32(buf, pos + 8, s6); pos = pos + 12; + buf_write_u32(buf, pos, CCR_SEG_TYPE); buf_write_u32(buf, pos + 4, o7); buf_write_u32(buf, pos + 8, s7); pos = pos + 12; + buf_write_u32(buf, pos, CCR_SEG_IFACE); buf_write_u32(buf, pos + 4, o8); buf_write_u32(buf, pos + 8, s8); pos = pos + 12; - // Strings + // === STR: strings === + buf_write_u32(buf, pos, g_str_count); pos = pos + 4; si : ., mut = 0; loop { if si >= g_str_count { break; } @@ -220,54 +611,91 @@ fn save_ccr(path: string) -> int { ci : ., mut = 0; loop { if ci >= sl { break; } - ch := str_load8(si, ci); - store8(buf, pos, ch); + store8(buf, pos, str_load8(si, ci)); pos = pos + 1; ci = ci + 1; } si = si + 1; } - // Func metadata + // === SYM: globals(16B each: name u32, type u32, init_val i64)=== + // v5 var_idx 槽 → type(var_idx 恒 == 行序——ir_gen reg_one_global 每全局 + // 一 var 行、行序与记录序锁步;下方校验强制,失配拒绝 = 防止未来布局漂移 + // 时静默生成行序错位的文件)。全局行 = var 命名空间前缀 0..G-1。 + buf_write_u32(buf, pos, g_ir_global_count); pos = pos + 4; + gi : ., mut = 0; + loop { + if gi >= g_ir_global_count { break; } + if r64(g_ir_globals, gi * 24 + 8) != gi { return -1; } + buf_write_u32(buf, pos, r64(g_ir_globals, gi * 24)); pos = pos + 4; // name + buf_write_u32(buf, pos, irv_type(gi)); pos = pos + 4; // type + buf_write_i64(buf, pos, r64(g_ir_globals, gi * 24 + 16)); pos = pos + 8; // init_val + gi = gi + 1; + } + + // === SYM: funcs(24B 头 + param_ents + var 声明区——v5 func_meta 的 + // instr_start/count 由 root_region span 取代、var_start/count 由声明区 + // 行序游标取代;v5 vars 表并入声明区:行序 = 创建序、前 param_count 个 + // = 参数。var 行序守卫:每函数声明区起点 == 前缀累计(globals 行 + 前 + // 函数 var 数),块序相接铺满 g_ir_vars)=== + // GC-4(SYM 评审 M2):位置级守卫——计数级(Σ == var_count + var_idx == gi) + // 总量守恒时察觉不到块错位(声明区左移/右移一格、总量不变 → 文件行序 + // 静默漂移,ENT var_id 命名空间错位)。失配 = 函数非序发射 → 拒绝;与 + // loader REG root span 校验(load 侧同域闭环)同风格。 + vcursor : ., mut = g_ir_global_count; + buf_write_u32(buf, pos, g_ir_func_count); pos = pos + 4; fi : ., mut = 0; loop { if fi >= g_ir_func_count { break; } + froot := ccr_func_root_sg(fi); + if froot < 0 { return -1; } + vc := r64(g_ir_func_var_count, fi * 8); + vs := r64(g_ir_func_var_start, fi * 8); + if vs != vcursor { return -1; } // 声明区起点 ≠ 前缀累计 → 行序错位 + vcursor = vcursor + vc; + pc := r64(g_ir_func_param_count, fi * 8); + if pc > vc { return -1; } + // v7 Task 2:条目块界实回填(内核 compute_live_ranges 已先行)——函数条目 + // 块 = ENT 文件序连续段 [es, es+ec)(SYM func 与 REG 根行同源双写; + // loader 块对照消费:pcnt==0 ↔ -1、ffe == 块首、fle == 块末) + es := r64(g_ir_func_entry_start, fi * 8); + ec := r64(g_ir_func_entry_count, fi * 8); + fe2 : ., mut = -1; + le2 : ., mut = -1; + if ec > 0 { fe2 = es; le2 = es + ec - 1; } buf_write_u32(buf, pos, r64(g_ir_func_name_idx, fi * 8)); pos = pos + 4; - buf_write_u32(buf, pos, r64(g_ir_func_param_count, fi * 8)); pos = pos + 4; + buf_write_u32(buf, pos, pc); pos = pos + 4; buf_write_u32(buf, pos, r64(g_ir_func_ret_type, fi * 8)); pos = pos + 4; - buf_write_u32(buf, pos, r64(g_ir_func_instr_start, fi * 8)); pos = pos + 4; - buf_write_u32(buf, pos, r64(g_ir_func_instr_count, fi * 8)); pos = pos + 4; - buf_write_u32(buf, pos, r64(g_ir_func_var_start, fi * 8)); pos = pos + 4; - buf_write_u32(buf, pos, r64(g_ir_func_var_count, fi * 8)); pos = pos + 4; + buf_write_i32(buf, pos, froot); pos = pos + 4; + buf_write_i32(buf, pos, fe2); pos = pos + 4; + buf_write_i32(buf, pos, le2); pos = pos + 4; + // param_ents 实回填:参数「入参」条目 = 该参数 var 的 def=-1 条目 id + // (函数内被重定值/从未引用 → -1;头注释 §3.2 语义)。loader 只做 + // 界拒绝(pid < -1)——与 ENT 的一致性由 Python 侧/测试网对照。 + pp : ., mut = 0; + loop { + if pp >= pc { break; } + buf_write_i32(buf, pos, ccr_param_entry_id(vs + pp, es, ec)); + pos = pos + 4; + pp = pp + 1; + } + // var 声明区(行序声明——name/type;ENT「存在即声明」的名称/类型投影) + buf_write_u32(buf, pos, vc); pos = pos + 4; + vv : ., mut = 0; + loop { + if vv >= vc { break; } + row := vs + vv; + if row < 0 || row >= g_ir_var_count { return -1; } + buf_write_u32(buf, pos, irv_name(row)); pos = pos + 4; + buf_write_u32(buf, pos, irv_type(row)); pos = pos + 4; + vv = vv + 1; + } fi = fi + 1; } + if vcursor != g_ir_var_count { return -1; } // 末块未铺满命名空间 → 洞 - // Instructions - ii : ., mut = 0; - loop { - if ii >= g_ir_instr_count { break; } - buf_write_u32(buf, pos, iri_op(ii)); pos = pos + 4; - buf_write_i32(buf, pos, iri_dest(ii)); pos = pos + 4; - // 修复 14:s1 改 64 位——IR_CONST 的大 int 常量 / float 位模式 - // 之前被截断成 32 位(float 常量静默损坏) - buf_write_i64(buf, pos, iri_s1(ii)); pos = pos + 8; - buf_write_i32(buf, pos, iri_s2(ii)); pos = pos + 4; - buf_write_i32(buf, pos, iri_s3(ii)); pos = pos + 4; - buf_write_u32(buf, pos, iri_tk(ii)); pos = pos + 4; - ii = ii + 1; - } - - // IR variables - vi : ., mut = 0; - loop { - if vi >= g_ir_var_count { break; } - buf_write_u32(buf, pos, irv_name(vi)); pos = pos + 4; - buf_write_u32(buf, pos, irv_id(vi)); pos = pos + 4; - buf_write_u32(buf, pos, irv_type(vi)); pos = pos + 4; - vi = vi + 1; - } - - // String constants + // === SYM: string constants === + buf_write_u32(buf, pos, g_ir_str_const_count); pos = pos + 4; sci : ., mut = 0; loop { if sci >= g_ir_str_const_count { break; } @@ -275,7 +703,8 @@ fn save_ccr(path: string) -> int { sci = sci + 1; } - // Structs + // === SYM: structs === + buf_write_u32(buf, pos, g_struct_count); pos = pos + 4; sti : ., mut = 0; loop { if sti >= g_struct_count { break; } @@ -292,42 +721,32 @@ fn save_ccr(path: string) -> int { sti = sti + 1; } - // Enums - ei : ., mut = 0; + // === SYM: enums === + buf_write_u32(buf, pos, g_enum_count); pos = pos + 4; + ei2 : ., mut = 0; loop { - if ei >= g_enum_count { break; } - vc := ei_variant_count(ei); - buf_write_u32(buf, pos, ei_name(ei)); pos = pos + 4; + if ei2 >= g_enum_count { break; } + vc := ei_variant_count(ei2); + buf_write_u32(buf, pos, ei_name(ei2)); pos = pos + 4; buf_write_u32(buf, pos, vc); pos = pos + 4; vi2 : ., mut = 0; loop { if vi2 >= vc { break; } - tcnt := ei_variant_type_count(ei, vi2); - buf_write_u32(buf, pos, ei_variant_name(ei, vi2)); pos = pos + 4; + tcnt := ei_variant_type_count(ei2, vi2); + buf_write_u32(buf, pos, ei_variant_name(ei2, vi2)); pos = pos + 4; buf_write_u32(buf, pos, tcnt); pos = pos + 4; tf : ., mut = 0; loop { if tf >= tcnt { break; } - buf_write_u32(buf, pos, ei_variant_type(ei, vi2, tf)); pos = pos + 4; + buf_write_u32(buf, pos, ei_variant_type(ei2, vi2, tf)); pos = pos + 4; tf = tf + 1; } vi2 = vi2 + 1; } - ei = ei + 1; - } - - // Globals: (name_idx u32, var_idx u32, init_val i64) = 16 bytes each - buf_write_u32(buf, pos, g_ir_global_count); pos = pos + 4; - gi : ., mut = 0; - loop { - if gi >= g_ir_global_count { break; } - buf_write_u32(buf, pos, r64(g_ir_globals, gi * 24)); pos = pos + 4; // name_idx - buf_write_u32(buf, pos, r64(g_ir_globals, gi * 24 + 8)); pos = pos + 4; // var_idx - w64(buf, pos, r64(g_ir_globals, gi * 24 + 16)); pos = pos + 8; // init_val - gi = gi + 1; + ei2 = ei2 + 1; } - // Optimization metadata (version 3+) + // === SYM: opt_meta === buf_write_u32(buf, pos, g_opt_meta_count); pos = pos + 4; mi : ., mut = 0; loop { @@ -346,21 +765,162 @@ fn save_ccr(path: string) -> int { mi = mi + 1; } - // v5: SG (region) section — sg_count × 24B (6×i32: kind/enter/exit/parent/nstart/ncount) + // === NOD: instructions(36B each;NOD id = 文件序 = 全局指令序;28B + // 语义字段与 v6 逐字段一致 + first_edge/edge_count 邻接索引——EDG 段 + // 由 edge_buf 在段体末尾落盘)=== + buf_write_u32(buf, pos, g_ir_instr_count); pos = pos + 4; + ii : ., mut = 0; + loop { + if ii >= g_ir_instr_count { break; } + buf_write_u32(buf, pos, iri_op(ii)); pos = pos + 4; + buf_write_i32(buf, pos, iri_dest(ii)); pos = pos + 4; + // 修复 14:s1 改 64 位——IR_CONST 的大 int 常量 / float 位模式 + // 之前被截断成 32 位(float 常量静默损坏) + buf_write_i64(buf, pos, iri_s1(ii)); pos = pos + 8; + buf_write_i32(buf, pos, iri_s2(ii)); pos = pos + 4; + buf_write_i32(buf, pos, iri_s3(ii)); pos = pos + 4; + buf_write_u32(buf, pos, iri_tk(ii)); pos = pos + 4; + // R2 P6 Task 3(β):项索引(i32;-1 = 无项)——与内存镜像同源 + // (g_ccr_nod_item,corec-only 侧装填;本文件纯搬运)。 + it1 : ., mut = -1; + if ii < g_ccr_nod_item_count { it1 = buf_read_i32(g_ccr_nod_item, ii * 4); } + buf_write_i32(buf, pos, it1); pos = pos + 4; + buf_write_u32(buf, pos, r64(edge_offs, ii * 8)); pos = pos + 4; // first_edge + buf_write_u32(buf, pos, r64(edge_counts, ii * 8)); pos = pos + 4; // edge_count + ii = ii + 1; + } + + // === ENT: entries(28B each;内存闭区间 → 盘上:version 组内序数、live_end+1 半开)=== + // 版本序数:每 var 一张计数槽(var 全局槽 id 唯一属一个函数——组内序 = 全局序) + buf_write_u32(buf, pos, g_entry_count); pos = pos + 4; + vcnt : string, mut = alloc((g_ir_var_count + 8) * 8); + vz : ., mut = 0; + loop { + if vz >= g_ir_var_count + 8 { break; } + w64(vcnt, vz * 8, 0); + vz = vz + 1; + } + ej : ., mut = 0; + loop { + if ej >= g_entry_count { break; } + ev := ccr_ent_var(ej); + ed := ccr_ent_def(ej); + els := ccr_ent_ls(ej); + ele := ccr_ent_le(ej); + eho := ccr_ent_home(ej); + efl := ccr_ent_flags(ej); + ord := r64(vcnt, ev * 8) + 1; + w64(vcnt, ev * 8, ord); + buf_write_i32(buf, pos, ev); pos = pos + 4; // var_id + buf_write_u32(buf, pos, ord); pos = pos + 4; // version(1-based) + buf_write_i32(buf, pos, ed); pos = pos + 4; // def_nod(-1 = 无定值) + buf_write_u32(buf, pos, els); pos = pos + 4; // live_start(含定值) + buf_write_u32(buf, pos, ele + 1); pos = pos + 4; // live_end(半开 = 内存闭区间 +1) + buf_write_i32(buf, pos, eho); pos = pos + 4; // home(-1 = 未分配) + buf_write_u32(buf, pos, efl); pos = pos + 4; // flags + ej = ej + 1; + } + + // === REG: region(24B each——spec §3.5 字段序 {kind, parent, enter_nod, + // exit_nod, first_ent, last_ent};v5 nstart/ncount 不落盘 = enter/exit 派生, + // 未闭合(exit < enter)region 无法表示 → 拒绝)=== + // first/last 语义:区内条目 = 定值点 def_nod ∈ [enter, exit)(定值点升序 → + // 文件条目序连续段);根 region(kind=SG_FUNC,函数 k = 行序第 k 个根)= + // 整个函数条目块(含 def=-1 参数条目);无条目 = -1。 buf_write_u32(buf, pos, g_sg_count); pos = pos + 4; + rfunc : ., mut = -1; // 当前根 region 的函数号(行序 = 压栈序) si2 : ., mut = 0; loop { if si2 >= g_sg_count { break; } f := si2 * ESZ_SG; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_KIND)); pos = pos + 4; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_ENTER)); pos = pos + 4; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_EXIT)); pos = pos + 4; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_PARENT)); pos = pos + 4; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_NSTART)); pos = pos + 4; - buf_write_i32(buf, pos, r64(g_sgs, f + OFF_SG_NCOUNT)); pos = pos + 4; + sk2 := r64(g_sgs, f + OFF_SG_KIND); + sen := r64(g_sgs, f + OFF_SG_ENTER); + sex := r64(g_sgs, f + OFF_SG_EXIT); + spa := r64(g_sgs, f + OFF_SG_PARENT); + if sex < sen { return -1; } // 未闭合 → ncount 不可派生 + if sk2 == SG_FUNC { rfunc = rfunc + 1; } + if rfunc < 0 || rfunc >= g_ir_func_count { return -1; } // 行序结构失配 + // v7 Task 2:条目范围实回填(内核 compute_live_ranges 已先行)——根 region + // (SG_FUNC)= 整个函数条目块(含 def=-1 参数条目;与 SYM func + // first/last 同源双写——loader 逐根行对照);嵌套 region = 定值点 + // def_nod ∈ [enter, exit) 的条目段(块内定值点升序 → 文件条目序连续 + // 一段;无 = -1)。头注释 §3.5 语义:区间 = def_nod 归属,非全块。 + fe2 : ., mut = -1; + le2 : ., mut = -1; + bes := r64(g_ir_func_entry_start, rfunc * 8); + bec := r64(g_ir_func_entry_count, rfunc * 8); + if sk2 == SG_FUNC { + if bec > 0 { fe2 = bes; le2 = bes + bec - 1; } + } else { + kk : ., mut = 0; + loop { + if kk >= bec { break; } + eid2 := bes + kk; + ed2 := ccr_ent_def(eid2); + if ed2 >= sen && ed2 < sex { + if fe2 < 0 { fe2 = eid2; } + le2 = eid2; + } + kk = kk + 1; + } + } + if sk2 == SG_FUNC { + // 根 region span = 函数指令范围(REG = 文件里函数边界的唯一真源, + // 与 func 表一致性校验——失配 = 内部状态漂移) + is2 := r64(g_ir_func_instr_start, rfunc * 8); + ic2 := r64(g_ir_func_instr_count, rfunc * 8); + if sen != is2 || sex != is2 + ic2 { return -1; } + } + buf_write_i32(buf, pos, sk2); pos = pos + 4; + buf_write_i32(buf, pos, spa); pos = pos + 4; + buf_write_i32(buf, pos, sen); pos = pos + 4; + buf_write_i32(buf, pos, sex); pos = pos + 4; + buf_write_i32(buf, pos, fe2); pos = pos + 4; + buf_write_i32(buf, pos, le2); pos = pos + 4; si2 = si2 + 1; } + // === EDG: edges(v7 必落;8B each {to_nod u32, kind u32}——所属节点 = + // 邻接隐含:节点 i 出边连续段 [first_edge, first_edge+edge_count), + // edge_buf = 收集遍的扁平记录(打包 to + kind×2^32,此处解包写 u32 对)=== + buf_write_u32(buf, pos, edge_total); pos = pos + 4; + ei3 : ., mut = 0; + loop { + if ei3 >= edge_total { break; } + ev := r64(edge_buf, ei3 * 8); + buf_write_u32(buf, pos, ev % 4294967296); pos = pos + 4; // to_nod + buf_write_u32(buf, pos, ev / 4294967296); pos = pos + 4; // kind + ei3 = ei3 + 1; + } + + // === TYPE(7):段体缓冲搬运(R2 P4 Task 2;内容构造 = corec-only + // ccr_types.cr 的 ccr_type_prepare_save,D18——本处只按段表搬运)=== + // 缓冲含首字段 row_count ⇒ 大小 ≡ ccr_type_seg_size()(同一来源)。空缓冲 = + // 未装填(调用点漏调 prepare)⇒ **拒绝落盘**:不得产出「两小节缺席」的 + // 半成品文件(三态纪律;行表恒含原生 9 行,空缓冲只可能是程序错误)。 + if g_ccr_type_seg_len <= 0 { return -1; } + ct : ., mut = 0; + loop { + if ct >= g_ccr_type_seg_len { break; } + store8(buf, pos, load8(g_ccr_type_seg, ct)); + pos = pos + 1; + ct = ct + 1; + } + + // === IFACE(8):段体缓冲搬运(R2 P4 Task 3;内容构造 = corec-only ccr_types.cr + // 的 ccr_iface_populate/ccr_iface_seg_build,D18——本处只按段表搬运)=== + // 缓冲含首字段 native_count ⇒ 大小 ≡ ccr_iface_seg_size()(同一来源)。空缓冲 = + // 未装填(调用点漏调 prepare)⇒ **拒绝落盘**(不得产出「五小节缺席」的半成品; + // 条目表恒 16 行 ⇒ 空缓冲只可能是程序错误,三态纪律)。 + if g_ccr_iface_seg_len <= 0 { return -1; } + ict : ., mut = 0; + loop { + if ict >= g_ccr_iface_seg_len { break; } + store8(buf, pos, load8(g_ccr_iface_seg, ict)); + pos = pos + 1; + ict = ict + 1; + } + // Use syscall directly (write_file uses str_len which stops at null) fd := syscall3(2, path, 577, 420); // open(O_WRONLY|O_CREAT|O_TRUNC, 0644) if fd < 0 { return -1; } @@ -371,10 +931,42 @@ fn save_ccr(path: string) -> int { return 0; } -// --- Load --- +// GC-4 测试钩子(corec ccr --inject-var-shift 载体;真实构建路径永不调用): +// func0 var 声明区起点左移 1 行(globals ≥ 1 时新块 [vs-1, vs-1+vc0) 恒留在 +// 命名空间内——行首行被 globals 末行顶替、行尾少写 func0 末行)。Σ 计数级 +// 守卫(Σ func var_count == g_ir_var_count,总量守恒)察觉不到;位置级守卫 +// (vs == 前缀累计)必须拒绝 save——模拟未来函数非序发射的静默错位。 +fn inject_var_shift() -> int { + if g_ir_func_count < 1 { return 1; } + vs := r64(g_ir_func_var_start, 0); + if vs < 1 { return 1; } // 无 globals 行可借位 → 注入前置不满足 + w64(g_ir_func_var_start, 0, vs - 1); + return 0; +} + +// --- Load(v8-only:校验 Header + 段表规范布局 + 逐段越界拒绝;version ≠ 8 +// = v7 读路径退役——拒绝。R2 P4 Task 1:段表 = 8 段规范序(加段成本 = 闸界 +// 6→8 一处,规范序/连续闸形状不变——D9),TYPE/IFACE 必落(D11)+ 空壳期 +// 内容校验(计数 = 0 且段体恰 4B))--- +// 内核完备 Task 1(语义对象模型):loader 产出 = 语义对象载入 + 守卫—— +// NOD→g_ir_instrs 线性重建段移出(调度重建 = 实例事务 build_linear_schedule, +// regalloc.cr;入口 = corearch.cr / 组合根 src/targets/x86_64-linux/main.cr +// 在 load 成功后调用)。 +// 解析序:STR → SYM(globals/funcs 声明区重建 var 命名空间行序)→ REG +// (nstart/ncount 派生 + func 指令边界回填)→ NOD(36B——28B 语义字段入对象 +// 缓冲 g_v7_nod_sem(对象留存形态裁决 (a),邻接域入 g_v7_nod_meta——不再写 +// 线性流)→ ENT(28B → 内存 24B 表,去掉 version、live_end 半开转回闭区间 +// −1;块界与 SYM func first/last 对照)→ EDG(邻接连续段校验 + 拓扑不变量 +// + 入 g_v7_edges 缓冲)→ TYPE/IFACE(R2 P4 Task 1 空壳:计数 = 0 且段体恰 +// 4B,非空拒绝——内容面归 Task 2/3)。守卫消费 = NOD 计数(局部 instr_cnt)+ 邻接域 +// (g_v7_nod_meta)+ REG 派生边界——全程零线性流(iri_*/g_ir_instrs)依赖。 +// 内存态(g_ir_vars 行 id=行序 / g_ir_globals var_idx=行序 / func 七数组 / +// g_sgs)与 v6 加载结果逐字节一致;线性流由 build_linear_schedule 从对象 +// 缓冲重建,与原重建段产物逐字节一致(28B 语义字段与 v6 逐字段相同,文件 +// 布局变化不影响下游——ELF 发射)。 fn load_ccr(data: string, fsize: int) -> int { - if fsize < 36 { return -1; } // minimum valid size + if fsize < 16 { return -1; } // header pos : ., mut = 0; @@ -382,59 +974,106 @@ fn load_ccr(data: string, fsize: int) -> int { magic := buf_read_u32(data, pos); pos = pos + 4; if magic != CCR_MAGIC { return -1; } - // Version (5 = serialization v2 with SG region section) + // Version — v6-only(无 v5 兼容/转换工具) ver := buf_read_u32(data, pos); pos = pos + 4; - if ver != 1 && ver != 2 && ver != 3 && ver != 4 && ver != 5 { return -1; } + if ver != CCR_VERSION { return -1; } - // Counts - func_cnt := buf_read_u32(data, pos); pos = pos + 4; - instr_cnt := buf_read_u32(data, pos); pos = pos + 4; - var_cnt := buf_read_u32(data, pos); pos = pos + 4; - str_cnt := buf_read_u32(data, pos); pos = pos + 4; - str_const_cnt := buf_read_u32(data, pos); pos = pos + 4; - struct_cnt := buf_read_u32(data, pos); pos = pos + 4; - enum_cnt := buf_read_u32(data, pos); pos = pos + 4; + // Seg count + reserved + seg_cnt := buf_read_u32(data, pos); pos = pos + 4; + rsv := buf_read_u32(data, pos); pos = pos + 4; + if seg_cnt > (fsize - 16) / 12 { return -1; } - // Reject impossible fixed-width counts before allocating their backing - // arrays. Variable sections are checked again at their exact position - // below, after preceding strings have been consumed. - if str_cnt > fsize / 4 || func_cnt > fsize / 28 || - instr_cnt > fsize / 28 || var_cnt > fsize / 12 || - str_const_cnt > fsize / 4 || struct_cnt > fsize / 8 || - enum_cnt > fsize / 8 { return -1; } - - // Grow dynamic arrays to needed capacity - grow_ir_vars(var_cnt); - grow_ir_instrs(instr_cnt); - grow_ir_func_meta(func_cnt); - grow_ir_str_consts(str_const_cnt); - grow_structs(struct_cnt); - grow_enums(enum_cnt); + // 段表:规范布局——tag 必须 = 行号(1..),offset 必须 = 前段尾,段不越界。 + // (段表 {tag, offset, size} 结构本身允段序自由,v7 writer 只用规范序—— + // loader 按规范序校验,非规范布局一律拒绝。) + seg_off1 : ., mut = 0; seg_off2 : ., mut = 0; seg_off3 : ., mut = 0; + seg_off4 : ., mut = 0; seg_off5 : ., mut = 0; seg_off6 : ., mut = 0; + seg_off7 : ., mut = 0; seg_off8 : ., mut = 0; + seg_end1 : ., mut = 0; seg_end2 : ., mut = 0; seg_end3 : ., mut = 0; + seg_end4 : ., mut = 0; seg_end5 : ., mut = 0; seg_end6 : ., mut = 0; + seg_end7 : ., mut = 0; seg_end8 : ., mut = 0; + have1 : ., mut = 0; have2 : ., mut = 0; have3 : ., mut = 0; + have4 : ., mut = 0; have5 : ., mut = 0; have6 : ., mut = 0; + have7 : ., mut = 0; have8 : ., mut = 0; + cursor : ., mut = 16 + seg_cnt * 12; + ri : ., mut = 0; + loop { + if ri >= seg_cnt { break; } + if !ccr_has_bytes(pos, 12, fsize) { return -1; } + tg := buf_read_u32(data, pos); pos = pos + 4; + soff := buf_read_u32(data, pos); pos = pos + 4; + ssz := buf_read_u32(data, pos); pos = pos + 4; + if tg < 1 || tg > CCR_SEG_COUNT { return -1; } + if tg != ri + 1 { return -1; } // 规范 tag 序 + if soff != cursor { return -1; } // 段体连续 + if ssz > fsize - cursor { return -1; } // 越界拒绝 + // 重复 tag 防御(规范序下不可能,双保险) + if tg == 1 { if have1 != 0 { return -1; } seg_off1 = soff; seg_end1 = soff + ssz; have1 = 1; } + if tg == 2 { if have2 != 0 { return -1; } seg_off2 = soff; seg_end2 = soff + ssz; have2 = 1; } + if tg == 3 { if have3 != 0 { return -1; } seg_off3 = soff; seg_end3 = soff + ssz; have3 = 1; } + if tg == 4 { if have4 != 0 { return -1; } seg_off4 = soff; seg_end4 = soff + ssz; have4 = 1; } + if tg == 5 { if have5 != 0 { return -1; } seg_off5 = soff; seg_end5 = soff + ssz; have5 = 1; } + if tg == 6 { if have6 != 0 { return -1; } seg_off6 = soff; seg_end6 = soff + ssz; have6 = 1; } + if tg == CCR_SEG_TYPE { if have7 != 0 { return -1; } seg_off7 = soff; seg_end7 = soff + ssz; have7 = 1; } + if tg == CCR_SEG_IFACE { if have8 != 0 { return -1; } seg_off8 = soff; seg_end8 = soff + ssz; have8 = 1; } + cursor = soff + ssz; + ri = ri + 1; + } + + // STR/SYM/NOD/REG 必备(v6 坐标化后 func 指令边界 = root_region span 的 + // 唯一真源,REG 缺段无法重建函数边界);EDG v7 必落(spec §3.4——文件 + // 语义载体 = NOD+EDG,缺边段 = 格式不一致拒绝);ENT 可缺(v5 精神: + // 旧段缺失 = 空表——Task 2 起 corec 恒产实记录,缺段等价空 = loader + // 兼容语义保留);TYPE/IFACE 必备(R2 P4 Task 1,D11——可选段 = 两种 + // .ccr 在野 = 任何消费者都要处理缺席 = 静默降级面;本阶段承诺即 + // 「信息恒随载体」,与 ENT 的可选先例不同源) + if have1 == 0 || have2 == 0 || have3 == 0 || have5 == 0 || have6 == 0 { return -1; } + if have7 == 0 || have8 == 0 { return -1; } + if have4 == 0 { seg_off4 = 0; seg_end4 = 0; } - // Reset arrays + // 状态重置(corearch 单次加载;保持可重入) g_str_count = 0; g_ir_var_count = 0; g_ir_instr_count = 0; g_ir_func_count = 0; g_ir_str_const_count = 0; - g_struct_count = struct_cnt; - g_enum_count = enum_cnt; - g_sg_count = 0; // v4 files carry no SG section; v5 restores below + g_struct_count = 0; + g_enum_count = 0; + g_sg_count = 0; + g_ir_global_count = 0; + g_opt_meta_count = 0; + g_entry_count = 0; + g_v7_edge_count = 0; + g_v7_nod_count = 0; // 内核完备 Task 1:NOD 对象缓冲计数(NOD 段载入后置位) + // R2 P4 Task 2:TYPE 段有真实内存表(g_types 行表 + 项层 g_type_terms/ + // g_tt_index)——复位在 TYPE 段解析处(行表大小随段内容,且需与项层 memo + // 同步作废;见该段注释)。 + // R2 P4 Task 3:IFACE 段同理有真实内存表(g_iface_entries/g_iface_shape_*/ + // g_ifaces/g_impl_for/g_methods)——计数复位在此(表缓冲随段内容重建; + // g_iface_registry_ok 清 0 = 「表未建立」,本段解析末尾置 1 = **段真源**)。 + g_iface_count = 0; + g_iface_entry_count = 0; + g_iface_registry_ok = 0; + g_iface_shape_count = 0; + g_impl_for_count = 0; + g_method_count = 0; - // Strings + // === STR: strings === + pos = seg_off1; + if !ccr_has_bytes(pos, 4, seg_end1) { return -1; } + str_cnt := buf_read_u32(data, pos); pos = pos + 4; si : ., mut = 0; loop { if si >= str_cnt { break; } - if !ccr_has_bytes(pos, 4, fsize) { return -1; } + if !ccr_has_bytes(pos, 4, seg_end1) { return -1; } sl := buf_read_u32(data, pos); pos = pos + 4; - if !ccr_has_bytes(pos, sl, fsize) { return -1; } + if !ccr_has_bytes(pos, sl, seg_end1) { return -1; } // Allocate buffer for string content s := alloc(sl + 1); ci : ., mut = 0; loop { if ci >= sl { break; } - ch := load8(data, pos); - store8(s, ci, ch); + store8(s, ci, load8(data, pos)); pos = pos + 1; ci = ci + 1; } @@ -443,126 +1082,166 @@ fn load_ccr(data: string, fsize: int) -> int { si = si + 1; } - // Func metadata + // === SYM: globals(16B each: name u32, type u32, init_val i64)=== + // var 命名空间重建(流式):全局行 = 记录序 0..G-1(行名/型随本记录携带, + // v5 的 var 行 = 同源冗余);id = 行序。后续函数声明区行序相接。 + pos = seg_off2; + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + gc := buf_read_u32(data, pos); pos = pos + 4; + if gc > (seg_end2 - seg_off2) / ESZ_GLOBAL_DISK { return -1; } + grow_ir_globals(gc); + gi : ., mut = 0; + loop { + if gi >= gc { break; } + if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end2) { return -1; } + gname_ni := buf_read_u32(data, pos); pos = pos + 4; + gtype := buf_read_u32(data, pos); pos = pos + 4; + ginit_val := buf_read_i64(data, pos); pos = pos + 8; + grow_ir_vars(g_ir_var_count + 1); + gvar := g_ir_var_count; + irv_set_name(gvar, gname_ni); + irv_set_id(gvar, gvar); + irv_set_type(gvar, gtype); + g_ir_var_count = gvar + 1; + w64(g_ir_globals, gi * 24, gname_ni); + w64(g_ir_globals, gi * 24 + 8, gvar); + w64(g_ir_globals, gi * 24 + 16, ginit_val); + g_ir_global_count = gi + 1; + gi = gi + 1; + } + + // === SYM: funcs(24B 头 + param_ents + var 声明区)=== + // 头字段 = spec §3.2:name/param_count/ret_type/root_region(REG 行 id)/ + // first_ent/last_ent(本函数条目文件范围)。instr/var 范围不落盘:instr = + // root_region span(REG 段解析后回填),var_start = 声明区行序游标。 + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + func_cnt := buf_read_u32(data, pos); pos = pos + 4; + if func_cnt > (seg_end2 - pos) / ESZ_FUNC_HEAD_DISK { return -1; } + grow_ir_func_meta(func_cnt); + // func 记录临时元数据(REG 段回填/ENT 块界校验用):{root i64, first i64, last i64} + fn_meta := alloc((func_cnt + 8) * 24); fi : ., mut = 0; loop { if fi >= func_cnt { break; } - if !ccr_has_bytes(pos, 28, fsize) { return -1; } - fv0 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_name_idx, fi * 8, fv0); - fv1 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_param_count, fi * 8, fv1); - fv2 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_ret_type, fi * 8, fv2); - fv3 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_instr_start, fi * 8, fv3); - fv4 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_instr_count, fi * 8, fv4); - fv5 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_var_start, fi * 8, fv5); - fv6 := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_func_var_count, fi * 8, fv6); + if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end2) { return -1; } + fname := buf_read_u32(data, pos); pos = pos + 4; + fpc := buf_read_u32(data, pos); pos = pos + 4; + fret := buf_read_u32(data, pos); pos = pos + 4; + froot := buf_read_i32(data, pos); pos = pos + 4; + ffe := buf_read_i32(data, pos); pos = pos + 4; + fle := buf_read_i32(data, pos); pos = pos + 4; + if froot < 0 { return -1; } // 每函数必有根 region(df_begin_func) + if ffe < -1 || fle < -1 || (ffe == -1) != (fle == -1) { return -1; } + w64(g_ir_func_name_idx, fi * 8, fname); + w64(g_ir_func_param_count, fi * 8, fpc); + w64(g_ir_func_ret_type, fi * 8, fret); + w64(fn_meta, fi * 24, froot); + w64(fn_meta, fi * 24 + 8, ffe); + w64(fn_meta, fi * 24 + 16, fle); + // param_ents(i32 each;内存无消费者——只做越界拒绝) + pj : ., mut = 0; + loop { + if pj >= fpc { break; } + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + pid := buf_read_i32(data, pos); pos = pos + 4; + if pid < -1 { return -1; } + pj = pj + 1; + } + // var 声明区(行序 = 创建序,前 param_count 个 = 参数) + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + fvc := buf_read_u32(data, pos); pos = pos + 4; + if fpc > fvc { return -1; } + if fvc > (seg_end2 - pos) / ESZ_VARDECL_DISK { return -1; } + fvs2 := g_ir_var_count; + w64(g_ir_func_var_start, fi * 8, fvs2); + dj : ., mut = 0; + loop { + if dj >= fvc { break; } + if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end2) { return -1; } + dname := buf_read_u32(data, pos); pos = pos + 4; + dtype := buf_read_u32(data, pos); pos = pos + 4; + grow_ir_vars(g_ir_var_count + 1); + drow := g_ir_var_count; + irv_set_name(drow, dname); + irv_set_id(drow, drow); + irv_set_type(drow, dtype); + g_ir_var_count = drow + 1; + dj = dj + 1; + } + if g_ir_var_count - fvs2 != fvc { return -1; } + w64(g_ir_func_var_count, fi * 8, fvc); g_ir_func_count = fi + 1; fi = fi + 1; } - // Instructions - ii : ., mut = 0; - loop { - if ii >= instr_cnt { break; } - if !ccr_has_bytes(pos, 28, fsize) { return -1; } - opcode := buf_read_u32(data, pos); pos = pos + 4; - dest := buf_read_i32(data, pos); pos = pos + 4; - s1 := buf_read_i64(data, pos); pos = pos + 8; // 修复 14:s1 64 位 - s2 := buf_read_i32(data, pos); pos = pos + 4; - s3 := buf_read_i32(data, pos); pos = pos + 4; - tk := buf_read_u32(data, pos); pos = pos + 4; - iri_set_op(ii, opcode); - iri_set_dest(ii, dest); - iri_set_s1(ii, s1); - iri_set_s2(ii, s2); - iri_set_s3(ii, s3); - iri_set_tk(ii, tk); - g_ir_instr_count = ii + 1; - ii = ii + 1; - } - - // IR variables - vi : ., mut = 0; - loop { - if vi >= var_cnt { break; } - if !ccr_has_bytes(pos, 12, fsize) { return -1; } - name_ni := buf_read_u32(data, pos); pos = pos + 4; - id := buf_read_u32(data, pos); pos = pos + 4; - tk := buf_read_u32(data, pos); pos = pos + 4; - irv_set_name(vi, name_ni); - irv_set_id(vi, id); - irv_set_type(vi, tk); - g_ir_var_count = vi + 1; - vi = vi + 1; - } - - // String constants + // === SYM: string constants(4B each)=== + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + str_const_cnt := buf_read_u32(data, pos); pos = pos + 4; + if str_const_cnt > (seg_end2 - seg_off2) / 4 { return -1; } + grow_ir_str_consts(str_const_cnt); sci : ., mut = 0; loop { if sci >= str_const_cnt { break; } - if !ccr_has_bytes(pos, 4, fsize) { return -1; } + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } scv := buf_read_u32(data, pos); pos = pos + 4; w64(g_ir_str_consts, sci * 8, scv); g_ir_str_const_count = sci + 1; sci = sci + 1; } - // Structs + // === SYM: structs === + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + struct_cnt := buf_read_u32(data, pos); pos = pos + 4; + if struct_cnt > (seg_end2 - seg_off2) / 8 { return -1; } + grow_structs(struct_cnt); + g_struct_count = struct_cnt; sti : ., mut = 0; loop { if sti >= struct_cnt { break; } - if !ccr_has_bytes(pos, 8, fsize) { return -1; } - name_ni := buf_read_u32(data, pos); pos = pos + 4; + if !ccr_has_bytes(pos, 8, seg_end2) { return -1; } + sname_ni := buf_read_u32(data, pos); pos = pos + 4; fc := buf_read_u32(data, pos); pos = pos + 4; - if fc > MAX_STRUCT_FIELDS { println("error: .ccr struct field count exceeds max"); return 1; } - if fc > (fsize - pos) / 8 { return -1; } - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, name_ni); + // 容量批 T3(裁-CAP-2 (a)):字段数**无硬上限**(侧表承载)⇒ 旧 MAX_STRUCT_FIELDS + // 读回闸与 16 槽清零循环一并删除;段界核算保留(fc 越段 ⇒ 拒绝,三态纪律)。 + if fc > (seg_end2 - pos) / 8 { return -1; } + w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni); w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc); - // Zero out all field slots and type nodes - zfi : ., mut = 0; - loop { - if zfi >= 16 { break; } - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_NAMES + zfi * 8, 0); - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_TYPES + zfi * 8, 0); - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_TYPE_NODES + zfi * 8, 0); - zfi = zfi + 1; - } + si_set_field_base(sti, g_si_f_used); // Zero generic slots w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 0); zgi : ., mut = 0; loop { if zgi >= 4 { break; } w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + zgi * 8, 0); zgi = zgi + 1; } - // Write field data + // Write field data(侧表;盘面逐字段变长,格式不变) fi2 : ., mut = 0; loop { if fi2 >= fc { break; } fn_ni := buf_read_u32(data, pos); pos = pos + 4; ft := buf_read_u32(data, pos); pos = pos + 4; - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_NAMES + fi2 * 8, fn_ni); - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_TYPES + fi2 * 8, ft); + si_set_field_name(sti, fi2, fn_ni); + si_set_field_type(sti, fi2, ft); fi2 = fi2 + 1; } + si_commit_fields(sti, fc); sti = sti + 1; } - // Enums + // === SYM: enums === + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + enum_cnt := buf_read_u32(data, pos); pos = pos + 4; + if enum_cnt > (seg_end2 - seg_off2) / 8 { return -1; } + grow_enums(enum_cnt); + g_enum_count = enum_cnt; ei : ., mut = 0; loop { if ei >= enum_cnt { break; } - if !ccr_has_bytes(pos, 8, fsize) { return -1; } + if !ccr_has_bytes(pos, 8, seg_end2) { return -1; } ename_ni := buf_read_u32(data, pos); pos = pos + 4; vc := buf_read_u32(data, pos); pos = pos + 4; - if vc > MAX_ENUM_VARIANTS { println("error: .ccr enum variant count exceeds max"); return 1; } - if vc > (fsize - pos) / 8 { return -1; } + // 容量批 T3(裁-CAP-2 (a)):变体/载荷数**无硬上限**(侧表)⇒ 旧两闸与 16 槽清零 + // 循环删除;段界核算保留(三态纪律) + if vc > (seg_end2 - pos) / 8 { return -1; } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni); w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc); - // Zero all variant slots - zvi : ., mut = 0; - loop { - if zvi >= 16 { break; } - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + zvi * OFF_EV_SIZE + OFF_EV_NAME, 0); - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + zvi * OFF_EV_SIZE + OFF_EV_TYPE_COUNT, 0); - ztj : ., mut = 0; - loop { if ztj >= 16 { break; } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + zvi * OFF_EV_SIZE + OFF_EV_TYPES + ztj * 8, 0); ztj = ztj + 1; } - zvi = zvi + 1; - } + ei_set_variant_base(ei, g_ei_v_used); w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 0); zgi2 : ., mut = 0; loop { if zgi2 >= 4 { break; } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + zgi2 * 8, 0); zgi2 = zgi2 + 1; } @@ -572,15 +1251,14 @@ fn load_ccr(data: string, fsize: int) -> int { if vi3 >= vc { break; } vni := buf_read_u32(data, pos); pos = pos + 4; tc := buf_read_u32(data, pos); pos = pos + 4; - if tc > MAX_VARIANT_TYPES { println("error: .ccr variant type count exceeds max"); return 1; } - if tc > (fsize - pos) / 4 { return -1; } - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vi3 * OFF_EV_SIZE + OFF_EV_NAME, vni); - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vi3 * OFF_EV_SIZE + OFF_EV_TYPE_COUNT, tc); + if tc > (seg_end2 - pos) / 4 { return -1; } + ei_set_variant_name(ei, vi3, vni); + ei_set_variant_type_count(ei, vi3, tc); tf : ., mut = 0; loop { if tf >= tc { break; } tval := buf_read_u32(data, pos); pos = pos + 4; - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vi3 * OFF_EV_SIZE + OFF_EV_TYPES + tf * 8, tval); + ei_set_variant_type(ei, vi3, tf, tval); tf = tf + 1; } vi3 = vi3 + 1; @@ -588,81 +1266,764 @@ fn load_ccr(data: string, fsize: int) -> int { ei = ei + 1; } - // Globals (version >= 2) - if ver >= 2 { - if !ccr_has_bytes(pos, 4, fsize) { return -1; } - gc := buf_read_u32(data, pos); pos = pos + 4; - global_size : ., mut = 8; - if ver >= 4 { global_size = 16; } - if gc > (fsize - pos) / global_size { return -1; } - grow_ir_globals(gc); - g_ir_global_count = 0; - gi : ., mut = 0; - loop { - if gi >= gc { break; } - name_ni := buf_read_u32(data, pos); pos = pos + 4; - var_idx := buf_read_u32(data, pos); pos = pos + 4; - init_val : ., mut = 0; - if ver >= 4 { - init_val = buf_read_i64(data, pos); pos = pos + 8; + // === SYM: opt_meta === + if !ccr_has_bytes(pos, 4, seg_end2) { return -1; } + mc := buf_read_u32(data, pos); pos = pos + 4; + mi : ., mut = 0; + loop { + if mi >= mc { break; } + if !ccr_has_bytes(pos, 8, seg_end2) { return -1; } + mk := buf_read_u32(data, pos); pos = pos + 4; + md_len := buf_read_u32(data, pos); pos = pos + 4; + if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end2) { return -1; } + // Allocate and store entry + grow_opt_meta(mi + 1); + mo := mi * OPT_META_STRIDE; + w32(g_opt_meta, mo, mk); + w32(g_opt_meta, mo + 4, md_len); + di : ., mut = 0; + loop { if di >= md_len { break; } + store8(g_opt_meta, mo + 8 + di, load8(data, pos)); + pos = pos + 1; + di = di + 1; + } + g_opt_meta_count = mi + 1; + mi = mi + 1; + } + + // === REG: region(24B each——spec §3.5 字段序 {kind, parent, enter_nod, + // exit_nod, first_ent, last_ent})=== + // v5 的 nstart/ncount 不落盘——由 enter/exit 派生(不变式 nstart ≡ enter、 + // ncount = exit − enter),内存态与 v5 记录逐字节一致。未闭合(exit < enter) + // 记录 v6 无法表示 → 拒绝。 + // first/last 校验:SG_FUNC 根行(行序第 k 个 = 函数 k)的区内条目范围必须 + // 与 SYM func 记录 first/last 一致(同信息双写,失配 = 格式不一致)。 + // 函数指令边界重建:func root_region(SYM)→ REG span = [enter, exit)。 + pos = seg_off5; + if !ccr_has_bytes(pos, 4, seg_end5) { return -1; } + sg_n := buf_read_u32(data, pos); pos = pos + 4; + if sg_n > (seg_end5 - seg_off5) / ESZ_SG_DISK { return -1; } + sg_i : ., mut = 0; + rfcnt : ., mut = 0; // 已见 SG_FUNC 根行数(行序 = 函数序 1:1) + loop { + if sg_i >= sg_n { break; } + grow_sg(sg_i + 1); + f := sg_i * ESZ_SG; + rk := buf_read_i32(data, pos); pos = pos + 4; + rp := buf_read_i32(data, pos); pos = pos + 4; + ren := buf_read_i32(data, pos); pos = pos + 4; + rex := buf_read_i32(data, pos); pos = pos + 4; + rfe := buf_read_i32(data, pos); pos = pos + 4; + rle := buf_read_i32(data, pos); pos = pos + 4; + if rex < ren { return -1; } // 未闭合 → ncount 不可派生 + if rfe < -1 || rle < -1 || (rfe == -1) != (rle == -1) { return -1; } + w64(g_sgs, f + OFF_SG_KIND, rk); + w64(g_sgs, f + OFF_SG_ENTER, ren); + w64(g_sgs, f + OFF_SG_EXIT, rex); + w64(g_sgs, f + OFF_SG_PARENT, rp); + w64(g_sgs, f + OFF_SG_NSTART, ren); + w64(g_sgs, f + OFF_SG_NCOUNT, rex - ren); + if rk == SG_FUNC { + if rfcnt >= func_cnt { return -1; } // 根行多于函数记录 + if rfe != r64(fn_meta, rfcnt * 24 + 8) || rle != r64(fn_meta, rfcnt * 24 + 16) { + return -1; // REG 根行条目范围 ≠ SYM func 记录 } - w64(g_ir_globals, gi * 24, name_ni); - w64(g_ir_globals, gi * 24 + 8, var_idx); - w64(g_ir_globals, gi * 24 + 16, init_val); - g_ir_global_count = gi + 1; - gi = gi + 1; + rfcnt = rfcnt + 1; } + sg_i = sg_i + 1; } + if rfcnt != func_cnt { return -1; } // 根行少于函数记录(每函数必有根) + g_sg_count = sg_n; - // Optimization metadata (version >= 3) - g_opt_meta_count = 0; - if ver >= 3 { - if pos + 4 > fsize { return -1; } - mc := buf_read_u32(data, pos); pos = pos + 4; - mi : ., mut = 0; - loop { if mi >= mc { break; } - if pos + 8 > fsize { return -1; } - mk := buf_read_u32(data, pos); pos = pos + 4; - md_len := buf_read_u32(data, pos); pos = pos + 4; - if md_len > OPT_META_STRIDE - 8 || pos + md_len > fsize { return -1; } - // Allocate and store entry - grow_opt_meta(mi + 1); - mo := mi * OPT_META_STRIDE; - w32(g_opt_meta, mo, mk); - w32(g_opt_meta, mo + 4, md_len); - di : ., mut = 0; - loop { if di >= md_len { break; } - store8(g_opt_meta, mo + 8 + di, load8(data, pos)); - pos = pos + 1; - di = di + 1; + // 函数指令边界回填:instr_start = root_region enter, instr_count = exit − enter + // (根 region span = 函数节点范围——与 v5 func_meta instr_start/count 恒等, + // 内存态与 v5 逐字节一致) + bfi : ., mut = 0; + loop { + if bfi >= func_cnt { break; } + rid := r64(fn_meta, bfi * 24); + if rid < 0 || rid >= sg_n { return -1; } + fr := rid * ESZ_SG; + if r64(g_sgs, fr + OFF_SG_KIND) != SG_FUNC { return -1; } + ren := r64(g_sgs, fr + OFF_SG_ENTER); + rex := r64(g_sgs, fr + OFF_SG_EXIT); + if rex < ren { return -1; } + w64(g_ir_func_instr_start, bfi * 8, ren); + w64(g_ir_func_instr_count, bfi * 8, rex - ren); + bfi = bfi + 1; + } + + // === NOD: instructions(36B each)=== + // 内核完备 Task 1(语义对象模型):loader 产出 = 对象载入 + 守卫——28B + // 语义字段 → 内核对象缓冲 g_v7_nod_sem(内存记录 = 盘记录剥离邻接的镜像, + // 布局 offset 同盘)+ 邻接域 first_edge/edge_count → g_v7_nod_meta + // (EDG 段校验 + 对象面 nod_edge_first/count 消费)。NOD→g_ir_instrs + // 线性重建段已移出本函数(调度重建 = 实例事务 build_linear_schedule, + // regalloc.cr——load 返回后、发射/分派前由入口调用);g_ir_instrs/ + // g_ir_instr_count 本段不再触碰(重建段原写点全清)。 + pos = seg_off3; + if !ccr_has_bytes(pos, 4, seg_end3) { return -1; } + instr_cnt := buf_read_u32(data, pos); pos = pos + 4; + if instr_cnt > (seg_end3 - seg_off3) / ESZ_NOD_DISK { return -1; } + g_v7_nod_sem = alloc((instr_cnt + 8) * ESZ_NOD_SEM); + g_v7_nod_meta = alloc((instr_cnt + 8) * ESZ_NOD_META); + ii : ., mut = 0; + loop { + if ii >= instr_cnt { break; } + if !ccr_has_bytes(pos, ESZ_NOD_DISK, seg_end3) { return -1; } + opcode := buf_read_u32(data, pos); pos = pos + 4; + dest := buf_read_i32(data, pos); pos = pos + 4; + s1 := buf_read_i64(data, pos); pos = pos + 8; // 修复 14:s1 64 位 + s2 := buf_read_i32(data, pos); pos = pos + 4; + s3 := buf_read_i32(data, pos); pos = pos + 4; + tk := buf_read_u32(data, pos); pos = pos + 4; + it2 := buf_read_i32(data, pos); pos = pos + 4; // R2 P6 Task 3:项索引(i32;-1 = 无项) + nfe := buf_read_u32(data, pos); pos = pos + 4; // first_edge(邻接索引) + nec := buf_read_u32(data, pos); pos = pos + 4; // edge_count + // 对象缓冲写(语义字段 = 盘字节镜像——w32/w64 低 32/64 位存储,存取 + // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest); + w64(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, tk); + w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it2); // R2 P6 Task 3:项索引(-1 = 无) + w64(g_v7_nod_meta, ii * ESZ_NOD_META, nfe); + w64(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec); + ii = ii + 1; + } + g_v7_nod_count = instr_cnt; + + // GC-3(SYM 评审 M1):REG root span 对 NOD 空间上界校验——函数指令边界 + // (root_region span)在 REG 段解析时回填,instr_cnt 直到 NOD 段才可知; + // 彼时只查了 rex ≥ ren(未闭合),无上界 → 越界 span 通过后 ELF 发射在 + // NOD 空间外读指令(缓冲外静默/崩溃)。逐函数校验 instr_start + count + // ≤ instr_cnt(含负值拒绝;风格与 ENT ele > instr_cnt 拒绝一致——载荷 + // 消费前的最后一次可拒绝点)。 + rfi : ., mut = 0; + loop { + if rfi >= func_cnt { break; } + ris := r64(g_ir_func_instr_start, rfi * 8); + ric := r64(g_ir_func_instr_count, rfi * 8); + if ris < 0 || ric < 0 || ris + ric > instr_cnt { return -1; } + rfi = rfi + 1; + } + + // === ENT: entries(28B each → 内存 24B 表)=== + // 盘上半开 live_end → 内存闭区间 −1;version 字段不入内存(组内序可推)。 + if have4 != 0 { + pos = seg_off4; + if !ccr_has_bytes(pos, 4, seg_end4) { return -1; } + ent_cnt := buf_read_u32(data, pos); pos = pos + 4; + if ent_cnt > (seg_end4 - seg_off4) / ESZ_ENTRY_DISK { return -1; } + grow_entries(ent_cnt); + eii : ., mut = 0; + loop { + if eii >= ent_cnt { break; } + if !ccr_has_bytes(pos, ESZ_ENTRY_DISK, seg_end4) { return -1; } + ev := buf_read_i32(data, pos); pos = pos + 4; // var_id + evr := buf_read_u32(data, pos); pos = pos + 4; // version(盘上仅校验用) + ed := buf_read_i32(data, pos); pos = pos + 4; // def_nod + els := buf_read_u32(data, pos); pos = pos + 4; // live_start(含定值) + ele := buf_read_u32(data, pos); pos = pos + 4; // live_end(半开) + eho := buf_read_i32(data, pos); pos = pos + 4; // home + efl := buf_read_u32(data, pos); pos = pos + 4; // flags + // 语义校验(越界拒绝,先例 ccr 校验风格) + if evr < 1 { return -1; } + if ev < 0 || ev >= g_ir_var_count { return -1; } // var 命名空间 = SYM 行重建总量 + if els >= ele || ele > instr_cnt { return -1; } + if ed >= 0 { + if ed >= instr_cnt { return -1; } + if ed != els { return -1; } // 定值条目 live_start == def } - g_opt_meta_count = mi + 1; - mi = mi + 1; + // 内存闭区间表 + mo2 := eii * ESZ_ENTRY; + w32(g_ir_entries, mo2 + OFF_ENTRY_VAR, ev); + w32(g_ir_entries, mo2 + OFF_ENTRY_DEF, ed); + w32(g_ir_entries, mo2 + OFF_ENTRY_LS, els); + w32(g_ir_entries, mo2 + OFF_ENTRY_LE, ele - 1); + w32(g_ir_entries, mo2 + OFF_ENTRY_HOME, eho); + w32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl); + g_entry_count = eii + 1; + eii = eii + 1; } + // 函数条目段界重建:条目按函数升序成块落盘(compute_entries func_i 升序), + // 每函数一段 [start, count)——块判定 = var 槽 ∈ 该函数 var 区间(var 槽 + // 全属唯一函数,段序与函数序一致)。重建结果与 SYM func 记录 + // first_ent/last_ent 逐函数对照(同信息双写,失配 = 格式不一致)。 + grow_func_entry_meta(func_cnt); + pf : ., mut = 0; + pe : ., mut = 0; + loop { + if pf >= func_cnt { break; } + fvs := r64(g_ir_func_var_start, pf * 8); + fvc := r64(g_ir_func_var_count, pf * 8); + pstart := pe; + w64(g_ir_func_entry_start, pf * 8, pe); + loop { + if pe >= g_entry_count { break; } + pv := buf_read_i32(g_ir_entries, pe * ESZ_ENTRY + OFF_ENTRY_VAR); + if fvc <= 0 || pv < fvs || pv >= fvs + fvc { break; } + pe = pe + 1; + } + pcnt := pe - pstart; + w64(g_ir_func_entry_count, pf * 8, pcnt); + ffe := r64(fn_meta, pf * 24 + 8); + fle := r64(fn_meta, pf * 24 + 16); + if pcnt == 0 { + if ffe != -1 || fle != -1 { return -1; } + } else { + if ffe != pstart || fle != pstart + pcnt - 1 { return -1; } + } + pf = pf + 1; + } + if pe != g_entry_count { return -1; } } - // SG (region) section (version >= 5): count + sg_count × 24B records (6×i32). - // Restores g_sgs/g_sg_count for frontend passes; the backend ignores - // regions, but the bytes must still be skipped correctly. - if ver >= 5 { - if pos + 4 > fsize { return -1; } - sg_n := buf_read_u32(data, pos); pos = pos + 4; - if sg_n > (fsize - pos) / ESZ_SG_DISK { return -1; } - sg_i : ., mut = 0; + // === EDG: edges(v7 必落;8B each {to_nod u32, kind u32}——所属节点 = + // 邻接隐含:节点 i 出边连续段 [first_edge, first_edge+edge_count),first_edge + // = 前缀累计(v7 spec §3.3 邻接约定)。校验(spec §4 + 守卫面强度保持): + // ① 连续段与 NOD 邻接域逐节点对照(first_edge == 前缀累计;段界不出 EDG) + // ② Σ edge_count == edg_count(规则 3;行走完 == 段体大小) + // ③ 每条边 to_nod < instr_cnt(NOD 引用界内)+ to_nod > 所属节点 + // (规则 1 拓扑不变量——数据/state 边前向;自环/后向 = 文件损坏拒绝) + // ④ kind ≤ 1(0=数据、1=state;2+ 预留 = 未知边类拒绝) + // 校验通过后边表入 g_v7_edges 缓冲(文件序连续段——图语义消费通道/调试)。 + // branch/jump 目标 = 操作数引用非边(v7 §4 边界声明)——NOD 操作数不参与 + // 本段校验。 + pos = seg_off6; + if !ccr_has_bytes(pos, 4, seg_end6) { return -1; } + edg_cnt := buf_read_u32(data, pos); pos = pos + 4; + if edg_cnt > (seg_end6 - seg_off6) / ESZ_EDGE_DISK { return -1; } + v7_buf : string, mut = ""; + if edg_cnt > 0 { + v7_buf = alloc(edg_cnt * 8); + } + g_v7_edges = v7_buf; + g_v7_edge_count = edg_cnt; + ei4 : ., mut = 0; + run_off : ., mut = 0; + loop { + if ei4 >= instr_cnt { break; } + // 邻接域读内核对象缓冲 g_v7_nod_meta(原 nod_edge_meta loader 局部—— + // 内核完备 Task 1 对象留存形态裁决 (a):对象 = 内核持有物,EDG 守卫 + // 与对象面 nod_edge_first/count 同源;守卫逻辑零改动) + nfe := r64(g_v7_nod_meta, ei4 * ESZ_NOD_META); + nec := r64(g_v7_nod_meta, ei4 * ESZ_NOD_META + 8); + if nfe != run_off { return -1; } // ① 前缀累计失配(段错位/伪造) + if run_off + nec > edg_cnt { return -1; } // ① 节点段越出 EDG 空间 + rec : ., mut = 0; loop { - if sg_i >= sg_n { break; } - grow_sg(sg_i + 1); - f := sg_i * ESZ_SG; - w64(g_sgs, f + OFF_SG_KIND, buf_read_i32(data, pos)); pos = pos + 4; - w64(g_sgs, f + OFF_SG_ENTER, buf_read_i32(data, pos)); pos = pos + 4; - w64(g_sgs, f + OFF_SG_EXIT, buf_read_i32(data, pos)); pos = pos + 4; - w64(g_sgs, f + OFF_SG_PARENT, buf_read_i32(data, pos)); pos = pos + 4; - w64(g_sgs, f + OFF_SG_NSTART, buf_read_i32(data, pos)); pos = pos + 4; - w64(g_sgs, f + OFF_SG_NCOUNT, buf_read_i32(data, pos)); pos = pos + 4; - sg_i = sg_i + 1; + if rec >= nec { break; } + if !ccr_has_bytes(pos, ESZ_EDGE_DISK, seg_end6) { return -1; } + eto := buf_read_u32(data, pos); pos = pos + 4; + ekind := buf_read_u32(data, pos); pos = pos + 4; + if ekind > 1 { return -1; } // ④ 未知边类 + if eto >= instr_cnt { return -1; } // ③ to 出 NOD 空间 + if eto <= ei4 { return -1; } // ③ 后向/自环(拓扑不变量) + w64(v7_buf, (run_off + rec) * 8, eto + ekind * 4294967296); + rec = rec + 1; + } + run_off = run_off + nec; + ei4 = ei4 + 1; + } + if run_off != edg_cnt { return -1; } // ② Σ edge_count == edg_count + if pos != seg_end6 { return -1; } // ② 行走完 == 段体大小 + + // === TYPE(7):类型面(R2 P4 Task 2 内容面——两小节解析 + 内存重建)=== + // D12 字节:row_count → 行表(24B/条)→ term_count → 项 DAG(40B/条;哈希 + // 不落盘、本处 tt_hash5 重算)。重建 = g_types 行 + g_type_terms 项 + + // g_tt_index 索引重放(grow_tt_index → tt_reindex)。 + // 逐条校验(违规 = 拒绝——不得静默当空表/截断表,三态纪律 C.5-3): + // ① 计数/长度自洽(计数 × 记录尺寸 ≤ 段余量);② 段体恰两小节(行走完 + // == seg_end7,无尾随字节);③ tag ∈ 0..10(TT_*)+ a..d ≥ -1;④ 子项引用 + // -1 或 < 自身行号(拓扑无环;标注槽按 tag 分派跳过,字段表见 + // ccr_type_term_ref_ok——ATOM 的 b = 自类型行号可达数千,不得误拒)。 + // 复位:行表/项层(含引擎预算/memo/lits——见 tt_layer_reset 注记)全部作废 + // 重建(旧行号语义的缓存一并清;行表大小随段内容,此处为唯一复位点)。 + // corearch 侧无桥接/checker 层:行表 = 数据(消费者 = --dump-types 通道 + + // 未来判定原语),本处不做语义解释。 + g_type_count = 0; + tt_layer_reset(); + pos = seg_off7; + if !ccr_has_bytes(pos, 4, seg_end7) { return -1; } + trow_cnt := buf_read_u32(data, pos); pos = pos + 4; + if trow_cnt > (seg_end7 - pos) / ESZ_TYPE_ROW { return -1; } + grow_types(trow_cnt); + trow : ., mut = 0; + loop { + if trow >= trow_cnt { break; } + if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end7) { return -1; } + w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)); pos = pos + 8; + w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_DATA, buf_read_i64(data, pos)); pos = pos + 8; + w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_EXTRA, buf_read_i64(data, pos)); pos = pos + 8; + trow = trow + 1; + } + g_type_count = trow_cnt; + if !ccr_has_bytes(pos, 4, seg_end7) { return -1; } + tterm_cnt := buf_read_u32(data, pos); pos = pos + 4; + if tterm_cnt > (seg_end7 - pos) / ESZ_TYPE_TERM_DISK { return -1; } + grow_type_terms(tterm_cnt + 1); + tj : ., mut = 0; + loop { + if tj >= tterm_cnt { break; } + if !ccr_has_bytes(pos, ESZ_TYPE_TERM_DISK, seg_end7) { return -1; } + ttag := buf_read_i64(data, pos); pos = pos + 8; + ta := buf_read_i64(data, pos); pos = pos + 8; + tb := buf_read_i64(data, pos); pos = pos + 8; + tc2 := buf_read_i64(data, pos); pos = pos + 8; + td := buf_read_i64(data, pos); pos = pos + 8; + if ttag < 0 || ttag > TT_CONS { return -1; } + if ta < -1 || tb < -1 || tc2 < -1 || td < -1 { return -1; } + if ccr_type_term_ref_ok(ttag, ta, tb, tc2, td, tj) == 0 { return -1; } + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_TAG, ttag); + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_A, ta); + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_B, tb); + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_C, tc2); + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_D, td); + w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)); + g_type_term_count = tj + 1; + tj = tj + 1; + } + if pos != seg_end7 { return -1; } // 段体恰两小节(无尾随字节) + grow_tt_index(g_type_term_count + 1); // 索引重建(cap 已清零 → 必走重建) + + // === IFACE(8):接口面(R2 P4 Task 3 内容面——D14 五小节解析 + 内存重建)=== + // 重建 = g_iface_entries(条目表;并置 g_iface_registry_ok = 1——**段 = corearch + // 侧真源**,防「常量表覆盖段内容」)+ g_iface_shape_names/terms + g_ifaces + // (ESZ_IFACEINFO 布局;corearch 侧无 AST ⇒ 节点槽恒 -1、**项槽** = 段值) + // + g_impl_for + g_methods。 + // 逐条校验(违规 = 拒绝;三态纪律 C.5-3——不得静默当空表/截断表): + // ① 五小节计数/长度自洽(计数 × 记录尺寸 ≤ 段余量)+ 行走完 == seg_end8(无尾随); + // ② native_count == IFACE_ENTRY_COUNT(表内容 = 常量表,计数漂移 = 损坏); + // ③ **跨段引用域**:name_ni/type_ni/method_ni/mangled_ni ∈ [0, g_str_count)(STR + // 段先于本段解析);ti_row ∈ {-1} ∪ [0, g_type_count);term ∈ {-1} ∪ [0, tt_count()) + // (TYPE(7) 段先于本段解析 ⇒ 两域已建立); + // ④ 结构域:ak ∈ 0..AK_NULL、method_count ≤ MAX_IFACE_METHODS、 + // param_count ≤ MAX_IFACE_METHOD_PARAMS、self_mode ∈ 0..3。 + // IFACE 段 = 纯信息面(不参与 ELF 发射);本段解析失败 = 整体拒绝(load_ccr -1)。 + pos = seg_off8; + if !ccr_has_bytes(pos, 4, seg_end8) { return -1; } + ifc_nat := buf_read_u32(data, pos); pos = pos + 4; + if ifc_nat != IFACE_ENTRY_COUNT { return -1; } + if !ccr_has_bytes(pos, ifc_nat * 24, seg_end8) { return -1; } + g_iface_entries = alloc(ifc_nat * ESZ_IFACE_ENTRY); + ifr : ., mut = 0; + loop { + if ifr >= ifc_nat { break; } + fak := buf_read_i32(data, pos); pos = pos + 4; + fti := buf_read_i32(data, pos); pos = pos + 4; + fname := buf_read_i32(data, pos); pos = pos + 4; + flit := buf_read_i32(data, pos); pos = pos + 4; + fops := buf_read_i64(data, pos); pos = pos + 8; + if fak < 0 || fak > AK_NULL { return -1; } // 原子类域 + if fti < -1 || fti >= g_type_count { return -1; } // 类型行引用域(跨段) + if fname < 0 || fname >= g_str_count { return -1; } // 名字引用域(跨段) + if flit < -1 { return -1; } + fo2 := ifr * ESZ_IFACE_ENTRY; + w64(g_iface_entries, fo2 + OFF_IE_AK, fak); + w64(g_iface_entries, fo2 + OFF_IE_TI, fti); + w64(g_iface_entries, fo2 + OFF_IE_NAME, fname); + w64(g_iface_entries, fo2 + OFF_IE_LIT, flit); + w64(g_iface_entries, fo2 + OFF_IE_OPS, fops); + ifr = ifr + 1; + } + g_iface_entry_count = ifc_nat; + g_iface_registry_ok = 1; + + // ② 横切形状(名 ni + 形状项) + if !ccr_has_bytes(pos, 4, seg_end8) { return -1; } + ifc_shn := buf_read_u32(data, pos); pos = pos + 4; + if ifc_shn > (seg_end8 - pos) / 8 { return -1; } + iface_shape_grow(ifc_shn); + ifs : ., mut = 0; + loop { + if ifs >= ifc_shn { break; } + sname := buf_read_i32(data, pos); pos = pos + 4; + sterm := buf_read_i32(data, pos); pos = pos + 4; + if sname < 0 || sname >= g_str_count { return -1; } + if sterm < 0 || sterm >= tt_count() { return -1; } // 项引用域(跨段) + w64(g_iface_shape_names, ifs * 8, sname); + w64(g_iface_shape_terms, ifs * 8, sterm); + ifs = ifs + 1; + } + g_iface_shape_count = ifc_shn; + + // ③ 用户接口(头 16B + 方法 80B × method_count) + if !ccr_has_bytes(pos, 4, seg_end8) { return -1; } + ifc_usn := buf_read_u32(data, pos); pos = pos + 4; + if ifc_usn > (seg_end8 - pos) / 16 { return -1; } + grow_ifaces(ifc_usn); + ifu : ., mut = 0; + loop { + if ifu >= ifc_usn { break; } + if !ccr_has_bytes(pos, 16, seg_end8) { return -1; } + uname := buf_read_i32(data, pos); pos = pos + 4; + umc := buf_read_i32(data, pos); pos = pos + 4; + ugc := buf_read_i32(data, pos); pos = pos + 4; + pos = pos + 4; // pad(预留) + if uname < 0 || uname >= g_str_count { return -1; } + if umc < 0 || umc > MAX_IFACE_METHODS { return -1; } + if ugc < 0 { return -1; } + if !ccr_has_bytes(pos, umc * 80, seg_end8) { return -1; } + ub := ifu * ESZ_IFACEINFO; + // 先清零整条记录(防上轮残留;节点槽/项槽的初值 = -1 由下方逐槽写) + uz : ., mut = 0; + loop { if uz >= ESZ_IFACEINFO { break; } w8(g_ifaces, ub + uz, 0); uz = uz + 1; } + w64(g_ifaces, ub + OFF_IF_NAME, uname); + w64(g_ifaces, ub + OFF_IF_METHOD_COUNT, umc); + w64(g_ifaces, ub + OFF_IF_GENERIC_COUNT, ugc); + ium : ., mut = 0; + loop { + if ium >= umc { break; } + mb := ub + OFF_IF_METHODS + ium * ESZ_IFMETHOD; + mname := buf_read_i32(data, pos); pos = pos + 4; + mpc := buf_read_i32(data, pos); pos = pos + 4; + msm := buf_read_i32(data, pos); pos = pos + 4; + mrt := buf_read_i32(data, pos); pos = pos + 4; + if mname < 0 || mname >= g_str_count { return -1; } + if mpc < 0 || mpc > MAX_IFACE_METHOD_PARAMS { return -1; } + if msm < 0 || msm > 3 { return -1; } + if mrt < -1 || mrt >= tt_count() { return -1; } // 项引用域(跨段) + w64(g_ifaces, mb + OFF_IFM_NAME, mname); + w64(g_ifaces, mb + OFF_IFM_PARAM_COUNT, mpc); + w64(g_ifaces, mb + OFF_IFM_SELF_MODE, msm); + w64(g_ifaces, mb + OFF_IFM_RET_TERM, mrt); + w64(g_ifaces, mb + OFF_IFM_RET_NODE, -1); // corearch 无 AST ⇒ 节点槽恒 -1 + mq : ., mut = 0; + loop { + if mq >= MAX_IFACE_METHOD_PARAMS { break; } + mt := buf_read_i32(data, pos); pos = pos + 4; + if mt < -1 || mt >= tt_count() { return -1; } // 项引用域(跨段) + w64(g_ifaces, mb + OFF_IFM_PARAM_TERMS + mq * 8, mt); + w64(g_ifaces, mb + OFF_IFM_PARAM_NODES + mq * 8, -1); // 节点槽恒 -1 + mq = mq + 1; + } + mp : ., mut = 0; + loop { + if mp >= MAX_IFACE_METHOD_PARAMS { break; } + mc2 := buf_read_i32(data, pos); pos = pos + 4; // 裸码(信息面) + if mc2 < -1 { return -1; } + w64(g_ifaces, mb + OFF_IFM_PARAM_TYPES + mp * 8, mc2); + mp = mp + 1; + } + ium = ium + 1; + } + ifu = ifu + 1; + } + g_iface_count = ifc_usn; + + // ④ impl 边(g_impl_for:{trait_ni, type_ni}——声明元数据) + if !ccr_has_bytes(pos, 4, seg_end8) { return -1; } + ifc_imp := buf_read_u32(data, pos); pos = pos + 4; + if ifc_imp > (seg_end8 - pos) / 8 { return -1; } + grow_impl_for(ifc_imp); + ifi : ., mut = 0; + loop { + if ifi >= ifc_imp { break; } + itr := buf_read_i32(data, pos); pos = pos + 4; + ity := buf_read_i32(data, pos); pos = pos + 4; + if itr < 0 || itr >= g_str_count { return -1; } + if ity < 0 || ity >= g_str_count { return -1; } + w64(g_impl_for, ifi * 16, itr); + w64(g_impl_for, ifi * 16 + 8, ity); + ifi = ifi + 1; + } + g_impl_for_count = ifc_imp; + + // ⑤ 方法表(g_methods:{type_ni, method_ni, mangled_ni}——iface_find_method 数据面) + if !ccr_has_bytes(pos, 4, seg_end8) { return -1; } + ifc_met := buf_read_u32(data, pos); pos = pos + 4; + if ifc_met > (seg_end8 - pos) / 12 { return -1; } + grow_methods(ifc_met); + ifm : ., mut = 0; + loop { + if ifm >= ifc_met { break; } + mtn := buf_read_i32(data, pos); pos = pos + 4; + mmn := buf_read_i32(data, pos); pos = pos + 4; + mmg := buf_read_i32(data, pos); pos = pos + 4; + if mtn < 0 || mtn >= g_str_count { return -1; } + if mmn < 0 || mmn >= g_str_count { return -1; } + if mmg < 0 || mmg >= g_str_count { return -1; } + w64(g_methods, ifm * 24, mtn); + w64(g_methods, ifm * 24 + 8, mmn); + w64(g_methods, ifm * 24 + 16, mmg); + ifm = ifm + 1; + } + g_method_count = ifc_met; + + if pos != seg_end8 { return -1; } // 五小节行走完 == 段体(无尾随字节) + + // === NOD 项索引一致性硬校验(R2 P6 Task 3 β)=== + // 时点约束:项索引的解析域 = TYPE 段重建的项表(g_type_terms/tt_count)⇒ **必须** + // 排在 TYPE/IFACE 段之后——NOD 段(规范序 3)解析时项表尚不存在(TYPE = 7)。 + // 判据(三态纪律:不得猜、不得静默当空表/当无项): + // · item < -1 ⇒ 拒绝(域外) + // · item ≥ tt_count() ⇒ 拒绝(域外) + // · item ≥ 0 且 tt_atom_of_term(item) != 盘上码 ⇒ 拒绝(**并存两值分歧 = 硬错**; + // 这正是本布局的 D20 合规条件——分歧不得静默择一,见 Task 3 报告 §1.1-3) + // · item = -1 ⇒ 无需校验(无项:复合行 / 辅码面 / + // 无面 / 暖态缺行——码由 `tk` 槽逐字节承担) + // 合法文件恒通过(写侧两值同源于 ccr_types.cr:ccr_nod_item_populate 的单次派生)。 + ni : ., mut = 0; + loop { + if ni >= g_v7_nod_count { break; } + itv := nod_item(ni); + if itv < -1 { return -1; } + if itv >= 0 { + if itv >= tt_count() { return -1; } + if tt_atom_of_term(itv) != nod_tk(ni) { return -1; } } - g_sg_count = sg_n; + ni = ni + 1; } return 0; } + +// ─── TYPE 段项引用拓扑(R2 P4 Task 2;load 侧校验 + 通道/dump 共用)─── +// 项引用 = -1(无)或 < 自身行号(DAG 由 tt_term 追加式构造 ⇒ 子项恒先建)。 +// **字段表按 tag 分派**(不得全槽施检——标注槽会被误拒): +// TT_UNION/TT_INTER/TT_CONS:a, b (两子项) +// TT_NOT:a ;TT_MU:b(a = 绑定变量标注) +// TT_ATOM:c(参数链头,-1 = 无参) ;a = ak、b = 标注(自类型行号/固定性位/ +// 令牌值/mut 标记——可达数千)、d = 保留 +// TT_BOT/TT_TOP/TT_NIL:无引用槽 ;TT_TOP_K:a = k;TT_VAR:a = 绑定变量 +// 构造点单源见 ty_shadow.cr 的 sh_ref_mut_marker/sh_name_token/sh_seq_term 三处注记。 +fn ccr_type_ref_before(r: int, row: int) -> int { + if r < 0 { return 1; } // -1 = 无引用 + if r < row { return 1; } // 子项恒先建(DAG 无环) + return 0; +} + +fn ccr_type_term_ref_ok(tag: int, a: int, b: int, c: int, d: int, row: int) -> int { + if tag == TT_UNION || tag == TT_INTER || tag == TT_CONS { + if ccr_type_ref_before(a, row) == 0 { return 0; } + return ccr_type_ref_before(b, row); + } + if tag == TT_NOT { return ccr_type_ref_before(a, row); } + if tag == TT_MU { return ccr_type_ref_before(b, row); } + if tag == TT_ATOM { return ccr_type_ref_before(c, row); } + return 1; // ⊥ / ⊤ / ⊤ₖ / VAR / NIL:无引用槽(a 为标注) +} + +// ─── --dump-types 通道(R2 P4 Task 2):段内容面打印 ─── +// 写侧(corec `ccr --dump-types`,经 ccr_types.cr 的 ccr_type_selftest_dump 调 +// 本函数)与读侧(corearch `--dump-types`)共用**同一条打印路径**——跨进程行 +// 格式零分歧;行格式契约见 tests/selfhost/test_ccr_types.py:parse_type_dump。 +// 读侧意义 = §6.3 的读回证据:载入段重建后的行表/项 DAG 与写侧逐行一致, +// 且 probe 行的跨进程同值 = 项层原语(tt_norm/tt_is_dnf/tt_is_literal)在同构 +// 重建表上逐点同值(下标含新建项 ⇒ 索引重建/去重行为的实证)。 +CCR_TYPE_PROBE_N : int = 16; // probe 窗口(前 N 项) + +fn ccr_type_surface_dump() { + print("rows: "); print_i(g_type_count); println(""); + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + print("row "); print_i(i); + print(" kind "); print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)); + print(" data "); print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)); + print(" extra "); print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)); + println(""); + i = i + 1; + } + print("terms: "); print_i(tt_count()); println(""); + j : ., mut = 0; + loop { + if j >= tt_count() { break; } + print("term "); print_i(j); + print(" tag "); print_i(tt_tag(j)); + print(" a "); print_i(tt_a(j)); + print(" b "); print_i(tt_b(j)); + print(" c "); print_i(tt_c(j)); + print(" d "); print_i(tt_d(j)); + println(""); + j = j + 1; + } + ccr_type_probe_dump(); +} + +// 项层原语跨进程同值探针(**项层**——type_terms.cr 面;判定原语 ty_sub 族不入 +// corearch 清单,见 build_selfhost_native.py 的 backend_support_nodes 注记): +// 预算状态复位(两侧同起点)→ 前 N 项逐个 tt_norm(规范化 = 判定算法输入形态, +// spec §1.3)→ 折叠**规范化结果的项下标** + 形态位(tt_is_dnf/tt_is_literal) +// 成摘要。两侧表同构(行表/项表/probe 前各节逐行同)⇒ 调用序、步数消耗、 +// **新建项的下标**逐点同 ⇒ 摘要同。下标入摘要即「重建后的索引(g_tt_index) +// 与去重行为」的实证:索引未重建/重建错位 ⇒ tt_norm 的去重退化/错配 ⇒ 新建项 +// 下标漂移 ⇒ 摘要变。 +// 本探针**会**向项表追加规范化新项(tt_norm 的构造面)——在 dump 的 terms 节 +// 之后运行,故不影响打印面(序列化缓冲早在 dump 前已生成,见 ccr_type_prepare_save)。 +// ─── --dump-ifaces 通道(R2 P4 Task 3):IFACE 段内容面打印 ─── +// 写侧(corec `ccr --dump-ifaces`,经 ccr_types.cr 的 ccr_iface_selftest_dump 调本 +// 函数)与读侧(corearch `--dump-ifaces`)共用**同一条打印路径**——跨进程行格式零 +// 分歧;行格式契约见 tests/selfhost/test_ccr_types.py:parse_iface_dump。 +// 读侧意义 = §6.3 的读回证据:载入段重建后的条目表/形状表/接口表/impl 边/方法表与 +// 写侧逐行一致;`ifaceprobe` 行 = **跨段引用域的可解析性**(每个项槽在重建的项表上 +// 解析:tag/a/c 折叠成摘要——两侧同值即「段内项索引在 corearch 侧解析到同构项」)。 +// **零建项**(纯读:不动项表/预算/memo——与 TYPE 的 probe 不同,本处无需构造面)。 +fn ccr_iface_surface_dump() { + print("ifacenative: "); print_i(g_iface_entry_count); println(""); + ei2 : ., mut = 0; + loop { + if ei2 >= g_iface_entry_count { break; } + eo2 := ei2 * ESZ_IFACE_ENTRY; + print("native "); print_i(ei2); + print(" ak "); print_i(r64(g_iface_entries, eo2 + OFF_IE_AK)); + print(" ti "); print_i(r64(g_iface_entries, eo2 + OFF_IE_TI)); + print(" name "); print_i(r64(g_iface_entries, eo2 + OFF_IE_NAME)); + print(" lit "); print_i(r64(g_iface_entries, eo2 + OFF_IE_LIT)); + print(" ops "); print_i(r64(g_iface_entries, eo2 + OFF_IE_OPS)); + println(""); + ei2 = ei2 + 1; + } + print("ifaceshapes: "); print_i(g_iface_shape_count); println(""); + sh2 : ., mut = 0; + loop { + if sh2 >= g_iface_shape_count { break; } + print("shape "); print_i(sh2); + print(" name "); print_i(r64(g_iface_shape_names, sh2 * 8)); + print(" term "); print_i(r64(g_iface_shape_terms, sh2 * 8)); + println(""); + sh2 = sh2 + 1; + } + print("ifaceuser: "); print_i(g_iface_count); println(""); + iu2 : ., mut = 0; + loop { + if iu2 >= g_iface_count { break; } + bo := iu2 * ESZ_IFACEINFO; + mc3 := r64(g_ifaces, bo + OFF_IF_METHOD_COUNT); + print("iface "); print_i(iu2); + print(" name "); print_i(r64(g_ifaces, bo + OFF_IF_NAME)); + print(" methods "); print_i(mc3); + print(" generics "); print_i(r64(g_ifaces, bo + OFF_IF_GENERIC_COUNT)); + println(""); + mj2 : ., mut = 0; + loop { + if mj2 >= mc3 { break; } + mbo := bo + OFF_IF_METHODS + mj2 * ESZ_IFMETHOD; + mpc := r64(g_ifaces, mbo + OFF_IFM_PARAM_COUNT); + print("method "); print_i(iu2); print(" "); print_i(mj2); + print(" name "); print_i(r64(g_ifaces, mbo + OFF_IFM_NAME)); + print(" params "); print_i(mpc); + print(" self "); print_i(r64(g_ifaces, mbo + OFF_IFM_SELF_MODE)); + print(" ret "); print_i(r64(g_ifaces, mbo + OFF_IFM_RET_TERM)); + println(""); + mq2 : ., mut = 0; + loop { + if mq2 >= MAX_IFACE_METHOD_PARAMS { break; } + print("mparam "); print_i(iu2); print(" "); print_i(mj2); + print(" "); print_i(mq2); + print(" code "); print_i(r64(g_ifaces, mbo + OFF_IFM_PARAM_TYPES + mq2 * 8)); + print(" term "); print_i(r64(g_ifaces, mbo + OFF_IFM_PARAM_TERMS + mq2 * 8)); + println(""); + mq2 = mq2 + 1; + } + mj2 = mj2 + 1; + } + iu2 = iu2 + 1; + } + print("ifaceimpl: "); print_i(g_impl_for_count); println(""); + ip2 : ., mut = 0; + loop { + if ip2 >= g_impl_for_count { break; } + print("impl "); print_i(ip2); + print(" trait "); print_i(r64(g_impl_for, ip2 * 16)); + print(" type "); print_i(r64(g_impl_for, ip2 * 16 + 8)); + println(""); + ip2 = ip2 + 1; + } + print("ifacemethods: "); print_i(g_method_count); println(""); + im2 : ., mut = 0; + loop { + if im2 >= g_method_count { break; } + print("gmethod "); print_i(im2); + print(" type "); print_i(r64(g_methods, im2 * 24)); + print(" method "); print_i(r64(g_methods, im2 * 24 + 8)); + print(" mangled "); print_i(r64(g_methods, im2 * 24 + 16)); + println(""); + im2 = im2 + 1; + } + // 跨段引用域探针:逐接口/方法/有效形参的项槽 + // resolved = 0 ≤ t < tt_count()(在重建项表上可解析);unbuilt = t == -1; + // bad = t < -1 || t ≥ tt_count()(**必须为 0**——写侧不产、loader 拒绝)。 + // 摘要 = Σ (tt_tag×31 + tt_a) 与 (tt_c×31 + 2) 的混入(两侧同值 = 段内项索引 + // 解析到同构项;TYPE 段的跨进程同构由 --dump-types 的 probe 行独立承担)。 + slots : ., mut = 0; + built : ., mut = 0; + unbuilt : ., mut = 0; + bad : ., mut = 0; + hsh : ., mut = 0; + ipc : ., mut = 0; + loop { + if ipc >= g_iface_count { break; } + boc := ipc * ESZ_IFACEINFO; + mcc := r64(g_ifaces, boc + OFF_IF_METHOD_COUNT); + mic : ., mut = 0; + loop { + if mic >= mcc { break; } + mbc := boc + OFF_IF_METHODS + mic * ESZ_IFMETHOD; + pcc := r64(g_ifaces, mbc + OFF_IFM_PARAM_COUNT); + slots = slots + 1; + rtc := r64(g_ifaces, mbc + OFF_IFM_RET_TERM); + if rtc == -1 { unbuilt = unbuilt + 1; } + if rtc >= 0 { + if rtc >= tt_count() { bad = bad + 1; } + if rtc < tt_count() { built = built + 1; hsh = hsh * 31 + tt_tag(rtc); hsh = hsh * 31 + tt_a(rtc); hsh = hsh * 31 + tt_c(rtc); } + } + if rtc < -1 { bad = bad + 1; } + pqc : ., mut = 0; + loop { + if pqc >= pcc { break; } + slots = slots + 1; + ptc := r64(g_ifaces, mbc + OFF_IFM_PARAM_TERMS + pqc * 8); + if ptc == -1 { unbuilt = unbuilt + 1; } + if ptc >= 0 { + if ptc >= tt_count() { bad = bad + 1; } + if ptc < tt_count() { built = built + 1; hsh = hsh * 31 + tt_tag(ptc); hsh = hsh * 31 + tt_a(ptc); hsh = hsh * 31 + tt_c(ptc); } + } + if ptc < -1 { bad = bad + 1; } + pqc = pqc + 1; + } + mic = mic + 1; + } + ipc = ipc + 1; + } + print("ifaceprobe: slots "); print_i(slots); + print(" built "); print_i(built); + print(" unbuilt "); print_i(unbuilt); + print(" bad "); print_i(bad); + print(" digest "); println(int_str(hsh)); +} + +fn ccr_type_probe_dump() { + n := tt_count(); + if n > CCR_TYPE_PROBE_N { n = CCR_TYPE_PROBE_N; } + tt_budget_state_reset(); + nn_ok : ., mut = 0; + nn_bad : ., mut = 0; + dnf_ok : ., mut = 0; + lit_ok : ., mut = 0; + h : ., mut = 0; + i : ., mut = 0; + loop { + if i >= n { break; } + norm := tt_norm(i); + if norm < 0 { nn_bad = nn_bad + 1; } else { nn_ok = nn_ok + 1; } + d := -1; + if norm >= 0 { d = tt_is_dnf(norm); } + if d == 1 { dnf_ok = dnf_ok + 1; } + if tt_is_literal(i) == 1 { lit_ok = lit_ok + 1; } + // 摘要 = 规范化结果下标 + DNF 位(-1 = 预算耗尽,下标面用 -1 表示) + h = h * 31 + (norm + 2); + h = h * 31 + (d + 2); + i = i + 1; + } + print("probe: n "); print_i(n); + print(" norm_ok "); print_i(nn_ok); + print(" norm_unknown "); print_i(nn_bad); + print(" dnf_ok "); print_i(dnf_ok); + print(" lit_ok "); print_i(lit_ok); + print(" exhausted "); print_i(g_ty_exhausted); + print(" digest "); println(int_str(h)); +} diff --git a/src/compiler/ccr_types.cr b/src/compiler/ccr_types.cr new file mode 100644 index 00000000..262727ae --- /dev/null +++ b/src/compiler/ccr_types.cr @@ -0,0 +1,398 @@ +// === ccr_types.cr === +// R2 P4 Task 2/3:TYPE(7)/IFACE(8) 两段的**内容构造**(D18 解耦——本文件 corec-only: +// 装填引用桥接层 sh_term_of_ti/sh_iface_sig_*(corearch 无此层)+ checker 层类型行表 / +// 接口表;ccr_io.cr 同时在 corearch 清单内,保持「共享层符号」纯度,只按段表搬运 +// g_ccr_type_seg/g_ccr_iface_seg 缓冲)。corearch 侧的读回(段 → g_types/g_type_terms/ +// g_tt_index + g_iface_entries/g_iface_shape_*/g_ifaces/g_impl_for/g_methods 重建)在 +// ccr_io.cr 的 load_ccr 内。 +// +// 段体字节(D12;小端、offset 相对段首;字段一律 i64 = 8B——与 g_types/ +// g_type_terms 的内存槽逐位同源,无截断面): +// [row_count u32] +// [row_count × 24B {kind,data,extra} i64×3] ← g_types 0..g_type_count-1 原样 +// [term_count u32] +// [term_count × 40B {tag,a,b,c,d} i64×5] ← 项 DAG(哈希不落盘,加载侧重算) +// 加载侧不变量(ccr_io.cr 逐条校验,违规 = 拒绝——三态纪律,不得静默当空表): +// · 子项引用(UNION/INTER 的 a,b;NOT 的 a;MU 的 b;CONS 的 a,b;ATOM 的 c——含 +// c 链元素,逐行归纳)必须 -1 或 **< 自身行号**(拓扑 = 无环;DAG 由 tt_term +// 追加式构造保证)——**ATOM 的 a(ak)与 b(自类型行号/固定性位/令牌值/mut 标记, +// 可达数千)是标注,不得按「< 行号」校验**(sh_ref_mut_marker/sh_name_token/ +// sh_seq_term 三构造点;tt_var/tt_mu 的 a = 绑定变量、tt_top_k 的 a = k 同理); +// · tag ∈ 0..10(TT_*);a..d ≥ -1;越界/拓扑违规 ⇒ 拒绝。 +// +// ── IFACE(8) 段体(R2 P4 Task 3;D14 五小节,各带 count u32;字段宽度见逐小节注)── +// ① [native_count u32 = IFACE_ENTRY_COUNT] [× 24B {ak i32, ti_row i32, name_ni i32, +// lit_code i32, ops i64}] ← g_iface_entries 原样(16 行) +// ② [shape_count u32] [× 8B {name_ni i32, term i32}] ← g_iface_shape_*(名字面 + 形状项) +// ③ [iface_count u32] [× {name_ni i32, method_count i32, generic_count i32, pad i32} +// + method_count × 80B {name_ni i32, param_count i32, self_mode i32, ret_term i32, +// param_terms[8] i32, param_codes[8] i32}] +// —— 方法记录的字段序 = 计划 Interfaces 的**逐字段声明行**({name_ni, param_count, +// self_mode, ret_term, param_terms[8], param_codes[8]});同块末行的尺寸式 +// (…+ param_codes[8] + param_terms[8] = 80B)两数组次序相反 ⇒ **取声明行为准** +// (登记见 Task 3 报告);ret_term/param_terms = 类型项索引(-1 = 不可建; +// 写侧 ccr_iface_populate 遇不可建 = **拒绝落盘**——见该函数注); +// param_codes = **信息面**裸码(`dyn_arr.cr` 的 param_tis 槽语义不变, +// S6 消费点不入本段)。 +// ④ [impl_count u32] [× 8B {trait_ni i32, type_ni i32}] ← g_impl_for(声明元数据) +// ⑤ [method_count u32] [× 12B {type_ni i32, method_ni i32, mangled_ni i32}] ← g_methods +// 加载侧不变量(ccr_io.cr 逐条校验,违规 = 拒绝):五小节计数/长度自洽 + 行走完 == +// seg_end8(无尾随字节)+ native_count == IFACE_ENTRY_COUNT + **跨段引用域**: +// name_ni/type_ni/method_ni/mangled_ni ∈ [0, STR 串数);ti_row ∈ {-1} ∪ [0, TYPE 行数); +// term ∈ {-1} ∪ [0, TYPE 项数)(TYPE(7) 段先于 IFACE(8) 解析 ⇒ 域已建立)。 + +// ─── D13 确定性装填 ─── +// 段内容必须 = 类型表/接口表的**纯函数**(不得随判定历史漂移):项 DAG 是惰性填充的 +// (sh_term_of_ti 只在判定/装填时建项)⇒ 直接序列化「活表」= 内容随判定次数/顺序变 +// (冷/热 .cir 缓存命中 ⇒ 判定次数不同 ⇒ 段不同 = 已登记的冷热分歧类,TODO #5 末条)。 +// 重建 = 项层/引擎/桥接复位 → 按**行序**逐行装填(0..g_type_count-1)→ (接口签名项 +// 装填归 Task 3)⇒ 项行号 = 行序遍历的构造序(确定),与判定历史无关。 +// 调用点前置核查(plan Step 2,逐站点核):save_ccr 两处调用点(main.cr:612 `ccr` +// / main.cr:636 `build`)之后无类型项消费者——`ccr` 分支随后即 return;`build` 分支 +// 其后只拼 corearch 命令行(子进程)并 syscall 执行(R2 P5 T5 前此处另有影子摘要 +// sh_finish——已随影子层下线删除)。 +fn ccr_type_populate() -> int { + // ① 复位:项层(含引擎预算/memo/lits——见 tt_layer_reset 注记)→ 桥接层。 + // 三面都持**旧行号语义**的缓存,少复位任一面 = 陈旧项被当活项(静默)。 + tt_layer_reset(); + sh_map_reset(); // ti→term 桥接缓存(cap/entries 复位;hits 计数随 P5 T5 删除) + sh_unf_map_reset(); // ti→展开项缓存 + // ①.5 形状重注册(D13 顺序第 2 步,R2 P4 Task 3 落地):六形状按 D17 名重注册 + // (幂等覆盖)——先于行装填 ⇒ 形状项行号 = 构造序前段(确定性;形状表本体 + // 随 IFACE 段 ② 小节落盘)。 + iface_shape_builtin_init(); + // ② 行序装填:0..g_type_count-1(原生 9 行经 sh_native_ak 快路径,不入缓存)。 + // 不可译行 ⇒ 拒绝(-1):**不得**把缺项的类型行静默写成空/跳过—— + // 段是「信息恒随载体」的兑现面,缺项即载体损坏。 + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + if sh_term_of_ti(i) < 0 { + print("error: TYPE segment: type row "); + print(int_str(i)); + println(" is not translatable to a type term (save refused)"); + return -1; + } + i = i + 1; + } + // ③ 接口签名项装填(D13 第③段,R2 P4 Task 3 落地):按接口声明序逐方法装填 + // ret_term/param_terms(写入 g_ifaces 的项槽——与 IFACE 段 ③ 小节同一读点)。 + return ccr_iface_populate(); +} + +// ─── IFACE 段 ③ 小节:接口签名项装填(corec 侧)─── +// 逐接口(声明序)逐方法逐形参调 `sh_iface_sig_ret_term`/`sh_iface_sig_param_term` +// (签名**规范形**建项,见 ty_shadow.cr 的 sh_sig_term_of_ti 注),写入 g_ifaces 的 +// OFF_IFM_RET_TERM / OFF_IFM_PARAM_TERMS 槽(未用形参槽 = -1)。 +// +// **不可建 ⇒ 拒绝落盘**(-1 + 诊断;计划 Task 3 Step 1-⑩ / Global Constraints): +// 「信息恒随载体」下签名项缺席即载体损坏。触发面 = 形参/返回类型行不可译进规范形 +// (实测唯一可达形态 = `dyn` 位图行:sh_sig_term_of_ti 对 TYP_DYN 回 -1); +// 接收者槽 = unit 占位项(sh_iface_sig_param_term 的 self 约定),不触发拒绝。 +// **先 report-only**:全语料(计划 Global Constraints 的五类)跑过、零命中后才作硬门 +// (报告登记;Corpus 实测见 Task 3 报告 §report-only)。 +fn ccr_iface_populate() -> int { + iface_ensure(); // 条目表可能早于 init_types 被查(自测通道);幂等 + ii : ., mut = 0; + loop { + if ii >= g_iface_count { break; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + if mc < 0 || mc > MAX_IFACE_METHODS { + print("error: IFACE segment: interface "); + print(int_str(ii)); + print(" method count "); print(int_str(mc)); + println(" out of range (save refused)"); + return -1; + } + mi : ., mut = 0; + loop { + if mi >= mc { break; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT); + if pc < 0 || pc > MAX_IFACE_METHOD_PARAMS { + print("error: IFACE segment: method "); + print(int_str(mi)); + print(" of interface "); print(int_str(ii)); + print(" param count "); print(int_str(pc)); + println(" out of range (save refused)"); + return -1; + } + rt := sh_iface_sig_ret_term(ii, mi); + if rt < 0 { + print("error: IFACE segment: interface "); + print(int_str(ii)); + print(" method "); print(int_str(mi)); + println(" return signature is not translatable to a type term (save refused)"); + return -1; + } + w64(g_ifaces, mbase + OFF_IFM_RET_TERM, rt); + pj : ., mut = 0; + loop { + if pj >= MAX_IFACE_METHOD_PARAMS { break; } + w64(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pj * 8, -1); // 未用槽 = -1 + pj = pj + 1; + } + pk : ., mut = 0; + loop { + if pk >= pc { break; } + pt := sh_iface_sig_param_term(ii, mi, pk); + if pt < 0 { + print("error: IFACE segment: interface "); + print(int_str(ii)); + print(" method "); print_i(mi); + print(" param "); print_i(pk); + println(" signature is not translatable to a type term (save refused)"); + return -1; + } + w64(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pk * 8, pt); + pk = pk + 1; + } + mi = mi + 1; + } + ii = ii + 1; + } + return 0; +} + +// ─── 段体构造(D12 布局 → g_ccr_type_seg/g_ccr_type_seg_len)─── +// 缓冲 = 完整段体(含首字段 row_count),故 ccr_type_seg_size() ≡ 本长度(单源)。 +// 调用前必须已 ccr_type_populate()(段内容 = 装填结果,非惰性活表)。 +fn ccr_type_seg_build() -> int { + rc := g_type_count; + tc := tt_count(); + need := 4 + rc * ESZ_TYPE_ROW + 4 + tc * ESZ_TYPE_TERM_DISK; + buf := alloc(need + 64); + pos : ., mut = 0; + w32(buf, pos, rc); pos = pos + 4; + i : ., mut = 0; + loop { + if i >= rc { break; } + w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)); pos = pos + 8; + w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)); pos = pos + 8; + w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)); pos = pos + 8; + i = i + 1; + } + w32(buf, pos, tc); pos = pos + 4; + j : ., mut = 0; + loop { + if j >= tc { break; } + w64(buf, pos, tt_tag(j)); pos = pos + 8; + w64(buf, pos, tt_a(j)); pos = pos + 8; + w64(buf, pos, tt_b(j)); pos = pos + 8; + w64(buf, pos, tt_c(j)); pos = pos + 8; + w64(buf, pos, tt_d(j)); pos = pos + 8; + j = j + 1; + } + g_ccr_type_seg = buf; + g_ccr_type_seg_len = need; + return 0; +} + +// ─── IFACE 段体构造(D14 五小节 → g_ccr_iface_seg/g_ccr_iface_seg_len)─── +// 缓冲 = 完整段体(含首字段 native_count),故 ccr_iface_seg_size() ≡ 本长度(单源)。 +// 调用前必须已 ccr_type_populate()(③ 小节项槽 = 装填结果)。 +// 尺寸 = 4 + 16×24(①)+ [4 + sc×8](②)+ [4 + Σ(16 + mc×80)](③)+ [4 + ic×8](④) +// + [4 + gc×12](⑤)——与写侧逐字节一致(calc 侧读同一函数)。 +fn ccr_iface_seg_build() -> int { + need : ., mut = 4 + IFACE_ENTRY_COUNT * 24; + need = need + 4 + g_iface_shape_count * 8; + need = need + 4; + ii : ., mut = 0; + loop { + if ii >= g_iface_count { break; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + need = need + 16 + mc * 80; + ii = ii + 1; + } + need = need + 4 + g_impl_for_count * 8; + need = need + 4 + g_method_count * 12; + buf := alloc(need + 64); + pos : ., mut = 0; + // ① 原生条目(IFACE_ENTRY_COUNT 行——写侧段 = 内存表原样;计数 = 常量而非 + // g_iface_entry_count:表内容恒为常量表,计数漂移 = 程序错误 ⇒ 用常量让 + // loader 的 == IFACE_ENTRY_COUNT 闸两侧同源) + w32(buf, pos, IFACE_ENTRY_COUNT); pos = pos + 4; + ei : ., mut = 0; + loop { + if ei >= IFACE_ENTRY_COUNT { break; } + eo := ei * ESZ_IFACE_ENTRY; + buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_AK)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_TI)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_NAME)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_LIT)); pos = pos + 4; + buf_write_i64(buf, pos, r64(g_iface_entries, eo + OFF_IE_OPS)); pos = pos + 8; + ei = ei + 1; + } + // ② 横切形状(名 ni + 形状项) + w32(buf, pos, g_iface_shape_count); pos = pos + 4; + si : ., mut = 0; + loop { + if si >= g_iface_shape_count { break; } + buf_write_i32(buf, pos, r64(g_iface_shape_names, si * 8)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_iface_shape_terms, si * 8)); pos = pos + 4; + si = si + 1; + } + // ③ 用户接口(头 16B + 方法 80B × method_count) + w32(buf, pos, g_iface_count); pos = pos + 4; + fi : ., mut = 0; + loop { + if fi >= g_iface_count { break; } + fo := fi * ESZ_IFACEINFO; + buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_NAME)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_METHOD_COUNT)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_GENERIC_COUNT)); pos = pos + 4; + buf_write_i32(buf, pos, 0); pos = pos + 4; // pad(对齐/预留) + mc2 := r64(g_ifaces, fo + OFF_IF_METHOD_COUNT); + mj : ., mut = 0; + loop { + if mj >= mc2 { break; } + mo := fo + OFF_IF_METHODS + mj * ESZ_IFMETHOD; + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_NAME)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_SELF_MODE)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_RET_TERM)); pos = pos + 4; + pk2 : ., mut = 0; + loop { + if pk2 >= MAX_IFACE_METHOD_PARAMS { break; } + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pk2 * 8)); pos = pos + 4; + pk2 = pk2 + 1; + } + pq : ., mut = 0; + loop { + if pq >= MAX_IFACE_METHOD_PARAMS { break; } + buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_TYPES + pq * 8)); pos = pos + 4; + pq = pq + 1; + } + mj = mj + 1; + } + fi = fi + 1; + } + // ④ impl 边(g_impl_for:{trait_ni, type_ni}——声明元数据,裁决 2 不入判定) + w32(buf, pos, g_impl_for_count); pos = pos + 4; + gi2 : ., mut = 0; + loop { + if gi2 >= g_impl_for_count { break; } + buf_write_i32(buf, pos, r64(g_impl_for, gi2 * 16)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_impl_for, gi2 * 16 + 8)); pos = pos + 4; + gi2 = gi2 + 1; + } + // ⑤ 方法表(g_methods:{type_ni, method_ni, mangled_ni}——iface_find_method 的唯一数据面) + w32(buf, pos, g_method_count); pos = pos + 4; + mi2 : ., mut = 0; + loop { + if mi2 >= g_method_count { break; } + buf_write_i32(buf, pos, r64(g_methods, mi2 * 24)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_methods, mi2 * 24 + 8)); pos = pos + 4; + buf_write_i32(buf, pos, r64(g_methods, mi2 * 24 + 16)); pos = pos + 4; + mi2 = mi2 + 1; + } + g_ccr_iface_seg = buf; + g_ccr_iface_seg_len = need; + return 0; +} + +// save 前单入口(main.cr 两处调用点共用:D13 装填 → 两段体构造)——-1 = 拒绝落盘。 +// R2 P4 Task 3:原 ccr_type_prepare_save 更名(IFACE 段同批入缓冲;调用点三处同步)。 +// R2 P5 Task 2(D23):单槽化的**建项失败闸**也在此(类型面 op 的类型行不可译 ⇒ 拒绝 +// 落盘,rc=1 + 诊断)——三态纪律:不可译**不得**静默留空项(那会让 .ccr 的 NOD 类型码 +// 静默降级)。时点在本函数入口:拆分器(emit 期与 .cir 装载侧)已把失败计数写好, +// 而落盘是最后一道闸(装填/段构造都不看该项面 ⇒ 不在入口拦就没人拦)。 +fn ccr_seg_prepare_save() -> int { + if g_tk_face_fail != 0 { + print("error: "); + print_i(g_tk_face_fail); + println(" type-face DF node(s) with untranslatable type row — refusing to save .ccr (D23)"); + return -1; + } + if ccr_type_populate() != 0 { return -1; } + if ccr_type_seg_build() != 0 { return -1; } + if ccr_iface_seg_build() != 0 { return -1; } + // R2 P6 Task 3(β):NOD 项索引装填——**必须排在 ccr_type_populate() 之后** + // (文件空间 = 该调用重建的项表;emit 期活表索引此前已作废),且排在两段体 + // 构造之后(本函数只**查询**桥接缓存——行装填已把 0..g_type_count-1 全部 + // 建项 ⇒ 全部命中、不新增项 ⇒ 段体字节不受影响)。失败 = 沿用 D23 闸。 + return ccr_nod_item_populate(); +} + +// ─── R2 P6 Task 3(β):NOD 项索引装填(corec 侧;D18——本文件 corec-only)─── +// 逐节点(序 = 全局指令序 = 盘上 NOD 记录序)由**盘上派生码**反查 TYPE 段**文件 +// 空间**的项索引: +// · face 0(类型行面且项可逆)⇒ item = sh_term_of_ti(tk)——此时 +// `tt_atom_of_term(项) == tk`(与分类表同一判定)⇒ loader 侧一致性校验必过; +// · 其余(复合行 / 辅码面 / 无面 / 行越界 / 桥接缺口——含**暖态缺行**) +// ⇒ item = -1(码仍由 NOD 的 `tk` 槽逐字节保真;**不得**猜项,不得近似)。 +// 单源:分类与建项一律走桥接层既有入口(sh_tk_face_of_code / sh_term_of_ti)—— +// 本函数**不引入**第二套建项/分类逻辑,也不置 D23 失败位(那是 sh_tk_split 的 +// 职责;纯查询不得污染落盘闸)。 +// 时点偏差登记:计划 Task 3 Step 3 原文写「项索引与派生码同出 emit 期的那一次 +// 拆分,不得二次构造」——实读时序下不可实施(ccr_type_populate 的 tt_layer_reset +// 在 save 前作废 emit 期引用);本实现 = 保存期由码经同一桥接函数重派生,与 +// `.cir` 装载侧的重派生同构(P4 T4 先例)。 +fn ccr_nod_item_populate() -> int { + n := g_ir_instr_count; + g_ccr_nod_item = alloc((n + 8) * 4); + g_ccr_nod_item_count = n; + ii : ., mut = 0; + loop { + if ii >= n { break; } + tk := iri_tk(ii); + it : ., mut = -1; + if sh_tk_face_of_code(iri_op(ii), tk) == 0 { it = sh_term_of_ti(tk); } + w32(g_ccr_nod_item, ii * 4, it); + ii = ii + 1; + } + return 0; +} + +// ─── 自测/对拍通道(corec 侧 `ccr --dump-types`)─── +// 共享面 dump(ccr_io.cr 的 ccr_type_surface_dump——与 corearch 侧 --dump-types +// **同一条打印路径**,跨进程行格式零分歧)+ corec-only 的「行 → 项」映射节 +// (corearch 无桥接层故无此节;测试按 "rowterms:" 标记切分两侧输出)。 +// 行格式契约见 tests/selfhost/test_ccr_types.py:parse_type_dump。 +fn ccr_type_selftest_dump() { + ccr_type_surface_dump(); + print("rowterms: "); print_i(g_type_count); println(""); + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + print("rowterm "); print_i(i); print(" "); + println(int_str(sh_term_of_ti(i))); + i = i + 1; + } +} + +// ─── 自测/对拍通道(corec 侧 `ccr --dump-ifaces`)─── +// 共享面 dump(ccr_io.cr 的 ccr_iface_surface_dump——与 corearch 侧 --dump-ifaces +// **同一条打印路径**,跨进程行格式零分歧)+ corec-only 的「签名项**活算**」节: +// 段体缓冲构造**之后**重算 sh_iface_sig_ret_term/sh_iface_sig_param_term(独立读点, +// 与 ccr_iface_populate 的写入路径分离)——测试据此断言「落盘值 == 活算值」, +// 即装填与签名来源零漂移(若 populate 写错槽/写旧值,本节与文件/共享转储不一致)。 +// 行格式契约见 tests/selfhost/test_ccr_types.py:parse_iface_dump("ifacesig:" 标记切分)。 +fn ccr_iface_selftest_dump() { + ccr_iface_surface_dump(); + print("ifacesig: "); print_i(g_iface_count); println(""); + ii : ., mut = 0; + loop { + if ii >= g_iface_count { break; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + mi : ., mut = 0; + loop { + if mi >= mc { break; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT); + print("ifacesig "); print_i(ii); print(" "); print_i(mi); + print(" ret "); print_i(sh_iface_sig_ret_term(ii, mi)); + print(" pc "); print_i(pc); println(""); + pj : ., mut = 0; + loop { + if pj >= pc { break; } + print("ifacesigp "); print_i(ii); print(" "); print_i(mi); + print(" "); print_i(pj); print(" "); + println(int_str(sh_iface_sig_param_term(ii, mi, pj))); + pj = pj + 1; + } + mi = mi + 1; + } + ii = ii + 1; + } +} diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index 52c24804..afe6d178 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -4,20 +4,247 @@ // Second pass: type-check function bodies // --- Type table --- -// Entries are 3 ints: kind, data, extra +// Entries are 3 ints: kind, data, extra(24B/条——布局常量 ESZ_TYPE_ROW/OFF_TR_* +// 在 globals.cr 单源,R2 P4 Task 2 起与 TYPE 段序列化/dump 同源) fn alloc_type(kind: int, data: int, extra: int) -> int { idx := g_type_count; grow_types(idx + 1); - w64(g_types, idx * 24, kind); - w64(g_types, idx * 24 + 8, data); - w64(g_types, idx * 24 + 16, extra); + w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_KIND, kind); + w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_DATA, data); + w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_EXTRA, extra); g_type_count = idx + 1; return idx; } +// ─── R2 P2a Task 1(F1):同名 TYP_NAMED 建表去重 ─── +// 背景(P1 影子对拍 9/9 差异的根因):同一类型名在**每个出现点**都建一行——struct 字面量 +// (:2206)、泛型应用的基型(:2191)等——于是同名多行。桥接层按行建原子(AK_NAMED 的 b 槽 +// = 行号)→ 引擎把两行当互异命名类型 → 判不了(unknown)。裁决(用户):根治 = 建表去重, +// 唯一分配点收敛到本函数(alloc_type 仍是裸分配器,语义不变)。 +// 实读核对:全仓 TYP_NAMED 分配点恰 8 处(:406/:554/:598/:613/:622/:920/:2191/:2206), +// 实参恒 (TYP_NAMED, name_idx, 0) → 键 = name_idx(extra 恒 0,不入键)。读取点全部经 +// get_type_data(ti) 取**名字**(:496 是解引用到名字的唯一入口),故按名字归一行不改读法。 +// **比行号的面(去重的收益面)**:泛型应用的**基型行**(TYP_GENERIC_APPLY 的 data = base_ti) +// 与桥接项 AK_NAMED 的 b 槽(`sh_apply_identity_term` 的规范形 = 基型行)都按行比——去重把 +// 「同名字不同出现点 ⇒ 不等」改成「同名 ⇒ 同 base 行,继续比实参」,这正是本条修复的目的 +// (P1 差异的对偶面)。R2 P5 Task 4 后回落的 legacy 结构判等已删 ⇒ 该行号面**只剩**桥接项 +// 的 b 槽(规范形,T3 起身份链按**名字令牌**比较,b 槽为标注)。 +// +// 侧表 g_named_dedup(16B/条 {name_idx, ti},开放寻址线性探测,与 ty_shadow.cr 的 +// g_term_map(P5 T5 前名 g_shadow_map)同式同因)。P0/P1 血泪三件套缺一即可能挂死,逐条落: +// ① 装填因子守卫(**探测前**):(count + 1) * 2 >= cap → 重建扩容。表满且键不存在时 +// 开放寻址永不落空 = 死循环;count 只增不减、恒等于占用槽数(兼作守卫判据)。 +// ② 重建 + **重放既有条目**:count 随重放重算(守卫读的就是它,不得沿用旧值);重放 +// 借道 named_dedup_probe(**不**经守卫)——否则重放自身可能再触发扩容 = 递归。 +// ③ 回写前重探:探测与回写之间若有插入/扩容,先前槽位即失效。本路径当前**不可达** +// (alloc_type 不回入本函数、grow_types 不碰本表),保留 = 固化契约 + 与先例同形。 +NAMED_DEDUP_INIT_CAP : int = 1024; + +fn named_dedup_init() { + if g_named_dedup_cap <= 0 { + nc : ., mut = NAMED_DEDUP_INIT_CAP; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_named_dedup = nb; + g_named_dedup_cap = nc; + g_named_dedup_count = 0; + } +} + +// 类型表重置(行号空间作废)时侧表随之作废:cap=0 → 下次用惰性重建(空槽全 -1)。 +// **可达且必需**(Task 1 评审纠错——原注「不可达防御」是错的):`reset_frontend_state()` +// (globals.cr:332)清 `g_type_count` 却**不碰本侧表**,而 LSP 的 `lsp_check_file` +// (src/lsp/lsp.cr:168→172→187)**每请求**都走 `reset_frontend_state() → check_all()` +// 而 `check_all()` 首行即 `init_types()`(= 本重置);且 `reset_frontend_state` 不清 +// 字符串表(`g_str_count`/`g_str_hash` 零触及)→ 长驻进程内 `str_intern` 名字下标跨请求 +// 稳定 ⇒ 陈旧 name→ti **必然命中**,返回的是**上一请求**的行号(此刻新表仅重建到基类型) +// → 越界/错行静默错型。删本重置 = 静默错型,不是可选防御。 +fn named_dedup_reset() { + g_named_dedup_cap = 0; + g_named_dedup_count = 0; +} + +// 无守卫探测(**只可在扩容守卫之后调用**;重建重放借道这里,不经守卫 = 防递归): +// 命中 → 该键所在槽;未命中 → 空槽(**不插入**)。形态 = sh_map_find_nogrow(槽位变量 +// `slot` + break + 尾 return;函数体不以无 break 的 loop 收尾——自托管 checker 对该形态 +// 有 TF01 误报面)。 +fn named_dedup_probe(name_idx: int) -> int { + cap := g_named_dedup_cap; + p : ., mut = tt_mod(name_idx, cap); + slot : ., mut = -1; + loop { + k := r64(g_named_dedup, p * 16); + if k < 0 { slot = p; break; } + if k == name_idx { slot = p; break; } + p = p + 1; if p >= cap { p = 0; } + } + return slot; +} + +// 扩容 = 重建 + 重放既有条目(sh_map_rehash / 引擎 grow_tt_index 同式);计数随重放重算。 +fn named_dedup_rehash() { + old := g_named_dedup; + old_cap := g_named_dedup_cap; + nc : ., mut = old_cap * 2; + if nc < NAMED_DEDUP_INIT_CAP { nc = NAMED_DEDUP_INIT_CAP; } + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_named_dedup = nb; + g_named_dedup_cap = nc; + g_named_dedup_count = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + k := r64(old, j * 16); + if k >= 0 { + s := named_dedup_probe(k); + w64(g_named_dedup, s * 16, k); + w64(g_named_dedup, s * 16 + 8, r64(old, j * 16 + 8)); + g_named_dedup_count = g_named_dedup_count + 1; + } + j = j + 1; + } +} + +// 槽位(含装填因子守卫 + 重建重放):命中 → 该键所在槽;未命中 → 空槽(不插入)。 +fn named_dedup_slot(name_idx: int) -> int { + named_dedup_init(); + if (g_named_dedup_count + 1) * 2 >= g_named_dedup_cap { named_dedup_rehash(); } + return named_dedup_probe(name_idx); +} + +// 同名 TYP_NAMED 归一行(唯一分配点,8 处调用点见文件头注):命中返回既有行,未命中 +// 分配 + 登记。键域契约:name_idx >= 0(-1 = 空槽哨兵)——负键不进侧表(退化为裸分配), +// 否则「负键」与「空槽」同形会把空槽读成命中并返回**未初始化 value 槽**的任意 ti +// (`alloc` 是 bump 分配且不置零 → 不是特指某个固定值,而是任意值——静默错型)。 +// 8 处实参均为名字下标(str_intern / si_name / ei_name / 类型节点的 ast_int_val)≥ 0, +// 该分支当前不可达 = 防御。 +fn alloc_named_type(name_idx: int) -> int { + if name_idx < 0 { return alloc_type(TYP_NAMED, name_idx, 0); } + slot := named_dedup_slot(name_idx); + if r64(g_named_dedup, slot * 16) == name_idx { return r64(g_named_dedup, slot * 16 + 8); } + ti := alloc_type(TYP_NAMED, name_idx, 0); + // 回写前重探(见文件头注③) + slot2 := named_dedup_slot(name_idx); + if r64(g_named_dedup, slot2 * 16) == name_idx { + // 防御(当前不可达):既有登记优先 → 本次刚分配的行成孤儿行(不写侧表、不计数)。 + // 反例写法(无条件写 slot2)会覆盖活跃条目 = 丢登记。 + return r64(g_named_dedup, slot2 * 16 + 8); + } + w64(g_named_dedup, slot2 * 16, name_idx); + w64(g_named_dedup, slot2 * 16 + 8, ti); + g_named_dedup_count = g_named_dedup_count + 1; + return ti; +} + +// 自测用(type_selftest.cr):该名字当前在类型表里占的行数(恒 0/1;0 = 尚未分配)。 +fn named_dedup_rows(name_idx: int) -> int { + n : ., mut = 0; + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + if get_type_kind(i) == TYP_NAMED && get_type_data(i) == name_idx { n = n + 1; } + i = i + 1; + } + return n; +} + +// ─── R2 P2a Task 3(C-4):侧表 ↔ res_type_node **管线内断言**(`--verify-named-dedup`)─── +// 背景(Task 1 交接必测项,评审裁决):`f1.*` 用例是**人造夹具**(自测通道不经 check_all, +// 手工 alloc + 单名查行数)——它证明不了「8 个生产分配点全走侧表」:任一生产点若退回裸 +// `alloc_type(TYP_NAMED, name, 0)`,自测照样全绿,而真实编译中该名字会**占两行**(读名字 +// 的点仍对,比**行号**的点错——现存比行号面 = 泛型应用基型行 / 桥接项 AK_NAMED 的 b 槽)。 +// 本函数把该性质升为**行为级证据**:跑在真实编译流水线(check_all 之后)上,三组断言 +// 全部基于本编译期的真实类型表 / 符号表 / AST,不构造任何夹具: +// ① 类型表 → 侧表:每个 TYP_NAMED 行按名查侧表必须**命中且回指本行**(唯一分配点 ⇒ +// 每个 named 行都该有登记;回指不等 = 有生产点绕过侧表另建了同名行); +// ② 侧表 → 类型表 + 唯一性:每条登记必须指回 kind=TYP_NAMED 且名字相符的**合法行**, +// 且该名字在类型表里**恰 1 行**(去重不变量,全表而非抽样); +// ③ 读取面(替换弱断言 f1.row_count 的那条):AST 中每个解析为**已注册命名类型**的 +// EXPR_IDENT 节点,`res_type_node(node)` 的返回值必须等于侧表命中 ti——覆盖 +// res_type_node 的两条出口(SYM_TYPE 注册行 / 惰性 alloc_named_type)。 +// 只计不改:不动类型表/侧表/符号表,只读 + 计数。返回 = 不一致条数(0 = 全过)。 +// 摘要行恒打印(本函数仅在 --verify-named-dedup 下被调用;默认路径零调用)。 +// 性能注:③ 的 find_gsym 是符号表倒序线性扫描 → 语料越大越慢,仅调试通道(默认关)。 +fn named_dedup_verify() -> int { + bad : ., mut = 0; // 不一致条数(返回) + named_rows : ., mut = 0; // 断言①覆盖的 TYP_NAMED 行数 + ident_checked : ., mut = 0; // 断言③覆盖的类型名引用节点数 + // ① 类型表 → 侧表 + ti : ., mut = 0; + loop { + if ti >= g_type_count { break; } + if get_type_kind(ti) == TYP_NAMED { + named_rows = named_rows + 1; + ni := get_type_data(ti); + slot := named_dedup_slot(ni); + if r64(g_named_dedup, slot * 16) != ni { bad = bad + 1; } + else if r64(g_named_dedup, slot * 16 + 8) != ti { bad = bad + 1; } + } + ti = ti + 1; + } + // ② 侧表 → 类型表 + 唯一性 + e : ., mut = 0; + loop { + if e >= g_named_dedup_cap { break; } + ni2 := r64(g_named_dedup, e * 16); + if ni2 >= 0 { + ti2 := r64(g_named_dedup, e * 16 + 8); + if get_type_kind(ti2) != TYP_NAMED { bad = bad + 1; } + else if get_type_data(ti2) != ni2 { bad = bad + 1; } + if named_dedup_rows(ni2) != 1 { bad = bad + 1; } + } + e = e + 1; + } + // ③ 读取面:EXPR_IDENT(类型名引用)→ res_type_node 必须落回侧表同一行 + ai : ., mut = 0; + loop { + if ai >= g_ast_count { break; } + if ast_kind(ai) == EXPR_IDENT { + ni3 := ast_int_val(ai); + si := find_gsym(ni3); + if si >= 0 && sym_kind(si) == SYM_TYPE { + sti := sym_type(si); + // 泛型形参也 def_sym 为 SYM_TYPE,但其行 kind = TYP_GENERIC_PARAM(不经侧表) + if get_type_kind(sti) == TYP_NAMED { + ident_checked = ident_checked + 1; + slot3 := named_dedup_slot(ni3); + if r64(g_named_dedup, slot3 * 16) != ni3 { bad = bad + 1; } + else if r64(g_named_dedup, slot3 * 16 + 8) != res_type_node(ai) { bad = bad + 1; } + } + } + } + ai = ai + 1; + } + print("[named-dedup] named_rows="); + print(int_str(named_rows)); + print(" ident_checks="); + print(int_str(ident_checked)); + print(" mismatches="); + println(int_str(bad)); + return bad; +} + fn init_types() { g_type_count = 0; + named_dedup_reset(); // 类型表重置 → 侧表随之作废(陈旧 name→ti 不得跨重置复用) + // R2 P3 Task 5:约束诊断去重侧表同理作废(键含 AST 节点下标:跨编译期复用会漏报/误报) + gen_constr_seen_reset(); + // R2 P2a Task 3 评审 Critical:桥接缓存(ti→term)**同理必须作废**——本批起判定路径无条件 + // 调 sh_term_of_ti,长驻进程(corelsp 每请求 init_types)复用行号时会命中陈旧 ti→term + // ⇒ 两个不同类型被判等(静默漏报;评审实证见 ty_shadow.cr:sh_map_reset 注记)。 + sh_map_reset(); + // R2 P3 Task 0:展开层缓存(ti→展开项)**同理必须作废**(同因:行号空间复用 ⇒ 陈旧 + // ti→展开项命中 = 把上一请求的类型结构安到当前行上;同 sh_map_reset 的评审 Critical)。 + sh_unf_map_reset(); + // R2 P5 Task 4:判定回落计数(g_replace_*)随 legacy 一并删除——未知面改走 P-A 硬错 + // (ICE04,无跨请求累积语义:诊断在产生它的那次编译里即出)。 + // R2 P5 Task 2(D23):单槽化建项失败位同生命周期(类型行号空间作废 ⇒ 该位只对本 + // 编译期有意义;LSP 每请求 init_types ⇒ 不跨请求累积)。 + g_tk_face_fail = 0; alloc_type(TYP_BASE, TY_INT, 0); // TI_INT = 0 alloc_type(TYP_BASE, TY_DEX, 0); // TI_DEX = 1 alloc_type(TYP_BASE, TY_BOOL, 0); // TI_BOOL = 2 @@ -31,6 +258,16 @@ fn init_types() { // is_ptr_var 的哨兵守卫对首个真实类型误触发。占住下标 8 后用户类型从 9 起, // 守卫永不再命中真实类型(TI_DEX_S 本身仍不查类型表,见 ir_gen.cr 注释)。 alloc_type(TYP_BASE, TY_DEX_S, 0); // TI_DEX_S = 8 占位 + // R2 P2b Task 1:本质条目表(iface_registry.cr)——静态数据(AK_*/TI_* 常量 + -1/0 + + // R2 P4 Task 3 起的注册名 ni),不 alloc 类型行、不缓存本函数刚分配的行号 ⇒ 重复调用 + // 无副作用(长驻进程每请求一次)。 + // 位置 = 9 行原生 alloc 之后(表内容不依赖类型表,此处仅为「随类型表生命周期初始化」)。 + iface_registry_init(); + // R2 P4 Task 3(裁决 1 + D17):六形状**名字生产注册面**(sequence/sequence_ro/ + // sequence_rw/indexable/iterable/product → sh_shape_*() 构造点)。无条件调用(幂等: + // iface_shape_register 同名覆盖;reset_frontend_state 清 count 后由本行重建)—— + // 段内容 = 表本体(生产编译下恰 6 条)。详细边界见 iface_shape_builtin_init 注。 + iface_shape_builtin_init(); } // ── Runtime builtin declarations (no .cr body, implemented in rt.s) ── @@ -97,68 +334,274 @@ fn get_type_extra(ti: int) -> int { return 0; } -fn type_equal(t1: int, t2: int) -> bool { - if t1 == t2 { return true; } - // Compare structure for non-base types - if t1 >= 0 && t2 >= 0 && t1 < g_type_count && t2 < g_type_count { - k1 := get_type_kind(t1); - k2 := get_type_kind(t2); - if k1 == TYP_NAMED && k2 == TYP_NAMED { - return get_type_data(t1) == get_type_data(t2); - } - - if k1 == TYP_ARRAY && k2 == TYP_ARRAY { - if type_equal(get_type_data(t1), get_type_data(t2)) { - if get_type_extra(t1) == get_type_extra(t2) { - return true; - } - } - return false; - } - if k1 == TYP_TUPLE && k2 == TYP_TUPLE { - if get_type_data(t1) != get_type_data(t2) { return false; } - start1 := get_type_extra(t1); - start2 := get_type_extra(t2); - cnt := get_type_data(t1); - i : ., mut = 0; - loop { - if i >= cnt { break; } - if !type_equal(r64(g_gen_apply_data, (start1 + i) * 8), r64(g_gen_apply_data, (start2 + i) * 8)) { return false; } - i = i + 1; - } - return true; - } - if k1 == TYP_REF && k2 == TYP_REF { - return get_type_extra(t1) == get_type_extra(t2) && type_equal(get_type_data(t1), get_type_data(t2)); - } - if k1 == TYP_PTR && k2 == TYP_PTR { - return type_equal(get_type_data(t1), get_type_data(t2)); - } - if k1 == TYP_SLICE && k2 == TYP_SLICE { - return type_equal(get_type_data(t1), get_type_data(t2)); +// ─── R2 P2a Task 2(F2):常量档数组长度约束 ─── +// 背景(P1 findings §6.F2):身份判等(当时的 type_equal 结构判等,R2 P5 Task 4 已删)曾把 N +// 与元素判等绑在一起 = N 属**类型身份**;而桥接按 R1 裁决 **N 不入身份**(AK_SEQUENCE 参数链 +// 只含元素项)→ 判定替换(Task 3:引擎 ty_equiv)后 `[int;4]` → `[int;3]` 会**静默通过** +// (现状是编译错误 error[TF01])。用户裁决:落「常量档长度约束」——保持现状拒绝语义,不留 +// 静默缺口。本函数即该约束:N 从身份中**迁出**,成为具名、可独立调用/独立测试的检查 +// (身份分支不再比 N)。 +// +// 语义(本批 = 常量档:N 皆字面量 → 编译期定 恒真/恒假): +// 沿两类型的**结构对应位置**下钻(下钻位置 = 结构判等(已删)的**递归位**:数组元素 / +// 指针元素 / 引用元素 / 切片元素 / 元组字段 / 泛型应用实参),在**数组位置**比较 N(extra) +// ——N 必须相等;不等即 0(违反)。其余情形(异 kind / 异元数 / 非数组构造子)长度面无约束 +// → 1(满足)。**1 = 满足;0 = 违反**。 +// ⚠「同形」仅指**递归位覆盖**(下钻走得到的位),**不**指比较项相同——各构造子另有非长度 +// 面,且**一律归身份判定(引擎)**,本约束有意不重复(只认 N = TYP_ARRAY 的 extra 一个维度): +// · REF —— 身份含 `extra`(mut 标记;桥接链 [mut 标记项, 元素项],见 ty_shadow.cr +// `sh_ref_mut_marker`),本函数不重复; +// · TUPLE / GENERIC_APPLY —— 身份另比元数、APPLY 另比基型行号(规范形),同上; +// · 反向不对称 —— PTR 的 `extra`(地址空间位 asp,`infer_expr` 的指针升格分配点)身份 +// **亦不比**(现状如此,非本任务面),本函数同样不引入该比较。 +// 故切勿据「同形」推断两者判等项一致(评审 M1 收口)。 +// 为何必须下钻:N 迁出身份后,嵌在非数组构造子内部的位置(`[[int;3];2]` 的数组元素位 / +// `G<[int;3]>` 的泛型实参位 / `*[int;3]` 的指针元素位 / `(int, [int;3])` 的元组字段位) +// 不再有任何判等负责——不下钻即静默放宽(收紧面的反面)。覆盖面由 type_selftest.cr 的 +// f2.* 用例逐位钉住(同结构同长 → 1 / 同结构异长 → 0 双断)。 +// 边界(如实登记):**符号档未实现**(VC 义务 = 验证管线消费,本计划显式不做);**动态档 = +// 运行期检查**(F11 侧表 + IR_BOUNDS_CHECK,R1 已落)⇒ 编译期**不放行**视图→固定(长度 +// 不可证,见下「方向规则」);异形不下钻(该面由身份判定的 kind/参数结构比较负责,本函数 +// 不越权给 0 = 不误拒)。 +// 无环:TYP_NAMED / TYP_GENERIC_PARAM / TYP_BASE / TYP_DYN 不下钻 → 深度 = 类型嵌套深度。 +// +// ─── R2 P3 Task 1:方向规则(定长退役收口)─── +// 语义(spec §5.1):`[T;N] <: [T]`(更强长度约束 = 子类型);`[T] ⊄ [T;N]`(除非证 len==N)。 +// **参序约定 =(源/实参 a,目标/形参 b)**:a 的长度约束须不弱于 b 的。规则: +// · 源 = TYP_SLICE(视图)且 目标 = TYP_ARRAY(固定)⇒ **0**(切片运行期长度不可证; +// 动态档交给运行期检查 ⇒ 编译期拒绝——**不给 -1 当 0**,「无法证」= 不放行); +// · 源 = TYP_ARRAY 且 目标 = TYP_SLICE ⇒ 1(拓宽,pB/pAsgnSlice 语义保持); +// · 其余跨 kind ⇒ 1(长度面无约束——身份判定负责,现状不变;负控 f2.nonarray_no_constraint)。 +// ⚠ 参序敏感 ⇒ 10 个判定点的实参序在本批**归一为(源,目标)**(P2a 落点时各站参序随原 +// `type_equal` 调用继承、未归一:站点 5 本即(值, 声明),站点 6/8/9/10 与 unify 三处为 +// (目标, 源)⇒ 换序)。**换序对既有语义零影响**的证明面:身份面 type_equal 与长度面 +// 同 kind 的 N 比较**两面皆对称**(ty_equiv 双向 / extra 相等),逐站复验见 p3-task1 报告; +// hotpatch 站(1,两侧皆声明、无源/目标)改用 `type_compat_sym`(对称核 = 现状语义)。 +// **方向只在「宽度不匹配」的固定性维上生效**——同 kind 面(含两个数组比 N)逐位保持 P2a 语义。 +fn array_len_walk(ti_a: int, ti_b: int, dir: int) -> int { + ka := get_type_kind(ti_a); + kb := get_type_kind(ti_b); + if ka != kb { + if dir == 1 { if ka == TYP_SLICE && kb == TYP_ARRAY { return 0; } } // 视图→固定:不给放行 + return 1; + } + if ka == TYP_ARRAY { + if get_type_extra(ti_a) != get_type_extra(ti_b) { return 0; } + return array_len_walk(get_type_data(ti_a), get_type_data(ti_b), dir); + } + if ka == TYP_PTR || ka == TYP_REF || ka == TYP_SLICE { + return array_len_walk(get_type_data(ti_a), get_type_data(ti_b), dir); + } + if ka == TYP_TUPLE { + fc := get_type_data(ti_a); + if fc != get_type_data(ti_b) { return 1; } // 字段数不同:身份判定负责 + fs1 := get_type_extra(ti_a); + fs2 := get_type_extra(ti_b); + fi : ., mut = 0; + loop { + if fi >= fc { break; } + if array_len_walk(r64(g_gen_apply_data, (fs1 + fi) * 8), r64(g_gen_apply_data, (fs2 + fi) * 8), dir) == 0 { return 0; } + fi = fi + 1; } - if k1 == TYP_GENERIC_APPLY && k2 == TYP_GENERIC_APPLY { - if get_type_data(t1) != get_type_data(t2) { return false; } - start1 := get_type_extra(t1); - start2 := get_type_extra(t2); - count1 := r64(g_gen_apply_data, start1 * 8); - count2 := r64(g_gen_apply_data, start2 * 8); - if count1 != count2 { return false; } - ai : ., mut = 0; - loop { - if ai >= count1 { break; } - if !type_equal(r64(g_gen_apply_data, (start1 + 1 + ai) * 8), r64(g_gen_apply_data, (start2 + 1 + ai) * 8)) { return false; } - ai = ai + 1; - } - return true; + return 1; + } + if ka == TYP_GENERIC_APPLY { + if get_type_data(ti_a) != get_type_data(ti_b) { return 1; } // 基型不同:身份判定负责 + as1 := get_type_extra(ti_a); + as2 := get_type_extra(ti_b); + ac1 := r64(g_gen_apply_data, as1 * 8); + ac2 := r64(g_gen_apply_data, as2 * 8); + if ac1 != ac2 { return 1; } // 实参数不同:身份判定负责 + ai : ., mut = 0; + loop { + if ai >= ac1 { break; } + if array_len_walk(r64(g_gen_apply_data, (as1 + 1 + ai) * 8), r64(g_gen_apply_data, (as2 + 1 + ai) * 8), dir) == 0 { return 0; } + ai = ai + 1; } - if k1 == TYP_GENERIC_PARAM && k2 == TYP_GENERIC_PARAM { - return get_type_data(t1) == get_type_data(t2); + return 1; + } + return 1; +} + +// 方向面(有源/目标语义的站点):见上「参序约定」注记。 +fn array_len_constraint_ok(ti_a: int, ti_b: int) -> int { + return array_len_walk(ti_a, ti_b, 1); +} + +// 对称核(无方向的站点:hotpatch 一致性——两侧皆声明):= R2 P2a Task 2 原语义逐字 +// (跨 kind 一律无约束)。保留为独立入口的理由 = 「无源/目标」不是「源/目标之一」的退化, +// 用方向版会退化成**任一路由参序决定的任意判**(同一声明对换个声明序即改判)。 +fn array_len_constraint_sym(ti_a: int, ti_b: int) -> int { + return array_len_walk(ti_a, ti_b, 0); +} + +// F2 判定点组合(**站点**用):身份判等 ∧ 常量档长度约束 ∧(Task 1)方向规则。 +// N 不入身份(R1 裁决)⇒ 判定点必须在 type_equal 之外显式补检——否则 Task 3 替换身份实现后 +// N/固定性不匹配静默通过。返回:1 = 兼容;0 = 身份不匹配(原诊断措辞);-1 = 长度/方向约束 +// 违反(专属措辞;**码不变**——TF01/TA01/TC02 的「门」= 拒绝集合不变,仅成因分列)。 +// **总是调用 type_equal**(判定入口单点):判定点**一律**经此组合函数,不得就地内联等价比较 +// ——否则判定面旁路(历史依据:影子对拍期站点调用次数/位置必须可比,P1 登记;影子通道已随 +// P5 Task 5 下线,本条 = 判定面单点纪律的继承)。 +// 参序 =(源,目标)——见 array_len_walk 上方的归一说明。 +fn type_compat_strict(ti_a: int, ti_b: int) -> int { + if !type_equal(ti_a, ti_b) { + // ─── R2 P3 Task 4:**可选目标的子类型注入**(`T ⊆ T?`、`null ⊆ T?`)─── + // 语义(spec §5.4):`T?` = `T ∪ null` ⇒ 裸 T 值与 None 值**本来就该**流进 T? 的槽 + // (返回位/赋值位/实参位/字段位…同一组合函数)。而行面判定是**身份**口径 + // (type_equal = 等价),等价不含 `int ⊆ int ∪ null` ⇒ 不注入则 `fn g() -> int? { return 5; }` + // 被拒(实测 TF01),「联合类型」在行为面等于没落地。 + // 收窄面(为何不是「站点改子类型」):注入**只在目标行 kind == TYP_OPTIONAL 时**触发 + // —— 该 kind 只由 `T?` 产生,语料零命中(全仓无 `T?`),故既有接受/拒绝集合**逐点不变**; + // 站点面(type_equal 采样、长度档、措辞分派)与 P2a/P2b 判据**零改动**。反方向 + // (`T?` 值进 `T` 槽)不注入:引擎 ty_sub(union, T) = 0 ⇒ 照旧拒绝(soundness 面)。 + // 三态纪律:引擎 0/-1(不含/未知)一律**不放行**(未知不得当放宽)。 + if get_type_kind(ti_b) == TYP_OPTIONAL { + if ti_subsumes(ti_a, ti_b) == 1 { return 1; } } + return 0; + } + if array_len_constraint_ok(ti_a, ti_b) == 0 { return -1; } + return 1; +} + +// R2 P3 Task 4:ti 级包含判定(源 ⊆ 目标)——桥接 + 引擎 `ty_sub` 三态,**仅 1 放行** +// (0 = 确定不含、-1 = 未知 ⇒ 都回 0:本入口只服务「放松注入」,未知绝不当放宽 = 三态纪律)。 +// 预算前后隔离照 type_equal_engine 的窗口纪律(引擎 memo 跨查询命中会让结果依赖预算历史); +// 不触碰 `g_replace_*` 计数(那两个计数是 type_equal 替换面的台账,本入口不属该面)。 +fn ti_subsumes(src_ti: int, tgt_ti: int) -> int { + a := sh_term_of_ti(src_ti); + b := sh_term_of_ti(tgt_ti); + if a < 0 || b < 0 { return 0; } + ty_budget_reset(200000); + s := ty_sub(a, b); + ty_budget_reset(200000); + if s == 1 { return 1; } + return 0; +} + +// 无方向站点的组合(与 type_compat_strict 同构,仅长度面走对称核):hotpatch 站(1)用。 +fn type_compat_sym(ti_a: int, ti_b: int) -> int { + if !type_equal(ti_a, ti_b) { return 0; } + if array_len_constraint_sym(ti_a, ti_b) == 0 { return -1; } + return 1; +} + +// F2 判定点诊断:verdict ≠ 1 时发码——-1(长度约束违反)用专属措辞,0 用原措辞。 +fn diag_type_incompatible(verdict: int, code: int, what: string, line: int, col: int) { + if verdict == -1 { + check_error(code, "Array length constraint not satisfied", line, col); + } else { + check_error(code, what, line, col); } +} + +// ─── R2 P5 Task 6(TODO #32):声明位点的值/注解兼容判定 ─── +// 背景:`EXPR_LET` 站点自 P3 Task 1 实测起为**无任何兼容检查**的洞(`checker.cr` 该分支 +// 只登记符号,ti = 注解行 ⇒ 后端按注解行发射 = 静默错产物)。实测(旧/新二进制同值): +// `x : int = "s"` / `x : [int;3] = s`(切片)/ `x : [int;4] = [1,2,3]`(异长常量档)/ +// `x : int? = 5; y : int = x`(可选流进窄槽)全部 check rc=0 零诊断。 +// 判定 = `type_compat_strict`(身份 + 长度档 + 可选目标注入),照赋值位点(EXPR_ASSIGN) +// 与返回位点(TF01)同款组合函数;参序归一(P3 T1 §3.1)=(源 = 初始化值, 目标 = 注解行)。 +// 两个调用点共用本函数:① `infer_expr` 的 `EXPR_LET` 分支(局部);② `check_global_let` +// (全局初始化器——同形缺口,一并在 #32 划界内收口)。 +// 豁免(逐条对齐邻站,各附理由;无豁免即无判定): +// ① 无注解 / `: .` / `: auto`(type_node < 0)或无初值(val_node < 0)⇒ 无契约可核; +// ② 注解 = `dyn` ⇒ 不判(dyn 槽按值追踪;照赋值位点 `tt == TI_DYN` 分支与本站下行 +// `dyn_set_type` 语义——注解 dyn 时值的类型**就是**该槽的合法类型集); +// ③ 值 = `never`(TI_NEVER)⇒ 不判——两义:**底部**(发散值,照返回位点 +// `body_ti != TI_NEVER` 豁免同路)+ 表达式层 TI_NEVER 的**错误标记**义(未定义名/ +// 未定义函数等错误路径的返回值,见 EXPR_IDENT/EXPR_CALL 的 `return TI_NEVER`)—— +// 后者是**诊断级联抑制**(实测 r1/r3 探针:只发一条 N01/N06,无二次 TA02)。 +// 实测登记:`-> never` 函数的**调用**被推断为 unit(非 never)⇒ `x : int = boom()` +// 新增 TA02——与邻站现状一致(`return boom()` 今天即报 TF01,探针 q15),非本检查 +// 新引入的类;全语料零 `-> never`(src/tests/examples 皆无)⇒ 零命中。 +// ④ 注解 kind == `TYP_GENERIC_PARAM` ⇒ 不判(声明期不可验证;照返回位点 +// `get_type_kind(ret_ti) != TYP_GENERIC_PARAM` 豁免)。 +// 三态纪律:本函数只消费 `type_compat_strict` 的 {1,0,-1};不可判(引擎 -1 / 桥接缺口)由 +// `type_equal` 内部按 P-A 发 ICE04(硬错),**不**在本函数内回落或近似。 +fn check_let_annot_compat(node: int, val_node: int, val_ti: int, ti: int) { + type_node := ast_b(node); // EXPR_LET: b = 注解类型节点(-1 = 无) + if type_node < 0 || val_node < 0 { return; } + if ti == TI_DYN { return; } + if val_ti == TI_NEVER { return; } + if get_type_kind(ti) == TYP_GENERIC_PARAM { return; } + compat := type_compat_strict(val_ti, ti); + if compat != 1 { + diag_type_incompatible(compat, EC_TA_DECL, "Variable declared as " + type_display(ti) + ", got " + type_display(val_ti), ast_line(node), ast_col(node)); + } +} + +// R2 P1:本文件曾同时有**结构判等实现**(原名 type_equal)与引擎判定两套。 +// R2 P2a Task 3:结构判等降级为 `type_equal_legacy`(影子对拍对照物 + 引擎 -1 的回落实现)。 +// **R2 P5 Task 4 删除**(本处):断言前提 = 清零判据成立(全语料 72 档 `decisions=agree=32988`、 +// `replace_bridge=replace_unknown=0`;残留 -1 面由 Task 3 命名面判定化 + Task 3b 不变槽元素 +// 三态收口,探针 `unknown_engine=0`)——D24 顺序:清零 → 删 legacy → 影子层下线(T5 已完成, +// 影子通道整体删除;判定面回归网 = 冻结基线同源对拍 + 行为探针 + 突变控制,见 TODO 与 +// src/ci/run.sh 的自述)。 +// 删除件与替代证据(D26)见 p5-task4-report §判据;P-A 政策见 `type_equal_engine` 头注。 + +// P-A(Task 4 Step 1 推荐案,落纸):判定不可判 = **硬错 ICE04**(三态纪律:未知不得当 0/1, +// 也**不得**回落/近似)。两个出口: +// ① 桥接缺口(sh_term_of_ti 返回 -1 = 该行译不成类型项——行越界/桥接未覆盖); +// ② 引擎三态 -1(预算耗尽 / 未覆盖面:μ/¬/⊤ₖ 等,见 Task 0 表 D 清点)。 +// 诊断面:新码 ICE04(main.cr 硬名单 ⇒ build 亦拒绝)+ 反例(两侧类型项文本,D23 先例)。 +// 判定点(type_equal 无 AST 位置)⇒ line/col = 0;定位由调用点自身的诊断(TA01/TF01…)承担。 +// 全语料零命中(report-only 先行,由 Task 3 的 0 计数支撑)⇒ 零行为变化。 +fn ty_indeterminate_report(kind: int, t1: int, t2: int, a: int, b: int, unc: int, exh: int) { + msg : ., mut = ""; + if kind == 0 { + msg = "type judgment indeterminate: no type term for type row " + int_str(t1) + " / " + int_str(t2) + " (bridge gap)"; + } else { + cause : ., mut = "uncovered face"; + if unc == 0 && exh != 0 { cause = "budget exhausted"; } + msg = "type judgment indeterminate: " + tt_display(a) + " vs " + tt_display(b) + " (" + cause + ")"; + } + check_error(EC_ICE_TY_INDET, msg, 0, 0); +} + +// ─── R2 P2a Task 3 / R2 P5 Task 4:判定 = 引擎唯一权威 ─── +// 判定主体(P1 的包装层拆两半:本函数 = 引擎判定,`type_equal` = 入口转发)。 +// ① 同一行快路径:t1 == t2 → true(含负 ti 情形;快路径不改任何判定结果,只省一次桥接 +// + 引擎查询); +// ② 桥接(sh_term_of_ti):任一侧译不成类型项(-1)⇒ **P-A 硬错**(ICE04 + 桥接缺口措辞) +// —— 判定不可进行 = 未知;旧行为(Task 4 前)= 回落 legacy + g_replace_bridge(已删); +// ③ 引擎三态:1 → true;**0 → false(引擎结论即权威)**;-1(未知:预算耗尽/未覆盖面)⇒ +// **P-A 硬错**(ICE04 + 成因 + 反例)——旧行为 = 回落 legacy + g_replace_unknown(已删); +// ④ 预算隔离:判定前后各 ty_budget_reset(200000)——引擎 memo 跨查询命中会让结果依赖预算 +// 历史而非输入项(P0 终审 Critical 3 实证);判定路径不得受前次查询影响。 +// 返回 false 是「未知」在 bool 面上唯一的保守出口(fail-closed:不因未知放宽), +// **不等价于**「确定不等价」——后者由 ICE04 与实际 0 区分(诊断只在未知时发)。 +// N 面(Task 2/3 评审裁决「N 不得回身份」):本函数路径**不含** N 比较——桥接的 AK_SEQUENCE +// 参数链只含元素项;长度拒绝一律由 array_len_constraint_ok 在判定点(type_compat_strict) +// 承担。本函数**不得**引入任何 N(get_type_extra 的数组位)比较。 +fn type_equal_engine(t1: int, t2: int) -> bool { + if t1 == t2 { return true; } // 快路径:同一行 + a := sh_term_of_ti(t1); + b := sh_term_of_ti(t2); + if a < 0 || b < 0 { + ty_indeterminate_report(0, t1, t2, a, b, 0, 0); + return false; + } + ty_budget_reset(200000); + e := ty_equiv(a, b); + // 成因位须在**第二次 reset 之前**读(ty_budget_reset 清 g_ty_uncovered/g_ty_exhausted) + unc := ty_uncovered(); + exh := ty_exhausted(); + ty_budget_reset(200000); // 判定后即复位(不污染后续查询) + if e == 1 { return true; } + if e == 0 { return false; } + // e == -1(未知):P-A —— 不静默、不回落:硬错 + 反例 + ty_indeterminate_report(1, t1, t2, a, b, unc, exh); return false; } +// R2 P1 影子对拍包装 / R2 P2a Task 3 判定入口 → R2 P5 Task 4 纯转发(对照物删除)。 +// **R2 P5 Task 5**:影子通道整体下线(`sh_compare` 及其调用点、ring/摘要/站点直方图/CLI 通道、 +// `g_shadow_*` 全部删除)。保留本名与转发形态 = 判定入口单点(调用面零改动;站点/计数面无残留)。 +fn type_equal(t1: int, t2: int) -> bool { + return type_equal_engine(t1, t2); +} + fn scan_for_yield(node: int) -> int { if node < 0 { return 0; } k := ast_kind(node); @@ -182,6 +625,153 @@ fn scan_for_yield(node: int) -> int { return 0; } +// ─── 落空(fall through)分析 —— TF01 收口 ─── +// 背景:函数体返回检查(站点 5)以「块类型 = 末语句类型」判返回;以**无 break 的 loop** +// 收尾的体运行时永不走到函数尾(只能从体内 return 出),其「unit」不是缺返回值的证据 +// ⇒ 历史误报一例 `ty_memo_slot_no_grow`(ty_shadow.cr 记载面)。判定**保守**:只认确定 +// 不能落空的形态,其余一律回 0(= 可落空 = 照旧判 TF01)——真落空体绝不能溜过 +// (那是静默接受洞,比误报危险得多)。 + +// 循环体内是否存在**直属于本循环**的 break(嵌套 loop/while/for 内的 break 归内层 ⇒ +// 在循环节点处截断、不下钻)。返回 1 亦覆盖「形态未知」(默认兜底)——未知按「可能有 +// break」处理 = fail-closed:宁可照旧报 TF01,也不误判「无 break ⇒ 永不落空」。 +fn loop_body_has_break(node: int) -> int { + if node < 0 { return 0; } + k := ast_kind(node); + if k == EXPR_BREAK { return 1; } + if k == EXPR_LOOP || k == EXPR_WHILE || k == EXPR_FOR { return 0; } + if k == EXPR_BLOCK { + ss := ast_a(node); sc := ast_b(node); + i : ., mut = 0; + loop { if i >= sc { break; } + if loop_body_has_break(r64(g_block_stmts, (ss + i) * 8)) != 0 { return 1; } + i = i + 1; } + return 0; + } + if k == EXPR_IF { + if loop_body_has_break(ast_a(node)) != 0 { return 1; } + if loop_body_has_break(ast_b(node)) != 0 { return 1; } + return loop_body_has_break(ast_c(node)); + } + if k == EXPR_MATCH { + // 臂 = EXPR_ARM 链(parser 以 arm.c 串联,尾 -1)——**非连续槽**,勿按段扫 + if loop_body_has_break(ast_a(node)) != 0 { return 1; } + an : ., mut = ast_b(node); + loop { if an < 0 { break; } + if loop_body_has_break(ast_b(an)) != 0 { return 1; } + an = ast_c(an); } + return 0; + } + if k == EXPR_ARM || k == EXPR_BINARY || k == EXPR_ASSIGN || k == EXPR_INDEX || + k == EXPR_RANGE || k == EXPR_AS || k == EXPR_ARG || k == EXPR_CALL { + if loop_body_has_break(ast_a(node)) != 0 { return 1; } + return loop_body_has_break(ast_b(node)); + } + if k == EXPR_ENUM_CONSTRUCTOR || k == EXPR_AT { + // a = 名 ni(**不是节点**):只下钻实参链(b) + return loop_body_has_break(ast_b(node)); + } + if k == EXPR_GO { + // a 恒 -1、体在 b(range 形态另有 c/data);a/b 双下钻 = 保守(go 体内 break 不漏判) + if loop_body_has_break(ast_a(node)) != 0 { return 1; } + return loop_body_has_break(ast_b(node)); + } + if k == EXPR_LET { + // a = 名 ni(**不是节点**):只下钻类型(b)与值(c) + if loop_body_has_break(ast_b(node)) != 0 { return 1; } + return loop_body_has_break(ast_c(node)); + } + if k == EXPR_STRUCT || k == EXPR_STRUCTPAT || k == EXPR_ENUMPAT || k == EXPR_GENERIC_APPLY { + // 连续子节点段:首 b、个数 c(wrapper.a = 值/子模式,经 EXPR_NONE 分支承接) + return seg_has_break(ast_b(node), ast_c(node)); + } + if k == EXPR_ARRAY || k == EXPR_TUPLE { + return seg_has_break(ast_a(node), ast_b(node)); + } + if k == EXPR_NONE { + // wrapper(a = 值节点)或基类型节点(a = 0):照 monomorph 的判据 + if ast_a(node) >= 0 && ast_a(node) != node { return loop_body_has_break(ast_a(node)); } + return 0; + } + if k == EXPR_STMT || k == EXPR_UNSAFE || k == EXPR_RETURN || k == EXPR_YIELD || + k == EXPR_AWAIT || k == EXPR_MOVE || k == EXPR_TRY || k == EXPR_UNARY || + k == EXPR_FIELD || k == EXPR_OPTIONAL || k == EXPR_REFTYPE || k == EXPR_PTRTYPE { + return loop_body_has_break(ast_a(node)); + } + if k == EXPR_INT || k == EXPR_DEX || k == EXPR_BOOL || k == EXPR_STRING || + k == EXPR_IDENT || k == EXPR_CHAR || k == EXPR_WILDCARD || k == EXPR_CONTINUE { + return 0; + } + // 兜底(含 EXPR_FN/PARAM/FLOW/EXTERN 等未枚举形态):按「可能有 break」保守处理 + return 1; +} + +fn seg_has_break(first: int, count: int) -> int { + if first < 0 { return 0; } + i : ., mut = 0; + loop { if i >= count { break; } + n := first + i; + if n >= 0 && n < g_ast_count { + if loop_body_has_break(n) != 0 { return 1; } + } + i = i + 1; } + return 0; +} + +// 语句是否**不能落空**(走到本语句之后的代码):1 = 确定不能,0 = 可能落空(保守默认)。 +// 注意 `return` 在此**不判 divergence**:它虽然「不能走完」,但**带返回值**——本分析只服务 +// 「块末语句类型是否代表函数返回值」这一问,而 return 的值类型恰是该问的被检对象(豁免它 +// = 洗白真错面,lits_copy 型)。故只认**不产出值**的不可落空形态:无直系 break 的 loop、 +// 双分支皆不可落空且无 else 的 if、包裹层(STMT/UNSAFE/嵌套 block)。 +// **登记面**:非落空体内的 return 值类型本分析**不核对**(checker 现模型无逐 return 核对; +// 该面 = 既有面——本条只把「以无 break 的 loop 收尾」从 TF01 误报中解放,不新增核对)。 +fn stmt_cannot_fall_through(node: int) -> int { + if node < 0 { return 0; } + k := ast_kind(node); + if k == EXPR_BLOCK { + ss := ast_a(node); sc := ast_b(node); + i : ., mut = 0; + loop { if i >= sc { break; } + if stmt_cannot_fall_through(r64(g_block_stmts, (ss + i) * 8)) != 0 { return 1; } + i = i + 1; } + return 0; + } + if k == EXPR_LOOP { + // 无直系 break ⇒ 只可能从体内 return 出(loop_body_has_break 的未知 = 1 保证 + // 此处判 1 只在**确定**无 break 时成立) + if loop_body_has_break(ast_a(node)) == 0 { return 1; } + return 0; + } + if k == EXPR_IF { + if ast_c(node) < 0 { return 0; } // 无 else:条件不成立即落空 + if stmt_cannot_fall_through(ast_b(node)) == 0 { return 0; } + return stmt_cannot_fall_through(ast_c(node)); + } + if k == EXPR_STMT || k == EXPR_UNSAFE { return stmt_cannot_fall_through(ast_a(node)); } + return 0; +} + +// 语句是否**确定发散**(本层不产出值、不落到后继)——**含 `return`**。 +// **仅服务 TC02 的「else 支类型是幻影」判定**(消费点 = infer_expr 的 EXPR_IF 合并点守卫); +// **不得**用于 TF01——那里豁免 return 体会洗白真错面(lits_copy 型),见上一条的头注。 +// 保守默认 0(不确定即 0):只认 return 族 + 包裹层(STMT/UNSAFE)+ 块内任一句确定发散。 +// 未覆盖面(登记,本批不判):`break`/`continue` 收尾的分支(需循环上下文)。 +fn stmt_diverges(node: int) -> int { + if node < 0 { return 0; } + k := ast_kind(node); + if k == EXPR_RETURN { return 1; } + if k == EXPR_STMT || k == EXPR_UNSAFE { return stmt_diverges(ast_a(node)); } + if k == EXPR_BLOCK { + ss := ast_a(node); sc := ast_b(node); + i : ., mut = 0; + loop { if i >= sc { break; } + if stmt_diverges(r64(g_block_stmts, (ss + i) * 8)) != 0 { return 1; } + i = i + 1; } + return 0; + } + return 0; +} + // --- Symbol table --- struct SymEntry { name_idx: int, @@ -368,16 +958,13 @@ fn res_type_node(node: int) -> int { if node < 0 { return TI_UNIT; } if ast_kind(node) == 0 { // Base type node: type_val = TY_* + // P2b Task 6:8 行内联链 → 单表 `ty_code_to_ti`(尾部回落显式写出,不藏表里); + // 本站点域 = {INT,DEX,BOOL,STRING,UNIT,NEVER,CHAR} ∪ {7 = dyn 码}——**与 res_call_type 的 + // 唯一语义差 = NEVER 格**(该函数无此格,见其注)。域外(如 TY_DEX_S=8/未知码)落 TI_UNIT。 tv := ast_type_val(node); - if tv == TY_INT { return TI_INT; } - if tv == TY_DEX { return TI_DEX; } - if tv == TY_BOOL { return TI_BOOL; } - if tv == TY_STRING { return TI_STR; } - if tv == TY_UNIT { return TI_UNIT; } - if tv == TY_NEVER { return TI_NEVER; } - if tv == TY_CHAR { return TI_CHAR; } - if tv == TI_DYN { return TI_DYN; } - return TI_UNIT; + mapped : ., mut = ty_code_to_ti(tv); + if mapped < 0 { mapped = TI_UNIT; } + return mapped; } if ast_kind(node) == EXPR_IDENT { // Named type: int_val = name string index @@ -387,9 +974,14 @@ fn res_type_node(node: int) -> int { return sym_type(si); } // Create named type entry - return alloc_type(TYP_NAMED, name_idx, 0); + return alloc_named_type(name_idx); } if ast_kind(node) == EXPR_ARRAY { + // 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 的类型构造器身份已退役—— + // 语义归处 = product(N 元聚合)/ 序列接口 + 长度 where(N 长序列)/ F11 图(长度事实, + // 可表达依赖长度)。本语法保留为「内联容量存储」表示提示(映射参数层,与 hw-map 同层; + // 随实例选择生效或退化,非经典范式映射可忽略)。见 + // docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 // Array type [T; N] or slice type [T] (size 0) elem := res_type_node(ast_a(node)); sz := ast_int_val(node); @@ -409,6 +1001,16 @@ fn res_type_node(node: int) -> int { inner := res_type_node(ast_a(node)); return alloc_type(TYP_PTR, inner, 0); } + if ast_kind(node) == EXPR_OPTIONAL { + // R2 P3 Task 4:`T?` = `T ∪ null`(spec §5.4)——行 = TYP_OPTIONAL(data = 内层行)。 + // 语义在三层落:① 桥接把本行译作 union(内层项, null 原子项)(ty_shadow.cr); + // ② 引擎按普通联合项判定(包含/等价/不相交/穷尽性,全走既有 DNF 路径,零新规则); + // ③ 判定点(type_compat_strict 等)不变——`int ⊆ int ∪ null`、`int ∪ null ⊄ int`。 + // 一条**不**去侧表的理由:本行无名字(旧形态 Option[T] 的「名字身份」正是本任务退役的 + // 东西),故不进 named_dedup;同内容的两个行在引擎侧译成同一个项(DAG 去重)⇒ 判定等价。 + inner := res_type_node(ast_a(node)); + return alloc_type(TYP_OPTIONAL, inner, 0); + } if ast_kind(node) == EXPR_GENERIC_APPLY { // Generic application: Box[int] name_idx := ast_a(node); @@ -420,19 +1022,35 @@ fn res_type_node(node: int) -> int { return TI_UNIT; } base_ti := sym_type(si); + // 两趟(照 #25 元组第二根因同款):实参类型解析**自身也会向 + // g_gen_apply_data 追加**(嵌套应用如 Box[Box[int]] 的内层载荷)—— + // 先解析进暂存,后一次性认领连续块。修复前「先认领块再逐参解析」会让 + // 内层 append 冲掉外层的 count 槽与后续载荷槽(终点 count 截断), + // `Box[Box[int]]` 的字段/返回位因此被误判(假拒)。 + args : string, mut; + if arg_count > 0 { + args = alloc(arg_count * 8); + ai : ., mut = 0; + an : ., mut = first_arg_node; + loop { + if ai >= arg_count { break; } + w64(args, ai * 8, res_type_node(an)); + ai = ai + 1; + an = an + 1; + } + } + // R2 P3 Task 5(Step 3):**实例化点**的结构/枚举泛型约束检查(`Box[T: I]` 的 `Box[P]`)。 + // 判定 = ty_sub(本质轴)+ 反例诊断;用户接口约束 = -1 零动作(P3b 阻塞面)。 + gen_inst_constr_check(node, find_struct(name_idx), find_enum(name_idx), args, arg_count); // Store args in g_gen_apply_data: [count, arg1, arg2, ...] data_start := g_gen_apply_data_count; grow_gen_apply_data(data_start + 1 + arg_count); w64(g_gen_apply_data, data_start * 8, arg_count); - g_gen_apply_data_count = data_start + 1; - ai : ., mut = 0; - an : ., mut = first_arg_node; + ai2 : ., mut = 0; loop { - if ai >= arg_count { break; } - arg_ti := res_type_node(an); - w64(g_gen_apply_data, (data_start + 1 + ai) * 8, arg_ti); - ai = ai + 1; - an = an + 1; + if ai2 >= arg_count { break; } + w64(g_gen_apply_data, (data_start + 1 + ai2) * 8, r64(args, ai2 * 8)); + ai2 = ai2 + 1; } g_gen_apply_data_count = data_start + 1 + arg_count; return alloc_type(TYP_GENERIC_APPLY, base_ti, data_start); @@ -470,6 +1088,35 @@ fn find_iface(name_ni: int) -> int { return -1; } +// ─── R2 P3 Task 0:展开层的「行 → 声明」入口(名字解析归 checker;展开层只读声明表)─── +// ti 接受两种形态(与桥接 sh_term_of_ti 的 TYP_NAMED / TYP_GENERIC_APPLY 两分支同域): +// TYP_NAMED(data = 名字 ni)/ TYP_GENERIC_APPLY(data = 基型行;基型须为 TYP_NAMED)。 +// -1 = 既非命名行也非泛型应用行 / 基型非命名行。**只读**(不改类型表、不报诊断、不分配)。 +fn decl_name_of_ti(ti: int) -> int { + if ti < 0 { return -1; } + k := get_type_kind(ti); + if k == TYP_NAMED { return get_type_data(ti); } + if k == TYP_GENERIC_APPLY { + base := get_type_data(ti); + if get_type_kind(base) == TYP_NAMED { return get_type_data(base); } + } + return -1; +} + +// 命名/泛型应用行 → struct 声明行(-1 = 非此二形态 / 该名字未声明为 struct) +fn find_struct_row_of(ti: int) -> int { + ni := decl_name_of_ti(ti); + if ni < 0 { return -1; } + return find_struct(ni); +} + +// 同上 → enum 声明行 +fn find_enum_row_of(ti: int) -> int { + ni := decl_name_of_ti(ti); + if ni < 0 { return -1; } + return find_enum(ni); +} + fn get_type_name(ti: int) -> int { k := get_type_kind(ti); if k == TYP_NAMED { return get_type_data(ti); } @@ -489,37 +1136,223 @@ fn get_type_name(ti: int) -> int { return -1; } +// R2 P3b Task 6(Step 3,mangling 退役):方法解析从「`Type.method` 字符串拼接 + `find_func`」 +// 改为**表查询**(`g_methods` 三元组 {type_ni, method_ni, func_name_ni},parser 的 impl 分支 +// 唯一写点;引擎侧同一入口 `iface_find_method`)。域等价(同一 impl 声明面),且**零 str_intern** +// ——旧形态对缺失方法名会往驻留表追加新串(`.ccr` STR 段增长面,见 A.3-②)。 fn type_has_method(type_ni: int, method_ni: int) -> bool { - tname := istr_get(type_ni); - mname := istr_get(method_ni); - mangled := tname + "." + mname; - mangled_ni := str_intern(mangled); - return find_func(mangled_ni) >= 0; + if iface_find_method(type_ni, method_ni) < 0 { return false; } + return true; } +// 接口满足判定(bool 面;语义 = 引擎轴 C 用户谓词的**同一实现** `iface_user_satisfies_ii`, +// 见 type_engine.cr:方法名表查询 + 接收者模式 + 签名项引擎判定)。保持 bool 返回值的理由 = +// 调用点(泛型约束的函数调用位点)的既有语义「不能判定为满足 ⇒ 报 TG02」逐字不变(措辞/去重/ +// rc 不在本任务面内);三态面由 `iface_satisfies` / `iface_user_satisfies` 承担。 fn check_iface(type_ni: int, iface_ii: int) -> bool { - method_count := r64(g_ifaces, iface_ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); - mi : ., mut = 0; + if iface_user_satisfies_ii(type_ni, iface_ii) == 1 { return true; } + return false; +} + +// ═══════════════ R2 P3 Task 5:泛型约束(保留 / 实例化判定 / 反例)═══════════════ +// 语义(spec §5.2):`fn f[T]`(约束 = ⊤)/ `fn f[T: I]`(约束 = 类型项 I);**结构/枚举泛型 +// 补上约束**(旧态:parser 写进 dummy 缓冲后丢弃,见 parser.cr 的 save_*_gen_constrs); +// 实例化检查 = `ty_sub(实参, 约束)`;失败给**非空反例**(`tt_witness`)。 +// **本批 = P3a 半边**(P3 计划 Task 5 与「与 P2b 的交接面」表第 4 行): +// · 本质轴(约束项 = 原生/已声明类型名):`gen_constr_satisfied` 走引擎判定 —— 本批落地; +// · 用户轴(`I` 是 `interface` 名):满足判定 = `iface_satisfies`(**P3b Task 0 已交付**, +// type_engine.cr 的轴 C)。两站点口径分家(**有意**):函数调用点 `gen_constr_satisfied` +// 只取 1(0/-1 回落既有 `check_iface` 名拼接路径——措辞/去重/rc 逐字保持,切换归 Task 6 +// Step 3);结构/枚举实例化点 `gen_inst_constr_satisfied` 直取真值(0 ⇒ TG02)。 +// ⇒ 不得据「函数调用点措辞未变」推断判定面未落地。 +// 三态纪律(P0/P1/P2a 继承):-1 一律**不判**——不得当 0(拒绝)或 1(放行)。 +// 预算隔离照 `ti_subsumes`(引擎 memo 跨查询命中会让结果依赖预算历史)。 + +// 约束名 → 类型行(-1 = 不是类型名)。原生名按 parse_type 的字典逐名判(parser.cr 的 +// 8 个基类型名分支:int/dex/bool/string/char/never/unit/dyn——**原生名不经符号表**, +// init_types 只建类型行不 def_sym);其余查符号表 SYM_TYPE(struct/enum/别名/泛型形参)。 +fn gen_constr_type_ti(c_ni: int) -> int { + if c_ni < 0 { return -1; } + s := istr_get(c_ni); + if str_eq(s, "int") != 0 { return TI_INT; } + if str_eq(s, "dex") != 0 { return TI_DEX; } + if str_eq(s, "bool") != 0 { return TI_BOOL; } + if str_eq(s, "string") != 0 { return TI_STR; } + if str_eq(s, "char") != 0 { return TI_CHAR; } + if str_eq(s, "unit") != 0 { return TI_UNIT; } + if str_eq(s, "never") != 0 { return TI_NEVER; } + if str_eq(s, "dyn") != 0 { return TI_DYN; } + si := find_gsym(c_ni); + if si >= 0 && sym_kind(si) == SYM_TYPE { return sym_type(si); } + return -1; +} + +// 约束满足三态:1 = 满足 / 0 = 违反 / -1 = **不判**(名字非类型/非接口 / 桥接失败)。三态 +// 直传(**不**把 -1 折成 0/1)。 +// R2 P3b Task 0:本函数 = `iface_satisfies` 统一入口的**函数调用点消费者**(P2b 交接契约①)。 +fn gen_constr_satisfied(c_ni: int, arg_ti: int) -> int { + if c_ni < 0 || arg_ti < 0 { return -1; } + if find_iface(c_ni) >= 0 { + // 用户轴(接口名):`iface_satisfies` 已交付(P3b Task 0)——**1 提前返回**(与回落 + // 路径结论一致,省一次名拼接/查表);**0 与 -1 一律落下方既有 `check_iface` 名拼接 + // 路径**(本层口径 = P3a「函数调用点逐字不动」:措辞/去重/rc 全保持)。0 → 诊断的 + // 切换(= 走 gen_constr_raise 的新措辞)归 Task 6 Step 3——两处谓词同源( + // iface_user_satisfies 与 check_iface 判同一面),故此处**不存在**判定分歧。 + s := iface_satisfies(arg_ti, c_ni); + if s == 1 { return 1; } + return -1; + } + cti := gen_constr_type_ti(c_ni); + if cti < 0 { return -1; } // 非类型名(未定义名等):不判、不发明诊断 + a := sh_term_of_ti(arg_ti); + b := sh_term_of_ti(cti); + if a < 0 || b < 0 { return -1; } // 桥接缺口 ⇒ 不判 + ty_budget_reset(200000); + s := ty_sub(a, b); + ty_budget_reset(200000); + return s; // 1/0/-1 直传 +} + +// 实例化点(结构/枚举 `Box[T: I]` 的 `Box[P]`)专用满足判定:**用户轴直取 iface_satisfies +// 的真值**(不回落)——回落面是函数调用点的消息/去重口径(见 gen_constr_satisfied 注), +// 与实例化点无关。purpose:本批(P3b Task 0)起 `T: I` 在实例化点**真判定**(此前 = 恒 -1 +// 不判、零诊断——P3a 登记面)。三态直传:-1 = 不判 ⇒ 调用方零动作。 +fn gen_inst_constr_satisfied(c_ni: int, arg_ti: int) -> int { + if c_ni < 0 || arg_ti < 0 { return -1; } + if find_iface(c_ni) >= 0 { return iface_satisfies(arg_ti, c_ni); } + return gen_constr_satisfied(c_ni, arg_ti); +} + +// 反例文本(诊断用):`实参 \ 约束` 的具体值。三态 -1 / 反例不可得 → ""(**不谎报反例**)。 +fn gen_constr_witness_str(arg_ti: int, c_ni: int) -> string { + cti := gen_constr_type_ti(c_ni); + if cti < 0 { return ""; } + a := sh_term_of_ti(arg_ti); + b := sh_term_of_ti(cti); + if a < 0 || b < 0 { return ""; } + ty_budget_reset(200000); + w := tt_witness(a, b); + ty_budget_reset(200000); + if w < 0 { return ""; } + return tt_display(w); +} + +// 诊断发射(措辞分派:既有接口路径的措辞/码不动;本路径 = 约束 + 反例)。码沿用 +// EC_TG_BOUND(TG02,软诊断:check rc=1 / build rc=0 —— 与既有约束检查同门,**不新增硬门**)。 +fn gen_constr_raise(arg_ti: int, c_ni: int, line: int, col: int) { + // 措辞分派:接口名约束 ⇒ **与既有 check_iface 路径同措辞**("does not satisfy interface + // 'X'"——两站点一致性;接口非类型行 ⇒ 反例项不可得 = 不附反例,不谎报);类型名约束 ⇒ + // 既有措辞 + 反例(措辞与反例面 P3a 已交付,逐字未动)。 + if find_iface(c_ni) >= 0 { + msg_i := "Type '" + type_display(arg_ti) + "' does not satisfy interface '" + istr_get(c_ni) + "'"; + check_error(EC_TG_BOUND, msg_i, line, col); + return; + } + msg := "Type '" + type_display(arg_ti) + "' does not satisfy constraint '" + istr_get(c_ni) + "'"; + w := gen_constr_witness_str(arg_ti, c_ni); + if str_len(w) > 0 { msg = msg + " (counterexample: " + w + ")"; } + check_error(EC_TG_BOUND, msg, line, col); +} + +// ─── 诊断去重侧表(开放寻址,16B/条 {key, 1};key = node * MAX_GENERICS + gi)─── +// 为何需要:实例化点的判定函数(res_type_node / res_call_type)按**语法出现点**调用,同一 +// AST 节点可被多次解析(字段访问、多次引用同一注解…),且 `check_error` 无去重 ⇒ 不去重即 +// 同一条约束违反被打印多次。键 = (节点, 形参下标)——同节点不同形参各报一条(信息不丢)。 +// 生命周期同 named_dedup(init_types 置 cap=0 → 惰性重建);键含 AST 节点下标 ⇒ 随编译期作废。 +fn gen_constr_seen_init() { + if g_constr_seen_cap > 0 { return; } + nc := 1024; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_constr_seen = nb; + g_constr_seen_cap = nc; + g_constr_seen_count = 0; +} + +fn gen_constr_seen_reset() { + g_constr_seen_cap = 0; + g_constr_seen_count = 0; +} + +fn gen_constr_seen_probe(k: int) -> int { + cap := g_constr_seen_cap; + p : ., mut = tt_mod(k, cap); + ret : ., mut = -1; loop { - if mi >= method_count { return true; } - mbase2 := iface_ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; - method_ni := r64(g_ifaces, mbase2 + OFF_IFM_NAME); - if !type_has_method(type_ni, method_ni) { return false; } - // Also verify param count and return type match - tname2 := istr_get(type_ni); - mname2 := istr_get(method_ni); - mangled2 := tname2 + "." + mname2; - mangled_ni2 := str_intern(mangled2); - fi2 := find_func(mangled_ni2); - if fi2 >= 0 { - iface_pc := r64(g_ifaces, mbase2 + OFF_IFM_PARAM_COUNT); - if fi_param_count(fi2) != iface_pc { return false; } - iface_rt := r64(g_ifaces, mbase2 + OFF_IFM_RET_TI); - if fi_return_type(fi2) != iface_rt { return false; } + kk := r64(g_constr_seen, p * 16); + if kk < 0 { ret = p; break; } + if kk == k { ret = p; break; } + p = p + 1; if p >= cap { p = 0; } + } + return ret; +} + +fn gen_constr_seen_rehash() { + old := g_constr_seen; + old_cap := g_constr_seen_cap; + nc := old_cap * 2; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_constr_seen = nb; + g_constr_seen_cap = nc; + g_constr_seen_count = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + kk := r64(old, j * 16); + if kk >= 0 { + s := gen_constr_seen_probe(kk); + w64(g_constr_seen, s * 16, kk); + w64(g_constr_seen, s * 16 + 8, 1); + g_constr_seen_count = g_constr_seen_count + 1; } - mi = mi + 1; + j = j + 1; + } +} + +// 1 = 首次(调用方报诊断),0 = 已报过(丢弃)。装填因子守卫在探测**前**(表满 + 键不存在 +// ⇒ 开放寻址永不退出,照 named_dedup / sh_map 同款同因)。 +fn gen_constr_seen_add(k: int) -> int { + gen_constr_seen_init(); + if (g_constr_seen_count + 1) * 2 >= g_constr_seen_cap { gen_constr_seen_rehash(); } + s := gen_constr_seen_probe(k); + if r64(g_constr_seen, s * 16) == k { return 0; } + w64(g_constr_seen, s * 16, k); + w64(g_constr_seen, s * 16 + 8, 1); + g_constr_seen_count = g_constr_seen_count + 1; + return 1; +} + +// ─── 实例化点检查(结构/枚举泛型:`Box[T: I]` 的 `Box[P]`)─── +// 消费点 = res_type_node / res_call_type 的 EXPR_GENERIC_APPLY 分支(实参已解析成 ti 处)。 +// sa/ea = struct/enum 声明行(-1 = 该名字不是 struct/enum —— 接口泛型/未知名,零动作)。 +// **实参缺位不判**(不越界读);**arg_ti < 0 不判**(未解析);用户接口约束经 +// gen_constr_satisfied 回 -1 ⇒ 零动作(P3b 阻塞面,见该函数注)。 +fn gen_inst_constr_check(app_node: int, sa: int, ea: int, args: string, arg_count: int) { + gc : ., mut = 0; + if sa >= 0 { gc = si_generic_count(sa); } + else if ea >= 0 { gc = ei_generic_count(ea); } + else { return; } + i : ., mut = 0; + loop { + if i >= gc { break; } + if i >= arg_count { break; } + c_ni : ., mut = -1; + if sa >= 0 { c_ni = si_gen_constr(sa, i); } else { c_ni = ei_gen_constr(ea, i); } + if c_ni >= 0 { + arg_ti := r64(args, i * 8); + if arg_ti >= 0 { + if gen_inst_constr_satisfied(c_ni, arg_ti) == 0 { + if gen_constr_seen_add(app_node * MAX_GENERICS + i) != 0 { + gen_constr_raise(arg_ti, c_ni, ast_line(app_node), ast_col(app_node)); + } + } + } + } + i = i + 1; } - return true; } // --- First pass: collect all declarations --- @@ -530,7 +1363,7 @@ fn collect_decls() { loop { if i >= g_struct_count { break; } name_idx := si_name(i); - type_idx := alloc_type(TYP_NAMED, name_idx, 0); + type_idx := alloc_named_type(name_idx); def_sym(name_idx, SYM_TYPE, type_idx, -1); i = i + 1; } @@ -574,31 +1407,28 @@ fn collect_decls() { loop { if i >= g_iface_count { break; } name_idx := r64(g_ifaces, i * ESZ_IFACEINFO + OFF_IF_NAME); - type_idx := alloc_type(TYP_NAMED, name_idx, 0); + type_idx := alloc_named_type(name_idx); def_sym(name_idx, SYM_TYPE, type_idx, -1); i = i + 1; } - // Register built-in Option type (for T? desugaring) - option_found : ., mut = 0; - i = 0; - loop { - if i >= g_enum_count { break; } - if ei_name(i) == str_intern("Option") { option_found = 1; } - i = i + 1; - } - if option_found == 0 { - // Auto-register Option as a generic built-in type - option_name_idx := str_intern("Option"); - option_ti := alloc_type(TYP_NAMED, option_name_idx, 0); - def_sym(option_name_idx, SYM_TYPE, option_ti, -1); - } + // ─── R2 P3 Task 4:**退役**内建 Option 注册(spec §5.4)─── + // 旧行为:无用户 `enum Option` 时自动注册一个名为 "Option" 的**命名类型**行并 def_sym + // (`T?` 经 parser 降级为 `Option[T]` 泛型应用,靠这个名字解析)。 + // 退役依据:① `T?` 的目标形态 = EXPR_OPTIONAL → TYP_OPTIONAL(`T ∪ null` 可直接表达); + // ② 名字路径把「可选」绑死在一个用户可重名的标识符上(用户声明 `enum Option` 时 `T?` + // 的含义静默改变——Task 3 §6-⑥ 登记的交互面);③ `.ccr` STR 段与行号序随该注册漂移。 + // 退役后 `Option` **不再是**语言内建名(无行、无符号):用户自声明 `enum Option` 则按 + // 普通枚举解析;一行不注册 = `test_optional.py` 的行数对照用例(selftest `t4.option_not_registered`)。 + // ⚠ 连带面(须登记):`Some`/`None` 不再靠该行解析——内建构造器路径见 EXPR_ENUM_CONSTRUCTOR + // (按关键字名给 TYP_OPTIONAL/TYP_NULL 行);`EXPR_TRY` 的 `base_name == "Option"/"Result"` + // 名字串判断同期退役(改按类型项结构解包)。 // Register all enum types and their variant constructors i = 0; loop { if i >= g_enum_count { break; } name_idx := ei_name(i); - type_idx := alloc_type(TYP_NAMED, name_idx, 0); + type_idx := alloc_named_type(name_idx); def_sym(name_idx, SYM_TYPE, type_idx, -1); // Register each variant as a function returning the enum type vi : ., mut = 0; @@ -673,11 +1503,16 @@ fn collect_decls() { type_node := ast_type_val(fn_node); if type_node > 0 && ast_kind(type_node) != 0 { rt_ti = res_type_node(type_node); - } else if rt == TY_INT { rt_ti = TI_INT; } - else if rt == TY_DEX { rt_ti = TI_DEX; } - else if rt == TY_BOOL { rt_ti = TI_BOOL; } - else if rt == TY_STRING { rt_ti = TI_STR; } - else if rt == TY_UNIT { rt_ti = TI_UNIT; } + } else { + // P2b Task 6:值经单表 `ty_code_to_ti`;**本站点域 = 现状链 {INT,DEX,BOOL,STRING,UNIT}** + // (无 CHAR/NEVER/7 ⇒ 三者与域外码一律保留 `rt_ti` 初值 TI_UNIT——域差异**逐站保留**, + // 不得按「表 + 尾回落」一刀切:站点域差异是**载荷**的,探针 F1/F2/F4 实证, + // `fn f() -> char` 的非泛型符号类型现状 = unit、extern 站点同形 = char)。 + rt_mapped := ty_code_to_ti(rt); + if rt_mapped >= 0 && rt_mapped != TI_CHAR && rt_mapped != TI_NEVER && rt_mapped != TI_DYN { + rt_ti = rt_mapped; + } + } } if hotpatch_ver > 0 { // @hotpatch function: register with mangled name fn_name.vN @@ -697,14 +1532,18 @@ fn collect_decls() { type_node2 := ast_type_val(first_fn); if type_node2 > 0 && ast_kind(type_node2) != 0 { first_rt_ti = res_type_node(type_node2); - } else if first_rt == TY_INT { first_rt_ti = TI_INT; } - else if first_rt == TY_DEX { first_rt_ti = TI_DEX; } - else if first_rt == TY_BOOL { first_rt_ti = TI_BOOL; } - else if first_rt == TY_STRING { first_rt_ti = TI_STR; } - else if first_rt == TY_UNIT { first_rt_ti = TI_UNIT; } - - if !type_equal(rt_ti, first_rt_ti) { - check_error(EC_TF_RETURN, "Hotpatch return type mismatch for '" + fn_name_str + "'", ast_line(fn_node), ast_col(fn_node)); + } else { + // P2b Task 6:同上一处(本站点 = **hotpatch 首版**返回,域与上一处逐字相同) + first_mapped := ty_code_to_ti(first_rt); + if first_mapped >= 0 && first_mapped != TI_CHAR && first_mapped != TI_NEVER && first_mapped != TI_DYN { + first_rt_ti = first_mapped; + } + } + + // P3 Task 1:本站两侧**皆声明**(无源/目标之分)⇒ 长度面走对称核(= 现状语义) + compat := type_compat_sym(rt_ti, first_rt_ti); + if compat != 1 { + diag_type_incompatible(compat, EC_TF_RETURN, "Hotpatch return type mismatch for '" + fn_name_str + "'", ast_line(fn_node), ast_col(fn_node)); } first_pc := fi_param_count(fj); cur_pc := fi_param_count(i); @@ -748,13 +1587,14 @@ fn collect_decls() { ret_type := ast_type_val(ei); // Resolve return type to type index + // P2b Task 6:值经单表 `ty_code_to_ti`;**本站点域 = {INT,DEX,BOOL,STRING,UNIT,CHAR}** + // (含 CHAR、缺 NEVER/7——与上方 hotpatch 注册站点的域**不同且载荷**:探针 F4 实测 + // `extern fn f() -> char` 的符号类型 = char,而 F1 同形非 extern = unit) rt_ti : ., mut = TI_UNIT; - if ret_type == TY_INT { rt_ti = TI_INT; } - else if ret_type == TY_DEX { rt_ti = TI_DEX; } - else if ret_type == TY_BOOL { rt_ti = TI_BOOL; } - else if ret_type == TY_STRING { rt_ti = TI_STR; } - else if ret_type == TY_UNIT { rt_ti = TI_UNIT; } - else if ret_type == TY_CHAR { rt_ti = TI_CHAR; } + ext_mapped := ty_code_to_ti(ret_type); + if ext_mapped >= 0 && ext_mapped != TI_NEVER && ext_mapped != TI_DYN { + rt_ti = ext_mapped; + } // Register in symbol table (skip if duplicate) if find_gsym(name_ni) < 0 { @@ -852,6 +1692,130 @@ fn find_func(name_idx: int) -> int { return -1; } +// ─── TODO #29 ①②:struct 字面量的名字绑定 / 类型比对 辅助 ─── + +// 字段名 idx → 声明下标(-1 = 该名字不在声明中)。名字 idx 来自 parser 写入的 wrapper.b +// (EXPR_STRUCT 契约,见 ast.cr / parser.cr struct 字面量分支)。 +fn struct_field_index_by_name(si: int, name_ni: int) -> int { + if si < 0 || name_ni < 0 { return -1; } + j : ., mut = 0; + loop { + if j >= si_field_count(si) { return -1; } + if si_field_name(si, j) == name_ni { return j; } + j = j + 1; + } + return -1; +} + +// 类型里是否**仍含未实例化的泛型参数**(TYP_GENERIC_PARAM,可嵌套在数组 / 指针 / 引用 / +// 切片 / 元组 / 泛型应用内)。字面量的「字段类型 vs 声明」「元素同质性」判定在**任一侧** +// 含未实例化参数时**跳过**——参数未实例化时比较不成立,强行比较 = 假拒(实测泛型函数体 +// `fn f[T](x: T) { p := P{a: 1, b: x}; }`:x 的类型是 T,与声明的 int 比较必假)。参数实例化 +// 由 monomorph 负责,此判定不越权。 +fn ti_has_generic_param(ti: int) -> int { + k := get_type_kind(ti); + if k < 0 { return 0; } + if k == TYP_GENERIC_PARAM { return 1; } + if k == TYP_ARRAY || k == TYP_PTR || k == TYP_REF || k == TYP_SLICE { + return ti_has_generic_param(get_type_data(ti)); + } + if k == TYP_TUPLE { + cnt := get_type_data(ti); + st := get_type_extra(ti); + i : ., mut = 0; + loop { + if i >= cnt { break; } + if ti_has_generic_param(r64(g_gen_apply_data, (st + i) * 8)) != 0 { return 1; } + i = i + 1; + } + return 0; + } + if k == TYP_GENERIC_APPLY { + st := get_type_extra(ti); + cnt := r64(g_gen_apply_data, st * 8); + i : ., mut = 0; + loop { + if i >= cnt { break; } + if ti_has_generic_param(r64(g_gen_apply_data, (st + 1 + i) * 8)) != 0 { return 1; } + i = i + 1; + } + return 0; + } + return 0; +} + +// 字段**声明类型节点**是否提及本结构体的泛型参数(`T` / `[T; 3]` / `Box[T]` …)。提及 = +// 该字段的声明类型在字面量处**无法静态解析**:结构体泛型参数在字面量作用域不在名字表里, +// res_type_node 会把 `T` 误解析成名为 "T" 的具名类型 → 与实参比较必假(假拒)。故这些字段 +// 的类型比对跳过;参数本身走绑定路径(is_struct_generic 判定,见 EXPR_STRUCT 分支)。 +fn type_node_mentions_struct_param(si: int, tn: int) -> int { + if tn < 0 || si < 0 { return 0; } + k := ast_kind(tn); + if k == EXPR_IDENT { + if is_struct_generic(si, ast_int_val(tn)) { return 1; } + return 0; + } + if k == EXPR_ARRAY || k == EXPR_REFTYPE || k == EXPR_PTRTYPE || k == EXPR_OPTIONAL { + // EXPR_OPTIONAL(P3 Task 4):内层提及形参(`T?` 字段)⇒ 同上跳过字面量处的静态比对。 + return type_node_mentions_struct_param(si, ast_a(tn)); + } + if k == EXPR_GENERIC_APPLY { + if is_struct_generic(si, ast_a(tn)) { return 1; } + cnt := ast_c(tn); + an : ., mut = ast_b(tn); + i : ., mut = 0; + loop { + if i >= cnt { break; } + if an >= 0 { + if type_node_mentions_struct_param(si, an) != 0 { return 1; } + } + an = an + 1; + i = i + 1; + } + return 0; + } + return 0; +} + +// 类型展示名(诊断措辞用):具名/基型/泛型应用基名走 get_type_name,其余构造子递归拼装。 +// 未识别 → "?"(不伪造名字)。 +fn type_display(ti: int) -> string { + k := get_type_kind(ti); + // 泛型应用**先于** get_type_name 捷径处理(#29 评审 Minor #2):后者对 + // TYP_GENERIC_APPLY 只返回基名 ⇒ 实参全丢(「expected Box, got Box」—— + // 真正不匹配的那个类型实参恰是读者最需要的),且使本函数下方的实参分支 + // 成死码。此处展开成 `Box[int]` / `Pair[Box[int], string]` 形式。 + if k == TYP_GENERIC_APPLY { + base := get_type_data(ti); + s : string, mut; + s = type_display(base); + s = s + "["; + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + i : ., mut = 0; + loop { + if i >= cnt { break; } + if i > 0 { s = s + ", "; } + s = s + type_display(r64(g_gen_apply_data, (start + 1 + i) * 8)); + i = i + 1; + } + s = s + "]"; + return s; + } + ni := get_type_name(ti); + if ni >= 0 { return istr_get(ni); } + if k == TYP_ARRAY { return "[" + type_display(get_type_data(ti)) + "; " + int_str(get_type_extra(ti)) + "]"; } + if k == TYP_SLICE { return "[" + type_display(get_type_data(ti)) + "]"; } + if k == TYP_PTR { return "*" + type_display(get_type_data(ti)); } + if k == TYP_REF { return "&" + type_display(get_type_data(ti)); } + if k == TYP_GENERIC_PARAM { return istr_get(get_type_data(ti)); } + if k == TYP_DYN { return "dyn"; } + // R2 P3 Task 4:`T?` 显示为 `T?`、null 单点类型显示为 "null"(诊断措辞面;未识别仍 "?") + if k == TYP_OPTIONAL { return type_display(get_type_data(ti)) + "?"; } + if k == TYP_NULL { return "null"; } + return "?"; +} + fn is_struct_generic(si: int, name_idx: int) -> bool { if si < 0 || si >= g_struct_count { return false; } gi : ., mut = 0; @@ -877,15 +1841,14 @@ fn res_call_type(node: int, func_fi: int) -> int { // Resolve a type node for call inference, treating generic params as TYP_GENERIC_PARAM if node < 0 { return TI_UNIT; } if ast_kind(node) == 0 { + // P2b Task 6:7 行内联链 → 单表 `ty_code_to_ti`;**本站点与 res_type_node 的唯一语义差 + // = NEVER 格**(现状链无 TY_NEVER 分支 ⇒ 落尾 TI_UNIT)——探针实测**可达** + // (`fn g[T](a: T) -> never {...}` 的调用位点;插桩 build 实测 PROBE6_CALL_NEVER 命中 1 次, + // 见报告 §探针)⇒ 差异**显式保留**,不静默合一;「never 在调用位点被当 unit」登记为 P3 待裁决项。 tv := ast_type_val(node); - if tv == TY_INT { return TI_INT; } - if tv == TY_DEX { return TI_DEX; } - if tv == TY_BOOL { return TI_BOOL; } - if tv == TY_STRING { return TI_STR; } - if tv == TY_UNIT { return TI_UNIT; } - if tv == TY_CHAR { return TI_CHAR; } - if tv == TI_DYN { return TI_DYN; } - return TI_UNIT; + mapped : ., mut = ty_code_to_ti(tv); + if mapped < 0 || tv == TY_NEVER { mapped = TI_UNIT; } + return mapped; } if ast_kind(node) == EXPR_IDENT { name_idx := ast_int_val(node); @@ -895,7 +1858,7 @@ fn res_call_type(node: int, func_fi: int) -> int { // Regular named type si := find_gsym(name_idx); if si >= 0 && sym_kind(si) == SYM_TYPE { return sym_type(si); } - return alloc_type(TYP_NAMED, name_idx, 0); + return alloc_named_type(name_idx); } if ast_kind(node) == EXPR_GENERIC_APPLY { name_idx := ast_a(node); @@ -904,18 +1867,30 @@ fn res_call_type(node: int, func_fi: int) -> int { si := find_gsym(name_idx); if si < 0 || sym_kind(si) != SYM_TYPE { return TI_UNIT; } base_ti := sym_type(si); + // 两趟(同 res_type_node 的泛型应用分支:实参解析会追加嵌套应用载荷) + args : string, mut; + if ac > 0 { + args = alloc(ac * 8); + ai : ., mut = 0; + an : ., mut = first_an; + loop { + if ai >= ac { break; } + w64(args, ai * 8, res_call_type(an, func_fi)); + ai = ai + 1; + an = an + 1; + } + } + // R2 P3 Task 5(Step 3):调用位点的实例化检查(与 res_type_node 同源;本节的存在理由 + // = 调用位点的类型语法(形参/返回/字段)两侧都要覆盖——见 res_call_type 头注的域差)。 + gen_inst_constr_check(node, find_struct(name_idx), find_enum(name_idx), args, ac); ds := g_gen_apply_data_count; grow_gen_apply_data(ds + 1 + ac); w64(g_gen_apply_data, ds * 8, ac); - g_gen_apply_data_count = ds + 1; - ai : ., mut = 0; - an : ., mut = first_an; + ai2 : ., mut = 0; loop { - if ai >= ac { break; } - at := res_call_type(an, func_fi); - w64(g_gen_apply_data, (ds + 1 + ai) * 8, at); - ai = ai + 1; - an = an + 1; + if ai2 >= ac { break; } + w64(g_gen_apply_data, (ds + 1 + ai2) * 8, r64(args, ai2 * 8)); + ai2 = ai2 + 1; } g_gen_apply_data_count = ds + 1 + ac; return alloc_type(TYP_GENERIC_APPLY, base_ti, ds); @@ -925,6 +1900,12 @@ fn res_call_type(node: int, func_fi: int) -> int { mf := ast_int_val(node); return alloc_type(TYP_REF, inner, mf); } + if ast_kind(node) == EXPR_OPTIONAL { + // R2 P3 Task 4:`T?` 在调用位点的解析(与 res_type_node 同语义;本函数只多一条 + // 泛型形参分支的差异,见函数头注——optional 分支两侧一致)。 + inner := res_call_type(ast_a(node), func_fi); + return alloc_type(TYP_OPTIONAL, inner, 0); + } return TI_UNIT; } @@ -939,7 +1920,8 @@ fn unify_types(pattern: int, concrete: int) -> bool { loop { if mi >= g_gen_map_count { break; } if r64(g_gen_map_names, mi * 8) == name_idx { - return type_equal(r64(g_gen_map_types, mi * 8), concrete); + // P3 Task 1 参序归一:源 = concrete(实参),目标 = 已绑定项 + return type_compat_strict(concrete, r64(g_gen_map_types, mi * 8)) == 1; } mi = mi + 1; } @@ -951,7 +1933,8 @@ fn unify_types(pattern: int, concrete: int) -> bool { return false; } if pk == TYP_GENERIC_APPLY && ck == TYP_GENERIC_APPLY { - if !type_equal(get_type_data(pattern), get_type_data(concrete)) { return false; } + // P3 Task 1 参序归一:源 = concrete 基型,目标 = pattern 基型(两侧恒 TYP_NAMED ⇒ 方向面不可达) + if type_compat_strict(get_type_data(concrete), get_type_data(pattern)) != 1 { return false; } ps := get_type_extra(pattern); cs := get_type_extra(concrete); pc := r64(g_gen_apply_data, ps * 8); @@ -965,7 +1948,8 @@ fn unify_types(pattern: int, concrete: int) -> bool { } return true; } - return type_equal(pattern, concrete); + // P3 Task 1 参序归一:源 = concrete(实参),目标 = pattern(声明) + return type_compat_strict(concrete, pattern) == 1; } fn substitute_return_type(ti: int) -> int { @@ -986,16 +1970,25 @@ fn substitute_return_type(ti: int) -> int { base := get_type_data(ti); start := get_type_extra(ti); count := r64(g_gen_apply_data, start * 8); + // 两趟(同 res_type_node:递归代换会追加嵌套应用载荷 ⇒ 先算进暂存再认领块) + subs : string, mut; + if count > 0 { + subs = alloc(count * 8); + ai : ., mut = 0; + loop { + if ai >= count { break; } + w64(subs, ai * 8, substitute_return_type(r64(g_gen_apply_data, (start + 1 + ai) * 8))); + ai = ai + 1; + } + } new_start := g_gen_apply_data_count; grow_gen_apply_data(new_start + 1 + count); w64(g_gen_apply_data, new_start * 8, count); - g_gen_apply_data_count = new_start + 1; - ai : ., mut = 0; + ai2 : ., mut = 0; loop { - if ai >= count { break; } - sub := substitute_return_type(r64(g_gen_apply_data, (start + 1 + ai) * 8)); - w64(g_gen_apply_data, (new_start + 1 + ai) * 8, sub); - ai = ai + 1; + if ai2 >= count { break; } + w64(g_gen_apply_data, (new_start + 1 + ai2) * 8, r64(subs, ai2 * 8)); + ai2 = ai2 + 1; } g_gen_apply_data_count = new_start + 1 + count; return alloc_type(TYP_GENERIC_APPLY, base, new_start); @@ -1031,7 +2024,20 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in } pi = pi + 1; + // ─── R2 P3 Task 5(Step 1):F4 形参链导航修复(TODO #21)─── + // 旧态 `pn = pn + 1` 假设 EXPR_PARAM 在节点表里**连续**——不成立:每个形参的类型 + // 节点由 parse_type 在**该形参节点之前**分配(parser.cr 的 `pty := parse_type()` → + // `alloc_node(EXPR_PARAM, …, pty, …)`),且**每个**类型(含基类型)都占一节点 + // (parse_type 尾部 `alloc_node(0,…,ty,0,…)`)⇒ 第 i+1 个形参不是 pn+1。 + // 实测症状(TODO #21 原始探针的机制,本批实读):`fn take[T](a: T, n: int)` 的 + // param1 落到 `int` 的类型节点上——`ast_data(基类型节点) = 0` ⇒ res_call_type(0) 读 + // **AST 节点 0**(= 本文件首个类型节点;若它恰是 `EXPR_IDENT(T)` 则回 gparam(T)): + // 非泛型形参的**声明类型约束**在调用推断中被绕过,且 gparam(T) 分支会给**未绑定**的 + // 形参凭空绑定 T(`fn take[T](n: int, a: T)` 场景)。 + // 修法 = 前扫到下一个 EXPR_PARAM(与 ir_gen.cr 的三处同款同因::1481-1487 的 dex 参数 + // 对齐 / :2279-2285 的参数建 var / :2715 的 mono 克隆),扫到表尾置 -1(外循环 break)。 pn = pn + 1; + loop { if pn >= g_ast_count { pn = -1; break; } if ast_kind(pn) == EXPR_PARAM { break; } pn = pn + 1; } an = ast_b(an); // next EXPR_ARG } @@ -1057,6 +2063,15 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in hmi = hmi + 1; } if concrete_ti >= 0 { + // R2 P3 Task 5(Step 3):先走**本质轴**引擎判定(约束名 = 原生/已声明 + // 类型名时 `ty_sub(实参项, 约束项)` + 反例);用户接口名 ⇒ 0 不在此直取 + // (1 走提前返回;0/-1 落下方既有 check_iface 路径——**措辞/去重/rc 逐字 + // 未动**,0 → 新措辞的诊断切换归 Task 6 Step 3;见 gen_constr_satisfied 注)。 + if gen_constr_satisfied(iface_ni, concrete_ti) == 0 { + if gen_constr_seen_add(call_node * MAX_GENERICS + gci) != 0 { + gen_constr_raise(concrete_ti, iface_ni, ast_line(call_node), ast_col(call_node)); + } + } else { type_ni := get_type_name(concrete_ti); if type_ni >= 0 { ii := find_iface(iface_ni); @@ -1066,6 +2081,7 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in } } } + } } } } @@ -1073,12 +2089,39 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in } } - // Store concrete type name on call node for backend monomorphization + // ─── R2 P3 Task 5(Step 4):调用点**泛型绑定**登记(monomorph 实例键类型项化)─── + // 旧态 = 把「首个绑定的类型名」写进调用节点 int_val(唯一读者 `ir_gen.cr` 的 + // find_or_create_mono_func 是**死码**——零调用者;ir_gen 的实际实例键来自**实参 IR 变量 + // 的类型**,对命名/复合实参一律塌缩 "int" ⇒ 异型实例折叠 + 错替换)。新态 = 按**被调方 + // 泛型形参声明序**登记 ti 段(块 = [count, ti…]),节点 int_val = 块起始 + 1(**0 = 无** + // ——parser 建 EXPR_CALL 时 iv 初值即 0,见 parser.cr 的 alloc_node(EXPR_CALL, …, 0, …))。 + // ir_gen 据此生成规范实例键(inst_key_of_ti)并以 ti 型替换(monomorph.cr); + // 绑定全缺(g_gen_map_count == 0,如 T 只出现在返回型)⇒ 不写(0 = 回落旧名字路径)。 if g_gen_map_count > 0 { - conc_type_ni := r64(g_gen_map_types, 0 * 8); - conc_name_ni := get_type_name(conc_type_ni); - if conc_name_ni >= 0 { - ast_set_int_val(call_node, conc_name_ni); + gc_b := fi_generic_count(fi); + if gc_b > 0 { + bstart := g_gen_binds_count; + grow_gen_binds(bstart + 1 + gc_b); + w64(g_gen_binds, bstart * 8, gc_b); + gi_b : ., mut = 0; + loop { + if gi_b >= gc_b { break; } + pname_b := fi_generic_name(fi, gi_b); + tv_b : ., mut = -1; + hmi_b : ., mut = 0; + loop { + if hmi_b >= g_gen_map_count { break; } + if r64(g_gen_map_names, hmi_b * 8) == pname_b { + tv_b = r64(g_gen_map_types, hmi_b * 8); + break; + } + hmi_b = hmi_b + 1; + } + w64(g_gen_binds, (bstart + 1 + gi_b) * 8, tv_b); + gi_b = gi_b + 1; + } + g_gen_binds_count = bstart + 1 + gc_b; + ast_set_int_val(call_node, bstart + 1); } } @@ -1143,12 +2186,14 @@ fn check_func(fi: int) { ti = res_type_node(orig_type_node); } else { // Base type: switch on type_val (TY_*) + // P2b Task 6:值经单表 `ty_code_to_ti`;**本站点域 = {INT,DEX,BOOL,STRING,CHAR}** + // (缺 UNIT/NEVER/7——UNIT 缺不等于行为差:映射值 = 初值 TI_UNIT;NEVER 差异载荷, + // 探针 F7 实测:`fn f(b: never)` 的形参符号类型 = unit(@raw_int 报 TF07)) ptype := ast_type_val(pn); - if ptype == TY_INT { ti = TI_INT; } - else if ptype == TY_DEX { ti = TI_DEX; } - else if ptype == TY_BOOL { ti = TI_BOOL; } - else if ptype == TY_STRING { ti = TI_STR; } - else if ptype == TY_CHAR { ti = TI_CHAR; } + ptype_mapped := ty_code_to_ti(ptype); + if ptype_mapped >= 0 && ptype_mapped != TI_NEVER && ptype_mapped != TI_DYN { + ti = ptype_mapped; + } } } else { // Self param: derive struct type from mangled function name "Struct.method" @@ -1197,20 +2242,23 @@ fn check_func(fi: int) { type_node := ast_type_val(fn_node); if type_node > 0 && ast_kind(type_node) != 0 { ret_ti = res_type_node(type_node); - } else if return_type == TY_INT { ret_ti = TI_INT; } - else if return_type == TY_DEX { ret_ti = TI_DEX; } - else if return_type == TY_BOOL { ret_ti = TI_BOOL; } - else if return_type == TY_STRING { ret_ti = TI_STR; } - else if return_type == TY_UNIT { ret_ti = TI_UNIT; } - else if return_type == TY_CHAR { ret_ti = TI_CHAR; } - else if return_type == TY_NEVER { ret_ti = TI_NEVER; } - if !type_equal(body_ti, ret_ti) && body_ti != TI_NEVER { + } else { + // P2b Task 6:值经单表 `ty_code_to_ti`;**本站点域 = {INT..CHAR}**(含 NEVER;缺 7—— + // 探针 F11:`fn f() -> dyn` 的体检查用 unit 兜底,与 F10 的 never 格互不干扰) + ret_ti_mapped := ty_code_to_ti(return_type); + if ret_ti_mapped >= 0 && ret_ti_mapped != TI_DYN { ret_ti = ret_ti_mapped; } + } + compat := type_compat_strict(body_ti, ret_ti); + // 落空分析(TF01 收口,见 stmt_cannot_fall_through 头注):体确定不能落空(如以 + // 无 break 的 loop 收尾)⇒ 「块类型 = 末语句类型」推出的 unit 不是缺返回值的证据, + // 等价于 never 格(与下行既有 TI_NEVER 豁免同路)。 + if compat != 1 && body_ti != TI_NEVER && stmt_cannot_fall_through(body) == 0 { // Skip check if return type is generic param (can't verify at declaration) // Skip check for flow functions (yield instead of return) is_flow_fn : ., mut = 0; if body >= 0 && scan_for_yield(body) != 0 { is_flow_fn = 1; } if !is_flow_fn && get_type_kind(ret_ti) != TYP_GENERIC_PARAM { - check_error(EC_TF_RETURN, "Function return type mismatch", ast_line(fn_node), ast_col(fn_node)); + diag_type_incompatible(compat, EC_TF_RETURN, "Function return type mismatch", ast_line(fn_node), ast_col(fn_node)); } } } @@ -1238,41 +2286,51 @@ fn check_impl_for() { mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; method_ni := r64(g_ifaces, mbase + OFF_IFM_NAME); method_pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT); - method_rt := r64(g_ifaces, mbase + OFF_IFM_RET_TI); - - // Check if the implementing type has this method - type_name := istr_get(type_ni); method_name := istr_get(method_ni); - mangled := type_name + "." + method_name; - mangled_ni := str_intern(mangled); + iface_name2 := istr_get(iface_ni); - fi := find_func(mangled_ni); + // R2 P3b Task 6(Step 3,mangling 退役):方法解析 = **表查询** + // (`g_methods` 三元组 → 函数名 ni → 函数行),不再拼 "Type.method" 串。 + func_ni := iface_find_method(type_ni, method_ni); + fi : ., mut = -1; + if func_ni >= 0 { fi = find_func(func_ni); } if fi < 0 { - check_error(EC_TF_METHOD_NOT_FOUND, "Impl missing method '" + method_name + "' for interface '" + istr_get(iface_ni) + "'", 0, 0); + check_error(EC_TF_METHOD_NOT_FOUND, "Impl missing method '" + method_name + "' for interface '" + iface_name2 + "'", 0, 0); mi = mi + 1; continue; } - // Check param count + // 接收者模式(调用约定维度;旧态两侧皆码 0 ⇒ 不可比 ⇒ 本任务起显式比较) + if sh_iface_self_mode(ii, mi) != sh_func_self_mode(fi) { + check_error(EC_TF_METHOD_ARG_TYP, "Param 1 type mismatch for method '" + method_name + "' in interface '" + iface_name2 + "'", 0, 0); + } + // 参数计数 actual_pc := fi_param_count(fi); if actual_pc != method_pc { check_error(EC_TF_METHOD_ARG_CNT, "Param count mismatch for method '" + method_name + "': expected " + int_str(method_pc) + " got " + int_str(actual_pc), 0, 0); } - // Check each param type + // 逐参类型(**签名项**比较:N 不入项 / 泛型应用展开 / 命名行按名 —— 取代旧裸码相等) pti : ., mut = 0; loop { - if pti >= method_pc || pti >= 8 { break; } - expected_pt := r64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pti * 8); - actual_pt := fi_param_type(fi, pti); - if expected_pt != actual_pt { - pnum_str := int_str(pti + 1); - check_error(EC_TF_METHOD_ARG_TYP, "Param " + pnum_str + " type mismatch for method '" + method_name + "' in interface '" + istr_get(iface_ni) + "'", 0, 0); + if pti >= method_pc || pti >= MAX_IFACE_METHOD_PARAMS { break; } + apt := sh_func_sig_param_term(fi, pti); + ept := sh_iface_sig_param_term(ii, mi, pti); + if apt >= 0 && ept >= 0 { + // 判定 = 引擎的不变槽结构比较原语(0/1 全域;理由见 type_engine.cr 的 + // iface_user_satisfies_ii 注——ty_sub 会把「确定不同」上抛为 -1 = 未覆盖面) + if tt_list_same(apt, ept) != 1 { + pnum_str := int_str(pti + 1); + check_error(EC_TF_METHOD_ARG_TYP, "Param " + pnum_str + " type mismatch for method '" + method_name + "' in interface '" + iface_name2 + "'", 0, 0); + } } pti = pti + 1; } - // Check return type - actual_rt := fi_return_type(fi); - if actual_rt != method_rt { - check_error(EC_TF_RETURN, "Return type mismatch for method '" + method_name + "' in interface '" + istr_get(iface_ni) + "'", 0, 0); + // 返回类型(签名项比较;同上) + art := sh_func_sig_ret_term(fi); + ert := sh_iface_sig_ret_term(ii, mi); + if art >= 0 && ert >= 0 { + if tt_list_same(art, ert) != 1 { + check_error(EC_TF_RETURN, "Return type mismatch for method '" + method_name + "' in interface '" + iface_name2 + "'", 0, 0); + } } mi = mi + 1; } @@ -1282,9 +2340,16 @@ fn check_impl_for() { fn check_global_let(node: int) { val_node := ast_c(node); // EXPR_LET: c = value + type_node := ast_b(node); // EXPR_LET: b = 注解类型节点(-1 = 无) + val_ti := TI_UNIT; if val_node >= 0 { - infer_expr(val_node); + val_ti = infer_expr(val_node); } + // R2 P5 Task 6(TODO #32):全局初始化器的值/注解兼容检查(局部站点同款; + // 全局符号类型在注册趟(check_global_lets)即取注解行 ⇒ 不查同样静默错产物)。 + ti := val_ti; + if type_node >= 0 { ti = res_type_node(type_node); } + check_let_annot_compat(node, val_node, val_ti, ti); } // --- Dynamic type set tracking --- @@ -1363,12 +2428,17 @@ fn infer_expr(node: int) -> int { if node < 0 { return TI_UNIT; } - if ast_kind(node) == EXPR_INT { return TI_INT; } + // R2 P2b Task 3:字面量定型 → 本质条目表查表(唯一真源 = iface_registry.cr 的 lit_code 列)。 + // 改动前 = 本处 5 条内联 if(`return TI_INT/TI_DEX/TI_STR/TI_BOOL/TI_CHAR`,计划时点 + // checker.cr:1892-1897 → 现址 :1896-1901,偏移 +4)**逐格转录**进表;5 个 kind 的判断 + // 顺序与短路行为**逐字保持**——EXPR_NONE 转发行仍夹在 EXPR_INT 与 EXPR_DEX 之间, + // **不得**重排、**不得**合并成「先取 kind 再查」的循环(那会改短路面)。 + if ast_kind(node) == EXPR_INT { return iface_lit_ti(EXPR_INT); } if ast_kind(node) == EXPR_NONE && ast_a(node) >= 0 && ast_a(node) != node { return infer_expr(ast_a(node)); } - if ast_kind(node) == EXPR_DEX { return TI_DEX; } - if ast_kind(node) == EXPR_STRING { return TI_STR; } - if ast_kind(node) == EXPR_BOOL { return TI_BOOL; } - if ast_kind(node) == EXPR_CHAR { return TI_CHAR; } + if ast_kind(node) == EXPR_DEX { return iface_lit_ti(EXPR_DEX); } + if ast_kind(node) == EXPR_STRING { return iface_lit_ti(EXPR_STRING); } + if ast_kind(node) == EXPR_BOOL { return iface_lit_ti(EXPR_BOOL); } + if ast_kind(node) == EXPR_CHAR { return iface_lit_ti(EXPR_CHAR); } if ast_kind(node) == EXPR_IDENT { name_idx := ast_int_val(node); @@ -1393,15 +2463,12 @@ fn infer_expr(node: int) -> int { left := ast_a(node); right := ast_b(node); op := ast_c(node); - if op == OP_ASSIGN { - // Assignment: left = right - lt := infer_expr(left); - rt := infer_expr(right); - if !type_equal(lt, rt) { - check_error(EC_TA_ASSIGN, "Assignment type mismatch", ast_line(node), ast_col(node)); - } - return rt; - } + // R2 P5 Task 4(D25):`EXPR_BINARY + OP_ASSIGN` 分支**已删**(站点 6 随之退役—— + // 其挂点在本分支内)。不可达三面证明:① `tok2op`(parser.cr)不产 OP_ASSIGN; + // ② `T_EQ` → `EXPR_ASSIGN`(parser.cr);③ `+=` 族显式构造 `EXPR_ASSIGN` 包裹的 + // `EXPR_BINARY` 且 op ∈ {ADD,SUB,MUL,DIV} ⇒ 全仓无生产者(含 opt/pass 面)。 + // 实证:全语料 shadow 站点直方图 `assign-binary=0`(32988 次判定)。**赋值判定点 + // = 站点 8(EXPR_ASSIGN,见 infer_expr 的 EXPR_ASSIGN 分支)**,逐字保留。 lt := infer_expr(left); rt := infer_expr(right); if op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV || op == OP_MOD { @@ -1418,8 +2485,13 @@ fn infer_expr(node: int) -> int { if op == OP_SUB && get_type_kind(lt) == TYP_PTR && get_type_kind(rt) == TYP_PTR { return TI_INT; } - // Check: arithmetic ops require int or dex - if lt != TI_INT && lt != TI_DEX && rt != TI_INT && rt != TI_DEX { + // Check: arithmetic ops require int or dex —— R2 P2b Task 4:查表(门形状 = ANY: + // 「至少一侧许可即通过」,与改动前的 `&&` 形状一字等价)。转录依据 = 本处改动前的 + // `lt != TI_INT && lt != TI_DEX && rt != TI_INT && rt != TI_DEX`;表的 ADD..MOD 格 + // **只含 int/dex**(PTR/STRING 不在内——早退规则 :1946/:1948/:1951/:1955(接线前实测 + // 行号;接线后本块 +4)仍在原处 = 结果规则,不进本门)。负控(须仍报 error[TB01]): + // `*T + *T`、`"a" - "b"`。 + if iface_permits(iface_kind_of(lt), op) == 0 && iface_permits(iface_kind_of(rt), op) == 0 { check_error(EC_TB_ADD, "Arithmetic operation requires int or dex", ast_line(node), ast_col(node)); } if lt == TI_DEX || rt == TI_DEX { return TI_DEX; } @@ -1429,7 +2501,12 @@ fn infer_expr(node: int) -> int { return TI_BOOL; } if op == OP_AND || op == OP_OR { - if lt != TI_BOOL && lt != TI_INT || rt != TI_BOOL && rt != TI_INT { + // R2 P2b Task 4:查表(门形状 = ALL「每侧都须许可」)。转录依据 = 本处改动前的 + // `lt != TI_BOOL && lt != TI_INT || rt != TI_BOOL && rt != TI_INT`(= 某侧「非 bool 且 + // 非 int」即报错);谓词写 OP_AND(**不另设** IP_LOGIC——OP_AND/OP_OR 在表中同步置位, + // 且现状 :1969 对两个 op 一字不分)。表的 AND/OR 格恰 {int, bool}——**dex 不在内** + // (`1.5 && true` 现状 error[TC01],探针 N7 实测)。 + if iface_permits(iface_kind_of(lt), OP_AND) == 0 || iface_permits(iface_kind_of(rt), OP_AND) == 0 { check_error(EC_TC_IF_COND, "Logical operator requires bool or int operands", ast_line(node), ast_col(node)); } return TI_BOOL; @@ -1592,19 +2669,28 @@ fn infer_expr(node: int) -> int { if imi2 >= imc2 { break; } imbase2 := ii2 * ESZ_IFACEINFO + OFF_IF_METHODS + imi2 * ESZ_IFMETHOD; if r64(g_ifaces, imbase2 + OFF_IFM_NAME) == method_ni { - tname2 := istr_get(gen_ni); - mname2 := istr_get(method_ni); - mangled2 := tname2 + "." + mname2; - mangled_ni2 := str_intern(mangled2); - ast_set_data(node, mangled_ni2); + // R2 P3b Task 6(Step 3,mangling 退役):不再合成 "T.m" + // 串——调用点记(**泛型形参名 ni**,标记位),实例化 + // 时由 monomorph 按具体类型查方法表解析为真实函数名 + // (对照 gen_clone_tree 的 EXPR_CALL 分支与 ast.cr 的 + // CALL_FLAG_IFACE_METHOD 注)。旧态 = 名字拼接 + 克隆 + // 期文本替换 ⇒ 实例体内调用目标悬空(实测产物运行 + // rc=139);本任务起表查询(同 iface_find_method)。 + ast_set_data(node, gen_ni); + ast_set_type_val(node, CALL_FLAG_IFACE_METHOD); iface_ret2 := r64(g_ifaces, imbase2 + OFF_IFM_RET_TI); - if iface_ret2 == TY_INT { func_ni = mangled_ni2; return TI_INT; } - if iface_ret2 == TY_DEX { func_ni = mangled_ni2; return TI_DEX; } - if iface_ret2 == TY_BOOL { func_ni = mangled_ni2; return TI_BOOL; } - if iface_ret2 == TY_STRING { func_ni = mangled_ni2; return TI_STR; } - if iface_ret2 == TY_UNIT { func_ni = mangled_ni2; return TI_UNIT; } - if iface_ret2 == TY_CHAR { func_ni = mangled_ni2; return TI_CHAR; } - func_ni = mangled_ni2; return TI_UNIT; + // P2b Task 6:值经单表 `ty_code_to_ti`;本站点值域 = parser 写入的 + // `unpack_type(返回型节点)`(parser.cr:1701,即 TY_* 码);**域 = {INT..CHAR}** + // (缺 NEVER/7 ⇒ 落尾 TI_UNIT)。**显式化**:现状 6 行用的是 `iface_ret2 == TY_*` + // 而值域是 TI_*,靠「TY_INT==TI_INT==0 … TY_CHAR==TI_CHAR==6」的数值撞车成立, + // 合一后改按语义查表(探针 F13:`-> never` 的 iface 方法在调用点 = unit; + // F14:`-> char` = char)。 + iface_ret_ti := ty_code_to_ti(iface_ret2); + if iface_ret_ti >= 0 && iface_ret_ti != TI_NEVER && iface_ret_ti != TI_DYN { + func_ni = gen_ni; + return iface_ret_ti; + } + func_ni = gen_ni; return TI_UNIT; } imi2 = imi2 + 1; } @@ -1626,6 +2712,26 @@ fn infer_expr(node: int) -> int { if func_ni >= 0 { si := find_gsym(func_ni); if si >= 0 && sym_kind(si) == SYM_FN { + // R2 P6 Task 4a(E-11,姊妹站点):与直调站点守卫同形—— + // `func_ni` = parser impl 分支写入的 mangled ni("S.m", + // parser.cr:1887-1894)⇒ 与直调面同一张 g_funcs/符号表, + // `fi_return_type`/`sym_type` 语义逐位同。差异一处(显式对齐): + // 本尾无泛型早退(直调面走 `infer_gen_call`)⇒ 补 + // `fi_generic_count == 0` 守卫,使泛型面两站点一致**不动** + // (参数化方法的返回型依实例化)。本面恒无 extern(impl 分支 + // 只认 `fn`,parser.cr:1864-1899)⇒ 不需 EXPR_FN 限定 + // (行为等价,非逐字镜像)。嵌套 if 同直调站点(`&&` 不短路 + + // 表读无护栏)。 + fi_m := find_func(func_ni); + if fi_m >= 0 { + if fi_generic_count(fi_m) == 0 { + if sym_type(si) == TI_UNIT { + if fi_return_type(fi_m) == TY_NEVER { + return TI_NEVER; + } + } + } + } return sym_type(si); } } @@ -1684,6 +2790,30 @@ fn infer_expr(node: int) -> int { if fi >= 0 && fi_generic_count(fi) > 0 { return infer_gen_call(fi, node, first_arg, arg_count); } + // R2 P6 Task 4a(E-11):被调**非泛型 `fn` 声明**的返回行 = `never` + // ⇒ 调用推断 `never`。声明注册趟把 NEVER 钳成 unit(collect_decls 的 + // 域守卫 :1491:`rt_mapped != TI_NEVER`)⇒ 此处按裸码 `fi_return_type` + // 复读一次;两处下游闸随之生效:声明位 `check_let_annot_compat` 豁免③ + // (:526)/ 返回位(:2234)。**只改推断结果**:`sym_type(si)` 值不动 + // ⇒ 发射面零耦合(ir_gen 的调用结果型经 sym_type,见 ir_gen.cr:1662)。 + // 守卫四条:① 泛型已由上方早退排除;② 仍是被钳的 unit 格;③ 裸码 = + // TY_NEVER;④ = `fn` 声明(**extern 面按裁④ 不动**)。 + // **嵌套 if**:bootstrap 构建的 corec 对 `&&` 两侧无条件求值 + // (bootstrap/corec/frontend/ir_gen.py:226-235),且 `fi_return_type` + // (dyn_arr.cr:459)与 `ast_kind`(dyn_arr.cr:370)均无护栏 ⇒ 不得写 + // `guard && 表读`。 + if fi >= 0 { + if sym_type(si) == TI_UNIT { + if fi_return_type(fi) == TY_NEVER { + fnd := fi_ast_node(fi); + if fnd >= 0 { + if ast_kind(fnd) == EXPR_FN { + return TI_NEVER; + } + } + } + } + } return sym_type(si); // return type } // Check runtime builtins (no .cr body, implemented in rt.s) @@ -1734,7 +2864,11 @@ fn infer_expr(node: int) -> int { else_node := ast_c(node); cond_ti := infer_expr(cond); // Accept int as truthy/falsy in conditions (not just strict bool) - if cond_ti != TI_BOOL && cond_ti != TI_INT { + // R2 P2b Task 4:查表(门形状 = ONE 单操作数)。转录依据 = 本处改动前的 + // `cond_ti != TI_BOOL && cond_ti != TI_INT`;IP_COND 格恰 {int, bool}——**dex 拒** + // (`if 1.5` 现状 error[TC01],探针 N8 实测)。**不得**与 `while` 的 IP_COND_BOOL 合并 + // (那条只收 bool——合并 = 收紧 `if` 或放宽 `while`)。 + if iface_permits(iface_kind_of(cond_ti), IP_COND) == 0 { check_error(EC_TC_IF_COND, "If condition must be bool or int", ast_line(node), ast_col(node)); } // --- Dyn type set merge: save pre-if state --- @@ -1788,8 +2922,18 @@ fn infer_expr(node: int) -> int { mi = mi + 1; } g_dyn_type_set_count = merge_count; - if !type_equal(then_ti, else_ti) && then_ti != TI_NEVER && else_ti != TI_NEVER { - check_error(EC_TC_IF_BRANCH, "If branches have different types", ast_line(node), ast_col(node)); + // P3 Task 1 参序归一:合并**结果类型 = then_ti**(下方 return then_ti)⇒ 源 = else 侧, + // 目标 = then 侧(长度约束更弱者放前面会被这里拦住 = 与结果类型一致;反之亦然) + compat := type_compat_strict(else_ti, then_ti); + // TC02 收口(P3):if 的**值类型定义为 then_ti**(下方 return then_ti)⇒ 当 **else 支 + // 确定发散**(`return` 族收尾、不产出值)时,else 侧的「类型」是幻影(EXPR_RETURN 推断 + // = 所返回值类型)⇒ 相容判定无对象 ⇒ 不报。 + // **不对称是有意的**:then 支发散时 then_ti 本身即幻影(模型面,另案)⇒ 该形态继续报 + // TC02(保留真信号)。谓词 = stmt_diverges(头注:只服务本判定点)。 + if compat != 1 && then_ti != TI_NEVER && else_ti != TI_NEVER { + if stmt_diverges(else_node) == 0 { + diag_type_incompatible(compat, EC_TC_IF_BRANCH, "If branches have different types", ast_line(node), ast_col(node)); + } } return then_ti; } @@ -1809,6 +2953,7 @@ fn infer_expr(node: int) -> int { // Range go: returns array of body type (size from data=range_node) range_node := ast_data(node); rng_count := ast_b(range_node) - ast_a(range_node); + // 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 退役为「内联容量存储」表示提示(语义归处 = product / 序列+长度约束 / F11)——完整注记见本文件 res_type_node 的 EXPR_ARRAY 分支处,spec 见 docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 return alloc_type(TYP_ARRAY, body_ti, rng_count); } @@ -1842,7 +2987,9 @@ fn infer_expr(node: int) -> int { cond := ast_a(node); body := ast_b(node); cond_ti := infer_expr(cond); - if cond_ti != TI_BOOL { + // R2 P2b Task 4:查表(门形状 = ONE)。转录依据 = 本处改动前的 `cond_ti != TI_BOOL`; + // IP_COND_BOOL 格**只含 bool**(`while 1` 现状 error[TC04],探针 N9 实测)。 + if iface_permits(iface_kind_of(cond_ti), IP_COND_BOOL) == 0 { check_error(EC_TC_WHILE_COND, "While condition must be bool", ast_line(node), ast_col(node)); } push_borrow_scope(); @@ -1876,14 +3023,53 @@ fn infer_expr(node: int) -> int { if ast_kind(node) == EXPR_MATCH { match_expr := ast_a(node); first_arm := ast_b(node); - infer_expr(match_expr); + match_ti := infer_expr(match_expr); res : ., mut = TI_UNIT; ai : ., mut = 0; an : ., mut = first_arm; + // R2 P3 Task 3:穷尽性判定面(补集空性 + 具体变体反例)。收集与臂体推断同趟: + // 模式类别/归属 → 模式项 CONS 链(m_pats)+ 覆盖位(m_cover)+ 不可映射标记 + // (m_unmappable);臂体推断后统一判(判据本体与消费点封装见 ty_shadow.cr 末段)。 + m_pats : ., mut = tt_nil(); + mc_save := mc_begin(); // 容量批 T3:覆盖位图入栈(无界;旧态 = 单 int 2^vi) + m_unmappable : ., mut = 0; + m_wild : ., mut = 0; // 通配/绑定已见(冗余臂判据用;非穷尽性判据本体) + m_dup : ., mut = -1; // 首个冗余臂模式节点(-1 = 无) loop { if an < 0 { break; } arm_pat := ast_a(an); // EXPR_ARM: a = pattern arm_body := ast_b(an); // EXPR_ARM: b = body + if arm_pat >= 0 { + pk := sh_match_pat_kind(arm_pat); + if pk == 1 { + if m_wild != 0 { if m_dup < 0 { m_dup = arm_pat; } } + m_wild = 1; + m_pats = tt_cons(tt_top(), m_pats); + } else if pk == 2 { + vi := sh_match_pat_variant(match_ti, arm_pat); + vt : ., mut = -1; + if vi >= 0 { vt = sh_match_variant_term(match_ti, vi); } + // R2 P3 Task 4:可选域(`T?`)——枚举归属不成立时试 `Some`/`None` 两分支 + // (sh_match_opt_pat 对非可选 scrutinee 恒 -1 ⇒ 既有枚举/非枚举面零变化)。 + oi : ., mut = -1; + if vi < 0 { + oi = sh_match_opt_pat(match_ti, arm_pat); + if oi >= 0 { vt = sh_match_opt_term(match_ti, oi); } + } + if vt < 0 { + m_unmappable = 1; // 非本枚举/非本域/未声明变体名 ⇒ 不判(不得当 ∅/⊤) + } else { + ci : ., mut = vi; + if vi < 0 { ci = oi; } // 可选域:覆盖位 0 = 有值 / 1 = null + if m_wild != 0 { if m_dup < 0 { m_dup = arm_pat; } } + if mc_has(ci) != 0 { if m_dup < 0 { m_dup = arm_pat; } } + mc_add(ci); + m_pats = tt_cons(vt, m_pats); + } + } else { + m_unmappable = 1; // 字面量/struct 模式 ⇒ 不判(登记) + } + } // Bind pattern variables in new scope push_scope(); if arm_pat >= 0 { @@ -1916,6 +3102,31 @@ fn infer_expr(node: int) -> int { an = ast_c(an); // next arm via linked list ai = ai + 1; } + // 穷尽性判定:引擎补集空性(预算前后隔离照 type_equal_engine 的窗口纪律)。 + // 三态:0 = 不穷尽(诊断 + 具体反例变体名);1 = 穷尽;-1 = 不判(非枚举域/不可映射 + // 模式/引擎未知)——**零诊断**(三态纪律:未知不得当「不穷尽」,也不得当「穷尽」)。 + ty_budget_reset(200000); + m_verdict := sh_match_exhaustive(match_ti, m_pats, m_unmappable); + ty_budget_reset(200000); + if m_verdict == 0 { + mi : ., mut = -1; + msg := "Non-exhaustive match"; + if get_type_kind(match_ti) == TYP_OPTIONAL { + // `T?` 两分支反例命名(R2 P3 Task 4):0 = 有值(Some)/ 1 = null(None) + mi = mc_first_missing(2); + if mi == 0 { msg = msg + ": missing variant 'Some'"; } + if mi == 1 { msg = msg + ": missing variant 'None'"; } + } else { + ea := find_enum_row_of(match_ti); + if ea >= 0 { mi = mc_first_missing(ei_variant_count(ea)); } + if mi >= 0 { msg = msg + ": missing variant '" + istr_get(ei_variant_name(ea, mi)) + "'"; } + } + check_error(EC_TM_EXHAUST, msg, ast_line(node), ast_col(node)); + } + if m_dup >= 0 { + check_error(EC_TM_REDUNDANT, "Redundant match arm", ast_line(m_dup), ast_col(m_dup)); + } + mc_end(mc_save); // 容量批 T3:覆盖位图出栈(清零本层位 + 回退长度) return res; } @@ -1937,6 +3148,8 @@ fn infer_expr(node: int) -> int { } ti := val_ti; if type_node >= 0 { ti = res_type_node(type_node); } + // R2 P5 Task 6(TODO #32):值/注解兼容检查(豁免面见 check_let_annot_compat 头注) + check_let_annot_compat(node, val_node, val_ti, ti); if istr_get(var_ni) != "_" { def_sym(var_ni, SYM_LOCAL, ti, -1); if ti == TI_DYN && val_node >= 0 { @@ -1971,6 +3184,52 @@ fn infer_expr(node: int) -> int { return sym_type(si); // enum type } name := istr_get(name_idx); + // ─── R2 P3 Task 4:内建可选构造器(退役内建 Option 注册后的解析路径)─── + // `Some`/`None` 是**关键字**(lexer.cr 的 T_SOME/T_NONE 仅由这两个词产出)⇒ 本分派是 + // 关键字名分派,不是「类型名分派」:用户声明同名**变体**时上面的 SYM_FN 命中优先 + // (p20 语义保持:用户 `enum Option[T] { None, Some(T) }` 时 Some/None 仍是它的变体)。 + // 类型面:`None` = TYP_NULL(null 单点类型,`T ∪ null` 里的 null);`Some(x)` = + // TYP_OPTIONAL(载荷行) ⇒ `Some(1)` 的类型 = `int?`(计划用例「Some(1) 满足 int?」)。 + // 表示面(如实登记):运行时形态沿用既有 EXPR_ENUM_CONSTRUCTOR 代码路径(ir_gen 的 + // IR_MAKE_ENUM + 字段存;EXPR_TRY 的 IR = 内层表达式)——本任务落**类型层**的联合/ + // 可选语义,运行期表示统一(Some 值 vs 裸值)不在本任务 Files 面内(计划未列 ir_gen/ + // 后端),登记为未覆盖面。 + if str_eq(name, "Some") != 0 { + at : ., mut = TI_UNIT; + if arg_count == 1 && first_arg >= 0 { + at = infer_expr(ast_a(first_arg)); + } else if arg_count > 1 { + // 多载荷(`Some(a, b)`):载荷行 = TYP_TUPLE(两趟落盘,照 EXPR_TUPLE 契约—— + // 元素推断自身可向 g_gen_apply_data 追加数据,不得边推边写;见该分支注记)。 + tis : string, mut = alloc(arg_count * 8); + ai : ., mut = 0; + an2 : ., mut = first_arg; + loop { + if ai >= arg_count { break; } + ev : ., mut = TI_UNIT; + if an2 >= 0 { + ev = infer_expr(ast_a(an2)); + an2 = ast_b(an2); + } + w64(tis, ai * 8, ev); + ai = ai + 1; + } + ds := g_gen_apply_data_count; + grow_gen_apply_data(ds + arg_count); + ai = 0; + loop { + if ai >= arg_count { break; } + w64(g_gen_apply_data, (ds + ai) * 8, r64(tis, ai * 8)); + ai = ai + 1; + } + g_gen_apply_data_count = ds + arg_count; + at = alloc_type(TYP_TUPLE, arg_count, ds); + } + return alloc_type(TYP_OPTIONAL, at, 0); + } + if str_eq(name, "None") != 0 { + return alloc_type(TYP_NULL, 0, 0); + } check_error(EC_N_UNDEFINED, "Undefined enum constructor '" + name + "'", ast_line(node), ast_col(node)); return TI_UNIT; } @@ -2053,7 +3312,16 @@ fn infer_expr(node: int) -> int { arr_kind := get_type_kind(arr_ti); // Range index: arr[low..high] → slice type if ast_kind(ast_b(node)) == EXPR_RANGE { - if arr_kind == TYP_ARRAY { + // R2 P3b Task 2:类别判定换位到**序列形状**(数组/切片同属 AK_SEQUENCE; + // sh_shape_seq = ⊤ₖ(AK_SEQUENCE),见 iface_registry.cr 的横切形状段)。 + // 固定性维度(N vs 视图)取自序列项的 b 槽(sh_seq_fixed_len_of_ti——表示层提示, + // 语义判定不看 N:spec §5.1 + T1 的固定性位登记)。行为保持:仅**固定长序列** + // (数组)在 range 下产视图;切片/非序列现状 = TI_UNIT(IP_INDEX_RANGE 未接线, + // 登记面 A.2 #15)。三态纪律:形状 -1(不可译行)**不**折算,回落旧 kind 直比 + // (该行改动前由 `arr_kind == TYP_ARRAY` 独判 ⇒ 回落即逐行同结论)。 + seq_ok := iface_satisfies_term(arr_ti, sh_shape_seq()); + if seq_ok == -1 && arr_kind == TYP_ARRAY { seq_ok = 1; } + if seq_ok == 1 && sh_seq_fixed_len_of_ti(arr_ti) >= 0 { // F11:字面量切片界编译期验证(TK05/06 既有错误码) arr_len : ., mut = get_type_extra(arr_ti); rn := ast_b(node); @@ -2100,7 +3368,18 @@ fn infer_expr(node: int) -> int { } return TI_INT; // string[i] → byte value } - check_error(EC_TK_INDEX, "Cannot index non-array type", ast_line(node), ast_col(node)); + // R2 P3b Task 2:兜底门从「许可位集直查」换位到**横切形状满足判定**(可索引形状 = + // ⊤ₖ(SEQUENCE) ∪ ⊤ₖ(STRING),见 iface_registry.cr 的横切形状段)。与 P2b Task 5 的 + // 表查同一口径:本门的**可达集** = 上方三个结果分支(arr→elem+F2 / slice→elem / + // str→int——同时决定结果类型,A.2 #16-18 明令原地保留)之后的落空集;形状的**拒绝集** + // 与之逐行相等(全类型行枚举守门 = selftest `x2.indexable_matches_ops` + 既有 + // `idx.gate_deny_covers_fallback`)。三态纪律:形状 -1(不可判:不可译行等)**不**折算成 + // 0/1,而是**回落**既有许可表——改动前同类行本就走该表,故回落 = 逐行同结论。 + idx_ok := iface_satisfies_term(arr_ti, sh_shape_indexable()); + if idx_ok == -1 { idx_ok = iface_permits(iface_kind_of(arr_ti), IP_INDEX); } + if idx_ok != 1 { + check_error(EC_TK_INDEX, "Cannot index non-array type", ast_line(node), ast_col(node)); + } return TI_INT; } @@ -2118,86 +3397,210 @@ fn infer_expr(node: int) -> int { dyn_set_type(target_si, vt); } } - } else if !type_equal(tt, vt) { - check_error(EC_TA_ASSIGN, "Assignment type mismatch", ast_line(node), ast_col(node)); + } else { + // P3 Task 1 参序归一:赋值 = 目标(tt) ← 值(vt) ⇒ 源 = vt,目标 = tt + compat := type_compat_strict(vt, tt); + if compat != 1 { + diag_type_incompatible(compat, EC_TA_ASSIGN, "Assignment type mismatch", ast_line(node), ast_col(node)); + } } return vt; } if ast_kind(node) == EXPR_STRUCT { - // Struct literal: a = name idx, b = first field value (wrapper), c = field count + // Struct literal: a = name idx, b = first field wrapper, c = field count。 + // F5 契约(见 parser.cr struct 分支):wrapper 在 g_ast 中连续、wrapper.a=字段值节点; + // 逐 wrapper 解引用(infer_expr 对 EXPR_NONE 前向)——直接按偏移取「下一个节点」当字段值 + // 只在字段值单槽时成立,复合字段值(调用/字面量)会整体错位(静默错误值)。 + // TODO #29 ①②(名字绑定 + 类型比对):wrapper.b = 字段名 idx(parser 写入;-1 = 无名字 + // 信息 → 不校验、按既有位序语义回落)。新增 = 未知字段(TS02)/ 重复字段(TS04)/ + // 缺字段(TS01)三校验 + 字段类型 vs 声明比对(TS03,走 type_compat_strict 引擎判定)。 + // **值按名字绑定**(与 Python bootstrap 的 gen_struct_lit 同语义;名字 → 声明下标, + // 消费者 ir_gen 同步按名字取字段位):修复前值按**声明位序**绑定 ⇒ P{b:11, a:22} 静默 + // 得 a=11(静默错值级,rc=0)。 name_ni := ast_a(node); - // Check if struct is generic si := find_struct_by_name(name_ni); - if si >= 0 && si_generic_count(si) > 0 { - // Generic struct: infer concrete types from field values - g_gen_map_count = 0; g_gen_map_cap = 0; - fi : ., mut = 0; - fn2 : ., mut = ast_b(node); + c := ast_c(node); + w0 := ast_b(node); + // 名字信息齐备?(parser 恒写;克隆体丢名字 → 回落位序、不做名字类校验) + named : ., mut = 0; + if w0 >= 0 && c > 0 { + named = 1; + ni2 : ., mut = 0; loop { - if fi >= ast_c(node) { break; } - if fi < si_field_count(si) && fn2 >= 0 { - field_val_ti := infer_expr(fn2); - orig_type_node := si_field_type_node(si, fi); - if orig_type_node >= 0 { - if ast_kind(orig_type_node) == EXPR_IDENT { - // Check if field type is a generic param - field_name_idx := ast_int_val(orig_type_node); - if is_struct_generic(si, field_name_idx) { - grow_gen_map(g_gen_map_count + 1); - w64(g_gen_map_names, g_gen_map_count * 8, field_name_idx); - w64(g_gen_map_types, g_gen_map_count * 8, field_val_ti); - g_gen_map_count = g_gen_map_count + 1; - } + if ni2 >= c { break; } + if ast_b(w0 + ni2) < 0 { named = 0; break; } + ni2 = ni2 + 1; + } + } + // didx[i] = 字面量第 i 个字段 → 声明字段下标(默认位序 = 无名字信息时的既有语义) + didx : string, mut = alloc((c + 1) * 8); + i : ., mut = 0; + loop { + if i >= c { break; } + w64(didx, i * 8, i); + i = i + 1; + } + if si >= 0 && (named != 0 || c == 0) { + // ── 名字 → 声明下标:未知字段 / 重复字段 / 缺字段 ── + fc := si_field_count(si); + used : string, mut = alloc((fc + 1) * 8); + j : ., mut = 0; + loop { + if j >= fc { break; } + w64(used, j * 8, 0); + j = j + 1; + } + if named != 0 { + i = 0; + loop { + if i >= c { break; } + nn := ast_b(w0 + i); + jdi := struct_field_index_by_name(si, nn); + if jdi < 0 { + check_error(EC_TS_UNKNOWN_FIELD, "Unknown field '" + istr_get(nn) + "' in struct literal " + istr_get(name_ni), ast_line(w0 + i), ast_col(w0 + i)); + } else { + if r64(used, jdi * 8) != 0 { + check_error(EC_TS_FIELD_DUP, "Field '" + istr_get(nn) + "' initialized more than once", ast_line(w0 + i), ast_col(w0 + i)); } + w64(used, jdi * 8, 1); + w64(didx, i * 8, jdi); } - fn2 = fn2 + 1; + i = i + 1; } - fi = fi + 1; - } - // Create TYP_GENERIC_APPLY for this struct - base_ti := alloc_type(TYP_NAMED, name_ni, 0); - ds := g_gen_apply_data_count; - grow_gen_apply_data(ds + 1 + g_gen_map_count); - w64(g_gen_apply_data, ds * 8, g_gen_map_count); - g_gen_apply_data_count = ds + 1; - mi : ., mut = 0; + } + j = 0; loop { - if mi >= g_gen_map_count { break; } - w64(g_gen_apply_data, (ds + 1 + mi) * 8, r64(g_gen_map_types, mi * 8)); - mi = mi + 1; + if j >= fc { break; } + if r64(used, j * 8) == 0 { + // 只报第一个缺字段(同一程序员错误不刷屏) + check_error(EC_TS_MISSING_FIELD, "Missing field '" + istr_get(si_field_name(si, j)) + "' in struct literal " + istr_get(name_ni), ast_line(node), ast_col(node)); + break; + } + j = j + 1; } - g_gen_apply_data_count = ds + 1 + g_gen_map_count; - return alloc_type(TYP_GENERIC_APPLY, base_ti, ds); } - // Non-generic struct - ti := alloc_type(TYP_NAMED, name_ni, 0); - fi : ., mut = 0; - fn2 : ., mut = ast_b(node); + // ── 值类型逐个推断(一次/字段:infer_expr 有副作用,勿重复推断;结构体名未找到时 + // 也要推断——嵌套诊断/dyn 记录等副作用在此,旧行为不可丢)── + vts : string, mut = alloc((c + 1) * 8); + i = 0; loop { - if fi >= ast_c(node) { break; } - if fn2 >= 0 { - infer_expr(fn2); // wrapper node — forwards to value - fn2 = fn2 + 1; + if i >= c { break; } + if w0 >= 0 { w64(vts, i * 8, infer_expr(w0 + i)); } + i = i + 1; + } + if si >= 0 { + gc := si_generic_count(si); + if gc > 0 { + // ── 泛型结构体:字段类型 = 泛型参数 → 绑定(重复绑定走 unify 已绑定路径比对)── + g_gen_map_count = 0; g_gen_map_cap = 0; + i = 0; + loop { + if i >= c { break; } + jdi := r64(didx, i * 8); + if jdi >= 0 && jdi < si_field_count(si) { + dtn := si_field_type_node(si, jdi); + if dtn >= 0 && ast_kind(dtn) == EXPR_IDENT && is_struct_generic(si, ast_int_val(dtn)) { + pt := alloc_type(TYP_GENERIC_PARAM, ast_int_val(dtn), 0); + if !unify_types(pt, r64(vts, i * 8)) { + check_error(EC_TS_FIELD_TYPE, "Field '" + istr_get(si_field_name(si, jdi)) + "': expected " + istr_get(ast_int_val(dtn)) + ", got " + type_display(r64(vts, i * 8)), ast_line(w0 + i), ast_col(w0 + i)); + } + } + } + i = i + 1; + } } - fi = fi + 1; + // ── 非参数声明类型:值类型 vs 声明类型比对(两侧任一含未实例化泛型参数 → 跳过)── + i = 0; + loop { + if i >= c { break; } + jdi := r64(didx, i * 8); + if jdi >= 0 && jdi < si_field_count(si) { + dtn := si_field_type_node(si, jdi); + is_param : ., mut = 0; + if dtn >= 0 && ast_kind(dtn) == EXPR_IDENT && is_struct_generic(si, ast_int_val(dtn)) { is_param = 1; } + if is_param == 0 && type_node_mentions_struct_param(si, dtn) == 0 { + vt := r64(vts, i * 8); + if ti_has_generic_param(vt) == 0 { + dt := res_type_node(dtn); + if ti_has_generic_param(dt) == 0 { + // P3 Task 1 参序归一:源 = 值类型(vt),目标 = 声明类型(dt) + verdict := type_compat_strict(vt, dt); + if verdict != 1 { + diag_type_incompatible(verdict, EC_TS_FIELD_TYPE, "Field '" + istr_get(si_field_name(si, jdi)) + "': expected " + type_display(dt) + ", got " + type_display(vt), ast_line(w0 + i), ast_col(w0 + i)); + } + } + } + } + } + i = i + 1; + } + if gc > 0 { + // ── 结果类型 TYP_GENERIC_APPLY:实参按**参数声明序**取映射 ── + // (旧代码按字面量字段序写映射 + 直取映射序 = 参数序 ≠ 字段序时实参错位) + base_ti := alloc_named_type(name_ni); + ds := g_gen_apply_data_count; + grow_gen_apply_data(ds + 1 + gc); + g_gen_apply_data_count = ds + 1; + found : ., mut = 0; + g : ., mut = 0; + loop { + if g >= gc { break; } + pni := si_generic_name(si, g); + bind : ., mut = -1; + mi : ., mut = 0; + loop { + if mi >= g_gen_map_count { break; } + if r64(g_gen_map_names, mi * 8) == pni { bind = r64(g_gen_map_types, mi * 8); break; } + mi = mi + 1; + } + if bind >= 0 { + w64(g_gen_apply_data, (ds + 1 + found) * 8, bind); + found = found + 1; + } + g = g + 1; + } + w64(g_gen_apply_data, ds * 8, found); + g_gen_apply_data_count = ds + 1 + found; + return alloc_type(TYP_GENERIC_APPLY, base_ti, ds); + } + return alloc_named_type(name_ni); } - return ti; + // 结构体名未找到:既有行为(不在此报错,返回具名类型) + return alloc_named_type(name_ni); } if ast_kind(node) == EXPR_ARRAY { - // Array literal: a = first elem, b = elem count - elem_ti := TI_INT; + // Array literal(F5 契约,见 parser.cr 下标分支):a = first wrapper(连续), + // b = elem count;wrapper.a=元素值节点(infer_expr 对 EXPR_NONE 前向)——不得按偏移 + // 直取相邻节点当元素,复合元素子树占多槽会整体错位(静默错型/错值)。 + // TODO #29 ③:元素**同质性**检查——旧代码逐个覆盖 elem_ti(最终 = **最后一个**元素的 + // 类型),异质字面量 [1, "x", 3] 静默通过且类型随末元素漂移。现取首元素类型为元素类型 + // (与 Python bootstrap 的 ArrayLit 同语义),后续元素逐个与首元素比对(TK02)。 + elem_ti := TI_INT; // 空字面量 []:沿用旧默认 ei : ., mut = 0; en : ., mut = ast_a(node); loop { if ei >= ast_b(node) { break; } if en >= 0 { - elem_ti = infer_expr(en); + eti := infer_expr(en); + if ei == 0 { + elem_ti = eti; + } else { + // 两侧任一含未实例化泛型参数 → 跳过(不假拒,见 ti_has_generic_param) + if ti_has_generic_param(elem_ti) == 0 && ti_has_generic_param(eti) == 0 { + // P3 Task 1 参序归一:源 = 本元素(eti),目标 = 首元素定下的元素类型(elem_ti) + verdict := type_compat_strict(eti, elem_ti); + if verdict != 1 { + diag_type_incompatible(verdict, EC_TK_ELEM_TYPE, "Expected array element type " + type_display(elem_ti) + ", got " + type_display(eti), ast_line(en), ast_col(en)); + } + } + } en = en + 1; } ei = ei + 1; } + // 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 退役为「内联容量存储」表示提示(语义归处 = product / 序列+长度约束 / F11)——完整注记见本文件 res_type_node 的 EXPR_ARRAY 分支处,spec 见 docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 return alloc_type(TYP_ARRAY, elem_ti, ast_b(node)); } @@ -2214,20 +3617,20 @@ fn infer_expr(node: int) -> int { return ret; } if ast_kind(node) == EXPR_TRY { - // Try operator: unwrap Option[T] → T, Result[T,E] → T + // R2 P3 Task 4:`?` 解包改按**类型项结构**(spec §5.4),废掉基名字符串判断 + // (旧实现按 `base_name == "Option" || base_name == "Result"` 取第一个类型实参—— + // 与「可选不再依赖内建 Option 枚举名」相悖,且对用户自定义枚举按名字猜测语义)。 + // 语义:`T?` = `T ∪ null` ⇒ 解包 = 取**非 null 析取支**。行面只有两种承载: + // · TYP_OPTIONAL(data = 内层行)→ 返回内层(= T); + // · TYP_NULL(null 单点类型,无值可取)→ **不做解包**(返回原行)——「解包 null」 + // 的结果是空类型,而本层的 `never`/`⊥` 行在引擎侧不是真 ⊥(AK_NEVER 仍按原子类 + // 判包含,见 type_engine.cr 的 lit_implies)⇒ 判 never 会引入**新的**拒绝面; + // 故保守不动(如实登记:`None?` 的类型 = null 自身)。 + // 其余(命名行/泛型应用行/原生行)**一律不解包**(旧行为:名字命中 Option/Result 才解包; + // 用户枚举不再按名字猜——语义面登记见任务报告「收紧清单/语义保持」双表)。 inner_ti := infer_expr(ast_a(node)); - if get_type_kind(inner_ti) == TYP_GENERIC_APPLY { - base_ti := get_type_data(inner_ti); - if get_type_kind(base_ti) == TYP_NAMED { - base_ni := get_type_data(base_ti); - base_name := istr_get(base_ni); - if base_name == "Option" || base_name == "Result" { - ga_start := get_type_extra(inner_ti); - if r64(g_gen_apply_data, ga_start * 8) >= 1 { - return r64(g_gen_apply_data, (ga_start + 1) * 8); // first type arg - } - } - } + if get_type_kind(inner_ti) == TYP_OPTIONAL { + return get_type_data(inner_ti); } return inner_ti; } @@ -2257,18 +3660,34 @@ fn infer_expr(node: int) -> int { } if ast_kind(node) == EXPR_TUPLE { // Tuple: create a TYP_TUPLE type with element types + // F5 契约:a=首 wrapper(g_ast 中连续)、b=元素个数;wrapper.a = 元素值节点。 + // 复合元素(如 [1,2,3])的值节点自身不连续,必须经 wrapper 解引用—— + // 直读 `elem_idx + e` 会把元素子节点当元素(类型错录 → 假拒 + soundness 漏放)。 elem_idx := ast_a(node); ec : ., mut = ast_b(node); - data_start := g_gen_apply_data_count; + // 元素类型**两趟**记录(F5 第二根因):先逐个推断(元素的推断自身可能向 + // g_gen_apply_data 追加数据——嵌套元组 / 泛型应用 / 泛型结构字面量的 + // g_gen_map 段),再一次性连续落盘。若照旧在循环前取 data_start 边推边写, + // 前面元素追加的数据会把后续元素的位置顶开,extra 与实际落点错位(实测: + // ((2,3),1) 的元素表被读成内层元组的字段 [int,int] → 嵌套元组异型互赋静默通过)。 + tis : string, mut = alloc(ec * 8); e : ., mut = 0; loop { if e >= ec { break; } - elem_ti := infer_expr(elem_idx + e); - grow_gen_apply_data(g_gen_apply_data_count + 1); - w64(g_gen_apply_data, g_gen_apply_data_count * 8, elem_ti); - g_gen_apply_data_count = g_gen_apply_data_count + 1; + wn : ., mut = -1; + if elem_idx >= 0 { wn = ast_a(elem_idx + e); } + w64(tis, e * 8, infer_expr(wn)); + e = e + 1; + } + data_start := g_gen_apply_data_count; + grow_gen_apply_data(data_start + ec); + e = 0; + loop { + if e >= ec { break; } + w64(g_gen_apply_data, (data_start + e) * 8, r64(tis, e * 8)); e = e + 1; } + g_gen_apply_data_count = data_start + ec; return alloc_type(TYP_TUPLE, ec, data_start); } @@ -2474,3 +3893,232 @@ fn check_all() { // Check impl-for relationships check_impl_for(); } + +// ─── 效应/纯度修正 Task 1(P0 插队批):真纯度计算 ───────────────────────── +// 语义出处:docs/superpowers/plans/2026-08-08-region-cfg.md:484「非纯调用才进链; +// find_func 不可得(外部函数)保守进链」。 +// +// 纯(1)⟺ 下列全部成立(全按 IR/DF 面判定,一律保守): +// ① 体无 store 族:IR_STORE / IR_STORE_FIELD / IR_STORE_INDEX / +// IR_STORE_INDEX_VAR / IR_STORE_PTR(裸指针写同为效应);无 unsafe 区 +// (IR 无 unsafe 专用 opcode——按 SG_UNSAFE 区的归属函数判定)。 +// ② 体无 IO/FFI/并发效应 opcode:IR_CALL_EXTERN / IR_HOTPATCH_ROUTE / +// IR_DYN_DISPATCH / IR_SPAWN / IR_YIELD / IR_AWAIT / IR_INLINE 见 ③。 +// ③ 传递闭包内被调者全纯:IR_CALL 的 s3 = 名 ni 经 find_func 解析(不可解析 = +// runtime builtin ⇒ 不纯);IR_INLINE 亦按 s1 经 find_func 解析,但 s1 的 +// 含义**依发射点分两形**:调用旗标形(ir_gen.cr:1523)s1 = 函数**名 ni**; +// `@inline(expr)` 形(ir_gen.cr:1373)s1 = **变量索引** ⇒ 该形必然解析失败、 +// 被保守判不纯(误差方向安全;终审 Minor #4 记录,注释原文「同为名 ni」失实)。 +// ④ 递归/SCC ⇒ 保守不纯:不动点从「全不纯」起点**升纯**——自环/互环永远等不到 +// 「被调者已纯」⇒ 自动留在不纯,无需显式 SCC 检测。 +// 泛型源函数(无 IR 体、永不被调用——调用点解析到实例):纯度 = 实例的合取 +// (无实例 = 0),故「实例与源同值」恒成立。 +// +// 时点:**IR 生成结束之后**(唯一入口 = df_state_finalize,见 dataflow.cr)。 +// IR 体是纯度的唯一权威来源,而 IR 体逐函数生成(调用者先于被调者、含递归与 +// 前向引用)⇒ check_all 尾部(声明就绪但 IR 体尚未生成)与 collect_decls 期 +// (调用图未闭合)都不可用;链消费者(df_connect_state)相应后移到同一时点 +// (df_replay_state_chain)——否则链只能读到生成期的乐观默认值。 +// +// 默认值冻结注记:checker.cr 注册函数时的 fi_set_ispure(..., 1)(乐观默认) +// **有意保留**——IR 生成期它的唯一消费者是 ir_gen.cr 的 lazy 判定,本批明令 +// 冻结该判定(use_count 时序缺陷另批修)⇒ 删掉默认值会让 lazy 全部不发射、 +// 发射面逐字节改变。真纯度在 IR 生成后由本函数覆盖写回。 + +fn purity_op_effect(op: int) -> int { + if op == IR_STORE { return 1; } + if op == IR_STORE_FIELD { return 1; } + if op == IR_STORE_INDEX { return 1; } + if op == IR_STORE_INDEX_VAR { return 1; } + if op == IR_STORE_PTR { return 1; } + if op == IR_CALL_EXTERN { return 1; } + if op == IR_HOTPATCH_ROUTE { return 1; } + if op == IR_DYN_DISPATCH { return 1; } + if op == IR_SPAWN { return 1; } + if op == IR_YIELD { return 1; } + if op == IR_AWAIT { return 1; } + return 0; +} + +// IR 函数序号(g_ir_func_* 表下标)→ 源 FuncInfo 下标;-1 = 越界(不可达防御)。 +// 依据:IR 生成循环按源序**跳过泛型函数**发 IR;monomorph 实例只追加到 g_funcs +// 末尾(唯一创建点 = ir_gen.cr 的调用点)⇒ IR 序 = 源序非泛型子序列 ++ 实例创建序, +// 与本函数「按源序跳过泛型」的重放一致。调用方另按 g_ir_func_name_idx 复核名字, +// 复核失败即保守判不纯(防映射漂移静默错标)。 +fn src_func_of_ir(irf: int) -> int { + cnt : ., mut = 0; + i : ., mut = 0; + loop { + if i >= g_func_count { return -1; } + if fi_generic_count(i) == 0 { + if cnt == irf { return i; } + cnt = cnt + 1; + } + i = i + 1; + } + return -1; +} + +// DF 节点序号 → 所属 IR 函数序号(-1 = 无)。g_df_func_node_start 单调 ⇒ 线性 +// 取下界即上界;unsafe 区稀少,不值得为它引二分。 +fn df_func_of_node(pos: int) -> int { + best : ., mut = -1; + irf : ., mut = 0; + loop { + if irf >= g_ir_func_count { break; } + if r64(g_df_func_node_start, irf * 8) <= pos { best = irf; } + irf = irf + 1; + } + return best; +} + +fn compute_all_purity() { + fcount := g_func_count; + if fcount <= 0 { return; } + if g_ir_func_count <= 0 { return; } // 无 IR 体(check-only 路径)⇒ 不覆盖默认值 + + // 侧表(本函数自持):local = 局部效应/硬不纯位;pure = 当前纯度工作值; + // callee = 每条 IR 指令的可解析被调 fi(-1 = 非调用;不可解析记在 local 位)。 + // alloc 不保证清零(rt.s bump 分配器)⇒ 三表全部显式初始化。 + local := alloc(fcount * 8); + pure := alloc(fcount * 8); + callee := alloc(g_ir_instr_count * 8); + z : ., mut = 0; + loop { + if z >= fcount { break; } + w64(local, z * 8, 0); + w64(pure, z * 8, 0); + z = z + 1; + } + z = 0; + loop { + if z >= g_ir_instr_count { break; } + w64(callee, z * 8, -1); + z = z + 1; + } + + // ── 1) 逐 IR 函数扫体:局部效应位 + 调用边解析 ── + irf : ., mut = 0; + loop { + if irf >= g_ir_func_count { break; } + sfi := src_func_of_ir(irf); + map_ok : ., mut = 1; + if sfi < 0 { map_ok = 0; } + else if r64(g_ir_func_name_idx, irf * 8) != fi_name(sfi) { map_ok = 0; } + else if ast_kind(fi_ast_node(sfi)) == EXPR_EXTERN { map_ok = 0; } + if map_ok == 0 { + // 映射漂移/extern 声明体(无可生成的体):保守判不纯 + if sfi >= 0 { w64(local, sfi * 8, 1); } + irf = irf + 1; continue; + } + istart := r64(g_ir_func_instr_start, irf * 8); + icnt := r64(g_ir_func_instr_count, irf * 8); + ii := istart; + loop { + if ii >= istart + icnt { break; } + op := iri_op(ii); + if purity_op_effect(op) != 0 { w64(local, sfi * 8, 1); } + if op == IR_CALL { + cf := find_func(iri_s3(ii)); + if cf < 0 { w64(local, sfi * 8, 1); } // 不可解析(runtime builtin)⇒ 不纯 + else { w64(callee, ii * 8, cf); } + } + if op == IR_INLINE { + cf2 := find_func(iri_s1(ii)); + if cf2 < 0 { w64(local, sfi * 8, 1); } + else { w64(callee, ii * 8, cf2); } + } + ii = ii + 1; + } + irf = irf + 1; + } + + // ── 2) unsafe 区 ⇒ 归属函数不纯(IR 无 unsafe opcode:效应藏在块内裸操作里; + // 归属 = 区起始节点落在哪个函数的节点区间)── + si : ., mut = 0; + loop { + if si >= g_sg_count { break; } + if r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_UNSAFE { + uf := df_func_of_node(r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART)); + if uf >= 0 { + usf := src_func_of_ir(uf); + if usf >= 0 { w64(local, usf * 8, 1); } + } + } + si = si + 1; + } + + // ── 3) 不动点:全不纯起点升纯(保守单调,语义见头注 ④)── + changed : ., mut = 1; + loop { + if changed == 0 { break; } + changed = 0; + irf = 0; + loop { + if irf >= g_ir_func_count { break; } + sf := src_func_of_ir(irf); + if sf >= 0 { + if r64(local, sf * 8) == 0 { + if r64(pure, sf * 8) == 0 { + ist2 := r64(g_ir_func_instr_start, irf * 8); + ic2 := r64(g_ir_func_instr_count, irf * 8); + allpure : ., mut = 1; + ii2 := ist2; + loop { + if ii2 >= ist2 + ic2 { break; } + c := r64(callee, ii2 * 8); + if c >= 0 { + if r64(pure, c * 8) == 0 { allpure = 0; break; } + } + ii2 = ii2 + 1; + } + if allpure != 0 { + w64(pure, sf * 8, 1); + changed = 1; + } + } + } + } + irf = irf + 1; + } + } + + // ── 4) 泛型源(无 IR 体)回填:源纯度 = 实例合取(无实例 = 0)── + fi : ., mut = 0; + loop { + if fi >= fcount { break; } + if fi_generic_count(fi) > 0 { + ninst : ., mut = 0; + allp : ., mut = 1; + gi : ., mut = 0; + loop { + if gi >= g_purity_inst_count { break; } + if r64(g_purity_inst, gi * 24) == fi { + ninst = ninst + 1; + nf := r64(g_purity_inst, gi * 24 + 16); + if nf >= 0 && nf < fcount { + if r64(pure, nf * 8) == 0 { allp = 0; } + } else { allp = 0; } + } + gi = gi + 1; + } + if ninst > 0 && allp != 0 { w64(pure, fi * 8, 1); } + } + fi = fi + 1; + } + + // ── 5) 写回 ── + fi = 0; + loop { + if fi >= fcount { break; } + fi_set_ispure(fi, r64(pure, fi * 8)); + fi = fi + 1; + } +} + +// 自测辅助:按名查纯度(-1 = 无此函数——与 0/1 区分,防「查不到」被当「不纯」)。 +fn fi_ispure_of(name_ni: int) -> int { + fi := find_func(name_ni); + if fi < 0 { return -1; } + return fi_ispure(fi); +} diff --git a/src/compiler/cir_cache.cr b/src/compiler/cir_cache.cr index c3c0fd43..fc152da8 100644 --- a/src/compiler/cir_cache.cr +++ b/src/compiler/cir_cache.cr @@ -7,15 +7,137 @@ // Magic header for .cir cache files // v12: invalidate older call flags, generic cloning/indexing, and yield ASTs. // v13: persist nested SG region metadata in per-function cache files. +// v14: 幽灵边播种修复(v7 Task 0 注 A 裁决)——快照内未产出 var 的 producer +// 槽语义随 grow_df_arrays 播种变更为恒 -1;旧快照(含 0 槽幽灵边 + 恢复 +// 路径不重播种)必须失效——cache miss = 无害重建。 // 注意:magic 位模式 = C1C1…(bytes);以 signed 十进制书写——hex 字面量 // 0xC1C1C1C1C1C1C1C1 超 i64 上界,会被词法溢出守卫拒绝(见 lexer P2 修复)。 +// v15(效应/纯度修正 Task 1):state 链(kind=1)不再随函数即时连接——改由 +// df_replay_state_chain 在 IR 生成结束后对成品图统一重建 ⇒ 快照里不再需要 +// (也不应)持久化链边。旧快照带链边 + 重建再连一次 = 重复链边,必须失效 +// (cache miss = 无害重建)。 +// v16(TODO #8 修复):参数槽区 16→64(dyn_arr.cr OFF_FI_PARAM_TYPES 扩容)—— +// 修复前编译 ≥18 形参函数时,越界写把 return_type/ast_node 踩坏,**且该坏 +// 状态已被写入 .cir 快照**(IR 体 46→28 instrs 等)。缓存键 = 源路径::函数名 + +// 纯 AST 指纹 ⇒ 同源文件在修复前后指纹相同 → 旧快照会被命中,把坏 IR 原样 +// 恢复成活产物(rc=0)——正是本修复要消灭的静默类。bump 使旧条目整体失效 +// (cache miss = 无害重建)。注意:本条只治「本仓旧快照」,键缺编译器身份 +// 的根因另见 TODO #5。 +// v17(TODO #5 修复):头部新增**编译器身份**字段(见下)。布局 = magic/ver/ +// identity/fp/sig/name_len/name…(相对 v16 整体后移 8B),旧条目读取即错位 ⇒ +// 必须 bump。v16→v17 的手工 bump 本身只覆盖「本仓旧条目」;身份字段起自动 +// 覆盖面(重建即失效)。 +// v18(不 bump——R2 P4 Task 4 / D15 的实测修正,见下):DF 节点内存记录 +// 64 → 72B 新增该项槽,但**快照盘记录仍 64B/节点**(第 9 槽不落盘)⇒ 格式 +// 与布局零变化 ⇒ 版本位不动。 +// 计划 D15 原写「`.cir` 快照入项槽并 CIR_CACHE_VER 17→18」,其前提 = 「不入槽 +// 则命中恢复路径与冷路径分歧」。**该前提实测不成立且方向相反**(本任务新增 +// `cir --dump-tk-terms` 通道实测,ptr_arith): +// 冷路径 terms=13 / bad_term=0;暖路径 terms=10 / **bad_term=329** +// ——暖路径所有恢复来的项引用域外。根因 = 项表(g_type_terms)是**进程内** +// 内容寻址表,emit 期按需追加(本语料冷路径 emit 新增 3 项)⇒ 快照里的项索引 +// 只在**写侧进程**的索引空间里成立;暖路径若跳过 emit(全命中)则项表更短, +// 恢复出的索引整体悬空——若日后有消费者,读到的就是错项(或越界读)= 正是 +// 本批要消灭的静默类。**且**「把 f(tk) 的纯函数值与其入参并存于同一记录」= +// 第二真源(本仓反复治过的病),分歧时盘面值静默胜出。 +// 修正(根因级)= **不落盘,装载时按 emit 的等价时点重派生**:load_cir_cache +// 逐节点(节点序 = emit 序)调 sh_tk_split(opcode, tk)——opcode/派生码本就 +// 在盘记录内(单源),派生值与冷路径**同项同索引**(项表按同序追加 ⇒ 两进程 +// 索引空间对齐,由 T4 用例的冷/暖 dump 逐行相等断言锁住)。 +// R2 P5 Task 2(D19 单槽化)后:盘上仍只有**一个** tk 字段(= 派生码,由 +// sh_dfn_code_of_slots 产出),装载侧由它同时重派生**两槽**(项引用 + 辅码)—— +// 盘面布局/版本位与 P4 逐字节相同(单槽化只在内存语义面)。 CIR_CACHE_MAGIC : int = -4485090715960753727; -CIR_CACHE_VER : int = 13; +CIR_CACHE_VER : int = 17; g_cir_write_buf : string, mut; g_cir_write_pos : int, mut; g_cir_write_cap : int, mut; +// === 编译器身份(TODO #5)=== +// 现象:缓存键 = 源路径::函数名 + 纯 AST 指纹(func_fingerprint/sig_fingerprint +// 只覆盖目标源),**无编译器身份分量**。编译器二进制重建后,字符串驻留序 +// (g_strs 的 intern 序)与快照内索引(var 的 irv_name / 指令 s1..s3 / 名字 +// 索引)不再一致,而键与指纹一字未变 ⇒ 旧条目被命中,旧序索引被当活产物 +// 使用(dump 变量名缺失/错位,rc=0 静默)。CIR_CACHE_VER 的手工 bump 是 +// 现状兜底,已经忘记过两次(14→15、15→16)——本字段即为其自动化。 +// +// 修复 = 头部写入**运行中编译器自身 ELF 的内容哈希**(/proc/self/exe 全文件 +// 单趟哈希),装载时比对:不等 = cache miss = 无害重建。不变量:**任何改变 +// 前端语义的重建都改变身份**(二进制变了,哈希必变)。 +// +// 为什么是「运行中二进制自哈希」而非「构建期源哈希常量」: +// * 自哈希捕获**全部**决定编译器语义的输入——src/compiler 清单 + 其顺序 + +// bootstrap Python 工具链 + rt.s + as/ld——源哈希只是其中一部分的代理, +// 漏掉任何一项就复现本单要消灭的静默类; +// * 无需构建管道改动/生成文件:其它编译路径(tests 直编 src/compiler、 +// full-bootstrap 的 corec2/corec3)不因缺一个生成文件而断; +// * 自哈希取内容而非路径 ⇒ 「同内容二进制」判为同身份(正确:语义同)。 +// 依赖(实测成立,判据在案):本仓 ELF 构建逐字节确定(两连建 cmp IDENTICAL +// ——本单修复前后两侧均实测;run.sh full-bootstrap 亦以 cmp 为判据)⇒ 同源 +// 重建身份不变,缓存命中面不退化。若某日构建变为非确定,症状 = 缓存恒 miss +// (性能面,非正确性面),由该判据暴露。 +// 退化路径(无 procfs / 读取失败):身份 = 0 ⇒ 两端点均关缓存,每次全量重建 +// ——绝不落「身份 0」条目(否则两个同样取不到身份的编译器互相当作同身份命中, +// 正是本单静默类复活)。实测:`unshare -rm` 把 tmpfs 挂到 /proc 掩掉 procfs 后 +// 编译 rc=0 且 **零条目落盘**;已删除(unlink)的编译器二进制反而仍可经 +// /proc/self/exe 打开取到原 inode 内容(身份照常成立)。 +// +// 与 CIR_CACHE_VER 的分工:VER = 手工粗粒度(格式/语义生成代,例如「快照里 +// 不该有链边」这类**格式**变更);身份 = 自动细粒度(编译器内容驱动,覆盖 +// 「同格式、不同前端行为」)。两者都必须匹配才命中——格式变更手工 bump, +// 行为变更由身份自动接管,不必再记得 bump。 +g_cir_compiler_id : int, mut = 0; +g_cir_compiler_id_ready : int, mut = 0; + +// 运行中编译器 ELF 的内容哈希(memoized)。0 = 取不到身份(无 procfs / +// 读取失败 / 空文件)——调用方按「身份不可证」处理(关缓存,见两端点)。 +fn cir_compiler_identity() -> int { + if g_cir_compiler_id_ready != 0 { return g_cir_compiler_id; } + g_cir_compiler_id_ready = 1; + g_cir_compiler_id = cir_hash_running_binary(); + return g_cir_compiler_id; +} + +// 单趟乘加哈希(FNV-1 64-bit 常数;本语言无 XOR 运算符——与 ir_gen.cr +// func_fingerprint 同族写法)。8 字节字一步(load64 = 单条未对齐小端读; +// corec 目标面即 x86-64 小端,字节序不影响「确定性」这一唯一要求)+ 余尾 +// 逐字节 + 末尾混入总长(长度不同必不同)。 +// 采样(本单判据,热身缓存,build/corec 1.32MB):逐字节形中位 +11.6ms/次 +// (小文件编译 17→29ms)→ 8 字节字粒度后 min 基准 +2~3ms/次(同件 15→17ms; +// 大件 src/compiler/main.cr 热身 5.5s 下不可分——采样噪声大于该值)。代价只在 +// **真正走到缓存**的进程里付一次(memoized:strace 实测热身全量编译 1 次 +// open("/proc/self/exe")),身份不可得/无条目可查时不付。 +// 非密码学用途(对侧不设敌手):它要区分的是「同一编译器的两次构建」,随机 +// 差异撞上 2^-64 忽略;能写缓存文件的对手本来就能写任意载荷。 +fn cir_hash_running_binary() -> int { + fd := syscall3(2, "/proc/self/exe", 0, 0); // open(O_RDONLY) + if fd < 0 { return 0; } + buf := alloc(65536); + h : ., mut = -3750763034362895579; // FNV-1 64 offset basis(signed i64) + total : ., mut = 0; + loop { + n := syscall3(0, fd, buf, 65536); // read(fd, buf, 65536) + if n <= 0 { break; } + i : ., mut = 0; + loop { + if i + 8 > n { break; } + h = h * 1099511628211 + load64(buf, i); + i = i + 8; + } + loop { + if i >= n { break; } + h = h * 1099511628211 + load8(buf, i); + i = i + 1; + } + total = total + n; + } + r1 := syscall3(3, fd, 0, 0); // close(fd) + if total == 0 { return 0; } + h = h * 1099511628211 + total; + return h; +} + // Count non-function regions belonging to one function. The function SG is // recreated by df_begin_func on a cache hit, so only nested records are stored. fn cir_cache_sg_count(node_start: int, node_count: int) -> int { @@ -63,6 +185,12 @@ fn cir_cache_sg_parent(sg_idx: int, node_start: int, node_count: int) -> int { // ir_fi: compact IR function index used for IR/DFG ranges // Returns 0 on success, -1 on failure. fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int { + // 身份不可证(无 procfs 等)⇒ 绝不落盘:否则身份 0 的条目会被另一个同样 + // 取不到身份的编译器当作「同身份」命中——即本单的静默类在退化路径复活。 + // 关缓存 = 每次都全量重建 = 正确性优先(见 cir_compiler_identity 头注)。 + cid := cir_compiler_identity(); + if cid == 0 { return -1; } + fd := syscall3(2, path, 577, 420); // O_WRONLY|O_CREAT|O_TRUNC, 0644 if fd < 0 { return -1; } @@ -83,8 +211,11 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int { // Serialize in memory and issue one write. The previous per-field writes // made a full self-host build perform millions of syscalls on its cache. - total_size : ., mut = 40 + name_len; + total_size : ., mut = 48 + name_len; // v17 头部 = magic/ver/identity/fp/sig/name_len total_size = total_size + 8 + var_count * 24; + // 节点盘记录 = 8 字段 × 8B = 64B(R2 P5 Task 2 起 = {op,dest,s1,s2,s3,**派生码**, + // first_edge,edge_count};第 9 槽 OFF_DF_AUX 不落盘——两槽装载时按派生码重派生, + // 见文件头 v18 注)。 total_size = total_size + 8 + node_count * 64; total_size = total_size + 8 + g_df_edge_count * 32; // v5: 4 fields incl. kind total_size = total_size + 8 + instr_count * 48; @@ -111,6 +242,7 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int { // Write header w64_cir(fd, CIR_CACHE_MAGIC); w64_cir(fd, CIR_CACHE_VER); + w64_cir(fd, cid); // v17: 编译器身份(重建即失效) w64_cir(fd, fp); w64_cir(fd, sig); w64_cir(fd, name_len); @@ -139,9 +271,15 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int { w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1)); w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2)); w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3)); - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK)); + // R2 P5 Task 2(D19):盘记录承载**派生码**(不是 40 槽——单槽化后 40 槽 = + // 进程内类型项引用,不得跨进程/跨复位,D20;辅码槽同理不入盘)。派生码逐节点 + // ≡ 单槽化前的混用码 ⇒ 盘面逐字节不变(布局与版本位零改动)。 + w64_cir(fd, sh_dfn_code_of_slots(r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX))); w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE)); w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT)); + // 项槽/辅码槽**不写盘**:两槽都是 (opcode, 派生码) 的纯函数,装载侧按 emit 的 + // 等价时点重派生(文件头 v18 注——实测快照携带进程内索引会在暖路径整体悬空)。 ni2 = ni2 + 1; } @@ -236,12 +374,72 @@ fn write_fd(fd: int, data: string, len: int) { } } +// === 快照装载读缓冲(R2 P5 Task 1:暖态 SIGSEGV 根因修复)=== +// 根因链(实测,自源语料 src/compiler/main.cr -O 0,seed 见报告 §1): +// ① 每函数快照的读侧原先 = read_file(path) = alloc(fsize+1),而 bump 分配器 +// (rt.s alloc)**不回收** ⇒ 946 个文件的读缓冲**累计驻留**(1,253,453,548B +// = 1.1674GiB); +// ② 单文件尺寸是 O(程序) 而非 O(函数)(快照含全程序边表 + 全程序字符串表, +// 实测 nod_s3(13 节点)文件 = 2.48MB,其中 98.7% 是 80100 条全程序边), +// 累计 1.1674GiB > 1GiB 堆(rt.s heap_start .space 1024*1024*1024); +// ③ alloc 耗尽返回 0(.Lalloc_oom)⇒ read_file 把 0 当 string 返回; +// ④ load_cir_cache 首行 str_len(0) → load64(0, -8) = SIGSEGV(暖态 rc=139; +// 冷态不读快照故 rc=0)——预存缺陷(P4 T5 §7-5 登记)。 +// 修法(根因级,非给长度加钳位):快照内容对装载是**瞬态**的——装完即弃, +// 无指针被保留(var 名以索引入表、字符串经 str_sub 拷贝后 str_intern、 +// 节点/指令字段逐值拷入全局数组)⇒ 单个复用缓冲按需增长,峰值 = 最大单文件 +// 尺寸(本语料实测最大 3,987,879B = 3.80MiB)而非全体累计。布局语义 = 与 read_file 返回的字符串 +// 逐位同:alloc(cap+8) 的长度头在 buf-8,每次读取后改写为 fsize+1 ⇒ +// str_len(g_cir_read_buf) == fsize,既有消费面(r64/str_sub)零改动。 +g_cir_read_buf : string, mut; +g_cir_read_cap : int, mut; // 数据区容量(不含 -8 处的 8B 长度头) + +// 读取快照文件到复用缓冲。返回文件长度;< 0 = 不可用(open 失败/空文件/短读/ +// 缓冲分配失败)——调用方按 cache miss 处理(三态纪律:载入失败 ⇒ 拒绝, +// 不得静默当空表)。快照恒为 save_cir_cache 单次 write 产出的常规文件 ⇒ +// fsize <= 0(空文件或伪文件)= 不可用,不做 read_file 的伪文件循环回退。 +fn cir_read_snapshot(path: string) -> int { + fd := syscall3(2, path, 0, 0); // open(O_RDONLY) + if fd < 0 { return -1; } + fsize := syscall3(8, fd, 0, 2); // lseek(fd, 0, SEEK_END) + if fsize <= 0 { + r0 := syscall3(3, fd, 0, 0); // close + return -1; + } + if fsize + 1 > g_cir_read_cap { + new_cap : ., mut = g_cir_read_cap; + if new_cap < 4096 { new_cap = 4096; } + loop { + if new_cap >= fsize + 1 { break; } + new_cap = new_cap * 2; + } + g_cir_read_buf = alloc(new_cap + 8); + g_cir_read_cap = new_cap; + } + r2 := syscall3(8, fd, 0, 0); // lseek(fd, 0, SEEK_SET) + nread := syscall3(0, fd, g_cir_read_buf, fsize); // read(fd, buf, fsize) + r3 := syscall3(3, fd, 0, 0); // close(fd) + // 缓冲不可得(alloc 耗尽 ⇒ g_cir_read_buf 仍为 0 ⇒ read 取 EFAULT)或短读 + // (并发截断等)⇒ 均**不可用** ⇒ miss:绝不拿半份/空载荷继续解析(静默错值 + // 类)。⚠ 此处刻意**不写** `buf == 0` 形态的空判——该字符串-整型混比在 + // Python bootstrap 侧(x86_64_stack_asm.py:168 按 _is_string_var 分派)会编成 + // `str_eq(buf, 0)` → `str_len(0)` 解引用 NULL-8 = 崩溃(与自举侧 int 比较 + // 语义不一致,登记见报告 §6);read 的 EFAULT 面已是同效且更稳的判据。 + if nread != fsize { return -1; } + w64(g_cir_read_buf, -8, fsize + 1); // 长度头 = fsize(str_len 口径同 read_file) + return fsize; +} + // Load a .cir cache file and restore DFG state. // func_idx is the function index used to verify the cached fingerprints // against the current AST. Returns 0 on success, -1 on failure (cache miss). fn load_cir_cache(path: string, func_idx: int) -> int { - data := read_file(path); - if str_len(data) < 48 { return -1; } + // 快照读入**复用缓冲**(不逐函数新分配——见 cir_read_snapshot 头注)。 + dlen := cir_read_snapshot(path); + if dlen < 0 { return -1; } + data := g_cir_read_buf; + // v17 头部 = 48B + 函数名(再至少 8B 载荷段头)。 + if dlen < 56 { return -1; } pos : ., mut = 0; // Validate header @@ -250,6 +448,14 @@ fn load_cir_cache(path: string, func_idx: int) -> int { ver := r64(data, pos); pos = pos + 8; if ver != CIR_CACHE_VER { return -1; } + // v17: 编译器身份——条目由另一个(或旧版)编译器二进制写入 ⇒ 其字符串 + // 驻留序/索引与本次运行不一致,而 fp/sig 只覆盖目标源(同源必同)⇒ 必须 + // 在此拒绝,否则下面恢复出的旧序索引即成活产物(TODO #5 静默类)。 + // 身份取不到(0)同样拒绝(save 侧对称地不落盘):身份不可证 = 不可命中。 + cid := cir_compiler_identity(); + entry_cid := r64(data, pos); pos = pos + 8; + if cid == 0 || entry_cid != cid { return -1; } + fp := r64(data, pos); pos = pos + 8; sig := r64(data, pos); pos = pos + 8; @@ -305,10 +511,23 @@ fn load_cir_cache(path: string, func_idx: int) -> int { w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1, r64(data, pos)); pos = pos + 8; w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2, r64(data, pos)); pos = pos + 8; w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3, r64(data, pos)); pos = pos + 8; - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK, r64(data, pos)); pos = pos + 8; + // R2 P5 Task 2(D19):盘上只有**一个** tk 字段(派生码);两槽皆由它**重派生** + // ——时点 = emit 的等价点(逐函数、逐节点序),拆分器单源 = sh_tk_split,故项表 + // 追加序与冷路径一致 ⇒ 冷/暖两态同项**同索引**(T4 用例逐行对拍)。项引用/辅码 + // **不读盘**(进程内项引用跨进程必悬空;盘上不存第二份 f(tk) 值 = 无第二真源)。 + tk_disk := r64(data, pos); pos = pos + 8; + // 装载侧变体(不置 D23 失败位——盘码可能落在暖进程更小的类型表之外,见 + // ty_shadow.cr 的 sh_tk_split_load 注;缓存命中绝不因此让编译失败)。 + sh_tk_split_load(r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE), tk_disk); + w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK, g_sh_slot_term); + w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX, g_sh_slot_aux); w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE, r64(data, pos)); pos = pos + 8; w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT, r64(data, pos)); pos = pos + 8; - // Record var producer + // Record var producer. 幽灵边修复(v7 注 A 裁决)缓存面:grow_df_arrays + // 对新增长区播种 -1(dyn_arr.cr 同款注释)——快照内未产出 var(参数等) + // 的 producer 槽恒 -1(修复前零页 = 0 =「节点 0」→ 缓存命中函数保留 + // 幽灵边);本处只覆写快照内真实定值的 var。旧快照由 CIR_CACHE_VER + // 13→14 失效。 dest := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST); if dest >= 0 { grow_df_arrays(dest + 1); diff --git a/src/compiler/corearch.cr b/src/compiler/corearch.cr index ee96b558..d03e13f0 100644 --- a/src/compiler/corearch.cr +++ b/src/compiler/corearch.cr @@ -2,6 +2,147 @@ // Backend: .ccr → ELF/assembly/SO // Supports: --elf (static), --shared (DSO), --link (dynamic linking) +// ═══════════════════════════════════════════════════════════════════ +// 注册契约最小面(内核抽取 Task 3,2026-09-10)——实例声明表 + 表驱动引导 +// ═══════════════════════════════════════════════════════════════════ +// 蓝图(docs/superpowers/specs/2026-09-09-corearch-rewrite-design.md §1.1③): +// 实例 = {能力声明、资源域/代数参数、请求分派形态},注册契约 = 内核与实例的 +// 唯一耦合面(本文件 = 内核引导 + 实例注册侧)。Core 无高阶函数 → 能力声明 = +// 数据表(g_instance_decl,行 = 声明字段),表驱动分派 = 引导决策逐点查询活动 +// 实例行(不设虚表/回调——查询即分派)。本任务 = 最小面:声明表 + 引导收敛 +// (资源域代数参数化(判定读 g_opt_meta 的耦合)/双向契约(home 回填)= 蓝图 +// 后续步骤——范围克制注,本任务不做)。 +// +// 声明行字段(10 × i32;INST_DECL_STRIDE = 40,行序 = id 序): +// id 实例标识(INST_X86 = 0 默认实例;INST_TABLE = 1) +// name str_idx(str_intern 内联名——instance_lookup 查表键;有效期 = +// 装载前引导选择期:load_ccr 会重置 g_strs 重载 .ccr STR 段, +// 本任务无装载后读名路径——见 instance_decl_init 注) +// opt_min/opt_max 声明优化级别窗口(语义侧职责窗口) +// allow_table 是否接受 HIT 表输入(表管线业务门) +// allow_link 是否允许 --link/--shared(M2-1 拒绝门) +// needs_alloc 生产路径是否执行寄存器分配职责(级别 ≥ 2 时) +// needs_verify 生产路径是否执行一致性自检职责(alloc 后) +// needs_eviction 判定③ 形式声明(x86 = 0——静态放置无驱逐事件) +// needs_call_sites 判定④ 形式声明(x86 = 0——callee-saved 平凡满足) +// ——③④ 引擎不实现(用户裁决——设计 spec §3.2:字段 = 能力声明承载,契约 +// 完整性 = 字段存在 + 文档;needs_* = 1 的未来实例出现时 = 引擎扩展的注册 +// 契约演进点)。行数据注:表模式路径行同 {0, 0}(表路径无驱逐/调用点事件 +// ——恒 O0 直线路径)。 +// +// 表数据 = x86 实例声明 + 表模式路径声明: +// · x86(原生路径) :窗口 [0,3](现 CLI 钳制常数即该窗口值),allow_link +// = 1,O2 职责全声明(级别 ≥ 2 → alloc + verify—— +// 旧直写条件)。 +// · table(表模式路径雏形):M1 表驱动直线路径 = 独立实例路径雏形(蓝图 §2 +// 表裁决)。恒 O0 语义/无 O2 组合验证 = needs_alloc/needs_verify = 0 → +// O2 职责永不执行(旧 hit_table_active 门的声明化);--table × +// --link/--shared 显式拒绝保持 = allow_link = 0。注:CLI 优化级别值透传 +// (不按表窗口 [0,0] 钳制——发射侧 O1+ 帧布局沿 g_opt_level 现状保持; +// 值域钳制按实例参数化 = 蓝图后续)。 +// +// 引导收敛(现三路分派 = --table×opt 门 / --opt-level / 调试 flag → 声明查询): +// corearch_main 读 flag → instance_select()(查表选实例)→ g_active_instance +// 固定 → 拒绝门/表管线门/O2 职责门逐点查询活动实例行字段。调试 flag 通道 +// (regalloc_debug_dispatch)= 请求分派形态(实例消费内核判定服务的方式)—— +// 六分支顺序与输出保持(--check-regalloc 顺序保序:强制 O2 → alloc → +// 看门狗 meta_reg_assign_total → 注入钩子×3 → regalloc_verify_all → 摘要/rc)。 + +INST_X86 : int = 0; +INST_TABLE : int = 1; +INST_DECL_STRIDE : int = 40; // 行 = 10 × i32(LE) +INST_OFF_ID : int = 0; +INST_OFF_NAME : int = 4; +INST_OFF_OPT_MIN : int = 8; +INST_OFF_OPT_MAX : int = 12; +INST_OFF_ALLOW_TABLE : int = 16; +INST_OFF_ALLOW_LINK : int = 20; +INST_OFF_NEEDS_ALLOC : int = 24; +INST_OFF_NEEDS_VERIFY : int = 28; +INST_OFF_NEEDS_EVICTION : int = 32; // 判定③ 形式声明(引擎不实现——§3.2) +INST_OFF_NEEDS_CALL_SITES : int = 36; // 判定④ 形式声明(引擎不实现——§3.2) + +g_instance_decl : string, mut; // 实例声明表(行 × INST_DECL_STRIDE 字节) +g_instance_count : int, mut; +g_instance_cap : int, mut; +g_active_instance : int, mut; // 引导选中实例行(= id;instance_select 设定) + +fn grow_instance_decl(needed: int) { + if needed < g_instance_cap { return; } + nc : ., mut = g_instance_cap * 2; if nc < 2 { nc = 2; } if nc < needed { nc = needed; } + nb := alloc(nc * INST_DECL_STRIDE); + _dyncpy(g_instance_decl, g_instance_cap * INST_DECL_STRIDE, nb); + g_instance_decl = nb; g_instance_cap = nc; +} + +fn instance_decl_add(id: int, name_idx: int, opt_min: int, opt_max: int, + allow_table: int, allow_link: int, needs_alloc: int, + needs_verify: int, needs_eviction: int, needs_call_sites: int) { + grow_instance_decl(g_instance_count + 1); + off : ., mut = g_instance_count * INST_DECL_STRIDE; + w32(g_instance_decl, off + INST_OFF_ID, id); + w32(g_instance_decl, off + INST_OFF_NAME, name_idx); + w32(g_instance_decl, off + INST_OFF_OPT_MIN, opt_min); + w32(g_instance_decl, off + INST_OFF_OPT_MAX, opt_max); + w32(g_instance_decl, off + INST_OFF_ALLOW_TABLE, allow_table); + w32(g_instance_decl, off + INST_OFF_ALLOW_LINK, allow_link); + w32(g_instance_decl, off + INST_OFF_NEEDS_ALLOC, needs_alloc); + w32(g_instance_decl, off + INST_OFF_NEEDS_VERIFY, needs_verify); + w32(g_instance_decl, off + INST_OFF_NEEDS_EVICTION, needs_eviction); + w32(g_instance_decl, off + INST_OFF_NEEDS_CALL_SITES, needs_call_sites); + g_instance_count = g_instance_count + 1; +} + +fn instance_decl_init() { + // 幂等(防重复调用——引导只调一次,注册函数最小面)。声明字段恒非负 + // (0/1/3/str idx)——r32 零扩展读安全(符号修正歧义只涉及负值)。 + // name 内联时机注:本函数跑在 load_ccr 前(引导期)——str idx 依当时 + // g_strs 内容(等值串恒同 idx——str_intern 去重,位置无关);load_ccr + // 重置 g_strs 重载 .ccr STR 段后这些 idx 不再指向内联名。本任务消费面 + // (instance_select)全在选择期(装载前),无装载后读名路径——届时注册 + // 校验若读名须装载后重内联(蓝图后续)。 + if g_instance_count > 0 { return; } + // ③④ 行数据 = {0, 0}(x86:静态放置无驱逐事件 + callee-saved 平凡满足; + // 表模式路径:恒 O0 直线路径无驱逐/调用点事件——同 {0, 0};引擎不实现, + // 设计 spec §3.2——字段 = 声明完整性)。 + instance_decl_add(INST_X86, str_intern("x86"), 0, 3, 0, 1, 1, 1, 0, 0); + instance_decl_add(INST_TABLE, str_intern("table"), 0, 0, 1, 0, 0, 0, 0, 0); +} + +fn inst_id(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_ID); } +fn inst_name(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_NAME); } +fn inst_opt_min(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_OPT_MIN); } +fn inst_opt_max(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_OPT_MAX); } +fn inst_allow_table(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_ALLOW_TABLE); } +fn inst_allow_link(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_ALLOW_LINK); } +fn inst_needs_alloc(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_NEEDS_ALLOC); } +fn inst_needs_verify(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_NEEDS_VERIFY); } +fn inst_needs_eviction(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_NEEDS_EVICTION); } +fn inst_needs_call_sites(i: int) -> int { return r32(g_instance_decl, i * INST_DECL_STRIDE + INST_OFF_NEEDS_CALL_SITES); } + +fn instance_lookup(name_idx: int) -> int { + // 按名查行(name = str_idx,str_intern 去重 → 等值串恒同 idx)。 + // 未找到返回 -1(构造保证不触发——两行两名)。 + i : ., mut = 0; + loop { + if i >= g_instance_count { break; } + if inst_name(i) == name_idx { return i; } + i = i + 1; + } + return -1; +} + +fn instance_select() -> int { + // 引导选择:读 flag → 实例名 → 查表 → 行 idx(行序 = id 序 → 行 = id)。 + // 选择旗标 = --table 值存在性(与装载条件同源);兜底 INST_X86 = 默认 + // 实例(防御性——lookup 未命中不掩构造错误,退默认路径)。 + nm : ., mut = "x86"; + if str_len(cli_get("table")) > 0 { nm = "table"; } + r := instance_lookup(str_intern(nm)); + if r < 0 { return INST_X86; } + return r; +} + fn init_backend_arrays() { g_x86_var_count = 0; g_x86_stack_size = 0; g_x86_func_idx = 0; g_x86_is_enum_count = 0; g_x86_var_cap = 0; g_x86_is_enum_cap = 0; g_stack_map = ""; } @@ -14,6 +155,134 @@ fn split_links(val: string) { start = i + 1; } i = i + 1; } } +// regalloc 移后端(2026-09-07):数据面/判定调试通道随编码决策层迁入—— +// 载体 = corec cir 原隐藏标志的 corearch 同名版本(load .ccr 后内存态自算, +// 载入 NOD 流 = pre-CSE 流,与 corec 原 dump 语义同源)。返回:0 = 未请求 +// (正常发射路径继续);1 = 已处理且通过;2 = 已处理且违反/失败。 +// 注册契约角色(Task 3):本函数 = 请求分派形态——实例经 flag 通道消费内核 +// 判定服务(dump-entries/dump-coexist = 诊断通道;check-regalloc/inject-* = +// 判定服务 + 机器侧注入钩子)的入口。分支顺序与输出保持(行为不变): +// --check-regalloc 保序 = 强制 O2 → alloc_registers → 看门狗 +// meta_reg_assign_total → 注入钩子×3 → regalloc_verify_all → 摘要/rc。 +fn regalloc_debug_dispatch() -> int { + if cli_has("dump-entries") != 0 { + compute_live_ranges(); + dump_entries_summary(); + return 1; + } + if cli_has("inject-coexist-oob") != 0 { + compute_live_ranges(); + if inject_coexist_oob() != 0 { return 2; } + return 1; + } + if cli_has("dump-coexist") != 0 { + compute_live_ranges(); + dump_coexist_summary(); + return 1; + } + if cli_has("dump-regassign") != 0 { + // 自算后逐对输出 var→reg(尊重 --opt-level 门:分配 = O2,O0/O1 → 空) + if g_opt_level >= 2 { alloc_registers(); } + mi : ., mut = 0; + loop { + if mi >= g_opt_meta_count { break; } + mo := mi * OPT_META_STRIDE; + if r32(g_opt_meta, mo) == OPT_KEY_REG_ASSIGN { + cnt := r32(g_opt_meta, mo + 8); + di : ., mut = 0; + loop { + if di >= cnt { break; } + print("regassign: "); print(int_str(r32(g_opt_meta, mo + 12 + di * 8))); + print(" "); println(int_str(r32(g_opt_meta, mo + 16 + di * 8))); + di = di + 1; + } + } + mi = mi + 1; + } + return 1; + } + if cli_has("check-regalloc") != 0 { + saved_opt := g_opt_level; + g_opt_level = 2; + alloc_registers(); + // 看门狗行:REG_ASSIGN 对总数(rc>0 = 寄存器真实分配实证,防回退—— + // 分配在 corearch 自算后计数 = 自算块;.ccr 不再带 corec 分配结果) + print("regalloc-assign: "); print(int_str(meta_reg_assign_total())); + println(" pairs"); + if cli_has("inject-home-conflict") != 0 { inject_home_conflict(); } + if cli_has("inject-reg-conflict") != 0 { inject_reg_conflict(); } + if cli_has("inject-read-gap") != 0 { inject_read_gap(); } + nv := regalloc_verify_all(); + g_opt_level = saved_opt; + print("regalloc-consistency: funcs "); print(int_str(g_ir_func_count)); + print(" violations "); println(int_str(nv)); + if nv != 0 { return 2; } + return 1; + } + return 0; +} + +// --dump-objects 调试通道(内核完备 Task 1 测试载体):实例侧薄通道—— +// 经内核对象面访问器(nod_op/nod_dest/nod_s1-3/nod_tk + nod_edge_first/count +// + v7_edge_to/v7_edge_kind——ent_kernel.cr)输出载入对象:逐节点语义字段 + +// 邻接域 + 配方出边遍历(Task 4 函数化收敛:出边遍历 = 内核配方查询封装 +// nod_inputs 调用——内联遍历自本通道搬移,dump 输出逐字节同,判据 = +// test_ccr_v7 对象面断言 + --dump-objects 语料 byte-compare)。 +// 行格式契约见 tests/selfhost/test_ccr_v7.py:parse_object_dump。 +fn dump_object_surface() { + print("objects: "); print_i(g_v7_nod_count); println(""); + ni : ., mut = 0; + loop { + if ni >= g_v7_nod_count { break; } + print("nod "); print_i(ni); + print(" op "); print_i(nod_op(ni)); + print(" dest "); print_i(nod_dest(ni)); + print(" s1 "); print_i(nod_s1(ni)); + print(" s2 "); print_i(nod_s2(ni)); + print(" s3 "); print_i(nod_s3(ni)); + print(" tk "); print_i(nod_tk(ni)); + print(" fe "); print_i(nod_edge_first(ni)); + print(" ec "); print_i(nod_edge_count(ni)); + println(""); + nod_inputs(ni); // 配方输入遍历(出边记录行——内核 nod_inputs 封装) + ni = ni + 1; + } +} + +// --dump-nod-items(R2 P6 Task 3 β 读回通道):逐节点打印 NOD 项索引 → TYPE 段项表 +// 取项 → `tt_atom_of_term` → 原子行。这是「项跨进程可解析」的**可观测证据**: +// 头行给计数(with_item = 带项节点数——非空转门),正文逐节点给 +// `{idx, atom}` 供测试断言 `atom == code`(idx ≥ 0 时)。load_ccr 已做同款**硬校验** +// (域外/不一致 ⇒ 拒绝),本通道是它的显式读出面。不触发发射路径(返回 0 早退)。 +fn dump_nod_items() { + with_item : ., mut = 0; + ni : ., mut = 0; + loop { + if ni >= g_v7_nod_count { break; } + if nod_item(ni) >= 0 { with_item = with_item + 1; } + ni = ni + 1; + } + print("[nod-items] nodes="); print_i(g_v7_nod_count); + print(" with_item="); print_i(with_item); + print(" terms="); print_i(tt_count()); + print(" rows="); print_i(g_type_count); + println(""); + nj : ., mut = 0; + loop { + if nj >= g_v7_nod_count { break; } + itv := nod_item(nj); + at : ., mut = -1; + if itv >= 0 { at = tt_atom_of_term(itv); } + print("item "); print_i(nj); + print(" op "); print_i(nod_op(nj)); + print(" code "); print_i(nod_tk(nj)); + print(" idx "); print_i(itv); + print(" atom "); print_i(at); + println(""); + nj = nj + 1; + } +} + fn corearch_main() -> int { cli_init("corearch", "Core architecture backend"); cli_flag_bool("elf", "", "Output ELF binary (default)"); @@ -21,12 +290,67 @@ fn corearch_main() -> int { cli_flag_bool("static", "", "Static linking (embed runtime)"); cli_flag("link", "l", "Comma-sep .so files, or 'auto' for ~/.core/lib/"); cli_flag("output", "o", "Output path"); - cli_flag("opt-level", "O", "Optimization level (0-3, default=0)"); + cli_flag("opt-level", "O", "Optimization level (0-3, default=0) — O2: load 后自算寄存器分配 + 一致性自检(emit 前,违反 = 编译错误)"); + cli_flag("table", "", "HIT table file (load & emit mapped ops through it)"); + cli_flag("hit-events-file", "", "Hidden debug: inject text event stream file (dump-events inverse; skips lowering — template compare test channel, M2a Task 2)"); + cli_flag_bool("dump-events", "", "Dump lowered HIT event stream + const pool"); + cli_flag_bool("dump-table", "", "Hidden debug: dump loaded HIT event/proj/step/runtime state (schema v2 read-back test channel)"); + // regalloc 移后端:数据面/判定调试通道(corec cir 原载体随迁——同名 flag) + cli_flag_bool("dump-entries", "", "Hidden debug: versioned entries summary (regalloc 移后端 test channel)"); + cli_flag_bool("dump-coexist", "", "Hidden debug: coexistence summary (regalloc 移后端 test channel)"); + cli_flag_bool("dump-regassign", "", "Hidden debug: per-pair var->reg dump after O2 self-alloc (regalloc 移后端 test channel)"); + cli_flag_bool("check-regalloc", "", "Hidden debug: O2-forced alloc + regalloc consistency self-check (regalloc 移后端 test channel)"); + cli_flag_bool("inject-home-conflict", "", "Hidden debug: inject coexisting entries onto same home slot, then verify (test hook)"); + cli_flag_bool("inject-reg-conflict", "", "Hidden debug: inject fake var->reg pair colliding with a real one, then verify (test hook)"); + cli_flag_bool("inject-read-gap", "", "Hidden debug: truncate last version interval to def point, then verify (test hook)"); + cli_flag_bool("inject-coexist-oob", "", "Hidden debug: probe entries_coexist with OOB indices (GC-1 test hook)"); + cli_flag_bool("dump-objects", "", "Hidden debug: dump loaded NOD/EDG semantic objects via object-surface accessors (内核完备 Task 1 test channel)"); + cli_flag_bool("dump-nod-items", "", "Hidden debug: dump loaded NOD item indices resolved through the TYPE segment term table (R2 P6 Task 3 β read-back channel)"); + cli_flag_bool("dump-types", "", "Hidden debug: dump loaded TYPE segment content (row table + term DAG + judgment probe; R2 P4 Task 2 read-back channel)"); + cli_flag_bool("dump-ifaces", "", "Hidden debug: dump loaded IFACE segment content (entry table + shapes + user ifaces + impls + method table + cross-segment term probe; R2 P4 Task 3 read-back channel)"); if cli_parse() != 0 { return 1; } + // 注册契约引导(Task 3):读 flag → 查表选实例(--table 值存在 → 表模式 + // 路径实例;否则 x86 原生实例)——后续拒绝门/表管线门/O2 职责门逐点查询 + // g_active_instance 声明行字段(表驱动分派,行为不变)。 + instance_decl_init(); + g_active_instance = instance_select(); + // 拒绝门(M2-1 显式拒绝保持——声明化):表实例声明 allow_link = 0 → + // 不接受 --link/--shared。表模式池 mov [rip+disp] 的 disp 由 elf.cr 按池槽 + // 回填,链接路径(ctx 重布局重定位用户代码段)下指向未经测试(M1 计划 + // 偏差 #2)。显式拒绝(stderr + exit 1)优于未测组合。消息/顺序不变。 + // 测试断言见 tests/selfhost/test_hit_table.py:test_reject_table_with_link_shared。 + if inst_allow_link(g_active_instance) == 0 { + if cli_has("shared") != 0 || cli_has("link") != 0 || str_len(cli_get("link")) > 0 { + em := "error: --table cannot be combined with --link/--shared (pool mov rip disp untested under ctx relayout)\n"; + syscall3(1, 2, em, str_len(em)); + return 1; } } g_opt_level = 0; ol : ., mut = cli_get("opt-level"); - if str_len(ol) > 0 { g_opt_level = str_int(ol); if g_opt_level > 3 { g_opt_level = 3; } if g_opt_level < 0 { g_opt_level = 0; } } + if str_len(ol) > 0 { + g_opt_level = str_int(ol); + // 值域钳制窗口 = x86 实例声明窗口 [0,3](默认实例——旧钳制常数即该 + // 窗口值;级别消毒与实例选择正交)。表实例声明窗口 [0,0] = 语义侧 + // 职责窗口(恒 O0——经 needs_alloc/needs_verify = 0 的职责门强制), + // CLI 级别值透传发射侧(O1+ 帧布局沿 g_opt_level 现状保持)。 + if g_opt_level > inst_opt_max(INST_X86) { g_opt_level = inst_opt_max(INST_X86); } + if g_opt_level < inst_opt_min(INST_X86) { g_opt_level = inst_opt_min(INST_X86); } + } + // --table(M1 Task 2):load 成功 → 表模式继续(表映射 op 走 emit_instr_tabled, + // 未映射落旧路径 = 混合模式);失败即退出。load 成功打印计数供测试断言。 + // 装载条件 = 实例选择同源旗标(选择已把表实例置活动——allow_table = 1)。 + tbl : ., mut = cli_get("table"); + if str_len(tbl) > 0 { + if load_hit_table(tbl) != 0 { return 1; } + g_hit_tabled_count = 0; + print("hit table loaded: "); + print_i(g_hit_event_count); + println(" events"); + // --dump-table(schema v2 读回测试通道):加载态 proj/步/runtime 结构 + // dump(hit.cr helper 只读表状态)。测试断言见 + // tests/selfhost/test_hit_table.py:test_v2_fixture_multi_step_sections。 + if cli_has("dump-table") != 0 { hit_dump_table_state(); } + } if cli_arg_count() < 1 { println("usage: corearch [options]"); println(" --elf ELF binary (default: dynamic)"); @@ -35,6 +359,8 @@ fn corearch_main() -> int { println(" --link auto link ~/.core/lib/*.so (default)"); println(" --link s1,s2 link specific .so files"); println(" -o FILE output path"); + println(" --table FILE load HIT table; emit mapped ops through it (M1: int sub)"); + println(" --dump-events dump lowered HIT event stream + const pool"); return 1; } src_path := cli_arg(0); @@ -51,6 +377,119 @@ fn corearch_main() -> int { if r != 0 { println("error: invalid .ccr file"); return 1; } init_backend_arrays(); + // --dump-types(R2 P4 Task 2 读回通道):载入 TYPE 段重建后的行表/项 DAG 打印 + // ——与 corec 侧 `ccr --dump-types` 共用同一条打印路径(ccr_io.cr 的 + // ccr_type_surface_dump;跨进程逐行对拍 + probe 行 = 判定原语同值)。不依赖 + // 线性流(在被重建前返回 0——不触发发射路径)。 + if cli_has("dump-types") != 0 { + ccr_type_surface_dump(); + return 0; + } + + // --dump-ifaces(R2 P4 Task 3 读回通道):载入 IFACE 段重建后的条目表/形状表/ + // 接口表/impl 边/方法表打印——与 corec 侧 `ccr --dump-ifaces` 共用同一条打印 + // 路径(ccr_io.cr 的 ccr_iface_surface_dump;跨进程逐行对拍 + ifaceprobe 行 = + // 跨段项索引在重建项表上的解析同值)。同样在被重建前返回 0(不触发发射路径)。 + if cli_has("dump-ifaces") != 0 { + ccr_iface_surface_dump(); + return 0; + } + + // --dump-nod-items(R2 P6 Task 3 β 读回通道):NOD 项索引 → TYPE 段项表取项 → + // `tt_atom_of_term` → 原子行。与上两通道同族:TYPE 段已在 load_ccr 内重建 + // (⇒ 项表可用),且在 `build_linear_schedule()` **之前**返回 0(不触发发射路径)。 + // load_ccr 的 TYPE 段后一致性 pass 已把「域外/不一致 ⇒ 拒绝」做成硬闸;本通道 + // 是它的显式读出面(可观测证据:项跨进程可解析 + 非空转计数)。 + if cli_has("dump-nod-items") != 0 { + dump_nod_items(); + return 0; + } + + // 内核完备 Task 1(调度重建移实例):loader 只产语义对象——线性流 + // (g_ir_instrs)重建 = 实例事务(build_linear_schedule,regalloc.cr—— + // 重建段自 load_ccr 纯搬移,产物逐字节一致),load 成功后、分派/发射前 + // 必须调用(ELF 发射/dump-entries/check-regalloc 全消费线性流)。 + build_linear_schedule(); + + // --dump-objects(Task 1 测试通道——对象面配方可读断言载体,见 + // tests/selfhost/test_ccr_v7.py:test_v7_object_surface_recipe_readable): + // dump 分支在重建后、发射前(返回 0——不触发 O2 职责门/发射路径)。 + if cli_has("dump-objects") != 0 { + dump_object_surface(); + return 0; + } + + // regalloc 移后端(R1a/R3,2026-09-07):O2 分配 + 一致性判定归位 corearch + // ——load 后自算自检(.ccr 不再传 REG_ASSIGN/ENT,D-1=Y)。违反 = 编译错误。 + // O2 职责门(Task 3 声明化):alloc + verify = 实例声明能力——needs_alloc/ + // needs_verify(x86 = 1/1 → 级别 ≥ 2 时执行;表实例 = 0/0 → O2 组合验证 + // 永不执行——M1 表驱动直线路径恒 O0 语义,与旧 hit_table_active 门等价: + // 此刻实例 = 装载态恒等——--table 装载成功才达此处)。 + dd := regalloc_debug_dispatch(); + if dd == 1 { return 0; } + if dd == 2 { return 1; } + if g_opt_level >= 2 && inst_needs_alloc(g_active_instance) != 0 { + alloc_registers(); + if inst_needs_verify(g_active_instance) != 0 { + // 判定:成功静默(自检通过不打扰构建输出);违反 = 诊断已打印 + 拦截 + if regalloc_verify_all() != 0 { return 1; } + } + } + + // --table(M1 Task 3):表模式 → 先降低(IR 直线子集 → 事件流 + 常量池)。 + // 超子集 op → 'needs more events' 错误 exit 1(发射前拒绝);成功 → 事件流 + // 供 emit_instr_tabled 消费(elf.cr 发射循环不变)。--dump-events 调试 dump。 + // --hit-events-file(M2a Task 2 注入测试通道):文本事件流文件取代降低—— + // 构造 g_hit_ev_stream/g_hit_pool + 指令映射,跳过 lower 直接 emit。测试专用 + // (真实构建路径不启用;与 --inject-* 通道同哲学)。须与 --table 同用。 + // 表管线业务门(Task 3 声明化)= 活动实例声明 allow_table(表实例 = 1): + // 此刻实例与装载态恒等(--table 装载成功才达此处——load 失败已提前退出, + // 未给 --table 则实例 = x86 且无任何装载路径)——旧 hit_table_active() 门 + // 的声明等价物。 + dump_ev : ., mut = cli_has("dump-events"); + inj_path : ., mut = cli_get("hit-events-file"); + if str_len(inj_path) > 0 && inst_allow_table(g_active_instance) == 0 { + println("error: --hit-events-file needs --table (events emit through table projections)"); + return 1; } + g_hit_inject_active = 0; + if inst_allow_table(g_active_instance) != 0 { + if str_len(inj_path) > 0 { + g_hit_inject_active = 1; + if hit_inject_events(inj_path) != 0 { return 1; } + } else { + if hit_lower_program() != 0 { return 1; } + } + if dump_ev != 0 { + print("hit events lowered: "); print_i(g_hit_ev_count); println(""); + ei : ., mut = 0; + loop { + if ei >= g_hit_ev_count { break; } + ev_id := hit_ev_id(ei); + es := hit_event_lookup(ev_id); + print(" ev "); + if es < 0 { print("?"); } else { print(hit_event_name(es)); } + print(" dst="); print_i(hit_ev_dst(ei)); + print(" s1="); + if hit_ev_flags(ei) % 2 == 1 { print("pool"); print_i(hit_ev_s1(ei)); } + else { print_i(hit_ev_s1(ei)); } + print(" s2="); + if hit_ev_flags(ei) / 2 % 2 == 1 { print("pool"); print_i(hit_ev_s2(ei)); } + else { print_i(hit_ev_s2(ei)); } + println(""); + ei = ei + 1; } + // 池值行(M2a Task 2:注入文件 pool 行的 dump 逆面——值域随槽序) + print("hit pool entries: "); print_i(g_hit_pool_count); println(""); + if g_hit_pool_count > 0 { + print("hit pool:"); + hpi : ., mut = 0; + loop { + if hpi >= g_hit_pool_count { break; } + print(" "); print_i(r64(g_hit_pool, hpi * 8)); + hpi = hpi + 1; } + println(""); } + } + } + emit_so := cli_has("shared"); link_val := cli_get("link"); out_path := cli_get("output"); @@ -161,4 +600,16 @@ fn corearch_main() -> int { syscall3(1, fd, g_elf_buf, sz); syscall3(3, fd, 0, 0); } print(" -> "); println(out_path); + // 表模式总结:g_hit_tabled_count = 表驱动实际发射事件条数(instr.cr 计数—— + // add 反减 = 2 事件/指令)——「events emitted」语义准确(M2-2b 改名)。 + if str_len(tbl) > 0 { + print("hit table: "); print_i(g_hit_tabled_count); println(" events emitted"); + } + // 注入通道(测试专用):事件须全部经表发射——预检拒绝(已打印)会使计数 + // 短于注入行数 = 注入文件错误 → exit 1(不静默落旧路径掩盖)。 + if g_hit_inject_active != 0 && g_hit_tabled_count != g_hit_ev_count { + print("error: HIT events: "); print_i(g_hit_tabled_count); + print(" of "); print_i(g_hit_ev_count); + println(" events emitted via table (injection file event not emittable)"); + return 1; } return 0; } diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index ae1584db..2308030b 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -81,29 +81,22 @@ fn sg_pop() { // Loop termination dependency: the region exit node (last node created in // the region, i.e. the exit label) must happen after the region's last // side effect — a VSDG state edge enforcing loop-exit ordering. - kind := r64(g_sgs, idx * ESZ_SG + OFF_SG_KIND); - if kind == SG_LOOP || kind == SG_FOR { - last_node := g_df_node_count - 1; - if last_node >= r64(g_sgs, idx * ESZ_SG + OFF_SG_NSTART) { - // The termination-edge source must be inside the region. When the - // loop body is pure, g_last_state_node is a pre-loop store - // (created before the region) — linking it would claim the loop - // exit follows a side effect the region does not contain. - if g_last_state_node >= r64(g_sgs, idx * ESZ_SG + OFF_SG_NSTART) { - df_add_edge_kind(g_last_state_node, last_node, 1); // termination dependency - } - // Advance the state-chain head to the region exit node: side - // effects after the loop must depend on loop termination (spec - // §4.2: a loop that never terminates must terminate the graph). - g_last_state_node = last_node; - } - } + // 循环终止依赖(kind=1)与链头推进**不在本处连接**——state 链统一在 + // 全部 IR 生成结束后重建(df_replay_state_chain;时点理由见该函数头注): + // 被调函数真纯度只有拿到全程序 IR 体才能算,链不能再边生成边连。 g_cur_sg = r64(g_sgs, idx * ESZ_SG + OFF_SG_PARENT); } // --- Node creation --- -fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, type_kind: int) -> int { +// R2 P5 Task 2(D19 单槽化):`tk_term` = 类型项引用(g_type_terms 行号;-1 = 无项)、 +// `tk_aux` = 辅码(旗标/宽度/计数/不可入项的原始行码;0 = 无),由**调用方** +// (唯一调用点 = ir_gen.cr 的 emit)经 `sh_tk_split` 派生后传入。本函数仍属 corec +// 侧(build_selfhost_native.py 的 corec_files),但**不引用任何前端/桥接符号** +// ——按「共享文件零前端依赖」纪律留余量(dataflow.cr 若某日并入 corearch 清单, +// 本函数零改动即可链接)。参数语义 = 「存储」:本函数只落槽,不解释、不校验、 +// 不派生(派生规则单源 = ty_shadow.cr 的 sh_tk_split;码由 sh_dfn_code_of_slots 派生)。 +fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, tk_term: int, tk_aux: int) -> int { nid := g_df_node_count; grow_df_nodes(nid + 1); grow_df_node_region(nid + 1); @@ -113,7 +106,8 @@ fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, type_ w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S1, src1); w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S2, src2); w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S3, src3); - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_TK, type_kind); + w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_TK, tk_term); // R2 P5 Task 2:类型项引用 + w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_AUX, tk_aux); // R2 P5 Task 2:辅码 w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_FIRST_EDGE, -1); w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_EDGE_COUNT, 0); g_df_node_count = nid + 1; @@ -125,9 +119,11 @@ fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, type_ } // Add edges for src fields that are IR variables (based on opcode) - df_connect_srcs(nid, opcode, src1, src2, src3, type_kind); - // VSDG state chain: side-effecting nodes are ordered by state edges - df_connect_state(nid, opcode, src3); + // 第 6 参 = 辅码(IR_BOUNDS_CHECK 的 `!= 0` = 动态上限旗标——单槽化后旗标在 + // 辅码槽;见 df_connect_srcs 的 IR_BOUNDS_CHECK 分支)。 + df_connect_srcs(nid, opcode, src1, src2, src3, tk_aux); + // VSDG state chain(kind=1)不在此连接——见 df_replay_state_chain 头注 + // (时点:真纯度需要全程序 IR 体 ⇒ 链统一在 IR 生成结束后重建)。 return nid; } @@ -154,15 +150,16 @@ fn df_add_edge(from_id: int, to_id: int) { } // VSDG state chain: keep the ordering of side-effecting operations in program -// order. Called for every created DFNode; only nodes that mutate memory or -// call impure functions enter the chain (each links to the previous one via a -// kind=1 state edge). +// order. 只有改内存或调不纯函数的节点入链(各自与前一节点以 kind=1 状态边相连)。 +// 时点(效应/纯度修正 Task 1):**不再逐节点即时连接**——改由 df_replay_state_chain +// 在全部 IR 生成结束后对成品图重放调用;入链判据未变,仍是本函数。 fn df_connect_state(node_id: int, opcode: int, s3: int) { is_side_effect : ., mut = 0; - if opcode == IR_STORE { is_side_effect = 1; } - if opcode == IR_STORE_FIELD { is_side_effect = 1; } - if opcode == IR_STORE_INDEX { is_side_effect = 1; } - if opcode == IR_STORE_INDEX_VAR { is_side_effect = 1; } + // opcode 级效应清单的**唯一真源** = purity_op_effect(checker.cr;D7「效应清单 + // 收敛为一份」——链分类与纯度判定同表,两条判据才不会各自漂移)。历史两处 + // 缺失即由此收敛:① IR_CALL_EXTERN 两侧皆缺(extern 调用既被乐观标纯、又不在 + // 本表);② IR_STORE_PTR/IR_AWAIT 只进了纯度侧(裸指针写、spawn 同步)。 + if purity_op_effect(opcode) != 0 { is_side_effect = 1; } if opcode == IR_CALL { // s3 = func name idx; resolve to func index for purity. Unknown/external // functions are conservatively treated as side-effecting. @@ -176,6 +173,99 @@ fn df_connect_state(node_id: int, opcode: int, s3: int) { } } +// ─── state 链重建(效应/纯度修正 Task 1)────────────────────────────── +// 出处:链原在 df_create_node 内逐节点即时连接。改为「全部 IR 生成结束后重放」的 +// 唯一理由 = **纯度时点**:入链判据含「调不纯函数」(df_connect_state 的 IR_CALL +// 分支),而「被调者是否纯」只能由全程序 IR 体算出(compute_all_purity——IR 体 +// 逐函数生成,调用者先于被调者生成是常态,且递归/前向引用无解)⇒ 生成期恒无 +// 全程序事实。链与发射面无关(NOD 序 = 节点创建序,与边无关;corearch 三轴不读 +// EDG)⇒ 时点后移零发射影响。 +// 重放保真:节点创建序 = 程序序;每函数链头重置(与 df_begin_func 同语义); +// 循环终止规则按「区关闭位 = 该区 OFF_SG_EXIT」逐句复刻原 sg_pop 语义。 +fn df_state_close_regions_at(head: string, next: string, pos: int) { + sg := r64(head, pos * 8); + loop { + if sg < 0 { break; } + // 原 sg_pop 的链语义(spec region-cfg §4.2)逐句复刻:关闭位 = 该区最后一个 + // 节点 + 1(当时 g_df_node_count)⇒ last_node = pos - 1。 + last_node := pos - 1; + nstart := r64(g_sgs, sg * ESZ_SG + OFF_SG_NSTART); + if last_node >= nstart { + // 终止边源必须在区内(循环体无副作用时链头是区前的 store——连过去等于 + // 宣称循环退出依赖于该区不含的副作用)。 + if g_last_state_node >= nstart { + df_add_edge_kind(g_last_state_node, last_node, 1); // termination dependency + } + // 链头推进到区退出节点:循环之后的副作用必须依赖循环终止 + // (spec §4.2:不终止的循环必须终止整图)。 + g_last_state_node = last_node; + } + sg = r64(next, sg * 8); + } +} + +fn df_replay_state_chain() { + if g_ir_func_count <= 0 { return; } + // 位置 → 关闭区(只收 SG_LOOP/SG_FOR——链规则只对这两族生效):head[pos] 与 + // next[sg] 两条 i64 链,按区号升序头插 ⇒ 同位置读出为区号降序 = sg_pop 的关闭序。 + // alloc 不保证清零(rt.s bump 分配器)⇒ head 逐项显式置 -1。 + head := alloc((g_df_node_count + 1) * 8); + next := alloc((g_sg_count + 1) * 8); + i : ., mut = 0; + loop { + if i > g_df_node_count { break; } + w64(head, i * 8, -1); + i = i + 1; + } + si : ., mut = 0; + loop { + if si >= g_sg_count { break; } + k := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND); + if k == SG_LOOP || k == SG_FOR { + ex := r64(g_sgs, si * ESZ_SG + OFF_SG_EXIT); + if ex >= 0 && ex <= g_df_node_count { + w64(next, si * 8, r64(head, ex * 8)); + w64(head, ex * 8, si); + } + } + si = si + 1; + } + irf : ., mut = 0; + loop { + if irf >= g_ir_func_count { break; } + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + g_last_state_node = -1; // 每函数独立链(与 df_begin_func 同语义) + n : ., mut = 0; + loop { + if n >= cnt { break; } + if n > 0 { df_state_close_regions_at(head, next, start + n); } + pos := start + n; + df_connect_state(pos, r64(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_OPCODE), + r64(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_S3)); + n = n + 1; + } + // 函数末端关闭位(SG_FUNC 恒在此;循环落在函数末句时该环也在此)——只在此 + // 处理:下一函数走 n=0 时跳过 start ⇒ 每个关闭位恰好处理一次。函数 0 的 + // start(位 0)无人处理,但那里 last_node = -1 < nstart ≥ 0 ⇒ 规则恒 no-op。 + df_state_close_regions_at(head, next, start + cnt); + irf = irf + 1; + } +} + +// 链最终化(唯一入口):真纯度 + 链重建。两个调用点均在「IR 生成结束、任何链 +// 消费者之前」——run 路径 = ir_gen_all 尾;文件路径 = main.cr IR 循环之后 +// (cir 转储 / 区域检查 / lower_to_ccr / 保存 .ccr 之前)。 +fn df_state_finalize() { + // 幂等护栏(Task 4 终审 Minor #5):重复调用会**二次连链**(同一图再加一遍 + // kind=1 边 + 重算纯度)。当前两调用点互斥故不触发,属潜在坑;旗标随 + // reset_frontend_state 复位(长驻进程每编译一次重建)。 + if g_df_state_finalized != 0 { return; } + g_df_state_finalized = 1; + compute_all_purity(); + df_replay_state_chain(); +} + fn df_use_var(consumer_node: int, var_idx: int) { if var_idx < 0 { return; } // Ensure the DF arrays are large enough for this variable index. @@ -191,7 +281,7 @@ fn df_use_var(consumer_node: int, var_idx: int) { // Connect source operands based on opcode semantics. // Only fields that carry IR variable indices create dataflow edges. -fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int, type_kind: int) { +fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int, tk_aux: int) { if opcode == IR_CONST { return; } // all srcs are scalar values/labels if opcode == IR_BINARY { @@ -267,7 +357,9 @@ fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int, type_ki } if opcode == IR_BOUNDS_CHECK { df_use_var(node_id, s1); - if type_kind != 0 { df_use_var(node_id, s2); } + // 单槽化(P5 T2):旗标已从混用 tk 槽移入**辅码**槽 ⇒ 读 tk_aux(值域不变: + // 1 = 动态上限 ⇒ 上限变量 s2 也是数据依赖;0 = 字面量上限 ⇒ 不连)。 + if tk_aux != 0 { df_use_var(node_id, s2); } return; } if opcode == IR_REF { @@ -365,7 +457,13 @@ fn lower_to_ccr() { iri_set_s1(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1)); iri_set_s2(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2)); iri_set_s3(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3)); - iri_set_tk(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK)); + // R2 P5 Task 2(D19):iri_tk = **派生码**(不是槽值)——单槽化后 40 槽存的是 + // 类型项引用,码一律经 sh_dfn_code_of_slots 派生(派生码逐节点 ≡ 单槽化前的 + // 混用码 ⇒ .ccr 字节不变)。本文件对桥接符号的依赖仅此一处(df_create_node + // 本身保持零前端依赖;lower_to_ccr 本就引 checker 侧 purity_op_effect)。 + iri_set_tk(idx, sh_dfn_code_of_slots( + r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX))); g_ir_instr_count = idx + 1; ni = ni + 1; } @@ -384,6 +482,10 @@ fn lower_to_ccr() { // After lowering, compute per-variable usage counts for optimization passes compute_usage_counts(); + + // regalloc 移后端(2026-09-07,D-1=Y):数据面(区间/条目)随分配归位 + // corearch——.ccr ENT 恒空,不再在此无条件计算;corearch load 后自算自检 + // (regalloc.cr compute_live_ranges/compute_entries,载入 NOD 流坐标同源)。 } // --- Mark function boundary in graph --- diff --git a/src/compiler/diag.cr b/src/compiler/diag.cr index 9f35337f..5e7f5e7e 100644 --- a/src/compiler/diag.cr +++ b/src/compiler/diag.cr @@ -160,3 +160,51 @@ fn print_parse_errors() { ei2 = ei2 + 1; } } +// ─── fail-closed 判据线(FC 批 T2):豁免登记表 ──────────────────────── +// 语义:**默认阻断 + 豁免登记表**——`main.cr` 的硬判定循环对每条诊断调本函数,返回 0 ⇒ 阻断 +// (rc=1 + 零产物)。判据 = 「判定继续 ⇒ 产出静默错产物」(R002 / TS01-04+TK02 / TM03 / ICE04 / +// TA02 五组先例;旧正向名单 11 码按新语义默认在闸内,故退役)。 +// **换代纪律(只减不增)**:撤条须带根因证据;**加条须维护者批**;每次开新面(新命令/新语料层) +// 必须重跑全语料 report-only 并对表(表外命中 ⇒ 停)。 +// 粒度 = 码级;每条附「位点证据 / 理由 / 退出条件」三字段(证据字段不参与判定)。 +// **不得**把本表写成「默认放行」形态(那会把静默错产物制度化)。 +fn diag_gate_exempt(code: int, scope: int) -> int { + // ── scope = BUILD(6 条;build/ccr/cir/run 面)──────────────────────── + // TF01 · 位点 = tests/suite/{chan,conc,go_e2e,go_final,go_parallel}_test.cr(5 档 build) + // 理由 = 并发族既有误报(同族两条已修先例:#40 lits_copy / ty_memo_slot_no_grow) + // 退出 = 逐档归因 + 修前端根因后撤 + if scope == GATE_SCOPE_BUILD && code == EC_TF_RETURN { return 1; } + // TF07 · 位点 = tests/suite/ptr_ref_first.cr:8(@raw_int)+ tests/selfhost/test_ccr_types.py:1805 + // 理由 = EC_TF_ARG_TYPE 全仓唯一 raise 点在 @raw_int 内建位(checker.cr:3727),非调用位点 + // 退出 = #20/F3 调用位点收口后撤 + if scope == GATE_SCOPE_BUILD && code == EC_TF_ARG_TYPE { return 1; } + // TB01 · 位点 = ptr_ref_first(TF07 级联)+ t3/t4 库单元 8 档(N 族级联) + // 理由 = **级联码**:ANY 门(checker.cr:2473-2474)仅在**双侧**非 int/dex 时报, + // 而错误标记 TI_NEVER 的回传点(:2433/:2810/:3728)先行 ⇒ 连带命中 + // 退出 = 随根因码(N 族划界 / TF07)消失;**真错形态(*T + *T、"a" - "b")不受影响** + if scope == GATE_SCOPE_BUILD && code == EC_TB_ADD { return 1; } + // TM04 · 位点 = tests/selfhost/test_match_exhaust.py:217(case_soft:build rc=0 + 产物照出) + // 理由 = P3 Task 3 政策(软面登记,同 Rust unreachable-pattern 警告口径) + // 退出 = 政策改判(引入真 warning 通道) + if scope == GATE_SCOPE_BUILD && code == EC_TM_REDUNDANT { return 1; } + // TK01 · 位点 = tests/selfhost/test_xcut_iface.py:234-241(case_reject soft=True:build rc=0 + 产物) + // 理由 = P3b Task 2 索引兜底门软面政策 + // 退出 = 政策改判 + if scope == GATE_SCOPE_BUILD && code == EC_TK_INDEX { return 1; } + // B04 · 位点 = tests/suite/ptr_arith.cr:12:8(= ELF canary 载体本人) + // 理由 = 借用模型无末次使用(NLL 式)收缩——借用只随作用域退出释放 + // (checker.cr:878 push / :884-915 pop) + // 退出 = 裁-FC-3 取 (c)(块包裹修法)或 (i)(实现借用收缩)后撤 + if scope == GATE_SCOPE_BUILD && code == EC_B_USE_WHILE_BORROWED { return 1; } + // ── scope = CHECK(3 条;**本批对 build/gate 无效,仅登记**)──────────── + // 维护者 2026-09-14 裁(裁-FC-1 = (C) 终局):check 面 rc 规则不消费本表 ⇒ 本 3 条对判定 + // **惰性**;保留 = 登记(T1 would_block.tsv 的 34 条 check 面命中即其证据)+ 若将来另裁 (B) + // 时的单点开关。**(B) 不是活选项**——不得据此实现活分支。 + // N01/N06/N11 · 位点 = 25 档库单元单独 check(t2 1 + t3 13 + t4 10 + t1 1) + // 理由 = concat 供货语境缺失(非独立编译单元),check rc=1 为既有语义 + // 退出 = 口径固定 + if scope == GATE_SCOPE_CHECK && code == EC_N_UNDEFINED { return 1; } + if scope == GATE_SCOPE_CHECK && code == EC_N_FUNC { return 1; } + if scope == GATE_SCOPE_CHECK && code == EC_N_DUPLICATE { return 1; } + return 0; // 默认:不豁免(= 阻断) +} diff --git a/src/compiler/dump.cr b/src/compiler/dump.cr index b61b24e1..016fff18 100644 --- a/src/compiler/dump.cr +++ b/src/compiler/dump.cr @@ -334,6 +334,98 @@ fn cir_text_dump() -> string { return dump_buf_finish(); } +// ─── R2 P5 Task 2 测试通道(corec 侧 hidden flag `cir --dump-tk-terms`)─── +// 用途 = 单槽化的**逐节点**观察面:两槽(OFF_DF_TK = 类型项引用、OFF_DF_AUX = 辅码) +// + **派生码**(sh_dfn_code_of_slots——与 iri_tk / .ccr NOD / .cir 盘记录同源)+ 项结构 +// (tag/a/b/c,直读 g_type_terms)——冷路径(emit 填槽)与暖路径(`.cir` 快照读回 + +// 重派生)两态用同一通道 dump 后逐行对拍。`tk` 列 = 派生码 ⇒ 与单槽化前同名通道的 +// `tk` 列**逐字节可比**(D22-① 的直方图判据就建在这上面)。 +// 只读:不改任何全局、不产产物(与 --dump-types/--dump-ifaces 同纪律)。 +// 行格式(制表分隔;头行含计数与越界项计数): +// [df-tk-terms] nodes=N with_term=M bad_term=B aux_nonzero=A face_fail=F terms=K rows=R mint=1,2,4,6,10,49,50 +// \t\t\t\t\t\t\t\t +// term < 0(无项)或项行号越出项表(bad_term,跨进程索引空间分歧的暴露面——不得 +// 静默读越界内存):tag/a/b/c 一律 -1。aux_nonzero = 辅码非零节点数(互斥不变量 +// D22-② 的观察面);face_fail = D23 建项失败计数(0 = 全语料无不可译类型面行); +// rows = 当前进程类型表行数(冷/暖比对用:暖态可小于冷态——见 sh_tk_split_load 注)。 +fn df_tk_term_dump() -> string { + dump_buf_reset(); + total := g_df_node_count; + with_term : ., mut = 0; + bad_term : ., mut = 0; + auxn : ., mut = 0; + n0 : ., mut = 0; + loop { + if n0 >= total { break; } + t0 := r64(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_TK); + if t0 >= 0 { with_term = with_term + 1; } + if t0 >= tt_count() { bad_term = bad_term + 1; } + if r64(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_AUX) != 0 { auxn = auxn + 1; } + n0 = n0 + 1; + } + dump_buf_append("[df-tk-terms] nodes="); + dump_buf_append(int_str(total)); + dump_buf_append(" with_term="); + dump_buf_append(int_str(with_term)); + dump_buf_append(" bad_term="); + dump_buf_append(int_str(bad_term)); + dump_buf_append(" aux_nonzero="); + dump_buf_append(int_str(auxn)); + dump_buf_append(" face_fail="); + dump_buf_append(int_str(g_tk_face_fail)); + dump_buf_append(" terms="); + dump_buf_append(int_str(tt_count())); + dump_buf_append(" rows="); + dump_buf_append(int_str(g_type_count)); + dump_buf_append(" mint="); + dump_buf_append(int_str(IR_CONST)); + dump_buf_append(","); + dump_buf_append(int_str(IR_BINARY)); + dump_buf_append(","); + dump_buf_append(int_str(IR_ALLOC)); + dump_buf_append(","); + dump_buf_append(int_str(IR_CALL)); + dump_buf_append(","); + dump_buf_append(int_str(IR_LOAD)); + dump_buf_append(","); + dump_buf_append(int_str(IR_I2F)); + dump_buf_append(","); + dump_buf_append(int_str(IR_F2I)); + dump_buf_append("\n"); + n : ., mut = 0; + loop { + if n >= total { break; } + op := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE); + t := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK); + ax := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX); + tk := sh_dfn_code_of_slots(t, ax); + dump_buf_append(int_str(n)); + dump_buf_append("\t"); + dump_buf_append(int_str(op)); + dump_buf_append("\t"); + dump_buf_append(int_str(tk)); + dump_buf_append("\t"); + dump_buf_append(int_str(t)); + dump_buf_append("\t"); + if t >= 0 && t < tt_count() { + dump_buf_append(int_str(tt_tag(t))); + dump_buf_append("\t"); + dump_buf_append(int_str(tt_a(t))); + dump_buf_append("\t"); + dump_buf_append(int_str(tt_b(t))); + dump_buf_append("\t"); + dump_buf_append(int_str(tt_c(t))); + } else { + dump_buf_append("-1\t-1\t-1\t-1"); + } + dump_buf_append("\t"); + dump_buf_append(int_str(ax)); + dump_buf_append("\n"); + n = n + 1; + } + return dump_buf_finish(); +} + fn cmd_cir(src_path: string) -> int { g_source = read_file(src_path); if str_len(g_source) == 0 { diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index 12a1a6c9..aef0014d 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -53,9 +53,9 @@ ESZ_ASTNODE : int = 72; // kind,a,b,c,int_val,type_val,data,line,col = 9×8 ESZ_SYMENTRY : int = 32; // name_idx,kind,type_idx,node_idx = 4×8 ESZ_IRVAR : int = 24; // name_idx,id,type_kind = 3×8 ESZ_IRINSTR : int = 48; // opcode,dest,src1,src2,src3,type_kind = 6×8 -ESZ_FUNCINFO : int = 208; +ESZ_FUNCINFO : int = 688; ESZ_STRUCTINFO : int = 440; -ESZ_ENUMINFO : int = 2360; // name+variants[16]+variant_count+generic_names[4]+generic_count +ESZ_ENUMINFO : int = 4408; // name+variants[16](含载荷类型节点槽,P3 Task 4)+variant_count+generic_names[4]+generic_count // Token field offsets OFF_TK_KIND : int = 0; OFF_TK_LEXEME : int = 8; @@ -79,30 +79,69 @@ OFF_IRI_S1 : int = 16; OFF_IRI_S2 : int = 24; OFF_IRI_S3 : int = 32; OFF_IRI_TK : int = 40; // FuncInfo offsets +// 形参槽上限:param_types 是**定长内嵌槽区**(跟在 param_count 之后、return_type +// 之前),槽数即硬上限。修复前槽数 = 16 且写入无界 → 第 17 个形参起改写 +// return_type/ast_node(TY_INT=0 恰把 ast_node 写成 0 = 节点表首项)→ +// 「TF01 误归 + .ccr name_idx/param_count=0 + rc=0 产物崩」静默误编译 +// (TODO #8,2026-09-11)。64 = 与 ast.cr FuncInfo 镜像 `[int; 64]` 对齐, +// 并覆盖 ≥22(16 栈参 + 6 寄存器参,SysV)的栈清理形。越界 = parser 硬错 +// P020(rc=1)+ 下方访问器护栏双保险——**任何情况下不得静默越界写**。 +MAX_FN_PARAMS : int = 64; OFF_FI_NAME : int = 0; OFF_FI_PARAM_COUNT : int = 8; -OFF_FI_PARAM_TYPES : int = 16; -OFF_FI_RETURN_TYPE : int = 144; OFF_FI_AST_NODE : int = 152; -OFF_FI_GENERIC_NAMES : int = 160; OFF_FI_GENERIC_COUNT : int = 192; -OFF_FI_ISPURE : int = 200; // 1 = pure (no side effects), 0 = impure +OFF_FI_PARAM_TYPES : int = 16; // 64 槽 × 8B = 512 → 至 527 +OFF_FI_RETURN_TYPE : int = 528; OFF_FI_AST_NODE : int = 536; +OFF_FI_GENERIC_NAMES : int = 544; OFF_FI_GENERIC_COUNT : int = 672; +OFF_FI_ISPURE : int = 680; // 1 = pure (no side effects), 0 = impure // StructInfo offsets -OFF_SI_NAME : int = 0; OFF_SI_FIELD_NAMES : int = 8; -OFF_SI_FIELD_TYPES : int = 136; OFF_SI_FIELD_TYPE_NODES : int = 264; +// 容量批 T3(裁-CAP-2 (a)):字段槽区**迁侧表**——本槽(原 field_names 首槽)改义为 +// `field_base`(侧表起始行);`OFF_SI_FIELD_TYPES`/`OFF_SI_FIELD_TYPE_NODES` 保留为 +// **未用填充**(记录尺寸不变 ⇒ 盘面与外部算术零改动)。 +OFF_SI_NAME : int = 0; OFF_SI_FIELD_BASE : int = 8; +OFF_SI_FIELD_TYPES : int = 136; OFF_SI_FIELD_TYPE_NODES : int = 264; // 保留未用 OFF_SI_FIELD_COUNT : int = 392; OFF_SI_GENERIC_NAMES : int = 400; OFF_SI_GENERIC_COUNT : int = 432; // DFNode sizes and offsets -ESZ_DFNODE : int = 64; // opcode,dest_var,src1,src2,src3,type_kind,first_edge,edge_count = 8x8 +// R2 P5 Task 2(D19 单槽化 α = 槽语义对调 + 辅码显式分离):类型面**单源**。 +// OFF_DF_TK (40) = **类型项引用**(g_type_terms 行号;-1 = 无项)= 类型面唯一真源; +// OFF_DF_AUX (64) = **辅码**(旗标/宽度/计数 + 不可入项的原始类型行码;0 = 无)。 +// 契约(四条,缺一即静默类): +// ① **字节零变化**:ESZ_DFNODE 保持 72B,两槽只是**语义对调**(原 40 = 混用码、 +// 原 64 = 项引用)⇒ `.ccr`(NOD 36B)与 `.cir` 快照(节点 64B)布局零改动, +// `CCR_VERSION=8` / `CIR_CACHE_VER=17` 均不 bump。 +// ② **互斥**(D22-②):辅码 ≠ 0 ⇒ 项 = -1;项 ≥ 0 ⇒ 辅码 = 0(由拆分器构造保证)。 +// ③ **码 = 派生量**(D22-①/D23):`iri_tk` / `.ccr` NOD / `.cir` 快照里的类型码 +// 一律由 `sh_dfn_code_of_slots(项, 辅码)` 派生,**不得**当独立真源存储或读回 +// (派生码逐节点 ≡ 单槽化前的混用码;`.ccr`/`.cir` 面因此逐字节不变)。 +// ④ 写点 = df_create_node(dataflow.cr,本文件不引用桥接符号)+ `.cir` 快照装载 +// 侧的**重派生**(cir_cache.cr——盘面只承载派生码,不入项/辅码槽:进程内项 +// 引用不得跨进程,D20)。 +// 拆分器((opcode, tk) → (项, 辅码))单源 = ty_shadow.cr 的 sh_tk_split;既有消费者 +// (instr.cr/regalloc/lower_to_core/dump/ccr_io)读的是**派生码**,零改动。 +ESZ_DFNODE : int = 72; // opcode,dest_var,src1,src2,src3,tk_term,first_edge,edge_count,tk_aux = 9x8 OFF_DF_OPCODE : int = 0; OFF_DF_DEST : int = 8; OFF_DF_S1 : int = 16; OFF_DF_S2 : int = 24; -OFF_DF_S3 : int = 32; OFF_DF_TK : int = 40; +OFF_DF_S3 : int = 32; OFF_DF_TK : int = 40; // ★语义(P5 T2):类型项引用(-1 = 无项) OFF_DF_FIRST_EDGE : int = 48; OFF_DF_EDGE_COUNT : int = 56; +OFF_DF_AUX : int = 64; // P5 T2(D19,原 OFF_DF_TK_TERM 槽位):辅码(0 = 无) // DFEdge sizes and offsets ESZ_DFEDGE : int = 32; // from_node,to_node,next_out,kind = 4x8 OFF_DFE_FROM : int = 0; OFF_DFE_TO : int = 8; OFF_DFE_NEXT : int = 16; OFF_DFE_KIND : int = 24; // 0=data (def-use), 1=state (ordering/termination) +// Entry record (v6 条目版本段:变量 × 定值点切分版本条目,compute_entries 填充)。 +// 内存布局 = .ccr v6 落盘格式逐字节一致(24B/条,六字段各 4B,w32/r32 LE 存取): +// var_idx u32, def_instr i32, live_start i32, live_end i32, home i32, flags u32。 +ESZ_ENTRY : int = 24; +OFF_ENTRY_VAR : int = 0; // 全局 IR 变量索引 +OFF_ENTRY_DEF : int = 4; // 定值指令全局索引(-1 = 无定值) +OFF_ENTRY_LS : int = 8; // 版本存在区间起点(全局指令序) +OFF_ENTRY_LE : int = 12; // 版本存在区间终点(全局指令序) +OFF_ENTRY_HOME : int = 16; // 分配槽位(-1 = 未分配) +OFF_ENTRY_FLAGS : int = 20; // 位 0 预留:无配方(条款 4b) + // Subgraph entry (48 bytes each) ESZ_SG : int = 48; OFF_SG_KIND : int = 0; // 0=func, 1=loop, 2=for, 3=flow, 4=unsafe @@ -120,23 +159,92 @@ SG_UNSAFE : int = 4; SG_IF : int = 5; // conditional region: covers [condition, merge) // InterfaceInfo: fixed-size entry per interface -// Header(24) + methods[16] * method_entry(88) = 1432 total -ESZ_IFACEINFO : int = 1432; +// Header(24) + methods[16] * method_entry(240) = 3864 total(R2 P4 Task 3:168 → 240, +// + 类型项槽 72B——见下 OFF_IFM_PARAM_TERMS/OFF_IFM_RET_TERM 注) +ESZ_IFACEINFO : int = 3864; OFF_IF_NAME : int = 0; OFF_IF_METHOD_COUNT : int = 8; OFF_IF_GENERIC_COUNT : int = 16; OFF_IF_METHODS : int = 24; // first method entry -// Each method entry: name_idx(8) + param_count(8) + ret_ti(8) + param_types[8](64) = 88 bytes -ESZ_IFMETHOD : int = 88; +// Each method entry: name_idx(8) + param_count(8) + ret_ti(8) + param_types[8](64) +// + param_nodes[8](64) + ret_node(8) + self_mode(8) + param_terms[8](64) + ret_term(8) = 240 bytes +// R2 P3b Task 6(Step 1 签名类型项化):**裸码槽保留**(`param_types` / `ret_ti`)—— +// S6 站点(checker 的泛型方法调用返回型映射)的值域 = 映射层编码(TY_*),P2b Task 6 已把 +// 该域**显式化钉住**(t6_S6_* 行为用例)⇒ 不得改;新增**类型节点**槽 = 签名的忠实来源 +// (裸码把非原生类型一律塌缩为 0 = TY_INT,与 int 不可区分)——供 Step 3 的签名项比较 +// (满足判定 / impl 声明面)与 `sh_iface_shape_term` 建项。-1 = 无节点(哨兵;不用 0—— +// 节点 0 是合法下标,用 0 会与「首个分配节点」混同)。接收者槽两侧皆无节点(parser 的 +// self 约定,见 self_mode)。 +ESZ_IFMETHOD : int = 240; OFF_IFM_NAME : int = 0; OFF_IFM_PARAM_COUNT : int = 8; OFF_IFM_RET_TI : int = 16; -OFF_IFM_PARAM_TYPES : int = 24; // first of up to 8 param types (each 8 bytes) +OFF_IFM_PARAM_TYPES : int = 24; // first of up to 8 param types (each 8 bytes) → 24..87 +OFF_IFM_PARAM_NODES : int = 88; // first of up to 8 param type nodes (8B each) → 88..151 +OFF_IFM_RET_NODE : int = 152; // return type node(-1 = 无,= unit) +OFF_IFM_SELF_MODE : int = 160; // 接收者模式:0 = 无接收者(首参为普通形参)/ 1 = self / + // 2 = &self / 3 = &mut self(照 EXPR_PARAM 的 int_val 约定) +// R2 P4 Task 3(IFACE 段签名项化)——**类型项槽**(-1 = 不可建/无): +// param_terms[8] = 各形参的签名类型项(`sh_iface_sig_param_term`;接收者槽 = unit 占位项) +// ret_term = 返回签名类型项(`sh_iface_sig_ret_term`) +// 与 param_nodes/ret_node 的关系:节点槽 = **corec 侧**的签名来源(AST 节点,判定路径消费); +// 项槽 = **载体面**(IFACE(8) 段落盘 + corearch 读回)——corearch 侧无 AST ⇒ 节点槽恒 -1, +// 项槽是签名在载入侧的唯一忠实表示(二者不同域,不得互相复用)。写侧 = ccr_types.cr 的 +// ccr_iface_populate 填充(与段体同一读点);读侧 = load_ccr 由段填充。**不入 .cir 快照** +// (快照字段清单不含 g_ifaces——见 Task 3 报告 §布局影响面)。 +OFF_IFM_PARAM_TERMS : int = 168; // first of up to 8 param terms (8B each) → 168..231 +OFF_IFM_RET_TERM : int = 232; // return signature term (-1 = 不可建) MAX_IFACE_METHOD_PARAMS : int = 8; +// R2 P3b Task 6(Step 1):方法上限单源化(旧态 = parser 内联字面量 16,与 ESZ_IFACEINFO +// 的槽数隐式绑定)。**本任务显式登记保留**(不解除):解除需把方法表迁到侧表(全部读点换位), +// 按 B.4-7 的 MAX_* 统一口径「先加护栏、再评估解除」;护栏现状 = 超限**硬错 rc=1**(非静默 +// 截断——实测),本任务加钉子用例(test_impl_iface.py 的 limit 组)。 +MAX_IFACE_METHODS : int = 16; + +// ─── 本质条目表布局(R2 P4 Task 3:自 iface_registry.cr **迁入**)─── +// 迁入理由 = IFACE(8) 段的 loader(corearch 侧,ccr_io.cr)要重建 `g_iface_entries` +// 并校验 `native_count == IFACE_ENTRY_COUNT`,而 iface_registry.cr **不入 corearch +// 清单**(corec-only:内含 checker 耦合的 iface_kind_of 之邻)⇒ 布局常量必须在双 +// concat 共享面(本文件 = 共享层;与 ESZ_IFACEINFO/ESZ_IFMETHOD 同居一处的先例)。 +// 迁入只放宽声明序(dyn_arr.cr < checker.cr < iface_registry.cr),零语义变化。 +// {ak, ti_row, name_ni, lit_code, ops}:ak = 原子类(AK_*);ti_row = 规范 checker +// 类型行(8 原生 = TI_* 常量;结构/命名 = -1);name_ni = 名字 ni(显示/查询用, +// R2 P4 Task 3 起在注册面 str_intern 填充——裁决 1 接受 STR 段增长);lit_code = +// 字面量定型(AST kind;-1 = 无);ops = 操作许可位集。 +ESZ_IFACE_ENTRY : int = 40; +OFF_IE_AK : int = 0; OFF_IE_TI : int = 8; OFF_IE_NAME : int = 16; +OFF_IE_LIT : int = 24; OFF_IE_OPS : int = 32; +// R2 P4 Task 3(裁决 3+7):13 → 16(+AK_NULL 原生第九员 + AK_SUM + AK_FN——纯信息面, +// ops = 0 = 无操作许可;判定面零变化由全类型行枚举守门)。 +IFACE_ENTRY_COUNT : int = 16; + +// 形状条目表 grow(R2 P4 Task 3:自 iface_registry.cr **迁入**——同 IFACE 段 loader +// 读回面:corearch 侧重建 g_iface_shape_* 需表增长;本文件 = grow 助手统一宿主, +// 零依赖(alloc/_dyncpy 皆共享层))。语义/布局零变化(逐字搬迁)。 +fn iface_shape_grow(needed: int) { + if needed <= g_iface_shape_cap { return; } + nc : ., mut = g_iface_shape_cap * 2; + if nc < 8 { nc = 8; } + if nc < needed { nc = needed + 8; } + nb := alloc(nc * 8); + _dyncpy(g_iface_shape_names, g_iface_shape_cap * 8, nb); + g_iface_shape_names = nb; + nt := alloc(nc * 8); + _dyncpy(g_iface_shape_terms, g_iface_shape_cap * 8, nt); + g_iface_shape_terms = nt; + g_iface_shape_cap = nc; +} // EnumInfo offsets -OFF_EI_NAME : int = 0; OFF_EI_VARIANTS : int = 8; -OFF_EI_VARIANT_COUNT : int = 2312; -OFF_EI_GENERIC_NAMES : int = 2320; OFF_EI_GENERIC_COUNT : int = 2352; -// EnumVariant within variants[N]: name(8) + types[16](128) + type_count(8) +// R2 P3 Task 4(T0 交接 ①):变体载荷**类型节点**槽(照 struct 先例 OFF_SI_FIELD_TYPE_NODES) +// ——裸码槽 OFF_EV_TYPES 的非基型载荷一律塌缩为 0 = TY_INT(parser 的 unpack_type), +// 「载荷是 int」与「载荷是 string/命名类型/泛型形参」不可区分 ⇒ 载荷面(泛型代入/满足判定) +// 在裸码表上不可能忠实。nodes 槽 = parse_type 的产物节点(-1/0 = 无信息)。 +// 布局:EnumVariant = name(8) + types[16](128) + type_count(8) + type_nodes[16](128) = 272 +// 容量批 T3(裁-CAP-2 (a)):变体槽区**迁侧表**——`OFF_EI_VARIANTS` 槽改义为 `variant_base` +// (侧表起始行);`OFF_EV_*` 与 272B 槽区保留为**未用填充**(记录尺寸不变 ⇒ 外部算术零改动)。 +OFF_EI_NAME : int = 0; OFF_EI_VARIANTS : int = 8; // = variant_base +OFF_EI_VARIANT_COUNT : int = 4360; +OFF_EI_GENERIC_NAMES : int = 4368; OFF_EI_GENERIC_COUNT : int = 4400; OFF_EV_NAME : int = 0; OFF_EV_TYPES : int = 8; OFF_EV_TYPE_COUNT : int = 136; -OFF_EV_SIZE : int = 144; +OFF_EV_TYPE_NODES : int = 144; +OFF_EV_SIZE : int = 272; // ============================================================ // Copy helper @@ -175,7 +283,9 @@ fn grow_syms(needed: int) { fn grow_types(needed: int) { if needed < g_type_cap { return; } nc : ., mut = g_type_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } - nb := alloc(nc * 24); _dyncpy(g_types, g_type_cap * 24, nb); + // R2 P4 Task 2:行尺寸经 ESZ_TYPE_ROW 单源(globals.cr——与 alloc_type/ + // TYPE 段序列化同源;原为字面量 24)。 + nb := alloc(nc * ESZ_TYPE_ROW); _dyncpy(g_types, g_type_cap * ESZ_TYPE_ROW, nb); g_types = nb; g_type_cap = nc; } fn grow_funcs(needed: int) { @@ -297,10 +407,19 @@ fn ast_alloc(kind: int, a: int, b: int, c: int, iv: int, tv: int, d: int, line: return idx; } // Accessor helpers for SymEntry -fn sym_name(n: int) -> int { return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME); } -fn sym_kind(n: int) -> int { return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND); } -fn sym_type(n: int) -> int { return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE); } -fn sym_node(n: int) -> int { return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE); } +// ─── R2 P3 Task 4:读取面护栏(n ∉ [0, g_sym_count) ⇒ -1,不触内存)─── +// 触发链(本任务实测):① 本任务退役了内建 Option 注册(collect_decls 早期的一次 def_sym) +// ⇒ 「无类型声明的源文件」在函数注册循环处 g_syms 仍为 **NULL**(首个 def_sym 尚未发生, +// 这是合法状态);② `collect_decls` 的重复函数检查写的是 `existing_si >= 0 && sym_kind(existing_si)` +// (checker.cr:1202),而 **bootstrap 构建的编译器二进制不求值短路**(T0 §5-③ 实测:`&&` +// 两侧无条件求值)⇒ `sym_kind(-1)` 被求值 → `r64(NULL, 负偏移)` → SIGSEGV(实测 d0 类 +// 「只有 fn、无类型声明」的源 rc=139)。修法 = 访问器自身带范围闸(与类型表访问器 +// get_type_kind/get_type_data 同款);合法读零变化,越界/未分配返回 -1(哨兵语义与 +// find_gsym 的 -1 一致)。根因(bootstrap 不短路)仍归 T0 §5-③ 登记,不在本任务面内。 +fn sym_name(n: int) -> int { if n < 0 || n >= g_sym_count { return -1; } return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME); } +fn sym_kind(n: int) -> int { if n < 0 || n >= g_sym_count { return -1; } return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND); } +fn sym_type(n: int) -> int { if n < 0 || n >= g_sym_count { return -1; } return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE); } +fn sym_node(n: int) -> int { if n < 0 || n >= g_sym_count { return -1; } return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE); } fn sym_set_name(n: int, v: int) { w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME, v); } fn sym_set_kind(n: int, v: int) { w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND, v); } fn sym_set_type(n: int, v: int) { w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE, v); } @@ -328,17 +447,31 @@ fn iri_set_s2(n: int, v: int) { w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S2, v fn iri_set_s3(n: int, v: int) { w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S3, v); } fn iri_set_tk(n: int, v: int) { w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_TK, v); } +// 变量名查询(原定义于 ir_gen.cr——2026-09-07 regalloc 移后端随迁本文件: +// corearch 闭包不含 ir_gen.cr,regalloc.cr 判定/注入打印消费本函数;双侧共享) +fn get_ir_var_name(var_idx: int) -> string { + if var_idx >= 0 && var_idx < g_ir_var_count { + ni := irv_name(var_idx); + return istr_get(ni); + } + return ""; +} + // FuncInfo helpers fn fi_name(n: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME); } fn fi_param_count(n: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT); } fn fi_return_type(n: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE); } fn fi_ast_node(n: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE); } fn fi_generic_count(n: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_COUNT); } -fn fi_param_type(n: int, pi: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi * 8); } +fn fi_param_type(n: int, pi: int) -> int { + if pi < 0 || pi >= MAX_FN_PARAMS { return 0; } // 越界 = 槽区外(读护栏;写侧同护栏 + parser 硬错) + return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi * 8); } fn fi_generic_name(n: int, gi: int) -> int { return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8); } fn fi_set_name(n: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME, v); } fn fi_set_param_count(n: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT, v); } -fn fi_set_param_type(n: int, pi: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi*8, v); } +fn fi_set_param_type(n: int, pi: int, v: int) { + if pi < 0 || pi >= MAX_FN_PARAMS { return; } // 槽区护栏:越界写会踩 return_type/ast_node/generic_*/ispure(TODO #8 根源) + w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi*8, v); } fn fi_set_return_type(n: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE, v); } fn fi_set_ast_node(n: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE, v); } fn fi_set_generic_name(n: int, gi: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi*8, v); } @@ -351,9 +484,48 @@ fn fi_set_ispure(n: int, v: int) { w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_ISPURE fn si_name(n: int) -> int { return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_NAME); } fn si_field_count(n: int) -> int { return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT); } fn si_generic_count(n: int) -> int { return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT); } -fn si_field_name(n: int, fi: int) -> int { return r64(g_structs, n*ESZ_STRUCTINFO + OFF_SI_FIELD_NAMES + fi*8); } -fn si_field_type(n: int, fi: int) -> int { return r64(g_structs, n*ESZ_STRUCTINFO + OFF_SI_FIELD_TYPES + fi*8); } -fn si_field_type_node(n: int, fi: int) -> int { return r64(g_structs, n*ESZ_STRUCTINFO + OFF_SI_FIELD_TYPE_NODES + fi*8); } +// ─── 容量批 T3(裁-CAP-2 (a)):结构体字段**侧表**(无硬上限)─── +// 布局:记录内 `OFF_SI_FIELD_BASE` = 本记录字段在侧表的起始行;`OFF_SI_FIELD_COUNT` = 计数 +// (真值)。三条扁平表按「记录基数 + 字段下标」寻址;高水位 `g_si_f_used` 由 +// `si_commit_fields` 提交(记录字段区一次性占用,不重叠)。 +fn grow_si_fields(needed: int) { + if needed <= g_si_f_cap { return; } + nc := g_si_f_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_si_f_names, g_si_f_cap * 8, nb); g_si_f_names = nb; + tb := alloc(nc * 8); _dyncpy(g_si_f_types, g_si_f_cap * 8, tb); g_si_f_types = tb; + ob := alloc(nc * 8); _dyncpy(g_si_f_nodes, g_si_f_cap * 8, ob); g_si_f_nodes = ob; + g_si_f_cap = nc; +} +fn si_field_base(n: int) -> int { return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE); } +fn si_set_field_base(n: int, v: int) { w64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE, v); } +// 提交字段区(base + 计数 ⇒ 高水位);parser 与 ccr_io 装载共用 +fn si_commit_fields(n: int, fc: int) { + b := si_field_base(n); + if b < 0 { return; } + if fc < 0 { return; } + if b + fc > g_si_f_used { g_si_f_used = b + fc; } +} +// 读护栏(R2 P4 Task 6 / TODO #35 的**计数为界**版本):fi 越界(< 0 或 ≥ count)⇒ 回哨兵 +// -1(不越读);在界读 = 侧表取值。 +fn si_field_row(n: int, fi: int) -> int { + if fi < 0 { return -1; } + if fi >= si_field_count(n) { return -1; } + b := si_field_base(n); + if b < 0 { return -1; } + return b + fi; +} +fn si_field_name(n: int, fi: int) -> int { + r := si_field_row(n, fi); + if r < 0 { return -1; } + return r64(g_si_f_names, r * 8); } +fn si_field_type(n: int, fi: int) -> int { + r := si_field_row(n, fi); + if r < 0 { return -1; } + return r64(g_si_f_types, r * 8); } +fn si_field_type_node(n: int, fi: int) -> int { + r := si_field_row(n, fi); + if r < 0 { return -1; } + return r64(g_si_f_nodes, r * 8); } fn si_generic_name(n: int, gi: int) -> int { return r64(g_structs, n*ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + gi*8); } // EnumInfo helpers @@ -361,9 +533,143 @@ fn ei_name(n: int) -> int { return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_NAME); fn ei_variant_count(n: int) -> int { return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT); } fn ei_generic_count(n: int) -> int { return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT); } fn ei_generic_name(n: int, gi: int) -> int { return r64(g_enums, n*ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + gi*8); } -fn ei_variant_name(n: int, vi: int) -> int { return r64(g_enums, n*ESZ_ENUMINFO + OFF_EI_VARIANTS + vi*OFF_EV_SIZE + OFF_EV_NAME); } -fn ei_variant_type(n: int, vi: int, ti: int) -> int { return r64(g_enums, n*ESZ_ENUMINFO + OFF_EI_VARIANTS + vi*OFF_EV_SIZE + OFF_EV_TYPES + ti*8); } -fn ei_variant_type_count(n: int, vi: int) -> int { return r64(g_enums, n*ESZ_ENUMINFO + OFF_EI_VARIANTS + vi*OFF_EV_SIZE + OFF_EV_TYPE_COUNT); } +// 读护栏(R2 P4 Task 6 / TODO #35):越界 = 槽区外(读会取到 variant_count/generic 槽或邻 +// 记录)⇒ 回哨兵(名字/类型码/类型节点 = -1;计数 = 0)。在界调用点(count ≤ 16)行为不变。 +// ─── 容量批 T3:枚举变体**侧表**(无硬上限;同 struct 字段形态)─── +fn grow_ei_variants(needed: int) { + if needed <= g_ei_v_cap { return; } + nc := g_ei_v_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + a := alloc(nc * 8); _dyncpy(g_ei_v_names, g_ei_v_cap * 8, a); g_ei_v_names = a; + b := alloc(nc * 8); _dyncpy(g_ei_v_tbase, g_ei_v_cap * 8, b); g_ei_v_tbase = b; + c := alloc(nc * 8); _dyncpy(g_ei_v_tcount, g_ei_v_cap * 8, c); g_ei_v_tcount = c; + z : ., mut = g_ei_v_cap; + loop { if z >= nc { break; } w64(b, z * 8, -1); z = z + 1; } // tbase 新槽 = -1 哨兵 + g_ei_v_cap = nc; +} +fn grow_ei_vtypes(needed: int) { + if needed <= g_ei_vt_cap { return; } + nc := g_ei_vt_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + a := alloc(nc * 8); _dyncpy(g_ei_vt_types, g_ei_vt_cap * 8, a); g_ei_vt_types = a; + b := alloc(nc * 8); _dyncpy(g_ei_vt_nodes, g_ei_vt_cap * 8, b); g_ei_vt_nodes = b; + g_ei_vt_cap = nc; +} +fn ei_variant_base(n: int) -> int { return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS); } +fn ei_set_variant_base(n: int, v: int) { w64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS, v); } +fn ei_commit_variants(n: int, vc: int) { + b := ei_variant_base(n); + if b < 0 { return; } + if vc < 0 { return; } + if b + vc > g_ei_v_used { g_ei_v_used = b + vc; } +} +fn ei_variant_row(n: int, vi: int) -> int { + if vi < 0 { return -1; } + if vi >= ei_variant_count(n) { return -1; } + b := ei_variant_base(n); + if b < 0 { return -1; } + return b + vi; +} +fn ei_variant_name(n: int, vi: int) -> int { + r := ei_variant_row(n, vi); + if r < 0 { return -1; } + return r64(g_ei_v_names, r * 8); } +fn ei_variant_type_count(n: int, vi: int) -> int { + r := ei_variant_row(n, vi); + if r < 0 { return 0; } + return r64(g_ei_v_tcount, r * 8); } +fn ei_variant_type(n: int, vi: int, ti: int) -> int { + r := ei_variant_row(n, vi); + if r < 0 { return -1; } + if ti < 0 || ti >= r64(g_ei_v_tcount, r * 8) { return -1; } + tb := r64(g_ei_v_tbase, r * 8); + if tb < 0 { return -1; } + return r64(g_ei_vt_types, (tb + ti) * 8); } +// R2 P3 Task 4(T0 交接 ①):载荷第 ti 个的**类型节点**(-1/0 = 无信息——.ccr 不落本列, +// 序列化读回侧该列恒 0;消费者须以 type_count 为界,不得据 0 反推「节点 0」)。 +fn ei_variant_type_node(n: int, vi: int, ti: int) -> int { + r := ei_variant_row(n, vi); + if r < 0 { return -1; } + if ti < 0 || ti >= r64(g_ei_v_tcount, r * 8) { return -1; } + tb := r64(g_ei_v_tbase, r * 8); + if tb < 0 { return -1; } + return r64(g_ei_vt_nodes, (tb + ti) * 8); } + +// ─── 枚举/结构体记录**受护访问器**(R2 P4 Task 6;TODO #35 收口,#8 同族形态)─── +// 背景(TODO #35 代码级定位;**容量批 T3 起该定长槽区已退役**——字段/变体迁侧表):EnumVariant 槽区(旧 `MAX_ENUM_VARIANTS` 槽 × `OFF_EV_SIZE`)与 +// StructInfo 字段槽区(旧 `MAX_STRUCT_FIELDS` 槽)曾是**定长内嵌槽区**——其后紧跟记录自身的 +// count/generic 槽,再往后是**下一条记录**(同一 buffer)⇒ 无界写入既踩自身记录尾也踩邻记录 +// (实测:第 17 变体槽起点 = `OFF_EI_VARIANT_COUNT` 自身、槽尾越过 `ESZ_ENUMINFO` 224B)。 +// 修复形态**照 TODO #8 收口**(`fi_param_type`/`fi_set_param_type`): +// ① **唯一受护写点** = 本组 setter(parser 全改走它们 ⇒ 未来新调用点亦不可能越界写); +// ② 读护栏 = 越界回哨兵(名字/类型节点 = -1 = 无效下标;计数 = 0),**不越读**; +// ③ 面向用户的拒绝由 parser 的 P022/P023 硬错承担(本层只做内存安全兜底,不代替诊断)。 +// count 槽(`ei_variant_count`/`si_field_count` 的写)**不设护栏**——真值是诊断依据,照 +// P020 的 `add_func(pc)` 先例(计数保持真实值,超限由硬错拒绝;越界读由②兜底)。 +fn ei_set_variant_name(n: int, vi: int, v: int) { + if vi < 0 { return; } + b := ei_variant_base(n); + if b < 0 { return; } + grow_ei_variants(b + vi + 1); + if b + vi + 1 > g_ei_v_used { g_ei_v_used = b + vi + 1; } + w64(g_ei_v_names, (b + vi) * 8, v); } +// 载荷基址**惰性分配**(首个载荷写入时占用高水位;后续增量推进) + +fn ei_variant_tbase_ensure(n: int, vi: int) -> int { + b := ei_variant_base(n); + if b < 0 { return -1; } + grow_ei_variants(b + vi + 1); + r := b + vi; + tb := r64(g_ei_v_tbase, r * 8); + if tb < 0 { + tb = g_ei_vt_used; + w64(g_ei_v_tbase, r * 8, tb); + } + return tb; +} +fn ei_set_variant_type(n: int, vi: int, ti: int, v: int) { + if vi < 0 || ti < 0 { return; } + tb := ei_variant_tbase_ensure(n, vi); + if tb < 0 { return; } + grow_ei_vtypes(tb + ti + 1); + w64(g_ei_vt_types, (tb + ti) * 8, v); + if tb + ti + 1 > g_ei_vt_used { g_ei_vt_used = tb + ti + 1; } } +fn ei_set_variant_type_count(n: int, vi: int, v: int) { + if vi < 0 { return; } + b := ei_variant_base(n); + if b < 0 { return; } + grow_ei_variants(b + vi + 1); + w64(g_ei_v_tcount, (b + vi) * 8, v); + if v > 0 { + tb := ei_variant_tbase_ensure(n, vi); // 申报区间整体占位(未写槽亦不与他人重叠) + if tb >= 0 && tb + v > g_ei_vt_used { g_ei_vt_used = tb + v; } + } } +fn ei_set_variant_type_node(n: int, vi: int, ti: int, v: int) { + if vi < 0 || ti < 0 { return; } + tb := ei_variant_tbase_ensure(n, vi); + if tb < 0 { return; } + grow_ei_vtypes(tb + ti + 1); + w64(g_ei_vt_nodes, (tb + ti) * 8, v); + if tb + ti + 1 > g_ei_vt_used { g_ei_vt_used = tb + ti + 1; } } +fn si_set_field_name(n: int, fi: int, v: int) { + if fi < 0 { return; } + b := si_field_base(n); + if b < 0 { return; } + grow_si_fields(b + fi + 1); + if b + fi + 1 > g_si_f_used { g_si_f_used = b + fi + 1; } + w64(g_si_f_names, (b + fi) * 8, v); } +fn si_set_field_type(n: int, fi: int, v: int) { + if fi < 0 { return; } + b := si_field_base(n); + if b < 0 { return; } + grow_si_fields(b + fi + 1); + if b + fi + 1 > g_si_f_used { g_si_f_used = b + fi + 1; } + w64(g_si_f_types, (b + fi) * 8, v); } +fn si_set_field_type_node(n: int, fi: int, v: int) { + if fi < 0 { return; } + b := si_field_base(n); + if b < 0 { return; } + grow_si_fields(b + fi + 1); + if b + fi + 1 > g_si_f_used { g_si_f_used = b + fi + 1; } + w64(g_si_f_nodes, (b + fi) * 8, v); } // ============================================================ // String table helpers (dynamic byte buffer) @@ -496,7 +802,19 @@ fn grow_df_arrays(needed: int) { if needed < g_df_cap { return; } nc : ., mut = g_df_cap * 2; if nc < 128 { nc = 128; } if nc < needed { nc = needed + 128; } sz := nc * 8; - n1 := alloc(sz); _dyncpy(g_df_var_producer, g_df_cap * 8, n1); g_df_var_producer = n1; + n1 := alloc(sz); _dyncpy(g_df_var_producer, g_df_cap * 8, n1); + // 幽灵边修复(v7 Task 0 注 A 裁决 = 播种修复,dataflow.cr/cir_cache.cr 同款): + // producer 槽必须对全部 var 下标恒 -1——alloc 新段(零页/复用页)不可依赖, + // 未定值 var(全局/参数/0 字面量槽)若留 0 =「节点 0」→ df_use_var 产幽灵边 + // 0→X + 自环 (0,0)。增长区显式播种 -1;既有区由 _dyncpy 保留(定值由 + // df_create_node 覆写、缓存放回路径同走本函数 → 未产出 var 保持 -1)。 + zi : ., mut = g_df_cap; + loop { + if zi >= nc { break; } + w64(n1, zi * 8, -1); + zi = zi + 1; + } + g_df_var_producer = n1; n2 := alloc(sz); _dyncpy(g_df_func_node_start, g_df_cap * 8, n2); g_df_func_node_start = n2; n3 := alloc(sz); _dyncpy(g_df_func_node_count, g_df_cap * 8, n3); g_df_func_node_count = n3; g_df_cap = nc; } @@ -653,6 +971,44 @@ fn grow_func_cp(needed: int) { nb := alloc(nc * 8); _dyncpy(g_x86_func_cp, g_x86_func_cp_cap * 8, nb); g_x86_func_cp = nb; g_x86_func_cp_cap = nc; } +fn grow_mw_tag_off(needed: int) { + if needed < g_x86_mw_tag_off_cap { return; } + nc : ., mut = g_x86_mw_tag_off_cap * 2; if nc < 128 { nc = 128; } if nc < needed { nc = needed + 128; } + nb := alloc(nc * 8); _dyncpy(g_x86_mw_tag_off, g_x86_mw_tag_off_cap * 8, nb); + g_x86_mw_tag_off = nb; g_x86_mw_tag_off_cap = nc; } + +fn grow_mw_jo_patch(needed: int) { + if needed < g_x86_mw_jo_cap { return; } + nc : ., mut = g_x86_mw_jo_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_x86_mw_jo_pos, g_x86_mw_jo_cap * 8, nb); + g_x86_mw_jo_pos = nb; + nb2 := alloc(nc * 8); _dyncpy(g_x86_mw_jo_dest, g_x86_mw_jo_cap * 8, nb2); + g_x86_mw_jo_dest = nb2; + nb3 := alloc(nc * 8); _dyncpy(g_x86_mw_jo_is_sub, g_x86_mw_jo_cap * 8, nb3); + g_x86_mw_jo_is_sub = nb3; + nb4 := alloc(nc * 8); _dyncpy(g_x86_mw_jo_resume, g_x86_mw_jo_cap * 8, nb4); + g_x86_mw_jo_resume = nb4; + g_x86_mw_jo_cap = nc; } + +fn grow_mw_oc_patch(needed: int) { + if needed < g_x86_mw_oc_cap { return; } + nc : ., mut = g_x86_mw_oc_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_x86_mw_oc_pos1, g_x86_mw_oc_cap * 8, nb); + g_x86_mw_oc_pos1 = nb; + nb2 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_pos2, g_x86_mw_oc_cap * 8, nb2); + g_x86_mw_oc_pos2 = nb2; + nb3 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_s1, g_x86_mw_oc_cap * 8, nb3); + g_x86_mw_oc_s1 = nb3; + nb4 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_s2, g_x86_mw_oc_cap * 8, nb4); + g_x86_mw_oc_s2 = nb4; + nb5 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_dest, g_x86_mw_oc_cap * 8, nb5); + g_x86_mw_oc_dest = nb5; + nb6 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_op, g_x86_mw_oc_cap * 8, nb6); + g_x86_mw_oc_op = nb6; + nb7 := alloc(nc * 8); _dyncpy(g_x86_mw_oc_resume, g_x86_mw_oc_cap * 8, nb7); + g_x86_mw_oc_resume = nb7; + g_x86_mw_oc_cap = nc; } + fn grow_opt_meta(needed: int) { if needed <= g_opt_meta_cap { return; } nc : ., mut = g_opt_meta_cap * 2; @@ -763,6 +1119,41 @@ fn grow_gen_constr(needed: int) { nb := alloc(nc * 8); _dyncpy(g_generic_constr, g_generic_constr_cap * 8, nb); g_generic_constr = nb; g_generic_constr_cap = nc; } +// R2 P3 Task 5:结构/枚举泛型约束侧表(键 = row * MAX_GENERICS + gi;索引空间与函数侧分家, +// 见 globals.cr 表注)。增长式样逐字照 grow_gen_constr(同族第三/第四份,语义同构)。 +fn grow_sgen_constr(needed: int) { + if needed < g_sgen_constr_cap { return; } + nc : ., mut = g_sgen_constr_cap * 2; if nc < 16 { nc = 16; } if nc < needed { nc = needed + 16; } + nb := alloc(nc * 8); _dyncpy(g_sgen_constr, g_sgen_constr_cap * 8, nb); + g_sgen_constr = nb; g_sgen_constr_cap = nc; } + +fn grow_egen_constr(needed: int) { + if needed < g_egen_constr_cap { return; } + nc : ., mut = g_egen_constr_cap * 2; if nc < 16 { nc = 16; } if nc < needed { nc = needed + 16; } + nb := alloc(nc * 8); _dyncpy(g_egen_constr, g_egen_constr_cap * 8, nb); + g_egen_constr = nb; g_egen_constr_cap = nc; } + +// R2 P3 Task 5(Step 4):调用点绑定侧表增长(i64 顺序追加,无洞——与上面两张稀疏表不同) +fn grow_gen_binds(needed: int) { + if needed < g_gen_binds_cap { return; } + nc : ., mut = g_gen_binds_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_gen_binds, g_gen_binds_cap * 8, nb); + g_gen_binds = nb; g_gen_binds_cap = nc; } + +// 结构/枚举第 gi 个泛型形参的约束名 ni(-1 = 无约束/越界)。读取面护栏(照 SI 访问器惯例: +// 越界不越读——gi 上限 MAX_GENERICS、行下标上限计数)。 +fn si_gen_constr(n: int, gi: int) -> int { + if n < 0 || gi < 0 || gi >= MAX_GENERICS { return -1; } + idx := n * MAX_GENERICS + gi; + if idx >= g_sgen_constr_count { return -1; } + return r64(g_sgen_constr, idx * 8); } + +fn ei_gen_constr(n: int, gi: int) -> int { + if n < 0 || gi < 0 || gi >= MAX_GENERICS { return -1; } + idx := n * MAX_GENERICS + gi; + if idx >= g_egen_constr_count { return -1; } + return r64(g_egen_constr, idx * 8); } + fn grow_sg(n: int) { if n < g_sg_cap { return; } nc := g_sg_cap; diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 12f3982f..fb7af00e 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -14,6 +14,32 @@ g_structs : string, mut; g_struct_count : int, mut; g_struct_cap : int, mu g_enums : string, mut; g_enum_count : int, mut; g_enum_cap : int, mut; g_syms : string, mut; g_sym_count : int, mut; g_sym_cap : int, mut; g_types : string, mut; g_type_count : int, mut; g_type_cap : int, mut; +// 类型行表布局单源(R2 P4 Task 2):24B/条 {kind,data,extra}(i64×3)—— +// alloc_type(checker.cr)/grow_types(dyn_arr.cr)/TYPE 段序列化与 dump(ccr_io.cr) +// 三方共用,改一处即全改(此前为三处字面量 24/8/16 并行)。 +ESZ_TYPE_ROW : int = 24; +OFF_TR_KIND : int = 0; OFF_TR_DATA : int = 8; OFF_TR_EXTRA : int = 16; + +// R2 P4 Task 2:TYPE(7) 段体缓冲(D18 解耦——段体**构造**在 corec-only 的 +// ccr_types.cr(引用桥接层 sh_term_of_ti:corearch 无此层),本文件只声明载体, +// ccr_io.cr(corearch 也链接)只按段表搬运/解析)。缓冲**含段体首字段** +// (row_count u32),故段体大小 ≡ 缓冲长度(ccr_type_seg_size 单源)。 +// 声明位置约束:本文件恒在最前(双 concat 共享面)——若把两件挪进 ccr_types.cr +// (corearch 清单不含),ccr_io.cr 的引用即 corearch 侧 N06 静默未定义(B.6 同族)。 +g_ccr_type_seg : string, mut; g_ccr_type_seg_len : int, mut; +// R2 P4 Task 3:IFACE(8) 段体缓冲(同 TYPE 的 D18 解耦——段体**构造**在 corec-only 的 +// ccr_types.cr(引用桥接层 sh_iface_sig_*/形状构造点),ccr_io.cr 只按段表搬运/解析)。 +// 缓冲**含段体首字段**(native_count u32)⇒ 段体大小 ≡ 缓冲长度(ccr_iface_seg_size 单源)。 +g_ccr_iface_seg : string, mut; g_ccr_iface_seg_len : int, mut; +// R2 P6 Task 3(β):NOD 项索引装填缓冲(同 TYPE/IFACE 的 D18 解耦——**装填**在 +// corec-only 的 ccr_types.cr(引用桥接层 sh_tk_face_of_code/sh_term_of_ti),本文件 +// 只声明载体,ccr_io.cr 保存侧按节点序搬运落盘(盘记录 +28 i32 槽)。 +// 语义 = TYPE 段**文件空间**的项索引:-1 = 无项(非 face-0 节点 / 暖态缺行—— +// 辅码槽保真码);≥0 = 该节点原子行对应的项 DAG 行号(写侧与盘上派生码同源, +// 读侧 load_ccr 在 TYPE 段解析后做**一致性硬校验**:域外或 atom_of ≠ 码 ⇒ 拒绝)。 +// 声明位置约束同上方两件:本文件恒在最前(双 concat 共享面)——挪进 ccr_types.cr +// 即 corearch 侧 N06 静默未定义(B.6 同族)。 +g_ccr_nod_item : string, mut; g_ccr_nod_item_count : int, mut; g_ast : string, mut; g_ast_count : int, mut; g_ast_cap : int, mut; g_tokens : string, mut; g_token_count : int, mut; g_tok_cap : int, mut; g_errors : string, mut; g_error_count : int, mut; g_err_cap : int, mut; @@ -24,6 +50,59 @@ g_ir_locals : string, mut; g_ir_local_count : int, mut; g_ir_local_cap : int, g_ir_globals : string, mut; g_ir_global_count : int, mut; g_ir_global_cap : int, mut; g_ir_str_consts : string, mut; g_ir_str_const_count : int, mut; g_ir_str_const_cap : int, mut; +// ── 可选运行期表示面(R2 P4 Task 5;裁决 5「解包侧判表示」,附录 B.7)── +// 事实:`Some(1)`/`None` 走 IR_MAKE_ENUM 对象([tag][payload…]),而 `T?` 槽里的 +// **裸值**仍是裸值 ⇒ 同一 `T?` 值两种表示;`Some` 臂的载荷解包把裸值当对象解引用 +// (双路径 SIGSEGV,check/build rc=0 零诊断)。 +// 机制:**表示位** = 每个可选槽(局部/形参/调用结果)配一个普通 int IR 变量的 +// **运行期值**(0 = 裸值、1 = 装箱)。写点置位(静态已知则常量、否则从源槽表示位 +// 拷贝)、**解包点分派**(`match` 的 Some/None 臂、`?`)——纯 IR,无新 opcode、 +// 无 ABI 改动,故 ELF 后端与解释器天然同源。 +// g_ir_var_rep:IR var → 表示位变量索引(i64 数组;-1 = 无表示位)。无表示位的槽 +// 走既有装箱假定(未覆盖面:结构体字段/数组元素/全局槽/match 结果/惰性 thunk, +// 逐形态实测入报告——响亮失败或既有正确值,不新增静默错值)。 +g_ir_var_rep : string, mut; g_ir_var_rep_cap : int, mut; +// g_ir_var_decl_ti(容量批 T2):IR var → **声明类型索引**(i64 数组;-1 = 无注解/未知)。 +// 用途 = 可选聚合槽写点装箱的「目标槽类型」判定:数组/切片元素类型只能由**声明** +// (`a : [int?;2]`)忠实给出(字面量推出的元素类型对可选元素退化为对象占位)。 +// 仅进程内状态,零布局变更;非可选程序不读取本表(零足迹)。 +g_ir_var_decl_ti : string, mut; g_ir_var_decl_cap : int, mut; +// 容量批 T3(E-3 / 裁-CAP-2 (a)):结构体字段**侧表**——字段槽区自定长内嵌记录迁出 +// (记录内 `OFF_SI_FIELD_BASE` 槽 = 本表起始行;记录尺寸与既有 OFF_* 不变)。 +// 三条扁平表按「全局字段行」索引(高水位 = g_si_f_used)。仅进程内状态;`.ccr` 盘面 +// 不变(盘面本就逐字段变长,见 ccr_io.cr 的 SYM 布局注)。 +g_si_f_names : string, mut; g_si_f_types : string, mut; g_si_f_nodes : string, mut; +g_si_f_used : int, mut; g_si_f_cap : int, mut; +// 容量批 T3:枚举变体**侧表**(同 struct 形态)——`OFF_EI_VARIANTS` 槽改义为 `variant_base`; +// 变体面三条(名/载荷基址/载荷计数)+ 载荷面两条(裸码/类型节点)。 +g_ei_v_names : string, mut; g_ei_v_tbase : string, mut; g_ei_v_tcount : string, mut; +g_ei_vt_types : string, mut; g_ei_vt_nodes : string, mut; +g_ei_v_used : int, mut; g_ei_vt_used : int, mut; g_ei_v_cap : int, mut; +g_ei_vt_cap : int, mut; +// 容量批 T3:match 覆盖位**无界位图**(旧态 = 单 int 的 2^vi 位图 ⇒ ≥63 变体溢出, +// e70 实测 SIGFPE)。位图按「位下标 vi」寻址;`g_cov_len` = 当前已用位数,**栈纪律**: +// mc_begin 存长度、mc_end 清零并回退(嵌套 match 安全)。 +g_cov_bits : string, mut; g_cov_cap : int, mut; g_cov_len : int, mut; +// g_optrep_on:本编译单元是否启用表示面(AST 预扫:EXPR_OPTIONAL / `Some` / `None`)。 +// 关 = **零足迹**(不注册隐藏全局、不发任何表示指令)⇒ 非可选程序的发射面逐字节不变。 +g_optrep_on : int, mut; +// g_optrep_ret_cell:返回表示的信道单元(IR 全局 var;-1 = 未注册)。返回点写、 +// 调用点读(读点紧跟 IR_CALL ⇒ 跨嵌套调用不被覆写;写点为值求值完成后 → 语义确定)。 +g_optrep_ret_cell : int, mut; +// g_optrep_arg_cell0 / g_optrep_arg_count:形参表示信道(每形参位一个 IR 全局 var, +// 连续分配)。调用点写、被调序言读(先于任何可能改写信道的调用)。 +g_optrep_arg_cell0 : int, mut; g_optrep_arg_count : int, mut; +// g_cur_ret_opt:当前函数的返回类型是否可选(ir_gen_func 设置;返回点写信道用)。 +g_cur_ret_opt : int, mut; +// 表示码(rep_enc_of_expr 的返回):>= 0 = 从该表示位槽**拷贝**;oe_bare() = -1 = 裸值 +// 常量;oe_boxed() = -2 = 装箱常量。**用函数而非文件级常量**:Python bootstrap 的 +// gen_let_decl 只对**裸字面量**记 constant_value(`-1` 解析成 UnaryOp ⇒ 丢初值 ⇒ +// StackAsmGen 发 `.quad 0`)⇒ 自举链二进制里的负值文件级常量**静默读 0**(实测踩过: +// 两常量读 0 后 `Some` 初值表示位与裸值同码;同类既存面 = cir_cache.cr 的 +// CIR_CACHE_MAGIC,其在自举二进制里同读 0——已登记报告)。返回值表达式不受该限制。 +fn oe_bare() -> int { return 0 - 1; } +fn oe_boxed() -> int { return 0 - 2; } + // Parser/checker dynamic arrays (shared between corec and corearch builds) g_global_lets : string, mut; g_global_let_count : int, mut; g_global_lets_cap : int, mut; g_loop_stack : string, mut; g_loop_depth : int, mut; g_loop_stack_cap : int, mut; @@ -36,6 +115,35 @@ g_borrow_scope_markers : string, mut; g_borrow_scope_depth : int, mut; g_borrow g_ifaces : string, mut; g_iface_count : int, mut; g_iface_cap : int, mut; g_impl_for : string, mut; g_impl_for_count : int, mut; g_impl_for_cap : int, mut; g_generic_constr : string, mut; g_generic_constr_count : int, mut; g_generic_constr_cap : int, mut; +// R2 P3 Task 5:结构/枚举泛型约束侧表(**索引空间与函数侧分家**——g_generic_constr 的键 = +// `fi * MAX_GENERICS + gi`,若共用同一缓冲,struct/enum 行号与函数行号会互相覆盖)。 +// 键 = `si * MAX_GENERICS + gi`(struct)/ `ei * MAX_GENERICS + gi`(enum),值 = 约束名 ni +// (-1 = 无约束;稀疏:未写槽不读)。**语义边界**(P3 计划 Task 5):保留(本表)归 P3a; +// `T: I` 的**满足判定**走统一入口 `iface_satisfies`(**R2 P3b Task 0 已交付**,type_engine.cr +// 的轴 C)——实例化点由 `gen_inst_constr_satisfied` 直取真值(本批起**真判定**:可证违反 ⇒ +// TG02 软诊断),函数调用点仍回落既有 check_iface 路径(措辞逐字不动,切换归 Task 6 Step 3)。 +// 见 checker.cr 的 gen_constr_* 段。 +g_sgen_constr : string, mut; g_sgen_constr_count : int, mut; g_sgen_constr_cap : int, mut; +g_egen_constr : string, mut; g_egen_constr_count : int, mut; g_egen_constr_cap : int, mut; +// R2 P3b Task 0(P2b 交接面回补):**横切轴形状条目表**(名字 ni → 形状类型项), +// `iface_satisfies` 的轴 A。与 `g_ifaces`(用户接口 = 方法集)平行:本表条目 = 一个**类型项** +// (如 `sequence` ⇒ `⊤_SEQUENCE`),满足判定 = 一条包含判定(spec §2.2)。 +// 条目由 Task 2 注册(`iface_shape_register`)——**本批零条目**(空表 ⇒ 轴 A 恒 -1 = 不判, +// 三态纪律不破)。⚠ 注册名须来自**已驻留**的名字 ni(lexer 已 intern 的源码标识符); +// 本表初始化路径**不得** `str_intern`(.ccr STR 段硬判据,照 iface_registry.cr 的 name_ni 注)。 +g_iface_shape_names : string, mut; g_iface_shape_terms : string, mut; +g_iface_shape_count : int, mut; g_iface_shape_cap : int, mut; +// R2 P3 Task 5(Step 4):调用点泛型绑定侧表(monomorph 实例键类型项化)。 +// 由 checker 的 infer_gen_call 按**被调方泛型形参声明序**登记:每调用点 gc 个槽,值 = 实参 +// 推断出的**类型行 ti**(-1 = 未绑定)。起始下标写入调用节点 `ast_int_val`(调用节点的该槽 +// 在本批之前只被**死码** `ir_gen.cr find_or_create_mono_func` 读——该函数零调用者,登记见 +// 报告)。ir_gen 侧据 ti 生成规范实例键 `inst_key_of_ti`,并以 ti 型替换(`g_gen_subst_tis`) +// 取代纯名字文本替换(旧法:非原生实参一律塌缩 "int" ⇒ 异型实例折叠 + 错替换)。 +g_gen_binds : string, mut; g_gen_binds_count : int, mut; g_gen_binds_cap : int, mut; +// R2 P3 Task 5:约束违反诊断去重侧表(开放寻址,16B/条 {key, 1};key = node*MAX_GENERICS+gi)。 +// 生命周期同 g_named_dedup(init_types → gen_constr_seen_reset 置 cap=0 惰性重建;键含 AST +// 节点下标 ⇒ 跨编译期复用即失效)。见 checker.cr 的 gen_constr_seen_* 段。 +g_constr_seen : string, mut; g_constr_seen_cap : int, mut; g_constr_seen_count : int, mut; g_gen_params : string, mut; g_gen_param_count : int, mut; g_gen_param_cap : int, mut; g_checker_current_fi : int, mut; g_unsafe_depth : int, mut; @@ -60,8 +168,44 @@ g_ir_func_var_count : string, mut; g_ir_func_var_count_cap : int, mut; g_ir_func_param_count : string, mut; g_ir_func_param_count_cap : int, mut; g_ir_func_count : int, mut; +// v6 数据基础:存在区间表(compute_live_ranges 填充,alloc_registers 读本表—— +// 填充方 = 内核 ent_kernel.cr(双 concat 共享:corec 写侧 save_ccr 直调 +// compute_live_ranges/compute_entries,corearch alloc_registers 读本表)。 +// 布局:每函数一段,段内每「函数内 var」两条 i64(first_ref/last_ref,函数内指令序 +// ——坐标限定:0..instr_count-1 的函数内下标,见 ent_kernel.cr live_range_slot); +// func_i 段起始 = Σ var_count[0..func_i),不乘固定稠密系数。未使用 var 为 -1。 +// 与 g_ir_slice_lens 同风格:16B 记录 + grow 函数(grow_live_ranges 在 ent_kernel.cr)。 +g_ir_live_ranges : string, mut; +g_live_range_count : int, mut; +g_live_range_cap : int, mut; + +// v6 条目表(条目版本化,compute_entries 填充——compute_live_ranges 尾部对全部 +// 函数运行;v7 ENT 主干化后 .ccr ENT = 实记录,corec 写侧 save_ccr 直调内核 +// 填充再落盘——填充方 = 内核 ent_kernel.cr,双 concat 共享)。24B/条(六字段各 4B, +// LE 存取:写 w32、读 buf_read_i32——r32 读在 bootstrap 产物中丢符号扩展, +// 见 ent_kernel.cr ent_* 访问器注记;与 v6 格式记录逐字节一致),字段偏移见 dyn_arr.cr: +// var_idx(0) u32 = 全局 IR 变量索引(g_ir_vars 序) +// def_instr(4) i32 = 定值指令全局索引——IR_ALLOC(dest=var) 或 IR_STORE(s1=var) +// (IR_STORE 定值形态 = ρ(s1):=ρ(s2),目标在 s1、dest 恒 -1, +// 见 docs/ir-op-semantics.md §2.1);-1 = 无定值条目 +// live_start(8)/live_end(12) i32 = 版本存在区间:全局指令序闭区间 +// [def_j, min(def_{j+1}−1, last_ref)](与 Task 1 区间表同 +// 闭区间语义,坐标转全局);无定值条目 = [first_ref, last_ref] +// home(16) i32 = 分配器回填的槽位(-1 = 未分配,Task 5 回填) +// flags(20) u32 = 0(位 0 预留:无配方,条款 4b) +// 布局:每函数一段(func_i 升序追加),段界在 g_ir_func_entry_start/count(8B/条)。 +// 版本号不落盘:同 var 条目按 def_instr 升序(扫描序即升序),版本序 = 组内 1-based 序号。 +g_ir_entries : string, mut; g_entry_count : int, mut; g_entry_cap : int, mut; +g_ir_func_entry_start : string, mut; g_ir_func_entry_count : string, mut; +g_ir_func_entry_cap : int, mut; + // Module system arrays (dynamic byte buffers) DIAG_REC_SIZE : int = 40; // g_diags 记录字节数:[ec(8) msg(8) line(8) col(8) file_id(8)] +// fail-closed 判据线(FC 批 T2):豁免表的**生效面**标签(消费点 = main.cr 的硬判定循环 +// 经 diag_gate_exempt(ec, scope);表 = diag.cr)。裁-FC-1 = (C):run_frontend 只以 +// GATE_SCOPE_BUILD 调用;check 面的 rc 规则(计数 > 0)不消费本表 ⇒ check 基线零变化。 +GATE_SCOPE_BUILD : int = 1; // build/ccr/cir/run 面(经 run_frontend 的 hard 判定) +GATE_SCOPE_CHECK : int = 2; // check 面(**本批不消费**:仅登记;见 diag_gate_exempt 表注) g_diags : string, mut; g_diag_count : int, mut; g_diag_cap : int, mut; g_files : string, mut; g_file_count : int, mut; g_file_cap : int, mut; g_mods : string, mut; g_mod_count : int, mut; g_mod_cap : int, mut; @@ -117,6 +261,51 @@ g_x86_global_cap : int, mut; g_x86_global_off : string, mut; g_x86_global_off_cnt : int, mut; g_x86_global_off_cap : int, mut; g_x86_func_offsets : string, mut; g_x86_func_offsets_cap : int, mut; g_x86_func_off_count : int, mut; g_x86_emit_vars : string, mut; g_x86_emit_vars_cap : int, mut; g_x86_emit_var_count : int, mut; g_x86_emit_stack_size : int, mut; +// int 多字 M1(tagged 槽,D1a):潜在多字变量的旁路 tag 区(栈帧尾附加)。 +// 见 docs/superpowers/plans/2026-09-06-int-multiword-m1.md Task 1 与 +// docs/superpowers/specs/2026-09-06-int-multiword-backend-design.md(D1a/D3)。 +// g_x86_mw_tag_off:当前函数的 per-局部-变量 tag 字节偏移表(i64 数组,按 +// 局部位置 v-var_start 索引;值 = 相对 rbp 的负字节偏移;-1 = 非 tagged)。 +// g_x86_mw_tag_count:当前函数 tagged 变量数(tag 区字节数 = 每变量 1 字节)。 +g_x86_mw_tag_off : string, mut; g_x86_mw_tag_off_cap : int, mut; +g_x86_mw_tag_count : int, mut; +// g_x86_mw_jo_*:int 多字 M1 Task 2/3——tagged int add/sub 快路径溢出跳 +// (jo 0F 80 rel32)站点记录表:emit_instr 发射 jo 时记录站点现场,frame.cr +// pf_epilogue 于该函数尾声(慢路径块位置后知)按站点发射真实 2-limb 修正块 +// 并统一回填(波 1 Task 3 起帧尾声/函数尾块归 frame.cr) +// (g2_init 清零——ret_patch 同款)。记录 = 4 条并行 i64 数组(共享 cap, +// 同步增长,索引 = 站点点序 = 指令序,单指令至多 1 站点): +// g_x86_mw_jo_pos — jo 指令绝对缓冲位置(rel32 字段 = pos+2、jo 长 6); +// g_x86_mw_jo_dest — 快路径 store 的 dest 变量索引(块内回存目标: +// 发射块时经 g2_slot(dest) 现算——含 O1/O2 reg 形态, +// 无需入录槽形态本身); +// g_x86_mw_jo_is_sub — 1 = 该站点为 sub(高 limb 修正规则不同:add 溢出 +// 高 limb = CF ? -1 : 0;sub = CF ? 0 : -1——CF/符号 +// 关系见 e2_mw_slow_block(tag2l.cr)注释推演); +// g_x86_mw_jo_resume — 块处理完成后跳回点(= 该站点快路径 store e2_st +// 之后的绝对位置——elf.cr 于指令发射完、块位置已知 +// 时填写,块发射时直接回填 jmp rel32)。 +// 见 plan Task 2/3 与 specs/2026-09-06-int-multiword-backend-design.md(D1a/D3)。 +g_x86_mw_jo_pos : string, mut; g_x86_mw_jo_cap : int, mut; g_x86_mw_jo_count : int, mut; +g_x86_mw_jo_dest : string, mut; g_x86_mw_jo_is_sub : string, mut; +g_x86_mw_jo_resume : string, mut; +// g_x86_mw_oc_*:int 多字 M1 Task 4——消费者站点(tagged 操作数读 tag)记录表: +// add/sub/比较站点在快路径前发 tag 检查(test byte + jne 0F 85 rel32——tag=1 +// = 操作数为 2-limb),jne 目标 = 该站点函数尾 2L 块(位置后知——jo 同款 +// 回填机制)。记录 = 8 条并行 i64 数组(共享 cap,索引 = 站点序 = 指令序, +// 单指令至多 1 站点): +// g_x86_mw_oc_pos1/pos2 — 该站点第 1/2 个检查 jne 的绝对缓冲位置 +// (-1 = 无——操作数行 untagged 或 s1==s2); +// g_x86_mw_oc_s1/s2/dest — 站点操作数行与 dest(块内现算槽形态/回存目标); +// g_x86_mw_oc_op — 站点 op(OP_ADD/OP_SUB = 128 算术块; +// OP_EQ..OP_GE = 128 比较块); +// g_x86_mw_oc_resume — 块完成后跳回点(= 该站点指令尾,同 jo)。 +// 见 plan Task 4 与 e2_mw_opnd_block(tag2l.cr——波 1 Task 4 自 instr.cr 整搬)。 +g_x86_mw_oc_pos1 : string, mut; g_x86_mw_oc_pos2 : string, mut; +g_x86_mw_oc_cap : int, mut; g_x86_mw_oc_count : int, mut; +g_x86_mw_oc_s1 : string, mut; g_x86_mw_oc_s2 : string, mut; +g_x86_mw_oc_dest : string, mut; g_x86_mw_oc_op : string, mut; +g_x86_mw_oc_resume : string, mut; g_x86_ret_patch_pos : string, mut; g_x86_ret_patch_cap : int, mut; g_x86_ret_patch_count : int, mut; g_x86_call_patch_pos : string, mut; g_x86_call_patch_name : string, mut; g_x86_call_patch_count : int, mut; g_x86_call_patch_cap : int, mut; @@ -136,6 +325,7 @@ g_df_node_region : string, mut; g_df_node_region_cap : int, mut; // per DFNod g_cur_sg : int, mut; // currently open region id (-1 = none) g_cur_ret_ti : int, mut; // 当前函数的返回 TI(dex 边界转换用,数值迁移 Task 4) g_last_state_node : int, mut; // last side-effect DFNode id (VSDG state chain; -1 = none) +g_df_state_finalized : int, mut; // df_state_finalize 幂等旗标(0 = 未跑;数据流图重建每编译恰一次——见 dataflow.cr) // Plugin extension registry: tags and return types from .so/stdlib plugins // Each entry: 24 bytes = [ns_ni, name_ni, data_ni] @@ -149,6 +339,110 @@ g_offsets : string, mut; g_offsets_count : int, mut; g_offsets_cap : int, mut; g_pa_alloc_count : int, mut; g_pa_alloc_nodes : string, mut; g_pa_alloc_nodes_cap : int, mut; +// R2 P0 类型项引擎:类型项 DAG 表 + 结构哈希索引 + 判定 memo + 预算守卫 +// (表 = 48B/条 {tag,a,b,c,d,hash},见 type_terms.cr;扩容 grow_type_terms/ +// grow_tt_index 亦在该文件——引擎自持,共享面 dyn_arr.cr 零改动;P0 只建层 +// ——checker/ir_gen/后端零消费者)。 +// 惰性 memo 槽(g_tt_top/g_tt_nil)+ 零初值 ready 位:bootstrap 后端只认字面量 +// 常量初值(`= -1` 会被降级为 0,见 bootstrap x86_64_stack_asm 的 .quad 0 路径), +// 故以 0 = 未建 + ready 位表达「未初始化」,与本仓库 g_home_dir_ok/g_ext_inited 同式。 +g_type_terms : string, mut; g_type_term_count : int, mut; g_type_term_cap : int, mut; +g_tt_index : string, mut; g_tt_index_cap : int, mut; g_tt_index_count : int, mut; +g_tt_nil : int, mut; g_tt_nil_ok : int, mut; +g_tt_top : int, mut; g_tt_top_ok : int, mut; +g_ty_memo_keys : string, mut; g_ty_memo_vals : string, mut; +g_ty_memo_count : int, mut; g_ty_memo_cap : int, mut; g_ty_memo_ok : int, mut; +g_ty_steps : int, mut; g_ty_budget_max : int, mut; g_ty_exhausted : int, mut; +g_ty_lits : string, mut; g_ty_lits_cap : int, mut; g_ty_lits_count : int, mut; +g_ty_uncovered : int, mut; // 未覆盖面命中位(如 AK_NAMED 具体行不展开)——P0 只登记不消费 + +// R2 P2b Task 1:本质条目表(native interface entries,spec §2)——40B/条 × 5 字段 +// {ak, ti_row, name_ni, lit_code, ops},布局常量与表本体(iface_registry.cr)+ 查询 API +// (iface_*)同文件;此处只声明全局(**声明位置约束**:bootstrap 名字解析对变量按声明序、 +// 跨文件前向引用不成立——g_purity_inst 先例,globals.cr:290-292;消费者在 checker.cr 之后 +// 的层,且本文件恒在最前)。 +// **不 alloc g_types 行、不占类型行号**(先例 init_builtins 的 g_rt_builtin_* 旁表) +// ⇒ `.ccr` 类型段/行号零扰动。ok 位 = 惰性建表旗标(0 初值惯例,见上方 g_tt_nil_ok 注记)。 +g_iface_entries : string, mut; g_iface_entry_count : int, mut; g_iface_registry_ok : int, mut; + +// R2 P2b Task 4:操作许可位集的**位下标常量**(spec §2.1「操作许可」;表数据在 +// iface_registry.cr 的 ops 列,消费点在 checker.cr 的三个门)。 +// 位下标约定(写死,跨 Task 一致): +// OP_* 1..14 → **直接用其值**(OP_ADD=1 … OP_ASSIGN=14;OP_AND=12/OP_OR=13 同理, +// 故**不另设** IP_LOGIC——逻辑族的谓词就写 iface_permits(kind, OP_AND))。 +// 注:OP_ASSIGN(14) 位**不使用**——赋值兼容 = type_compat_strict(P2a 引擎面)。 +// UOP_* 1..4 → 经 +IP_UOP_BIAS 偏置(UOP_NEG→21 / UOP_NOT→22 / UOP_REF→23 / UOP_DEREF→24)。 +// 新族 25.. → IP_INDEX=25 / IP_INDEX_RANGE=26 / IP_FIELD=27 / IP_METHOD=28 / IP_AS=29 +// / IP_COND=30(真值性:`if` 现状收 bool|int)/ IP_COND_BOOL=31(严格 bool: +// `while` 现状**只收 bool**——两条规则现状不同,**不得合并成一个「更统一」的位**: +// 合并 = 收紧 `if` 或放宽 `while`)。 +// **声明位置约束**(Task 1 实测登记):bootstrap 名字解析对变量/常量按声明序、跨文件前向引用 +// 不成立 —— checker.cr 位于 iface_registry.cr **之前**,故 IP_* 必须声明在本文件(checker.cr 可直接 +// 引用 `IP_COND` 等);**函数**不受此限(checker.cr 可前向引用 iface_* 函数)。 +IP_UOP_BIAS : int = 20; +IP_INDEX : int = 25; IP_INDEX_RANGE : int = 26; IP_FIELD : int = 27; +IP_METHOD : int = 28; IP_AS : int = 29; IP_COND : int = 30; IP_COND_BOOL : int = 31; + +// R2 P1:checker ti → 类型项**桥接缓存**(16B/条 {ti, term};探测/装填因子守卫/扩容重放见 +// ty_shadow.cr——桥接层自持,与引擎 g_tt_index 同式)。**这是生产面**(判定路径无条件 +// 调用 sh_term_of_ti),非影子调试面。 +// **R2 P5 Task 5:原名 `g_shadow_map`/`g_shadow_map_cap`/`g_shadow_entries` 改名**(影子 +// 判定通道已随本任务下线;`sh_` 前缀现为历史前缀,语义 = 「桥接/类型项层」)。影子期的 +// 调试计数 `g_shadow_hits` 一并删除(其唯一用途 = 影子对账的命中统计)。 +// entries 只增不减、恒等于占用槽数 → 兼作扩容判据(不另设 count 全局)。 +g_term_map : string, mut; g_term_map_cap : int, mut; +g_term_map_entries : int, mut; + +// R2 P5 Task 2(D19/D23):DFNode 类型面单槽化的拆分出参 + 建项失败位。 +// 出参(Core 无多返回值):`sh_tk_split` 先置默认(-1 / 0)再写 ⇒ 无残留状态; +// 读点 = ir_gen.cr 的 emit 与 cir_cache.cr 的装载侧(唯一两个分流出参消费面)。 +// g_tk_face_fail:类型面 op 的 tk **不可译**(越出类型表 / 桥接缺口,D23)⇒ 计数 +1, +// save 侧拒绝落盘(rc=1 + 诊断,计数入消息);复合行(域内可译、项不可逆)= 正常 +// 路径**不置位**。复位 = init_types()(同 g_replace_* 的生命周期)。 +g_sh_slot_term : int, mut; g_sh_slot_aux : int, mut; +g_tk_face_fail : int, mut; + +// **R2 P5 Task 5 删除**(本处 = 影子判定通道的全局态):`g_shadow_on` / `g_shadow_site` / +// 四分类桶(total/agree/old_stricter/old_looser/unknown + bridge·engine·uncovered·budget +// 拆因)/ 差异环形缓冲(ring/cap/count)/ 站点直方图(site_counts/cap)。影子层 = P1 的 +// 迁移期对拍仪器(对照物 = `type_equal_legacy`):对照物已在 P5 Task 4 删除 ⇒ 对拍停摆 +// (全语料 `decisions=0`,见 p5-task5-report 的 gate 节)⇒ 本任务整体下线。 +// 判定面回归网自此 = 冻结基线同源对拍 + 行为探针 + 突变控制(见 src/ci/run.sh 的自述)。 + +// R2 P2a Task 1(F1):同名 TYP_NAMED 建表去重侧表(开放寻址,16B/条 {name_idx, ti})。 +// 唯一分配点 alloc_named_type 查表命中即复用存量行 → 同一类型名(struct 字面量/泛型 +// 应用基型在多处出现)恒占一行。P1 影子对拍 9/9 差异的根因正是「同名多行」:桥接层按行 +// 建原子(AK_NAMED 的 b 槽 = 行号)→ 引擎把两行当互异命名类型 → 判不了(unknown)。 +// 探测/装填因子守卫/重建重放/回写前重探见 checker.cr(与 ty_shadow.cr 的 g_term_map +// 同式同因——P0/P1 三件套缺一即可能挂死)。count 只增不减、恒等于占用槽数 → 兼作扩容 +// 判据(不另设 count 之外的全局)。init_types() 置 cap=0 → 类型表重置(行号空间作废) +// 时侧表随之惰性重建,防「陈旧 name→ti 复用」把已失效行号当命中(silent miscompile)。 +g_named_dedup : string, mut; g_named_dedup_cap : int, mut; g_named_dedup_count : int, mut; + +// R2 P3 Task 0:引擎展开层的 per-ti 缓存(开放寻址,16B/条 {ti, 展开项})。 +// **第二张表**(与 g_term_map 键空间相同 = ti,但**值语义不同**):本表存**展开项** +// (struct → AK_PRODUCT / enum → AK_SUM 域,见 ty_shadow.cr 展开段),桥接表存**原子名义项** +// (命名类型的等价面身份)。同一 ti 两值不同 ⇒ **不得**共用一张表——共用即让展开项进入 +// 等价判定 = 同形不同名类型被判等价 = 语义漂移(裁错边界,见 ty_shadow.cr 头注)。 +// 生命周期同 g_term_map:init_types() 置 cap=0(sh_unf_map_reset)→ 类型表重置(行号空间 +// 作废)时惰性重建,防陈旧 ti→展开项跨请求复用。探测/装填守卫/重建重放/回写重探见 +// ty_shadow.cr(与桥接缓存同式同因;count 只增不减、恒等于占用槽数)。 +g_unf_map : string, mut; g_unf_map_cap : int, mut; +g_unf_entries : int, mut; g_unf_hits : int, mut; + +// 效应/纯度修正 Task 1(P0 插队批):泛型实例 → 源映射侧表(24B/条 +// {src_fi, type_args_ni, inst_fi},布局同 monomorph.cr 的 g_gen_instances)。 +// monomorph 于实例创建时追加(gen_create_instance),compute_all_purity 读它回填 +// 泛型源函数的纯度(源 = 实例合取 ⇒「实例与源同值」)。 +// **声明位置约束**:bootstrap 的名字解析按声明序(跨文件前向引用不成立)—— +// checker.cr 的 compute_all_purity 要用本表,故它必须声明在 checker.cr 之前 +// (globals.cr)。grow 帮助函数放 monomorph.cr(函数前向引用合法)。 +g_purity_inst : string, mut; g_purity_inst_count : int, mut; g_purity_inst_cap : int, mut; + +// R2 P2a Task 3 的判定回落计数(g_replace_unknown/g_replace_bridge)**R2 P5 Task 4 删除**: +// 清零判据成立(全语料 replace_*=0)⇒ 回落面已删(legacy 一并删除);未知面改走 P-A 硬错 +// (EC_ICE_TY_INDET,见 checker.cr 的 ty_indeterminate_report)。 + fn grow_plugin_tags(needed: int) { if needed < g_plugin_tag_cap { return; } ncap : ., mut = g_plugin_tag_cap * 2; if ncap < 8 { ncap = 8; } if ncap < needed { ncap = needed + 8; } @@ -206,6 +500,12 @@ fn reset_frontend_state() { g_sym_count = 0; g_type_count = 0; g_iface_count = 0; g_impl_for_count = 0; g_method_count = 0; g_type_alias_count = 0; g_generic_constr_count = 0; g_gen_param_count = 0; + // R2 P3 Task 5:三条新侧表与声明表同生命周期(si/ei/调用点下标跨编译复用——不清则 + // 陈旧约束/绑定被新声明命中,静默错判;照 g_generic_constr_count 同址同因) + g_sgen_constr_count = 0; g_egen_constr_count = 0; g_gen_binds_count = 0; + // R2 P3b Task 0:形状条目表与声明表同生命周期(注册名 = 驻留 ni,跨编译复用会命中陈旧条目 + // ⇒ 静默错判;照 g_sgen_constr_count 同址同因)。`g_strs` 驻留表本身不重置(名字 ni 稳定)。 + g_iface_shape_count = 0; g_global_let_count = 0; g_loop_depth = 0; g_scope_depth = 0; g_borrow_count = 0; g_holder_count = 0; @@ -213,6 +513,10 @@ fn reset_frontend_state() { g_gen_map_count = 0; g_dyn_type_set_count = 0; g_mod_func_count = 0; g_mod_path_count = 0; g_file_count = 0; g_mod_count = 0; + // Task 1 侧表与 g_func_count 同生命周期(fi 下标会整体复用——不清则陈旧 + // 映射把新函数的纯度算到旧实例上,静默错标) + g_purity_inst_count = 0; + g_df_state_finalized = 0; // 与上同理:链重建每编译恰一次(幂等旗标随前端状态复位) g_seg_count = 0; g_line_count = 0; g_unsafe_depth = 0; g_alloc_pts_cap = 0; diff --git a/src/compiler/iface_axis.cr b/src/compiler/iface_axis.cr new file mode 100644 index 00000000..b54dd7e3 --- /dev/null +++ b/src/compiler/iface_axis.cr @@ -0,0 +1,210 @@ +// === iface_axis.cr === +// R2 P4 Task 0(引擎核纯化拆分,链接面纯化):iface 满足判定簇 + `iface_kind_of` 的 corec-only 宿主。 +// +// 迁移面(**逐字搬迁,零语义改动**;出处以搬迁时点行号标注): +// · type_engine.cr:613-772 —— `IFACE_SAT_BUDGET` + `iface_satisfies` / `iface_satisfies_term` / +// `iface_satisfies_ti` / `iface_find_method` / `iface_user_satisfies` / `iface_user_satisfies_ii` +// (含各段头注与轴分派说明); +// · iface_registry.cr:271-293 —— `iface_kind_of`(+ 头注)。 +// 原位各留一行指路注释(type_engine.cr 尾 / iface_registry.cr 的 ty_code_to_ti 之后)。 +// +// **为何移出**(R2 P4 计划 D16):type_engine.cr 1-612 行(纯核)零 checker/桥接/注册表引用, +// 本簇是唯一耦合段——找到 iface_satisfies/iface_kind_of 的实现者 = 前端层符号。corearch 链接纯核 +// 时若本簇仍在,构建会以未定义符号告终(concat 面 = resolver「Undefined name」;project-mode 面 +// = `error[N06]` 静默未定义 + rc=0,B.6 同族)。移出后:type_engine.cr = 纯核(corearch 链接目标), +// iface_registry.cr 除 `sh_shape_seq_ro` 对 ty_shadow 的 `sh_ref_mut_marker` 一处外亦纯。 +// +// 本文件**只入 corec/corelsp 清单**(corearch 清单不含本文件)。直取的跨层符号(同 concat 单元内 +// 由清单前置文件提供,见 build_selfhost_native.py 与 src/compiler/_import.cr 的相对序): +// checker.cr —— find_iface / find_func / decl_name_of_ti / gen_constr_type_ti / get_type_kind / get_type_data +// ty_shadow.cr —— sh_term_of_ti / sh_iface_shape_term / sh_shape_member_at / sh_iface_self_mode / +// sh_func_self_mode / sh_func_sig_term +// iface_registry.cr —— iface_shape_lookup / iface_by_ty_code(后者经 iface_kind_of 的 TYP_BASE 分支) +// type_engine.cr —— ty_sub / ty_budget_reset / tt_c / tt_list_same(纯核原语,同清单前置) +// ⚠ iface_kind_of 头注内「只见上面 iface_by_ty_code」的「上面」在搬迁后 = iface_registry.cr 的对应 +// 函数(跨文件同 concat 单元)——注释文字按「逐字搬迁」原样保留,语义指向不变。 + +// ─── R2 P3b Task 0:满足判定统一入口(P2b 交接契约 ① 的落地)─── +// 语义(spec §2.2/§2.3/§2.5 + P3 计划「与 P2b 的交接面」①):`iface_satisfies(t_ti, iface_ni)` +// 是**原生/横切/用户三类同入口**的满足判定;三态 1/0/-1(`-1` = 未覆盖/不判,**禁止**被消费 +// 方当 0(不满足)或 1(满足)用——P0 三态纪律)。 +// +// 轴分派(**顺序 = 形状 → 接口 → 本质轴**,各自不判时逐级下落,绝无静默折算): +// A 横切形状名:`iface_shape_lookup(iface_ni)`(Task 2 注册;本批空表 ⇒ 恒未命中) +// ⇒ 包含判定 `ty_sub(实参项, 形状项)`。形状项由 Task 2 按 spec §2.2 给出(如 `⊤_SEQUENCE`)。 +// B 本质轴(原生/已声明类型名):= P3a `gen_constr_satisfied` 的引擎面**逐字同口径** +// (`sh_term_of_ti` 桥接 + `ty_sub` + 预算隔离),本函数即该面的统一入口。 +// C 用户接口名:`find_iface >= 0` ⇒ `iface_user_satisfies`(Task 6 Step 3:形状项 + +// 逐成员签名判定;= `check_iface` 同源,见该函数头注)。 +// ⚠ 名字同时命中多轴时按上式**取先命中者**(A > C > B;原生名与接口名同名的撞车面在 C > B +// 处保持 P3a 现状:`find_iface` 命中即走用户轴,不做本质轴判定)。 +// +// 预算隔离(照 `type_equal_engine`/`gen_constr_satisfied` 先例):每次进入引擎前 +// `ty_budget_reset`、返回前再复位——memo 跨查询命中会让结果依赖预算历史(P0 终审 Critical 3)。 +// 本常量 = 与 `gen_constr_satisfied` 同值(两处同为「一遍桥接 + 一次包含判定」的规模; +// 常量定义在本文件(调用面在其后),`globals.cr` 无 IFACE_* 预算位)。 +IFACE_SAT_BUDGET : int = 200000; + +fn iface_satisfies(t_ti: int, iface_ni: int) -> int { + if t_ti < 0 { return -1; } + if iface_ni < 0 { return -1; } + // ── 轴 A:横切形状名(空表 ⇒ 未命中,落下方轴)── + st := iface_shape_lookup(iface_ni); + if st >= 0 { return iface_satisfies_term(t_ti, st); } + // ── 轴 C:用户接口(`find_iface` 命中 ⇒ 用户轴;不得再落本质轴)── + if find_iface(iface_ni) >= 0 { + // 形状项路由(R2 P3b Task 6 Step 1/3 已落地):形状项 = 接口方法集(product of fn, + // 建项见 ty_shadow.cr 的 sh_iface_shape_term),判定 = **逐成员包含**(下述 + // iface_user_satisfies:方法名经 impl 方法表查表 + 成员 fn 项经引擎 ty_sub)。 + // **不是** `iface_satisfies_term(t_ti, shp)`(整形状 ty_sub)——两条实测理由: + // ① 引擎对 AK_NAMED 不展开(P0 未覆盖面②)⇒ 命名行实参与 product 形状的 `ty_sub` + // 恒 -1(全部负例会静默降级为「不判」= 静默通过面复活); + // ② product 的引擎比较是**不变槽链结构相等**而非子集包含 ⇒ 方法集 ⊇ 形状集 + // (实现方带额外方法,常态)会被误拒。 + // ⇒ 整形状 ty_sub 路由**不可用**(登记为未覆盖面);形状项作为签名载体由逐成员判定消费。 + return iface_user_satisfies(t_ti, iface_ni); + } + // ── 轴 B:本质轴(原生/已声明类型名;非类型名 ⇒ -1 = 不判,不发明诊断)── + cti := gen_constr_type_ti(iface_ni); + if cti < 0 { return -1; } + return iface_satisfies_ti(t_ti, cti); +} + +// ─── 引擎面原语(Task 2 的无名字消费点可直接用 `iface_satisfies_term`)─── +// `实参行 <: 形状项`:桥接失败(行不可译)⇒ -1;否则 `ty_sub` 三态**直传**(-1 上抛)。 +fn iface_satisfies_term(t_ti: int, shape: int) -> int { + if t_ti < 0 { return -1; } + if shape < 0 { return -1; } + a := sh_term_of_ti(t_ti); + if a < 0 { return -1; } + ty_budget_reset(IFACE_SAT_BUDGET); + s := ty_sub(a, shape); + ty_budget_reset(IFACE_SAT_BUDGET); + return s; +} + +// `实参行 <: 约束行`(本质轴;= gen_constr_satisfied 的引擎面) +fn iface_satisfies_ti(t_ti: int, cti: int) -> int { + if t_ti < 0 { return -1; } + if cti < 0 { return -1; } + a := sh_term_of_ti(t_ti); + b := sh_term_of_ti(cti); + if a < 0 || b < 0 { return -1; } + ty_budget_reset(IFACE_SAT_BUDGET); + s := ty_sub(a, b); + ty_budget_reset(IFACE_SAT_BUDGET); + return s; +} + +// 方法行查找:`(类型名 ni, 方法名 ni) → mangled 函数名 ni`(-1 = 无此方法)。 +// 数据面 = parser 在 impl 块登记的 `g_methods`(24B/条 {type_ni, method_ni, mangled_ni}, +// parser.cr 的 impl 分支**唯一**写点)——与 `type_has_method`(`find_func(str_intern("T.m"))`) +// **同域**:两者判的都是「impl 块声明过 `Type.method`」这一面(等价性守门 = selftest +// `isat.predicate_same_as_check_iface` 全表对拍 + 行为探针)。 +// ⚠ 本函数**零 str_intern**:查名走表、不构造串——方法缺失(= 判定 0 的路径)不得往 g_strs +// 追加新串(.ccr STR 段硬判据;`type_has_method` 的 str_intern 只对**已存在**方法名幂等, +// 对缺失方法名会增长驻留表——本层刻意避开该形态)。 +fn iface_find_method(type_ni: int, method_ni: int) -> int { + if type_ni < 0 { return -1; } + if method_ni < 0 { return -1; } + i : ., mut = 0; + loop { + if i >= g_method_count { return -1; } + if r64(g_methods, i * 24) == type_ni { + if r64(g_methods, i * 24 + 8) == method_ni { return r64(g_methods, i * 24 + 16); } + } + i = i + 1; + } + return -1; +} + +// ─── 轴 C:用户接口的满足谓词(**与 check_iface 同源**;R2 P3b Task 6 Step 3 重写)─── +// 语义(spec §2.3):`interface I` 的形状类型项 = 方法集(product of fn,建项 = +// `sh_iface_shape_term`);判定 = **逐成员包含**:对 I 的每个方法 m—— +// ① T 的方法表含 m(表查询 `iface_find_method`,**mangling 已退役**:不再拼 "T.m" 串); +// ② 接收者模式相等(调用约定维度;两侧显式比较,见 `sh_iface_sig_param_term` 注); +// ③ 签名包含:成员 fn 项与 impl 函数行的 fn 项做引擎 `ty_sub`——项为**身份规范形** +// (N 不入项 / 泛型应用展开 / 命名行按名规范,见 ty_shadow.cr 的 sh_sig_term_of_ti 注) +// ⇒ 判定 = 逐参类型 + 返回类型(**类型项等价**,取代旧态的裸码比较)。 +// 三态: +// · 1 = 全部方法在位且签名相符(含**零方法接口**的空洞满足); +// · 0 = 方法名不在位 / 接收者模式不同 / 参数数不符(链长不同)/ 签名项不同(**可证违反**); +// · -1 = T 非命名行(原生/`dyn`/泛型形参/复合构造子——无方法表 ⇒ **不判**);方法表登记了 +// 名字但函数行缺失(表内不一致);接口形状项不可建(签名节点缺失 / 域外参数数); +// 函数行签名项不可建(变参/未知 kind)。**-1 绝不因「查不到」而报 0**。 +// 覆盖边界(登记面,非漏放):签名含**泛型形参行**的匹配只在同一声明内成立(行号身份)—— +// 接口侧不产生该形态(`interface I[T]` 的形参被 parser 丢弃,登记);`dyn` 位图行不参与 +// 规范形 ⇒ 含 dyn 的签名 = -1 不判。 +// 预算隔离:每次引擎查询前后 `ty_budget_reset`(照本文件其它入口同款)。 +fn iface_user_satisfies(t_ti: int, iface_ni: int) -> int { + ii := find_iface(iface_ni); + if ii < 0 { return -1; } + // 域限定:仅命名行(TYP_NAMED / TYP_GENERIC_APPLY 的基名)有方法表身份;其余 ⇒ 不判。 + // 用 decl_name_of_ti(展开层的行→声明入口)而非 get_type_name:后者对 TYP_BASE 行会 + // str_intern 基类型名(本层禁止驻留表增长,见 iface_find_method 注)。 + tname_ni := decl_name_of_ti(t_ti); + if tname_ni < 0 { return -1; } + return iface_user_satisfies_ii(tname_ni, ii); +} + +// 名字面核心(`iface_user_satisfies` 与 checker 的 `check_iface` 的**共同实现**——两侧 +// 同源由构造保证,selftest `isat.predicate_same_as_check_iface` 仍逐例对拍)。 +fn iface_user_satisfies_ii(tname_ni: int, ii: int) -> int { + if tname_ni < 0 || ii < 0 { return -1; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + if mc < 0 || mc > MAX_IFACE_METHODS { return -1; } + shp := sh_iface_shape_term(r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_NAME)); + if shp < 0 { return -1; } + mi : ., mut = 0; + loop { + if mi >= mc { return 1; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + m_ni := r64(g_ifaces, mbase + OFF_IFM_NAME); + f_ni := iface_find_method(tname_ni, m_ni); + if f_ni < 0 { return 0; } // 无此方法 = 可证违反(名字面忠实) + fi := find_func(f_ni); + if fi < 0 { return -1; } // 表内不一致 ⇒ 不判(不发明违反) + if sh_iface_self_mode(ii, mi) != sh_func_self_mode(fi) { return 0; } // 接收者模式 + + ifm := sh_shape_member_at(shp, mi); + if ifm < 0 { return -1; } // 形状项与方法表失同步 ⇒ 不判 + implf := sh_func_sig_term(fi); + if implf < 0 { return -1; } // impl 侧签名项不可建 ⇒ 不判 + // 判定 = 引擎的**不变槽结构比较原语** `tt_list_same`(0/1 **全域**)——**不是** + // 直接 `ty_sub`:引擎在不变槽上把「确定不同」上抛为 -1(type_engine 的 + // tt_list_variance_at 分支:`tt_type_elem_same != 1 → g_ty_uncovered = 1; return -1`, + // 「确定不等 ⇒ 0」的加强仍是登记面)⇒ 用 ty_sub 会让签名不符退化成「不判」= + // 静默通过面复活(实测:isat.axis_c_ret_code 得 -1)。项为**规范形**(见 ty_shadow.cr + // 的 sh_sig_term_of_ti 注)⇒ 同型恒同节点、异型恒异节点 ⇒ 节点同一性就是签名相等的 + // 正确判据。**比的是 fn 项的链**(`tt_c`):fn 原子的 b 槽 = 方法名(接口侧标注; + // impl 侧为 -1)是元数据不入签名——整项同一是 `tt_list_same` 的「非 CONS ⇒ 节点同一」 + // 规则下的假不等(实测命中)。本原语不计步(不动预算/memo)⇒ 无需预算隔离。 + if tt_list_same(tt_c(implf), tt_c(ifm)) != 1 { return 0; } + mi = mi + 1; + } + return 1; +} + +// checker 类型行号 → AK_*(-1 = 越界/负)。 +// 分派表 = 侦查 §4.1 的 checker 侧原子宇宙 13 类,与条目表的 ak 列同源。 +// ⚠ P2b Task 2 边界(**勿混**):桥接层 `sh_native_ak` **不**调本函数——它保留自己的 +// `get_type_kind != TYP_BASE → -1` 门(原生快路径契约:TI_DYN 行经 sh_term_of_ti 的 TYP_DYN +// 分支,结构/命名行走通用路径),只共用上面的 `iface_by_ty_code` 一张 TY 码表。故本函数对 +// TYP_DYN 行回 AK_DYN、对结构/命名行回其类,而 sh_native_ak 对同样这些行回 -1——「单源化」 +// = 两入口共用**逐项分派表**,不是把两个入口合并成同一语义(type_selftest.cr 的 +// `iface.bridge_gate_kept` 把该差异钉在红)。 +fn iface_kind_of(ti: int) -> int { + k := get_type_kind(ti); + if k < 0 { return -1; } // 负值/越界行(get_type_kind 已做范围闸) + if k == TYP_BASE { return iface_by_ty_code(get_type_data(ti)); } + if k == TYP_DYN { return AK_DYN; } + if k == TYP_NAMED { return AK_NAMED; } + if k == TYP_GENERIC_PARAM { return AK_NAMED; } + if k == TYP_GENERIC_APPLY { return AK_NAMED; } + if k == TYP_ARRAY { return AK_SEQUENCE; } + if k == TYP_SLICE { return AK_SEQUENCE; } + if k == TYP_REF { return AK_REF; } + if k == TYP_PTR { return AK_PTR; } + if k == TYP_TUPLE { return AK_PRODUCT; } + return -1; // 未知 kind(当前类型表无此情形) +} diff --git a/src/compiler/iface_registry.cr b/src/compiler/iface_registry.cr new file mode 100644 index 00000000..9ba52f7e --- /dev/null +++ b/src/compiler/iface_registry.cr @@ -0,0 +1,451 @@ +// === iface_registry.cr === +// R2 P2b Task 1:本质条目表(native interface entries)+ `iface_*` 查询 API(spec §2/§2.5)。 +// +// 定位:spec §2「一张注册表、检查器统一查表」的**第一段地面**——本文件只**建层**: +// · 16 条本质条目(R2 P4 Task 3 扩列:8 原生 + product/sequence/ref/ptr/named + null/sum/fn +// ——旧 13 = T1..T2 值)= **静态数据**(表内容 = 常量 + 注册名 ni,不缓存任何类型表行号 +// ——P1 M1 教训:init_types 的行号序不是可依赖的契约); +// · 操作许可位集(`ops`)+ 字面量定型码(`lit_code`)+ 查询 API(`iface_*`)。 +// **零消费者、零行为变化**(Task 1 硬口径):checker/ir_gen/后端一律未接线;表**不 alloc +// `g_types` 行**(先例 `init_builtins` 的 `g_rt_builtin_*` 旁表,checker.cr:261-303)⇒ `.ccr` +// 类型段/行号零扰动。接线在 Task 3(字面量定型)/4(操作许可)/5(容器面);`ops` 位集本 Task +// 落**空集**(全 0),由 Task 4/5 逐格填——**空集期间不得有消费者**(本 Task 即如此)。 +// +// ⚠️ **AK↔TI 下标不 1:1(P1 血泪,硬性)**:`AK_STRING=2` 而 `TI_STR=3`、`AK_BOOL=3` 而 +// `TI_BOOL=2`(ty_shadow.cr:18-26)。本表 `ak`/`ti_row` 两列**逐项按语义写死**,禁止任何 +// 数值直传/下标互换;`iface_by_ty_code` 即改动前 `sh_base_ak`(ty_shadow.cr)的语义,逐项分派。 +// 守门 = type_selftest.cr 的 `iface.*_ti` 逐条目用例(bool/string 两行互换即红)。 +// +// ⚠️ **声明序(本仓库 globals 可见性)**:本文件位于 `checker.cr` **之后**(清单序 = +// ast→globals→…→checker→type_terms→type_engine→**iface_registry**→ty_shadow→…)⇒ +// **函数**可被 checker.cr 前向引用(既有事实),**变量/常量不可**(globals.cr:290-292 的 +// g_purity_inst 先例)。故本文件的 ESZ_/OFF_ 常量只可在本文件与**其后**的文件 +// (ty_shadow.cr / type_selftest.cr)使用;Task 4/5 若要在 `checker.cr` 里直接写 `IP_*` +// 位下标常量,须把 `IP_*` 声明在 `globals.cr`(否则解析期 Undefined name)。 +// +// 现状依据(逐格转录;Task 3/4/5 接线时按此对拍,格注 = 计划 Global Constraints 第 4 条): +// lit_code 列 = infer_expr 头部的 5 条内联 if(计划时点 checker.cr:1892-1897;接线时点 +// 实测 :1896-1901,偏移 +4):EXPR_INT→TI_INT / +// EXPR_DEX→TI_DEX / EXPR_STRING→TI_STR / EXPR_BOOL→TI_BOOL / EXPR_CHAR→TI_CHAR。 +// ti_row 列 = ast.cr:286-293 的 TI_* 常量(**常量,非位置推演**)。 +// ak/kind 列 = checker 侧原子宇宙 13 类(侦查 §4.1):TYP_BASE→8 原生(经 TY_* 码)、 +// TYP_NAMED/TYP_GENERIC_PARAM/TYP_GENERIC_APPLY→AK_NAMED、TYP_ARRAY/TYP_SLICE→AK_SEQUENCE、 +// TYP_REF→AK_REF、TYP_PTR→AK_PTR、TYP_TUPLE→AK_PRODUCT、TYP_DYN→AK_DYN。 +// **无 AK_SUM/AK_FN 条目**:checker 侧无对应原子(enum 类型行 = TYP_NAMED,checker.cr:982; +// 无函数类型行)。 +// ops 列 = **Task 4 逐格填写**(Task 1 落空集)——每格 = 现状 checker.cr 的判定, +// 逐格注现状行号(见 iface_registry_init 内的组位集):算术门 ADD..MOD **只含 int/dex** +// (现状「两层结构」:早退规则(串拼接/指针算术/指针差)留代码 = 结果规则,本列只记门)。 +// 消费点 = checker.cr 的三个门(ANY 算术 :1959 / ALL 逻辑 :1969 / ONE 条件 :2274+:2385 —— +// **行号一律为接线前实测**;接线后同处 +4,按内容定址)。 +// **Task 5 消费点(单点接线,逐格台账见实施报告 §5 / 事实表 §8)**:`IP_INDEX` → checker.cr 索引面 +// 兜底拒绝(接线前 :2659;接线后门体即原 TK01 调用)——见 iface_registry_init 的 +// AK_STRING/AK_SEQUENCE 条目注。**其余容器位本批不接线**(各有实测理由,不接不代表位错): +// `IP_INDEX_RANGE`(range 分支的非数组落空**无诊断** ⇒ 门是恒真门 = 空转)、`IP_FIELD` +// (EXPR_FIELD 全形 rc=0、无任何诊断路径 ⇒ 同上)、`IP_METHOD`(dyn 消费点的谓词是**逐具体 +// 行的方法表成员判定**(`type_has_method` 名拼接),非「原子类是否有方法面」——类级门会 +// **抑制** int/string 行今日的 EC_N_METHOD(实测 D1/D4)⇒ 接线 = 放宽)、`IP_AS` +// (EXPR_AS 除 PTR 结果规则外恒等透传、无诊断)。四项均为 P3 收紧旋钮(登记,未接线)。 + +// ─── 条目布局(40B/条 × 5 字段;与 g_types(24B/条)/ESZ_TYPE_TERM(48B/条) 同族:扁平 i64 缓冲)─── +// {ak, ti_row, name_ni, lit_code, ops} +// ak = 原子类(AK_*;type_engine.cr 的 AK_* 块) +// ti_row = 该原子的**规范 checker 类型行**(8 原生 = TI_INT..TI_DYN 常量;结构/命名 = -1) +// name_ni = 名字 ni(显示/查询用)——R2 P4 Task 3 起**注册面 str_intern 填充**(裁决 1 +// 接受 STR 段随之增长;T1..T2 期留 -1,注记原文保留在 iface_registry_init 内) +// lit_code = **字面量定型**:AST 字面量 kind(EXPR_INT/EXPR_DEX/EXPR_STRING/EXPR_BOOL/ +// EXPR_CHAR)→ 本条目(`iface_lit_ti`/`iface_lit_ak` 的查表键);-1 = 无字面量 +// ops = 操作许可位集(bit(OP_*)/bit(UOP_*+20)/bit(IP_*);见 Task 4 的位下标约定) +// ⚠ 布局常量(ESZ_IFACE_ENTRY/OFF_IE_*/IFACE_ENTRY_COUNT)已**迁入 dyn_arr.cr**(R2 P4 +// Task 3:IFACE 段 loader 在 corearch 侧要重建条目表 + 校验计数,本文件不入 corearch 清单)。 + +// 位构造(乘 2 循环,照既有 dyn_set_type 惯例——本语言无移位运算符)。下标语义见 globals.cr +// 的 IP_* 块;n ≥ 63 时 i64 负号翻转(= 全位命中)由 `iface_permits` 的越界闸拒绝,本函数只 +// 供表内**已知常量**使用,不做校验(表内容 = 常量,n 恒 < 32)。 +fn iface_bit(n: int) -> int { + b : ., mut = 1; + k : ., mut = n; + loop { if k <= 0 { break; } b = b * 2; k = k - 1; } + return b; +} + +// ─── 建表(由 init_types() 尾部调用;幂等)─── +// 表内容 = 常量(AK_*/TI_*/-1/0)+ **注册名 ni**(R2 P4 Task 3 起)⇒ 不读类型表、不缓存行号、 +// 不因 init_types 的重复调用而失效(行号空间复用不影响本表——这正是「不缓存行号」的目的; +// 长驻进程 corelsp 每请求 init_types;str_intern 对同名幂等 ⇒ 重复调用零增长)。 +// +// **name_ni 的历史口径与翻转(原文保留 + 翻转注记)**:T1..T2 期本列留 -1,理由 = `.ccr` 的 +// STR 段 = 编译期 `g_strs` **全量**(ccr_io.cr 逐串落盘)⇒ 在本函数里 `str_intern("product")` +// 会把新串**追加**进 g_strs ⇒ `.ccr` 逐字节变(当时的全局硬判据「预期逐字节不变」)。 +// **R2 P4 Task 3 起翻转为「逐名注册」**:维护者裁决 1(2026-09-12)明示**接受 STR 段增长**, +// 换结构性判据(TODO #26);IFACE 段 ① 小节要求 name_ni 是 STR 段内的合法索引(跨段引用域 +// 检查),无名的条目表无法兑现「信息恒随载体」。⇒ 本函数 = 生产注册面(**固定增量**: +// 逐名列举于下,守门用例按新口径重定「判定/查询路径零新增驻留 + 初始化面固定增量」)。 +fn iface_registry_init() { + if g_iface_registry_ok != 0 { return; } // 幂等(内容恒定,无需重建) + g_iface_entries = alloc(IFACE_ENTRY_COUNT * ESZ_IFACE_ENTRY); + g_iface_entry_count = IFACE_ENTRY_COUNT; + // ─── ops 列组位集(Task 4 逐格填写;每格注现状 checker.cr 行号 = 逐格转录依据)─── + // 算术门(唯一消费者 = checker.cr:1959 的 ANY 门):**只含 int/dex**——这不是省事,而是与 + // :1959 一字等价所要求的最小集。现状「算术许可」是**两层**:早退规则(串拼接 :1946 / + // 指针算术 :1948+:1951 / 指针差 :1955)命中即 return、不会走到门;门只管「两侧皆非数值才 + // 报错」。故把 PTR/STRING 填进本组 = **放宽**(ANY 语义下 permits(PTR,ADD)=1 足以放行 + // `*T + *T`、`"a" - "b"`——两条现状均为 error[TB01],有负控钉红)。 + o_arith := iface_bit(OP_ADD) + iface_bit(OP_SUB) + iface_bit(OP_MUL) + iface_bit(OP_DIV) + iface_bit(OP_MOD); + // 逻辑族(消费者 = checker.cr:1969 的 ALL 门):现状每侧须 `bool|int`(:1969 逐字: + // `(lt != BOOL && lt != INT) || (rt != BOOL && rt != INT)`)⇒ 本组**恰 {int, bool}**。 + // ⚠ 计划 Task 4 许可格表把本组写在「AK_INT/AK_DEX」行(括注「允许 bool|int」自相矛盾)—— + // **实测裁决**:dex 在现状被拒(`1.5 && true` → error[TC01],探针实测)⇒ DEX 格必为 0, + // 否则 = 放宽。取「与 :1969 一字等价」为准(计划 Step 2 的替换代码即此要求)。 + o_logic := iface_bit(OP_AND) + iface_bit(OP_OR); + // 比较族(:1965-1967 六个比较 op 恒返 bool、**不校验操作数**)+ 一元族(:1979 NEG/NOT 透传、 + // :1982 REF 对任意操作数产 TYP_PTR、:2003-2014 DEREF 兜底透传)+ 转换(:2892-2908 除 PTR 的 + // asp 外恒等透传)⇒ **对全部 13 类置 1**(现状宽松面的集中体现;P3 收紧旋钮,本批只登记)。 + // 口径:这四项在现状**没有拒绝路径**,故本批不接线(无门可换)——表登记为 P3 的可审计位。 + o_cmp := iface_bit(OP_EQ) + iface_bit(OP_NE) + iface_bit(OP_LT) + iface_bit(OP_GT) + iface_bit(OP_LE) + iface_bit(OP_GE); + o_un := iface_bit(IP_UOP_BIAS + UOP_NEG) + iface_bit(IP_UOP_BIAS + UOP_NOT) + + iface_bit(IP_UOP_BIAS + UOP_REF) + iface_bit(IP_UOP_BIAS + UOP_DEREF); + o_base := o_cmp + o_un + iface_bit(IP_AS); // 13 类共有面(比较/一元/转换全许可) + o_cond := iface_bit(IP_COND); // `if`(:2274 收 bool|int) + o_cond_b := iface_bit(IP_COND_BOOL); // `while`(:2385 **只收 bool**——不得与上合并) + // 8 原生(ti_row 取 TI_* 常量;AK↔TI 逐项分派——bool/string 两行按语义错位书写) + // 第 4 参 = name_ni(R2 P4 Task 3:生产注册面固定增量——逐名 = 下表 16 行;见头注) + // AK_INT :门 + 逻辑 + 条件(:2274 收 int)——**无 IP_COND_BOOL**(:2385 拒 int,探针 N9 实测) + iface_put(0, AK_INT, TI_INT, str_intern("int"), EXPR_INT, o_base + o_arith + o_logic + o_cond); + // AK_DEX :仅门——逻辑/条件均**不含 dex**(:1969/:2274 只认 bool|int;探针 N7/N8 实测拒) + iface_put(1, AK_DEX, TI_DEX, str_intern("dex"), EXPR_DEX, o_base + o_arith); + // AK_STRING:无算术/逻辑/条件(串拼接走 :1946 早退,不进本表);索引面 = :2619(串下标→int) + // ——Task 5:IP_INDEX 已接线(兜底门;串的**结果**分支 :2644 原地保留) + iface_put(2, AK_STRING, TI_STR, str_intern("string"), EXPR_STRING, o_base + iface_bit(IP_INDEX)); + // AK_BOOL :逻辑 + 两条条件位(:2274 收 bool、:2385 收 bool) + iface_put(3, AK_BOOL, TI_BOOL, str_intern("bool"), EXPR_BOOL, o_base + o_logic + o_cond + o_cond_b); + iface_put(4, AK_UNIT, TI_UNIT, str_intern("unit"), -1, o_base); + iface_put(5, AK_NEVER, TI_NEVER, str_intern("never"), -1, o_base); + iface_put(6, AK_CHAR, TI_CHAR, str_intern("char"), EXPR_CHAR, o_base); + // AK_DYN :+ 方法面(:2066 dyn 方法校验路径)——Task 5:**未接线**(该路径的拒绝谓词 = + // `type_has_method(具体行名, 方法名)`(名拼接方法表判定),非本类级位;实测 int/string + // 行今日也发 EC_N_METHOD(探针 D1/D4)⇒ 类级门会抑制 = 放宽) + iface_put(7, AK_DYN, TI_DYN, str_intern("dyn"), -1, o_base + iface_bit(IP_METHOD)); + // 结构/命名(ti_row = -1 = 类级,无「规范行」) + // AK_PRODUCT :+ 字段面(:2566-2577 元组 `.N`)——Task 5:IP_FIELD **未接线**(落空 + // `return TI_UNIT` 无诊断;其前的 kind 分支是结果规则) + iface_put(8, AK_PRODUCT, -1, str_intern("product"), -1, o_base + iface_bit(IP_FIELD)); + // AK_SEQUENCE:+ 索引面(:2605-2618 数组/切片元素 + F2 越界;:2586 range→slice) + // ——Task 5:IP_INDEX 已接线(兜底门;ARRAY/SLICE 的**结果**分支原地保留); + // IP_INDEX_RANGE **未接线**(range 分支 :2612-2627 的非数组落空 `return TI_UNIT` 无诊断 + // ⇒ 门恒真 = 空转;登记为 P3 旋钮) + iface_put(9, AK_SEQUENCE, -1, str_intern("sequence"), -1, o_base + iface_bit(IP_INDEX) + iface_bit(IP_INDEX_RANGE)); + // AK_REF/AK_PTR:门全 0——**指针算术由 :1948-1955 早退承担**(结果规则留代码),非本表 + iface_put(10, AK_REF, -1, str_intern("ref"), -1, o_base); + iface_put(11, AK_PTR, -1, str_intern("ptr"), -1, o_base); + // AK_NAMED :+ 字段面(:2522-2563 struct 字段表)+ 方法面(:2088 方法表)——Task 5: + // 两位均**未接线**(EXPR_FIELD 全形 rc=0、零诊断路径;方法面 = 逐方法表名判定, + // 同 AK_DYN 注的理由);登记为 P3 旋钮 + iface_put(12, AK_NAMED, -1, str_intern("named"), -1, o_base + iface_bit(IP_FIELD) + iface_bit(IP_METHOD)); + // ─── R2 P4 Task 3 扩列(裁决 3 + 7):13 → 16 —— **纯信息面**(只加「可查询性」)─── + // 三行共同点:ti_row = -1(无规范行;ti_row 是**行级**面,本三原子是类级/无行)、lit_code = -1 + // (无字面量)、ops = 0(**无操作许可**——null/sum/fn 面在现状对算术/逻辑/条件门全拒, + // 与「未登记」同值 ⇒ 本三行不改任何判定;`iface_permits` 对 0 位集恒 0)。 + // ⚠ 硬性边界(计划 Step 3):`iface_kind_of`(iface_axis.cr)**零改动**——`TYP_NULL`/ + // `TYP_OPTIONAL`/`TYP_DYN` 等行的分派与 P2b 钉死的域逐格保持;扩列只加「可查询性」, + // 不加「行 → 类」新映射(守门 = selftest 的全类型行枚举用例 + t4.null_no_ops_no_entry 重定)。 + // AK_NULL(裁决 7:独立原子槽,不作 ⊥ 折叠——`type_engine.cr` 的 AK_NULL 语义零改动) + iface_put(13, AK_NULL, -1, str_intern("null"), -1, 0); + // AK_SUM(枚举/联合的类级原子;checker 侧枚举类型行 = TYP_NAMED ⇒ 本行无行分派, + // 忠实反映「checker 侧无 AK_SUM 行」这一现状——登记面而非新映射) + iface_put(14, AK_SUM, -1, str_intern("sum"), -1, 0); + // AK_FN(函数类型项(AK_FN)的类级原子;checker 侧无函数类型行 ⇒ 同行分派现状) + iface_put(15, AK_FN, -1, str_intern("fn"), -1, 0); + g_iface_registry_ok = 1; +} + +// 单行写入(照 init_builtins 的 bi_add 先例:表驱动的写入收敛到一处) +fn iface_put(e: int, ak: int, ti: int, name_ni: int, lit_code: int, ops: int) { + w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK, ak); + w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI, ti); + w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME, name_ni); + w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_LIT, lit_code); + w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS, ops); +} + +// 未建表守卫(init_types 会建;此处兜「查询早于 init_types」——自测通道/未来调用点不得读空表: +// 空表指针 + 线性扫 = 立即越界读)。照 ty_memo_slot 的 ok 位惰性建表先例(type_engine.cr:256)。 +fn iface_ensure() { + if g_iface_registry_ok == 0 { iface_registry_init(); } +} + +// ─── 查询 API(spec §2.5)─── +fn iface_count() -> int { return IFACE_ENTRY_COUNT; } + +// 原子类 → 条目行号(-1 = 无此原子) +fn iface_entry(ak: int) -> int { + iface_ensure(); + i : ., mut = 0; + loop { + if i >= g_iface_entry_count { break; } + if r64(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_AK) == ak { return i; } + i = i + 1; + } + return -1; +} + +// 操作许可位集(**spec §2.5 的签名**);-1/未知原子 → 全 0 = 无许可 +fn iface_ops(ak: int) -> int { + e := iface_entry(ak); + if e < 0 { return 0; } + return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS); +} + +// 位测试(1/0;op 已含 UOP/IP 偏置——偏置表见 Task 4 的位下标约定)。 +// 位下标越界 → 0(**不得**回绕:位构造是「乘 2」循环,op ≥ 63 会把 i64 位集推成负值 = 全位命中)。 +fn iface_permits(ak: int, op: int) -> int { + if op < 0 { return 0; } + if op > 62 { return 0; } + ops := iface_ops(ak); + if ops == 0 { return 0; } + bit : ., mut = 1; + k : ., mut = op; + loop { if k <= 0 { break; } bit = bit * 2; k = k - 1; } + if (ops / bit) % 2 != 0 { return 1; } + return 0; +} + +// 字面量 AST kind → 条目行号(-1 = 非字面量 kind)。查表键 = 条目的 lit_code 列 +// (13 条线性扫;AST kind 面只有 5 个命中项)。lit_kind < 0 = 条目的「无字面量」哨兵, +// 必须先行拒绝(否则 -1 会把无字面量的条目当成命中)。 +fn iface_lit_entry(lit_kind: int) -> int { + if lit_kind < 0 { return -1; } + iface_ensure(); + i : ., mut = 0; + loop { + if i >= g_iface_entry_count { break; } + if r64(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_LIT) == lit_kind { return i; } + i = i + 1; + } + return -1; +} + +// 字面量 AST kind → TI_*(-1 = 非字面量 kind)——Task 3 已接线:infer_expr(现址 :1896-1901)的 +// 5 个字面量分支逐条改调本函数(表 = 唯一真源;`iface.lit_*` 用例 + `lit.infer_*` 端到端用例对拍) +fn iface_lit_ti(lit_kind: int) -> int { + e := iface_lit_entry(lit_kind); + if e < 0 { return -1; } + return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI); +} + +// 字面量 AST kind → AK_*(同上;交叉断言/引擎侧用) +fn iface_lit_ak(lit_kind: int) -> int { + e := iface_lit_entry(lit_kind); + if e < 0 { return -1; } + return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK); +} + +// TY_*(基类型码)→ AK_*:逐项语义分派(**唯一实现在此**);-1 = 无对应。 +// P2b Task 2 单源化:桥接层 `sh_base_ak` 已改为**薄委托**本函数(改动前的字面拷贝 +// `sh_base_ak_legacy`/`sh_native_ak_legacy` 曾存于 ty_shadow.cr 供全表对拍,**R2 P5 Task 4 +// 已删**——对拍面改为 type_selftest.cr 的**冻结期望表** `ts_t4_ak_expected`)。 +// TY_DEX_S → AK_DEX(同值域不同表示——表示层差异不进类型身份,ty_shadow.cr:78-80 已裁决) +// TY_GENERIC_PARAM → AK_NAMED(泛型参数哨兵 → 命名类,不展开;ty_shadow.cr:86 已裁决) +fn iface_by_ty_code(ty: int) -> int { + if ty == TY_INT { return AK_INT; } + if ty == TY_DEX { return AK_DEX; } + if ty == TY_DEX_S { return AK_DEX; } + if ty == TY_BOOL { return AK_BOOL; } + if ty == TY_STRING { return AK_STRING; } + if ty == TY_UNIT { return AK_UNIT; } + if ty == TY_NEVER { return AK_NEVER; } + if ty == TY_CHAR { return AK_CHAR; } + if ty == TY_GENERIC_PARAM { return AK_NAMED; } + return -1; +} + +// TY_*(基类型码)→ TI_*(类型表行常量);**-1 = 无对应**(调用方自行回落,本表不兜底)。 +// P2b Task 6:`res_type_node`/`res_call_type` 的**双份基型分支** + 同族 6 处内联链 +// (checker.cr 的 hotpatch 注册 / extern 注册 / 形参 / 返回 / iface 返回)合一的**唯一映射表**。 +// · 逐项按语义写死(**禁止按值直传**——P1 血泪:AK/TI 下标不 1:1,见本文件头注); +// · 码 7 一格 = **现状两表原文原样保留**(`if tv == TI_DYN { return TI_DYN; }`):TY_GENERIC_PARAM=7 +// 与 TI_DYN=7 的纯数字撞车(spec §2.4),而 parser.cr:98 对 `dyn` 类型名**正产** `type_val=TI_DYN` +// ⇒ 本格**可达**(探针 B2:`x : dyn = 5; x.nosuch()` → N08,即 res_type_node 走本格)。 +// · **TY_DEX_S(8) 不入表**(计划 Interfaces 注「占位行,仅 init_types 面用;不属两表并集」): +// 两处调用点现状对码 8 均落 `TI_UNIT`(尾部回落)⇒ 入表 = 若该码可达即行为变化(selftest +// `t6.R_dex_s_unit`/`t6.C_dex_s_unit` 把「码 8 → TI_UNIT」钉红)。全 src 实测:TY_DEX_S 只出现在 +// init_types 的位置公理(checker.cr:253)与注释;无任何 parser/铸点写 `type_val = 8`(parser +// 只产 int/dex/bool/string/char/never/dyn 七码 + 复合节点)。**域外码一律 -1**(含负值)。 +fn ty_code_to_ti(ty: int) -> int { + if ty == TY_INT { return TI_INT; } + if ty == TY_DEX { return TI_DEX; } + if ty == TY_BOOL { return TI_BOOL; } + if ty == TY_STRING { return TI_STR; } + if ty == TY_UNIT { return TI_UNIT; } + if ty == TY_NEVER { return TI_NEVER; } + if ty == TY_CHAR { return TI_CHAR; } + if ty == TI_DYN { return TI_DYN; } // ← 现状原样:7 = TY_GENERIC_PARAM 的数值撞车格(见头注) + return -1; +} + +// iface_kind_of 已移至 iface_axis.cr(R2 P4 Task 0,链接面纯化) + +// 原子类 → 规范行(8 原生 = TI_*;结构/命名条目 ti_row = -1 ⇒ 恒 -1) +fn iface_ti_of(ak: int) -> int { + e := iface_entry(ak); + if e < 0 { return -1; } + return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI); +} + +// 原子类 → 注册名 ni(R2 P4 Task 3 起非 -1;-1 = 无此原子/未注册)。 +// 消费面 = IFACE 段 ① 小节的 name_ni 列 + 自测守门(逐名对拍);**零 str_intern** +// (只读表,不构造串——判定/查询路径不得驻留新串)。 +fn iface_name_of(ak: int) -> int { + e := iface_entry(ak); + if e < 0 { return -1; } + return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME); +} + +// ═══════════════ R2 P3b Task 0:横切轴形状条目表(`iface_satisfies` 的轴 A)═══════════════ +// 语义(spec §2.2):每条横切接口 = **一个形状类型项**(如 `sequence` ⇒ `⊤_SEQUENCE`、 +// `可索引`/`可迭代`/`product` 各一条),满足判定 = `ty_sub(实参项, 形状项)`——与用户轴/本质轴 +// 共用同一套引擎判定,不另建规则体系。本表 = 形状条目的**唯一数据面**(名字 ni → 类型项)。 +// +// ⚠ 空表口径(本批 = P3b Task 0 backfill):**零条目注册** ⇒ 轴 A 恒未命中 ⇒ `iface_satisfies` +// 落轴 C/B(本批 P3b 的判定面 = 用户接口 + 本质轴)。条目细化(`sequence`/只读/可写/可索引/ +// 可迭代/product 四~六条 + 各自形状项)归 **Task 2 Step 1**(该步的「形状条目细化」原文); +// 到位后 `iface_satisfies(t, <形状名 ni>)` 即按包含判定给 1/0/-1,消费点(索引/切片/迭代) +// 的换位归 Task 2 Step 3。 +// +// ⚠ 注册名须为**已驻留**的名字 ni:本层**不做任何 `str_intern`**(初始化/查询路径 str_intern +// 会把新串追加进 g_strs ⇒ .ccr STR 段变——A.3-② 硬约束;源码标识符由 lexer 驻留,调用方 +// 直接传 ni)。表本身不 alloc `g_types` 行、不进 .ccr 序列化 ⇒ 类型段/行号零扰动。 +// +// 生命周期:count 随 `reset_frontend_state` 清零、cap 保留(缓冲复用)——照 g_sgen_constr 先例。 +// 同名**覆盖**(最后一次注册生效;幂等重注册不增长表)。 +// iface_shape_grow 已迁入 dyn_arr.cr(R2 P4 Task 3,grow 助手统一宿主;IFACE 段 corearch +// 读回侧重建形状表需要) + +// 注册(返回行号;-1 = 参数非法)。name_ni < 0 / term < 0 一律拒绝——**不得**把负值当 +// 「未注册」哨兵写进表(那会让 lookup 把 -1 名字与空槽混淆)。 +fn iface_shape_register(name_ni: int, term: int) -> int { + if name_ni < 0 { return -1; } + if term < 0 { return -1; } + i : ., mut = 0; + loop { + if i >= g_iface_shape_count { break; } + if r64(g_iface_shape_names, i * 8) == name_ni { + w64(g_iface_shape_terms, i * 8, term); // 覆盖(幂等重注册) + return i; + } + i = i + 1; + } + iface_shape_grow(g_iface_shape_count + 1); + w64(g_iface_shape_names, g_iface_shape_count * 8, name_ni); + w64(g_iface_shape_terms, g_iface_shape_count * 8, term); + g_iface_shape_count = g_iface_shape_count + 1; + return g_iface_shape_count - 1; +} + +// 查询:名字 ni → 形状项(-1 = 未注册/参数非法) +fn iface_shape_lookup(name_ni: int) -> int { + if name_ni < 0 { return -1; } + i : ., mut = 0; + loop { + if i >= g_iface_shape_count { return -1; } + if r64(g_iface_shape_names, i * 8) == name_ni { return r64(g_iface_shape_terms, i * 8); } + i = i + 1; + } + return -1; +} + +fn iface_shape_count() -> int { return g_iface_shape_count; } + +// 清空(**仅自测**:形状注册是进程级全局态,自测用例注册后复位,防条目泄漏进后续用例/ +// 编译——LSP 长驻进程内多编译共用本表,生产路径的复位点是 reset_frontend_state)。 +fn iface_shape_reset() { g_iface_shape_count = 0; } + +// 类型项 → AK_*(单一原子;非单一 → -1;⊤ₖ → 其 k)。 +// 越界/负项先行拒绝:tt_* 访问器**无范围闸**(约定「调用方保证 0 ≤ i < tt_count()」), +// 直接喂 -1 会读到表前偏移。 +fn iface_of_term(t: int) -> int { + if t < 0 { return -1; } + if t >= tt_count() { return -1; } + tg := tt_tag(t); + if tg == TT_ATOM { return tt_a(t); } + if tg == TT_TOP_K { return tt_a(t); } + return -1; // union/inter/not/bot/top/mu/var/nil/cons +} + +// ═══════════════ R2 P3b Task 2:横切轴形状项(Step 1「条目细化」的类型项数据面)══════════════ +// spec §2.2:每条横切接口 = **一个形状类型项**,满足判定 = 一条包含判定 `T <: 形状`(与本质轴 +// /用户轴共用同一套引擎判定,不另建规则体系)。本段 = 六条形状项的**唯一构造点**;名字面注册 +// 走上方的 `iface_shape_register`(`iface_satisfies` 轴 A 的查表键)。 +// +// ⚠ **不得**写成 `sequence<⊤>` 参数链(P3b Task 0 §1.3 勘误,实测):AK_SEQUENCE 的槽 0 是 +// **不变**槽(Task 1 变型表:本语言数组/切片**可写**,元素协变不健全)⇒ 不变槽遇 ⊤ 会落 +// **-1**(未覆盖面)而非 1 ⇒ 序列面形状一律取**类别形** `⊤ₖ(AK_SEQUENCE)`。 +// ⚠ 每个构造点**零 str_intern**(.ccr STR 段硬约束 = A.3-②):形状项只进类型项 DAG +// (append-only ⇒ 跨编译/跨 init_types 稳定),不驻留新串——守门 = selftest `x2.no_str_intern`。 +// ⚠ **名字面(P3b 原文保留 + R2 P4 Task 3 翻转注记)**:P3b 期生产路径**不注册**(理由 = +// 形状名是驻留 ni,初始化路径 str_intern 会把新串追加进 g_strs ⇒ `.ccr` STR 段变,当时的 +// 全局硬判据)。**R2 P4 Task 3 起翻转为「按名注册」**:维护者裁决 1 接受 STR 段增长 ⇒ +// 生产注册面 = 文件尾的 `iface_shape_builtin_init()`(init_types 尾部调用 + D13 重建重跑)。 +// 消费侧不变:本批消费者(索引/切片)仍是**无名字消费点**(直接 `iface_satisfies_term`); +// 命名消费(`T: 可索引` 一类语法)待命名面裁决后接(不在本批)。 + +// 序列接口(spec §2.2 的 `序列接口 ⟺ sequence<⊤>`,按上方勘误兑现为 ⊤ₖ 类别形)。 +// 数组行与切片行同属 AK_SEQUENCE ⇒ 皆满足;`&[T]`/`&mut [T]`(AK_REF 行)**不**满足(见只读形状)。 +fn sh_shape_seq() -> int { return tt_top_k(AK_SEQUENCE); } + +// 只读序列 = 序列本体(数组/切片)+ **只读视图**(`&[T]`)。 +// 「只读 = **协变**」(计划 Step 1):视图部分的元素落在 AK_REF 的**协变槽**(Task 1 变型表: +// 只读 ref 的元素槽协变、可写 ref 降为不变)⇒ `&[int] <: 只读序列` 由协变槽判定(元素不是 ⊤ +// 也成立)——T1 §6-④ 登记的「只读序列形状」在**视图面**由此闭合。 +// **等式语义**(Task 1 裁决:mut 标记是判定维度,非 Rust 式单向放宽):`&mut [T]` 的标记不同 +// ⇒ **不**满足只读形状(判 0,可判定);`&mut [T]` 也不满足可写形状(见下:视图面不可表达)。 +fn sh_shape_seq_ro() -> int { + view := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(tt_top_k(AK_SEQUENCE), tt_nil()))); + return tt_union(tt_top_k(AK_SEQUENCE), view); +} + +// 可写序列 = **序列本体**(数组/切片——本语言切片可写,T1 探针 pSliceW `s[0] = 9` rc=0 实证) +// ⇒ 与序列接口同项;**拒绝只读视图**(`&[T]` 是 AK_REF 行,非序列类 ⇒ 0)。 +// 可写**视图**(`&mut [T]`)的形状**不可表达**:AK_REF 可写侧槽 1 = 不变(变型表)⇒ 槽内 ⊤ 落 +// -1(未覆盖面)而非 1 ⇒ 本形状只覆盖本体面;该边界由 selftest `x2.rw_view_unexpressible` +// 钉红(**-1 不得当 0/1 用**)。 +fn sh_shape_seq_rw() -> int { return tt_top_k(AK_SEQUENCE); } + +// 可索引 = 序列 ∪ 字符串(串下标 → 字节值,checker 既有结果分支)。 +// **与 IP_INDEX 许可集逐行同集**({AK_SEQUENCE, AK_STRING})——这是索引兜底门换位后行为保持的 +// 等价前提(全类型行枚举守门 = selftest `x2.indexable_matches_ops`)。 +fn sh_shape_indexable() -> int { return tt_union(tt_top_k(AK_SEQUENCE), tt_top_k(AK_STRING)); } + +// 可迭代(首版)= 序列(数组/切片)。 +// 字符串**不**入本形状:迭代面现状**无任何类型检查**(checker 的 EXPR_FOR 只推迭代源类型、 +// ir_gen 把它当数值界用)⇒ 无「串可迭代」的代码面证据,不宣称。 +fn sh_shape_iterable() -> int { return tt_top_k(AK_SEQUENCE); } + +// product = 元组行(AK_PRODUCT)。struct 行 = AK_NAMED(结构项经展开层 / 用户轴面)⇒ 不在首版。 +fn sh_shape_product() -> int { return tt_top_k(AK_PRODUCT); } + +// ═══════════ R2 P4 Task 3:形状名生产注册面(裁决 1 + D17)═══════════ +// 六个形状**按名注册**(D17 的六个 ASCII 名:sequence / sequence_ro / sequence_rw / +// indexable / iterable / product——与 spec §2.2 的构造子英文名同形、与 AK_* 命名同族、 +// 避免非 ASCII 进 STR 段)。语义零变化:注册值 = 上方 sh_shape_*() 的构造点(逐名同项, +// 守门 = selftest 的 `x2.shape_builtin_names`)。 +// +// 注册时机 = `init_types()` 尾部(iface_registry_init 之后)——**唯一生产入口**: +// · 名字 ni 在该点 str_intern(裁决 1 接受 STR 段增长;共 6 个新串,逐名登记); +// · `iface_shape_register` 同名覆盖幂等 ⇒ 重复调用(corelsp 每请求 init_types; +// `reset_frontend_state` 清 count 后重建)不增长表; +// · 注册**不做 reset**:表内容 = 生产注册面(本函数 6 行)+ 自测注册面(仅 selftest); +// 段体序列化的是**表本体**(`g_iface_shape_*`)⇒ 生产编译下 = 恰 6 条。 +// 与 D13 的关系:ccr_iface_populate 在此**重跑**本函数(order:reset → 形状重注册 → +// 行装填 → 签名装填)⇒ 段内容 = 类型表/接口表的纯函数(与判定历史无关)。 +// +// ⚠ 命名**消费语法**(`T: 可索引` 一类)不在本批(P3 未开工清单项,另裁)——本函数只兑现 +// 「形状名入生产注册面」;消费方(轴 A 判定)今日走 `iface_satisfies_term` 的无名字路径。 +fn iface_shape_builtin_init() { + iface_shape_register(str_intern("sequence"), sh_shape_seq()); + iface_shape_register(str_intern("sequence_ro"), sh_shape_seq_ro()); + iface_shape_register(str_intern("sequence_rw"), sh_shape_seq_rw()); + iface_shape_register(str_intern("indexable"), sh_shape_indexable()); + iface_shape_register(str_intern("iterable"), sh_shape_iterable()); + iface_shape_register(str_intern("product"), sh_shape_product()); +} diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index 33ae5600..4554fd53 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -5,11 +5,32 @@ // // Limitations: // - String constants: 解释器不调用 syscall3,alloc/print/syscall 相关函数返回 0 -// - 递归/跨函数调用:inline 执行不支持 IR_CALL,只处理 main→callee 的单层调用 -// - for 循环:dataflow 图按顺序执行,label/branch 机制不与 for 循环兼容 +// - 递归/跨函数调用:callee 图内联执行(ir_interp_run_fn),支持嵌套/递归/互递归; +// 深度上限 IR_INTERP_MAX_DEPTH(超限响亮报错,不挂死) +// - 动态分发/外部函数(IR_DYN_DISPATCH/IR_CALL_EXTERN,见 ir_interp_call)与 +// binary64 转换(IR_I2F/IR_F2I):解释器无对应语义——响亮报错 + 非零退出码, +// 绝不静默落 0(TODO #11 的静默分叉类) +// - 主循环与 callee 内联路径共用同一份 opcode 分派(ir_interp_call/ir_interp_run_fn), +// 双路径一致性由 tests/selfhost/test_interp_parity.py 钉死(interp callee ≡ +// interp main ≡ ELF oracle) g_ir_vals : string, mut; g_ir_vals_cap : int, mut; +// === 调用分派 / callee 内联执行(主循环与 callee 路径的唯一实现)=== +// ir_interp_abort:解释器中止码(0 = 正常;非 0 = ir_interpret 应立即返回的 +// 值)。callee(含嵌套层)里的响亮失败——越界陷阱(-1)/ 动态分发·extern(2)/ +// binary64 转换(-1)——沿调用链上抛到 ir_interpret,绝不静默落 0 +// (TODO #11 的静默分叉类:主循环有、callee 路径无 → 双路径分叉)。 +g_interp_abort : int, mut; +// 嵌套内联深度(callee 内再调用用户函数 = 递归内联)。超限响亮报错,不挂死、 +// 不静默——Python 参照解释器无此概念,深度上限取足够覆盖正常递归用例。 +g_interp_call_depth : int, mut; +IR_INTERP_MAX_DEPTH : int = 400; +// 活动函数栈(g_interp_stack[d] = 第 d 层内联执行的函数索引)——用于重入(递归/ +// 互递归)检测:同一函数再次进入时必须逐帧保存/恢复其槽区(扁平槽模型里同一 +// 函数的形参/局部只有一份槽,不保存则外层帧被内层调用覆写 → 静默错值)。 +g_interp_stack : string, mut; + // IR_BINARY 统一分派(主循环与 callee 内联循环共用): // 整数路径(模 2⁶⁴)。dex 为缩放整数——同走整数路径(数值迁移 #48 定稿); // IR_I2F/IR_F2I 在 dispatch 入口显式报错(interp 无 binary64 语义)。 @@ -64,6 +85,364 @@ fn ir_interp_str_sub(a: int, start: int, len: int) -> int { return str_intern(str_sub(istr_get(a), start, len)); } +// IR_CALL (4) / IR_SPAWN (27) 统一分派——d=结果槽,s1=实参首槽,s2=实参数, +// s3=函数名驻留索引。内置函数近似与原主循环分支逐条同语义同守卫;用户函数经 +// ir_interp_run_fn 内联执行(callee 内再调用 = 嵌套内联,深度守卫见 MAX_DEPTH)。 +fn ir_interp_call(d: int, s1: int, s2: int, s3: int) { + fn_ni := s3; + fn_name := istr_get(fn_ni); + sfi := find_so_fn(fn_ni); + if sfi >= 0 && s2 >= 1 { + tf := sym_type(sfi); + if tf == 1 || tf == 3 { // TAG_VARIADIC: print/println — NOOP (syscall3 returns 0) + } else if tf == 2 || tf == 3 { // TAG_AUTO_STR: print_i/println_i — NOOP (syscall3 returns 0) + } + } + // syscall3/syscall4 — interpreter returns 0 + if str_eq(fn_name, "syscall3") != 0 || str_eq(fn_name, "syscall4") != 0 { + if d >= 0 { w64(g_ir_vals, d * 8, 0); } + } + if str_eq(fn_name, "str_len") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, istr_len(r64(g_ir_vals, s1 * 8))); } + } + if str_eq(fn_name, "str_eq") != 0 { + if d >= 0 && s2 >= 2 { + left_s := istr_get(r64(g_ir_vals, s1 * 8)); + right_s := istr_get(r64(g_ir_vals, (s1 + 1) * 8)); + w64(g_ir_vals, d * 8, str_eq(left_s, right_s)); + } + } + if str_eq(fn_name, "concat") != 0 { + if d >= 0 && s2 >= 2 { + w64(g_ir_vals, d * 8, ir_interp_str_concat( + r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); + } + } + if str_eq(fn_name, "int_str") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))); } + } + if str_eq(fn_name, "chr") != 0 { + if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(chr(r64(g_ir_vals, s1 * 8)))); } + } + if str_eq(fn_name, "get_char") != 0 { + if d >= 0 && s2 >= 2 { w64(g_ir_vals, d * 8, ir_interp_str_char(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); } + } + if str_eq(fn_name, "str_sub") != 0 { + if d >= 0 && s2 >= 3 { w64(g_ir_vals, d * 8, ir_interp_str_sub(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8), r64(g_ir_vals, (s1 + 2) * 8))); } + } + if str_eq(fn_name, "load_str_ptr") != 0 { + if d >= 0 && s2 >= 2 { + b := r64(g_ir_vals, s1 * 8); p := r64(g_ir_vals, s1 + 1 * 8); + lo := load8(b, p) + load8(b, p+1)*256 + + load8(b, p+2)*65536 + load8(b, p+3)*16777216; + hi := load8(b, p+4) + load8(b, p+5)*256 + + load8(b, p+6)*65536 + load8(b, p+7)*16777216; + if hi < 0 { hi = hi + 4294967296; } + w64(g_ir_vals, d * 8, lo + hi * 4294967296); + } + } + if str_eq(fn_name, "store_str_ptr") != 0 { + if s2 >= 3 { + b := r64(g_ir_vals, s1 * 8); p := r64(g_ir_vals, s1 + 1 * 8); v := r64(g_ir_vals, s1 + 2 * 8); + lo : ., mut = v % 4294967296; hi : ., mut = v / 4294967296; + if v < 0 { lo = v; hi = -1; } + store8(b, p, lo%256); store8(b, p+1, (lo/256)%256); + store8(b, p+2, (lo/65536)%256); store8(b, p+3, (lo/16777216)%256); + store8(b, p+4, hi%256); store8(b, p+5, (hi/256)%256); + store8(b, p+6, (hi/65536)%256); store8(b, p+7, (hi/16777216)%256); + } + if d >= 0 { w64(g_ir_vals, d * 8, 0); } + } + // 字符串内置已处理 → 不再走用户函数查找(对照主循环的 ip+1;continue) + if str_eq(fn_name, "str_len") != 0 || str_eq(fn_name, "str_eq") != 0 || + str_eq(fn_name, "concat") != 0 || str_eq(fn_name, "int_str") != 0 || + str_eq(fn_name, "chr") != 0 || str_eq(fn_name, "get_char") != 0 || + str_eq(fn_name, "str_sub") != 0 { return; } + // 用户函数内联执行(单层 / 嵌套递归) + if d < 0 { return; } + cfi : ., mut = 0; + loop { + if cfi >= g_ir_func_count { break; } + if r64(g_ir_func_name_idx, cfi * 8) == fn_ni { + f_start := r64(g_df_func_node_start, cfi * 8); + f_count := r64(g_df_func_node_count, cfi * 8); + if f_start >= 0 && f_count > 0 { + rval := ir_interp_run_fn(cfi, s1, s2); + if g_interp_abort == 0 { w64(g_ir_vals, d * 8, rval); } + } + break; + } + cfi = cfi + 1; + } +} + +// callee 图内联执行(唯一实现):主循环的 IR_CALL 与 callee 内的 IR_CALL 都走 +// 这里——两路径不再各有一份分派。建标签表 → 拷实参 → 顺序执行 f_count 节点 → +// 恢复标签表 → 返回 IR_RETURN 值(无值返回 = 0,与主循环 ret_slot 清零同语义)。 +// 深度守卫:嵌套超过 IR_INTERP_MAX_DEPTH 响亮报错 + 中止(不挂死、不静默)。 +fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int { + f_start := r64(g_df_func_node_start, cfi * 8); + f_count := r64(g_df_func_node_count, cfi * 8); + if f_start < 0 || f_count <= 0 { return 0; } + if g_interp_call_depth >= IR_INTERP_MAX_DEPTH { + println("interpreter error: IR_INTERP_MAX_DEPTH exceeded — corec run cannot execute this recursion; build & run natively (corec build) instead"); + g_interp_abort = -1; + return 0; + } + // 重入检测(递归 / 互递归):本函数已在活动栈上 → 外层帧与本层共用同一份 + // 槽区(扁平槽模型),必须在入口保存、出口恢复,否则外层帧被覆写(静默错值)。 + reentrant : ., mut = 0; + si3 : ., mut = 0; + loop { if si3 >= g_interp_call_depth { break; } + if r64(g_interp_stack, si3 * 8) == cfi { reentrant = 1; break; } + si3 = si3 + 1; } + vs := r64(g_ir_func_var_start, cfi * 8); + vc := r64(g_ir_func_var_count, cfi * 8); + frame : string, mut = ""; has_frame : ., mut = 0; + if reentrant != 0 && vc > 0 && vs >= 0 { + frame = alloc(vc * 8); + has_frame = 1; + fi4 : ., mut = 0; + loop { if fi4 >= vc { break; } + w64(frame, fi4 * 8, r64(g_ir_vals, (vs + fi4) * 8)); + fi4 = fi4 + 1; } + } + // 实参先入缓冲再落形参槽:递归调用时实参槽与形参槽同属本函数槽区,直接 + // 逐个搬运可能被己方写覆盖(缓冲后行为与原子搬移一致)。 + args_buf : string, mut = ""; + if argc > 0 { + args_buf = alloc(argc * 8); + pai : ., mut = 0; + loop { if pai >= argc { break; } + w64(args_buf, pai * 8, r64(g_ir_vals, (arg_base + pai) * 8)); + pai = pai + 1; } + } + // 压栈(重入检测在上;深度守卫已在入口) + w64(g_interp_stack, g_interp_call_depth * 8, cfi); + g_interp_call_depth = g_interp_call_depth + 1; + rval : ., mut = 0; // callee 无值返回(IR_RETURN s1<0,如裸 return)→ 0 + // Save label state(递归安全:每层帧在局部变量里保存/恢复) + old_lc := g_label_count; + old_poses := g_label_poses; + old_poses_cap := g_label_cap; + g_label_poses = alloc(64 * 8); g_label_cap = 64; + // Build label map for callee + li2 : ., mut = 0; + loop { if li2 >= f_count { break; } + n_op := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_OPCODE); + n_s1 := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_S1); + if n_op == 21 { if n_s1 >= 0 { + grow_label_poses(n_s1 + 1); + w64(g_label_poses, n_s1 * 8, li2); + if n_s1 + 1 > g_label_count { g_label_count = n_s1 + 1; } + }} + li2 = li2 + 1; } + // Copy args from buffer to callee param vars + pstart := vs; + pai2 : ., mut = 0; + loop { if pai2 >= argc { break; } + w64(g_ir_vals, (pstart + pai2) * 8, r64(args_buf, pai2 * 8)); + pai2 = pai2 + 1; } + // Execute callee graph (inline) + ip2 : ., mut = 0; + loop { + if ip2 >= f_count { break; } + if g_interp_abort != 0 { break; } + op2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_OPCODE); + d2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_DEST); + t1 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S1); + t2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S2); + t3 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S3); + // R2 P5 Task 2(D19):这里是**派生码**(不是槽)——单槽化后 40 槽 = 类型项 + // 引用。`ir_interp_binary` 当前不消费该参(槽 3 的 op 码决定运算),值仍与 + // 单槽化前逐字节相同(派生码 ≡ 旧混用码)。 + t4 := sh_dfn_code_of_slots(r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_AUX)); + if op2 == 1 && d2 >= 0 { w64(g_ir_vals, d2 * 8, t1); } + if op2 == 2 && t1 >= 0 && t2 >= 0 { ir_interp_binary(d2, t1, t2, t3, t4); } + if op2 == 49 || op2 == 50 { + println("interpreter error: IR_I2F/IR_F2I needs binary64 semantics (apx dex) — `corec run` cannot execute apx dex arithmetic; build & run natively (corec build) instead"); + g_interp_abort = -1; break; + } + if op2 == 3 && t1 >= 0 { + ov2 := r64(g_ir_vals, t1 * 8); + if t3 == 1 { w64(g_ir_vals, d2 * 8, -ov2); } + else if t3 == 2 { if ov2 == 0 { w64(g_ir_vals, d2 * 8, 1); } else { w64(g_ir_vals, d2 * 8, 0); } } } + // Keep callee locals in the shared value store. + // The old inline path skipped these opcodes, so + // loop-carried assignments never changed state. + if op2 == 6 && d2 >= 0 { w64(g_ir_vals, d2 * 8, 0); } // IR_ALLOC + if op2 == 9 && t1 >= 0 && t2 >= 0 { w64(g_ir_vals, t1 * 8, r64(g_ir_vals, t2 * 8)); } // IR_STORE + if op2 == 10 && d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } // IR_LOAD + // 聚合族(与主循环 IR_ALLOC_ARRAY/IR_ALLOC_STRUCT/LOAD|STORE_INDEX + // /LOAD|STORE_FIELD/ADDR_INDEX 同语义):此前外函数内联路径缺这组 + // opcode——callee 里读写聚合全局(数组/结构体)静默落 0。本批 + // §1.3 让这类全局在 main 序言获得运行期存储(ir_gen.cr),callee + // 侧的读路径必须同批补齐,否则双路径语义仍分叉。 + if op2 == 8 && d2 >= 0 { // IR_ALLOC_ARRAY + cnt2 := t1; esz2 := t2; + if esz2 <= 0 { esz2 = 8; } + need3 := cnt2 * esz2 + 8; + bp2 := alloc(need3); + vi3 : ., mut = 0; + loop { if vi3 >= need3 { break; } store8(bp2, vi3, 0); vi3 = vi3 + 1; } + w64(g_ir_vals, d2 * 8, bp2); + } + if op2 == 13 && d2 >= 0 && t1 >= 0 { // IR_LOAD_INDEX: t1=arr_var, t3=literal_idx + av2 := r64(g_ir_vals, t1 * 8); + if irv_type(t1) == TI_STR { w64(g_ir_vals, d2 * 8, str_load8(av2, t3)); } + else { w64(g_ir_vals, d2 * 8, r64(av2, t3 * 8)); } + } + if op2 == 14 && t1 >= 0 && t2 >= 0 { // IR_STORE_INDEX: t1=arr_var, t2=val_var + av2 := r64(g_ir_vals, t1 * 8); + if irv_type(t1) == TI_STR { store8(istr_get(av2), t3, r64(g_ir_vals, t2 * 8)); } + else { w64(av2, t3 * 8, r64(g_ir_vals, t2 * 8)); } + } + if op2 == 15 && d2 >= 0 && t1 >= 0 && t2 >= 0 { // IR_LOAD_INDEX_VAR: t1=arr_var, t2=idx_var + av2 := r64(g_ir_vals, t1 * 8); + ix2 := r64(g_ir_vals, t2 * 8); + if irv_type(t1) == TI_STR { w64(g_ir_vals, d2 * 8, str_load8(av2, ix2)); } + else { w64(g_ir_vals, d2 * 8, r64(av2, ix2 * 8)); } + } + if op2 == 16 && d2 >= 0 && t1 >= 0 && t2 >= 0 { // IR_STORE_INDEX_VAR: d2=val_var, t1=arr_var, t2=idx_var + av2 := r64(g_ir_vals, t1 * 8); + ix2 := r64(g_ir_vals, t2 * 8); + if irv_type(t1) == TI_STR { store8(istr_get(av2), ix2, r64(g_ir_vals, d2 * 8)); } + else { w64(av2, ix2 * 8, r64(g_ir_vals, d2 * 8)); } + } + if op2 == 31 && d2 >= 0 && t1 >= 0 && t2 >= 0 { // IR_ADDR_INDEX: &arr[i] + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + r64(g_ir_vals, t2 * 8) * 8); + } + if op2 == 11 && d2 >= 0 && t1 >= 0 { // IR_LOAD_FIELD: t1=struct_var, t3=field_idx + pf2 := r64(g_ir_vals, t1 * 8); + if pf2 != 0 { w64(g_ir_vals, d2 * 8, r64(pf2, t3 * 8)); } + else { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } + } + if op2 == 12 && t1 >= 0 && t2 >= 0 { // IR_STORE_FIELD: t1=struct_var, t2=val_var, t3=field_idx + pf2 := r64(g_ir_vals, t1 * 8); + if pf2 != 0 { w64(pf2, t3 * 8, r64(g_ir_vals, t2 * 8)); } + else { w64(g_ir_vals, t1 * 8, r64(g_ir_vals, t2 * 8)); } + } + if op2 == 7 && d2 >= 0 { // IR_ALLOC_STRUCT + si2 := find_struct(t3); + fc2 : ., mut = 0; + if si2 >= 0 { fc2 = si_field_count(si2); } + need3 := fc2 * 8 + 8; + bp2 := alloc(need3); + vi3 : ., mut = 0; + loop { if vi3 >= need3 { break; } store8(bp2, vi3, 0); vi3 = vi3 + 1; } + w64(g_ir_vals, d2 * 8, bp2); + } + if op2 == 32 && d2 >= 0 { w64(g_ir_vals, d2 * 8, 0); } // IR_ARENA_NEW + if op2 == 33 { } // IR_ARENA_RESET + if op2 == 46 || op2 == 47 { + if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } + } + // —— TODO #11:以下 18 族与主循环同语义同守卫补齐(此前内联路径静默落空)—— + // IR_MAKE_ENUM (17):d := alloc(8·(1+s2));M[d+0] := s1(tag = 变体名索引)—— + // 与 ELF 布局 [tag][payload...] 一致的堆镜像(payload 由后续 IR_STORE_FIELD 写堆) + if op2 == 17 { + if d2 >= 0 { + need3 := 8 + t2 * 8; + bp2 := alloc(need3); + vi3 : ., mut = 0; + loop { if vi3 >= need3 { break; } store8(bp2, vi3, 0); vi3 = vi3 + 1; } + w64(bp2, 0, t1); + w64(g_ir_vals, d2 * 8, bp2); + } + } + // IR_LOAD_ENUM_TAG (23):d := M[ρ(s1)+0] + if op2 == 23 { + if d2 >= 0 && t1 >= 0 { + p23 := r64(g_ir_vals, t1 * 8); + if p23 != 0 { w64(g_ir_vals, d2 * 8, r64(p23, 0)); } + } + } + // IR_REF (18):d := &ρ(s1)——槽模型近似:槽号即「地址」(ir_interp_deref_* 同规则) + if op2 == 18 { if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, t1); } } + // IR_DEREF (25):d := M[ρ(s1)](栈槽间接或堆读取,见 ir_interp_deref_read) + if op2 == 25 { + if d2 >= 0 && t1 >= 0 { + w64(g_ir_vals, d2 * 8, ir_interp_deref_read(r64(g_ir_vals, t1 * 8))); + } + } + // IR_SLICE (24): d := arr + low*8(与 ELF 编码一致) + if op2 == 24 { + if d2 >= 0 && t1 >= 0 && t2 >= 0 { + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + r64(g_ir_vals, t2 * 8) * 8); + } + } + // IR_BOUNDS_CHECK (30): s1=index var, s2=max_len 字面量(ti=1 = 动态上限)—— + // index < 0 或 index >= max → 陷阱(中止码 -1,与主循环 return -1 同语义) + if op2 == 30 && t2 >= 0 { + iv30 := r64(g_ir_vals, t1 * 8); + lim30 : ., mut = t2; + if t4 == 1 { lim30 = r64(g_ir_vals, t2 * 8); } + if iv30 < 0 || iv30 >= lim30 { g_interp_abort = -1; break; } + } + // IR_STORE_PTR (26):M[ρ(s1)] := ρ(s2)(与 ELF 操作数一致;d=-1 发射态不再影响) + if op2 == 26 { if t1 >= 0 && t2 >= 0 { ir_interp_deref_write(r64(g_ir_vals, t1 * 8), r64(g_ir_vals, t2 * 8)); } } + // IR_YIELD (28):eager 值传递近似(d ← ρ(s1),同 IR_AWAIT) + if op2 == 28 { + if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } + } + // IR_AWAIT (29) + if op2 == 29 { if d2 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } } + // IR_FNADDR (48) — no real addresses in the interpreter; dest = 0 + if op2 == 48 { if d2 >= 0 { w64(g_ir_vals, d2 * 8, 0); } } + // IR_APPROX (51) — pure annotation, skip(无运算语义,不能崩) + if op2 == 51 { } + // IR_DYN_PACK (43):dyn 变量双槽 [value, tag]——slot[d]=值、slot[d+1]=tag + if op2 == 43 { + if d2 >= 0 && t1 >= 0 { + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); + w64(g_ir_vals, (d2 + 1) * 8, t2); + } + } + // IR_DYN_TAG (41):d := slot[ρ(s1)+1](读 +8 槽) + if op2 == 41 { if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, (t1 + 1) * 8)); } } + // IR_DYN_VAL (42):d := slot[ρ(s1)](读 +0 槽) + if op2 == 42 { if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } } + // IR_DYN_DISPATCH (44):响亮报错(与主循环同文案同码;不再静默落空) + if op2 == 44 { + println("error: interp 不支持动态分发(IR_DYN_DISPATCH)"); + g_interp_abort = 2; break; + } + // IR_CALL_EXTERN (45):响亮报错(ELF 静态构建同样拒绝,见 F16) + if op2 == 45 { + print("error: interp 无法调用外部函数:"); println(istr_get(t1)); + g_interp_abort = 2; break; + } + // IR_CALL (4) / IR_SPAWN (27):与主循环同一分派(嵌套内联 / 内置近似) + if op2 == 4 || op2 == 27 { ir_interp_call(d2, t1, t2, t3); } + if op2 == 5 { + if t1 >= 0 { rval = r64(g_ir_vals, t1 * 8); } + ip2 = f_count; // return exits the callee immediately + } // IR_RETURN + if op2 == 19 && t1 >= 0 { + cv2 := r64(g_ir_vals, t1 * 8); + if cv2 != 0 { if t2 >= 0 && t2 < g_label_count { ip2 = r64(g_label_poses, t2 * 8); } } + else { if t3 >= 0 && t3 < g_label_count { ip2 = r64(g_label_poses, t3 * 8); } } } + if op2 == 20 { if t1 >= 0 && t1 < g_label_count { ip2 = r64(g_label_poses, t1 * 8); } } + ip2 = ip2 + 1; + } + // Restore label state + g_label_count = old_lc; + g_label_poses = old_poses; + g_label_cap = old_poses_cap; + // 重入帧恢复:外层帧的形参/局部/临时槽回到调用前(返回结果经局部 rval 传递, + // 不占本槽区,故恢复不丢返回值) + if has_frame != 0 { + fi5 : ., mut = 0; + loop { if fi5 >= vc { break; } + w64(g_ir_vals, (vs + fi5) * 8, r64(frame, fi5 * 8)); + fi5 = fi5 + 1; } + } + g_interp_call_depth = g_interp_call_depth - 1; + if g_interp_abort != 0 { return 0; } + return rval; +} + fn ir_interpret() -> int { // Find main function in the dataflow graph main_idx : ., mut = -1; @@ -79,6 +458,14 @@ fn ir_interpret() -> int { node_count := r64(g_df_func_node_count, main_idx * 8); if node_start < 0 || node_count <= 0 { return -1; } + // 解释器状态复位(同一进程内可多次调用 ir_interpret)。 + // 注:callee 返回值不再经「暂存槽」传递(原 ret_slot = g_ir_var_count, + // 更早的 g_ir_vals[0] 约定会静默覆写首个全局)——现由 ir_interp_run_fn 的 + // 局部返回值传递,该缺陷类结构性消失。 + g_interp_abort = 0; + g_interp_call_depth = 0; + g_interp_stack = alloc(IR_INTERP_MAX_DEPTH * 8); // 活动函数栈(重入检测) + // Initialize value store (size = node_count + padding for destinations) need := g_ir_var_count + 64; if g_ir_vals_cap < need { @@ -92,6 +479,20 @@ fn ir_interpret() -> int { vi = vi + 1; } + // 编译期标量常量全局初始化阶段——与 ELF _start 的常量初始化循环 + // (os/linux/entry.cr 的 g_ir_globals +16 环)同语义、同数据源。此前解释器 + // 无此阶段:可变标量全局读回 0(不可变标量因 find_global_const_node 折叠 + // 而侥幸正确)。运行期初始化的聚合/非常量全局不在此——它们在 main 序言以 + // IR 序列注入(ir_gen.cr inject_global_inits,主循环执行)。 + gi2 : ., mut = 0; + loop { + if gi2 >= g_ir_global_count { break; } + iv2 := r64(g_ir_globals, gi2 * 24 + 16); + gv2 := r64(g_ir_globals, gi2 * 24 + 8); + if iv2 != 0 && gv2 >= 0 && gv2 < need { w64(g_ir_vals, gv2 * 8, iv2); } + gi2 = gi2 + 1; + } + // Pre-scan: build label→node mapping (for branches) g_label_count = 0; li : ., mut = 0; @@ -146,7 +547,9 @@ fn ir_interpret() -> int { s1 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S1); s2 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S2); s3 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S3); - ti := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_TK); + // R2 P5 Task 2(D19):派生码(同上一处;IR_BINARY 传参,当前不消费)。 + ti := sh_dfn_code_of_slots(r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_AUX)); if op == 1 { if d >= 0 { w64(g_ir_vals, d * 8, s1); } } // IR_CONST(dex 的 s1 为缩放整数) if op == 5 { if s1 >= 0 { return r64(g_ir_vals, s1 * 8); } return 0; } // IR_RETURN @@ -391,158 +794,12 @@ fn ir_interpret() -> int { // IR_AWAIT if op == 29 { if d >= 0 { w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)); } } - // IR_CALL or IR_SPAWN — only handles direct calls from main's graph - // Inline-executed callee graphs do NOT support nested calls. + // IR_CALL (4) / IR_SPAWN (27) —— 统一分派(ir_interp_call;与 callee 内联 + // 路径共用同一实现:内置近似 / 用户函数内联 / 嵌套递归 / 中止码上抛全在此一处)。 + // 原实现把这段分派复制在主循环里,callee 路径无对应物 → 双路径静默分叉(TODO #11)。 if op == 4 || op == 27 { - fn_ni := s3; - fn_name := istr_get(fn_ni); - sfi := find_so_fn(fn_ni); - if sfi >= 0 && s2 >= 1 { - tf := sym_type(sfi); - if tf == 1 || tf == 3 { // TAG_VARIADIC: print/println — NOOP (syscall3 returns 0) - } else if tf == 2 || tf == 3 { // TAG_AUTO_STR: print_i/println_i — NOOP (syscall3 returns 0) - } - } - // syscall3/syscall4 — interpreter returns 0 - if str_eq(fn_name, "syscall3") != 0 || str_eq(fn_name, "syscall4") != 0 { - if d >= 0 { w64(g_ir_vals, d * 8, 0); } - } - if str_eq(fn_name, "str_len") != 0 { - if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, istr_len(r64(g_ir_vals, s1 * 8))); } - } - if str_eq(fn_name, "str_eq") != 0 { - if d >= 0 && s2 >= 2 { - left_s := istr_get(r64(g_ir_vals, s1 * 8)); - right_s := istr_get(r64(g_ir_vals, (s1 + 1) * 8)); - w64(g_ir_vals, d * 8, str_eq(left_s, right_s)); - } - } - if str_eq(fn_name, "concat") != 0 { - if d >= 0 && s2 >= 2 { - w64(g_ir_vals, d * 8, ir_interp_str_concat( - r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); - } - } - if str_eq(fn_name, "int_str") != 0 { - if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))); } - } - if str_eq(fn_name, "chr") != 0 { - if d >= 0 && s2 >= 1 { w64(g_ir_vals, d * 8, str_intern(chr(r64(g_ir_vals, s1 * 8)))); } - } - if str_eq(fn_name, "get_char") != 0 { - if d >= 0 && s2 >= 2 { w64(g_ir_vals, d * 8, ir_interp_str_char(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))); } - } - if str_eq(fn_name, "str_sub") != 0 { - if d >= 0 && s2 >= 3 { w64(g_ir_vals, d * 8, ir_interp_str_sub(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8), r64(g_ir_vals, (s1 + 2) * 8))); } - } - if str_eq(fn_name, "load_str_ptr") != 0 { - if d >= 0 && s2 >= 2 { - b := r64(g_ir_vals, s1 * 8); p := r64(g_ir_vals, s1 + 1 * 8); - lo := load8(b, p) + load8(b, p+1)*256 + - load8(b, p+2)*65536 + load8(b, p+3)*16777216; - hi := load8(b, p+4) + load8(b, p+5)*256 + - load8(b, p+6)*65536 + load8(b, p+7)*16777216; - if hi < 0 { hi = hi + 4294967296; } - w64(g_ir_vals, d * 8, lo + hi * 4294967296); - } - } - if str_eq(fn_name, "store_str_ptr") != 0 { - if s2 >= 3 { - b := r64(g_ir_vals, s1 * 8); p := r64(g_ir_vals, s1 + 1 * 8); v := r64(g_ir_vals, s1 + 2 * 8); - lo : ., mut = v % 4294967296; hi : ., mut = v / 4294967296; - if v < 0 { lo = v; hi = -1; } - store8(b, p, lo%256); store8(b, p+1, (lo/256)%256); - store8(b, p+2, (lo/65536)%256); store8(b, p+3, (lo/16777216)%256); - store8(b, p+4, hi%256); store8(b, p+5, (hi/256)%256); - store8(b, p+6, (hi/65536)%256); store8(b, p+7, (hi/16777216)%256); - } - if d >= 0 { w64(g_ir_vals, d * 8, 0); } - } - if str_eq(fn_name, "str_len") != 0 || str_eq(fn_name, "str_eq") != 0 || - str_eq(fn_name, "concat") != 0 || str_eq(fn_name, "int_str") != 0 || - str_eq(fn_name, "chr") != 0 || str_eq(fn_name, "get_char") != 0 || - str_eq(fn_name, "str_sub") != 0 { ip = ip + 1; continue; } - // Regular function call (single level — no recursive/nested call support) - if d >= 0 { - cfi : ., mut = 0; - loop { - if cfi >= g_ir_func_count { break; } - if r64(g_ir_func_name_idx, cfi * 8) == fn_ni { - f_start := r64(g_df_func_node_start, cfi * 8); - f_count := r64(g_df_func_node_count, cfi * 8); - if f_start >= 0 && f_count > 0 { - // Save label state - old_lc := g_label_count; - old_poses := g_label_poses; - old_poses_cap := g_label_cap; - g_label_poses = alloc(64 * 8); g_label_cap = 64; - // Build label map for callee - li2 : ., mut = 0; - loop { if li2 >= f_count { break; } - n_op := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_OPCODE); - n_s1 := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_S1); - if n_op == 21 { if n_s1 >= 0 { - grow_label_poses(n_s1 + 1); - w64(g_label_poses, n_s1 * 8, li2); - if n_s1 + 1 > g_label_count { g_label_count = n_s1 + 1; } - }} - li2 = li2 + 1; } - // Copy args from caller positions to callee param vars - pstart := r64(g_ir_func_var_start, cfi * 8); - pai : ., mut = 0; - loop { if pai >= s2 { break; } - w64(g_ir_vals, (pstart + pai) * 8, r64(g_ir_vals, (s1 + pai) * 8)); - pai = pai + 1; } - // Execute callee graph (inline) - ip2 : ., mut = 0; - loop { - if ip2 >= f_count { break; } - op2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_OPCODE); - d2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_DEST); - t1 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S1); - t2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S2); - t3 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S3); - t4 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_TK); - if op2 == 1 && d2 >= 0 { w64(g_ir_vals, d2 * 8, t1); } - if op2 == 2 && t1 >= 0 && t2 >= 0 { ir_interp_binary(d2, t1, t2, t3, t4); } - if op2 == 49 || op2 == 50 { println("interpreter error: IR_I2F/IR_F2I needs binary64 semantics (apx dex)"); return -1; } - if op2 == 3 && t1 >= 0 { - ov2 := r64(g_ir_vals, t1 * 8); - if t3 == 1 { w64(g_ir_vals, d2 * 8, -ov2); } - else if t3 == 2 { if ov2 == 0 { w64(g_ir_vals, d2 * 8, 1); } else { w64(g_ir_vals, d2 * 8, 0); } } } - // Keep callee locals in the shared value store. - // The old inline path skipped these opcodes, so - // loop-carried assignments never changed state. - if op2 == 6 && d2 >= 0 { w64(g_ir_vals, d2 * 8, 0); } // IR_ALLOC - if op2 == 9 && t1 >= 0 && t2 >= 0 { w64(g_ir_vals, t1 * 8, r64(g_ir_vals, t2 * 8)); } // IR_STORE - if op2 == 10 && d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } // IR_LOAD - if op2 == 32 && d2 >= 0 { w64(g_ir_vals, d2 * 8, 0); } // IR_ARENA_NEW - if op2 == 33 { } // IR_ARENA_RESET - if op2 == 46 || op2 == 47 { - if d2 >= 0 && t1 >= 0 { w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)); } - } - if op2 == 5 { - if t1 >= 0 { w64(g_ir_vals, 0, r64(g_ir_vals, t1 * 8)); } - ip2 = f_count; // return exits the callee immediately - } // IR_RETURN - if op2 == 19 && t1 >= 0 { - cv2 := r64(g_ir_vals, t1 * 8); - if cv2 != 0 { if t2 >= 0 && t2 < g_label_count { ip2 = r64(g_label_poses, t2 * 8); } } - else { if t3 >= 0 && t3 < g_label_count { ip2 = r64(g_label_poses, t3 * 8); } } } - if op2 == 20 { if t1 >= 0 && t1 < g_label_count { ip2 = r64(g_label_poses, t1 * 8); } } - ip2 = ip2 + 1; - } - rval := r64(g_ir_vals, 0 * 8); - // Restore label state - g_label_count = old_lc; - g_label_poses = old_poses; - g_label_cap = old_poses_cap; - w64(g_ir_vals, d * 8, rval); - } - break; - } - cfi = cfi + 1; } - } + ir_interp_call(d, s1, s2, s3); + if g_interp_abort != 0 { return g_interp_abort; } } ip = ip + 1; diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 613a548c..efcc593c 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -125,8 +125,11 @@ fn arr_len_lit_of(arr_var: int) -> int { } if ti == TI_STR { prod := r64(g_df_var_producer, arr_var * 8); + // R2 P5 Task 2(D19):读 **派生码**(不是 40 槽——单槽化后 40 槽 = 类型项引用); + // 常量节点的行是原子基行 ⇒ 派生码逐字节 ≡ 单槽化前该槽的混用码。 if prod >= 0 && r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CONST && - r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_TK) == TI_STR { + sh_dfn_code_of_slots(r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_AUX)) == TI_STR { return istr_len(r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1)); } } @@ -190,6 +193,310 @@ fn new_ir_var(name: string, type_idx: int) -> int { return idx; } +// ── 可选运行期表示面(R2 P4 Task 5;裁决 5「解包侧判表示」;附录 B.7)── +// 事实:`Some(1)`/`None` 走 IR_MAKE_ENUM 对象([tag][payload…]),而 `T?` 槽里的 +// **裸值**仍是裸值 ⇒ 同一 `T?` 值两种表示;`Some` 臂的载荷解包(IR_LOAD_FIELD fi+1) +// 把裸值当对象解引用 ⇒ 双路径 SIGSEGV(check/build rc=0 零诊断)。 +// 机制(**纯 IR**:无新 opcode、无 ABI/帧布局改动 ⇒ ELF 后端与解释器天然同源): +// 每个可选槽(局部/形参/调用结果)配一个**表示位 IR 变量**(普通 int 局部,运行期值: +// 0 = 裸值、1 = 装箱)。写点置位(静态已知 → 常量;源已配表示位 → 拷贝),**解包点 +// 分派**(`match` 的 Some/None 臂、`?`): +// · 裸值(0)→ 槽值**即**载荷(Some 臂直取、不读 tag;None 臂不命中) +// · 装箱(≠0)→ 既有 tag 分派 + 字段 fi+1 取载荷(逐字不变) +// 跨函数面(返回/形参)用隐藏全局信道(纯 IR:返回点写/调用点读、调用点写/被调序言读)。 +// 未覆盖面(结构体字段/数组元素/全局槽/match 结果/惰性 thunk):不配表示位 ⇒ 解包走 +// **既有**装箱假定(P3 前语义)——响亮失败或既有正确值,不新增静默错值(逐形态实测入报告)。 +// g_optrep_on 关(AST 无 EXPR_OPTIONAL 且无 `Some`/`None`)⇒ 本面**零足迹**(发射面逐字节不变)。 + +// 表示码常量 OE_BARE/OE_BOXED 声明在 **globals.cr**(负值文件级常量放本文件会静默读 0 +// ——见该处注记与 iface_registry.cr:23 先例;实测踩过)。 + +fn grow_ir_var_rep(needed: int) { + if needed < g_ir_var_rep_cap { return; } + nc := g_ir_var_rep_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_ir_var_rep, g_ir_var_rep_cap * 8, nb); + // 新槽填 **-1(哨兵 = 无表示位)**——alloc 零初始化,不填则「未写槽」被读成 + // 表示位 var 0(每个值都被判成「有表示位」= 静默错值;实测踩过:`None` 初值 + // 的表示位被读成 var0 的当前值 0 = 裸值)。照 tag2l.cr 的 tag 区初始化先例。 + z : ., mut = g_ir_var_rep_cap; + loop { if z >= nc { break; } w64(nb, z * 8, -1); z = z + 1; } + g_ir_var_rep = nb; g_ir_var_rep_cap = nc; +} + +// 槽的表示位变量索引(-1 = 无表示位 = 非可选槽 / 未覆盖面 / 本编译单元未启用)。 +// 守卫走 **cap(int 全局,默认 0)而非 str_len(base)**——base 未分配时是空串/空指针, +// 未初始化的 str_len 即 SIGSEGV(实测:文件路径(main.cr 逐函数 + .cir 缓存)不经过 +// 批量入口的复位面 ⇒ 本函数必须自守卫)。 +fn irv_rep(var_idx: int) -> int { + if var_idx < 0 { return -1; } + if g_ir_var_rep_cap <= 0 { return -1; } + if var_idx >= g_ir_var_rep_cap { return -1; } + return r64(g_ir_var_rep, var_idx * 8); +} + +fn irv_set_rep(var_idx: int, rep_var: int) { + if var_idx < 0 { return; } + grow_ir_var_rep(var_idx + 1); + w64(g_ir_var_rep, var_idx * 8, rep_var); +} + +// 置表示位:enc >= 0 → 从该表示位槽拷贝;OE_BARE/OE_BOXED → 直接常量写。 +fn emit_rep_set(rep_var: int, enc: int) { + if rep_var < 0 { return; } + if enc == oe_bare() { emit(IR_CONST, rep_var, 0, 0, 0, TI_INT); return; } + if enc == oe_boxed() { emit(IR_CONST, rep_var, 1, 0, 0, TI_INT); return; } + if enc >= 0 { emit(IR_STORE, -1, rep_var, enc, 0, 0); } +} + +// 给可选槽配表示位变量(初始值 = 1 装箱 = 既有语义);非可选/未启用 → -1。 +fn opt_rep_slot(ti: int, name: string) -> int { + if g_optrep_on == 0 { return -1; } + if ti < 0 || ti >= g_type_count { return -1; } + if get_type_kind(ti) != TYP_OPTIONAL { return -1; } + rv := new_ir_var(name + "_rep", TI_INT); + emit(IR_CONST, rv, 1, 0, 0, TI_INT); + return rv; +} + +// 全局槽的声明类型是否可选(globals 无表示位 ⇒ 视作未覆盖面;照声明区分裸/装箱假定)。 +fn global_decl_optional(name_ni: int) -> int { + gi : ., mut = 0; + loop { + if gi >= g_global_let_count { break; } + lnode := r64(g_global_lets, gi * 8); + if ast_a(lnode) == name_ni { + tn := ast_b(lnode); + if tn >= 0 && res_type_node(tn) >= 0 { + if get_type_kind(res_type_node(tn)) == TYP_OPTIONAL { return 1; } + } + return 0; + } + gi = gi + 1; + } + return 0; +} + +// 被调方返回类型是否可选。判据走**返回类型节点**(`EXPR_FN.type_val`)而非 +// `fi_return_type`:后者是 parser 存的**裸类型码**,而 `T?` 的类型节点 `type_val` = 0 +// ⇒ 被读成 int 行、判据恒假(实测踩过:裸值返回不写返回信道 + 调用点不读信道, +// 回落装箱假定 ⇒ `fn g() -> int? { return 5; }` 的调用点解包仍崩)。 +fn callee_ret_optional(func_ni: int) -> int { + if func_ni < 0 { return 0; } + cfi := find_func(func_ni); + if cfi < 0 { return 0; } + cfn := fi_ast_node(cfi); + if cfn < 0 || ast_kind(cfn) != EXPR_FN { return 0; } + rtn := ast_type_val(cfn); + if rtn < 0 { return 0; } + rti := res_type_node(rtn); + if rti < 0 || rti >= g_type_count { return 0; } + if get_type_kind(rti) == TYP_OPTIONAL { return 1; } + return 0; +} + +// ── 容量批 T2(裁-CAP-1 (a)):可选**聚合槽**的存储边界规范化(写点装箱)── +// 法则:可选聚合槽(结构体字段 / 数组·切片元素 / 元组元素 / 枚举载荷 / 全局槽)任何时刻恒持 +// **装箱值(Some 对象)**或 None;**裸 T 值在写点装箱**。读侧零改动(既有装箱假定)。 +// 局部/形参/返回/调用结果**不走本律**(R2 P4 Task 5 表示位机制不变,可持裸值)。 +// 零足迹:目标槽类型不可选 ⇒ 本组函数恒不发射任何 IR(非可选程序逐字节不变)。 +// 未覆盖面(登记):指针写 `IR_STORE_PTR`——同一站点同时服务局部与聚合槽,装箱会与局部 +// 表示位冲突(实测 c1 今日正确 / 装箱后静默错值);元组·数组字面量的非 ident/调用元素。 + +// 槽声明类型侧表(var → 声明类型索引;-1 = 无注解/未知)。仅进程内状态,零布局变更。 +// 内容(IR var 索引 → 类型索引)由声明面写入(LET 注解 / 形参类型节点),读点 = 元素写。 +fn grow_ir_var_decl(needed: int) { + if needed < g_ir_var_decl_cap { return; } + nc := g_ir_var_decl_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); _dyncpy(g_ir_var_decl_ti, g_ir_var_decl_cap * 8, nb); + z : ., mut = g_ir_var_decl_cap; + loop { if z >= nc { break; } w64(nb, z * 8, -1); z = z + 1; } + g_ir_var_decl_ti = nb; g_ir_var_decl_cap = nc; +} +fn irv_decl_ti(v: int) -> int { + if v < 0 { return -1; } + if g_ir_var_decl_cap <= 0 { return -1; } + if v >= g_ir_var_decl_cap { return -1; } + return r64(g_ir_var_decl_ti, v * 8); +} +fn irv_set_decl_ti(v: int, ti: int) { + if v < 0 || ti < 0 { return; } + grow_ir_var_decl(v + 1); + w64(g_ir_var_decl_ti, v * 8, ti); +} + +// 目标槽类型是否可选(未知/越界 ⇒ 0 = 不装箱) +fn ti_is_optional(ti: int) -> int { + if ti < 0 { return 0; } + if ti >= g_type_count { return 0; } + if get_type_kind(ti) == TYP_OPTIONAL { return 1; } + return 0; +} + +// 槽(IR var)**声明**类型:声明侧表优先,否则运行期类型 +fn slot_decl_ti(v: int) -> int { + d := irv_decl_ti(v); + if d >= 0 { return d; } + return irv_type(v); +} + +// 声明类型 → 数组/切片元素类型(-1 = 非数组/切片) +fn elem_ti_of_decl(ti: int) -> int { + if ti < 0 { return -1; } + if ti >= g_type_count { return -1; } + k := get_type_kind(ti); + if k == TYP_ARRAY || k == TYP_SLICE { return get_type_data(ti); } + return -1; +} + +// 数组/切片元素类型(-1 = 非数组/切片或未知) +fn elem_ti_of(arr_var: int) -> int { + return elem_ti_of_decl(slot_decl_ti(arr_var)); +} + +// 结构体字段声明类型(field_node = EXPR_FIELD 目标节点;checker 写入 ast_c = 结构体名 ni; +// 数字元组下标形(ast_type_val > 0)不是结构体字段 ⇒ 不判) +fn field_ti_of_node(field_node: int, fi: int) -> int { + if field_node < 0 || fi < 0 { return -1; } + if ast_type_val(field_node) > 0 { return -1; } + si := find_struct(ast_c(field_node)); + if si < 0 { return -1; } + return res_type_node(si_field_type_node(si, fi)); +} + +// 结构体行字段声明类型(结构体字面量写点用;si = 行,来自 find_struct_by_name) +fn struct_row_field_ti(si: int, fi: int) -> int { + if si < 0 || fi < 0 { return -1; } + return res_type_node(si_field_type_node(si, fi)); +} + +// 全局槽声明类型(-1 = 非全局/无类型节点) +fn global_decl_ti(name_ni: int) -> int { + gi : ., mut = 0; + loop { + if gi >= g_global_let_count { break; } + lnode := r64(g_global_lets, gi * 8); + if ast_a(lnode) == name_ni { + tn := ast_b(lnode); + if tn >= 0 { return res_type_node(tn); } + return -1; + } + gi = gi + 1; + } + return -1; +} + +// 枚举变体载荷声明类型(-1 = 非用户枚举构造器/未知) +fn enum_payload_ti(name_ni: int, pos: int) -> int { + si := find_gsym(name_ni); + if si < 0 { return -1; } + ei := find_enum_row_of(sym_type(si)); + if ei < 0 { return -1; } + vi : ., mut = 0; + loop { + if vi >= ei_variant_count(ei) { return -1; } + if ei_variant_name(ei, vi) == name_ni { + return res_type_node(ei_variant_type_node(ei, vi, pos)); + } + vi = vi + 1; + } + return -1; +} + +// 元素表达式(元组/数组字面量的元素槽无声明面)的可选性:**保守 = 不可选 ⇒ 不装箱** +// 覆盖面 = ident(声明侧表)/ 调用(被调返回类型可选);其余形态登记为未覆盖面。 +fn elem_node_optional(node: int) -> int { + if node < 0 { return 0; } + k := ast_kind(node); + if k == EXPR_IDENT { + lv := find_local(ast_int_val(node)); + if lv >= 0 { return ti_is_optional(slot_decl_ti(lv)); } + return 0; + } + if k == EXPR_CALL { + ni := ast_a(node); + fi2 := find_func(ni); + if fi2 >= 0 { return callee_ret_optional(ni); } + return 0; + } + return 0; +} + +// 装箱体(静态):obj = Some(v)(无新 opcode;形态照既有 EXPR_ENUM_CONSTRUCTOR 发射: +// IR_MAKE_ENUM 的 s1 = 变体名索引、载荷经 IR_STORE_FIELD 存 fi+1;tag 值 = 名索引) +fn emit_box_some(val_var: int) -> int { + obj := new_ir_var("box", TI_INT); + emit(IR_MAKE_ENUM, obj, str_intern("Some"), 1, 0, 0); + emit(IR_STORE_FIELD, -1, obj, val_var, 1, 0); + return obj; +} + +// 写点规范化(按**可选性标志**):不可选 ⇒ 原样(零 IR);已装箱 ⇒ 原样; +// 静态裸值 ⇒ 直接装箱;源槽带表示位 ⇒ **运行期条件装箱**(裸则装、装箱则原样)。 +fn box_for_slot_flag(val_node: int, val_var: int, is_opt: int) -> int { + if val_var < 0 { return val_var; } + if is_opt == 0 { return val_var; } + enc := rep_enc_of_expr(val_node, val_var); + if enc == oe_boxed() { return val_var; } + if enc == oe_bare() { return emit_box_some(val_var); } + dest := new_ir_var("boxed", TI_INT); + bare_lbl := new_label(); + keep_lbl := new_label(); + end_lbl := new_label(); + zb := new_ir_var("_bz", TI_INT); + emit(IR_CONST, zb, 0, 0, 0, TI_INT); + isb := new_ir_var("box_isbare", TI_INT); + emit(IR_BINARY, isb, enc, zb, OP_EQ, 0); + emit(IR_BRANCH, -1, isb, bare_lbl, keep_lbl, 0); + emit(IR_LABEL, -1, bare_lbl, 0, 0, 0); + boxv := emit_box_some(val_var); + emit(IR_STORE, -1, dest, boxv, 0, 0); + emit(IR_JUMP, -1, end_lbl, 0, 0, 0); + emit(IR_LABEL, -1, keep_lbl, 0, 0, 0); + emit(IR_STORE, -1, dest, val_var, 0, 0); + emit(IR_LABEL, -1, end_lbl, 0, 0, 0); + return dest; +} + +// 写点规范化(按**目标槽类型**) +fn box_for_optional_slot(val_node: int, val_var: int, slot_ti: int) -> int { + return box_for_slot_flag(val_node, val_var, ti_is_optional(slot_ti)); +} + +// 值表达式的**静态表示判定**(解包侧判表示的另一半:写点置位的入参)。 +// 返回:>= 0 = 从该表位槽拷贝(源值自身已配表示位:可选局部/形参/调用结果); +// OE_BARE = 静态裸值(T 值注入 `T?`:字面量/算术/解包结果/非可选槽/聚合字面量…); +// OE_BOXED = 静态装箱(`Some`/`None`)**或**未覆盖面回落(字段/下标/match 结果/ +// 惰性 thunk/未识别形态)——回落取装箱 = P3 前既有语义,绝不新造静默错值。 +fn rep_enc_of_expr(node: int, val_var: int) -> int { + if val_var >= 0 { + rv := irv_rep(val_var); + if rv >= 0 { return rv; } + } + if node < 0 { return oe_boxed(); } + k := ast_kind(node); + if k == EXPR_ENUM_CONSTRUCTOR { + nm := istr_get(ast_a(node)); + if str_eq(nm, "Some") != 0 || str_eq(nm, "None") != 0 { return oe_boxed(); } + return oe_bare(); // 用户枚举变体 = T 值(对象指针即载荷) + } + if k == EXPR_INT || k == EXPR_BOOL || k == EXPR_DEX || k == EXPR_STRING { return oe_bare(); } + if k == EXPR_CHAR { return oe_bare(); } + if k == EXPR_BINARY || k == EXPR_UNARY || k == EXPR_AS { return oe_bare(); } + if k == EXPR_TRY { return oe_bare(); } // `?` 解包结果 = T 值 + if k == EXPR_STRUCT || k == EXPR_ARRAY || k == EXPR_TUPLE { return oe_bare(); } + if k == EXPR_IDENT { + ni := ast_int_val(node); + if find_local(ni) >= 0 { return oe_bare(); } // 无表示位(上方已排除可选槽)= 非可选槽 + gv := find_global(ni); + if gv >= 0 { + if global_decl_optional(ni) != 0 { return oe_boxed(); } + return oe_bare(); + } + return oe_bare(); // 枚举裸变体等 T 值 + } + return oe_boxed(); +} + fn emit(opcode: int, dest: int, src1: int, src2: int, src3: int, type_kind: int) { // Build linear IR (.ccr) — consumed by x86-64 backend idx := g_ir_instr_count; @@ -199,10 +506,17 @@ fn emit(opcode: int, dest: int, src1: int, src2: int, src3: int, type_kind: int) iri_set_s1(idx, src1); iri_set_s2(idx, src2); iri_set_s3(idx, src3); - iri_set_tk(idx, type_kind); + // R2 P5 Task 2(D19 单槽化):类型面**单源** = DFNode 的(项, 辅码)两槽。 + // 本处是**唯一**拆分点:同一次 sh_tk_split 同时喂 DFNode 两槽与 iri_tk 的派生码 + // (sh_dfn_code_of_slots)——既无第二真源,也不可能两处漂移。 + sh_tk_split(opcode, type_kind); + iri_set_tk(idx, sh_dfn_code_of_slots(g_sh_slot_term, g_sh_slot_aux)); g_ir_instr_count = idx + 1; // Build dataflow graph (.cir) in parallel - df_create_node(opcode, dest, src1, src2, src3, type_kind); + // 拆分时点 = **emit 期**(不得后移到 save 前:快照在 IR 生成期逐函数写 ⇒ 后填 + // 必然冷/暖分歧)。代价 = 每 emit 一次分类查询(非类型面 op = 几次整数比较即 + // 返回;类型面 op = sh_term_of_ti 的原生快路径/桥接缓存,实测耗时见报告 §6)。 + df_create_node(opcode, dest, src1, src2, src3, g_sh_slot_term, g_sh_slot_aux); } fn new_label() -> int { @@ -360,14 +674,6 @@ fn ir_call_return_type(func_ni: int) -> int { return TI_UNIT; } -fn get_ir_var_name(var_idx: int) -> string { - if var_idx >= 0 && var_idx < g_ir_var_count { - ni := irv_name(var_idx); - return istr_get(ni); - } - return ""; -} - // --- Track string constants --- fn track_str(str_idx: int) { i : ., mut = 0; @@ -761,6 +1067,10 @@ fn gen_expr(node: int) -> int { lv := find_local(name_idx); if lv >= 0 { return lv; } const_node := find_global_const_node(name_idx); + // 容量批 T2:**可选全局槽不得常量折叠**——字面量初值(`g : int? = 5`)的读点若被 + // 折成裸常量 5,则 match 的 tag 读会解引用该裸值(双路径 139,实测 e2_global_bare)。 + // 折叠面只对非可选全局生效(零足迹);可选全局由运行期初始化写入装箱对象。 + if const_node >= 0 && g_optrep_on != 0 && global_decl_optional(name_idx) != 0 { const_node = -1; } if const_node >= 0 { const_type : ., mut = TI_INT; if ast_kind(const_node) == EXPR_BOOL { const_type = TI_BOOL; } @@ -791,70 +1101,10 @@ fn gen_expr(node: int) -> int { right := ast_b(node); op := ast_c(node); - // Assignment - if op == OP_ASSIGN { - val_var := gen_expr(right); - val_var = force_if_thunk(val_var); - // Determine lhs kind - if ast_kind(left) == EXPR_IDENT { - name_idx := ast_int_val(left); - target := find_local(name_idx); - if target >= 0 { - if irv_type(target) == TI_DYN { - // Dyn variable assignment: pack value with type tag - tag := irv_type(val_var); - if tag < 0 { tag = TI_INT; } - emit(IR_DYN_PACK, target, val_var, tag, 0, 0); - } else { - // dex 槽位形式转换(数值迁移 Task 4:apx 槽存 bits,精确槽存缩放) - val_var = dex_store_adjust(target, val_var, right); - // F11:切片长度沿赋值传播(字面量/运行时界长度变量同步;源无记录则清除) - slice_len_copy_to(target, val_var); -emit(IR_STORE, -1, target, val_var, 0, 0); - } - } else { - gtarget := find_global(name_idx); - if gtarget >= 0 { - val_var = dex_store_adjust(gtarget, val_var, right); - emit(IR_STORE, -1, gtarget, val_var, 0, 0); - } - } - return val_var; - } - if ast_kind(left) == EXPR_FIELD { - obj_var := gen_expr(ast_a(left)); - obj_var = force_if_thunk(obj_var); - field_ni := ast_int_val(left); - fi := ast_data(left); // field index stored by checker - emit(IR_STORE_FIELD, -1, obj_var, val_var, fi, 0); - return val_var; - } - if ast_kind(left) == EXPR_INDEX { - arr_var := gen_expr(ast_a(left)); - arr_var = force_if_thunk(arr_var); - idx_node := ast_b(left); - idx_kind := ast_kind(idx_node); - // F1:写路径越界守卫钩子(EXPR_BINARY OP_ASSIGN 遗留路径,同步修复) - arr_len_lit : ., mut = arr_len_lit_of(arr_var); - if idx_kind == EXPR_INT { - emit_string_lit_bounds(arr_var, ast_int_val(idx_node)); - emit_slice_lit_bounds(arr_var, ast_int_val(idx_node)); - if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0 { - emit(IR_STORE_INDEX, -1, arr_var, val_var, ast_int_val(idx_node), 0); - } - } else { - idx_var := gen_expr(idx_node); - idx_var = force_if_thunk(idx_var); - emit_string_bounds(arr_var, idx_var); - emit_slice_bounds(arr_var, idx_var); - if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0 { - emit(IR_STORE_INDEX_VAR, val_var, arr_var, idx_var, 0, 0); - } - } - return val_var; - } - return val_var; - } + // R2 P5 Task 4(D25):`EXPR_BINARY + OP_ASSIGN` 赋值分支**已删**——不可达 + // (parser 不产该组合:`tok2op` 零 OP_ASSIGN;`T_EQ` → EXPR_ASSIGN;`+=` 族构造 + // `EXPR_ASSIGN` 包裹的 EXPR_BINARY 且 op ∈ {ADD,SUB,MUL,DIV};语料站点直方图 0 命中)。 + // 赋值发射面在下方 `EXPR_ASSIGN` 分支(gen_expr 的另一 arm),逐字未动。 // Regular binary left_var := gen_expr(left); @@ -1066,11 +1316,16 @@ emit(IR_STORE, -1, target, val_var, 0, 0); // F11:切片长度沿赋值传播(字面量/运行时界长度变量同步;源无记录则清除) slice_len_copy_to(lv, val_var); emit(IR_STORE, -1, lv, val_var, 0, 0); + // 可选表示(R2 P4 Task 5):赋值写点置位(同 LET——解包点据此分派) + rpv := irv_rep(lv); + if rpv >= 0 { emit_rep_set(rpv, rep_enc_of_expr(val_node, val_var)); } } } else { gv := find_global(name_idx); if gv >= 0 { val_var = dex_store_adjust(gv, val_var, val_node); + // 容量批 T2:可选全局槽写点规范化(裸值装箱;目标不可选 ⇒ 零 IR) + if g_optrep_on != 0 { val_var = box_for_optional_slot(val_node, val_var, global_decl_ti(name_idx)); } emit(IR_STORE, -1, gv, val_var, 0, 0); } } @@ -1080,6 +1335,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); obj_var := gen_expr(ast_a(target)); obj_var = force_if_thunk(obj_var); fi := ast_data(target); + // 容量批 T2:可选字段写点规范化(字段声明类型取自 checker 记的 ast_c 结构体名) + if g_optrep_on != 0 { val_var = box_for_optional_slot(val_node, val_var, field_ti_of_node(target, fi)); } emit(IR_STORE_FIELD, -1, obj_var, val_var, fi, 0); return val_var; } @@ -1087,6 +1344,16 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); arr_var := gen_expr(ast_a(target)); arr_var = force_if_thunk(arr_var); idx_node := ast_b(target); + // 容量批 T2:可选元素写点规范化(元素类型 = 数组/切片声明类型的内层; + // 全局数组的声明面在 g_global_lets ⇒ ident 形目标回退查全局声明) + et := elem_ti_of(arr_var); + if et < 0 { + anode := ast_a(target); + if anode >= 0 && ast_kind(anode) == EXPR_IDENT { + et = elem_ti_of_decl(global_decl_ti(ast_int_val(anode))); + } + } + if g_optrep_on != 0 { val_var = box_for_optional_slot(val_node, val_var, et); } // F1:写路径越界守卫钩子(修复前完全没有——见 compcert-round4 F1) arr_len_lit : ., mut = arr_len_lit_of(arr_var); if ast_kind(idx_node) == EXPR_INT { @@ -1244,8 +1511,12 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); is_module_call := call_flags == CALL_FLAG_MODULE || call_flags == CALL_FLAG_MODULE + CALL_FLAG_INLINE; is_inline_call := call_flags == CALL_FLAG_INLINE || call_flags == CALL_FLAG_MODULE + CALL_FLAG_INLINE; arg_vars : string, mut; arg_vars_cap : int, mut; + // R2 P4 Task 5:实参**节点**并行表(表示面写信道按实参位判表示需要节点—— + // 值 var 的类型不足以区分裸 T 与装箱 T?;方法调用时 arg 0 = 接收者节点) + arg_nodes : string, mut; arg_nodes_cap : int, mut; ac : ., mut = 0; arg_vars = alloc(64 * 8); arg_vars_cap = 64; + arg_nodes = alloc(64 * 8); arg_nodes_cap = 64; func_ni : ., mut = -1; // @builtin(args) — parser wraps @foo(args) as EXPR_CALL(func=EXPR_AT, ...) @@ -1434,6 +1705,7 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); self_var := gen_expr(obj_node); self_var = force_if_thunk(self_var); if ac >= arg_vars_cap { nc := arg_vars_cap * 2; nb := alloc(nc * 8); _dyncpy(arg_vars, arg_vars_cap * 8, nb); arg_vars = nb; arg_vars_cap = nc; } w64(arg_vars, ac * 8, self_var); + if ac >= arg_nodes_cap { nc := arg_nodes_cap * 2; nb := alloc(nc * 8); _dyncpy(arg_nodes, arg_nodes_cap * 8, nb); arg_nodes = nb; arg_nodes_cap = nc; } w64(arg_nodes, ac * 8, obj_node); ac = ac + 1; } } else if ast_kind(func_node) == EXPR_IDENT { @@ -1447,6 +1719,7 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); arg_var := gen_expr(ast_a(an)); arg_var = force_if_thunk(arg_var); if ac >= arg_vars_cap { nc := arg_vars_cap * 2; nb := alloc(nc * 8); _dyncpy(arg_vars, arg_vars_cap * 8, nb); arg_vars = nb; arg_vars_cap = nc; } w64(arg_vars, ac * 8, arg_var); + if ac >= arg_nodes_cap { nc := arg_nodes_cap * 2; nb := alloc(nc * 8); _dyncpy(arg_nodes, arg_nodes_cap * 8, nb); arg_nodes = nb; arg_nodes_cap = nc; } w64(arg_nodes, ac * 8, ast_a(an)); ac = ac + 1; an = ast_b(an); } @@ -1501,26 +1774,62 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if func_ni >= 0 && (ast_kind(func_node) == EXPR_IDENT) { gen_fi := find_func(func_ni); if gen_fi >= 0 && fi_generic_count(gen_fi) > 0 { - // Build type args string from call argument types + // ─── R2 P3 Task 5(Step 4):实例键类型项化 ─── + // 优先:checker 登记的**调用点绑定段**(g_gen_binds;节点 int_val = 段起始+1, + // 0 = 无)——按被调方泛型形参**声明序**给 ti,键 = `inst_key_of_ti` 规范结构名 + // (含命名/应用实参真身份),替换走 ti 路径(monomorph.cr)。前置 = 段长与形参 + // 数一致 + 全绑定 + 全具体(否则回落旧路径;半解实例比旧路径更坏)。 + // 回落(旧路径,**逐字保留**):键由**实参 IR 变量类型**拼名串(非原生 → "int" + // 兜底),替换走名字路径——`gen_create_instance(func, args, -1)`。 + gc_g := fi_generic_count(gen_fi); + biv := ast_int_val(node); type_args : ., mut = ""; - ai : ., mut = 0; - loop { - if ai >= ac { break; } - av := r64(arg_vars, ai * 8); - ti := TI_INT; - if av >= 0 { ti = irv_type(av); } - if ai > 0 { type_args = type_args + ","; } - if ti == TI_INT { type_args = type_args + "int"; } - else if ti == TI_STR { type_args = type_args + "string"; } - else if ti == TI_BOOL { type_args = type_args + "bool"; } - else if ti == TI_CHAR { type_args = type_args + "char"; } - else if ti == TI_DEX || ti == TI_DEX_S { type_args = type_args + "dex"; } - else if ti == TI_UNIT { type_args = type_args + "unit"; } - else { type_args = type_args + "int"; } - ai = ai + 1; + bstart : ., mut = -1; + if biv > 0 { + bs := biv - 1; + if bs < g_gen_binds_count { + bcnt := r64(g_gen_binds, bs * 8); + if bcnt == gc_g && bs + 1 + bcnt <= g_gen_binds_count { + ok_b : ., mut = 1; + gi_b : ., mut = 0; + loop { + if gi_b >= bcnt { break; } + bt := r64(g_gen_binds, (bs + 1 + gi_b) * 8); + if bt < 0 { ok_b = 0; break; } + if inst_ti_concrete(bt) == 0 { ok_b = 0; break; } + if inst_type_node_of_ti(bt) < 0 { ok_b = 0; break; } + if gi_b > 0 { type_args = type_args + ","; } + type_args = type_args + inst_key_of_ti(bt); + gi_b = gi_b + 1; + } + if ok_b != 0 { bstart = bs; } + } + } + } + if bstart < 0 { + // 旧路径:逐实参类型名串(**不得**改动——checker 未给绑定时的唯一退路) + type_args = ""; + ai : ., mut = 0; + loop { + if ai >= ac { break; } + av := r64(arg_vars, ai * 8); + ti := TI_INT; + if av >= 0 { ti = irv_type(av); } + if ai > 0 { type_args = type_args + ","; } + if ti == TI_INT { type_args = type_args + "int"; } + else if ti == TI_STR { type_args = type_args + "string"; } + else if ti == TI_BOOL { type_args = type_args + "bool"; } + else if ti == TI_CHAR { type_args = type_args + "char"; } + else if ti == TI_DEX || ti == TI_DEX_S { type_args = type_args + "dex"; } + else if ti == TI_UNIT { type_args = type_args + "unit"; } + else { type_args = type_args + "int"; } + ai = ai + 1; + } } // Find or create specialized function instance - spec_ni := gen_find_or_create(gen_fi, type_args); + spec_ni : ., mut = -1; + if bstart >= 0 { spec_ni = gen_find_or_create_bind(gen_fi, type_args, bstart); } + else { spec_ni = gen_find_or_create(gen_fi, type_args); } if spec_ni >= 0 { func_ni = fi_name(spec_ni); // Fall through to normal IR_CALL emission @@ -1576,8 +1885,16 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } // Hotpatch function call: emit IR_HOTPATCH_ROUTE instead of IR_CALL + // R2 P5 Task 2(F1 裁决):第 6 实参**必须显式给**。修复前本调用点只传 5 参, + // 而 `emit` 形参 6 个 ⇒ 第 6 参 = **残留寄存器值**(corec 由 Python bootstrap + // 生成:bootstrap/corec/backend/x86_64_stack_asm.py:222-226 只为实际存在的 + // 实参写寄存器,r9 保持前序计算的残留 ⇒ 实测同一语料两种程序形状得 0 / 4 两值; + // 自托管语义下缺省实参 = 0)。op39 的类型面 = 「无」(Task 0 表 A),故显式 0。 + // 已裁决偏差登记:含 hotpatch 的语料该节点码 4→0(该值本是垃圾、零消费者; + // canary 语料 ptr_arith 不含 hotpatch ⇒ 不受影响)。把未定义行为烘进分类表 + // 不可接受——分类表要求「每格有依据」。 if func_ni >= 0 && is_hotpatch_func(func_ni) != 0 { - emit(IR_HOTPATCH_ROUTE, dest, func_ni, first_arg_var, ac); + emit(IR_HOTPATCH_ROUTE, dest, func_ni, first_arg_var, ac, 0); return dest; } // Extern function call: emit IR_CALL_EXTERN for FFI dispatch @@ -1591,9 +1908,29 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } } } + // 可选表示(R2 P4 Task 5):实参表示写信道——逐实参位写(被调序言最早处读; + // 写点 = 全部实参求值之后、IR_CALL 紧邻之前 ⇒ 嵌套调用不覆写信道)。 + if g_optrep_on != 0 && g_optrep_arg_count > 0 { + ai3 : ., mut = 0; + loop { + if ai3 >= ac { break; } + if ai3 >= g_optrep_arg_count { break; } + av3 := r64(arg_vars, ai3 * 8); + an3 : ., mut = -1; + if ai3 < arg_nodes_cap { an3 = r64(arg_nodes, ai3 * 8); } + emit_rep_set(g_optrep_arg_cell0 + ai3, rep_enc_of_expr(an3, av3)); + ai3 = ai3 + 1; + } + } emit(IR_CALL, dest, first_arg_var, ac, func_ni, call_ti); + // 可选表示(R2 P4 Task 5):被调返回可选 ⇒ 1) 读返回信道(读点必须**紧跟真实 + // 调用**:调用点与返回点同进程同栈、其间无写入);2) **禁惰性 thunk**——thunk 把 + // 调用推迟到 force 点,读点即悬空(实测踩过:`v := g()` 的 g 判纯 ⇒ thunk ⇒ 表示位 + // 回落装箱 ⇒ 裸值返回解包崩)。代价 = 仅「返回可选」的被调不做 thunk 延迟。 + ret_opt : ., mut = 0; + if g_optrep_on != 0 && g_optrep_ret_cell >= 0 { ret_opt = callee_ret_optional(func_ni); } // Lazy thunk: if calling a pure function with single use, wrap as thunk - if func_ni >= 0 { + if func_ni >= 0 && ret_opt == 0 { call_fi := find_func(func_ni); if call_fi >= 0 && fi_ispure(call_fi) != 0 { grow_var_use_count(dest + 1); @@ -1605,6 +1942,11 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } } } + if ret_opt != 0 { + rvr := new_ir_var("call_rep", TI_INT); + emit(IR_LOAD, rvr, g_optrep_ret_cell, 0, 0, TI_INT); + irv_set_rep(dest, rvr); + } return dest; } @@ -1803,6 +2145,16 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); // Allocate a result variable for the match expression value result_var := new_ir_var("match_res", TI_INT); emit(IR_ALLOC, result_var, 0, 0, 0, TI_INT); + // 容量批 T2(裁-CAP-1 (a) 的 match 面 = 裁决 (i) 表示位案):结果槽配**表示位** + // (与既有局部机制同源;默认 1 = 装箱),各臂写点置位 ⇒ 消费点(LET/返回/解包) + // 自动继承正确表示。修复前:臂写裸值时无表示 ⇒ 消费侧落 `oe_boxed()` 装箱假定 + // ⇒ 双路径 139(T1 实测 e2_matchres_bare)。 + mrep_var : ., mut = -1; + if g_optrep_on != 0 { + mrep_var = new_ir_var("match_res_rep", TI_INT); + emit(IR_CONST, mrep_var, 1, 0, 0, TI_INT); + irv_set_rep(result_var, mrep_var); + } merge_lbl := new_label(); an : ., mut = first_arm; loop { @@ -1813,6 +2165,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if arm_pat >= 0 { pat_kind = ast_kind(arm_pat); } is_wildcard := 0; if pat_kind == EXPR_WILDCARD { is_wildcard = 1; } + // 可选表示位(R2 P4 Task 5):臂条件与臂体绑定共用(声明在臂作用域) + rep_v : ., mut = -1; body_lbl := new_label(); fall_lbl : ., mut = merge_lbl; has_next := 0; @@ -1821,14 +2175,44 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); emit(IR_JUMP, -1, body_lbl, 0, 0, 0); } else if pat_kind == EXPR_ENUMPAT { variant_ni := get_variant_name_idx(ast_a(arm_pat)); - tag_var := new_ir_var("tag", TI_INT); - emit(IR_LOAD_ENUM_TAG, tag_var, match_val, 0, 0, 0); - vtag := new_ir_var("vtag", TI_INT); - emit(IR_CONST, vtag, variant_ni, 0, 0, TI_INT); - cmp_var := new_ir_var("cmp", TI_INT); - emit(IR_BINARY, cmp_var, tag_var, vtag, OP_EQ, 0); - if has_next == 1 { fall_lbl = new_label(); } - emit(IR_BRANCH, -1, cmp_var, body_lbl, fall_lbl, 0); + // R2 P4 Task 5:**解包侧判表示**——scrutinee 配表示位(可选槽)时按位分派: + // 裸值(0)→ 值即载荷:Some 模式命中臂体(**不读 tag**——裸值非对象, + // 读 tag 即 SIGSEGV = 原红态);None 模式不命中(裸值非 null)。 + // 装箱(≠0)→ 既有 tag 分派(逐字不变)。 + // 无表示位(非可选槽 / 未覆盖面)→ 既有路径逐字不变(装箱假定)。 + if g_optrep_on != 0 && match_val >= 0 { rep_v = irv_rep(match_val); } + if rep_v < 0 { + tag_var := new_ir_var("tag", TI_INT); + emit(IR_LOAD_ENUM_TAG, tag_var, match_val, 0, 0, 0); + vtag := new_ir_var("vtag", TI_INT); + emit(IR_CONST, vtag, variant_ni, 0, 0, TI_INT); + cmp_var := new_ir_var("cmp", TI_INT); + emit(IR_BINARY, cmp_var, tag_var, vtag, OP_EQ, 0); + if has_next == 1 { fall_lbl = new_label(); } + emit(IR_BRANCH, -1, cmp_var, body_lbl, fall_lbl, 0); + } else { + is_some_pat : ., mut = 0; + if str_eq(istr_get(variant_ni), "Some") != 0 { is_some_pat = 1; } + tag_lbl := new_label(); + if has_next == 1 { fall_lbl = new_label(); } + zb := new_ir_var("_rz", TI_INT); + emit(IR_CONST, zb, 0, 0, 0, TI_INT); + isb := new_ir_var("rep_isbare", TI_INT); + if is_some_pat != 0 { emit(IR_BINARY, isb, rep_v, zb, OP_EQ, 0); } + else { emit(IR_BINARY, isb, rep_v, zb, OP_NE, 0); } + // Some:裸 → 直接命中;装箱 → 落 tag 比对。None:裸 → 不命中(落 fall); + // 装箱 → 落 tag 比对。 + if is_some_pat != 0 { emit(IR_BRANCH, -1, isb, body_lbl, tag_lbl, 0); } + else { emit(IR_BRANCH, -1, isb, tag_lbl, fall_lbl, 0); } + emit(IR_LABEL, -1, tag_lbl, 0, 0, 0); + tag_var := new_ir_var("tag", TI_INT); + emit(IR_LOAD_ENUM_TAG, tag_var, match_val, 0, 0, 0); + vtag := new_ir_var("vtag", TI_INT); + emit(IR_CONST, vtag, variant_ni, 0, 0, TI_INT); + cmp_var := new_ir_var("cmp", TI_INT); + emit(IR_BINARY, cmp_var, tag_var, vtag, OP_EQ, 0); + emit(IR_BRANCH, -1, cmp_var, body_lbl, fall_lbl, 0); + } } else if pat_kind == EXPR_INT { pat_val := new_ir_var("pval", TI_INT); emit(IR_CONST, pat_val, ast_int_val(arm_pat), 0, 0, TI_INT); @@ -1854,7 +2238,27 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); loop { if fi >= sub_count { break; } fv := new_ir_var("fld", TI_INT); - emit(IR_LOAD_FIELD, fv, match_val, 0, fi + 1, 0); // +1 for tag offset + if rep_v < 0 { + emit(IR_LOAD_FIELD, fv, match_val, 0, fi + 1, 0); // +1 for tag offset + } else { + // 解包侧判表示:裸值(0)→ 槽值**即**载荷(直取,不解引用); + // 装箱(≠0)→ 字段 fi+1(既有语义)。裸分支须**跳过**装 + // 箱分支的字段读(end_lbl)——否则裸值仍被解引用(原红态)。 + bare_lbl := new_label(); + boxed_lbl := new_label(); + end_lbl := new_label(); + zb2 := new_ir_var("_rz", TI_INT); + emit(IR_CONST, zb2, 0, 0, 0, TI_INT); + ib2 := new_ir_var("rep_isbare", TI_INT); + emit(IR_BINARY, ib2, rep_v, zb2, OP_EQ, 0); + emit(IR_BRANCH, -1, ib2, bare_lbl, boxed_lbl, 0); + emit(IR_LABEL, -1, bare_lbl, 0, 0, 0); + emit(IR_STORE, -1, fv, match_val, 0, 0); + emit(IR_JUMP, -1, end_lbl, 0, 0, 0); + emit(IR_LABEL, -1, boxed_lbl, 0, 0, 0); + emit(IR_LOAD_FIELD, fv, match_val, 0, fi + 1, 0); + emit(IR_LABEL, -1, end_lbl, 0, 0, 0); + } spn := ast_b(arm_pat) + fi; if spn >= 0 && ast_kind(spn) == EXPR_IDENT { bind_local(ast_int_val(spn), fv); @@ -1866,6 +2270,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); body_val = force_if_thunk(body_val); if body_val >= 0 { emit(IR_STORE, -1, result_var, body_val, 0, 0); + // 容量批 T2:结果槽表示位置位(同 LET/赋值的写点口径——解包点据此分派) + if mrep_var >= 0 { emit_rep_set(mrep_var, rep_enc_of_expr(arm_body, body_val)); } } pop_ir_scope(); emit(IR_JUMP, -1, merge_lbl, 0, 0, 0); @@ -1894,6 +2300,13 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); var := new_ir_var(istr_get(var_ni), TI_UNIT); declared_ti : ., mut = TI_UNIT; if type_node >= 0 { declared_ti = res_type_node(type_node); } + // 容量批 T2:登记声明类型(元素写点判定目标元素类型用——声明的 `[T?;N]` 才是 + // 权威;字面量推出的元素类型对可选元素退化为对象占位 TI_UNIT) + if g_optrep_on != 0 && type_node >= 0 { irv_set_decl_ti(var, declared_ti); } + // 可选表示(R2 P4 Task 5):显式 `T?` 标注的槽配表示位(默认 1 = 装箱)。 + // 推断槽(无标注)在初值求值后按初值表示面补配(`v := g()` / `x := Some(1)`)。 + rep_var : ., mut = opt_rep_slot(declared_ti, istr_get(var_ni)); + if rep_var >= 0 { irv_set_rep(var, rep_var); } // `dex` is the source-level type; its IR slot has two forms. Only an // explicitly tagged `apx` declaration uses binary64 bits. target_ti : ., mut = declared_ti; @@ -1921,6 +2334,12 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if val_node >= 0 { val_var := gen_expr(val_node); val_var = force_if_thunk(val_var); + // 可选表示:推断槽按初值表示面补配表示位(有表示位可拷贝 / Some/None 构造 / + // 未覆盖面回落值——回落配位无害:默认 1 = 装箱 = 既有语义) + if rep_var < 0 && g_optrep_on != 0 && rep_enc_of_expr(val_node, val_var) != oe_bare() { + rep_var = new_ir_var(istr_get(var_ni) + "_rep", TI_INT); + irv_set_rep(var, rep_var); + } // An `apx` dex local stores binary64 bits, unlike the default // scaled-integer dex form. The annotation alone is not enough: // switch the slot type and convert the initializer before later @@ -1950,6 +2369,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); // 运行时界 slice 同步长度变量——slice_len_copy_to) slice_len_copy_to(var, val_var); emit(IR_STORE, -1, var, val_var, 0, 0); + // 可选表示:写点置位(静态已知常量 / 源表示位拷贝)——解包点据此分派 + if rep_var >= 0 { emit_rep_set(rep_var, rep_enc_of_expr(val_node, val_var)); } } bind_local(var_ni, var); if is_apx != 0 { emit(IR_APPROX, -1, 0, 0, 0, 0); } @@ -1976,13 +2397,19 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if ast_kind(node) == EXPR_RETURN { if ast_a(node) >= 0 { - val_var := gen_expr(ast_a(node)); + val_node := ast_a(node); + val_var := gen_expr(val_node); val_var = force_if_thunk(val_var); // dex 边界规则(数值迁移 Task 4):函数返回一律精确形式(缩放整数)—— // apx 位模式在返回点转 scaled(F2I(bits×S),按定点 6 位舍入) if g_cur_ret_ti == TI_DEX && irv_type(val_var) == TI_DEX { val_var = dex_bits_to_scaled(val_var); } + // 可选表示(R2 P4 Task 5):返回点写返回信道——值求值**完成后**、IR_RETURN + // 紧邻处(其间无调用 ⇒ 嵌套调用不覆写信道;调用点紧跟 IR_CALL 读)。 + if g_cur_ret_opt != 0 { + emit_rep_set(g_optrep_ret_cell, rep_enc_of_expr(val_node, val_var)); + } emit(IR_RETURN, -1, val_var, 0, 0, 0); } else { emit(IR_RETURN, -1, -1, 0, 0, 0); @@ -2083,6 +2510,9 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if an < 0 { break; } val_var := gen_expr(ast_a(an)); val_var = force_if_thunk(val_var); + // 容量批 T2:可选载荷写点规范化(载荷类型 = 变体声明面;`Some`/`None` 关键字 + // 形不带变体行 ⇒ enum_payload_ti 返回 -1 ⇒ 原样,不装箱) + if g_optrep_on != 0 { val_var = box_for_optional_slot(ast_a(an), val_var, enum_payload_ti(name_idx, ai)); } emit(IR_STORE_FIELD, -1, s, val_var, ai + 1, 0); // +1 for tag offset an = ast_b(an); ai = ai + 1; @@ -2092,18 +2522,33 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); // Struct literal if ast_kind(node) == EXPR_STRUCT { + // F5 契约(见 parser.cr struct 分支):a=name idx、b=首 wrapper(连续)、c=字段数; + // wrapper.a=字段值节点(gen_expr 对 EXPR_NONE 前向)。逐 wrapper 解引用,不得按偏移 + // 直取相邻节点当字段值——复合字段值子树占多槽会错位(静默错误值)。 + // TODO #29 ①(名字绑定):wrapper.b=字段名 idx(parser 写入;-1 = 无名字信息 → 位序 + // 回落)。**存字段位按名字解出**(与 Python bootstrap 的 gen_struct_lit 同语义)—— + // 修复前按 wrapper 序直取 fi = 声明位序绑定 ⇒ P{b:11, a:22} 静默得 a=11。发出顺序 + // 仍是**源序**(求值顺序 = 源码书写顺序,与 bootstrap 一致)。 name_ni := ast_a(node); s := new_ir_var("struct", TI_UNIT); emit(IR_ALLOC_STRUCT, s, 0, 0, name_ni, 0); + si := find_struct_by_name(name_ni); fi : ., mut = 0; fn2 : ., mut = ast_b(node); loop { if fi >= ast_c(node) { break; } if fn2 >= 0 { - // fn2 = wrapper node (kind=0, a=value expr) + // fn2 = wrapper node (kind=EXPR_NONE, a=value expr, b=field name idx) val_var := gen_expr(fn2); val_var = force_if_thunk(val_var); - field_idx := fi; + field_idx : ., mut = fi; + nn := ast_b(fn2); + if si >= 0 && nn >= 0 { + jdi := struct_field_index_by_name(si, nn); + if jdi >= 0 { field_idx = jdi; } + } + // 容量批 T2:可选字段写点规范化(字面量形;元素值节点 = wrapper.a) + if g_optrep_on != 0 { val_var = box_for_optional_slot(ast_a(fn2), val_var, struct_row_field_ti(si, field_idx)); } emit(IR_STORE_FIELD, -1, s, val_var, field_idx, 0); fn2 = fn2 + 1; } @@ -2114,6 +2559,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); // Array literal if ast_kind(node) == EXPR_ARRAY { + // F5 契约(见 parser.cr 下标分支):a=首 wrapper(连续)、b=元素个数;wrapper.a=元素值 + // 节点(gen_expr 对 EXPR_NONE 前向)。逐 wrapper 解引用,不得按偏移直取相邻节点。 v := new_ir_var("arr", TI_UNIT); emit(IR_ALLOC_ARRAY, v, ast_b(node), 0, 0, 0); elem_ti : ., mut = TI_INT; @@ -2124,7 +2571,11 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); if en >= 0 { e_var := gen_expr(en); e_var = force_if_thunk(e_var); - if ei == 0 { elem_ti = irv_type(e_var); } + if ei == 0 { elem_ti = irv_type(e_var); } // 先取原元素类型(装箱前) + // 容量批 T2:可选元素写点规范化(字面量形;元素槽无声明面 ⇒ 按元素 + // 表达式可选性保守判定——ident/调用覆盖面,其余形态登记)。 + // 注意:数组字面量的 en 是 **wrapper** 节点(值在 ast_a)——须解引用后判定。 + if g_optrep_on != 0 { e_var = box_for_slot_flag(ast_a(en), e_var, elem_node_optional(ast_a(en))); } emit(IR_STORE_INDEX, -1, v, e_var, ei, 0); en = en + 1; } @@ -2194,8 +2645,32 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); return cast_var; } if ast_kind(node) == EXPR_TRY { - // Try: unwrap Result/Option, just emit the inner expr for now - return gen_expr(ast_a(node)); + // R2 P4 Task 5:`?` 解包 = 解包点之一 ⇒ 同规则判表示:裸值(0)→ 槽值即载荷 + // (直取);装箱(≠0)→ 字段 1(tag 之后)。无表示位(未覆盖面/非可选)→ 既有 + // 行为**逐字不变**(原样返回内层表达式值)。 + inner := gen_expr(ast_a(node)); + if g_optrep_on != 0 && inner >= 0 { + rv := irv_rep(inner); + if rv >= 0 { + dest := new_ir_var("tryv", irv_type(inner)); + bl := new_label(); + xl := new_label(); + xend := new_label(); + zb := new_ir_var("_rz", TI_INT); + emit(IR_CONST, zb, 0, 0, 0, TI_INT); + ib := new_ir_var("rep_isbare", TI_INT); + emit(IR_BINARY, ib, rv, zb, OP_EQ, 0); + emit(IR_BRANCH, -1, ib, bl, xl, 0); + emit(IR_LABEL, -1, bl, 0, 0, 0); + emit(IR_STORE, -1, dest, inner, 0, 0); + emit(IR_JUMP, -1, xend, 0, 0, 0); + emit(IR_LABEL, -1, xl, 0, 0, 0); + emit(IR_LOAD_FIELD, dest, inner, 0, 1, 0); + emit(IR_LABEL, -1, xend, 0, 0, 0); + return dest; + } + } + return inner; } if ast_kind(node) == EXPR_STRUCTPAT { return -1; @@ -2206,6 +2681,8 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); } if ast_kind(node) == EXPR_TUPLE { // Tuple: allocate array for N elements, store each + // F5 契约:a=首 wrapper(g_ast 中连续)、b=元素个数;wrapper.a = 元素值节点 + // (复合元素值节点不连续,须经 wrapper 解引用——见 checker.cr EXPR_TUPLE 注)。 elem_idx := ast_a(node); ec : ., mut = ast_b(node); tv := new_ir_var("tuple", TI_INT); @@ -2214,8 +2691,13 @@ emit(IR_STORE, -1, lv, val_var, 0, 0); e : ., mut = 0; loop { if e >= ec { break; } - elem_var := gen_expr(elem_idx + e); + en : ., mut = -1; + if elem_idx >= 0 { en = ast_a(elem_idx + e); } + elem_var := gen_expr(en); elem_var = force_if_thunk(elem_var); + // 容量批 T2:可选元素写点规范化(元组元素槽无声明面 ⇒ 按元素表达式可选性 + // 保守判定;ident/调用覆盖面,其余形态登记) + if g_optrep_on != 0 { elem_var = box_for_slot_flag(en, elem_var, elem_node_optional(en)); } emit(IR_STORE_FIELD, -1, tv, elem_var, e, 0); e = e + 1; } @@ -2264,6 +2746,19 @@ fn ir_gen_func(fi: int) { pvar := new_ir_var(pname, param_type); // Bind param name bind_local(pname_idx, pvar); + // 可选表示(R2 P4 Task 5):形参表示位——**序言最早处**从实参信道读(先于任何 + // 可能改写信道的调用;调用点在 IR_CALL 前写)。形参类型节点 = EXPR_PARAM.data。 + ptn := ast_data(pn); + // 容量批 T2:形参声明类型登记(元素写点判定用;与表示位面互不干扰) + if g_optrep_on != 0 && ptn >= 0 { irv_set_decl_ti(pvar, res_type_node(ptn)); } + if g_optrep_on != 0 && g_optrep_arg_count > 0 && ptn >= 0 && pi < g_optrep_arg_count { + pti := res_type_node(ptn); + if pti >= 0 && get_type_kind(pti) == TYP_OPTIONAL { + prv := new_ir_var(pname + "_prep", TI_INT); + emit(IR_LOAD, prv, g_optrep_arg_cell0 + pi, 0, 0, TI_INT); + irv_set_rep(pvar, prv); + } + } pi = pi + 1; // Scan past type nodes to next EXPR_PARAM pn = pn + 1; @@ -2283,19 +2778,39 @@ fn ir_gen_func(fi: int) { emit(IR_ARENA_NEW, arena_var, 0, 0, 0, 0); arena_instr := g_ir_instr_count - 1; + // 程序启动期全局初始化(2026-09-10 R1 ②):只在 main 序言注入——run 与 build + // 两路径都从 main 进入(interp.cr 按名查找 main;ELF _start call main),因此 + // 无需新 IR 函数/新 .ccr 段/新 _start 指令 = 无序列化与字节布局扰动。 + // 无需运行期初始化的程序不发射任何指令(零变化)。 + if str_eq(istr_get(name_idx), "main") != 0 { inject_global_inits(); } + // Generate body(记录返回 TI——EXPR_RETURN 的 dex 边界转换用) g_cur_ret_ti = ret_ti; + // 可选表示(R2 P4 Task 5):本函数返回可选时,返回点写返回信道(见 EXPR_RETURN)。 + // 判据走**返回类型节点**(ast_type_val(fn_node) = 返回类型节点下标——见上方 ret_ti + // 注释)而非 fi_return_type 裸码(`T?` 的类型节点 type_val = 0 ⇒ 恒判否)。 + g_cur_ret_opt = 0; + if g_optrep_on != 0 && g_optrep_ret_cell >= 0 { + rtn := ast_type_val(fn_node); + if rtn >= 0 { + rti := res_type_node(rtn); + if rti >= 0 && rti < g_type_count && get_type_kind(rti) == TYP_OPTIONAL { g_cur_ret_opt = 1; } + } + } if body >= 0 { gen_expr(body); } g_cur_ret_ti = -1; + g_cur_ret_opt = 0; // Patch arena size and reset before return total := r64(g_sg_alloc_total, (g_sg_count - 1) * 8); if total > 0 { iri_set_s1(arena_instr, total); } emit(IR_ARENA_RESET, -1, arena_var, 0, 0, 0); - // Add return at end if not already terminated + // Add return at end if not already terminated(可达 = 兜底 return;不可达 = 死码, + // 两后端同语义不执行)。可选返回点写装箱默认值(与 ABI 返回寄存器的未定义值同档)。 + if g_cur_ret_opt != 0 { emit_rep_set(g_optrep_ret_cell, oe_boxed()); } emit(IR_RETURN, -1, -1, 0, 0, 0); w64(g_ir_func_instr_count, func_idx * 8, g_ir_instr_count - r64(g_ir_func_instr_start, func_idx * 8)); @@ -2343,6 +2858,111 @@ fn global_init_val(name_idx: int) -> int { return 0; } +// 类型节点 → `[T; N]` 的元素数 N(`[T]` 切片 = 0);非聚合 = -1。 +// 类型别名解析一层:`type Arr = [int;2]; g : Arr;` 的声明类型节点是 EXPR_IDENT +// (parser.cr parse_type 的用户类型分支),须经 g_type_aliases({name_idx, type_node} +// 16B/条)回到底层 EXPR_ARRAY——否则同一「定长数组全局」类仍留 SIGSEGV。 +fn agg_elem_count_of(tn: int) -> int { + if tn < 0 { return -1; } + if ast_kind(tn) == EXPR_ARRAY { return ast_int_val(tn); } + if ast_kind(tn) == EXPR_IDENT { + ni := ast_int_val(tn); + ai : ., mut = 0; + loop { + if ai >= g_type_alias_count { break; } + if r64(g_type_aliases, ai * 16) == ni { + rt := r64(g_type_aliases, ai * 16 + 8); + if rt >= 0 && ast_kind(rt) == EXPR_ARRAY { return ast_int_val(rt); } + break; + } + ai = ai + 1; + } + } + return -1; +} + +// 文件级 let 是否需要「运行期初始化」(2026-09-10 语言面收窄 §1.3 / R1 ②)。 +// 判据(与 global_init_val 互补——它只认编译期标量常量,其余静默归 0): +// ① 声明类型是聚合(`[T; N]`/`[T]`,含类型别名一层解析)→ 必须运行期分配存储; +// ② 初值存在且不是 int/bool/dex 字面量(含负号字面量)→ 必须运行期求值。 +fn global_needs_runtime_init(name_idx: int) -> int { + i : ., mut = g_global_let_count - 1; + loop { + if i < 0 { break; } + node := r64(g_global_lets, i * 8); + if ast_a(node) == name_idx { + // 容量批 T2:可选全局槽**必须**走运行期初始化——静态度量(.quad 裸值)写进槽后 + // 读侧按装箱解引用 ⇒ 双路径 139(实测 e2_global_bare)。装箱在 inject_global_inits + // 内完成;非可选全局不受影响(零足迹)。 + if g_optrep_on != 0 && ti_is_optional(global_decl_ti(name_idx)) != 0 { return 1; } + if agg_elem_count_of(ast_b(node)) >= 0 { return 1; } + vn := ast_c(node); + if vn < 0 { return 0; } + vk := ast_kind(vn); + if vk == EXPR_INT || vk == EXPR_BOOL || vk == EXPR_DEX { return 0; } + if vk == EXPR_UNARY && ast_c(vn) == UOP_NEG { + inner := ast_a(vn); + ik := ast_kind(inner); + if ik == EXPR_INT || ik == EXPR_BOOL || ik == EXPR_DEX { return 0; } + } + return 1; + } + i = i - 1; + } + return 0; +} + +// 按 name_idx 查已注册的全局 IR var(未注册 = -1)。 +fn global_var_of(name_idx: int) -> int { + gi : ., mut = 0; + loop { + if gi >= g_ir_global_count { break; } + if r64(g_ir_globals, gi * 24) == name_idx { return r64(g_ir_globals, gi * 24 + 8); } + gi = gi + 1; + } + return -1; +} + +// 把需要运行期初始化的文件级 let 降级为 IR 序列(写进当前函数 = main 序言)。 +// 顺序 = g_global_lets 源序(跨全局依赖如 `b : [int;2] = a;` 因此正确)。 +fn inject_global_inits() { + i : ., mut = 0; + loop { + if i >= g_global_let_count { break; } + lnode := r64(g_global_lets, i * 8); + name_idx := ast_a(lnode); + if global_needs_runtime_init(name_idx) != 0 { + gv := global_var_of(name_idx); + if gv >= 0 { + vn := ast_c(lnode); + v : ., mut = -1; + if vn >= 0 { + v = gen_expr(vn); + v = force_if_thunk(v); + } else { + // 无初值聚合:整流分配(零初始化由 alloc 语义保证——rt.s 的 rep stosb; + // 解释器 IR_ALLOC_ARRAY 显式清零)。仅 `[T; N]`(N ≥ 1)注入;`[T]` + // (切片无长度,N = 0)不注入——保持 BSS 零 = 空切片/哑指针, + // 空切片解引用属独立 null 陷阱类,不在本批。 + cnt := agg_elem_count_of(ast_b(lnode)); + if cnt > 0 { + v = new_ir_var("ginit", TI_UNIT); + emit(IR_ALLOC_ARRAY, v, cnt, 0, 0, 0); + irv_set_type(v, alloc_type(TYP_ARRAY, TI_INT, cnt)); + } + } + if v >= 0 { + // 容量批 T2:可选全局槽**初值**写点规范化(与赋值点 :1155 是两处独立 + // 代码点——初值走 inject_global_inits,赋值走 EXPR_ASSIGN) + if g_optrep_on != 0 { v = box_for_optional_slot(vn, v, global_decl_ti(name_idx)); } + emit(IR_STORE, -1, gv, v, 0, 0); + } + } + } + i = i + 1; + } +} + // Register one IR global, deduplicated by name_idx. fn reg_one_global(name_idx: int) { found : ., mut = 0; @@ -2430,6 +3050,80 @@ fn ir_gen_globals() { reg_one_global(str_intern("g_arena_cap")); reg_one_global(str_intern("g_arena_pool_data")); reg_one_global(str_intern("g_arena_free_list")); + reg_optrep_globals(); +} + +// 表示面初始化(**两条 IR 生成入口都必须先调**:ir_gen_all 批量路径 + main.cr 文件路径 +// (逐函数 + .cir 缓存)——后者不经过本文件的批量入口,漏调即侧表未分配)。位置: +// **先于 ir_gen_globals**(隐藏信道的 var 行序 = 全局行序依赖)。 +fn optrep_begin() { + g_ir_var_rep = ""; + g_ir_var_rep_cap = 0; + g_ir_var_decl_ti = ""; + g_ir_var_decl_cap = 0; + g_optrep_ret_cell = -1; + g_optrep_arg_cell0 = -1; + g_optrep_arg_count = 0; + g_cur_ret_opt = 0; + optrep_prescan(); +} + +// 表示面启用扫描(**零足迹门**):AST 含任一 `T?` 类型节点(EXPR_OPTIONAL)或任一 +// `Some`/`None` 构造(关键字名分派,与 checker 同规约:用户变体同名时上方 SYM_FN 优先, +// 但扫描**宽于**实际使用只会多启用一层空机器、不会漏启用——判据面为「启用 ⇒ 表示面完整」)。 +fn optrep_prescan() { + g_optrep_on = 0; + i : ., mut = 0; + loop { + if i >= g_ast_count { break; } + k := ast_kind(i); + if k == EXPR_OPTIONAL { g_optrep_on = 1; break; } + if k == EXPR_ENUM_CONSTRUCTOR { + nm := istr_get(ast_a(i)); + if str_eq(nm, "Some") != 0 || str_eq(nm, "None") != 0 { g_optrep_on = 1; break; } + } + i = i + 1; + } +} + +// 隐藏全局注册(照 reg_one_global 形态但不查 g_global_lets——编译器内部名,零初值)。 +// var_idx == 行序(save_ccr 的全局行序守卫)由「本函数只在 ir_gen_globals 尾部调用、 +// 此后不再注册全局」保证。 +fn reg_hidden_global(name_ni: int) -> int { + gvar := new_ir_var(istr_get(name_ni), TI_INT); + grow_ir_globals(g_ir_global_count + 1); + w64(g_ir_globals, g_ir_global_count * 24, name_ni); + w64(g_ir_globals, g_ir_global_count * 24 + 8, gvar); + w64(g_ir_globals, g_ir_global_count * 24 + 16, 0); + g_ir_global_count = g_ir_global_count + 1; + return gvar; +} + +// 可选表示的跨函数信道注册(仅在 g_optrep_on 时):返回信道单元 + 每形参位一个实参 +// 信道单元(连续 var 索引;被调按形参位读、调用点按实参位写)。 +fn reg_optrep_globals() { + g_optrep_ret_cell = -1; + g_optrep_arg_cell0 = -1; + g_optrep_arg_count = 0; + if g_optrep_on == 0 { return; } + maxp : ., mut = 0; + fi2 : ., mut = 0; + loop { + if fi2 >= g_func_count { break; } + pc := fi_param_count(fi2); + if pc > maxp { maxp = pc; } + fi2 = fi2 + 1; + } + if maxp > 64 { maxp = 64; } + g_optrep_ret_cell = reg_hidden_global(str_intern("__optrep_ret")); + j : ., mut = 0; + loop { + if j >= maxp { break; } + cv := reg_hidden_global(str_intern("__optrep_arg" + int_str(j))); + if j == 0 { g_optrep_arg_cell0 = cv; } + j = j + 1; + } + g_optrep_arg_count = maxp; } // --- AST walk: patch method call names for monomorphization --- @@ -2499,13 +3193,50 @@ fn ast_patch_node(node: int, subst_from: string, subst_to: string) { } else if k == EXPR_STMT { if ast_a(node) >= 0 { ast_patch_node(ast_a(node), subst_from, subst_to); } } else if k == EXPR_STRUCT { + // a=type_name_ni, b=first wrapper(连续), c=field_count;wrapper.a=字段值节点 + // (F5 契约,见 parser.cr struct 字面量分支)——须解引用 wrapper 再递归, + // 旧代码把 EXPR_NONE wrapper 本身交给 ast_patch_node(无该 kind 分支)= 空转, + // 泛型实例体内 struct 字面量字段中的方法调用名得不到替换。 an5 := ast_b(node); ac5 := ast_c(node); ai5 : ., mut = 0; - loop { if ai5 >= ac5 { break; } if an5 >= 0 { ast_patch_node(an5, subst_from, subst_to); an5 = an5 + 1; } ai5 = ai5 + 1; } - } else if k == EXPR_ARRAY || k == EXPR_TUPLE { - an6 := ast_b(node); ac6 := ast_c(node); + loop { + if ai5 >= ac5 { break; } + if an5 >= 0 { + vn5 : ., mut = -1; + if ast_kind(an5) == EXPR_NONE { vn5 = ast_a(an5); } + if vn5 >= 0 { ast_patch_node(vn5, subst_from, subst_to); } + an5 = an5 + 1; + } + ai5 = ai5 + 1; + } + } else if k == EXPR_ARRAY { + // a=first wrapper(连续), b=elem_count;wrapper.a=元素值节点(F5 契约,见 parser.cr 下标分支) + an6 := ast_a(node); ac6 := ast_b(node); + ai6 : ., mut = 0; + loop { + if ai6 >= ac6 { break; } + if an6 >= 0 { + vn6 : ., mut = -1; + if ast_kind(an6) == EXPR_NONE { vn6 = ast_a(an6); } + if vn6 >= 0 { ast_patch_node(vn6, subst_from, subst_to); } + an6 = an6 + 1; + } + ai6 = ai6 + 1; + } + } else if k == EXPR_TUPLE { + // a=first wrapper(连续), b=elem_count;wrapper.a=元素值节点(F5 契约) + an6 := ast_a(node); ac6 := ast_b(node); ai6 : ., mut = 0; - loop { if ai6 >= ac6 { break; } if an6 >= 0 { ast_patch_node(an6, subst_from, subst_to); an6 = an6 + 1; } ai6 = ai6 + 1; } + loop { + if ai6 >= ac6 { break; } + if an6 >= 0 { + vn6 : ., mut = -1; + if ast_kind(an6) == EXPR_NONE { vn6 = ast_a(an6); } + if vn6 >= 0 { ast_patch_node(vn6, subst_from, subst_to); } + an6 = an6 + 1; + } + ai6 = ai6 + 1; + } } else if k == EXPR_FIELD || k == EXPR_INDEX || k == EXPR_UNARY || k == EXPR_RETURN || k == EXPR_TRY || k == EXPR_MOVE { if ast_a(node) >= 0 { ast_patch_node(ast_a(node), subst_from, subst_to); } } @@ -2595,6 +3326,8 @@ fn ir_gen_all() { g_next_label = 1; g_ir_loop_depth = 0; g_ir_str_const_count = 0; + // 可选表示面:侧表复位 + 启用扫描(先于全局注册——隐藏信道的 var 行序依赖) + optrep_begin(); // Initialize dataflow graph init_df(); @@ -2612,6 +3345,10 @@ fn ir_gen_all() { df_end_func(ir_func_idx); i = i + 1; } + + // 纯度 + state 链最终化(全部 IR 体就绪后;理由见 checker.cr compute_all_purity + // 与 dataflow.cr df_replay_state_chain 头注)。文件路径的同点调用在 main.cr。 + df_state_finalize(); } // Compute function body fingerprint: hash of the function body source text. diff --git a/src/compiler/lexer.cr b/src/compiler/lexer.cr index 29bbf243..6f166204 100644 --- a/src/compiler/lexer.cr +++ b/src/compiler/lexer.cr @@ -197,7 +197,11 @@ fn str_to_f64_bits(s: string) -> int { } // Parse an integer literal after tokenization. Supports decimal separators -// and 0x/0o/0b prefixes; overflow is reported as a lexer error. +// and 0x/0o/0b prefixes; out-of-shape literals are rejected as lexer errors. +// 层语义(int-unbounded-semantics 定稿):语言 int = 无上限数学整数——本守卫报的 +// 「integer literal overflow」是编码层限制错误(编译器内部表示 = 经典 64 位机器字 +// 投影,超形状 = 该投影无载体),不是语义溢出事件——语义层无溢出概念。表示升级 +// (扩展/多字)为 hw-map/远期事项,见 specs/2026-09-06-int-unbounded-semantics.md。 fn str_int_literal(s: string) -> int { sl := str_len(s); base : ., mut = 10; @@ -421,9 +425,11 @@ fn tokenize(_src: string) { // Hex/octal/binary prefix if c == 48 && _pos - start == 1 { nx := cur_char_at(_src, _pos, _slen); - if nx == 120 || nx == 88 { _pos = _pos + 1; loop { hc := cur_char_at(_src, _pos, _slen); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) || hc == 95 { _pos = _pos + 1; } else { break; } } } - else if nx == 111 || nx == 79 { _pos = _pos + 1; loop { oc := cur_char_at(_src, _pos, _slen); if (oc >= 48 && oc <= 55) || oc == 95 { _pos = _pos + 1; } else { break; } } } - else if nx == 98 || nx == 66 { _pos = _pos + 1; loop { bc := cur_char_at(_src, _pos, _slen); if bc == 48 || bc == 49 || bc == 95 { _pos = _pos + 1; } else { break; } } } + // 三条进制前缀循环均**不再接受** '_'(2026-09-10 语言面收窄 §2): + // 遗留 '_' 由下方后缀段落统一响亮报错(不静默消费)。 + if nx == 120 || nx == 88 { _pos = _pos + 1; loop { hc := cur_char_at(_src, _pos, _slen); if is_digit(hc) != 0 || (hc >= 65 && hc <= 70) || (hc >= 97 && hc <= 102) { _pos = _pos + 1; } else { break; } } } + else if nx == 111 || nx == 79 { _pos = _pos + 1; loop { oc := cur_char_at(_src, _pos, _slen); if (oc >= 48 && oc <= 55) { _pos = _pos + 1; } else { break; } } } + else if nx == 98 || nx == 66 { _pos = _pos + 1; loop { bc := cur_char_at(_src, _pos, _slen); if bc == 48 || bc == 49 { _pos = _pos + 1; } else { break; } } } bad_prefix_digit : ., mut = 0; badc := cur_char_at(_src, _pos, _slen); if is_ident_char(badc) != 0 { bad_prefix_digit = 1; } @@ -438,30 +444,25 @@ fn tokenize(_src: string) { has_dot = 1; loop { if is_digit(cur_char_at(_src, _pos, _slen)) != 0 { _pos = _pos + 1; } else { break; } } } - // Suffix - suffix : ., mut = ""; + // 数字词法收窄(2026-09-10 用户裁决,语言面收窄 §2):`_` 分隔符与宽度后缀 + // (f32/f64/i8..u64)**均不支持**——一律响亮报错且**不消费**残余字符 + // (按标识符继续 tokenize,使后续解析给出第二重信号,不静默、不猜)。 + // 修复前:`_` 被 is_alpha(95) 当后缀静默消费 → T_INT 取「无值」= 0 + // (`x := 1_000` 静默成 0 = 实质误编译);宽度后缀同样静默丢弃。 sx := cur_char_at(_src, _pos, _slen); - if is_alpha(sx) != 0 { - ss := _pos; - loop { - if is_alpha(cur_char_at(_src, _pos, _slen)) != 0 { _pos = _pos + 1; } else { break; } - } - suffix = str_sub(_src, ss, _pos - ss); + if sx == 95 { + add_error("invalid character '_' in numeric literal (digit separators not supported)"); + } else if is_alpha(sx) != 0 { + add_error("invalid suffix on numeric literal (width suffixes retired 2026-09-10)"); } - num_str := str_sub(_src, start, _pos - start - str_len(suffix)); - // dex 字面量(含小数点或 f32/f64 后缀):int_val = 定点缩放整数(精确解析, - // 数值迁移 Task 4——十进制 → 缩放整数,非二进制近似);lexeme 槽 = 原数字串 + num_str := str_sub(_src, start, _pos - start); + // dex 字面量(含小数点):int_val = 定点缩放整数(精确解析,数值迁移 + // Task 4——十进制 → 缩放整数,非二进制近似);lexeme 槽 = 原数字串 // (parser 在 apx 场景需要 binary64 位模式:str_to_f64_bits(num_str))。 - // 修复前 float 走 str_int(3.14 解析成 3,小数静默丢弃) - if has_dot != 0 || suffix == "f32" || suffix == "f64" { + if has_dot != 0 { add_tok_int_lex(T_DEX, str_to_scaled(num_str), str_intern(num_str), start_line, start_col); } else { - ival : ., mut = str_int_literal(num_str); - if suffix == "u8" || suffix == "u16" || suffix == "u32" || suffix == "u64" { } - else if suffix == "i8" || suffix == "i16" || suffix == "i32" || suffix == "i64" { } - else if str_len(suffix) > 0 { } - if str_len(suffix) > 0 { add_tok(T_INT, -1, start_line, start_col); } - else { add_tok_int(T_INT, ival, start_line, start_col); } + add_tok_int(T_INT, str_int_literal(num_str), start_line, start_col); } _pos = skip_ws(_src, _pos, _slen); continue; diff --git a/src/compiler/main.cr b/src/compiler/main.cr index f35015aa..8156ab40 100644 --- a/src/compiler/main.cr +++ b/src/compiler/main.cr @@ -135,21 +135,49 @@ fn run_frontend() -> int { if g_error_count > 0 { print_parse_errors(); return 1; } println("[4/5] type check..."); check_all(); - // Type-check diagnostics are non-fatal (match Python bootstrap behavior). - // Only parse errors and resolver errors are fatal. - // 例外(F2):编译期确定的常量索引越界(R002)与字面量切片界越界(TK05/TK06) - // 是硬错误——拦截编译(修复前静默生成越界二进制)。 + // Type-check diagnostics:**fail-closed(FC 批 T2;维护者裁)= 默认阻断 + 豁免登记表**。 + // 判据(成文化)= 「判定继续 ⇒ 产出静默错产物」——五组先例:R002 常量档越界(F2)· + // TS01-04+TK02 聚合字面量三校验(TODO #29)· TM03 match 非穷尽(#31,修复前缺臂 ⇒ 未匹配值 + // 静默得 0)· ICE04 判定不可判(P-A 三态纪律)· TA02 声明位点不兼容(#32,修复前符号取注解行、 + // 值按注解行发射 ⇒ 静默错值)。**旧正向名单(11 码)整体退役**:新语义下它们本就在闸内。 + // 豁免表 = `diag.cr::diag_gate_exempt`(码级 + 位点证据/理由/退出条件三字段;只减不增)。 + // `scope`(裁-FC-1 = (C)):本处**只以 GATE_SCOPE_BUILD 调用**;`check` 分支的 rc 规则 + // (下方「计数 > 0」)不消费本表 ⇒ **check 面基线零变化**(72 档 34×0/38×1 不换代)。 if g_diag_count > 0 { hard : ., mut = 0; di : ., mut = 0; + n_block : ., mut = 0; // --diag-gate-report 用(默认关时恒 0,零行为影响) + rep : ., mut = ""; loop { if di >= g_diag_count { break; } ec := r64(g_diags, di * DIAG_REC_SIZE); - if ec == EC_R_OOB || ec == EC_TK_SLICE_BOUNDS || ec == EC_TK_SLICE_LEN { hard = 1; } + // 默认阻断:**不在豁免表内 ⇒ hard = 1**(表 = diag.cr;只减不增)。 + if diag_gate_exempt(ec, GATE_SCOPE_BUILD) == 0 { + hard = 1; + n_block = n_block + 1; + if str_len(rep) > 0 { rep = rep + ","; } + rep = rep + error_cat_prefix(ec / 1000) + pad_diag_num(ec % 1000); + } di = di + 1; } + // 隐藏通道(--diag-gate-report,默认关;同 --verify-* 家族纪律):**只读报告**—— + // 逐条列出会被闸门阻断的码 + 计数。开/关两态 **rc 与产物逐字节同**(本行仅追加输出)。 + if cli_has("diag-gate-report") != 0 { + print("[diag-gate] face=build blocked="); + print(int_str(n_block)); + print(" total="); + print(int_str(g_diag_count)); + print(" codes="); + println(rep); + } print_diagnostics(); - if hard != 0 { return 1; } + if hard != 0 { + // 进度行在此(诊断之后、返回之前)打印,与原位置同序:S0 期本行只覆盖 hard==0 路径, + // 本批使**被阻断路径也打印**(否则 25 档语料的日志会缺行)。唯一可见差异 = 「诊断码 + // 全在旧硬名单」的档(S0 提前返回无本行;本批打印)——72 档语料内 0 档、探针 1 档。 + println("[5/5] frontend done"); + return 1; + } } // AST-level constant folding and optimization (O1+) /* @@ -164,6 +192,18 @@ if g_opt_level >= 1 && g_func_count > 0 { */ println("[5/5] frontend done"); + // R2 P2a Task 3(C-4):侧表 ↔ res_type_node 管线内断言(隐藏调试通道,默认关)。 + // 必须跑在 check_all 之后(类型表/侧表已成型);不一致 → rc≠0(判据可挂)。默认关 = + // 一次全局读 + 返回(产物/输出零影响)。 + if cli_has("verify-named-dedup") != 0 { + if named_dedup_verify() != 0 { return 1; } + } + // R2 P3 Task 4:枚举载荷**类型节点**列入库断言(隐藏调试通道,默认关;与上一条同址同式)。 + // 断言 = 每个载荷槽都有类型节点(parser 同写;缺口复发即 rc≠0);`collapses` 计数 = 裸码 + // 列丢失真实载荷类型的槽数(信息量,非失败)。见 ty_shadow.cr 的 sh_evp_verify。 + if cli_has("verify-evp-nodes") != 0 { + if sh_evp_verify() != 0 { return 1; } + } return 0; } @@ -185,6 +225,10 @@ fn default_out_path(src_path: string, ext: string) -> string { return out; } +// **R2 P5 Task 5 删除**:`sh_finish`(R2 P1 影子对拍收尾 = 摘要行 + `--type-shadow-dump` 时的 +// 差异条目转储)与其两处调用点(run/build 路径各一)随影子通道整体下线;`--type-shadow` / +// `--type-shadow-dump` 两个 CLI 通道同删(D27)。运行期不再有任何影子收尾钩子。 + fn corec_main() -> int { cli_init("corec", "Core compiler frontend"); cli_cmd("build", "Compile .cr or directory to ELF binary"); @@ -193,9 +237,20 @@ fn corec_main() -> int { cli_cmd("ccr", "Output linear CFG (.ccr)"); cli_cmd("run", "Execute code directly (interpreter mode)"); cli_cmd("clean-cache", "Delete incremental compilation cache"); + cli_cmd("selftest-types", "Run type-engine self tests (R2 P0)"); + cli_cmd("selftest-purity", "Run effect-purity self tests (Task 1)"); cli_flag("output", "o", "Output path"); cli_flag_bool("static", "", "Static linking (embed runtime)"); - cli_flag("opt-level", "O", "Optimization level (0,1,2,3; default=1)"); + cli_flag("opt-level", "O", "Optimization level (0,1,2,3; default=1) — O1 CSE(corec 进程内);O2 寄存器分配+判定在 corearch(corec build 透传 --opt-level,.ccr 不承载分配结果)"); + cli_flag_bool("inject-var-shift", "", "Hidden debug: shift func0 var decl block left by 1, then save (GC-4 test hook)"); + cli_flag_bool("dump-types", "", "Hidden debug: dump TYPE segment content (row table + term DAG + cross-process judgment probe) after populate (R2 P4 Task 2 test channel)"); + cli_flag_bool("dump-ifaces", "", "Hidden debug: dump IFACE segment content (entry table + shapes + user ifaces + impls + method table + cross-segment term probe) after populate (R2 P4 Task 3 test channel)"); + cli_flag_bool("dump-tk-terms", "", "Hidden debug: dump per-DFNode tk + type-term slot (cir; R2 P4 Task 4 test channel — cold/warm snapshot symmetry)"); + // **R2 P5 Task 5 删除**:`--type-shadow` / `--type-shadow-dump`(R2 P1 影子对拍的两个隐藏 + // 通道)随影子层整体下线(D27/TODO #24 同族清偿);判定路径不再有开关(无条件经引擎)。 + cli_flag_bool("verify-named-dedup", "", "R2 P2a: assert side-table == res_type_node for all named types (debug)"); + cli_flag_bool("verify-evp-nodes", "", "R2 P3 T4: assert enum variant payload type nodes recorded (debug)"); + cli_flag_bool("diag-gate-report", "", "FC T2: report fail-closed gate verdicts per compile (debug; default off — 不改 rc/产物)"); if cli_parse() != 0 { return 1; } // Parse -O flag (default O1) @@ -344,7 +399,8 @@ fn corec_main() -> int { } } - if run_frontend() != 0 { return 1; } + run_rc := run_frontend(); + if run_rc != 0 { return 1; } ir_gen_all(); return ir_interpret(); } @@ -357,6 +413,12 @@ fn corec_main() -> int { return 0; } + // === selftest-types: 类型项引擎自测(R2 P0)——不读源文件、不产生产物 === + if cli_eq(cmd, "selftest-types") { return type_selftest_run(); } + + // === selftest-purity: 效应纯度自测(Task 1)——自建内联源 + 完整前端/IR === + if cli_eq(cmd, "selftest-purity") { return purity_selftest_run(); } + // === File-based subcommands: build | check | cir | ccr === if cli_arg_count() < 1 { print("error: "); @@ -378,7 +440,8 @@ fn corec_main() -> int { g_source = rt_src + "\n" + g_source; } - if run_frontend() != 0 { return 1; } + fe_rc := run_frontend(); + if fe_rc != 0 { return 1; } // === check: type-check only === if cli_eq(cmd, "check") { @@ -404,6 +467,9 @@ fn corec_main() -> int { g_ir_source_hash = 0; g_ir_source_hash_ready = 0; init_df(); + // 可选表示面(R2 P4 Task 5):本路径不经过 ir_gen_all ⇒ 必须自行初始化(侧表复位 + // + 启用扫描),且**先于 ir_gen_globals**(隐藏信道的 var 行序依赖)。 + optrep_begin(); ir_gen_globals(); // Incremental cache: ensure cache directory exists @@ -418,6 +484,13 @@ fn corec_main() -> int { if fi_generic_count(cache_scan) > 0 { cache_enabled = 0; break; } cache_scan = cache_scan + 1; } + // 可选表示面启用 ⇒ **关 .cir 快照缓存**(R2 P4 Task 5):表示位侧表是**编译期 + // 进程内状态**(var → 表示位 var 映射),快照只存指令/节点不存侧表 ⇒ 命中恢复的 + // 函数解包点会回落既有装箱路径,与冷路径**产物分歧**(冷/热分歧类,同 Task 4 的 + // bad_term 教训)。关缓存 = 每次全量重建 = 正确性优先;非可选程序照常缓存。 + if g_optrep_on != 0 { + cache_enabled = 0; + } // Generate IR for each function, checking cache first fi : ., mut = 0; @@ -476,18 +549,42 @@ fn corec_main() -> int { df_end_func(ir_func_idx); } else { // Cache miss: do full frontend IR gen + // TODO #60(裁-W1 = (b) 先行,**见证式**一般化):缓存命中会**跳过本函数的生成期 + // 副作用**——凡该副作用落在「快照**不携带**的共享空间」上,命中后该空间与冷路径 + // **分叉**,读它的判定即静默失效(首例 = TU03:ir_gen 期 alloc_type 出的 TYP_PTR + // extra=1 行不随快照 ⇒ 暖态 get_type_extra 判定落空;见 /tmp/fct4/task1-report.md E1)。 + // 判据 = 「**生成期对快照不载的共享面有副作用 ⇒ 本条目不可写**」(非某诊断码专用): + // 下次运行必 miss ⇒ 重放全部副作用 ⇒ 两态一致(宁可 miss 不可静默)。 + // 见证面清单(**扩展点**:新增「快照不载 + 生成期写 + 生成后被读」的共享面 ⇒ 加一行): + // ① `g_type_count`(类型行表;E1 实锤);② 已核**非**共享面者不列:`g_ir_locals` + // (仅 ir_gen 生成期自用,无生成后消费者)· 状态链/项表(v15/v18 已各自收口)· + // 可选表示侧表(optrep 程序已整体关缓存,main.cr 的 g_optrep_on 门)。 + // ③ **未判**(T1 E5:未构造/未测)—— 内层 SG/`g_df_node_region`(装载有 Minor #4 + // 近似)· 外链重定位(探针未触发)⇒ 若日后证实,按同一形态加见证。 + tc0 := g_type_count; ir_gen_func(fi); df_end_func(ir_func_idx); - // Save cache for future compilations - if cache_enabled != 0 { save_cir_cache(cache_path, fi, ir_func_idx); } + // Save cache only when no side effect on snapshot-uncarried shared faces. + if cache_enabled != 0 { + if g_type_count == tc0 { save_cir_cache(cache_path, fi, ir_func_idx); } + } } fi = fi + 1; } + // === 纯度 + state 链最终化(IR 生成结束、任何链消费者之前)=== + // 真纯度只能由全程序 IR 体算(checker.cr compute_all_purity 头注),链的连接 + // 依赖真纯度 ⇒ 两者同点时后移(dataflow.cr df_replay_state_chain 头注)。 + df_state_finalize(); + // === cir: output dataflow graph === if cli_eq(cmd, "cir") { + // regalloc 判定/条目调试通道(--dump-entries/--dump-coexist/--check-regalloc/ + // --inject-*)已随编码决策层迁 corearch(2026-09-07 regalloc 移后端)—— + // corearch load .ccr 后自算自检(src/arch/x86_64/regalloc.cr),载体 = + // corearch 同名隐藏 flag。 dot := df_graph_to_dot(); out := cli_get("output"); if str_len(out) == 0 { @@ -504,6 +601,10 @@ fn corec_main() -> int { print(cir_text_dump()); print(" -> "); println(out); + // R2 P4 Task 4 测试通道(hidden):逐节点 tk/项槽 dump——冷路径(emit 填槽) + // 与暖路径(.cir 快照读回)用同一通道对拍(test_ccr_types.py 的 T4 节)。 + // 时点 = 全图构建完(含命中恢复);只读,不产产物。 + if cli_has("dump-tk-terms") != 0 { print(df_tk_term_dump()); } return 0; } @@ -520,13 +621,11 @@ fn corec_main() -> int { } // === build | ccr need lower_to_ccr === + // regalloc 移后端(2026-09-07):O2 分配 + 一致性判定已归位 corearch—— + // corearch load .ccr 后自算自检(违反 = 编译错误);corec 只留语义层 CSE。 if g_opt_level >= 1 { pass_cse(); } - if g_opt_level >= 2 { - alloc_registers(); - pass_stack_share(); - } println("lower to ccr..."); lower_to_ccr(); print("lower done: "); @@ -541,6 +640,29 @@ fn corec_main() -> int { if str_len(out) == 0 { out = default_out_path(src_path, ".ccr"); } + // Hidden debug (--inject-var-shift): GC-4 test hook — shift func0's var decl + // block start left by 1 (count-level guards can't see it; the position-level + // guard in save_ccr must reject). Real build paths never inject. + if cli_has("inject-var-shift") != 0 { + if inject_var_shift() != 0 { + print("error: inject-var-shift: precondition failed (func0 block start < 1)"); + println(""); + return 1; + } + } + // R2 P4 Task 2/3(D13/D14):TYPE/IFACE 段内容构造 = 确定性装填(项层/引擎/ + // 桥接复位 → 形状重注册 → 行序装填 → 接口签名项装填)+ 两段体缓冲;save_ccr + // 只按段表搬运缓冲(D18)。装填失败(不可译行/签名项)= 拒绝落盘(rc=1 + + // 诊断——不得产出缺项的类型/接口段)。 + if ccr_seg_prepare_save() != 0 { + println("error: could not build TYPE/IFACE segments"); + return 1; + } + // 测试通道(hidden):载入前 dump 段内容面(行表/项 DAG/probe;接口表/形状/ + // 签名项/跨段引用域探针)——与 corearch --dump-types/--dump-ifaces 同一条打印 + // 路径(跨进程对拍),见 test_ccr_types.py。 + if cli_has("dump-types") != 0 { ccr_type_selftest_dump(); } + if cli_has("dump-ifaces") != 0 { ccr_iface_selftest_dump(); } r := save_ccr(out); if r != 0 { print("error: could not write "); @@ -565,6 +687,12 @@ fn corec_main() -> int { // Save .ccr alongside output (real IR artifact) println("save .ccr..."); ccr_path : ., mut = out_path + ".ccr"; + // R2 P4 Task 2/3(D13/D14/D18):TYPE/IFACE 段装填 + 段体缓冲(同 ccr 分支; + // 前置核查 = 本点之后无类型项/接口消费者——其后仅拼 corearch 命令行并子进程执行)。 + if ccr_seg_prepare_save() != 0 { + println("error: could not build TYPE/IFACE segments"); + return 1; + } r := save_ccr(ccr_path); if r != 0 { println("error: could not write .ccr"); return 1; } println("generate ELF..."); @@ -590,6 +718,12 @@ fn corec_main() -> int { } } exit_code := system(cmd2); + if exit_code != 0 { + // fail-closed 零产物(裁-FC-6):corearch 失败 ⇒ 删除**本次** save_ccr 写下的 `.ccr` + // 半成品(`save_ccr` 已覆盖同名旧文件 ⇒ 删除不额外损失;**未创建者不删**——不删目标 ELF + // 的既有旧文件,见本批报告「零产物边界」)。 + system("rm -f \"" + ccr_path + "\""); + } return exit_code; } diff --git a/src/compiler/module.cr b/src/compiler/module.cr index 20ac5bc1..5058795b 100644 --- a/src/compiler/module.cr +++ b/src/compiler/module.cr @@ -541,10 +541,57 @@ fn res_imports() { path = "src/runtime/" + fs_path + ".cr"; content = module_get_source(path); } + // ── 三轴实例目录(x86 实例化波 1 Task 1 搬迁落位)── + // 架构 × 格式 × OS 三轴目录 = 跨轴 import 的唯一通道(H5: + // elf→arch 6 处跨轴缝依赖此回退链)。**置于 src/compiler/ + // 之前**:①实例文件须先于通用面命中(实例 = 特化,通用面 = + // 兜底——与 src/arch/hit 同理);②否则 src/compiler/elf.cr + // (遗留死文件:零 importer + 不入任何 concat 清单)会遮蔽 + // src/format/elf/elf.cr,目标单元解析漂移 → elf_gen 等 + // N06 静默未定义。既有条目命中不漂:本三条目录为新建, + // 其文件名(instr/sizes/regalloc/elf/resolve/ld)在 + // g_source_dir / src/stdlib / src/runtime 三级均无同名文件。 + if str_len(content) == 0 { + path = "src/format/elf/" + fs_path + ".cr"; + content = module_get_source(path); + } + if str_len(content) == 0 { + path = "src/arch/x86_64/" + fs_path + ".cr"; + content = module_get_source(path); + } + if str_len(content) == 0 { + path = "src/os/linux/" + fs_path + ".cr"; + content = module_get_source(path); + } if str_len(content) == 0 { path = "src/compiler/" + fs_path + ".cr"; content = module_get_source(path); } + if str_len(content) == 0 { + path = "src/arch/hit/" + fs_path + ".cr"; + content = module_get_source(path); + } + // ent_kernel.cr(语义内核,格层 src/lattice/——2026-09-10 自 + // 旧实例目录迁出:实例目录不再承载语义内核;该目录于 x86 + // 实例化波 1 Task 1 拆为三轴 src/arch/x86_64 + src/format/elf + // + src/os/linux)双 concat + // 共享——corec 写侧 save_ccr 直调 compute_live_ranges/ + // grow_entries 等;镜像 src/compiler 跨树回退(同 concat 共享 + // 文件可被任意编译单元 import——src/compiler 单元自身需经此路 + // 径收编内核;实例单元与独立工程构建同经此路径命中) + if str_len(content) == 0 { + path = "src/lattice/" + fs_path + ".cr"; + content = module_get_source(path); + } + // 组合根(三轴组合 = target triple 命名):project-mode 入口 + // = 该目录 main.cr(load_project 直读)+ _import.cr(load_imports + // 直读)——**后置于 compiler/lattice**:目录内 main.cr 非 import + // 目标,前置会遮蔽既有 main/entry 命中(src/compiler/_import.cr + // 的 import main/entry);此条目为跨轴/跨单元收编面而备。 + if str_len(content) == 0 { + path = "src/targets/x86_64-linux/" + fs_path + ".cr"; + content = module_get_source(path); + } if str_len(content) == 0 { path = fs_path + ".cr"; content = module_get_source(path); diff --git a/src/compiler/monomorph.cr b/src/compiler/monomorph.cr index b837d633..36619736 100644 --- a/src/compiler/monomorph.cr +++ b/src/compiler/monomorph.cr @@ -16,6 +16,15 @@ g_gen_subst_new : string, mut; g_gen_subst_count : int, mut; g_gen_subst_cap : int, mut; +// ── R2 P3 Task 5(Step 4):ti 型替换表(与上面的名字表**并行同键**:g_gen_subst_old 的 +// 名字 ni → 实参**类型行 ti**)。来源 = checker 的调用点绑定段(g_gen_binds,globals.cr +// 表注);命中时替换产物 = `inst_type_node_of_ti` 造的**完整类型节点**(命名/复合/泛型 +// 应用皆可表达),取代旧「拼名字串再 str_intern 查名字」的塌缩(非原生实参一律 "int")。 +// -1/缺位 → 回落名字路径(旧行为逐字保持)。── +g_gen_subst_tis : string, mut; +g_gen_subst_tis_count : int, mut; +g_gen_subst_tis_cap : int, mut; + // ── Temporary dedup map (old AST node → new AST node during clone) ── g_gen_dedup_old : string, mut; g_gen_dedup_new : string, mut; @@ -31,6 +40,14 @@ fn grow_gen_instances(needed: int) { nb := alloc(nc * 24); _dyncpy(g_gen_instances, g_gen_instance_cap * 24, nb); g_gen_instances = nb; g_gen_instance_cap = nc; } +// 效应/纯度修正 Task 1:实例 → 源映射侧表(声明在 globals.cr——checker.cr 的 +// compute_all_purity 要读它,bootstrap 名字解析按声明序)。 +fn grow_purity_inst(needed: int) { + if needed < g_purity_inst_cap { return; } + nc : ., mut = g_purity_inst_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 24); _dyncpy(g_purity_inst, g_purity_inst_cap * 24, nb); + g_purity_inst = nb; g_purity_inst_cap = nc; } + fn grow_gen_subst(needed: int) { if needed < g_gen_subst_cap { return; } nc : ., mut = g_gen_subst_cap * 2; if nc < 16 { nc = 16; } if nc < needed { nc = needed + 16; } @@ -47,6 +64,13 @@ fn grow_gen_dedup(needed: int) { n2 := alloc(sz); _dyncpy(g_gen_dedup_new, g_gen_dedup_cap * 8, n2); g_gen_dedup_new = n2; g_gen_dedup_cap = nc; } +// R2 P3 Task 5(Step 4):ti 型替换表增长(与 g_gen_subst_old 同长——同一次实例化建立) +fn grow_gen_subst_tis(needed: int) { + if needed < g_gen_subst_tis_cap { return; } + nc : ., mut = g_gen_subst_tis_cap * 2; if nc < 16 { nc = 16; } if nc < needed { nc = needed + 16; } + nb := alloc(nc * 8); _dyncpy(g_gen_subst_tis, g_gen_subst_tis_cap * 8, nb); + g_gen_subst_tis = nb; g_gen_subst_tis_cap = nc; } + // ============================================================ // Substitution mapping // ============================================================ @@ -80,6 +104,32 @@ fn gen_build_subst(func_ni: int, type_args: string) { } } +// R2 P3 Task 5(Step 4):把调用点绑定段(块 = [count, ti…],见 checker.cr 写入点)装进 +// ti 型替换表——**名表与 ti 表同序同键**(第 i 项同为第 i 个泛型形参)。返回装填条数; +// 0 = 无绑定段/段非法(调用方回落名字路径)。 +fn gen_build_subst_tis(func_ni: int, bstart: int) -> int { + g_gen_subst_tis_count = 0; g_gen_subst_tis_cap = 0; + if bstart < 0 { return 0; } + gc := fi_generic_count(func_ni); + if gc <= 0 { return 0; } + if bstart >= g_gen_binds_count { return 0; } + cnt := r64(g_gen_binds, bstart * 8); + if cnt != gc { return 0; } // 段长必须与形参数一致(不同 ⇒ 不信任) + if bstart + 1 + cnt > g_gen_binds_count { return 0; } + grow_gen_subst_tis(gc); + i : ., mut = 0; + loop { + if i >= gc { break; } + t := r64(g_gen_binds, (bstart + 1 + i) * 8); + if t < 0 { return 0; } // 未绑定 ⇒ 全段放弃(不半解) + if inst_ti_concrete(t) == 0 { return 0; } // 含泛型形参行 ⇒ 全段放弃(同上) + w64(g_gen_subst_tis, i * 8, t); + g_gen_subst_tis_count = i + 1; + i = i + 1; + } + return g_gen_subst_tis_count; +} + // Look up substitution for a generic param name. // Returns concrete type name index, or -1 if not a generic param. fn gen_lookup_subst(name_ni: int) -> int { @@ -107,6 +157,259 @@ fn gen_base_type_tv(name_ni: int) -> int { return -1; } +// ═══════════════ R2 P3 Task 5(Step 4):ti ⇄ 节点/键 三件套 ═══════════════ +// 背景(旧态,逐条实测):实例键由 ir_gen 从**实参 IR 变量的类型**拼名字串得到,非原生 +// (struct/tuple/slice/ref…)一律兜底 "int"(ir_gen.cr 的 else 分支)⇒ ① 两个不同 struct +// 实参折叠成同一实例(实测 .ccr:`f[unit]` 一份);② 替换 = 名字文本替换(gen_build_subst) +// ⇒ 泛型形参被替换成**错的名字**(`fn f[T](a: int, b: T)` 以 `f(1, "s")` 调用 → T := int, +// 而形参 b 的声明类型是 T ⇒ 实例体按 int 落地)。本套即治此二病: +// ① `inst_key_of_ti`:类型行 → 规范结构名(键的构成单元;结构忠实 ⇒ 异型不再折叠); +// ② `inst_type_node_of_ti`:类型行 → 类型语法节点(替换产物;命名/应用/序列/引用/指针/ +// 可选皆可表达); +// ③ `gen_subst_ti`:克隆期把嵌套调用点绑定里的泛型形参行按**当前实例**替换。 +// 分隔符约定(**键必须能被 gen_build_subst 按逗号切分**):项内分隔用 "|"、长度用 "; "、 +// 形参间才用 ","(实例键的 join 分隔符)⇒ 规范名内**不得出现逗号**。 +// 语法节点合成契约:**仅**合成「根节点占单槽」的类型(原生/命名/泛型形参)——EXPR_GENERIC_APPLY +// 的实参读取按 `first_arg + i`(res_type_node)⇒ 实参根节点必须**连续**,而合成复合实参时 +// 子树节点会插在中间(parser 侧靠「子树的根恒为该子树最后分配的节点」自然成立)⇒ 复合实参 +// 一律回 -1(调用方回落名字路径;登记为未覆盖面)。 + +fn inst_key_of_ti(ti: int) -> string { + k := get_type_kind(ti); + if k < 0 { return "?"; } + if k == TYP_BASE { + d := get_type_data(ti); + if d == TY_INT { return "int"; } + if d == TY_DEX { return "dex"; } + if d == TY_BOOL { return "bool"; } + if d == TY_STRING { return "string"; } + if d == TY_CHAR { return "char"; } + if d == TY_UNIT { return "unit"; } + if d == TY_NEVER { return "never"; } + return "#" + int_str(d); + } + if k == TYP_NAMED || k == TYP_GENERIC_PARAM { return istr_get(get_type_data(ti)); } + if k == TYP_GENERIC_APPLY { + base := get_type_data(ti); + s : ., mut = "?"; + if get_type_kind(base) == TYP_NAMED { s = istr_get(get_type_data(base)); } + s = s + "["; + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + i : ., mut = 0; + loop { + if i >= cnt { break; } + if i > 0 { s = s + "|"; } + s = s + inst_key_of_ti(r64(g_gen_apply_data, (start + 1 + i) * 8)); + i = i + 1; + } + return s + "]"; + } + if k == TYP_ARRAY { return "[" + inst_key_of_ti(get_type_data(ti)) + "; " + int_str(get_type_extra(ti)) + "]"; } + if k == TYP_SLICE { return "[" + inst_key_of_ti(get_type_data(ti)) + "]"; } + if k == TYP_REF { + if get_type_extra(ti) != 0 { return "&mut " + inst_key_of_ti(get_type_data(ti)); } + return "&" + inst_key_of_ti(get_type_data(ti)); + } + if k == TYP_PTR { return "*" + inst_key_of_ti(get_type_data(ti)); } + if k == TYP_OPTIONAL { return inst_key_of_ti(get_type_data(ti)) + "?"; } + if k == TYP_NULL { return "null"; } + if k == TYP_DYN { return "dyn"; } + if k == TYP_TUPLE { + s : ., mut = "("; + cnt2 := get_type_data(ti); + start2 := get_type_extra(ti); + i2 : ., mut = 0; + loop { + if i2 >= cnt2 { break; } + if i2 > 0 { s = s + "|"; } + s = s + inst_key_of_ti(r64(g_gen_apply_data, (start2 + i2) * 8)); + i2 = i2 + 1; + } + return s + ")"; + } + return "#" + int_str(ti); // 未知 kind:行号兜底(同编译期内确定;跨行同型不去重,登记) +} + +// 类型行 → 类型语法节点(**单槽根**约束见上注);-1 = 无对应形态(调用方回落名字路径) +fn inst_type_node_of_ti(ti: int) -> int { + k := get_type_kind(ti); + if k < 0 { return -1; } + if k == TYP_BASE { return alloc_node(0, 0, 0, 0, 0, get_type_data(ti), 0, 0, 0); } + if k == TYP_NAMED || k == TYP_GENERIC_PARAM { + return alloc_node(EXPR_IDENT, 0, 0, 0, get_type_data(ti), 0, 0, 0, 0); + } + if k == TYP_ARRAY { + inner := inst_type_node_of_ti(get_type_data(ti)); + if inner < 0 { return -1; } + return alloc_node(EXPR_ARRAY, inner, 0, 0, get_type_extra(ti), 0, 0, 0, 0); + } + if k == TYP_SLICE { + inner := inst_type_node_of_ti(get_type_data(ti)); + if inner < 0 { return -1; } + return alloc_node(EXPR_ARRAY, inner, 0, 0, 0, 0, 0, 0, 0); + } + if k == TYP_REF { + inner := inst_type_node_of_ti(get_type_data(ti)); + if inner < 0 { return -1; } + return alloc_node(EXPR_REFTYPE, inner, 0, 0, get_type_extra(ti), 0, 0, 0, 0); + } + if k == TYP_PTR { + inner := inst_type_node_of_ti(get_type_data(ti)); + if inner < 0 { return -1; } + return alloc_node(EXPR_PTRTYPE, inner, 0, 0, 0, 0, 0, 0, 0); + } + if k == TYP_OPTIONAL { + inner := inst_type_node_of_ti(get_type_data(ti)); + if inner < 0 { return -1; } + return alloc_node(EXPR_OPTIONAL, inner, 0, 0, 0, 0, 0, 0, 0); + } + if k == TYP_GENERIC_APPLY { + base := get_type_data(ti); + if get_type_kind(base) != TYP_NAMED { return -1; } + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + if cnt <= 0 { return -1; } + first_an : ., mut = -1; + i : ., mut = 0; + loop { + if i >= cnt { break; } + at := r64(g_gen_apply_data, (start + 1 + i) * 8); + ak := get_type_kind(at); + // 实参根节点连续契约:仅接受单槽根(原生/命名/形参)——复合实参见上注(登记) + if ak != TYP_BASE && ak != TYP_NAMED && ak != TYP_GENERIC_PARAM { return -1; } + an := inst_type_node_of_ti(at); + if an < 0 { return -1; } + if i == 0 { first_an = an; } + i = i + 1; + } + return alloc_node(EXPR_GENERIC_APPLY, get_type_data(base), first_an, cnt, 0, 0, 0, 0, 0); + } + return -1; // TUPLE / NULL / DYN / 未知 kind:无语法形态(DYN 另见 res_type_node 的码 7 格) +} + +fn gen_lookup_subst_ti(name_ni: int) -> int { + mi : ., mut = 0; + loop { + if mi >= g_gen_subst_tis_count { break; } + if r64(g_gen_subst_old, mi * 8) == name_ni { return r64(g_gen_subst_tis, mi * 8); } + mi = mi + 1; + } + return -1; +} + +// 类型行 → 按当前实例替换泛型形参行后的类型行(-1 保持原行;复合行结构性重建) +fn gen_subst_ti(ti: int) -> int { + k := get_type_kind(ti); + if k < 0 { return ti; } + if k == TYP_GENERIC_PARAM { + s := gen_lookup_subst_ti(get_type_data(ti)); + if s >= 0 { return s; } + return ti; + } + if k == TYP_ARRAY { return alloc_type(TYP_ARRAY, gen_subst_ti(get_type_data(ti)), get_type_extra(ti)); } + if k == TYP_SLICE { return alloc_type(TYP_SLICE, gen_subst_ti(get_type_data(ti)), 0); } + if k == TYP_REF { return alloc_type(TYP_REF, gen_subst_ti(get_type_data(ti)), get_type_extra(ti)); } + if k == TYP_PTR { return alloc_type(TYP_PTR, gen_subst_ti(get_type_data(ti)), get_type_extra(ti)); } + if k == TYP_OPTIONAL { return alloc_type(TYP_OPTIONAL, gen_subst_ti(get_type_data(ti)), 0); } + if k == TYP_GENERIC_APPLY { + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + // 两趟(照 res_type_node 同款:子项替换自身会追加 gen_apply 载荷) + subs : string, mut; + if cnt > 0 { + subs = alloc(cnt * 8); + i : ., mut = 0; + loop { + if i >= cnt { break; } + w64(subs, i * 8, gen_subst_ti(r64(g_gen_apply_data, (start + 1 + i) * 8))); + i = i + 1; + } + } + ns := g_gen_apply_data_count; + grow_gen_apply_data(ns + 1 + cnt); + w64(g_gen_apply_data, ns * 8, cnt); + i2 : ., mut = 0; + loop { + if i2 >= cnt { break; } + w64(g_gen_apply_data, (ns + 1 + i2) * 8, r64(subs, i2 * 8)); + i2 = i2 + 1; + } + g_gen_apply_data_count = ns + 1 + cnt; + return alloc_type(TYP_GENERIC_APPLY, get_type_data(ti), ns); + } + return ti; +} + +// 替换后是否**全具体**(无残留泛型形参行):克隆期嵌套调用点绑定重映射的前置条件 +// (残留 = 该绑定在本实例上下文无解 ⇒ 整段放弃、回落旧名字路径,不发明半解实例) +fn inst_ti_concrete(ti: int) -> int { + k := get_type_kind(ti); + if k < 0 { return 1; } // 越界行:非形参,按具体处理(判定面自会拒绝) + if k == TYP_GENERIC_PARAM { return 0; } + if k == TYP_ARRAY || k == TYP_SLICE || k == TYP_REF || k == TYP_PTR || k == TYP_OPTIONAL { + return inst_ti_concrete(get_type_data(ti)); + } + if k == TYP_GENERIC_APPLY { + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + i : ., mut = 0; + loop { + if i >= cnt { return 1; } + if inst_ti_concrete(r64(g_gen_apply_data, (start + 1 + i) * 8)) == 0 { return 0; } + i = i + 1; + } + return 1; + } + if k == TYP_TUPLE { + start2 := get_type_extra(ti); + cnt2 := get_type_data(ti); + i2 : ., mut = 0; + loop { + if i2 >= cnt2 { return 1; } + if inst_ti_concrete(r64(g_gen_apply_data, (start2 + i2) * 8)) == 0 { return 0; } + i2 = i2 + 1; + } + return 1; + } + return 1; +} + +// 克隆期:源调用点绑定段(iv = 段起始 + 1,见 checker.cr 写入点)→ 按当前实例重登记的段 +// (返回新的 **iv**;0 = 无/不可重映射 ⇒ 克隆体回落旧名字路径)。 +// 为何必须重映射:克隆体内的调用节点携带**源函数上下文**的绑定(如 T := gparam(U))—— +// 直接沿用 = 用源上下文的形参行给实例建键(错实例);置 0 = 丢信息(旧行为)。 +fn gen_remap_binds(iv: int) -> int { + if iv <= 0 { return 0; } + start := iv - 1; + if start >= g_gen_binds_count { return 0; } + cnt := r64(g_gen_binds, start * 8); + if cnt <= 0 { return 0; } + if start + 1 + cnt > g_gen_binds_count { return 0; } + // 前置:全部条目替换后须**全具体**(否则整段放弃——不发明半解实例)。替换结果先落暂存 + // (gen_subst_ti 对复合行会追加类型行/载荷 ⇒ 逐条重算 = 双倍分配;且避免边写边读)。 + subs : string, mut = alloc(cnt * 8); + i : ., mut = 0; + loop { + if i >= cnt { break; } + t := gen_subst_ti(r64(g_gen_binds, (start + 1 + i) * 8)); + if inst_ti_concrete(t) == 0 { return 0; } + w64(subs, i * 8, t); + i = i + 1; + } + ns := g_gen_binds_count; + grow_gen_binds(ns + 1 + cnt); + w64(g_gen_binds, ns * 8, cnt); + i2 : ., mut = 0; + loop { + if i2 >= cnt { break; } + w64(g_gen_binds, (ns + 1 + i2) * 8, r64(subs, i2 * 8)); + i2 = i2 + 1; + } + g_gen_binds_count = ns + 1 + cnt; + return ns + 1; +} + // ============================================================ // Dedup mapping for tree clone // ============================================================ @@ -184,6 +487,14 @@ fn gen_clone_tree(node: int) -> int { // ── EXPR_IDENT: might be a generic param reference ── if k == EXPR_IDENT { ni := iv; + // R2 P3 Task 5(Step 4):**ti 路径优先**——由类型行合成完整类型节点(命名/应用/序列/ + // 引用/指针/可选皆可表达),治旧「名字文本替换」对非原生实参的塌缩。合成失败(-1: + // 元组/复合实参等无语法形态)→ 落名字路径(旧行为逐字保持)。 + sti := gen_lookup_subst_ti(ni); + if sti >= 0 { + sn := inst_type_node_of_ti(sti); + if sn >= 0 { gen_dedup_add(node, sn); return sn; } + } sub := gen_lookup_subst(ni); if sub >= 0 { // Substitute generic param with concrete type @@ -236,6 +547,10 @@ fn gen_clone_tree(node: int) -> int { if k == EXPR_FIELD { a2 := gen_clone_tree(a); n := ast_alloc(k, a2, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } if k == EXPR_STMT { a2 := gen_clone_tree(a); n := ast_alloc(k, a2, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } if k == EXPR_GO { a2 := gen_clone_tree(a); n := ast_alloc(k, a2, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } + // ── R2 P3 Task 4:`T?`(a = 内层类型节点,可能含泛型形参)——单子节点克隆, + // 形参代入由 EXPR_IDENT 分支承担(clone 时经 gen_lookup_subst),故此处只递归 a。 + // **必配分支**:默认兜底会把 d(=0)当 AST 子节点克隆(读进节点 0)——显式分支消除该面。 + if k == EXPR_OPTIONAL { a2 := gen_clone_tree(a); n := ast_alloc(k, a2, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } // ── Two children: `a` and `b` ── if k == EXPR_BINARY { a2 := gen_clone_tree(a); b2 := gen_clone_tree(b); n := ast_alloc(k, a2, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -247,7 +562,33 @@ fn gen_clone_tree(node: int) -> int { if k == EXPR_AS { a2 := gen_clone_tree(a); b2 := gen_clone_tree(b); n := ast_alloc(k, a2, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } // ── EXPR_CALL: a=func_node(YES), b=first_ARG(YES), c=arg_count(NOT) ── - if k == EXPR_CALL { a2 := gen_clone_tree(a); b2 := gen_clone_tree(b); n := ast_alloc(k, a2, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } + if k == EXPR_CALL { + a2 := gen_clone_tree(a); b2 := gen_clone_tree(b); + // R2 P3 Task 5(Step 4):调用点绑定的**实例化重映射**——克隆体内的调用节点携带 + // **源函数上下文**的绑定(如 T := gparam(U))⇒ 按当前实例替换重登记(gen_remap_binds); + // 不可完全落地 → 0(= 无绑定,回落旧名字路径)。**必须显式改写**:iv 经 ast_alloc + // 原样拷贝,沿用 = 用源上下文的形参行给实例建键(错实例)。 + n := ast_alloc(k, a2, b2, c, iv, tv, d, ln, cl); + ast_set_int_val(n, gen_remap_binds(iv)); + // R2 P3b Task 6(Step 3,mangling 退役):接口方法调用(泛型形参接收者)的**实例化解析**。 + // 源节点携带(data = 泛型形参名 ni,type_val = CALL_FLAG_IFACE_METHOD;检查见 checker 的 + // 泛型约束方法路径注)⇒ 此处按当前实例的具体类型**查方法表**取真实函数名(`g_methods` + // 三元组 → 函数名 ni)。旧态 = 克隆期沿用合成的 "T.m" 串(文本替换只作用于 EXPR_IDENT) + // ⇒ 实例体内调用目标悬空(实测:产物运行 rc=139)。 + // 解析失败(形参未绑定 / 具体类型无此方法 / 嵌套形参)⇒ 保持原状(登记面,不发明目标)。 + if tv == CALL_FLAG_IFACE_METHOD { + cni := gen_lookup_subst(d); + if cni >= 0 && a2 >= 0 { + m_ni := ast_int_val(a2); + f_ni := iface_find_method(cni, m_ni); + if f_ni >= 0 { + ast_set_data(n, f_ni); + ast_set_type_val(n, 0); + } + } + } + gen_dedup_add(node, n); return n; + } // ── EXPR_ARG: a=expr(YES), b=next_arg(YES or -1) ── if k == EXPR_ARG { a2 := gen_clone_tree(a); b2 := gen_clone_tree(b); n := ast_alloc(k, a2, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -270,11 +611,33 @@ fn gen_clone_tree(node: int) -> int { // ── EXPR_LET: a=name_ni(NOT), b=type(YES or -1), c=value(YES or -1), d=is_mut(NOT) ── if k == EXPR_LET { b2 := gen_clone_tree(b); c2 := gen_clone_tree(c); n := ast_alloc(k, a, b2, c2, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } - // ── EXPR_STRUCT: a=type_name_ni(NOT), b=first_field_idx(NOT), c=field_count(NOT) - // Fields are stored as 2 consecutive AST nodes per field: (name_node, value_node) ── + // ── EXPR_STRUCT: a=type_name_ni(NOT), b=first wrapper(YES, consecutive), c=field_count(NOT); + // wrapper.a = field value node、wrapper.b = 字段名 idx(TODO #29 ①;-1 = 无名字信息) + // (F5 契约,见 parser.cr struct 字面量分支)── if k == EXPR_STRUCT { if b >= 0 && c > 0 { - new_first := gen_clone_consecutive(b, c * 2); + // 字段值先逐个深克隆(各自子树自占槽位),再统建连续 wrapper—— + // 不得「克隆值后随建 wrapper」逐元素交错:wrapper 将不连续(与 parser 同契约)。 + // **名字随克隆保留**(#29 ①):实例体里的字段绑定仍按名字(源序 ≠ 声明序时, + // 丢名字会让 ir_gen 回落位序 ⇒ 静默错值——正是 #29 要消灭的类)。 + nvs : string, mut = alloc(c * 8); + nms : string, mut = alloc(c * 8); + i : ., mut = 0; + loop { + if i >= c { break; } + vn : ., mut = -1; + if b + i >= 0 { vn = ast_a(b + i); } + w64(nvs, i * 8, gen_clone_tree(vn)); + w64(nms, i * 8, ast_b(b + i)); + i = i + 1; + } + new_first := g_ast_count; + i = 0; + loop { + if i >= c { break; } + ast_alloc(EXPR_NONE, r64(nvs, i * 8), r64(nms, i * 8), 0, 0, 0, 0, ln, cl); + i = i + 1; + } n := ast_alloc(k, a, new_first, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -282,10 +645,27 @@ fn gen_clone_tree(node: int) -> int { gen_dedup_add(node, n); return n; } - // ── EXPR_ARRAY: a=first_elem(YES), b=elem_count(NOT) — elements are consecutive ── + // ── EXPR_ARRAY: a=first wrapper(YES, consecutive), b=elem_count(NOT); + // wrapper.a = element value node(F5 契约,见 parser.cr 下标分支)── if k == EXPR_ARRAY { if a >= 0 && b > 0 { - new_first := gen_clone_consecutive(a, b); + // 元素值先逐个深克隆(各自子树自占槽位),再统建连续 wrapper(与 parser 同契约)。 + nvs : string, mut = alloc(b * 8); + i : ., mut = 0; + loop { + if i >= b { break; } + vn : ., mut = -1; + if a + i >= 0 { vn = ast_a(a + i); } + w64(nvs, i * 8, gen_clone_tree(vn)); + i = i + 1; + } + new_first := g_ast_count; + i = 0; + loop { + if i >= b { break; } + ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl); + i = i + 1; + } n := ast_alloc(k, new_first, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -293,10 +673,28 @@ fn gen_clone_tree(node: int) -> int { gen_dedup_add(node, n); return n; } - // ── EXPR_TUPLE: a=first_elem(YES), b=elem_count(NOT) — elements are consecutive ── + // ── EXPR_TUPLE: a=first wrapper(YES, consecutive), b=elem_count(NOT); + // wrapper.a = element value node(F5 契约,见 parser.cr 元组分支)── if k == EXPR_TUPLE { if a >= 0 && b > 0 { - new_first := gen_clone_consecutive(a, b); + // 元素值先逐个深克隆(各自子树自占槽位),再统建连续 wrapper—— + // 不得「克隆值后随建 wrapper」逐元素交错:wrapper 将不连续(与 parser 同契约)。 + nvs : string, mut = alloc(b * 8); + i : ., mut = 0; + loop { + if i >= b { break; } + vn : ., mut = -1; + if a + i >= 0 { vn = ast_a(a + i); } + w64(nvs, i * 8, gen_clone_tree(vn)); + i = i + 1; + } + new_first := g_ast_count; + i = 0; + loop { + if i >= b { break; } + ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl); + i = i + 1; + } n := ast_alloc(k, new_first, b, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -327,8 +725,32 @@ fn gen_clone_tree(node: int) -> int { // ── EXPR_ENUMPAT: a=name_ni(NOT), b=first_subpat(YES, consecutive), c=subpat_count(NOT) ── if k == EXPR_ENUMPAT { b2 := gen_clone_consecutive(b, c); n := ast_alloc(k, a, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } - // ── EXPR_STRUCTPAT: a=name_ni(NOT), b=first_field(NOT, consecutive), c=field_count(NOT) ── - if k == EXPR_STRUCTPAT { b2 := gen_clone_consecutive(b, c * 2); n := ast_alloc(k, a, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } + // ── EXPR_STRUCTPAT: a=name_ni(NOT), b=first wrapper(YES, consecutive), c=field_count(NOT); + // wrapper.a = 子模式节点(F5 契约,见 parser.cr struct 模式分支)── + if k == EXPR_STRUCTPAT { + if b >= 0 && c > 0 { + nvs : string, mut = alloc(c * 8); + i : ., mut = 0; + loop { + if i >= c { break; } + vn : ., mut = -1; + if b + i >= 0 { vn = ast_a(b + i); } + w64(nvs, i * 8, gen_clone_tree(vn)); + i = i + 1; + } + new_first := g_ast_count; + i = 0; + loop { + if i >= c { break; } + ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl); + i = i + 1; + } + n := ast_alloc(k, a, new_first, c, iv, tv, d, ln, cl); + gen_dedup_add(node, n); return n; + } + n := ast_alloc(k, a, b, c, iv, tv, d, ln, cl); + gen_dedup_add(node, n); return n; + } // ── EXPR_AT: a=name_ni(NOT), b=args_node(YES or -1) ── if k == EXPR_AT { b2 := gen_clone_tree(b); n := ast_alloc(k, a, b2, c, iv, tv, d, ln, cl); gen_dedup_add(node, n); return n; } @@ -373,7 +795,15 @@ fn gen_find_cached(func_ni: int, type_args: string) -> int { fn gen_find_or_create(func_ni: int, type_args: string) -> int { cached := gen_find_cached(func_ni, type_args); if cached >= 0 { return cached; } - return gen_create_instance(func_ni, type_args); + return gen_create_instance(func_ni, type_args, -1); +} + +// R2 P3 Task 5(Step 4):带**调用点绑定段**的查/建(ir_gen 提供规范键 + 段起始)。 +// 缓存键 = type_args 串(规范结构名 join,含真身份)⇒ 同实例同键、异实例异键(不再折叠)。 +fn gen_find_or_create_bind(func_ni: int, type_args: string, bstart: int) -> int { + cached := gen_find_cached(func_ni, type_args); + if cached >= 0 { return cached; } + return gen_create_instance(func_ni, type_args, bstart); } // Create a specialized copy of a generic function. @@ -383,9 +813,13 @@ fn gen_find_or_create(func_ni: int, type_args: string) -> int { // 3. Register the new function in g_funcs // 4. Cache the new function index // 5. Return the new function index -fn gen_create_instance(func_ni: int, type_args: string) -> int { +fn gen_create_instance(func_ni: int, type_args: string, bstart: int) -> int { // 1. Build substitution mapping + // · 名表(g_gen_subst_old/new):键 = 键串按逗号切分——**回落路径**;规范键下其名字对 + // 复合类型不是声明名 ⇒ 只在 ti 路径失效时才可能被用到(见 gen_clone_tree 的优先级)。 + // · ti 表(g_gen_subst_tis):bstart ≥ 0 且段合法时装填(**优先路径**——完整类型节点)。 gen_build_subst(func_ni, type_args); + gen_build_subst_tis(func_ni, bstart); // 2. Clone the original function AST orig_fn_node := fi_ast_node(func_ni); @@ -435,6 +869,17 @@ fn gen_create_instance(func_ni: int, type_args: string) -> int { w64(g_gen_instances, g_gen_instance_count * 24 + 16, new_fi); g_gen_instance_count = g_gen_instance_count + 1; + // 效应/纯度修正 Task 1:登记实例 → 源(纯度回填用,见 globals.cr 表注)。 + // 注意:不在此处写 fi_set_ispure(new_fi, fi_ispure(func_ni))——本时点(IR 生成期) + // 源函数还没有真纯度(真纯度只能由全程序 IR 体算,见 checker.cr 头注), + // 抄过来的是乐观默认值,会把有效应泛型实例错标为纯。实例纯度由其自身体 + // 在 df_state_finalize 算出;源函数纯度 = 实例合取(compute_all_purity 回填)。 + grow_purity_inst(g_purity_inst_count + 1); + w64(g_purity_inst, g_purity_inst_count * 24, func_ni); + w64(g_purity_inst, g_purity_inst_count * 24 + 8, type_args_ni); + w64(g_purity_inst, g_purity_inst_count * 24 + 16, new_fi); + g_purity_inst_count = g_purity_inst_count + 1; + // 5. Return new function index return new_fi; } diff --git a/src/compiler/opt.cr b/src/compiler/opt.cr index 978518bc..d39d52f2 100644 --- a/src/compiler/opt.cr +++ b/src/compiler/opt.cr @@ -1,7 +1,9 @@ // === opt.cr === -// AST-level optimization passes. -// Runs after check_all(), before ir_gen_all(). -// Only transforms AST nodes (g_ast), never touches IR or backend. +// Corec 侧语义优化(2026-09-07 regalloc 移后端后剩件——编码层资源决策 +// 数据面/分配/判定已迁 src/arch/x86_64/regalloc.cr): +// - AST 常量折叠(ast_optimize_body 族——AST 层) +// - 线性流 CSE(pass_cse——IR 层,main.cr O≥1 调用) +// - optimize_all(历史编排入口——无调用者,见函数内注记) // ------------------------------------------------------------------ @@ -124,11 +126,22 @@ fn ast_optimize_body(body: int) { loop { if ai >= ac { break; } if an >= 0 { ast_optimize_body(an); an = an + 1; } ai = ai + 1; } return; } - // EXPR_STRUCT: optimize field values + // EXPR_STRUCT: a=type_name_ni, b=first wrapper(连续), c=field_count;wrapper.a=字段值节点 + // (F5 契约,见 parser.cr struct 字面量分支)。旧代码直接对 wrapper 递归,而 wrapper 的 + // kind=EXPR_NONE 在本函数无分支 ⇒ 常量折叠静默空转(字段值从不被优化)。 if bk == EXPR_STRUCT { fn2 := ast_b(body); fc := ast_c(body); i : ., mut = 0; - loop { if i >= fc { break; } if fn2 >= 0 { ast_optimize_body(fn2); fn2 = fn2 + 1; } i = i + 1; } + loop { + if i >= fc { break; } + if fn2 >= 0 { + vn : ., mut = -1; + if ast_kind(fn2) == EXPR_NONE { vn = ast_a(fn2); } + if vn >= 0 { ast_optimize_body(vn); } + fn2 = fn2 + 1; + } + i = i + 1; + } return; } // EXPR_LET: optimize value expression @@ -171,11 +184,37 @@ fn ast_optimize_body(body: int) { if ast_a(body) >= 0 { ast_optimize_body(ast_a(body)); } return; } - // EXPR_ARRAY, EXPR_TUPLE: optimize elements - if bk == EXPR_ARRAY || bk == EXPR_TUPLE { - an := ast_b(body); ac := ast_c(body); + // EXPR_ARRAY: a=first wrapper(连续), b=elem_count;wrapper.a=元素值节点 + // (F5 契约,见 parser.cr 下标分支);旧代码直接对 wrapper 递归 = 空转(EXPR_NONE 无分支)。 + if bk == EXPR_ARRAY { + an := ast_a(body); ac := ast_b(body); i : ., mut = 0; - loop { if i >= ac { break; } if an >= 0 { ast_optimize_body(an); an = an + 1; } i = i + 1; } + loop { + if i >= ac { break; } + if an >= 0 { + vn : ., mut = -1; + if ast_kind(an) == EXPR_NONE { vn = ast_a(an); } + if vn >= 0 { ast_optimize_body(vn); } + an = an + 1; + } + i = i + 1; + } + return; + } + // EXPR_TUPLE: a=first wrapper(连续), b=elem_count;wrapper.a=元素值节点(F5 契约) + if bk == EXPR_TUPLE { + an := ast_a(body); ac := ast_b(body); + i : ., mut = 0; + loop { + if i >= ac { break; } + if an >= 0 { + vn : ., mut = -1; + if ast_kind(an) == EXPR_NONE { vn = ast_a(an); } + if vn >= 0 { ast_optimize_body(vn); } + an = an + 1; + } + i = i + 1; + } return; } // EXPR_AS: optimize both sides @@ -186,220 +225,6 @@ fn ast_optimize_body(body: int) { // EXPR_BINARY already handled above; fallthrough for EXPR_INDEX etc. } -// ------------------------------------------------------------------ -// Register allocation: rewrite IR operands to encode physical regs -// ------------------------------------------------------------------ -// Rewrites g_ir_instrs operand fields: operands pointing to IR vars -// that should go in registers are replaced with negative register -// encodings (-1=rax, -2=rcx, -3=rdx, ...). -// Backend g2_slot() checks v < 0 and returns v directly as reg num. -// No backend decision-making needed — pure mechanical translation. - -fn alloc_registers() { - if g_opt_level < 1 { return; } - fi : ., mut = 0; - loop { - if fi >= g_ir_func_count { break; } - ic := r64(g_ir_func_instr_count, fi * 8); - ist := r64(g_ir_func_instr_start, fi * 8); - vc := r64(g_ir_func_var_count, fi * 8); - vs := r64(g_ir_func_var_start, fi * 8); - if vc <= 0 { fi = fi + 1; continue; } - - // Build live intervals: [first_ref, last_ref] per var - iv_buf := alloc(vc * 16); - vi : ., mut = 0; - loop { if vi >= vc { break; } w64(iv_buf, vi*16, -1); w64(iv_buf, vi*16+8, -1); vi = vi + 1; } - - ii : ., mut = 0; - loop { - if ii >= ic { break; } - inst := ist + ii; - op := iri_op(inst); d := iri_dest(inst); s1 := iri_s1(inst); s2 := iri_s2(inst); - vc2 : ., mut = 0; - vars : string, mut = alloc(64 * 8); vars_cap : int, mut = 64; - if vars_cap == 0 { vars = alloc(64); vars_cap = 8; } - if d >= vs && d < vs + vc { if vc2 < vars_cap { w64(vars, vc2 * 8, d - vs); vc2 = vc2 + 1; } } - if s1 >= vs && s1 < vs + vc { if vc2 < vars_cap { w64(vars, vc2 * 8, s1 - vs); vc2 = vc2 + 1; } } - if s2 >= vs && s2 < vs + vc { if vc2 < vars_cap { w64(vars, vc2 * 8, s2 - vs); vc2 = vc2 + 1; } } - vj : ., mut = 0; - loop { if vj >= vc2 { break; } - lv := r64(vars, vj * 8); - if r64(iv_buf, lv*16) < 0 { w64(iv_buf, lv*16, ii); } - w64(iv_buf, lv*16+8, ii); - vj = vj + 1; } - ii = ii + 1; - } - - // Use only callee-saved registers (preserved across function calls) - // rbx(3), r12(12), r13(13), r14(14), r15(15) = 5 registers - // (rsp/rbp excluded; caller-saved regs get clobbered by function calls) - MAX_REGS : int = 5; - reg_idx : ., mut = 0; - reg_phys : string, mut = alloc(5 * 8); - w64(reg_phys, 0, 3); w64(reg_phys, 8, 12); - w64(reg_phys, 16, 13); w64(reg_phys, 24, 14); w64(reg_phys, 32, 15); - - // Map local var index → physical register (-1 = stack) - var_reg : string, mut; var_reg_cap : int, mut; - vrc : ., mut = 64; if vc * 2 > vrc { vrc = vc * 2; } - var_reg = alloc(vrc * 8); var_reg_cap = vrc; - vr_clear : ., mut = 0; - loop { if vr_clear >= vrc { break; } w64(var_reg, vr_clear * 8, -1); vr_clear = vr_clear + 1; } - - // Simple linear scan: for each instruction, allocate regs for dest - ii = 0; - loop { - if ii >= ic { break; } - inst := ist + ii; - op := iri_op(inst); d := iri_dest(inst); s1 := iri_s1(inst); s2 := iri_s2(inst); - - // Free regs for vars that end before this instruction - vi = 0; - loop { if vi >= vc { break; } - if r64(var_reg, vi * 8) >= 0 { - last_ref := r64(iv_buf, vi*16+8); - if last_ref < ii { - // Return reg to pool - w64(var_reg, vi * 8, -1); - } - } - vi = vi + 1; } - - // Allocate reg for dest if it has a live range - if d >= vs && d < vs + vc { - lvi := d - vs; - if r64(var_reg, lvi * 8) < 0 { - first_ref := r64(iv_buf, lvi*16); - last_ref := r64(iv_buf, lvi*16+8); - if first_ref >= 0 && last_ref >= 0 && reg_idx < MAX_REGS { - w64(var_reg, lvi * 8, r64(reg_phys, reg_idx * 8)); - reg_idx = reg_idx + 1; - } - } - } - // (operands NOT rewritten — metadata stored separately) - // Register assignments collected into g_opt_meta below. - ii = ii + 1; - } - - // Write register assignments to g_opt_meta (flat array of var_idx+reg_num pairs) - // Store register assignments in g_opt_meta (simple format: var_idx,reg pairs) - rc : ., mut = 0; - vi = 0; - loop { if vi >= vc { break; } - if r64(var_reg, vi * 8) >= 0 { rc = rc + 1; } - vi = vi + 1; } - if rc > 0 { - ei : ., mut = g_opt_meta_count; - grow_opt_meta(ei + 1); - // Write key(u32) + data_len(u32) header using store8 - eo := ei * OPT_META_STRIDE; - store8(g_opt_meta, eo, 0); store8(g_opt_meta, eo+1, 0); - store8(g_opt_meta, eo+2, 0); store8(g_opt_meta, eo+3, 0); // OPT_KEY_REG_ASSIGN=0 - dl : ., mut = 4 + rc * 8; - store8(g_opt_meta, eo+4, dl%256); store8(g_opt_meta, eo+5, (dl/256)%256); - store8(g_opt_meta, eo+6, (dl/65536)%256); store8(g_opt_meta, eo+7, (dl/16777216)%256); - // Write count - store8(g_opt_meta, eo+8, rc%256); store8(g_opt_meta, eo+9, (rc/256)%256); - store8(g_opt_meta, eo+10, (rc/65536)%256); store8(g_opt_meta, eo+11, (rc/16777216)%256); - // Write pairs: [var_idx(u32), reg(u32)]... - di : ., mut = 12; // after header(8) + count(4) - vi = 0; - loop { if vi >= vc { break; } - rn := r64(var_reg, vi * 8); - if rn >= 0 { - vw := vs + vi; - store8(g_opt_meta, eo+di, vw%256); store8(g_opt_meta, eo+di+1, (vw/256)%256); - store8(g_opt_meta, eo+di+2, (vw/65536)%256); store8(g_opt_meta, eo+di+3, (vw/16777216)%256); - store8(g_opt_meta, eo+di+4, rn%256); store8(g_opt_meta, eo+di+5, (rn/256)%256); - store8(g_opt_meta, eo+di+6, (rn/65536)%256); store8(g_opt_meta, eo+di+7, (rn/16777216)%256); - di = di + 8; - } - vi = vi + 1; } - g_opt_meta_count = ei + 1; - } - fi = fi + 1; - } -} - -fn pass_stack_share() { - if g_ir_func_count <= 0 { return; } - - // Allocate g_stack_map with -1 for all IR vars - g_stack_map = alloc(g_ir_var_count * 8); - svi : ., mut = 0; - loop { if svi >= g_ir_var_count { break; } w64(g_stack_map, svi * 8, -1); svi = svi + 1; } - - fi : ., mut = 0; - loop { - if fi >= g_ir_func_count { break; } - ic := r64(g_ir_func_instr_count, fi * 8); - ist := r64(g_ir_func_instr_start, fi * 8); - vc := r64(g_ir_func_var_count, fi * 8); - vs := r64(g_ir_func_var_start, fi * 8); - if vc <= 0 || ic <= 0 { fi = fi + 1; continue; } - - // Build intervals for all vars (as in reg alloc) - iv := alloc(vc * 16); - zz : ., mut = 0; - loop { if zz >= vc { break; } w64(iv, zz*16, -1); w64(iv, zz*16+8, -1); zz = zz + 1; } - ii : ., mut = 0; - loop { - if ii >= ic { break; } - inst := ist + ii; - d := iri_dest(inst); s1 := iri_s1(inst); s2 := iri_s2(inst); - vn : ., mut = 0; - va : string, mut = alloc(24); va_cap : int, mut = 3; - if va_cap == 0 { va = alloc(24); va_cap = 3; } - if d >= vs && d < vs+vc { if vn < va_cap { w64(va, vn * 8, d-vs); vn=vn+1; } } - if s1 >= vs && s1 < vs+vc { if vn < va_cap { w64(va, vn * 8, s1-vs); vn=vn+1; } } - if s2 >= vs && s2 < vs+vc { if vn < va_cap { w64(va, vn * 8, s2-vs); vn=vn+1; } } - vi2 : ., mut = 0; - loop { if vi2 >= vn { break; } - lv := r64(va, vi2 * 8); - if r64(iv, lv*16) < 0 { w64(iv, lv*16, ii); } - w64(iv, lv*16+8, ii); - vi2 = vi2 + 1; } - ii = ii + 1; } - - // For each stack var, try to find another to share with - vj1 : ., mut = 0; - loop { if vj1 >= vc { break; } - global_v1 := vs + vj1; - // Skip if this var is in a register (negative encoding in IR means register) - is_reg : ., mut = 0; - // Check first instruction that uses this var - first_ref := r64(iv, vj1*16); - if first_ref >= 0 { - inst_chk := ist + first_ref; - if iri_dest(inst_chk) == global_v1 { - if iri_dest(inst_chk) < 0 { is_reg = 1; } - } else if iri_s1(inst_chk) == global_v1 { - if iri_s1(inst_chk) < 0 { is_reg = 1; } - } else if iri_s2(inst_chk) == global_v1 { - if iri_s2(inst_chk) < 0 { is_reg = 1; } - } - } - if is_reg != 0 { vj1 = vj1 + 1; continue; } - - s1_start := r64(iv, vj1*16); - s1_end := r64(iv, vj1*16+8); - vj2 : ., mut = 0; - loop { if vj2 >= vj1 { break; } - s2_start := r64(iv, vj2*16); - s2_end := r64(iv, vj2*16+8); - // Check disjoint: s1 ends before s2 starts, or s2 ends before s1 starts - if (s1_end < s2_start || s2_end < s1_start) && s1_start >= 0 && s2_start >= 0 { - // Map vj1 to use vj2's slot - w64(g_stack_map, (vs+vj1)*8, vs+vj2); - break; - } - vj2 = vj2 + 1; } - vj1 = vj1 + 1; } - fi = fi + 1; } -} - // ------------------------------------------------------------------ // Common subexpression elimination (CFIR) // ------------------------------------------------------------------ @@ -517,10 +342,10 @@ fn optimize_all() { ast_optimize_body(body); fi = fi + 1; } - // pass_cse skipped — causes GPF in self-compiled binary + // 注(2026-09-07 regalloc 移后端):alloc_registers/pass_stack_share 已迁 + // corearch(src/arch/x86_64/regalloc.cr——.ccr 不再传 REG_ASSIGN/ENT)。 + // 本函数无调用者(main.cr 直调各 pass),保留为历史编排入口。 if g_opt_level >= 2 { pass_cse(); - alloc_registers(); - pass_stack_share(); } } diff --git a/src/compiler/parser.cr b/src/compiler/parser.cr index 980f74db..6d359439 100644 --- a/src/compiler/parser.cr +++ b/src/compiler/parser.cr @@ -43,6 +43,11 @@ fn parse_type() -> int { line := tok_ln(t); col := tok_cl(t); res : ., mut = 0; + // 表示层概念(2026-09-10 语言面收窄裁决 §1):`[T; N]` 的类型构造器身份已退役—— + // 语义归处 = product(N 元聚合)/ 序列接口 + 长度 where(N 长序列)/ F11 图(长度事实, + // 可表达依赖长度)。本语法保留为「内联容量存储」表示提示(映射参数层,与 hw-map 同层; + // 随实例选择生效或退化,非经典范式映射可忽略)。见 + // docs/superpowers/specs/2026-09-10-language-surface-narrowing-design.md §1 if tok_k(t) == T_LBRACKET { advance_tok(); inner := parse_type(); @@ -130,11 +135,14 @@ fn parse_type() -> int { g_diag_count = g_diag_count + 1; res = alloc_node(0, 0, 0, 0, 0, TY_UNIT, 0, line, col); } - // Handle T? desugaring → Option[T] + // R2 P3 Task 4:`T?` 的目标形态 = **EXPR_OPTIONAL**(内层类型节点入 a 槽)。 + // 旧形态(T? → EXPR_GENERIC_APPLY(Option, T))要求「内建 Option 名」在符号表里被注册, + // 正是本任务退役的做法(spec §5.4);且旧形态无法表达 `T? = T ∪ null`(GenericApply 行 + // 在桥接侧是不展开的 AK_NAMED 原子)。新形态**零名字依赖**(不再 str_intern("Option"), + // 见 test_optional.py 的行数对照用例)。 if check(T_QUESTION) { advance_tok(); - option_ni := str_intern("Option"); - res = alloc_node(EXPR_GENERIC_APPLY, option_ni, res, 1, 0, 0, 0, line, col); + res = alloc_node(EXPR_OPTIONAL, res, 0, 0, 0, 0, 0, line, col); } return res; } @@ -389,33 +397,20 @@ fn is_upper_first(s: string) -> bool { fn parse_primary() -> int { t := cur_tok(); - if tok_k(t) == T_INT || (tok_k(t) >= T_INT_I8 && tok_k(t) <= T_INT_U64) { + if tok_k(t) == T_INT { advance_tok(); - kn := tok_k(t); - w : ., mut = 0; - if kn == T_INT_I8 { w = W_I8; } - else if kn == T_INT_I16 { w = W_I16; } - else if kn == T_INT_I32 { w = W_I32; } - else if kn == T_INT_I64 { w = W_I64; } - else if kn == T_INT_U8 { w = W_U8; } - else if kn == T_INT_U16 { w = W_U16; } - else if kn == T_INT_U32 { w = W_U32; } - else if kn == T_INT_U64 { w = W_U64; } - return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, w, tok_ln(t), tok_cl(t)); - } - if tok_k(t) == T_DEX || tok_k(t) == T_FLOAT_F32 || tok_k(t) == T_FLOAT_F64 { + return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)); + } + if tok_k(t) == T_DEX { advance_tok(); - kn := tok_k(t); - w : ., mut = 0; - if kn == T_FLOAT_F32 { w = W_F32; } - else if kn == T_FLOAT_F64 { w = W_F64; } // 节点字段(数值迁移 Task 4):a = binary64 位模式(apx 快路径字面量表示, // 由 lexer 存入 token 的 lexeme 槽的数字串还原);int_val = 定点缩放整数 - // (精确表示,默认路径);data = 宽度标注(_f32/_f64,保留) + // (精确表示,默认路径)。宽度后缀退役(2026-09-10 语言面收窄 §2):data 槽 + // 不再承载宽度标注,恒 0。 bits : int = 0; tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME); // 词素串下标(-1 = 无) if tl >= 0 { bits = str_to_f64_bits(istr_get(tl)); } - return alloc_node(EXPR_DEX, bits, 0, 0, tok_iv(t), TY_DEX, w, tok_ln(t), tok_cl(t)); + return alloc_node(EXPR_DEX, bits, 0, 0, tok_iv(t), TY_DEX, 0, tok_ln(t), tok_cl(t)); } if tok_k(t) == T_STRING { advance_tok(); @@ -431,11 +426,33 @@ fn parse_primary() -> int { advance_tok(); ni := str_intern("Some"); if check(T_LPAREN) { - // Parse Some(expr) + // Parse Some(expr[, expr…]) —— R2 P3 Task 4 修复:**实参须照通用枚举构造器分支 + // (下方 parse_postfix 的路径)建 EXPR_ARG 链**。旧实现把值节点直接放 b 槽, + // 而 checker/ir_gen 的 EXPR_ENUM_CONSTRUCTOR 消费点按 EXPR_ARG 链走 + // (`an := ast_b(node); ast_a(an); an = ast_b(an)`)⇒ 旧形态令该链读进值节点自身 + // 的 a/b 槽(节点 0 当实参、按 ast_b 前行)——实测(本任务开工前): + // `enum Option[T] { None, Some(T) }` + `x: int? = Some(5)` **checker 死循环** + // (`corec check` rc=124 超时)。链一修,消费点契约恢复(parser.cr:300-312 同款)。 advance_tok(); - val := parse_expr(); + af : ., mut = -1; + ac : ., mut = 0; + if !check(T_RPAREN) { + first_expr := parse_expr(); + af = alloc_node(EXPR_ARG, first_expr, -1, 0, 0, 0, 0, tok_ln(t), tok_cl(t)); + ac = 1; + prev_arg : ., mut = af; + loop { + if !check(T_COMMA) { break; } + advance_tok(); + next_expr := parse_expr(); + new_arg := alloc_node(EXPR_ARG, next_expr, -1, 0, 0, 0, 0, tok_ln(t), tok_cl(t)); + ast_set_b(prev_arg, new_arg); + prev_arg = new_arg; + ac = ac + 1; + } + } advance_tok(); // consume ) - return alloc_node(EXPR_ENUM_CONSTRUCTOR, ni, val, 1, 0, 0, 0, tok_ln(t), tok_cl(t)); + return alloc_node(EXPR_ENUM_CONSTRUCTOR, ni, af, ac, 0, 0, 0, tok_ln(t), tok_cl(t)); } // Some without parens → treat as identifier (will be resolved by uppercase → enum constructor) return alloc_node(EXPR_IDENT, 0, 0, 0, ni, 0, 0, tok_ln(t), tok_cl(t)); @@ -457,20 +474,55 @@ fn parse_primary() -> int { ni := str_intern(name); if check(T_LBRACE) && g_parse_no_struct_literal == 0 { advance_tok(); - ff := -1; + // 契约(F5 修复,与元组分支同款):EXPR_STRUCT = a=类型名 idx、b=首 wrapper、c=字段数; + // wrapper 在 g_ast 中**连续**(kind=EXPR_NONE,wrapper.a=该字段的值节点)→ 消费者 + // 经 EXPR_NONE 前向解引用取值。字段值节点自身对复合表达式(调用 / 字面量 / 嵌套聚合) + // **不连续**(子树自占多槽),故必须分两趟:先解析全部字段值,再统建连续 wrapper。 + // 旧写法「逐值后随建 wrapper」交错分配:复合值子树夹在相邻 wrapper 之间 ⇒ 第 2 个 + // 起字段槽位整体错位,读到子节点(实测 P{a:11, b:g()} 的 b 静默得 0,rc=0)。 + // TODO #29 ①:wrapper.b = 字段名 idx(**名字绑定**,-1 = 无名字信息)——旧代码取 + // `fni` 后从未写入 ⇒ 值按声明位序绑定(P{b:11,a:22} 静默得 a=11)。名字随 wrapper + // 同行(与值并列的平行表,两趟结构不变,仍无交错分配)。 + cap : ., mut = 8; + vals : string, mut = alloc(cap * 8); + names : string, mut = alloc(cap * 8); fc : ., mut = 0; loop { - if check(T_RBRACE) { break; } + // EOF 护栏(TODO #16 根因面):本循环只认 `}`,而 advance_tok 在 EOF 是空操作 + // ⇒ 解析失步至 EOF 后自旋,每轮分配 AST 节点直至 OOM(alloc 失败返回 NULL → + // grow_ast 向 NULL 拷贝 SIGSEGV)。同族 6 处循环统一补 EOF 退出。 + if check(T_RBRACE) || check(T_EOF) { break; } ft := advance_tok(); fni := str_intern(tok_lx(ft)); advance_tok(); - fv := parse_expr(); - ast_alloc(0, fv, 0, 0, 0, 0, 0, tok_ln(ft), tok_cl(ft)); - if fc == 0 { ff = g_ast_count - 1; } + if fc >= cap { + ncap := cap * 2; + nv := alloc(ncap * 8); + _dyncpy(vals, cap * 8, nv); + vals = nv; + nn := alloc(ncap * 8); + _dyncpy(names, cap * 8, nn); + names = nn; + cap = ncap; + } + w64(vals, fc * 8, parse_expr()); // 字段值(子树自占若干槽) + w64(names, fc * 8, fni); // 字段名 idx(① 名字绑定) fc = fc + 1; if check(T_COMMA) { advance_tok(); } } advance_tok(); + ff : ., mut = -1; + fi2 : ., mut = 0; + loop { + if fi2 >= fc { break; } + vn := r64(vals, fi2 * 8); + ln : ., mut = tok_ln(t); + cl : ., mut = tok_cl(t); + if vn >= 0 { ln = ast_line(vn); cl = ast_col(vn); } + wl := ast_alloc(EXPR_NONE, vn, r64(names, fi2 * 8), 0, 0, 0, 0, ln, cl); + if fi2 == 0 { ff = wl; } + fi2 = fi2 + 1; + } return alloc_node(EXPR_STRUCT, ni, ff, fc, 0, 0, 0, tok_ln(t), tok_cl(t)); } return alloc_node(EXPR_IDENT, 0, 0, 0, ni, 0, 0, tok_ln(t), tok_cl(t)); @@ -483,15 +535,42 @@ fn parse_primary() -> int { g_parse_no_struct_literal = saved_nsl; if check(T_COMMA) { // Tuple: (e1, e2, ...) - ef := e; + // 契约(F5 修复):EXPR_TUPLE = a=首 wrapper、b=元素个数;wrapper 在 g_ast 中 + // **连续**,每个 wrapper(kind=EXPR_NONE)的 a = 该元素的值节点 → 消费者经 + // `ast_a(wrapper + i)` 取元素值节点。元素值节点本身对复合表达式**不连续** + // (复合元素子树自占多槽),故必须分两趟:先解析全部元素值,再统建连续 wrapper。 + // 注:不得照 struct 字面量分支(本文件 T_IDENT+T_LBRACE 段)的「逐值后随建 + // wrapper」交错顺序——复合值会插在相邻 wrapper 之间致其不连续,同属本根因。 + cap : ., mut = 8; + vals : string, mut = alloc(cap * 8); + w64(vals, 0, e); ec : ., mut = 1; loop { advance_tok(); // consume comma - parse_expr(); // next element (stored in consecutive g_ast slots) + if ec >= cap { + ncap := cap * 2; + nv := alloc(ncap * 8); + _dyncpy(vals, cap * 8, nv); + vals = nv; + cap = ncap; + } + w64(vals, ec * 8, parse_expr()); // 元素值(子树自占若干槽) ec = ec + 1; if !check(T_COMMA) { break; } } advance_tok(); // consume ) + ef : ., mut = -1; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + vn := r64(vals, ei * 8); + ln : ., mut = tok_ln(t); + cl : ., mut = tok_cl(t); + if vn >= 0 { ln = ast_line(vn); cl = ast_col(vn); } + wl := ast_alloc(EXPR_NONE, vn, 0, 0, 0, 0, 0, ln, cl); + if ei == 0 { ef = wl; } + ei = ei + 1; + } return alloc_node(EXPR_TUPLE, ef, ec, 0, 0, 0, 0, tok_ln(t), tok_cl(t)); } advance_tok(); @@ -558,21 +637,36 @@ fn parse_primary() -> int { } if tok_k(t) == T_LBRACKET { advance_tok(); - ef := -1; + // 契约(F5 修复,与元组/struct 字面量分支同款):**字面量形** EXPR_ARRAY = + // a=首 wrapper、b=元素个数;wrapper 在 g_ast 中**连续**(kind=EXPR_NONE,wrapper.a=元素值 + // 节点)→ 消费者经 EXPR_NONE 前向解引用取值。元素值节点自身对复合表达式(嵌套字面量 / + // 调用 / 下标)**不连续**(子树自占多槽),故必须两趟:先解析全部元素值,再统建 wrapper。 + // 旧交错写法下第 2 个元素起槽位整体错位(实测 [[1,2],[3,4]] 的 a[1] 被读成扁平 3 → + // a[1][0] SIGSEGV 139;且 [[1,2],[3,4]] 与 [5,6] 类型互赋静默通过 = soundness 漏放)。 + // 区分:**类型形** [T; N] / [T] 由 parse_type 产出(a=内层类型节点、b=0、int_val=尺寸), + // 不经本分支,不受本契约影响。 + cap : ., mut = 8; + vals : string, mut = alloc(cap * 8); ec : ., mut = 0; if !check(T_RBRACKET) { - ef = parse_expr(); + w64(vals, 0, parse_expr()); // 元素值(子树自占若干槽) ec = 1; if check(T_SEMI) { - // Repeat array: [value; count] + // Repeat array: [value; count] —— 值节点只解析一次,其后仅**共享**同一值节点 + // 追加 wrapper(旧写法浅拷贝根节点 N-1 份;共享值节点语义等价,且不再复制多槽子树)。 advance_tok(); ct := advance_tok(); cnt : ., mut = tok_iv(ct); - // Replicate element AST nodes + if cnt > cap { + nv := alloc(cnt * 8); + _dyncpy(vals, cap * 8, nv); + vals = nv; + cap = cnt; + } ri : ., mut = 1; loop { if ri >= cnt { break; } - ast_alloc(ast_kind(ef), ast_a(ef), ast_b(ef), ast_c(ef), ast_int_val(ef), ast_type_val(ef), ast_data(ef), ast_line(ef), ast_col(ef)); + w64(vals, ri * 8, r64(vals, 0)); ri = ri + 1; } ec = cnt; @@ -580,12 +674,31 @@ fn parse_primary() -> int { loop { if !check(T_COMMA) { break; } advance_tok(); - parse_expr(); // parse remaining elements + if ec >= cap { + ncap := cap * 2; + nv := alloc(ncap * 8); + _dyncpy(vals, cap * 8, nv); + vals = nv; + cap = ncap; + } + w64(vals, ec * 8, parse_expr()); // 元素值(子树自占若干槽) ec = ec + 1; } } } advance_tok(); + ef : ., mut = -1; + ei2 : ., mut = 0; + loop { + if ei2 >= ec { break; } + vn := r64(vals, ei2 * 8); + ln : ., mut = tok_ln(t); + cl : ., mut = tok_cl(t); + if vn >= 0 { ln = ast_line(vn); cl = ast_col(vn); } + wl := ast_alloc(EXPR_NONE, vn, 0, 0, 0, 0, 0, ln, cl); + if ei2 == 0 { ef = wl; } + ei2 = ei2 + 1; + } return alloc_node(EXPR_ARRAY, ef, ec, 0, 0, 0, 0, tok_ln(t), tok_cl(t)); } add_error("Unexpected token in expression"); @@ -766,6 +879,28 @@ fn parse_new_var_decl() -> int { return first_node; } +// 跳过整段嵌套函数声明(P021 已报错,仅错误恢复路径):`fn name(params) -> T {body}` +// 按花括号配平跳过整段;`= expr;` 形式(含 extern 无体形式)遇分号收尾。起始 token 可为 +// `pub`。花括号配平从函数体的 `{` 起算(depth 0→1),体内嵌套块/struct 字面量的花括号 +// 成对计入,不会在 depth 归零前误判结束。 +fn skip_nested_fn() { + depth : ., mut = 0; + seen_brace : ., mut = 0; + loop { + k := tok_k(cur_tok()); + if k == T_EOF { return; } + if k == T_LBRACE { depth = depth + 1; seen_brace = 1; advance_tok(); continue; } + if k == T_RBRACE { + advance_tok(); + depth = depth - 1; + if depth <= 0 { return; } + continue; + } + if k == T_SEMI && seen_brace == 0 { advance_tok(); return; } + advance_tok(); + } +} + fn parse_stmt() -> int { // Drain batch extras from previous call if g_extra_let_count > 0 { @@ -774,6 +909,25 @@ fn parse_stmt() -> int { } t := cur_tok(); + // 嵌套 `fn`/`flow` 声明不属语言面——grammar/core.ebnf 的 Statement 不含 FunctionDecl + // (函数声明仅顶层 TopLevelDecl),bootstrap 参考实现亦以 positioned error 拒绝。 + // 修复前此处落回 parse_primary 的「Unexpected token in expression」通用兜底:只消费 + // `fn` 一个 token,解析失步后 `IDENT {` 进入 struct 字面量循环并把外层 `}` 当字段吃掉, + // 至 EOF 后因该循环只认 `}` 且 advance_tok 在 EOF 是空操作而自旋——每轮分配 AST 节点, + // 直到 bump allocator 耗尽返回 NULL、grow_ast 向 NULL 拷贝(rc=139 SIGSEGV,TODO #16)。 + // 现显式报 P021 并整段跳过该声明:错误定位到声明处,且后续语句恢复正常解析。 + if tok_k(t) == T_FN || tok_k(t) == T_FLOW + || (tok_k(t) == T_PUB && (tok_k(t + 1) == T_FN || tok_k(t + 1) == T_FLOW)) { + fnt : ., mut = t; + if tok_k(fnt) == T_PUB { fnt = fnt + 1; } + msg : ., mut = "Nested function declaration is not supported; declare it at top level"; + if tok_k(fnt + 1) == T_IDENT { + msg = "Nested function declaration '" + tok_lx(fnt + 1) + "' is not supported; declare it at top level"; + } + check_error(EC_P_NESTED_FN, msg, tok_ln(fnt), tok_cl(fnt)); + skip_nested_fn(); + return 0; + } // New variable declaration syntax if tok_k(t) == T_IDENT && is_new_var_decl() { return parse_new_var_decl(); @@ -894,19 +1048,9 @@ fn parse_pattern() -> int { advance_tok(); return alloc_node(EXPR_WILDCARD, 0, 0, 0, 0, 0, 0, tok_ln(t), tok_cl(t)); } - if tok_k(t) == T_INT || (tok_k(t) >= T_INT_I8 && tok_k(t) <= T_INT_U64) { + if tok_k(t) == T_INT { advance_tok(); - kn := tok_k(t); - w : ., mut = 0; - if kn == T_INT_I8 { w = W_I8; } - else if kn == T_INT_I16 { w = W_I16; } - else if kn == T_INT_I32 { w = W_I32; } - else if kn == T_INT_I64 { w = W_I64; } - else if kn == T_INT_U8 { w = W_U8; } - else if kn == T_INT_U16 { w = W_U16; } - else if kn == T_INT_U32 { w = W_U32; } - else if kn == T_INT_U64 { w = W_U64; } - return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, w, tok_ln(t), tok_cl(t)); + return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)); } if tok_k(t) == T_STRING { advance_tok(); @@ -973,21 +1117,42 @@ fn parse_pattern() -> int { } if check(T_LBRACE) { // Struct pattern: Name { field = pat, ... } + // 契约(F5 修复,与 struct 字面量分支同款):EXPR_STRUCTPAT = a=名字 idx、b=首 wrapper、 + // c=字段数;wrapper 在 g_ast 中**连续**(kind=EXPR_NONE,wrapper.a=子模式节点)。 + // 复合子模式(嵌套 struct/enum/tuple 模式)子树占多槽,故两趟:先全部解析、后统建 wrapper。 advance_tok(); - ff := -1; + cap : ., mut = 8; + vals : string, mut = alloc(cap * 8); fc : ., mut = 0; loop { - if check(T_RBRACE) { break; } + if check(T_RBRACE) || check(T_EOF) { break; } ft := advance_tok(); fni := str_intern(tok_lx(ft)); advance_tok(); // = - fp := parse_pattern(); - ast_alloc(0, fp, 0, 0, 0, 0, 0, tok_ln(ft), tok_cl(ft)); - if fc == 0 { ff = g_ast_count - 1; } + if fc >= cap { + ncap := cap * 2; + nv := alloc(ncap * 8); + _dyncpy(vals, cap * 8, nv); + vals = nv; + cap = ncap; + } + w64(vals, fc * 8, parse_pattern()); fc = fc + 1; if check(T_COMMA) { advance_tok(); } } advance_tok(); + ff : ., mut = -1; + fi2 : ., mut = 0; + loop { + if fi2 >= fc { break; } + vn := r64(vals, fi2 * 8); + ln : ., mut = tok_ln(t); + cl : ., mut = tok_cl(t); + if vn >= 0 { ln = ast_line(vn); cl = ast_col(vn); } + wl := ast_alloc(EXPR_NONE, vn, 0, 0, 0, 0, 0, ln, cl); + if fi2 == 0 { ff = wl; } + fi2 = fi2 + 1; + } return alloc_node(EXPR_STRUCTPAT, ni, ff, fc, 0, 0, 0, tok_ln(t), tok_cl(t)); } if is_upper_first(name) { @@ -1081,6 +1246,53 @@ fn save_func_gen_constrs(fi: int, constrs: string, count: int) { } } +// ─── R2 P3 Task 5(Step 2):结构/枚举泛型约束登记(**索引空间分家** —— 键 = row*MAX_GENERICS+gi, +// 与函数侧 g_generic_constr 不同缓冲;见 globals.cr 表注)─── +// **空槽预填 -1**(本批实测缺陷修复):表是**稀疏**填的(只写有约束的槽),而缓冲零初值 ⇒ +// 高水位区内的「本行未写过的槽」读出 0——0 是**合法的名字 ni**(首个驻留串)⇒ 读取方 +// `c_ni >= 0` 会把空槽当有效约束名(把首个驻留串当接口/类型名查表)= 凭空约束。 +// 故本三函数一律先整行(MAX_GENERICS 槽)写 -1 再覆盖有约束的槽——行的语义 = 「本行全部 +// 形参位都有明确值」。读取器的越界闸(idx ≥ count)仍保留(整行未登记时的兜底)。 +fn save_struct_gen_constrs(si: int, constrs: string, count: int) { + grow_sgen_constr(si * MAX_GENERICS + MAX_GENERICS); + if si * MAX_GENERICS + MAX_GENERICS > g_sgen_constr_count { g_sgen_constr_count = si * MAX_GENERICS + MAX_GENERICS; } + zi : ., mut = 0; + loop { + if zi >= MAX_GENERICS { break; } + w64(g_sgen_constr, (si * MAX_GENERICS + zi) * 8, -1); + zi = zi + 1; + } + gi : ., mut = 0; + loop { + if gi >= count { break; } + if gi >= MAX_GENERICS { break; } + if r64(constrs, gi * 8) >= 0 { + w64(g_sgen_constr, (si * MAX_GENERICS + gi) * 8, r64(constrs, gi * 8)); + } + gi = gi + 1; + } +} + +fn save_enum_gen_constrs(ei: int, constrs: string, count: int) { + grow_egen_constr(ei * MAX_GENERICS + MAX_GENERICS); + if ei * MAX_GENERICS + MAX_GENERICS > g_egen_constr_count { g_egen_constr_count = ei * MAX_GENERICS + MAX_GENERICS; } + zi : ., mut = 0; + loop { + if zi >= MAX_GENERICS { break; } + w64(g_egen_constr, (ei * MAX_GENERICS + zi) * 8, -1); + zi = zi + 1; + } + gi : ., mut = 0; + loop { + if gi >= count { break; } + if gi >= MAX_GENERICS { break; } + if r64(constrs, gi * 8) >= 0 { + w64(g_egen_constr, (ei * MAX_GENERICS + gi) * 8, r64(constrs, gi * 8)); + } + gi = gi + 1; + } +} + fn add_func(name: string, pc: int, rt: int, an: int) -> int { idx := g_func_count; grow_funcs(idx + 1); @@ -1106,6 +1318,8 @@ fn add_struct(name: string) -> int { zi = zi + 1; } w64(g_structs, base + OFF_SI_NAME, ni); + // 容量批 T3:字段区基址 = 侧表高水位(字段写入点按 base + 下标寻址;体尾 si_commit_fields 提交) + si_set_field_base(idx, g_si_f_used); g_struct_count = idx + 1; return idx; } @@ -1123,6 +1337,8 @@ fn add_enum(name: string) -> int { zi = zi + 1; } w64(g_enums, base + OFF_EI_NAME, ni); + // 容量批 T3:变体区基址 = 侧表高水位(体尾 ei_commit_variants 提交) + ei_set_variant_base(idx, g_ei_v_used); g_enum_count = idx + 1; return idx; } @@ -1233,10 +1449,20 @@ fn parse_body(fn_name: string, fn_ni: int, fn_line: int, fn_col: int, hotpatch_v } fn_node := alloc_node(EXPR_FN, fn_ni, pf, pc, rtv + hotpatch_ver * 256, rt, body, fn_line, fn_col); + // 形参上限硬错(防御面,TODO #8):FuncInfo.param_types 是定长内嵌槽区 + // (MAX_FN_PARAMS 槽),超限签名无法表示 ⇒ 拒绝编译(rc=1)而非截断/越界写。 + // 形参表容纳不下时**必须**在这条路径上停住:静默越界写曾踩 ast_node 致 + // name_idx/param_count 归零 + TF01 误归 + rc=0 产物崩。 + if pc > MAX_FN_PARAMS { + check_error(EC_P_TOO_MANY_PARAMS, + "Function has too many parameters (" + int_str(pc) + " > " + int_str(MAX_FN_PARAMS) + ")", + fn_line, fn_col); + } fi := add_func(fn_name, pc, rtv, fn_node); if fi >= 0 && gc > 0 { save_func_generics(fi, gnames, gc); save_func_gen_constrs(fi, gconstrs, gc); } - // Store param types in FuncInfo - if fi >= 0 { pstore_i : ., mut = 0; pstore_n : ., mut = pf; + // Store param types in FuncInfo(pc > MAX_FN_PARAMS 已被上方硬错拒绝; + // 此处再显式跳过 + fi_set_param_type 内槽区护栏 = 双保险,未来新调用点亦不越过界) + if fi >= 0 && pc <= MAX_FN_PARAMS { pstore_i : ., mut = 0; pstore_n : ., mut = pf; loop { if pstore_i >= pc { break; } if pstore_n < 0 { break; } if ast_kind(pstore_n) == EXPR_PARAM { fi_set_param_type(fi, pstore_i, ast_type_val(pstore_n)); @@ -1378,9 +1604,9 @@ fn parse_declaration() { name := tok_lx(nt); sg_names : string, mut; sg_names_cap : int, mut; sg_names = alloc(64 * 8); sg_names_cap = 64; - sg_dummy : string, mut; sg_dummy_cap : int, mut; - sg_dummy = alloc(64 * 8); sg_dummy_cap = 64; - sg_count := parse_generics_into(sg_names, sg_dummy); + sg_constrs : string, mut; sg_constrs_cap : int, mut; + sg_constrs = alloc(64 * 8); sg_constrs_cap = 64; + sg_count := parse_generics_into(sg_names, sg_constrs); advance_tok(); // { si := add_struct(name); @@ -1393,21 +1619,29 @@ fn parse_declaration() { w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + sgi * 8, str_intern(r64(sg_names, sgi * 8))); sgi = sgi + 1; } + // R2 P3 Task 5(Step 2):约束**不再丢弃**(旧态 = 写进 dummy 缓冲后随作用域 + // 消失 ⇒ `struct Box[T: I]` 静默无约束)。登记到 struct 侧表,实例化点 + // (res_type_node 的 EXPR_GENERIC_APPLY 分支)消费。 + save_struct_gen_constrs(si, sg_constrs, sg_count); } fc : ., mut = 0; loop { - if check(T_RBRACE) { break; } + if check(T_RBRACE) || check(T_EOF) { break; } ft := advance_tok(); fn2 := tok_lx(ft); - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_NAMES + fc * 8, str_intern(fn2)); + fni := str_intern(fn2); + // 容量批 T3(裁-CAP-2 (a)):字段**无硬上限**(侧表;第 17 字段起照常写入) + // ⇒ 旧 P023 闸与槽上限分支一并删除(不再存在越界写对象)。 + si_set_field_name(si, fc, fni); advance_tok(); fty := parse_type(); - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_TYPES + fc * 8, unpack_type(fty)); - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_TYPE_NODES + fc * 8, fty); + si_set_field_type(si, fc, unpack_type(fty)); + si_set_field_type_node(si, fc, fty); fc = fc + 1; if check(T_COMMA) { advance_tok(); } } w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc); + si_commit_fields(si, fc); // 提交字段区(高水位 = base + fc) } advance_tok(); return; @@ -1420,9 +1654,9 @@ fn parse_declaration() { name := tok_lx(nt); eg_names : string, mut; eg_names_cap : int, mut; eg_names = alloc(64 * 8); eg_names_cap = 64; - eg_dummy : string, mut; eg_dummy_cap : int, mut; - eg_dummy = alloc(64 * 8); eg_dummy_cap = 64; - eg_count := parse_generics_into(eg_names, eg_dummy); + eg_constrs : string, mut; eg_constrs_cap : int, mut; + eg_constrs = alloc(64 * 8); eg_constrs_cap = 64; + eg_count := parse_generics_into(eg_names, eg_constrs); advance_tok(); ei := add_enum(name); @@ -1435,31 +1669,41 @@ fn parse_declaration() { w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + egi * 8, str_intern(r64(eg_names, egi * 8))); egi = egi + 1; } + // R2 P3 Task 5(Step 2):同 struct 分支——枚举泛型约束登记(旧态同款丢弃) + save_enum_gen_constrs(ei, eg_constrs, eg_count); } vc : ., mut = 0; loop { - if check(T_RBRACE) { break; } + if check(T_RBRACE) || check(T_EOF) { break; } vt := advance_tok(); vname := tok_lx(vt); - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vc * OFF_EV_SIZE + OFF_EV_NAME, str_intern(vname)); + vni := str_intern(vname); + // 容量批 T3(裁-CAP-2 (a)):变体**无硬上限**(侧表)⇒ 旧 P022 闸与槽上限 + // 分支一并删除(不再存在越界写对象) + ei_set_variant_name(ei, vc, vni); tc : ., mut = 0; if check(T_LPAREN) { advance_tok(); loop { if check(T_RPAREN) { break; } fty := parse_type(); - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vc * OFF_EV_SIZE + OFF_EV_TYPES + tc * 8, unpack_type(fty)); + ei_set_variant_type(ei, vc, tc, unpack_type(fty)); + // R2 P3 Task 4(T0 交接 ①):载荷类型**节点**随裸码同写(照 struct 的 + // OFF_SI_FIELD_TYPE_NODES 先例)——裸码把非基型载荷塌缩成 0 = TY_INT, + // 节点是载荷面(泛型形参代入 / 满足判定)的唯一忠实来源。 + ei_set_variant_type_node(ei, vc, tc, fty); tc = tc + 1; if !check(T_COMMA) { break; } advance_tok(); } advance_tok(); } - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANTS + vc * OFF_EV_SIZE + OFF_EV_TYPE_COUNT, tc); + ei_set_variant_type_count(ei, vc, tc); vc = vc + 1; if check(T_COMMA) { advance_tok(); } } w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc); + ei_commit_variants(ei, vc); // 提交变体区(高水位 = base + vc) } advance_tok(); return; @@ -1489,7 +1733,7 @@ fn parse_declaration() { method_count : ., mut = 0; loop { - if check(T_RBRACE) { break; } + if check(T_RBRACE) || check(T_EOF) { break; } if check(T_FN) { advance_tok(); // fn mt := advance_tok(); // method name @@ -1502,23 +1746,36 @@ fn parse_declaration() { param_tis = alloc(128 * 8); param_tis_cap = 128; pi2 : ., mut = 0; loop { if pi2 >= 8 { break; } w64(param_tis, pi2 * 8, TY_UNIT); pi2 = pi2 + 1; } + // R2 P3b Task 6(Step 1):签名**类型节点**槽(与裸码槽并行写,照 struct/enum + // 的 code+node 双写先例)。self 接收者槽恒无节点(两侧同约定,见 OFF_IFM_SELF_MODE)。 + param_nodes : string, mut; param_nodes_cap : int, mut; + param_nodes = alloc(128 * 8); param_nodes_cap = 128; + pni2 : ., mut = 0; + loop { if pni2 >= 8 { break; } w64(param_nodes, pni2 * 8, -1); pni2 = pni2 + 1; } + self_mode : ., mut = 0; if !check(T_RPAREN) { loop { fst := cur_tok(); // Handle &self / &mut self / self if tok_k(fst) == T_AMPERSAND || tok_k(fst) == T_SELF { + self_mode_t : ., mut = 1; // self if tok_k(fst) == T_AMPERSAND { advance_tok(); // & - if check(T_MUT) { advance_tok(); } // mut + self_mode_t = 2; // &self + if check(T_MUT) { advance_tok(); self_mode_t = 3; } // &mut self } nt2 := advance_tok(); // self if pc < 8 { w64(param_tis, pc * 8, 0); } // match function's default for &self + if pc == 0 { self_mode = self_mode_t; } // 首参 = 接收者才记模式 pc = pc + 1; } else { advance_tok(); // param name advance_tok(); // : ptype := parse_type(); - if pc < 8 { w64(param_tis, pc * 8, unpack_type(ptype)); } + if pc < 8 { + w64(param_tis, pc * 8, unpack_type(ptype)); + w64(param_nodes, pc * 8, ptype); + } pc = pc + 1; } if !check(T_COMMA) { break; } @@ -1529,15 +1786,19 @@ fn parse_declaration() { // Parse return type ret_ti : ., mut = TY_UNIT; + ret_node : ., mut = -1; if check(T_ARROW) { advance_tok(); - ret_node := parse_type(); - ret_ti = unpack_type(ret_node); + rn2 := parse_type(); + ret_node = rn2; + ret_ti = unpack_type(rn2); } advance_tok(); // ; // Store method in interface entry (with overflow checks) - if method_count >= 16 { + // R2 P3b Task 6(Step 1):上限**显式登记保留**(单源常量 MAX_IFACE_METHODS)—— + // 超限 = 硬错 rc=1(非静默截断),解除面(侧表迁移)见 dyn_arr.cr 常量注。 + if method_count >= MAX_IFACE_METHODS { grow_diags(g_diag_count + 1); w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_FIELD_SYNTAX); store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "interface '" + iface_name + "' exceeds max 16 methods"); @@ -1549,9 +1810,12 @@ fn parse_declaration() { w64(g_ifaces, mbase + OFF_IFM_NAME, method_ni); w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc); w64(g_ifaces, mbase + OFF_IFM_RET_TI, ret_ti); + w64(g_ifaces, mbase + OFF_IFM_RET_NODE, ret_node); + w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, self_mode); pj : ., mut = 0; loop { if pj >= 8 || pj >= pc { break; } w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, r64(param_tis, pj * 8)); + w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, r64(param_nodes, pj * 8)); pj = pj + 1; } if pc > 8 { grow_diags(g_diag_count + 1); @@ -1589,7 +1853,7 @@ fn parse_declaration() { } advance_tok(); // { loop { - if check(T_RBRACE) { break; } + if check(T_RBRACE) || check(T_EOF) { break; } if check(T_FN) { ft := advance_tok(); method_nt := advance_tok(); diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index 085ff619..43943c86 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -54,7 +54,10 @@ fn provenance_verify_func(nstart: int, ncount: int) { loop { if ni >= nstart + ncount { break; } op := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE); s1 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1); - width : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK); + // R2 P5 Task 2(D19):本处读的是**访问宽度**(字节数)——单槽化前它住在混用 + // tk 槽,现居**辅码**槽(OFF_DF_AUX)。值域/语义零变化(仅 IR_DEREF/IR_STORE_PTR + // 消费;两 op 均属辅码面)。读 40 槽(类型项引用)会把项行号当宽度 = 静默错值。 + width : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX); if (op == IR_DEREF || op == IR_STORE_PTR) && s1 >= 0 { // Skip checks in unsafe blocks diff --git a/src/compiler/purity_selftest.cr b/src/compiler/purity_selftest.cr new file mode 100644 index 00000000..93932e39 --- /dev/null +++ b/src/compiler/purity_selftest.cr @@ -0,0 +1,299 @@ +// === purity_selftest.cr === +// 效应/纯度修正 Task 1(P0 插队批):真纯度计算自测通道(`corec selftest-purity`)。 +// +// 判据:每例一行 PASS/FAIL,末行 "N/M purity cases passed",rc=0 = 全过。 +// +// 与 type_selftest.cr 的差别:纯度是**跨函数**事实(IR 体扫描 + 调用图不动点), +// 必须走完整前端 + IR 生成 ⇒ 本通道自建最小管线(不读文件、不产产物),且断言 +// 的是 IR 生成后由 compute_all_purity 写回的值(时点见 checker.cr 头注)。 +// 语言面注意:本语言无三元运算符。 + +fn ps_check(name: string, got: int, want: int) -> int { + if got == want { + print("PASS "); println(name); + return 0; + } + print("FAIL "); print(name); + print(": got "); print(int_str(got)); + print(" want "); println(int_str(want)); + return 1; +} + +// 单次完整前端 + IR 生成(自测用;与 main.cr 的 run 路径同序,无文件缓存)。 +fn ps_compile(src: string) -> int { + reset_frontend_state(); + g_source = src; + g_error_count = 0; + g_source_dir = ""; + tokenize(g_source); + res_imports(); + parse_all(); + if g_error_count > 0 { print_parse_errors(); return 1; } + if g_diag_count > 0 { print_diagnostics(); return 1; } + check_all(); + if g_diag_count > 0 { print_diagnostics(); return 1; } + ir_gen_all(); + return 0; +} + +// 按名查纯度(-1 = 无此函数——与 0/1 区分,防「查不到」被当「不纯」混过) +fn ps_purity(name: string) -> int { + return fi_ispure_of(str_intern(name)); +} + +// ── state 链面(Task 1 的落点:纯度是手段,「调用进链」才是目的)────────── +// 链 = kind=1 的 DF 边(dataflow.cr df_replay_state_chain 重放产出)。以下三条 +// 是 Task 1 的**行为级**判据:不检查实现细节,只检查成品图的边集语义—— +// ① 效应调用必须被链穿过(前 store → call → 后 store);② 可证纯调用必须 +// 不被链触及;③ 循环终止依赖必须存在(重放复刻 sg_pop 规则的守门——该规则 +// 从「生成期即时连接」迁到「生成后重放」,判据不可省)。 + +// 源函数名 → IR 函数序号(-1 = 无)。IR 表名 = 源函数名(monomorph 实例为 +// 改写名,本通道只用非泛型函数名)。 +fn ps_ir_index_of(name: string) -> int { + ni := str_intern(name); + irf : ., mut = 0; + loop { + if irf >= g_ir_func_count { return -1; } + if r64(g_ir_func_name_idx, irf * 8) == ni { return irf; } + irf = irf + 1; + } + return -1; +} + +// 函数 irf 体内「调 callee」的 DF 节点序号(-1 = 无) +fn ps_call_node(irf: int, callee: string) -> int { + cni := str_intern(callee); + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + n : ., mut = start; + loop { + if n >= start + cnt { return -1; } + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL { + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni { return n; } + } + n = n + 1; + } + return -1; +} + +// 函数 irf 体内「调 callee」的 DF 节点总数(非空断言用:防「查不到 ⇒ 恒 0」 +// 把「零入链」退化断言混过) +fn ps_count_call_nodes(irf: int, callee: string) -> int { + if irf < 0 { return -1; } + cni := str_intern(callee); + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + r : ., mut = 0; + n : ., mut = start; + loop { + if n >= start + cnt { return r; } + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL { + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni { r = r + 1; } + } + n = n + 1; + } + return r; +} + +// 函数 irf 体内首个 opcode 节点(-1 = 无) +fn ps_op_node(irf: int, opcode: int) -> int { + if irf < 0 { return -1; } + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + n : ., mut = start; + loop { + if n >= start + cnt { return -1; } + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == opcode { return n; } + n = n + 1; + } + return -1; +} + +// 节点 nid 是否被 kind=1 边指入(1 = 有入边,0 = 无,-1 = 无此节点) +fn ps_has_state_in(nid: int) -> int { + if nid < 0 { return -1; } + t := ps_state_touch(nid); + return t - (t / 2) * 2; // t 的入边位(无按位与;t >= 0 ⇒ 取模非负) +} + +// 节点 nid 是否以 kind=1 边指出(1 = 有出边,0 = 无,-1 = 无此节点) +fn ps_has_state_out(nid: int) -> int { + if nid < 0 { return -1; } + t := ps_state_touch(nid); + return t / 2 - (t / 4) * 2; +} + +// 函数 irf 体内「调 callee 且被链指入」的节点数(Task 2 验收例:同一被调者 +// 被调两次 ⇒ 必须两个都在链上;纯调用同形 ⇒ 0) +fn ps_calls_in_chain(irf: int, callee: string) -> int { + if irf < 0 { return -1; } + cni := str_intern(callee); + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + r : ., mut = 0; + n : ., mut = start; + loop { + if n >= start + cnt { return r; } + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL { + if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni { + if ps_has_state_in(n) == 1 { r = r + 1; } + } + } + n = n + 1; + } + return r; +} + +// 节点 nid 被 kind=1 边触及的面:+1 = 有入边,+2 = 有出边(返回 0..3) +fn ps_state_touch(nid: int) -> int { + if nid < 0 { return -1; } + r : ., mut = 0; + e : ., mut = 0; + loop { + if e >= g_df_edge_count { break; } + if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1 { + if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) == nid { r = r + 1; } + if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_FROM) == nid { r = r + 2; } + } + e = e + 1; + } + return r; +} + +// 函数 irf 区间内是否存在「指向 label 节点的 kind=1 边」(循环终止依赖: +// 循环退出节点必须依赖循环体内最后一个副作用——spec region-cfg §4.2) +fn ps_has_label_state_target(irf: int) -> int { + start := r64(g_df_func_node_start, irf * 8); + cnt := r64(g_df_func_node_count, irf * 8); + e : ., mut = 0; + loop { + if e >= g_df_edge_count { break; } + if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1 { + tgt := r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO); + if tgt >= start && tgt < start + cnt { + if r64(g_df_nodes, tgt * ESZ_DFNODE + OFF_DF_OPCODE) == IR_LABEL { return 1; } + } + } + e = e + 1; + } + return 0; +} + +fn purity_selftest_run() -> int { + fails : ., mut = 0; + total : ., mut = 0; + + // 用例程序:正控(含传递正控)+ 负控①-⑥(含 SCC 互递归 + 有效应泛型) + src := "fn pure_add(a: int, b: int) -> int { return a + b; }\n"; + src = src + "fn pure_caller(a: int, b: int) -> int { return pure_add(a, b); }\n"; + src = src + "fn effect() { st : ., mut = 0; st = 1; }\n"; + src = src + "fn call_effect() { effect(); }\n"; + src = src + "extern fn ext_probe(x: int) -> int;\n"; + src = src + "fn call_extern(x: int) -> int { return ext_probe(x); }\n"; + src = src + "fn call_builtin(x: int) -> int { return load64(x); }\n"; + src = src + "fn self_rec(n: int) -> int { if n <= 0 { return 0; } return self_rec(n - 1); }\n"; + src = src + "fn mut_rec_a(n: int) -> int { if n <= 0 { return 0; } return mut_rec_b(n - 1); }\n"; + src = src + "fn mut_rec_b(n: int) -> int { if n <= 0 { return 0; } return mut_rec_a(n - 1); }\n"; + src = src + "fn id[T](x: T) -> T { return x; }\n"; + src = src + "fn use_id(x: int) -> int { return id(x); }\n"; + src = src + "fn gid[T](x: T) -> T { gst : ., mut = 0; gst = 1; return x; }\n"; + src = src + "fn use_gid(x: int) -> int { return gid(x); }\n"; + // state 链面用例(行为级):效应调用夹在两 store 之间 / 纯调用同形 / 循环 + src = src + "fn chain_effect() -> int { a : ., mut = 0; a = 1; effect(); a = 2; return a; }\n"; + src = src + "fn chain_pure() -> int { a : ., mut = 0; a = 1; b := pure_add(a, 1); a = 2; return a + b; }\n"; + src = src + "fn chain_loop() -> int { acc : ., mut = 0; i : ., mut = 0; loop { if i >= 3 { break; } acc = acc + 1; i = i + 1; } return acc; }\n"; + // Task 2 用例:① 效应调用双调(验收例:两个 CALL 都必须进链)+ 同形纯调用 + // 负控;② IR_CALL_EXTERN / IR_SPAWN / IR_YIELD 三类 Task 2 新入链 opcode + // ——每例都带前 store(链头播种)与后 store(出边),故期望 touch == 3。 + src = src + "fn chain_two() -> int { a : ., mut = 0; a = 1; effect(); effect(); b := pure_add(a, 1); a = 2; return a + b; }\n"; + src = src + "fn chain_extern_op(x: int) -> int { a : ., mut = 0; a = 1; r := ext_probe(a); a = 2; return a + r; }\n"; + src = src + "fn spawnee(x: int) -> int { return x + 1; }\n"; + src = src + "fn chain_spawn_op() -> int { a : ., mut = 0; a = 1; r := go i 0..2 spawnee(i); a = 2; return a; }\n"; + src = src + "flow fn chain_yield_op(n: int) -> int { a : ., mut = 0; a = 1; yield n; a = 2; return a; }\n"; + src = src + "fn main() -> int { return 0; }\n"; + + if ps_compile(src) != 0 { + print("FAIL "); println("purity.selftest_compile"); + return 1; + } + + // ── 正控:无 store、无调用(及经纯函数的传递调用)⇒ 1 ── + total = total + 1; fails = fails + ps_check("purity.pure_add", ps_purity("pure_add"), 1); + total = total + 1; fails = fails + ps_check("purity.pure_caller", ps_purity("pure_caller"), 1); + + // ── 负控① store ── + total = total + 1; fails = fails + ps_check("purity.effect_store", ps_purity("effect"), 0); + // ── 负控② 传递:被调者不纯 ⇒ 调用者不纯 ── + total = total + 1; fails = fails + ps_check("purity.call_effect", ps_purity("call_effect"), 0); + // ── 负控③ extern 调用(IR_CALL_EXTERN 出现即不纯)── + total = total + 1; fails = fails + ps_check("purity.call_extern", ps_purity("call_extern"), 0); + // ── 负控④ 不可解析调用(runtime builtin:load64 无 FuncInfo)── + total = total + 1; fails = fails + ps_check("purity.call_builtin", ps_purity("call_builtin"), 0); + // ── 负控⑤ 自递归(保守不纯:不动点永等不到自己被证明纯)── + total = total + 1; fails = fails + ps_check("purity.self_rec", ps_purity("self_rec"), 0); + // ── 负控⑤b 互递归(SCC 保守;两成员都要钉)── + total = total + 1; fails = fails + ps_check("purity.mut_rec_a", ps_purity("mut_rec_a"), 0); + total = total + 1; fails = fails + ps_check("purity.mut_rec_b", ps_purity("mut_rec_b"), 0); + + // ── 负控⑥ 泛型实例:与源函数同值。纯源:**两断**(同值 + 实例值 = 1)—— + // 只断同值会被「两侧同为不纯」的退化实现假绿;只断实例值则漏掉源值。 + inst_p := ps_purity("id[int]"); + src_p := ps_purity("id"); + total = total + 1; fails = fails + ps_check("purity.generic_instance_pure", inst_p, 1); + total = total + 1; fails = fails + ps_check("purity.generic_instance_eq_source", (inst_p == src_p), 1); + // 负控⑥b 有效应泛型(体内有 store):实例与源同为 0(反向:防「实例恒纯」) + g_inst := ps_purity("gid[int]"); + total = total + 1; fails = fails + ps_check("purity.generic_effect_instance", g_inst, 0); + total = total + 1; fails = fails + ps_check("purity.generic_effect_eq_source", (g_inst == ps_purity("gid")), 1); + + // ── state 链面(Task 1 目的:效应调用重新进链;纯调用不入链;循环终止依赖)── + // ① 效应调用被链穿过:调用节点既要有入边(前 store → call)又要有出边 + // (call → 后 store)——touch == 3。 + irf_ce := ps_ir_index_of("chain_effect"); + n_ce := ps_call_node(irf_ce, "effect"); + total = total + 1; fails = fails + ps_check("chain.impure_call_pierced", ps_state_touch(n_ce), 3); + // ② 可证纯调用不被链触及:同形(store / call / store)下 touch == 0—— + // 与 ① 只差被调者纯度 ⇒ 两条合起来把「按真纯度入链」钉死(退化实现 + // 「全入链」挂②、「全不入链」挂①)。 + irf_cp := ps_ir_index_of("chain_pure"); + n_cp := ps_call_node(irf_cp, "pure_add"); + total = total + 1; fails = fails + ps_check("chain.pure_call_untouched", ps_state_touch(n_cp), 0); + // ③ 循环终止依赖:链重放复刻 sg_pop 规则(regcfg §4.2)——循环体内的最后 + // 副作用必须有边指向循环退出节点(label)。该规则随本次迁移从生成期移到 + // 重放,缺判据则回归无感。 + irf_cl := ps_ir_index_of("chain_loop"); + total = total + 1; fails = fails + ps_check("chain.loop_exit_termination", ps_has_label_state_target(irf_cl), 1); + // ③' 负控:无循环的函数区间内不得出现指向 label 的链边(防「恒真」实现) + total = total + 1; fails = fails + ps_check("chain.no_label_edge_without_loop", + ps_has_label_state_target(irf_cp), 0); + + // ── Task 2:分类表补全(效应 opcode 全族入链;单一真源 = purity_op_effect)── + // ④ 验收例:effect 被调两次 ⇒ **两个** CALL 都在链上;同形纯调用 ⇒ 0。 + irf_tw := ps_ir_index_of("chain_two"); + total = total + 1; fails = fails + ps_check("chain.two_calls_exist", + ps_count_call_nodes(irf_tw, "effect"), 2); // 非空断言(防「零入链」退化假绿) + total = total + 1; fails = fails + ps_check("chain.two_impure_calls_in_chain", + ps_calls_in_chain(irf_tw, "effect"), 2); + total = total + 1; fails = fails + ps_check("chain.two_pure_call_untouched", + ps_calls_in_chain(irf_tw, "pure_add"), 0); + // ⑤ IR_CALL_EXTERN:extern 调用入链(前后 store 夹逼 ⇒ touch == 3) + irf_ex := ps_ir_index_of("chain_extern_op"); + n_ex := ps_op_node(irf_ex, IR_CALL_EXTERN); + total = total + 1; fails = fails + ps_check("chain.extern_op_exists", (n_ex >= 0), 1); + total = total + 1; fails = fails + ps_check("chain.extern_op_pierced", ps_state_touch(n_ex), 3); + // ⑥ IR_SPAWN(range-go 每迭代发射):入链 + irf_sp := ps_ir_index_of("chain_spawn_op"); + n_sp := ps_op_node(irf_sp, IR_SPAWN); + total = total + 1; fails = fails + ps_check("chain.spawn_op_exists", (n_sp >= 0), 1); + total = total + 1; fails = fails + ps_check("chain.spawn_op_in_chain", ps_has_state_in(n_sp), 1); + // ⑦ IR_YIELD(flow 函数体):入链 + irf_yl := ps_ir_index_of("chain_yield_op"); + n_yl := ps_op_node(irf_yl, IR_YIELD); + total = total + 1; fails = fails + ps_check("chain.yield_op_exists", (n_yl >= 0), 1); + total = total + 1; fails = fails + ps_check("chain.yield_op_pierced", ps_state_touch(n_yl), 3); + + print(int_str(total - fails)); print("/"); print(int_str(total)); println(" purity cases passed"); + if fails != 0 { return 1; } + return 0; +} diff --git a/src/compiler/regalloc-consistency.cr b/src/compiler/regalloc-consistency.cr new file mode 100644 index 00000000..c7709cb1 --- /dev/null +++ b/src/compiler/regalloc-consistency.cr @@ -0,0 +1,69 @@ +// === regalloc-consistency.cr === +// 分配器(贪心放置 CAG)逻辑正确性契约——文档载体(2026-09-06 定位修正重写)。 +// +// 【本文件是什么 / 不是什么】 +// ✗ 不是规约(.corespec = 程序逻辑验证语言;寄存器分配一致性不是「程序逻辑」—— +// 它属于编译器自身正确性:自举闭环后编译器是 Core 程序,分配器是其一段代码) +// ✗ 不是规约语言文档(无 (* *) 语法——原 .corespec 形态已废弃) +// ✗ 不是编译单元(不在 build concat 清单;verify 实现在 regalloc.cr,DRY) +// ✓ 是分配器逻辑契约的注释文档:四条判定(regalloc-cache-mapping.md §四)在 +// Core 数据模型(条目/区间/共存)上的精确陈述 + 与实现的映射 +// ✓ 三层正确性各有其位(2026-09-06 澄清): +// 程序逻辑规约 —— #check/#ensure(.corespec + 验证器) +// 分配器逻辑契约 —— 本文件(可验证对象;证书层 = 最优性证明,远期) +// 运行自检 —— verify_regalloc_consistency(regalloc.cr,corearch +// O2 路径实证——2026-09-07 regalloc 移后端后自算自检) + +// 【语义出处】 +// 条目/存在区间/共存 = v6 格式定稿 specs/2026-09-05-lattice-ir-v6-format.md +// §3.4/§4(内存态闭区间 [ls,le];落盘半开 le_disk = le_mem + 1) +// 判定四条 = docs/regalloc-cache-mapping.md §四(可判定、局部、条目泛型) +// 上下文贪心 = regalloc-cache-mapping.md(唯一分配算法定案) + +// 【数据模型(Core 编译器内存态)】 +// 条目 entry = IR 变量 × 版本(每次定值 = 新版本;def=-1 = 参数/无定值单条) +// 存在区间 = 条目指令序存活闭区间 [live_start, live_end](全局指令序) +// 共存 coexist = 两条目区间相交(对称、无传递性——无格承诺,最弱理论) +// 位置 location = 条目材料物理承载:寄存器(分配结果 = g_opt_meta REG_ASSIGN +// 对 var→reg,后端 g2_slot 以 E2_REG_SLOT_BASE+reg 哨兵解析) +// 或 home 槽(条目表 home 字段,分配器回填 seam——M1 未回填) +// 分配结果 = g_opt_meta REG_ASSIGN 对(var→reg)——corearch 进程内自算 +// (2026-09-07 移后端;负编码写 IR 操作数是已废弃旧设计) + +// 【四条判定(逻辑契约)——实现映射 src/arch/x86_64/regalloc.cr】 +// ① 共存互斥:同位置(寄存器/home)的条目不共存 +// —— verify_regalloc_consistency 规则①(sweep:归并排序 O(k log k) + +// 组内最大 live_end 单遍——消费端门禁,不复用 O(E²) 两两) +// ② 读点无陈旧:每个读点所在版本须为活跃版本 +// —— 规则②框架(rl_rule2_func;覆盖义务边界 = 首版本化定值后—— +// GC 批 2 已按格式定稿 §4.1 升级 dest≥0 定值建模(含 ALLOC_ARRAY/ +// STRUCT 内存对象诞生)——自动扩权,代码零改动,承诺已验证) +// ③ 驱逐配对:被驱逐条目须有配对写回(未来:spill 机制落地后验) +// —— 规约已述,代码 TODO——当前无驱逐(>5 共存 = 栈驻留),无事件可验 +// ④ 调用失效:调用点处 caller-saved 条目须失效/保存 +// —— 同上(caller-saved 标注落地后验) +// 注入红测试(--inject-* 通道)实证 verify 真消费数据:rules 1/1/2 诊断 +// (2026-09-06 定位修正注:R3/R4 不是独立「待实现挂账」——验证体系已闭环; +// 它们待验的数据源(驱逐机制/caller-saved 标注)属分配器演进,落地时规则②的 +// sweep/框架结构直接扩接,无架构改动) + +// 【已知缺口(挂账)】 +// - 判定/条目表与分配器同为文字区间模型(非 CFG 活性)——RegionCheck 图层 +// 接线时健全化(衔接决策 b 预留;行为语义锚兜底检测) +// - pass_stack_share:已停用(2026-09-07 regalloc 移后端 D-3=b——原实现产物 +// 死路径、corearch 恒空跑;恢复 = corearch 内以 alloc 同 seam 实现) +// - param_ents=-1(重定值参数无入参版本条目:尾部 def=-1 补条 pass 条件 +// prev[lv]<0 = 从未定值才补,重定值参数全 def'd 条目 → 无入参版本条目; +// 未引用参数同形)= 数据面既有缺口(compute_entries 尾部补条逻辑—— +// regalloc.cr;.ccr 已不落 param_ents(恒 -1),原定向测试随 ENT 载体 +// 消亡删除;入参版本条目建模 = 后续若判定②扩权需要) +// - M-5:entries_coexist 无上界校验——已修(GC-1 守卫在 entries_coexist, +// 2026-09-06;探针 --inject-coexist-oob 随迁 corearch) + +// 参考实现(唯一真源,勿在此复制代码): +// src/arch/x86_64/regalloc.cr —— compute_live_ranges / compute_entries / +// entries_coexist / coexist_version_conflicts / +// verify_regalloc_consistency / alloc_registers(CAG 上下文贪心) +// (2026-09-07 自 opt.cr 迁入 corearch——corec 侧不再含分配实现) +// 语法家 —— grammar/corespec.ebnf(程序逻辑规约用;SpecImplies 产生式为 +// corespec 语法演进保留,与本文件无绑定) diff --git a/src/compiler/ty_shadow.cr b/src/compiler/ty_shadow.cr new file mode 100644 index 00000000..7e7369a5 --- /dev/null +++ b/src/compiler/ty_shadow.cr @@ -0,0 +1,1434 @@ +// === ty_shadow.cr === +// **R2 P5 Task 5 后本文件不含影子层**(文件名 = 历史遗留;`sh_` 前缀 = 同因历史前缀)。 +// 现存两层**生产面**: +// ① **桥接层**:checker 的类型表行号(ti)→ P0 引擎的类型项(term)——`sh_term_of_ti` +// 及其构造点(原生/命名身份链/泛型应用/序列/ref mut 标记/名字令牌/元组/null)+ +// P5 Task 2 的类型面分类表与拆分器(`sh_tk_*`,DFNode 单槽化的派生单源); +// ② **引擎展开层**(文件末段):named/struct/enum/generic-apply → 结构项,只服务「满足 +// 判定」与「穷尽性域/模式项」两条路径;等价判定保持原子名义(边界与理由见该段头注 +// ——裁错即判定全面漂移)。 +// +// ─── 影子对拍通道(R2 P1 的迁移期仪器):**已整体下线(R2 P5 Task 5)** ─── +// 删除件 = 站点挂点(checker 的判定点 → `sh_site_begin`)/ 判定对账 `sh_compare` / 差异 +// 环形缓冲 / 摘要 `sh_report` / 转储 `sh_dump_write` / 站点直方图 / kind 空间 / CLI 通道 +// (`--type-shadow`、`--type-shadow-dump`)/ 全局态(`g_shadow_*`);桥接缓存的调试计数 +// `g_shadow_hits` 同删(补偿 hack 一并消失),`g_shadow_map[+/_cap/_entries]` **改名** +// `g_term_map[+/_cap/_entries]`(本表是生产桥接,不再是影子面)。 +// **删除前置(gate)**:对照物 `type_equal_legacy` 已在 P5 Task 4 删除 ⇒ 对拍停摆——全语料 +// 72 档 `decisions=0 agree=0` 且全差异桶 0(p5-task5-report gate 节实测)。 +// **替代回归网(D26-①,自本任务起生效)** = 冻结基线同源对拍(pre-P5 二进制 × 当前源 vs +// 当前二进制 × 当前源,72 档 `check` rc + 日志逐档 diff)+ 行为探针(`test_named_face.py` +// 等套件 + 各批探针语料)+ 突变控制 + 三态纪律(ICE04)。**任何后续批次不得引用已下线的 +// 影子计数/摘要/站点直方图**(声明落 TODO 与 `src/ci/run.sh`)。 +// +// 历史沿革(逐条 = 证据链指向任务报告): +// R2 P1 Task 1/2:桥接层 + 判定挂点/分类计数/摘要/转储(影子通道本体)。 +// R2 P2a Task 3:判定权移交引擎(`type_equal` = 快路径 + 桥接 + `ty_equiv`);对照物 = +// 旧结构判等(`type_equal_legacy`),分类语义 = 「引擎 vs 旧结构判等」(替换门对拍)。 +// R2 P3 Task 1:序列项 / ref mut 标记(见下方构造点注记):序列项的 b 槽 = 固定性位 +// (N 或 -1;**不入等价身份**),AK_REF 链 = [mut 标记, 元素](mut 成为判定维度——旧约定 +// 「不入链」下引擎把 `&T` 与 `&mut T` 判等价 = 静默放宽;本批关闭)。 +// R2 P3 Task 3 Step 0(Task 2 评审硬性要求):unknown 桶拆因(bridge / engine + 成因位)。 +// R2 P5 Task 4:对照物 `type_equal_legacy` 删除 ⇒ 判定观察面停摆。 +// R2 P5 Task 5:影子通道整体删除 + 回归网切换(本任务)。 +// ⚠ 口径继承(**影子通道已不存在,故对后续批次 = 强制**):影子对 **N 面已失明**(legacy +// Task 2 起 N-free)——数组长度面的对拍恒 0(同义反复)⇒ **N 面证据只能来自行为探针** +// (`array_len_constraint_ok` 判定点 + 异长/同长/嵌套位),不得以「无差异」充当 N 面证据 +// (Task 2 评审 Important)。 +// +// ⚠️ **`AK_*` 与 `TI_*` 下标不 1:1(Task 1 评审 M4)——必须按语义分派,禁止数值直传**: +// checker 表前 8 行 = TI_INT=0 TI_DEX=1 TI_BOOL=2 TI_STR=3 TI_UNIT=4 TI_NEVER=5 +// TI_CHAR=6 TI_DYN=7 +// 引擎原子类 = AK_INT=0 AK_DEX=1 AK_STRING=2 AK_BOOL=3 AK_UNIT=4 AK_NEVER=5 +// AK_CHAR=6 AK_DYN=7 +// 即**下标 2/3 互换**(AK_STRING=2 而 TI_STR=3;AK_BOOL=3 而 TI_BOOL=2)。照数值直传 +// 会把 bool↔string 静默错标,且两侧同错自洽 → 差异清单全成假信号。故本层用两张显式 +// 分支表按语义逐项分派(sh_native_ak / sh_base_ak);守门用例 = bridge.str_ak / +// bridge.bool_ak(type_selftest.cr)。 +// **R2 P2b Task 2:分派单源化**——同一映射此前有两份实现(本层 + 注册表 iface_registry.cr 的 +// iface_by_ty_code/iface_kind_of),不合一 = P2b 亲手制造「同一映射两份」(本批要治的病)。 +// 现本层两个入口**委托** `iface_by_ty_code`(唯一实现)。**R2 P5 Task 4**:改动前实现的两份 +// 字面拷贝(`sh_base_ak_legacy`/`sh_native_ak_legacy`)已删——单源化后它们再无生产判据价值, +// 自测的全表对拍改钉**冻结期望表**(type_selftest.cr 的 `ts_t4_ak_expected`)。 +// M1(Task 1 评审):native 分派读**类型表本体**(kind == TYP_BASE 且 data == TY_* 码), +// 不再依赖「init_types 行号序恰与 TI_* 码序一致」这一隐式等价(当前真、不保证)。 +// +// 本层只读 checker 侧(g_types / g_gen_apply_data,见 sh_term_of_ti 的递归翻译)与引擎 +// 构造/判定 API,不写任何 checker 判定状态、不改判定结果、不写产物(**唯一**例外 = 展开层 +// 的字段类型解析可追加类型行,见该段头注的「非纯只读」登记)。 +// 判定路径**无条件**调用 `sh_term_of_ti`(R2 P2a Task 3 起;此前仅 `--type-shadow` 下译项) +// ⇒ ① 桥接缓存**必须随类型表重置失效**(`sh_map_reset`,经 `init_types()` 调用——否则长驻 +// 进程复用行号命中陈旧 ti→term = 两个不同类型被判等,见该函数注记;corelsp 评审 Critical); +// ② 引擎预算/memo 的窗口隔离在**消费方**(`type_equal_engine` / `ti_subsumes` 等的 +// `ty_budget_reset` 前后各一次),不属本层。 +// +// 缓存 g_term_map(16B/条 {ti, term};**R2 P5 Task 5 前名 g_shadow_map**):开放寻址线性 +// 探测(与引擎 g_tt_index 同式),键 = ti 本体(term 不入键)。计划骨架的两处缺口在本实现 +// 补齐(偏差逐条见 Task 1 报告): +// ① 骨架无扩容、无装填因子守卫 → 表满(或 ti 探测链满)时 sh_map_find 的探测 +// **永不落空 = 死循环**。P1 Task 3 的语料是编译器自身(ti 数可上数千 > 初始容量 +// 1024),P0 引擎同族缺陷(memo 装填因子)已有实证,故此处按同款守卫落: +// 探测前 (g_term_map_entries + 1) * 2 >= cap → 扩容重建 + 重放(sh_map_rehash)。 +// 计数即 g_term_map_entries(只增不减,恒等于占用槽数——无需第五个全局)。 +// ② 骨架「先探槽位 → 递归翻译子项 → 回写该槽位」:递归中的插入可触发扩容(表指针与 +// 容量变更)→ 回写的槽位索引失效 = 静默错写/丢条目(P0「扩容后去重静默失效」同族)。 +// 本实现:翻译完成后**重探**再写;重探必落空槽(ti 图无环——子项 ti 恒小于父项, +// 父项此刻未入表),命中分支仅为防未来改动的兜底。 + +TERM_MAP_INIT_CAP : int = 1024; + +// ─── 原生清单 → AK_*(**按语义逐项对应,不得按下标直通**;下标互换事实见文件头注)─── +// 计划注释「AK_* 与 TI_* 前 7 项 1:1」与 spec §11 实测**不一致**:照抄骨架的 +// `tt_atom(ti, ti, -1)` 会把 bool↔string 静默错标(两侧同错且自洽 → 差异清单全假信号)。 +// M1(Task 1 评审):**读表本体**判定,不按行号猜。判据 = 该行 kind 恰为 TYP_BASE 且 +// data(TY_* 码)有语义对应 → 该行的类型身份即此原生类。行号序与 TY_*/TI_* 码序的 +// 「巧合一致」不再是正确性前提(表布局一变即静默错标的隐式依赖已消除)。 +// 注:TYP_DYN 行(TI_DYN=7)kind ≠ TYP_BASE → 不在此路,走 sh_term_of_ti 的 TYP_DYN 分支; +// TY_DEX_S 占位行(TI_DEX_S=8)经 sh_base_ak 归 AK_DEX(同值域不同表示) +// ——旧实现按行号白名单把它留给通用路径,两条路产出的项完全一致(仅缓存占用差异)。 +fn sh_native_ak(ti: int) -> int { + if ti < 0 { return -1; } + // **原门保留**(P2b Task 2 的关键点):本快路径只认「类型表本体 = TYP_BASE」的行; + // TYP_DYN 行与全部结构/命名行一律 -1(TI_DYN 走 sh_term_of_ti 的 TYP_DYN 分支,见文件头注)。 + // ⚠ **不得**把本函数直接换成 iface_kind_of:后者对 TYP_DYN 行回 AK_DYN、对结构行回 + // AK_SEQUENCE/…(= 注册表的分派面),与本层的「原生快路径」契约不同——两函数只用 + // 同一个 TY_* 逐项分派表(iface_by_ty_code),门各自保留。 + if get_type_kind(ti) != TYP_BASE { return -1; } + return iface_by_ty_code(get_type_data(ti)); +} + +// TYP_BASE 的 data(TY_*)→ AK_*:同款语义对应(TY 序与 TI 序一致,故与 AK 下标同样 +// 不可混用)。-1 = 无对应(调用方回退 AK_NAMED)。 +// R2 P2b Task 2:实现已**单源化**到 iface_registry.cr 的 `iface_by_ty_code`(唯一实现; +// 逐项语义分派,含 TY_DEX_S→AK_DEX / TY_GENERIC_PARAM→AK_NAMED 两条已裁决灰格)。 +fn sh_base_ak(ty: int) -> int { + return iface_by_ty_code(ty); +} + +// ─── 对照物(R2 P2b Task 2 的改动前实现字面拷贝)**R2 P5 Task 4 删除**─── +// 用途曾是 `iface.by_ty_code_all_codes` / `iface.native_ak_all_rows`(逐 TY 码 × 逐 ti 行, +// 非抽样)的旧版对照。删因:单源化的收益已由**冻结期望表**承担(期望值以数据形式落在 +// type_selftest.cr 的 `ts_t4_ak_expected`,不再需要生产文件里的第二份实现)。 +// TY_DEX_S(dex 定点形式)的裁决仍有效:值域仍是 dex(缩放整数表示)⇒ 归 AK_DEX +// (引擎无「同值域不同表示」的区分——表示层差异不进类型身份)。 + +// ─── 桥接缓存(ti → term)─── +// **重置 = 随类型表作废**(R2 P2a Task 3 评审 Critical 修复;与 named_dedup_reset 同式): +// 键 = ti 本体,而 `init_types()` 会把类型表清空重建(行号空间复用)→ 陈旧 ti→term 若不清, +// 长驻进程(corelsp 每请求 `reset_frontend_state → check_all → init_types`)里**命中即返回** +// 上一请求的类型项 ⇒ 两个不同类型被判等(静默漏报)。评审实证:同 URI 两次 didOpen 只把 +// 数组元素型 int→bool,第二次 diagnostics=0(应 1 条 Assignment type mismatch)。 +// cap = 0 → 下次 sh_map_find 惰性重建(空槽全 -1);entries 归零(否则装填因子守卫按陈旧 +// 计数提前扩容)。**R2 P5 Task 5**:影子期的 hits 复位已删(计数本体随影子层下线)。 +fn sh_map_reset() { + g_term_map_cap = 0; + g_term_map_entries = 0; +} + +fn sh_map_cap_init() { + if g_term_map_cap <= 0 { + nc : ., mut = TERM_MAP_INIT_CAP; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_term_map = nb; + g_term_map_cap = nc; + } +} + +// 无守卫探测(**只可在扩容守卫之后调用**;sh_map_rehash 重放时借道这里): +// 命中返回槽位,未命中返回空槽位(不插入)。 +// 形态 = tt_term 的探测循环(ret + break + 尾 return),**不**用「循环体内裸 return 收尾」 +// (如 ty_memo_slot_no_grow):后者曾是自托管 checker 的误报面——函数体以无 break 的 loop +// 收尾时它按「落空 = 返回 unit」判 → TF01 "Function return type mismatch"。**该误报已修** +// (TF01 收口:checker.cr 的 `stmt_cannot_fall_through`/`loop_body_has_break` 落空分析 + +// type_engine.cr 的 `lits_copy` 返回型改 string),此处形态保留仅为 typed 探测的旧写法, +// 不再有「不许这么写」的合规约束(不过改写无益,故不动)。 +fn sh_map_find_nogrow(ti: int) -> int { + cap := g_term_map_cap; + // 非负槽位(负数取模 = 负下标 → 探针越界;与引擎 tt_mod 同式同因——直接复用引擎 + // 该函数,避免本仓库第三份取模式各自漂移) + p : ., mut = tt_mod(ti, cap); + ret : ., mut = -1; + loop { + k := r64(g_term_map, p * 16); + if k < 0 { ret = p; break; } + if k == ti { ret = p; break; } + p = p + 1; if p >= cap { p = 0; } + } + return ret; +} + +fn sh_map_find(ti: int) -> int { + sh_map_cap_init(); + // 装填因子守卫(**必须在探测前**):表满且键不存在时开放寻址永不退出(死循环) + if (g_term_map_entries + 1) * 2 >= g_term_map_cap { sh_map_rehash(); } + return sh_map_find_nogrow(ti); +} + +// 扩容 = 重建 + **重放既有条目**(引擎 grow_tt_index/tt_reindex 同式);计数随重放重算 +// (装填因子守卫读的就是它,不得沿用旧值)。 +fn sh_map_rehash() { + old := g_term_map; + old_cap := g_term_map_cap; + nc : ., mut = old_cap * 2; + if nc < TERM_MAP_INIT_CAP { nc = TERM_MAP_INIT_CAP; } + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_term_map = nb; + g_term_map_cap = nc; + g_term_map_entries = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + k := r64(old, j * 16); + if k >= 0 { + s := sh_map_find_nogrow(k); + w64(g_term_map, s * 16, k); + w64(g_term_map, s * 16 + 8, r64(old, j * 16 + 8)); + g_term_map_entries = g_term_map_entries + 1; + } + j = j + 1; + } +} + +// TYP_TUPLE → AK_PRODUCT(参数链 = 逐字段项,顺序 = 声明顺序)。 +// **实读布局**(checker.cr:118-131 type_equal 的 TUPLE 分支 + :2041-2050 字段访问 t.N): +// data = 字段数;extra = 字段 ti 在 g_gen_apply_data 的起始下标(8B/元素)—— +// 第 i 个字段 ti = r64(g_gen_apply_data, (extra + i) * 8)。 +// 计划文中的「字段在 g_tuple_*」在本仓库**不存在**(全仓 grep 零命中),以实读为准。 +// CONS 链**逆序构造**(i 自末尾向前 cons):DAG 项不可变,正向追加需改写已建项—— +// 不可行;逆序构造的结果顺序仍 = 声明顺序。 +// ─── R2 P3 Task 1:序列项 / ref mut 标记(桥接的两个新构造点)─── +// 序列项(TYP_ARRAY 与 TYP_SLICE 的**唯一**构造点):tt_atom(AK_SEQUENCE, fixed_len, [元素项])。 +// b 槽 = **固定性位**:数组 = N(长度字面量),切片/视图 = -1。 +// ⚠ 引擎比较只看 a(原子类)与 c(参数链)两槽(`lit_implies` 的**双方皆正原子**分支; +// b 槽是**标注**——AK_NAMED 的 b = 行号即此约定)⇒ **N 不入等价/包含身份**(R1 裁决 + +// P2a 长度约束不变量的延续):`[int;3]` 与 `[int;4]` 的项除 b 外逐位相同 ⇒ `ty_equiv == 1`、 +// `ty_sub == 1`,拒绝一律由 checker 的常量档约束承担。固定性位的消费者 = checker.cr +// `array_len_constraint_ok` 的**方向规则**(视图→固定拒绝)与后续横切形状(P3 Task 2)。 +// -1 = 元素项译不成(调用方回 -1,不缓存)。 +fn sh_seq_term(elem_ti: int, fixed_len: int) -> int { + el := sh_term_of_ti(elem_ti); + if el < 0 { return -1; } + return tt_atom(AK_SEQUENCE, fixed_len, tt_cons(el, tt_nil())); +} + +// AK_REF 链槽 0 = mut 标记(R2 P3 Task 1 起 mut **成为判定维度**,替代旧约定的「不入链」—— +// 旧约定下 `&T` 与 `&mut T` 的项逐位相同 ⇒ 引擎判等价,而 legacy 比 extra(mut) 判不等 ⇒ +// 判定替换后 mut 面静默放宽;本标记即该缺口的关闭)。 +// 标记项 = tt_atom(AK_UNIT, mut, -1):**语法令牌**(b 槽 = 标记值,照 AK_NAMED 的 b = 行号 +// 同约定),非类型集——引擎侧按**节点同一性**比较(type_engine.cr 的 tt_list_variance_at +// 槽 0 分支;不同标记 = 确定不匹配 ⇒ 0,不回 -1)。 +fn sh_ref_mut_marker(mutv: int) -> int { + return tt_atom(AK_UNIT, mutv, -1); +} + +// 名字令牌(P3 Task 1 起:变体身份链的 name **索引入链**升级为**令牌原子上链**)。 +// 旧约定(Task 0)= `tt_cons(ei_name(ea), tt_cons(name_ni, tt_nil()))` 直接把 **name 索引** +// 当链元素——索引是**别名**:任何把链元素当术语解释的比较(如按结构/等价比较)会把 +// `str_intern` 得到的小整数当**术语下标**读取(`tt_tag(ni)` = 第 ni 个项的 tag)⇒ 名字 +// 碰撞即静默判等。本任务实证两类:① 不变槽改用 ty_equiv 比较时 +// `unf.enum_variant_identity_scoped` 红(异枚举同名变体被判等);② 固定性守卫 +// (tt_type_elem_same)会读链元素的 class。⇒ 令牌改为**原子节点** +// `tt_atom(AK_UNIT, ni, -1)`(与 mut 标记同式;b = 值),只按**节点同一性**比较, +// 结构比较再也不可能把它当术语解释。语义等价(不同名字 → 不同节点;同名字 → 同节点)。 +fn sh_name_token(ni: int) -> int { + return tt_atom(AK_UNIT, ni, -1); +} + +fn sh_tuple_to_product(ti: int) -> int { + cnt := get_type_data(ti); + start := get_type_extra(ti); + tail : ., mut = tt_nil(); + i : ., mut = cnt - 1; + loop { + if i < 0 { break; } + et := sh_term_of_ti(r64(g_gen_apply_data, (start + i) * 8)); + if et < 0 { return -1; } // 任一字段无法翻译 → 整项无法翻译(不缓存) + tail = tt_cons(et, tail); + i = i - 1; + } + return tt_atom(AK_PRODUCT, -1, tail); +} + +// ─── R2 P3 Task 4:null 项(`T? = T ∪ null` 的右支;`None` 类型的项)─── +// 规范形态 = tt_atom(AK_NULL, -1, -1):**行号不入项**(b 槽是标注;此处刻意连标注都空)。 +// 理由:`None` 每出现一次就分配一行(TYP_NULL 不做行去重)⇒ 若把行号写进 b 槽,同一类型 +// 会有 N 个「不同节点」——虽然引擎比较只看 a/c 两槽(b 是标注)⇒ 语义不受影响,但节点同一 +// 性快路径(ty_sub 的 `a == b`、tt_norm 的哈希去重)会失去同项合并,读数与缓存都变差。 +// 单点语义 ⇒ 项恒定,构造去重(tt_term 的哈希合表)保证全仓只有**一个** null 节点。 +fn sh_null_term() -> int { + return tt_atom(AK_NULL, -1, -1); +} + +// ─── R2 P5 Task 3:命名面**身份链**(两构造点共用;取代「空链 + b 槽当身份」)─── +// 语义:命名型(TYP_NAMED / TYP_GENERIC_PARAM / TYP_GENERIC_APPLY)的**身份** = 身份链: +// TYP_NAMED / TYP_GENERIC_PARAM : c = [sh_name_token(name_ni)] +// TYP_GENERIC_APPLY : c = [sh_name_token(基名 ni), 实参项…] +// b 槽(NAMED/PARAM = 本行;APPLY = **基型行**)仍是**标注**:引擎命名面只比 a/c 两槽 +// (type_engine.cr 的 te_named_pair / te_chain_cmp),b 的既有消费者 = D21 的 +// tt_atom_of_term(DF 载体的类型行派生)。 +// APPLY 的 b 槽取**基型行**(不取本行)是**规范形**要求:`Box[int]` 的两处实例化各占一行 +// (alloc_type 裸分配)⇒ 若 b = 本行则同型两行**节点不同** ⇒ 节点同一性快路径 / 不变槽 +// 结构比较 / 接口签名(iface_axis 的 tt_list_same 判等)全部失真。基型行经 named_dedup +// 按名规范 ⇒ 同型恒同节点。本形态 = sh_sig_term_of_ti 原形态,本任务把它单源化到**两构造 +// 点** ⇒ 两点逐位同构(计划停条件③;两点差异只剩实参项的递归函数——签名面 N 不入项)。 +// 命名面**不展开**(P1 裁决 + P3a 反证:展开项接进等价面 ⇒ 同形不同名 struct 判等)—— +// 链里只有身份令牌与实参类型项,没有任何结构定义。 +// -1 = 不可构造(行越界 / 基型非命名行 / 实参不可译)——调用方按桥接缺口上抛。 +fn sh_named_identity_term(ti: int) -> int { + if ti < 0 { return -1; } + ni := get_type_data(ti); + if ni < 0 { return -1; } + return tt_atom(AK_NAMED, ti, tt_cons(sh_name_token(ni), tt_nil())); +} + +// 泛型应用实参链(两构造点共用骨架;variant:0 = 桥接面递归 sh_term_of_ti / +// 1 = 签名面递归 sh_sig_term_of_ti)。 +fn sh_apply_args_chain(ti: int, variant: int) -> int { + start := get_type_extra(ti); + cnt := r64(g_gen_apply_data, start * 8); + if cnt < 0 { return -1; } + tail : ., mut = tt_nil(); + j : ., mut = cnt - 1; + loop { + if j < 0 { break; } + ai := r64(g_gen_apply_data, (start + 1 + j) * 8); + at : ., mut = -1; + if variant == 0 { at = sh_term_of_ti(ai); } else { at = sh_sig_term_of_ti(ai); } + if at < 0 { return -1; } // 任一实参不可译 ⇒ 整项不可译(不缓存) + tail = tt_cons(at, tail); + j = j - 1; + } + return tail; +} + +// 泛型应用行 → 命名原子(身份 = 基名令牌 + 实参项链;b = 基型行 = 规范形)。-1 = 不可构造。 +fn sh_apply_identity_term(ti: int, variant: int) -> int { + if ti < 0 { return -1; } + base := get_type_data(ti); + if get_type_kind(base) != TYP_NAMED { return -1; } + bni := get_type_data(base); + if bni < 0 { return -1; } + tail := sh_apply_args_chain(ti, variant); + if tail < 0 { return -1; } + return tt_atom(AK_NAMED, base, tt_cons(sh_name_token(bni), tail)); +} + +// ti → 类型项(带 per-ti 缓存;-1 = 无法翻译) +fn sh_term_of_ti(ti: int) -> int { + if ti < 0 { return -1; } + // 原生快路径:无表读、不占缓存条目 + nak := sh_native_ak(ti); + if nak >= 0 { return tt_atom(nak, ti, -1); } + slot := sh_map_find(ti); + if r64(g_term_map, slot * 16) == ti { + return r64(g_term_map, slot * 16 + 8); + } + // 未命中:按 kind 翻译(递归子项) + term : ., mut = -1; + k1 := get_type_kind(ti); + d1 := get_type_data(ti); + if k1 < 0 { return -1; } // 行号越界(ti >= g_type_count)= 无法翻译 + if k1 == TYP_BASE { + bak := sh_base_ak(d1); + if bak >= 0 { term = tt_atom(bak, ti, -1); } + } else if k1 == TYP_DYN { + // data = 类型集位图(0 = 单一已知类型)——计划表定 TYP_DYN → AK_DYN。位图是 + // dyn 的**收窄**,AK_DYN 在引擎侧 = ⊤(与一切相容)→ 该项过宽(可致 Task 3 + // 里同源 dyn 集被判「等价」的假信号);P1 不改引擎,先按表落 + 报告注记。 + term = tt_atom(AK_DYN, ti, -1); + } else if k1 == TYP_ARRAY || k1 == TYP_SLICE { + // TYP_ARRAY 的 extra = N(长度),**不入身份**(R1 裁决)→ 参数链只含元素项, + // N 落 b 槽 = 固定性位(P3 Task 1;构造点单源化到 sh_seq_term) + fl : ., mut = -1; + if k1 == TYP_ARRAY { fl = get_type_extra(ti); } + term = sh_seq_term(d1, fl); + } else if k1 == TYP_PTR { + // 同 PTR:元素入链(AK_PTR 槽 0 变型 = 不变,见 type_engine.cr 变型表); + // extra(asp 地址空间位)不入链——与 legacy 的 PTR 分支(只比 data)同语义 + in1 := sh_term_of_ti(d1); + if in1 >= 0 { term = tt_atom(AK_PTR, -1, tt_cons(in1, tt_nil())); } + } else if k1 == TYP_REF { + // extra = mut 标记:**入链**(P3 Task 1)——链 = [mut 标记项, 元素项],标记与元素 + // 各占一槽(槽 0 不变 / 槽 1 只读协变、可写不变,见 type_engine.cr 变型表) + in2 := sh_term_of_ti(d1); + if in2 >= 0 { term = tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(get_type_extra(ti)), tt_cons(in2, tt_nil()))); } + } else if k1 == TYP_OPTIONAL { + // R2 P3 Task 4:`T?` = `T ∪ null`(spec §5.4)。这是桥接层**唯一**译作非原子项的行 + // (其余行 → 单原子);依据 = 定义式本身(不是名义展开——T? 无名字可展开)。语义落点: + // 引擎对 union 的一切判定(包含/等价/不相交/穷尽性)零新规则。 + in3 := sh_term_of_ti(d1); + if in3 >= 0 { term = tt_union(in3, sh_null_term()); } + } else if k1 == TYP_NULL { + // null 单点类型(`None` 值的类型):规范项 = null 原子(无参数、无 b 槽行号——同一 + // 类型的不同出现点(每处 `None` 各一行)必须译成**同一**项,否则 `ty_sub(null, null)` + // 会走结构比较而非节点同一/规范化快路径(语义仍对,但多绕一圈且不可读)。 + term = sh_null_term(); + } else if k1 == TYP_TUPLE { + term = sh_tuple_to_product(ti); + } else if k1 == TYP_NAMED || k1 == TYP_GENERIC_PARAM { + // R2 P5 Task 3:身份链(名字令牌)——取代原「空链 + b 槽当身份」(见 + // sh_named_identity_term 文件头注) + term = sh_named_identity_term(ti); + } else if k1 == TYP_GENERIC_APPLY { + term = sh_apply_identity_term(ti, 0); + } else { + // 未知 kind 保守归入命名类:**无链** ⇒ 引擎命名面域外(-1,见 te_named_pair 守卫)。 + // 全部 kind 已在上面逐项分派 ⇒ 本分支不可达(防御面,非未覆盖面)。 + term = tt_atom(AK_NAMED, ti, -1); + } + if term < 0 { return -1; } + // 写回前**重探**(递归翻译期间可能已扩容——见文件头注记②) + slot2 := sh_map_find(ti); + if r64(g_term_map, slot2 * 16) == ti { + return r64(g_term_map, slot2 * 16 + 8); // 兜底:递归期间同 ti 已入表 + } + w64(g_term_map, slot2 * 16, ti); + w64(g_term_map, slot2 * 16 + 8, term); + g_term_map_entries = g_term_map_entries + 1; + return term; +} + +// ─── R2 P5 Task 2(D19/D21/D22/D23):DFNode 类型面单槽化 = 唯一分类表 + 拆分器 ─── +// 单槽化(α,内存面):`OFF_DF_TK` = **类型项引用**(类型面唯一真源), +// `OFF_DF_AUX` = **辅码**(原混用槽里的非类型部分:旗标/宽度/计数/不可入项的原始行码)。 +// `.ccr` NOD 与 `.cir` 快照**只承载派生码**(`sh_dfn_code_of_slots`)⇒ 盘面与版本位 +// 零改动(布局零变化,见 dyn_arr.cr 的四条契约)。 +// +// 分类表(Task 0 表 A 的代码化;逐 op 依据 = p5-task0-report §2 的 51 行表 + 后端 +// `ti` 消费者机器审计): +// · **类型行面**(face 0,tk 语义 = 类型行 ⇒ 建项):IR_CONST / IR_BINARY / IR_ALLOC / +// IR_CALL / IR_LOAD / IR_I2F / IR_F2I —— P4 的 {CONST,BINARY} 允许清单 + Task 0 +// 对 P4 六排除项的再裁(前五者「调用点显式传 TI_*/类型行变量」⇒ 语义即类型行; +// IR_LOAD_ENUM_TAG 两调用点传字面量 0 ⇒ 保留「无面」)。 +// · **辅码面**(face 1,tk 语义 = 非类型码 ⇒ 原码进辅码槽):IR_BOUNDS_CHECK(0/1 +// 动态上限旗标,instr.cr:1340 `ti != 0`)/ IR_DEREF / IR_STORE_PTR(访问宽度码 +// 8/4/2/1,instr.cr 的 e2_ptr_bounds_check)/ IR_SPAWN(spawn_count;-1 = 动态)/ +// IR_HOTPATCH_ROUTE(F1 修正后的显式 0)。 +// · **无面**(face 2,tk 语义 = 无):其余全部 op。 +// 数值陷阱(P4 §2 实测):旗标 `1` 数值上 = TI_DEX 行、宽度 `8` = TI_DEX_S 行 ⇒ +// 分类**必须逐 op**,按行号猜即凭空给「指针解引用」安 dex 项。 +// +// **原子行 vs 复合行**(F2 裁决 = Task 0 候选 (iii)+(ii)):类型面**只接受项可逆的行** +// (`atom_of(项) == 行号`,当且仅当项是 TT_ATOM 且 b 槽即行号)。指针/引用/数组/切片/ +// 可选/null/元组行的项是结构性项(如 AK_PTR 的 b = -1)⇒ **不建项**(D21:非原子 ⇒ -1, +// 不近似),原码按辅码槽**保真**⇒ 派生码逐字节 ≡ 旧混用码(D22-① 在全语料成立,含 +// 复合行)。反例(实测):`tests/suite/ptr_ref_first.cr` 的 IR_BINARY tk=10 = 指针行。 +// +// 三态纪律(D23):类型面 op 的行**越出类型表**或**在域内但译不出项**(桥接缺口) +// ⇒ **建项失败**(`g_tk_face_fail` 置位)⇒ save 侧拒绝落盘(rc=1 + 诊断),绝不静默 +// 留空项。`tk < 0` = 「无类型」(IR_LOAD/CALL 的未定型路径)不是行码,不算失败。 +fn sh_tk_is_type_face(opcode: int) -> int { + if opcode == IR_CONST || opcode == IR_BINARY || opcode == IR_ALLOC || + opcode == IR_CALL || opcode == IR_LOAD || opcode == IR_I2F || opcode == IR_F2I { + return 1; + } + return 0; +} + +// 行三态(D23 判据面):-1 = 非行码(tk < 0 = 无类型,合法);0 = **不可译**(越界 / +// 桥接缺口 ⇒ 建项失败);1 = 可译(原子行 ⇒ 项;复合行 ⇒ 辅码保真)。 +fn sh_tk_row_state(tk: int) -> int { + if tk < 0 { return -1; } + if tk >= g_type_count { return 0; } + if sh_term_of_ti(tk) < 0 { return 0; } + return 1; +} + +// D21 契约的实现在 **type_terms.cr:tt_atom_of_term**(R2 P6 Task 3 整函数迁出: +// corearch 清单不含本文件,而 `load_ccr` 的项索引一致性校验 + corearch 回读通道 +// 需要它)——本层及上层调用点一律用该单源入口,**不再**在此留第二定义。 + +// 分类(纯函数):0 = 类型行面(**项可逆**才归此面)/ 1 = 辅码面 / 2 = 无面。 +fn sh_tk_face_of_code(opcode: int, tk: int) -> int { + if sh_tk_is_type_face(opcode) != 0 { + if sh_tk_row_state(tk) == 1 { + if tt_atom_of_term(sh_term_of_ti(tk)) == tk { return 0; } + } + return 1; // 复合行 / 越界 / 桥接缺口 / 负值 —— 一律辅码(码保真) + } + if opcode == IR_BOUNDS_CHECK || opcode == IR_DEREF || opcode == IR_STORE_PTR || + opcode == IR_SPAWN || opcode == IR_HOTPATCH_ROUTE { + return 1; + } + return 2; +} + +// 拆分器(**单源**;emit 与 `.cir` 装载侧共用):(opcode, tk) → (项槽, 辅码槽)。 +// 出参经两个全局返回(Core 无多返回值)——**读前必先调用**(函数入口先置默认值, +// 无残留状态)。规则: +// face 0 ⇒ 项 = `sh_term_of_ti(tk)`(此时构造已证可逆)、辅码 = 0; +// 其余 ⇒ 项 = -1、辅码 = tk(**码保真**:旗标/宽度/复合行/无面 op 的非零码 +// 一律原样承载,派生码因此逐字节 ≡ 旧混用码)。 +// 该调用**不进**影子对账计数(hits/entries 只统计判定站点;本函数是载体面派生, +// 冷/暖两态与判定历史无关)——故 sh_term_of_ti 的命中计数先存后复原。 +// entries(装填因子输入 = 真实占用槽数)**不复原**——复原即欺骗扩容守卫。 +fn sh_tk_split(opcode: int, tk: int) { + sh_tk_split_impl(opcode, tk, 1); +} + +// 装载侧变体(`.cir` 快照重派生):**不置 D23 失败位**。理由 = 判据面不同: +// · emit 路径(count_fail=1):类型面行的可译性是**真不变量**——行刚由前端/IR 生成 +// 产出,越界/译不出 = 编译器 bug(或桥接缺口)⇒ 拒绝落盘。 +// · 装载路径(count_fail=0):盘上的码是**不透明历史值**,而暖进程的类型表可能**更小** +// ——缓存命中跳过该函数的 IR 生成,而 ir_gen 自身会 `alloc_type` 新行 +// (ir_gen.cr:1277/:1283/:2434/:2491/:2792 的 addr/ref/cast/数组行)⇒ 冷路径存在的行 +// 在暖进程可能不存在。这是**预存的缓存面局限**(登记:报告 §5-④;β 盘面项索引 +// 承接面 = P6),不是本进程的编译器 bug。代码由辅码槽保真 ⇒ 派生码仍逐字节正确, +// 只有该项在本进程不可重建(term = -1)。缓存命中**绝不**因此让编译失败。 +fn sh_tk_split_load(opcode: int, tk: int) { + sh_tk_split_impl(opcode, tk, 0); +} + +fn sh_tk_split_impl(opcode: int, tk: int, count_fail: int) { + fac := sh_tk_face_of_code(opcode, tk); + g_sh_slot_term = -1; + g_sh_slot_aux = 0; + if fac == 0 { + g_sh_slot_term = sh_term_of_ti(tk); + } else { + // 码保真:旗标/宽度/计数/复合行码/无面 op 的非零码一律原样承载(含负码—— + // IR_SPAWN 的 tk = -1 = 「动态 spawn 计数」,是合法辅码,**不得**钳成 0)。 + // 辅码哨兵:0 = 无;非零(含负)= 该码本身。 + g_sh_slot_aux = tk; + // D23:类型面 op 的行**不可译**(越界/桥接缺口)⇒ 建项失败——复合行是 + // 域内可译 ⇒ 正常路径,不置位。 + if count_fail != 0 { + if sh_tk_is_type_face(opcode) != 0 && sh_tk_row_state(tk) == 0 { g_tk_face_fail = g_tk_face_fail + 1; } + } + } +} + +// 槽 → 派生码(**唯一**派生点;dataflow.cr 的 lower_to_ccr / ir_gen.cr 的 emit / +// cir_cache.cr 的装载侧共用):辅码 ≠ 0 ⇒ 辅码;无项 ⇒ 0;否则 `atom_of(项)` +// (不可逆 ⇒ -1:显式、响亮,绝不近似成 0/1)。 +fn sh_dfn_code_of_slots(tk_term: int, tk_aux: int) -> int { + if tk_aux != 0 { return tk_aux; } + if tk_term < 0 { return 0; } + return tt_atom_of_term(tk_term); +} + +// 兼容入口(自测/诊断面):单槽化前 `sh_tk_term_of_code(opcode, tk)` 的语义 = +// 「类型面派生出的项;其余 -1」——即拆分器的**项槽**输出,但**纯查询**:不写槽出参、 +// 不计 D23 失败位(诊断调用不得污染落盘闸;有副作用的生产路径一律走 sh_tk_split)。 +// 与 split 共用同一个分类函数 sh_tk_face_of_code ⇒ 规则单源,无第二份判定。 +fn sh_tk_term_of_code(opcode: int, tk: int) -> int { + t : ., mut = -1; + if sh_tk_face_of_code(opcode, tk) == 0 { t = sh_term_of_ti(tk); } + return t; +} + +// ─── 桥接统计(自测断言用;**R2 P5 Task 5**:影子期的 hits 通道已删——桥接缓存现有 +// 唯一可观测面 = 占用槽数 entries(装填因子守卫的输入,恒等于真实占用))─── +fn sh_map_entries() -> int { return g_term_map_entries; } + +// ─── R2 P3 Task 5:类型项文本化(诊断反例用)─── +// 用途 = `tt_witness` 的反例项 → 人可读文本(P3 计划 Task 5 Step 3「诊断含反例值」)。 +// **只读、零 str_intern**:`.ccr` 的 STR 段 = 编译期 g_strs 全量(P2b 附录 A.3-②)⇒ 渲染 +// 不得驻留新串(诊断文本一律用 dyn 串拼接;istr_get 读既有串)。 +// 形态 = 结构化 JSON 风格(`int`/`string`/`sequence`/`{int, string}`/`A ∪ B`/`¬A`…); +// 未识别 tag → "?"(**不谎报**形态)。深度上限 = 避免病态项上的栈爆(超限 → "…")。 +fn tt_display_at(t: int, depth: int) -> string { + if t < 0 { return "?"; } + if depth > 24 { return "…"; } + if t >= tt_count() { return "?"; } + tg := tt_tag(t); + if tg == TT_BOT { return "⊥"; } + if tg == TT_TOP { return "⊤"; } + if tg == TT_TOP_K { return "⊤" + int_str(tt_a(t)); } + if tg == TT_UNION { return tt_display_at(tt_a(t), depth + 1) + " ∪ " + tt_display_at(tt_b(t), depth + 1); } + if tg == TT_INTER { return tt_display_at(tt_a(t), depth + 1) + " ∩ " + tt_display_at(tt_b(t), depth + 1); } + if tg == TT_NOT { return "¬" + tt_display_at(tt_a(t), depth + 1); } + if tg == TT_MU { return "μ" + int_str(tt_a(t)) + "." + tt_display_at(tt_b(t), depth + 1); } + if tg == TT_VAR { return "X" + int_str(tt_a(t)); } + if tg == TT_NIL { return "nil"; } + if tg == TT_CONS { return "cons(" + tt_display_at(tt_a(t), depth + 1) + ", " + tt_display_at(tt_b(t), depth + 1) + ")"; } + if tg == TT_ATOM { + ak := tt_a(t); + // AK_NAMED 单列提前返回(**不是**风格偏好):若并入下方 if-else 链,链内既有赋值分支 + // 又有 return 分支 ⇒ 自托管 checker 报 `error[TC02]: If branches have different types` + // (本文件属 `check src/compiler` 语料 ⇒ 会给 CI 的 check 作业加一条**新**诊断—— + // 实测 build10 前版本命中:`tt_display_at` 的 AK_FN/AK_NAMED 分支)。 + if ak == AK_NAMED { + nb := tt_b(t); + if nb >= 0 { return "named#" + int_str(nb); } + return "named"; + } + head : ., mut = ""; + if ak == AK_INT { head = "int"; } + else if ak == AK_DEX { head = "dex"; } + else if ak == AK_STRING { head = "string"; } + else if ak == AK_BOOL { head = "bool"; } + else if ak == AK_UNIT { head = "unit"; } + else if ak == AK_NEVER { head = "never"; } + else if ak == AK_CHAR { head = "char"; } + else if ak == AK_DYN { head = "dyn"; } + else if ak == AK_NULL { head = "null"; } + else if ak == AK_PRODUCT { head = "product"; } + else if ak == AK_SUM { head = "sum"; } + else if ak == AK_SEQUENCE { head = "sequence"; } + else if ak == AK_REF { head = "ref"; } + else if ak == AK_PTR { head = "ptr"; } + else if ak == AK_FN { head = "fn"; } + else { head = "atom#" + int_str(ak); } + // 参数链(AK_REF 的槽 0 是 mut 标记:语义为 &/&mut,照桥接约定还原) + if ak == AK_REF && tt_c(t) >= 0 && tt_tag(tt_c(t)) == TT_CONS { + m := tt_a(tt_c(t)); + mut_s : ., mut = "&"; + if tt_tag(m) == TT_ATOM && tt_a(m) == AK_UNIT && tt_b(m) == 1 { mut_s = "&mut "; } + rest := tt_b(tt_c(t)); + if rest >= 0 && tt_tag(rest) == TT_CONS { return mut_s + tt_display_at(tt_a(rest), depth + 1); } + return mut_s; + } + p := tt_c(t); + if p < 0 || tt_tag(p) != TT_CONS { return head; } + s : ., mut = head + "<"; + k : ., mut = 0; + loop { + if p < 0 { break; } + if tt_tag(p) != TT_CONS { break; } + if k > 0 { s = s + ", "; } + s = s + tt_display_at(tt_a(p), depth + 1); + p = tt_b(p); + k = k + 1; + } + return s + ">"; + } + return "?"; +} + +fn tt_display(t: int) -> string { return tt_display_at(t, 0); } + +// ═══════════════ R2 P1 影子判定挂点 + 分类计数 + 摘要/转储:**R2 P5 Task 5 已删除** ═══════════════ +// 本段原为影子通道本体——站点挂点(`sh_site_begin`,checker 的判定点各调一次)+ 站点 id↔语义表 +// (1 hotpatch-ret / 2 generic-arg / 3 generic-apply-base / 4 unify-fallback / 5 fn-body-ret / +// 6 退役(EXPR_BINARY+OP_ASSIGN,P5 T4 删)/ 7 if-branch / 8 assign-node / 9 struct-field-type / +// 10 array-elem-type)+ 分类计数(total/agree/old_stricter/old_looser/unknown×4 拆因)+ 差异环形 +// 缓冲(前 256 条 {site,t1,t2,old_ok,kind})+ 判定对账 `sh_compare` + 摘要 `sh_report` + 转储 +// `sh_dump_write` + kind 空间(`sh_kind_name`)。 +// **删除依据(D24/D26-①)**:对照物 `type_equal_legacy` 已在 P5 Task 4 删除 ⇒ 对账停摆(全语料 +// 72 档 `decisions=0`、全差异桶 0——gate 实测见 p5-task5-report)。**替代回归网** = 冻结基线同源 +// 对拍 + 行为探针 + 突变控制 + 三态纪律(ICE04);**不得**引用已下线的计数/摘要/站点直方图。 +// 十个判定点本体**一律保留**(站点下线 ≠ 判定点下线,D25):见 checker.cr 的 type_compat_strict / +// type_compat_sym 调用点(hotpatch 返回位 / unify_types 三径 / 函数体返回位 / if 合并 / 赋值 / +// struct 字段 / 数组元素)——它们**无站点标注、无计数、无开关**,纯判定。 + +// ═══════════════ R2 P3 Task 0:引擎展开层(named/struct/enum/generic-apply → 结构项)═══════════════ +// **语义边界(裁错即判定全面漂移——本层第一性约束)**:展开**只服务**两条路径 +// ① 满足判定 `T <: 形状`(P3 Task 2 横切形状 / Task 6 用户接口;本批 = type_engine.cr 的 +// `iface_satisfies` 占位) +// ② 穷尽性域/模式项(`sh_enum_domain_term` / `sh_variant_term`,P3 Task 3) +// **等价/包含判定(`type_equal` → `type_equal_engine` → `sh_term_of_ti` → `ty_equiv`)一律 +// 不经本层**:命名类型在等价面的身份 = **原子名义**(AK_NAMED + 行号;`sh_term_of_ti` 的 +// NAMED/GENERIC_* 分支本批**一行未动**)。把展开项接进等价判定 = 两个**同形不同名**的 +// struct 被判等价(语义漂移),并推翻 P2a 对拍归零基线(`old_stricter=0 / old_looser=0` +// ——那条基线正是 P5 Task 4 删除 `type_equal_legacy` 的前提,删除已落地)。守门用例 = +// `unf.nominal_*` 三例(双钉:展开项**结构相等** ∧ 桥接项 `ty_equiv` 判**确定不同 0** +// 〔T3 之后;T4 起 `type_equal` 判 false 且**零 ICE04 诊断** = 引擎真判〕)。 +// 深度 = **一层**:字段/变体子项一律走既有 `sh_term_of_ti`(其命名/泛型分支**不展开**) +// ⇒ 递归结构(`struct Node { next: *Node }`)在第二层即终止;一层不足 → 按引擎既有约定 +// **-1 上抛**(-1 = 不可展开/未覆盖面,消费方按三态处理——**不得**静默判否)。 +// **非纯只读(诚实登记)**:字段类型节点经 checker 的 `res_type_node` 解析(**唯一实现**—— +// 本层不复制其语义),故对非基型字段可追加类型行(alloc_type/alloc_named_type)、并可能报 +// EC_N_GENERIC_TYPE。与 checker.cr EXPR_FIELD(:2583-2606)的字段解析**同源同径**。本批零 +// 生产消费者;接入判定路径(Task 2/6)前须评估「判定中途分配类型行」的时序影响。 +// 预算:展开步进计入引擎预算(`tt_step`)——耗尽 → -1(不得当 0/1)。范式与 +// `ty_budget_reset` 的窗口隔离兼容(消费方在查询前 reset ⇒ 展开步数计入该窗口)。 + +// ─── 展开项布局(Task 3/4/6 消费约定;按**引擎比较面**设计)─── +// ⚠ 引擎的原子比较只看 `a`(原子类)与 `c`(参数链)两槽(lit_implies:175-182;`b` 槽是 +// **标注**、不参与比较——AK_NAMED 的 b 槽 = 行号即此约定)⇒ **语义身份必须入参数链**, +// 否则两类不同声明会静默合并(例:两个枚举的同名变体、两个不同枚举的域)。 +// struct → tt_atom(AK_PRODUCT, ti, 字段项链) // 链序 = 声明序;**结构项**(同形同项) +// enum → 各变体项之**并**(左深 union)= 域 // 空枚举 → ⊥(无值可取,见下) +// 变体项 → tt_atom(AK_SUM, ti, [枚举名令牌, 变体名令牌]) // 身份 = (枚举, 变体) 两名**入链** +// (P3 Task 1:两名以 **sh_name_token 原子**上链,不再用裸 name 索引——见该函数注记) +// 变体项**不含 payload**:枚举表只存 payload 的**裸 TY 码**(parser.cr:1601 的 unpack_type; +// 非基型塌缩为 0 = TY_INT,与「payload 是 int」**不可区分**)⇒ 含入即静默谎报;故 payload +// 不入项(**未覆盖面登记**:payload 面的满足判定归 Task 4——若需要,须先扩枚举表存 payload +// 类型节点,照 struct 的 OFF_SI_FIELD_TYPE_NODES 先例)。穷尽性按变体身份判定不受影响 +// (现状模式面亦不按 payload 分解——EXPR_ENUMPAT 的子模式是另一条路径)。 + +UNF_MAP_INIT_CAP : int = 256; + +// 展开缓存随类型表作废(同 sh_map_reset 同因同式:init_types 行号空间复用 ⇒ 陈旧 ti→展开项 +// 命中 = 把上一个请求的类型结构安到当前行上;长驻 corelsp 每请求一次 init_types)。 +fn sh_unf_map_reset() { + g_unf_map_cap = 0; + g_unf_entries = 0; + g_unf_hits = 0; +} + +fn sh_unf_cap_init() { + if g_unf_map_cap <= 0 { + nc : ., mut = UNF_MAP_INIT_CAP; + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_unf_map = nb; + g_unf_map_cap = nc; + } +} + +// 无守卫探测(**只可在扩容守卫之后调用**;重建重放借道)——形态照 sh_map_find_nogrow +// (typed 探测用 ret + break + 尾 return;函数体不以无 break 的 loop 收尾——自托管 checker +// 对该形态有 TF01 误报面,见该函数注记)。 +fn sh_unf_find_nogrow(ti: int) -> int { + cap := g_unf_map_cap; + p : ., mut = tt_mod(ti, cap); + ret : ., mut = -1; + loop { + k := r64(g_unf_map, p * 16); + if k < 0 { ret = p; break; } + if k == ti { ret = p; break; } + p = p + 1; if p >= cap { p = 0; } + } + return ret; +} + +fn sh_unf_find(ti: int) -> int { + sh_unf_cap_init(); + // 装填因子守卫(**必须在探测前**):表满且键不存在时开放寻址永不退出(死循环) + if (g_unf_entries + 1) * 2 >= g_unf_map_cap { sh_unf_rehash(); } + return sh_unf_find_nogrow(ti); +} + +// 扩容 = 重建 + **重放既有条目**(引擎 grow_tt_index / 桥接 sh_map_rehash 同式);计数随重放重算 +fn sh_unf_rehash() { + old := g_unf_map; + old_cap := g_unf_map_cap; + nc : ., mut = old_cap * 2; + if nc < UNF_MAP_INIT_CAP { nc = UNF_MAP_INIT_CAP; } + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + g_unf_map = nb; + g_unf_map_cap = nc; + g_unf_entries = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + k := r64(old, j * 16); + if k >= 0 { + s := sh_unf_find_nogrow(k); + w64(g_unf_map, s * 16, k); + w64(g_unf_map, s * 16 + 8, r64(old, j * 16 + 8)); + g_unf_entries = g_unf_entries + 1; + } + j = j + 1; + } +} + +fn sh_unf_hits() -> int { return g_unf_hits; } +fn sh_unf_entries() -> int { return g_unf_entries; } + +// 枚举变体名 → 变体下标(-1 = 该枚举无此变体名)。线性扫(容量批 T3 起无变体上限)。 +fn sh_enum_variant_index(ea: int, name_ni: int) -> int { + i : ., mut = 0; + ret : ., mut = -1; + loop { + if i >= ei_variant_count(ea) { break; } + if ei_variant_name(ea, i) == name_ni { ret = i; break; } + i = i + 1; + } + return ret; +} + +// struct 第 fi 字段 → 项(-1 = 不可展开)。泛型形参代入照 checker.cr EXPR_FIELD 的路径 +// (那里返回 ti、消费方是 checker;此处返回项):字段节点为 EXPR_IDENT 且名字 ∈ 该 struct 的 +// 泛型形参名表 ⇒ 从**泛型应用实参**(ga,g_gen_apply_data: [count, arg1, …])取对应实参项; +// 其余(非形参 / 非应用形态 / 实参缺位)→ 经 res_type_node 解析字段类型节点。 +// 注:非应用形态下泛型形参保持**名义**(res_type_node 取该形参的类型行 ⇒ AK_NAMED 原子)—— +// 不假装知道实参是什么(EXPR_FIELD 的落空路径同款)。嵌套代入(`Box[Box[T]]`)现状不可达: +// 形参只做**一层**代入(与 checker 现状一致;F4 同族缺陷,TODO #21 / Task 5 收口)。 +fn sh_struct_field_term(sa: int, fi: int, ga: int) -> int { + fnode := si_field_type_node(sa, fi); + if fnode < 0 { return -1; } + if ast_kind(fnode) == EXPR_IDENT { + fni := ast_int_val(fnode); + if is_struct_generic(sa, fni) { + if ga >= 0 && get_type_kind(ga) == TYP_GENERIC_APPLY { + gstart := get_type_extra(ga); + gcnt := r64(g_gen_apply_data, gstart * 8); + gpi : ., mut = 0; + loop { + if gpi >= si_generic_count(sa) { break; } + if si_generic_name(sa, gpi) == fni { + if gpi < gcnt { return sh_term_of_ti(r64(g_gen_apply_data, (gstart + 1 + gpi) * 8)); } + break; // 实参缺位 → 落空到名义项(下方 res_type_node),不得取越界槽 + } + gpi = gpi + 1; + } + } + } + } + fti := res_type_node(fnode); + if fti < 0 { return -1; } + return sh_term_of_ti(fti); +} + +// struct 命名行 / 泛型应用行 → AK_PRODUCT(参数链 = 逐字段项,**声明序**)。 +// -1 = 不可展开(既非命名行也非泛型应用行 / 名字未声明为 struct / 任一字段不可展开 / 预算耗尽)。 +fn sh_struct_term(ti: int) -> int { + if ti < 0 { return -1; } + if get_type_kind(ti) < 0 { return -1; } // 行号越界(ti ≥ g_type_count) + sa := find_struct_row_of(ti); + if sa < 0 { return -1; } + slot := sh_unf_find(ti); + if r64(g_unf_map, slot * 16) == ti { + g_unf_hits = g_unf_hits + 1; + return r64(g_unf_map, slot * 16 + 8); + } + ga : ., mut = -1; + if get_type_kind(ti) == TYP_GENERIC_APPLY { ga = ti; } + // CONS 链**逆序构造**(i 自末尾向前):DAG 项不可变,正向追加需改写已建项;逆序构造的 + // 结果顺序仍 = 声明序(照 sh_tuple_to_product 同款同因)。 + tail : ., mut = tt_nil(); + i : ., mut = si_field_count(sa) - 1; + loop { + if i < 0 { break; } + if tt_step() == -1 { return -1; } // 预算耗尽 → 上抛(不得当「空 product」) + ft := sh_struct_field_term(sa, i, ga); + if ft < 0 { return -1; } // 任一字段不可展开 ⇒ 整项不可展开(不缓存) + tail = tt_cons(ft, tail); + i = i - 1; + } + term := tt_atom(AK_PRODUCT, ti, tail); + // 写回前**重探**(字段解析期间可能已扩容——照 sh_term_of_ti 注记②同款) + slot2 := sh_unf_find(ti); + if r64(g_unf_map, slot2 * 16) == ti { + g_unf_hits = g_unf_hits + 1; + return r64(g_unf_map, slot2 * 16 + 8); + } + w64(g_unf_map, slot2 * 16, ti); + w64(g_unf_map, slot2 * 16 + 8, term); + g_unf_entries = g_unf_entries + 1; + return term; +} + +// 变体名 → 变体项(穷尽性**模式项**用;与域中的对应变体项**同一构造**——两者必须逐位相同, +// 否则补集判定失真)。name_ni 必须是该枚举**声明过的**变体名(否则 -1:不得为未声明变体臆造项)。 +// ti 接受命名行与泛型应用行(变体身份与实参无关——payload 不入项时 Option[int]/Option[str] +// 的变体集相同,正是穷尽性所需)。 +fn sh_variant_term(ti: int, name_ni: int) -> int { + ea := find_enum_row_of(ti); + if ea < 0 { return -1; } + if sh_enum_variant_index(ea, name_ni) < 0 { return -1; } + // P3 Task 1:身份两名经 **sh_name_token** 上链(旧约定 = 裸 name 索引入链——索引用作 + // 链元素时会被结构比较当术语下标解释,见 sh_name_token 注记);两构造点(域/模式项) + // 仍逐位同一 ✓ + return tt_atom(AK_SUM, ti, tt_cons(sh_name_token(ei_name(ea)), tt_cons(sh_name_token(name_ni), tt_nil()))); +} + +// enum 命名行 / 泛型应用行 → **域** = 各变体项之并(左深 union)。 +// -1 = 不可展开(非枚举行 / 预算耗尽)。空枚举(0 变体)→ ⊥ = 无值可取(**唯一**的 +// 「展开成功但域为空」形态;Task 3 判穷尽时对该形态须显式裁决——空域在补集语义下恒穷尽)。 +fn sh_enum_domain_term(ti: int) -> int { + if ti < 0 { return -1; } + if get_type_kind(ti) < 0 { return -1; } + ea := find_enum_row_of(ti); + if ea < 0 { return -1; } + slot := sh_unf_find(ti); + if r64(g_unf_map, slot * 16) == ti { + g_unf_hits = g_unf_hits + 1; + return r64(g_unf_map, slot * 16 + 8); + } + acc : ., mut = tt_bot(); + i : ., mut = 0; + loop { + if i >= ei_variant_count(ea) { break; } + if tt_step() == -1 { return -1; } + v := sh_variant_term(ti, ei_variant_name(ea, i)); + if v < 0 { return -1; } + acc = tt_union(acc, v); + i = i + 1; + } + slot2 := sh_unf_find(ti); + if r64(g_unf_map, slot2 * 16) == ti { + g_unf_hits = g_unf_hits + 1; + return r64(g_unf_map, slot2 * 16 + 8); + } + w64(g_unf_map, slot2 * 16, ti); + w64(g_unf_map, slot2 * 16 + 8, acc); + g_unf_entries = g_unf_entries + 1; + return acc; +} + +// 接口 → 形状项(方法集 = product of fn)——**仍为占位,恒 -1**(阻塞精确定位见下): +// 现状接口方法签名存**裸 TY_***(parser.cr:1671/1685 的 `unpack_type` ⇒ 非原生类型节点一律 +// ═══════════════ R2 P3b Task 6(Step 1/3):签名规范项 + 接口形状项 ═══════════════ +// 语义(spec §2.3「interface I → 形状类型项:方法集 = product of fn 原子(方法签名各自为 +// fn(τ₁…τₙ)->τ 项)」):本段是**签名项化**的唯一构造点。Step 1 交付了类型节点槽 +// (`OFF_IFM_PARAM_NODES` / `OFF_IFM_RET_NODE`)⇒ 签名首次可用**类型**而非映射层码表达。 +// +// ⚠ 为何需要**规范形**(而不是直接复用 `sh_term_of_ti` 的项):判定走引擎 `ty_sub`,而 +// 引擎在「不变槽」上按**节点同一性**比较链元素(`tt_list_same`),且对 AK_NAMED **不展开** +// (P0 未覆盖面②)。直接桥接的项有两个身份缺陷: +// ① `TYP_GENERIC_APPLY` 行经 `alloc_type` **裸分配**(不去重)⇒ 同型的两次出现是**不同** +// 节点 ⇒ 同签名被判不同(假违反); +// ② 序列项的 b 槽(N / 固定性)在节点同一性下**入身份** ⇒ `[int;3]` 与 `[int;4]` 判不同 +// ——与「N 不入身份」不变量(R1 裁决 / P3a Task 1)相悖(N 面由常量档承担)。 +// 规范形把**身份维度**入项、其余归一:泛型应用 = 基名行(named_dedup 已按名规范) + 实参链; +// 序列 = N 归 -1;指针 = asp 不入项(照桥接);命名行 = 行(按名规范);引用 = mut 标记入项 +// (身份维度,照桥接)。⇒ **同型恒同节点、异型恒异节点**,引擎的 `ty_sub`(顶原子 AK_FN 的 +// 不变槽链比较)即给出签名相等判定(1/0;-1 面只剩建项失败)。 +// dyn(位图行不参与规范形)与未知 kind ⇒ -1(未覆盖面,**不得**当 0/1)。 +// 零 str_intern:全部走既有 ni/行(.ccr STR 段守)。 +fn sh_sig_term_of_ti(ti: int) -> int { + if ti < 0 { return -1; } + k := get_type_kind(ti); + if k < 0 { return -1; } + if k == TYP_BASE { + d := get_type_data(ti); + ak := sh_base_ak(d); + if ak < 0 { return -1; } + return tt_atom(ak, ti, -1); + } + if k == TYP_NAMED || k == TYP_GENERIC_PARAM { + // 命名行:named_dedup 按名规范(同名恒同行);泛型形参行 = 声明点行(跨声明不同行 = + // 已登记面:签名内含泛型形参的匹配只在同一声明内成立) + // R2 P5 Task 3:身份链与 sh_term_of_ti 的命名分支**逐位同构**(同一 sh_name_token + // 链形态)——双构造点契约(计划停条件③;改动必须两点同时)。 + return sh_named_identity_term(ti); + } + if k == TYP_NULL { return sh_null_term(); } + if k == TYP_ARRAY || k == TYP_SLICE { + el := sh_sig_term_of_ti(get_type_data(ti)); + if el < 0 { return -1; } + return tt_atom(AK_SEQUENCE, -1, tt_cons(el, tt_nil())); + } + if k == TYP_PTR { + el2 := sh_sig_term_of_ti(get_type_data(ti)); + if el2 < 0 { return -1; } + return tt_atom(AK_PTR, -1, tt_cons(el2, tt_nil())); + } + if k == TYP_REF { + el3 := sh_sig_term_of_ti(get_type_data(ti)); + if el3 < 0 { return -1; } + return tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(get_type_extra(ti)), tt_cons(el3, tt_nil()))); + } + if k == TYP_OPTIONAL { + el4 := sh_sig_term_of_ti(get_type_data(ti)); + if el4 < 0 { return -1; } + return tt_union(el4, sh_null_term()); + } + if k == TYP_TUPLE { + cnt := get_type_data(ti); + start := get_type_extra(ti); + tail : ., mut = tt_nil(); + i : ., mut = cnt - 1; + loop { + if i < 0 { break; } + et := sh_sig_term_of_ti(r64(g_gen_apply_data, (start + i) * 8)); + if et < 0 { return -1; } + tail = tt_cons(et, tail); + i = i - 1; + } + return tt_atom(AK_PRODUCT, -1, tail); + } + if k == TYP_GENERIC_APPLY { + // R2 P5 Task 3:形态与 sh_term_of_ti 的 APPLY 分支**同构**(基型行 + [基名令牌, + // 实参项…])——不再只挂裸实参链(那是「基型行当身份」的旧形态:与桥接侧命名分支 + // 失同步 ⇒ 两构造点对同型给出不同节点)。差别只剩实参项递归走**本构造点** + // (variant=1:签名面 N 不入项,见 sh_apply_args_chain 注)。 + return sh_apply_identity_term(ti, 1); + } + return -1; // TYP_DYN(位图行)/ 未知 kind:未覆盖面 +} + +// 类型**节点** → 签名项(节点契约:<0 = 无节点 ⇒ unit;照 checker 的 `type_node > 0` 约定, +// 但用 -1 哨兵以免与「节点 0」混同)。res_type_node 的解析面 = 与其它消费点同一入口。 +fn sh_sig_term_of_node(node: int) -> int { + if node < 0 { return sh_sig_term_of_ti(TI_UNIT); } + ti := res_type_node(node); + return sh_sig_term_of_ti(ti); +} + +// 接口方法签名的**参数项**(idx 自 0 起)。接收者槽(self_mode ≠ 0 且 idx == 0)= unit +// **占位**——接收者模式(self/&self/&mut self)是调用约定而非类型集维度,由消费方**显式比较** +//(`sh_iface_self_mode` / `sh_func_self_mode`);不为它造令牌原子的理由:令牌的 b 槽在引擎 +// 的类级比较(只看 a/c 两槽)下不可见 ⇒ 模式 1/2/3 会互相判等(静默)。 +fn sh_iface_sig_param_term(ii: int, mi: int, idx: int) -> int { + if ii < 0 || mi < 0 || idx < 0 { return -1; } + if idx >= MAX_IFACE_METHOD_PARAMS { return -1; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + if r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) <= idx { return -1; } + smode := r64(g_ifaces, mbase + OFF_IFM_SELF_MODE); + if idx == 0 && smode != 0 { return sh_sig_term_of_ti(TI_UNIT); } + return sh_sig_term_of_node(r64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + idx * 8)); +} + +fn sh_iface_sig_ret_term(ii: int, mi: int) -> int { + if ii < 0 || mi < 0 { return -1; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + return sh_sig_term_of_node(r64(g_ifaces, mbase + OFF_IFM_RET_NODE)); +} + +fn sh_iface_self_mode(ii: int, mi: int) -> int { + if ii < 0 || mi < 0 { return -1; } + return r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD + OFF_IFM_SELF_MODE); +} + +// impl 侧(函数行)签名的**参数项**(idx 自 0 起;从 AST 形参链前扫解析——形参节点不连续, +// 每参的类型节点插在其后,照 ir_gen/checker 同款扫描约定)。接收者槽同占位约定。 +fn sh_func_sig_param_term(fi: int, idx: int) -> int { + if fi < 0 || idx < 0 { return -1; } + fn_node := fi_ast_node(fi); + if fn_node < 0 { return -1; } + if ast_kind(fn_node) != EXPR_FN { return -1; } + if idx >= fi_param_count(fi) { return -1; } + pn := ast_b(fn_node); // EXPR_FN: b = first param node(a = 函数名 ni——不是形参入口) + pi : ., mut = 0; + loop { + if pn < 0 { return -1; } + if pn >= g_ast_count { return -1; } + if ast_kind(pn) == EXPR_PARAM { + if pi == idx { + mode := ast_int_val(pn); + if mode == -1 { return -1; } // 变参:签名项不可表达(登记) + if mode != 0 { return sh_sig_term_of_ti(TI_UNIT); } + return sh_sig_term_of_node(ast_data(pn)); + } + pi = pi + 1; + } + pn = pn + 1; + } + return -1; +} + +fn sh_func_self_mode(fi: int) -> int { + if fi < 0 { return -1; } + fn_node := fi_ast_node(fi); + if fn_node < 0 { return -1; } + if ast_kind(fn_node) != EXPR_FN { return -1; } + if fi_param_count(fi) <= 0 { return 0; } + pn := ast_b(fn_node); // EXPR_FN: b = first param node + loop { + if pn < 0 { return -1; } + if pn >= g_ast_count { return -1; } + if ast_kind(pn) == EXPR_PARAM { + m := ast_int_val(pn); + if m == -1 { return -1; } + return m; + } + pn = pn + 1; + } + return -1; +} + +// impl 侧函数行的返回项(解析规则 = check_func 的返回位逐字同款:非基型节点走 +// res_type_node;基型/无节点走码表(域含 NEVER、缺 7 ⇒ unit 兜底))。 +fn sh_func_sig_ret_term(fi: int) -> int { + if fi < 0 { return -1; } + fn_node := fi_ast_node(fi); + if fn_node < 0 { return -1; } + if ast_kind(fn_node) != EXPR_FN { return -1; } + rnode := ast_type_val(fn_node); + ret_ti : ., mut = TI_UNIT; + if rnode > 0 && ast_kind(rnode) != 0 { + ret_ti = res_type_node(rnode); + } else { + rm := ty_code_to_ti(fi_return_type(fi)); + if rm >= 0 && rm != TI_DYN { ret_ti = rm; } + } + return sh_sig_term_of_ti(ret_ti); +} + +// 函数行 → **fn 项**:`tt_atom(AK_FN, -1, [返回项, 参数项…])`(链序 = 返回在首,参数按声明序; +// 逆序构造,照 sh_tuple_to_product 同因)。b 槽留空(引擎比较只看 a/c ⇒ 方法名不参与判定)。 +fn sh_func_sig_term(fi: int) -> int { + if fi < 0 { return -1; } + pc := fi_param_count(fi); + if pc < 0 || pc > MAX_FN_PARAMS { return -1; } + rt := sh_func_sig_ret_term(fi); + if rt < 0 { return -1; } + tail : ., mut = tt_cons(rt, tt_nil()); + i : ., mut = pc - 1; + loop { + if i < 0 { break; } + pt := sh_func_sig_param_term(fi, i); + if pt < 0 { return -1; } + tail = tt_cons(pt, tail); + i = i - 1; + } + return tt_atom(AK_FN, -1, tail); +} + +// 接口 → 形状类型项(spec §2.3):`tt_atom(AK_PRODUCT, -1, [fn 项…])`,链序 = 方法声明序 +// (与 `g_ifaces` 方法表同序——消费方按表索引取成员,见 type_engine 的轴 C)。成员 fn 项的 +// **b 槽 = 方法名 ni**(消费方的查表键;引擎不比较 b,本层显式读取)。 +// -1 = 不可建(接口名未声明 / 方法数越界(>16)/ 任一签名项不可建 / 参数数越界(>8))。 +// 零方法接口 ⇒ 空 product(`tt_nil()` 链)——空洞满足的载体(判定面见轴 C)。 +// 三态纪律:-1 = 未覆盖面,**不得**被消费方当 0(不满足)或 1(满足)用。 +fn sh_iface_shape_term(iface_ni: int) -> int { + if iface_ni < 0 { return -1; } + ii := find_iface(iface_ni); + if ii < 0 { return -1; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + if mc < 0 { return -1; } + if mc > MAX_IFACE_METHODS { return -1; } + tail : ., mut = tt_nil(); + i : ., mut = mc - 1; + loop { + if i < 0 { break; } + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + i * ESZ_IFMETHOD; + m_ni := r64(g_ifaces, mbase + OFF_IFM_NAME); + pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT); + if pc < 0 || pc > MAX_IFACE_METHOD_PARAMS { return -1; } + rt2 := sh_iface_sig_ret_term(ii, i); + if rt2 < 0 { return -1; } + ptail : ., mut = tt_cons(rt2, tt_nil()); + pj : ., mut = pc - 1; + loop { + if pj < 0 { break; } + pterm := sh_iface_sig_param_term(ii, i, pj); + if pterm < 0 { return -1; } + ptail = tt_cons(pterm, ptail); + pj = pj - 1; + } + tail = tt_cons(tt_atom(AK_FN, m_ni, ptail), tail); + i = i - 1; + } + return tt_atom(AK_PRODUCT, -1, tail); +} + +// 形状项的第 idx 个成员(fn 项;-1 = 越界/非形状项)。消费方(轴 C)按方法表索引取用。 +fn sh_shape_member_at(shp: int, idx: int) -> int { + if shp < 0 || idx < 0 { return -1; } + if tt_tag(shp) != TT_ATOM { return -1; } + if tt_a(shp) != AK_PRODUCT { return -1; } + c := tt_c(shp); + i : ., mut = 0; + loop { + if i > idx { return -1; } + if c < 0 { return -1; } + if tt_tag(c) != TT_CONS { return -1; } + if i == idx { return tt_a(c); } + c = tt_b(c); + i = i + 1; + } + return -1; +} + +// ─── R2 P3b Task 2:序列行的**固定性**读取(range 消费点的表示层维度)─── +// 返回:固定长序列的 N(数组,≥0)/ -1(视图(切片)/ 非序列行 / 不可译行)。 +// 依据 = 序列项的 **b 槽**(`sh_seq_term`:数组 = N、切片/视图 = -1)——**表示提示层**, +// 语义判定不看 N(spec §5.1「N 是表示提示,语义判定不看」;T1 裁定「固定性位不入身份/比较」, +// 消费者逐条登记 = 常量档检查 + 表示层 + 本 Task 的横切形状面)。 +// 用途 = checker 的 range 索引分支(`arr[lo..hi]`):固定长 ⇒ 产视图(`[T]`);视图 ⇒ 现状 +// `TI_UNIT`(IP_INDEX_RANGE 未接线,登记面)。**不得**把它读作身份/子类型维度(那是常量档的 +// 职责;本站只决定「range 在本行上产不产视图」的结果形态,行为与改动前逐行相同)。 +fn sh_seq_fixed_len_of_ti(ti: int) -> int { + t := sh_term_of_ti(ti); + if t < 0 { return -1; } + if tt_tag(t) != TT_ATOM { return -1; } + if tt_a(t) != AK_SEQUENCE { return -1; } + return tt_b(t); +} + +// ═══════════════ R2 P3 Task 3:match 穷尽性消费点(补集空性 + 具体变体反例)═══════════════ +// 判据本体 = 引擎的**补集空性**(`ty_exhaustive`:domain \ ⋃patterns 的可满足性;P0 已落)。 +// **引擎不在本 Task 文件面内**(协调者裁决)⇒ 引擎侧的两处缺口一律在**消费点**消化: +// ① **域守卫**(域限定,计划显式登记):仅**枚举 scrutinee** 判穷尽(域 = 变体集); +// scrutinee 非枚举/域不可展开(struct/原生/int/string/bool…)⇒ **不判**(-1)。非枚举域 +// **不得**假装穷尽、**不得**假装不穷尽——故本层零诊断、零硬错误。 +// ② **模式面守卫**:任一臂模式**不可忠实映射** ⇒ **不判**(-1)。不可映射 = 字面量模式 +// (int/string/char/bool)/ struct 模式 / 非本枚举的变体名。**不得**把它们当 ∅(凭空判 +// 「不穷尽」)或当 ⊤(凭空判「穷尽」)——三态纪律:未知一律 -1,由消费方零动作。 +// ③ **反例的具体值**:引擎 witness 的原始形态 = 各析取支之**并**(`tt_norm` 不做空支净化: +// 缺失 B 时 = (A∩¬A)∪(B∩¬A),按 `ty_equiv` 与「缺失变体」**不等**——Task 0 §5-② 实测) +// ⇒ 反例**不**取 witness 项,而按**变体逐项覆盖位**命名(`sh_match_first_missing`): +// 与引擎判据同源(同一变体项构造)但独立计算 ⇒ 两路一致性由自测用例钉死。 +// 模式项约定(与展开层/引擎对齐):通配 `_` 与绑定 ident = `tt_top()`(引擎侧 ⊤ 吸收 = 覆盖 +// 一切;值域不绑);变体模式 = `sh_variant_term(scrut_ti, vni)`——**与域内对应变体同一构造** +// (两个构造点不同 ⇒ 补集判定失真)。 + +// 臂模式 → 类别:1 = ⊤(通配 `_` / 绑定 ident)/ 2 = 枚举变体模式(须再判归属)/ 0 = 不可映射。 +fn sh_match_pat_kind(pat_node: int) -> int { + if pat_node < 0 { return 0; } + k := ast_kind(pat_node); + if k == EXPR_WILDCARD { return 1; } + if k == EXPR_IDENT { return 1; } // 绑定 = 通配(值域不绑;与 ir_gen 的按值比较臂不同层) + if k == EXPR_ENUMPAT { return 2; } + return 0; // 字面量 / struct 模式 / EXPR_NONE ⇒ 不可映射(登记) +} + +// 变体模式 → **本枚举**变体下标(-1 = 不属本枚举 / 名字不可解析 / scrutinee 不可展开)。 +// 名字两形态(parser.cr parse_pattern 的产物):裸变体名(`Red`)/ 限定名(`Color.Red` +// ——parser 的 name + "." + variant 拼接)。限定名取**末点后段**为变体名(口径与 ir_gen.cr +// get_variant_name_idx 一致;此处**不调 str_intern**——`.ccr` STR 段 = g_strs 全量, +// 见 P2b 附录 A.3-② 的硬判据约束)。前缀不是本枚举名 ⇒ -1(该模式对本域无覆盖,但按②取 +// **不可判**而非 ∅)。变体名比较用 str_eq 逐项(不驻留新串)。 +fn sh_match_pat_variant(scrut_ti: int, pat_node: int) -> int { + ea := find_enum_row_of(scrut_ti); + if ea < 0 { return -1; } + if pat_node < 0 { return -1; } + // 名字槽 = **a**(parser.cr parse_pattern:`alloc_node(EXPR_ENUMPAT, ni, …)`;ir_gen 的 + // 消费点同用 ast_a 取之。**不是** int_val——EXPR_IDENT 才把名字放 int_val)。 + pname := istr_get(ast_a(pat_node)); + slen := str_len(pname); + dot : ., mut = -1; + i : ., mut = 0; + loop { + if i >= slen { break; } + c := get_char(pname, i); + if str_eq(c, ".") != 0 { dot = i; } + i = i + 1; + } + vname : ., mut = pname; + if dot >= 0 { + head := str_sub(pname, 0, dot); + if str_eq(head, istr_get(ei_name(ea))) == 0 { return -1; } + vname = str_sub(pname, dot + 1, slen - dot - 1); + } + vi : ., mut = 0; + loop { + if vi >= ei_variant_count(ea) { return -1; } + if str_eq(istr_get(ei_variant_name(ea, vi)), vname) != 0 { return vi; } + vi = vi + 1; + } + return -1; +} + +// 变体下标 → 变体项(-1 = 越界/scrutinee 不可展开);与 sh_variant_term 的唯一入口关系 +fn sh_match_variant_term(scrut_ti: int, vi: int) -> int { + ea := find_enum_row_of(scrut_ti); + if ea < 0 { return -1; } + if vi < 0 || vi >= ei_variant_count(ea) { return -1; } + return sh_variant_term(scrut_ti, ei_variant_name(ea, vi)); +} + +// ─── 容量批 T3(裁-CAP-2 (a)):match 覆盖位**无界位图**(替换 2^vi 单 int)─── +// 动机:变体上限解除后 `2^vi` 在 vi ≥ 63 溢出 ⇒ 覆盖判定失真(e70 实测 SIGFPE 136)。 +// 位图 = 字节缓冲(每字节 8 位)+ 位下标寻址(本语言无按位与 ⇒ 除法/取模,照 iface_permits +// 惯例);**栈纪律**:`mc_begin` 记长度、`mc_end` 清零回退 ⇒ 嵌套 match 互不污染。 +fn grow_cov_bits(needed: int) { + if needed <= g_cov_cap { return; } + nc := g_cov_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc); _dyncpy(g_cov_bits, g_cov_cap, nb); + z : ., mut = g_cov_cap; + loop { if z >= nc { break; } store8(nb, z, 0); z = z + 1; } + g_cov_bits = nb; g_cov_cap = nc; +} +// 进入一次 match 覆盖收集:返回当前长度(供 mc_end 回退) +fn mc_begin() -> int { return g_cov_len; } +fn mc_add(vi: int) { + if vi < 0 { return; } + grow_cov_bits(vi / 8 + 1); + if vi + 1 > g_cov_len { g_cov_len = vi + 1; } + b := bu8(g_cov_bits, vi / 8); + pw : ., mut = 1; + k : ., mut = vi % 8; + loop { if k <= 0 { break; } pw = pw * 2; k = k - 1; } + if (b / pw) % 2 == 0 { store8(g_cov_bits, vi / 8, b + pw); } +} +fn mc_has(vi: int) -> int { + if vi < 0 { return 0; } + if vi >= g_cov_len { return 0; } + if vi / 8 >= g_cov_cap { return 0; } + b := bu8(g_cov_bits, vi / 8); + pw : ., mut = 1; + k : ., mut = vi % 8; + loop { if k <= 0 { break; } pw = pw * 2; k = k - 1; } + return (b / pw) % 2; +} +// 首个未覆盖位(-1 = 全覆盖);三态口径与 sh_match_first_missing 同 +fn mc_first_missing(count: int) -> int { + i : ., mut = 0; + loop { + if i >= count { return -1; } + if mc_has(i) == 0 { return i; } + i = i + 1; + } + return -1; +} +// 退出:清零本层用过的位并回退长度(嵌套安全) +fn mc_end(saved: int) { + i : ., mut = saved; + loop { + if i >= g_cov_len { break; } + if i / 8 < g_cov_cap { store8(g_cov_bits, i / 8, 0); } + i = i + 1; + } + g_cov_len = saved; +} + +// 覆盖位(**遗留 API,仅供 selftest 的 ≤62 域使用**):bit(vi) = 2^vi(本语言无移位 ⇒ +// 循环乘 2)。**生产路径自容量批 T3 起改用无界位图 `mc_*`**(本函数在 vi ≥ 63 溢出—— +// 旧前提「MAX_ENUM_VARIANTS = 16 保证不溢出」已随变体上限解除而失效)。 +fn sh_match_bit(vi: int) -> int { + if vi < 0 { return 0; } + b : ., mut = 1; + k : ., mut = vi; + loop { if k <= 0 { break; } b = b * 2; k = k - 1; } + return b; +} + +// 首个**未覆盖**变体下标(-1 = 全覆盖 = 反例面为空)。反例命名 = 本函数(头注③);位测试照 +// iface_permits 的 (bits / bit) % 2 惯例(**无按位与运算符**;bits ≥ 0、bit > 0 ⇒ 取模非负)。 +fn sh_match_first_missing(cover_bits: int, count: int) -> int { + i : ., mut = 0; + loop { + if i >= count { return -1; } + b := sh_match_bit(i); // i ≥ 0 ⇒ b ≥ 1(除零不可达) + if (cover_bits / b) % 2 == 0 { return i; } + i = i + 1; + } + return -1; +} + +// 穷尽性三态:1 = 穷尽(补集空)/ 0 = 不穷尽(有反例变体)/ -1 = **不判**(域不可展开 / +// 模式不可映射 / 引擎未知=预算耗尽)。消费方:0 → 诊断 + 反例命名;1/-1 → 零动作。 +// R2 P3 Task 4:域面扩一条 —— `T?`(TYP_OPTIONAL)的域 = `T ∪ null`(sh_match_domain_term)。 +fn sh_match_exhaustive(scrut_ti: int, pat_terms: int, unmappable: int) -> int { + if unmappable != 0 { return -1; } + dom := sh_match_domain_term(scrut_ti); + if dom < 0 { return -1; } // 非枚举/非可选域 ⇒ 不判(域限定的显式登记) + return ty_exhaustive(dom, pat_terms); +} + +// ═══════════════ R2 P3 Task 4:联合/可选(`T?` = `T ∪ null`)的 match 域面 ═══════════════ +// 域:枚举行 → 变体集之并(Task 0/3 的 sh_enum_domain_term);**TYP_OPTIONAL 行 → T ∪ null**。 +// 分支(两分,与 spec §5.4 的「T? 两分支:有值 + None」一致): +// 0 = 有值部分(`Some(…)` 模式;项 = 内层项本身——`T?` 的值域含裸 T 值,见 checker 的行语义) +// 1 = null 部分(`None` 模式;项 = sh_null_term()) +// 三态纪律:非可选 scrutinee ⇒ -1(不判);可选 scrutinee 上的**异域模式**(别的变体名/字面量) +// ⇒ -1(不可映射,同 Task 3 的②:不得当 ∅ 或 ⊤)。 +// 名字形态:仅裸名 `Some`/`None`(**限定名不接受**——可选无类型名可作前缀;用户声明 +// `enum Option` 时那走枚举域那条路,与本路互不干扰)。 +fn sh_match_domain_term(ti: int) -> int { + if ti < 0 { return -1; } + if get_type_kind(ti) == TYP_OPTIONAL { + in5 := sh_term_of_ti(get_type_data(ti)); + if in5 < 0 { return -1; } // 内层不可译 ⇒ 域不可展开(不判) + return tt_union(in5, sh_null_term()); + } + return sh_enum_domain_term(ti); +} + +// 可选模式 → 分支下标(0 = 有值 / 1 = null;-1 = 不可映射或非可选 scrutinee)。 +// 名字槽 = ast_a(EXPR_ENUMPAT 契约,Task 3 §6-②);`Some`/`None` 是关键字名。 +fn sh_match_opt_pat(scrut_ti: int, pat_node: int) -> int { + if scrut_ti < 0 { return -1; } + if get_type_kind(scrut_ti) != TYP_OPTIONAL { return -1; } + if pat_node < 0 { return -1; } + pname := istr_get(ast_a(pat_node)); + if str_eq(pname, "Some") != 0 { return 0; } // `Some(…)` 与裸 `Some` 同判(载荷绑定不参与) + if str_eq(pname, "None") != 0 { return 1; } + return -1; +} + +// 分支下标 → 覆盖项(与 sh_match_domain_term 的析取支**同一构造**——两个构造点不同 ⇒ 补集失真)。 +fn sh_match_opt_term(scrut_ti: int, part: int) -> int { + if part == 0 { return sh_term_of_ti(get_type_data(scrut_ti)); } + return sh_null_term(); +} + +// ─── R2 P3 Task 4:枚举变体**载荷项**(T0 交接 ① 的消费面;载荷类型节点列的唯一读取点)─── +// 背景(Task 0 §5-①):枚举表旧布局只存载荷**裸 TY 码**(OFF_EV_TYPES),非基型一律塌缩为 +// 0 = TY_INT ⇒ 「载荷是 int」与「载荷是 string/命名类型/泛型形参」**不可区分**。本任务按 +// struct 先例补 **OFF_EV_TYPE_NODES 列**(parser 随裸码同写类型节点;见 parser.cr 枚举分支), +// 本函数即该列的忠实读取点:泛型形参按泛型应用行的实参**代入**(照 sh_struct_field_term 的 +// 路径——一层代入,嵌套代入属 F4 同族缺陷/TODO #21)。 +// 返回:单载荷 = 该载荷项;多载荷 = AK_PRODUCT 链(**声明序**,逆序构造);**0 载荷(tag 变体)→ -1** +// (语义域外登记:载荷为空 ≠ 载荷 unit——不发明「tag 变体 = 单点」的项,Task 5/6 若需要另裁)。 +// -1 = 不可展开(非枚举行 / 无此变体名 / 无载荷 / 载荷节点缺失 / 任一载荷不可译 / 预算耗尽)。 +fn sh_variant_payload_at(ea: int, vi: int, i: int, ga: int) -> int { + pn := ei_variant_type_node(ea, vi, i); + if pn < 0 { return -1; } + if ast_kind(pn) == EXPR_IDENT { + pni := ast_int_val(pn); + gi2 : ., mut = 0; + loop { + if gi2 >= ei_generic_count(ea) { break; } + if ei_generic_name(ea, gi2) == pni { + // 泛型形参:泛型应用行的对应实参(缺位 → 落空到名义,照 sh_struct_field_term) + if ga >= 0 && get_type_kind(ga) == TYP_GENERIC_APPLY { + gstart2 := get_type_extra(ga); + gcnt2 := r64(g_gen_apply_data, gstart2 * 8); + if gi2 < gcnt2 { return sh_term_of_ti(r64(g_gen_apply_data, (gstart2 + 1 + gi2) * 8)); } + } + break; + } + gi2 = gi2 + 1; + } + } + pti := res_type_node(pn); + if pti < 0 { return -1; } + return sh_term_of_ti(pti); +} + +fn sh_variant_payload_term(ti: int, name_ni: int) -> int { + ea := find_enum_row_of(ti); + if ea < 0 { return -1; } + vi := sh_enum_variant_index(ea, name_ni); + if vi < 0 { return -1; } + tc := ei_variant_type_count(ea, vi); + if tc <= 0 { return -1; } + ga : ., mut = -1; + if get_type_kind(ti) == TYP_GENERIC_APPLY { ga = ti; } + tail : ., mut = tt_nil(); + i : ., mut = tc - 1; + loop { + if i < 0 { break; } + if tt_step() == -1 { return -1; } + pt := sh_variant_payload_at(ea, vi, i, ga); + if pt < 0 { return -1; } + tail = tt_cons(pt, tail); + i = i - 1; + } + if tc == 1 { return tt_a(tail); } // 单载荷:链首即载荷项(不套空实义 product) + return tt_atom(AK_PRODUCT, ti, tail); +} + +// ─── R2 P3 Task 4:枚举载荷列入库断言(调试通道 `--verify-evp-nodes`,默认关)─── +// 报文 = `[enum-payload-verify] enums=E variants=V slots=S collapses=C bad=B`,返回 bad +// (>0 ⇒ 调用方 rc=1)。断言:**每个载荷槽都有类型节点**(< 0 = parser 未写 = 本任务的前置 +// 缺口复发)。`collapses` = 裸码列说 int(TY_INT=0)而**节点列不是 int 基型节点**的槽数—— +// 即「裸码丢失了真实载荷类型」的可数证据(T0 §5-① 的仓库级量化);它是**信息量**(≥ 0 正常), +// 不是失败(bad 才算失败)。只读、只计数、不写任何表。 +fn sh_evp_verify() -> int { + bad : ., mut = 0; + slots : ., mut = 0; + collapsed : ., mut = 0; + variants : ., mut = 0; + ei : ., mut = 0; + loop { + if ei >= g_enum_count { break; } + vi : ., mut = 0; + loop { + if vi >= ei_variant_count(ei) { break; } + variants = variants + 1; + tc2 := ei_variant_type_count(ei, vi); + ti2 : ., mut = 0; + loop { + if ti2 >= tc2 { break; } + slots = slots + 1; + code := ei_variant_type(ei, vi, ti2); + pn2 := ei_variant_type_node(ei, vi, ti2); + if pn2 < 0 { + bad = bad + 1; + } else if code == TY_INT { + if ast_kind(pn2) != 0 || ast_type_val(pn2) != TY_INT { collapsed = collapsed + 1; } + } + ti2 = ti2 + 1; + } + vi = vi + 1; + } + ei = ei + 1; + } + print("[enum-payload-verify] enums="); + print(int_str(g_enum_count)); + print(" variants="); + print(int_str(variants)); + print(" slots="); + print(int_str(slots)); + print(" collapses="); + print(int_str(collapsed)); + print(" bad="); + println(int_str(bad)); + return bad; +} diff --git a/src/compiler/type_engine.cr b/src/compiler/type_engine.cr new file mode 100644 index 00000000..7f6b0d0c --- /dev/null +++ b/src/compiler/type_engine.cr @@ -0,0 +1,823 @@ +// === type_engine.cr === +// R2 P0:类型项判定引擎(语义包含 = 集合语义的值包含判定,spec §1/§3)。 +// 三态约定(spec §3.3):1 = 成立;0 = 不成立;**-1 = 预算耗尽/未覆盖**(禁止当 0 用)。 +// +// 判定路线(P0 落地形态): +// 规范化(tt_norm:NNF + DNF)→ 逐 product 覆盖 → 字面蕴含(含原子类互斥公理) +// → μ 展开用「假设-判定」memo(余归纳:进行中的 (a,b) 记为真,标准递归子类型做法) +// → 每步计预算,超限置 g_ty_exhausted 并返回 -1(不静默、不猜)。 +// +// 未覆盖面(P0 显式登记,命中即 g_ty_uncovered = 1,不当作「不成立」): +// ① 参数化原子的参数位**变型规则** = R2 P3 Task 1 **已落地**(`ty_variance_of` + +// `tt_list_variance`:AK_SEQUENCE/AK_PTR/AK_REF 三构造子)。**协变槽**已判(只读 ref +// 元素 = ty_sub 三态);**不变槽的非同形参数** = R2 P5 Task 3b **已收口**(`te_elem_cmp` +// 三态:令牌按节点同一性 / 类型项按 ty_equiv ⇒ 确定不同回 0、仅域外回 -1——「链元素皆 +// 类型项或身份令牌」的排序分派即 P0 登记时缺的那个不变量,见 tt_list_variance_at 注记); +// ② AK_NAMED 的具体行不展开(命名类型的结构定义在 P2 接入 checker 后可用)—— +// R2 P5 Task 3 **部分收口**:命名面按**身份链**判定(同链 1 / 链异 0 / 域外 -1,见 +// `te_named_pair` 头注);**结构展开**仍不进判定面(P3a 反证:同形不同名 struct 判等)。 +// 残留域外面 = 空链/位置错位/AK_NEVER·AK_DYN 混类(登记于 P5 Task 3 报告); +// ③ μ 的**空递归**(如 μX.X)保守判为可空。 +// +// 原子类 id 与 g_types 行解耦(spec §1):natives 1:1 映射,结构/命名原子挂 g_types 行 +// (ti = -1 = 类级)。 + +// ─── 原子类 id(AK_*)─── +AK_INT : int = 0; AK_DEX : int = 1; AK_STRING : int = 2; AK_BOOL : int = 3; +AK_UNIT : int = 4; AK_NEVER : int = 5; AK_CHAR : int = 6; AK_DYN : int = 7; +AK_PRODUCT : int = 8; AK_SUM : int = 9; AK_SEQUENCE : int = 10; +AK_REF : int = 11; AK_PTR : int = 12; AK_FN : int = 13; AK_NAMED : int = 14; +// R2 P3 Task 4(联合/可选):`null`(`None` 值的类型)——**原生第九员**,值域 = 单点。 +// 语义(spec §5.4):`T?` = `T ∪ null`(桥接侧把 TYP_OPTIONAL 行译作该原子与内层项之并)。 +// 公理(ak_disjoint):与一切异类原子**不相交**(含 AK_UNIT / AK_NEVER / AK_NAMED——null 不是 +// 任何别的原子类;与 AK_DYN 保持 DYN 的相容规则);自身相等(x == y 早退)。 +// 可空:AK_NULL 是**有值**类型(唯一值 None)⇒ ty_inhabited = 1(引擎的 inh 只判字面矛盾, +// 无需特例)。无类型参数(ty_variance_of 落表外默认不变,链恒空)。 +// 注册表:**不入 iface_registry 条目表**(IFACE_ENTRY_COUNT = 13 为 P2b 判据硬值;且本原子 +// 无操作许可 —— 算术/逻辑/条件门对其全拒,正是期望行为)⇒ `iface_kind_of` 对承载行回 -1。 +AK_NULL : int = 15; + +// ─── 字面收集侧缓冲(product 的字面集)─── +fn grow_ty_lits(needed: int) { + if needed < g_ty_lits_cap { return; } + nc : ., mut = g_ty_lits_cap * 2; + if nc < 64 { nc = 64; } + if nc < needed { nc = needed + 64; } + nb := alloc(nc * 8); + _dyncpy(g_ty_lits, g_ty_lits_cap * 8, nb); + g_ty_lits = nb; + g_ty_lits_cap = nc; +} + +fn ty_lits_reset() { g_ty_lits_count = 0; } + +fn ty_lits_push(t: int) { + if g_ty_lits_count + 1 > g_ty_lits_cap { grow_ty_lits(g_ty_lits_count + 1); } + w64(g_ty_lits, g_ty_lits_count * 8, t); + g_ty_lits_count = g_ty_lits_count + 1; +} + +fn ty_lit_at(k: int) -> int { return r64(g_ty_lits, k * 8); } + +// product(∩-链)或单字面 → 字面集(写 g_ty_lits) +fn lit_collect(i: int) { + if tt_tag(i) == TT_INTER { lit_collect(tt_a(i)); lit_collect(tt_b(i)); return; } + ty_lits_push(i); +} + +// ─── 原子类互斥公理(P0 表:不同原子类两两不相交;AK_DYN = ⊤ 与一切相容)─── +fn ak_disjoint(x: int, y: int) -> int { + if x == y { return 0; } + if x == AK_DYN || y == AK_DYN { return 0; } + // R2 P3 Task 4:null 是**它自己的**单点原子类——与其余一切类不相交。位置在 AK_NEVER/ + // AK_NAMED 两条之前:与 NEVER 相交(两者都无公共值);与 NAMED 的「不得断言互斥」无关 + // (NAMED 保守律针对的是「命名类型可能是任何结构类型」——null 不是结构类型,任何用户 + // 声明类型的值集里都不含 None 这个值 ⇒ 断互斥是**可断言**的,不违 P0 终审 Important B)。 + if x == AK_NULL || y == AK_NULL { return 1; } + if x == AK_NEVER || y == AK_NEVER { return 1; } + // AK_NAMED 的具体行不展开(未覆盖面②)→ **不得断言互斥**(P0 终审 Important B 实证: + // named 可能是任何结构类型的别名,断言互斥即不可能断言) + if x == AK_NAMED || y == AK_NAMED { return 0; } + return 1; +} + +// 字面的原子类(⊥/⊤/VAR 无类 → -1) +fn ty_ak_of(i: int) -> int { + t := tt_tag(i); + if t == TT_ATOM { return tt_a(i); } + if t == TT_NOT && tt_tag(tt_a(i)) == TT_ATOM { return tt_a(tt_a(i)); } + if t == TT_TOP_K { return tt_a(i); } + return -1; +} + +// ─── 字面矛盾判定(product 是否为空集的一阶判据)─── +// 字面所属原子类(ATOM/⊤ₖ 正位置,及其否定)——-1 = 无类(VAR/复合/⊥/⊤) +fn lit_class_of(i: int) -> int { + t := tt_tag(i); + if t == TT_ATOM || t == TT_TOP_K { return tt_a(i); } + if t == TT_NOT { + it := tt_tag(tt_a(i)); + if it == TT_ATOM || it == TT_TOP_K { return tt_a(tt_a(i)); } + } + return -1; +} + +fn lit_is_neg(i: int) -> int { if tt_tag(i) == TT_NOT { return 1; } return 0; } + +// 单对字面是否互斥(⟦p⟧ ∩ ⟦q⟧ = ∅)——**规则要窄**: +// 正×正 异类 → ∅ ✓;正×负 **异类不空**(int ∩ ¬string = int ≠ ∅——P0 Task 4 自测实证 +// 早期版本把这条写错,导致 witness 恒 -1);正׬⊤ₖ 同类 → ∅;负×负 永不空。 +fn lit_empty_pair(p: int, q: int) -> int { + if tt_tag(p) == TT_BOT || tt_tag(q) == TT_BOT { return 1; } + if tt_not(q) == p { return 1; } // X ∧ ¬X + pc := lit_class_of(p); + qc := lit_class_of(q); + if pc < 0 || qc < 0 { return 0; } + pneg := lit_is_neg(p); + qneg := lit_is_neg(q); + if pneg == 0 && qneg == 0 { return ak_disjoint(pc, qc); } // 正 × 正 + if pneg == 0 && qneg != 0 { + if tt_tag(q) == TT_NOT && tt_tag(tt_a(q)) == TT_TOP_K && pc == qc { return 1; } + return 0; + } + if pneg != 0 && qneg == 0 { + if tt_tag(p) == TT_NOT && tt_tag(tt_a(p)) == TT_TOP_K && pc == qc { return 1; } + return 0; + } + return 0; // 负 × 负 +} + +// 独立缓冲版(避免侧缓冲互踩:sub_cover 需同时持有 p/q 两侧字面集) +fn lits_contradictory_buf(buf: string, n: int) -> int { + i : ., mut = 0; + loop { + if i >= n { break; } + p := r64(buf, i * 8); + j : ., mut = i + 1; + loop { + if j >= n { break; } + q := r64(buf, j * 8); + if lit_empty_pair(p, q) == 1 { return 1; } + j = j + 1; + } + i = i + 1; + } + return 0; +} + +// 收集 product 的字面到独立缓冲,返回缓冲(元素数 = g_ty_lits_count) +// 返回型 = `string`:`alloc` 在类型模型里就是 `() -> string`(checker.cr 的 bi_add("alloc", +// TI_STR)),本值流转面也全按字节缓冲走(`r64`/`w64`/`lits_contradictory_buf` 皆收 string) +// ——旧声明 `-> int` 是真·类型洗白(TF01;check 作业 rc=1 划界之一,本批修)。 +fn lits_copy(p: int) -> string { + ty_lits_reset(); + lit_collect(p); + n := g_ty_lits_count; + buf := alloc((n + 1) * 8); + i : ., mut = 0; + loop { if i >= n { break; } w64(buf, i * 8, ty_lit_at(i)); i = i + 1; } + return buf; +} + +fn lits_contradictory() -> int { + return lits_contradictory_buf(g_ty_lits, g_ty_lits_count); +} + +// ═══════════ R2 P5 Task 3:命名面可判定域(身份链判定)═══════════ +// 背景:AK_NAMED 原子原为**一律 -1**(未覆盖面②「命名行不展开」)⇒ 任何两个命名型比较都 +// 回落 legacy(checker 的 g_replace_unknown;**R2 P5 Task 4 已随 legacy 删除**——残留 -1 面 +// 现为 P-A 硬错 ICE04)。本任务把**可判定**的命名面接进引擎:身份 = +// 桥接层构造的**身份链**(ty_shadow.cr 的 sh_named_identity_term / sh_apply_identity_term: +// `[名字令牌]` / `[基名令牌, 实参项…]`),**不是**结构性展开(P3a 实测反证:把展开项接进 +// 等价面 ⇒ 同形不同名 struct 判等 + 推翻 P2a 归零基线,见 ty_shadow.cr 展开层头注)。 +// +// 规则(三态;-1 = **适用域外**,调用方置 g_ty_uncovered 上抛——不得近似成 0/1): +// ① 链同(逐元素)⇒ 1;② 链异且两侧元素皆在可判定域内 ⇒ 0(确定不同); +// ③ 域外 ⇒ -1:空链(未覆盖面②的原形态:无身份的命名原子)/ 位置错位(令牌位放类型项或 +// 反之)/ **AK_NEVER·AK_DYN** 的混类(⊥/⊤ 语义未在本层建模——"never ⊆ X"、"X ⊆ dyn" +// 不属本批,保持 -1;其余类 vs 命名型 ⇒ 0 = 名义型与异类原子不交,与 legacy 跨 kind +// 同结论)。 +// 位置语义(链 = proper list;位置 0 = 身份令牌,位置 ≥ 1 = 泛型实参类型项): +// · 令牌位 → **节点同一性**(令牌不是类型集:拿 ty_equiv 比会把一切 AK_UNIT 令牌判等 +// ——P3 Task 1 实测的令牌碰撞类;见 tt_list_variance_at 注); +// · 实参位 → 引擎**自身的等价判定** ty_equiv(三态直传)——链元素是桥接构造的规范项 +// (同型 ⇒ 同节点),N 面(数组固定性 b 槽)经既有不变槽 b-忽略规则处理 +// (`[int;3]` 与 `[int;4]` 作实参仍判等 ✓)。 +// 与 legacy 的口径对齐(等价面):同链 ⇒ legacy 判 1;链异 ⇒ legacy 判 0。legacy 的判据 = +// kind 相同 + data 递归(命名行 data = 名字索引;named_dedup 使「同名 ⇔ 同行」⇒ 与令牌 +// 同一性一致)。**已登记未编码角落**:本链不区分 kind(TYP_NAMED vs TYP_GENERIC_PARAM +// 同名 ⇒ 判等,legacy 按 kind 判否)——需同名跨类比较才可达,可达性未证实(P5 T3 报告 §)。 +fn te_is_name_token(e: int) -> int { + if e < 0 || e >= tt_count() { return 0; } + if tt_tag(e) != TT_ATOM { return 0; } + if tt_a(e) != AK_UNIT { return 0; } + if tt_b(e) < 0 { return 0; } + return 1; +} + +// 实参位允许的项:类型项(原子/并/交/链/空链)。μ 变元/¬/裸值一律域外。 +// 注:AK_UNIT 原子(原生 unit 类型项)**在**域内——它与令牌同形(b ≥ 0),本层不按键区分: +// 实参位比较走 ty_equiv(unit vs int ⇒ 类不同 ⇒ 0,与 legacy 同结论;令牌不会出现在实参位, +// 令牌位的比较另有 te_token_cmp)。 +fn te_is_arg_term(e: int) -> int { + if e < 0 || e >= tt_count() { return 0; } + t := tt_tag(e); + if t == TT_ATOM || t == TT_UNION || t == TT_INTER || t == TT_CONS || t == TT_NIL { return 1; } + return 0; +} + +// 位置 0(身份令牌)比较;调用方已保证 e1 != e2。差异 ⇒ 确定不同(名字令牌的身份 = 节点)。 +fn te_token_cmp(e1: int, e2: int) -> int { + if te_is_name_token(e1) != 0 && te_is_name_token(e2) != 0 { return 0; } + return -1; // 位置错位(非令牌)⇒ 域外,不猜 +} + +// 位置 ≥ 1(泛型实参类型项)比较:引擎等价(0/1/-1 直传)。 +fn te_arg_cmp(e1: int, e2: int) -> int { + if te_is_arg_term(e1) == 0 || te_is_arg_term(e2) == 0 { return -1; } + return ty_equiv(e1, e2); +} + +// 身份链比较(三态):slot = 当前位置(0 = 令牌位)。链形不同 = 结构不同(proper list 前提: +// nil vs cons ⇒ 0;任一侧非链形(非 nil/cons)⇒ 域外 ⇒ -1)。 +fn te_chain_cmp(slot: int, p: int, q: int) -> int { + if p == q { return 1; } + if p < 0 || q < 0 { return 0; } // 空链 vs 非空链 + tp := tt_tag(p); + tq := tt_tag(q); + if tp != TT_CONS || tq != TT_CONS { + if tp == TT_NIL || tq == TT_NIL { return 0; } + return -1; + } + e1 := tt_a(p); + e2 := tt_a(q); + d : ., mut = 1; + if e1 != e2 { + if slot == 0 { d = te_token_cmp(e1, e2); } else { d = te_arg_cmp(e1, e2); } + } + if d == -1 { return -1; } + if d == 0 { return 0; } + return te_chain_cmp(slot + 1, tt_b(p), tt_b(q)); +} + +// ═══════════ R2 P5 Task 3b:不变槽元素比较(三态)═══════════ +// 背景(P5 Task 3 §6 残留面,本步收口):不变槽元素原为「结构同形(tt_type_elem_same) +// 否则 -1 + 未覆盖面」⇒ 元素是**已可判**的命名原子时(`[NA;2]` vs `[NB;2]`、`*NA` vs +// `*NB`、元组含异名元素)判不出 ⇒ 回落 legacy。本函数把「确定不同 ⇒ 0」从命名面扩到 +// **全部不变槽元素**(native/struct/token)。适用域(三态;-1 = 域外 ⇒ 调用方上抛): +// ① 两侧皆**身份令牌**(`te_is_name_token`:AK_UNIT ∧ b ≥ 0,= sh_name_token / +// sh_ref_mut_marker 的形态)⇒ **节点同一性**:不同节点 = 不同名字/标记 = 确定不同 +// (禁 ty_equiv:令牌不是类型集,同形不同名会被判等——P3 Task 1 实测的令牌碰撞类); +// ② 两侧皆**类型项**(te_is_arg_term)⇒ 引擎等价三态直传(`ty_equiv`)——命名/序列/ +// 原生元素各按其自有的可判定域递归(域外在内层置位上抛,本层不吞); +// ③ 其余(任一侧**既非令牌也非类型项**:μ 变元 / ¬ / ⊤ₖ / 裸值…)⇒ -1 + 未覆盖面位(不猜)。 +// 注:**排序错位**(名字/标记令牌 × 普通类型项,如 vs `int` 项)落 ②——令牌是 TT_ATOM, +// 按类型项面走 ty_equiv(异类原子不交 ⇒ 0,**不产生假等**);AK_UNIT 类**内部**的混排 +// (令牌 × 原生 unit 项)落 ①(形态二义,见下)。 +// 域内前提(同 (ak, slot) 上不出现排序混排——**构造点**保证,见 ty_shadow.cr 链构造段): +// AK_SUM(展开层变体项)链 = [枚举名令牌, 变体名令牌] ⇒ 槽 0/1 皆令牌 ⇒ ①;AK_REF 槽 0 +// (mut 标记)另有判定分支、槽 1 及其余构造子(SEQUENCE/PTR/PRODUCT/FN)链元素皆类型项 +// ⇒ ②;命名面链(AK_NAMED)先分派 te_named_pair,不经本函数。③ = **域外面**(可达:接口 +// 形状面把 `⊤ₖ(序列)` 放进可写 ref 的不变槽 ⇒ 该槽无判据——守门例 `x2.rw_view_unexpressible`; +// 本步对该面**保持 -1**,未动),非「不可达防御分支」。 +// ⚠ **AK_UNIT 形态二义**(登记):原生 unit 类型项 = tt_atom(AK_UNIT, 行号, -1) 与令牌 +// 同形(b ≥ 0)⇒ 两者结构不可分辨。选择「令牌先判」是**失效方向**要求:若走 ty_equiv, +// token × unit 同类同形 ⇒ 判**等**(假等 = 令牌碰撞类);先判令牌则最坏回 0 = 确定不同, +// 绝不产生假等。可达性:唯一原生 unit 项在 DAG 里**只有一个节点**(单行 + 去重)⇒ unit +// × unit 恒同节点(快路径);token × unit 需构造点在同一槽混排两排序——全构造点无此形态 +// (负控见 type_selftest.cr 的 t3b.* 段)。 +fn te_elem_cmp(p: int, q: int) -> int { + if p == q { return 1; } + if te_is_name_token(p) != 0 && te_is_name_token(q) != 0 { return 0; } + if te_is_arg_term(p) != 0 && te_is_arg_term(q) != 0 { return ty_equiv(p, q); } + g_ty_uncovered = 1; + return -1; +} + +// 命名面成对判定(三态):p/q 皆 TT_ATOM 且至少一侧 AK_NAMED(调用方保证)。 +fn te_named_pair(pk: int, p: int, qk: int, q: int) -> int { + if pk == qk { + // 双方皆 AK_NAMED(同类 ∧ 至少一侧 NAMED) + cp := tt_c(p); + cq := tt_c(q); + if cp < 0 || cq < 0 { return -1; } // 空链 = 无身份(未覆盖面②原形态)⇒ 域外 + return te_chain_cmp(0, cp, cq); + } + // 名义型 × 异类原子:确定不同(legacy 跨 kind 同结论)。守卫:命名侧必须带身份链; + // 异类侧不得是 AK_NEVER/AK_DYN(⊥/⊤ 语义未建模 ⇒ 域外,保持 -1)。 + if pk == AK_NAMED { + if tt_c(p) < 0 { return -1; } + if qk == AK_NEVER || qk == AK_DYN { return -1; } + return 0; + } + if tt_c(q) < 0 { return -1; } + if pk == AK_NEVER || pk == AK_DYN { return -1; } + return 0; +} + +// ─── R2 P6 Task 2(E-10):同类原子的「确定不交」判定(¬ 面专用)─── +// 用途:`lit_implies` 的 ¬ 面判 `lp ⊆ ¬x ⟺ lp ∩ x = ∅`;两侧**同类**原子时,类目互斥公理 +// (`ak_disjoint`)给不出信息(同类 ⇒ `x == y` 早退 0)⇒ 原实现判 0(**过判**:`B ⊄ ¬R` +// 与真值 1 相反,见 `type_selftest.cr` 的 t3 段登记注)。本函数按**身份链**补该判定。 +// +// 域(逐条见 P6 T2 域证明;**越域一律回 0 = 维持现状**,不新开未覆盖面): +// ① 域内:**AK_SUM 变体项**——链 = [枚举名令牌, 变体名令牌](`sh_variant_term`)⇒ 异链 = +// 异 (枚举, 变体) = 值集不交 ⇒ **1**;同链(DAG 去重 ⇒ 同节点 = 同一变体)⇒ 相交 ⇒ **0**。 +// 跨枚举同名变体亦判「不交」(链首令牌含枚举名,即 P3 Task 1 的 +// `unf.enum_variant_identity_scoped` 事实)。 +// ② 域外·类顶:`⊤ₖ(k)` 含该类**全部值** ⇒ 与同类原子相交非空 ⇒ 0(**必须显式排除**: +// `tt_top_k` 的 c 槽 = 0(非 -1)⇒ 仅靠空链守卫拦不住,会把 term 0 当链比较)。 +// ③ 域外·参数化构造子(SEQUENCE/PTR/REF/PRODUCT/FN)与原生类:**「链不同 ⇒ 不交」在 +// 该面不成立**(反例:`[int]` vs `[int ∪ string]` 元素项不同而值集相交;`&T` vs +// `&mut T` 的值集语义未建模)⇒ 维持现状 0。**不得**顺手扩域(负控 t2.neg_param_*)。 +// ④ 域外·无身份链(空链):照 `te_named_pair` 的「空链 = 无身份」登记面 ⇒ 维持现状 0 +// (**P1-a 已裁定**:域外逐位不变 ⇒ 零新增未覆盖面、零行为变化;P1-b(-1 + 未覆盖面) +// = 扩面,须 report-only 先行 ⇒ 另立登记,不在本批)。 +// ⑤ AK_SUM 链元素非令牌(手造畸形项)⇒ 不可判 ⇒ 同 P1-a ⇒ 0。 +// 三态说明:本函数**只回 0/1**,不置 `g_ty_uncovered`(域外面由调用点既有守卫承担)。 +// 生产可达性:`sh_variant_term`(ty_shadow.cr:823)恒造双令牌链 ⇒ ④/⑤ 生产不可达。 +fn atom_same_class_disjoint(p: int, q: int) -> int { + if tt_tag(p) == TT_TOP_K || tt_tag(q) == TT_TOP_K { return 0; } // ② 类顶 + if tt_a(p) != AK_SUM { return 0; } // ③ 非身份型原子(含原生/参数化) + cp := tt_c(p); + cq := tt_c(q); + if cp < 0 || cq < 0 { return 0; } // ④ 空链 = 无身份 + d := sum_chain_same(cp, cq); // 三态(见下) + if d == 1 { return 0; } // 同身份 ⇒ 相交 + if d == 0 { return 1; } // 异身份 ⇒ 确定不交 + return 0; // ⑤ 不可判(同 P1-a) +} + +// AK_SUM 变体项身份链的「同链」判定(三态:1 = 同链 / 0 = 确定异链 / -1 = 不可判)。 +// 链 = [枚举名令牌, 变体名令牌](唯一生产构造点 `sh_variant_term`,ty_shadow.cr:823)⇒ +// **逐元素皆令牌**,按**节点同一性**比较:异节点 = 异 (枚举, 变体) = 确定异链。 +// ⚠ **不得复用 `te_chain_cmp`**:它的槽位语义是命名面布局(槽 0 = 令牌 / 槽 ≥1 = 泛型实参 +// **类型项**),对 AK_SUM 的**槽 1(变体令牌)**会走 `te_arg_cmp → ty_equiv` ⇒ 两个不同令牌 +// 在结构比较下**判等**(令牌的 b 槽只是标注,引擎只看 a/c ⇒ 同形)⇒ 误判「同链」。 +// 实测(本批):首版用 `te_chain_cmp` 时三例 `t3.*_over_claim` 仍判 0,而 9 例负控全过 +// (N5 跨枚举在**槽 0** 被令牌规则救回)⇒ 补 N10「同枚举异变体」后才暴露。 +// 不可判面(空链 / 形状异常 / 非令牌元素)= 手造畸形项,生产不可达;调用方按 P1-a 归 0。 +fn sum_chain_same(p: int, q: int) -> int { + if p == q { return 1; } + if p < 0 || q < 0 { return -1; } + if tt_tag(p) != TT_CONS || tt_tag(q) != TT_CONS { return -1; } + e1 := tt_a(p); + e2 := tt_a(q); + if e1 != e2 { + if te_is_name_token(e1) == 0 || te_is_name_token(e2) == 0 { return -1; } + return 0; // 两侧皆令牌且异节点 ⇒ 确定异链 + } + return sum_chain_same(tt_b(p), tt_b(q)); +} + +// ─── 字面蕴含(⟦lp⟧ ⊆ ⟦lq⟧)─── +fn lit_implies(lp: int, lq: int) -> int { + if lp == lq { return 1; } + if tt_tag(lq) == TT_TOP { return 1; } + if tt_tag(lp) == TT_BOT { return 1; } + if tt_tag(lp) == TT_TOP { return tt_tag(lq) == TT_TOP; } + // lq = ⊤ₖ + if tt_tag(lq) == TT_TOP_K { + k := tt_a(lq); + if tt_tag(lp) == TT_TOP_K { return tt_a(lp) == k; } + if tt_tag(lp) == TT_ATOM { return tt_a(lp) == k; } + return 0; + } + // lq = ¬x:lp ⊆ ¬x ⟺ lp ∩ x = ∅ + if tt_tag(lq) == TT_NOT { + x := tt_a(lq); + // ¬P ⊆ ¬X ⟺ **X ⊆ P**(逆否)——P0 终审 Critical 1:早期写成 ty_sub(P, X)(方向反), + // 使 ¬A ⊆ ¬B 与 A ⊊ B 同时成立(自相矛盾)。 + if tt_tag(lp) == TT_NOT { return ty_sub(x, tt_a(lp)); } + // ¬(μ…) 等无类字面:未知(登记,保守不覆盖) + if lit_class_of(lp) < 0 || lit_class_of(x) < 0 { + if tt_tag(x) == TT_BOT { return 1; } + if tt_tag(lp) == TT_BOT { return 1; } + g_ty_uncovered = 1; + return -1; + } + pk := lit_class_of(lp); + xk := lit_class_of(x); + if pk == AK_NAMED || xk == AK_NAMED { g_ty_uncovered = 1; return -1; } // 不展开 → 未知 + // R2 P6 Task 2(E-10):同类 ⇒ 身份判据(异身份 ⇒ 确定不交 ⇒ lp ⊆ ¬x 成立)。 + // 异类面照旧走 ak_disjoint 的公理表——identity 只存在于**项**里,而 ak_disjoint 只见 + // 类 id(故本加强不得落在 ak_disjoint 本体:那会退化成「同类一律不交」= 假不交)。 + if pk == xk { return atom_same_class_disjoint(lp, x); } + if ak_disjoint(pk, xk) == 1 { return 1; } + return 0; + } + // 双方皆正原子:同类 + 参数链按**变型**比较(R2 P3 Task 1 取代 P0「仅同形判等」) + if tt_tag(lp) == TT_ATOM && tt_tag(lq) == TT_ATOM { + pk := tt_a(lp); + qk := tt_a(lq); + // R2 P5 Task 3:命名面(身份链)——取代原「AK_NAMED ⇒ 一律 -1」守卫。 + // 可判定域/域外三态见 te_named_pair 头注(P5 前此面 = unknown 清零对象) + if pk == AK_NAMED || qk == AK_NAMED { + d := te_named_pair(pk, lp, qk, lq); + if d == -1 { g_ty_uncovered = 1; return -1; } + return d; + } + if pk != qk { return 0; } + lv := tt_list_variance(pk, tt_c(lp), tt_c(lq)); + if lv == 1 { return 1; } + if lv == -1 { return -1; } // 未知(不定槽的 ty_equiv 三态负值):**上抛**,不得当 0 + // 确定不蕴含(不变槽查到确定不等价 / 协变槽 ty_sub 确定不成立 / 链形不同): + // P0 此处一律回 -1 + 未覆盖面(登记「参数仅同形判等」)——变型落地后它是**已判**面 + return 0; + } + return 0; +} + +// 参数链同形(结构相等;R2 P3 Task 1 起为**变型表的默认槽比较**——不变槽即此恒等) +fn tt_list_same(p: int, q: int) -> int { + if p == q { return 1; } + if p < 0 || q < 0 { return 0; } + if tt_tag(p) != TT_CONS || tt_tag(q) != TT_CONS { return 0; } + if tt_list_same(tt_a(p), tt_a(q)) == 0 { return 0; } + return tt_list_same(tt_b(p), tt_b(q)); +} + +// ─── R2 P3 Task 1:不变槽的「类型项相等」——序列项**固定性位(b)不入** ─── +// 背景(本任务实测):序列项的 b 槽是表示提示(N / 固定性,见 ty_shadow.cr 的 sh_seq_term), +// 但 b 会经**嵌套元素位的节点同一性**泄进判定:`[[int;3];2]` 的元素项(序列项)与 +// `[[int];2]` 的元素项**除 b 外逐位相同却节点不同** ⇒ tt_list_same 判「非同形」⇒ 引擎回 -1 +// (未覆盖面)⇒ 回落 legacy——① 身份路径出现 N/固定性(违 R1 不变量「N 只在常量档与表示层」); +// ② 站点措辞从长度专属退化为普通不匹配(实测探针 pN3:`return [s, s]` 给 +// 「Function return type mismatch」而非「Array length constraint not satisfied」)。 +// 修法:**双方皆 AK_SEQUENCE 原子**时按「忽略 b 的结构相等」递归(元素仍可能是序列 ⇒ 继续 +// 忽略);**其余构造子一律节点同一性(tt_list_same)**——AK_NAMED 的 b = 行号是**身份** +// ([S] 与 [T] 必须不同)、令牌原子(AK_SUM 的 name 令牌 / AK_REF 的 mut 标记)的 b 也是身份。 +// 守卫「皆 AK_SEQUENCE」使本函数**永不**解释令牌(令牌恒为 AK_UNIT 原子,见 sh_name_token / +// sh_ref_mut_marker)——旧约定(裸 name 索引入链)下的别名风险已随之消除。 +fn tt_type_elem_same(p: int, q: int) -> int { + if p == q { return 1; } + if p < 0 || q < 0 { return 0; } + if tt_tag(p) == TT_ATOM && tt_a(p) == AK_SEQUENCE && + tt_tag(q) == TT_ATOM && tt_a(q) == AK_SEQUENCE { return tt_seq_same(p, q); } + return tt_list_same(p, q); +} + +// 序列项相等(忽略 b):class 相同 + 参数链逐元素递归(元素位经 tt_type_elem_same 再判定)。 +fn tt_seq_same(p: int, q: int) -> int { + if p == q { return 1; } + cp := tt_c(p); + cq := tt_c(q); + if cp == cq { return 1; } + if cp < 0 || cq < 0 { return 0; } + if tt_tag(cp) != TT_CONS || tt_tag(cq) != TT_CONS { return 0; } + if tt_type_elem_same(tt_a(cp), tt_a(cq)) != 1 { return 0; } + return tt_type_elem_same(tt_b(cp), tt_b(cq)); +} + +// ═══════════════ R2 P3 Task 1:变型表(slot 单调性)+ 参数链按变型比较 ═══════════════ +// 语义(spec §5.1):只读视图**协变**(⟦槽a⟧ ⊆ ⟦槽b⟧ ⇒ 蕴含),可写视图**不变**(槽须等价)。 +// 0 = 不变 / 1 = 协变。**其余构造子默认不变**(= P0 同形判等逐位保持)。 +// 首版覆盖三构造子(计划 Task 1 Step 2 指定): +// · AK_SEQUENCE 槽 0(元素)= 不变:本语言数组/切片**可写**(实测探针 `s := a[0..3]; s[0] = 9;` +// rc=0)⇒ 元素协变不健全(写坏是静默错值级)。定长退役的**方向**面([T;N] <: [T])不走本表: +// N/固定性存于序列项的 **b 槽(标注,不入比较)**,方向由 checker 的 array_len_constraint_ok +// 承担(理由与参序约定见该函数注记——身份/子类型路径零 N 是 P2a 不变量)。 +// · AK_PTR 槽 0(元素)= 不变:可写裸指针(与 legacy 的 PTR 分支「只比元素」同语义)。 +// · AK_REF 槽 0 = **mut 标记**(不变,且比较**可判定**——标记是语法令牌非类型集,见 +// tt_list_variance_at);槽 1(元素)= **只读协变 / 可写不变**:变型依赖槽 0 的值 ⇒ 静态 +// 二元表表达不了该条件,故本表给只读侧(mut = 0)的默认值 1,可写侧由 tt_list_variance_at +// 依链首标记降为 0(**条件单点收敛在比较函数内**,本表不重复表达)。 +fn ty_variance_of(ak: int, slot: int) -> int { + if ak == AK_REF { + if slot == 0 { return 0; } + if slot == 1 { return 1; } + return 0; + } + if ak == AK_SEQUENCE { return 0; } + if ak == AK_PTR { return 0; } + return 0; // 表外构造子(含原生八员/命名类):不变 = 现状结构相等 +} + +// 参数链按变型比较(lit_implies 的正原子同形面入口)。返回 1 / 0 / **-1 = 未知**(上抛)。 +// 槽位语义(逐槽成对推进): +// · 协变槽 → ty_sub(槽a, 槽b):三态直传(-1 上抛;0 = 确定不蕴含 ⇒ 0); +// · AK_REF 槽 0(mut 标记)→ **节点同一性且判定**:标记是**语法令牌**(不同标记 = 确定 +// 不匹配 ⇒ **0**,不回 -1)——它**不是**类型集,故不适用「未知」保守性;回 -1 只会让 +// 调用方回落 legacy 把同一结论重算一遍(mut 面 legacy 本就比 extra)。标记项 = +// tt_atom(AK_UNIT, mut, -1)(b 槽 = 标记值,照 AK_NAMED 的 b = 行号同约定;见 +// ty_shadow.cr 的 sh_ref_mut_marker)。 +// · 其余不变槽 → 「同形(tt_type_elem_same)⇒ 同」,否则 **`te_elem_cmp` 三态** +// (R2 P5 Task 3b 落地):令牌按节点同一性 / 类型项按引擎等价 ⇒ 确定不同回 **0**(已判面)、 +// 仅域外回 **-1 + 未覆盖面**(上抛)。⚠ 早期(P0~P5 T3)此处一律「非同形 ⇒ -1」的理由是 +// 「链元素并非皆类型项」(展开层把身份令牌直接放链)——te_elem_cmp 的**排序分派**(先判 +// 令牌、再判类型项)即该不变量,令牌不再被 ty_equiv 当术语解释(P3 Task 1 实测的令牌 +// 碰撞类;M 改法红例 = 异枚举同名变体被判等),故加强面现在可安全落地。 +// 同形快路径:p == q → 1(DAG 去重使「同形同项」恒为同一节点 ⇒ 主流路径零额外开销,且 +// **改动前后逐位一致**)。链形不同(非 CONS / 负)→ 0(结构不同 = 现状语义)。 +fn tt_list_variance_at(ak: int, slot: int, mutv: int, p: int, q: int) -> int { + if p == q { return 1; } + if p < 0 || q < 0 { return 0; } + if tt_tag(p) != TT_CONS || tt_tag(q) != TT_CONS { return 0; } + ha := tt_a(p); + hb := tt_a(q); + if ak == AK_REF && slot == 0 { + if tt_list_same(ha, hb) != 1 { return 0; } // 标记槽:可判定(见上) + } else { + v : ., mut = ty_variance_of(ak, slot); + if ak == AK_REF && slot == 1 && mutv == 1 { v = 0; } // 可写 ref:元素降为不变 + if v == 1 { + s := ty_sub(ha, hb); + if s != 1 { return s; } + } else { + if tt_type_elem_same(ha, hb) != 1 { + // R2 P5 Task 3b:非同形 ⇒ 三态加强(确定不同 ⇒ 0;域外/内层未知 ⇒ -1 上抛, + // 位由 te_elem_cmp / 内层置起——本层不吞、不改标) + d := te_elem_cmp(ha, hb); + if d == -1 { return -1; } + if d == 0 { return 0; } + } + } + } + return tt_list_variance_at(ak, slot + 1, mutv, tt_b(p), tt_b(q)); +} + +fn tt_list_variance(ak: int, p: int, q: int) -> int { + mutv : ., mut = 0; + if ak == AK_REF { + // 槽 0 已在调用前判定同形(p == q 快路径或由本函数逐槽比较)——此处只取标记值: + // 链首 = 标记项(链形约定见 sh_ref_mut_marker;非标记形态保守取 0 = 只读) + if p >= 0 && tt_tag(p) == TT_CONS { + m := tt_a(p); + if tt_tag(m) == TT_ATOM && tt_a(m) == AK_UNIT { mutv = tt_b(m); } + } + } + return tt_list_variance_at(ak, 0, mutv, p, q); +} + +// ─── memo(假设-判定;0 = 假 / 1 = 真 / 2 = 进行中即假设真)─── +fn grow_ty_memo(needed: int) { + if needed < g_ty_memo_cap { return; } + nc : ., mut = 1024; + loop { if nc >= needed { break; } nc = nc * 2; } + nb := alloc(nc * 16); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); i = i + 1; } + nv := alloc(nc * 8); + i2 : ., mut = 0; + loop { if i2 >= nc { break; } w64(nv, i2 * 8, 0); i2 = i2 + 1; } + g_ty_memo_keys = nb; + g_ty_memo_vals = nv; + g_ty_memo_cap = nc; + g_ty_memo_count = 0; +} + +// 扩容 = 重建 + **重放既有条目**(保留「进行中」状态:清表会丢余归纳假设 → 可能不终止) +fn ty_memo_rehash() { + old_keys := g_ty_memo_keys; + old_vals := g_ty_memo_vals; + old_cap := g_ty_memo_cap; + nc : ., mut = old_cap * 2; + if nc < 1024 { nc = 1024; } + nb := alloc(nc * 16); + nv := alloc(nc * 8); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 16, -1); w64(nv, i * 8, 0); i = i + 1; } + g_ty_memo_keys = nb; + g_ty_memo_vals = nv; + g_ty_memo_cap = nc; + g_ty_memo_count = 0; + j : ., mut = 0; + loop { + if j >= old_cap { break; } + k := r64(old_keys, j * 16); + if k >= 0 { + b2 := r64(old_keys, j * 16 + 8); + s := ty_memo_slot_no_grow(k, b2); + w64(g_ty_memo_keys, s * 16, k); + w64(g_ty_memo_keys, s * 16 + 8, b2); + w64(g_ty_memo_vals, s * 8, r64(old_vals, j * 8)); + g_ty_memo_count = g_ty_memo_count + 1; + } + j = j + 1; + } +} + +fn ty_memo_slot_no_grow(a: int, b: int) -> int { + h : ., mut = (a * 31 + b) % g_ty_memo_cap; + if h < 0 { h = 0 - h; } + p : ., mut = h; + loop { + k := r64(g_ty_memo_keys, p * 16); + if k < 0 { return p; } + if k == a && r64(g_ty_memo_keys, p * 16 + 8) == b { return p; } + p = p + 1; if p >= g_ty_memo_cap { p = 0; } + } +} + +fn ty_memo_slot(a: int, b: int) -> int { + if g_ty_memo_ok == 0 { grow_ty_memo(1024); g_ty_memo_ok = 1; } + // 装填因子守卫(**必须在探测前**):表满且键不存在时开放寻址永不退出 + // (P0 终审 Critical 2 实证:1200 互异对 → 98% CPU 空转)→ 先扩容(重建 + 重放) + if (g_ty_memo_count + 1) * 2 >= g_ty_memo_cap { ty_memo_rehash(); } + // memo 查找计入预算(防备忘录自身成为无界成本);耗尽 → -1 上抛(调用方处理) + if tt_step() == -1 { return -1; } + return ty_memo_slot_no_grow(a, b); +} + +fn ty_memo_state(slot: int) -> int { + if r64(g_ty_memo_keys, slot * 16) < 0 { return -2; } // 空槽(未占用) + return r64(g_ty_memo_vals, slot * 8); +} + +fn ty_memo_set(slot: int, a: int, b: int, v: int) { + if r64(g_ty_memo_keys, slot * 16) < 0 { + w64(g_ty_memo_keys, slot * 16, a); + w64(g_ty_memo_keys, slot * 16 + 8, b); + g_ty_memo_count = g_ty_memo_count + 1; + } + w64(g_ty_memo_vals, slot * 8, v); +} + +// ─── μ 展开(代入)─── +fn tt_subst_list(p: int, v: int, r: int) -> int { + if p < 0 { return -1; } + if tt_tag(p) != TT_CONS { return tt_subst(p, v, r); } + return tt_cons(tt_subst(tt_a(p), v, r), tt_subst_list(tt_b(p), v, r)); +} + +fn tt_subst(i: int, v: int, r: int) -> int { + t := tt_tag(i); + if t == TT_VAR { if tt_a(i) == v { return r; } return i; } + if t == TT_UNION { return tt_union(tt_subst(tt_a(i), v, r), tt_subst(tt_b(i), v, r)); } + if t == TT_INTER { return tt_inter(tt_subst(tt_a(i), v, r), tt_subst(tt_b(i), v, r)); } + if t == TT_NOT { return tt_not(tt_subst(tt_a(i), v, r)); } + if t == TT_MU { + if tt_a(i) == v { return i; } // 内层同名 μ 遮蔽(避免捕获) + return tt_mu(tt_a(i), tt_subst(tt_b(i), v, r)); + } + if t == TT_ATOM { return tt_atom(tt_a(i), tt_b(i), tt_subst_list(tt_c(i), v, r)); } + return i; +} + +// ─── 预算守卫 ─── +fn ty_budget_reset(limit: int) { + g_ty_steps = 0; + g_ty_budget_max = limit; + g_ty_exhausted = 0; + g_ty_uncovered = 0; + // memo 随之清空(下次 ty_memo_slot 重建):**每个顶层查询独立**——否则跨查询的 + // 缓存命中会让结果依赖预算历史而非输入项(P0 终审 Critical 3 实证) + // 注意还要清 cap:grow_ty_memo 有 `needed < cap → return` 早退,若 cap 已涨过 + // (经 rehash),只清 ok 位会复用旧表 → 隔离承诺落空(修复复审 N1) + g_ty_memo_ok = 0; + g_ty_memo_cap = 0; +} + +fn ty_exhausted() -> int { return g_ty_exhausted; } +fn ty_uncovered() -> int { return g_ty_uncovered; } + +// ─── 覆盖判定:⟦p⟧ ⊆ ⟦q⟧(p/q 已规范化;q 可为 union)─── +fn sub_cover(p: int, q: int) -> int { + g_ty_steps = g_ty_steps + 1; + if g_ty_steps > g_ty_budget_max { g_ty_exhausted = 1; return -1; } + // μ 展开(左右各自)——**必须走 ty_sub_core(memo 入口)**:展开后的项对会重复出现, + // 靠「进行中 = 假设成立」的余归纳终止(直接递归 sub_cover 会不收敛——Task 4 自测 + // rec.absorb 实证:μX.(int ∪ X) ≤ int 需要假设-判定才成立)。 + if tt_tag(p) == TT_MU { return ty_sub_core(tt_subst(tt_b(p), tt_a(p), p), q); } + if tt_tag(q) == TT_MU { return ty_sub_core(p, tt_subst(tt_b(q), tt_a(q), q)); } + if tt_tag(p) == TT_UNION { + x := sub_cover(tt_a(p), q); + if x != 1 { return x; } + return sub_cover(tt_b(p), q); + } + if tt_tag(q) == TT_UNION { + // p 被某个析取支覆盖即可;三态:任一为 1 → 1;全 0 → 0;含 -1 → -1 + x := sub_cover(p, tt_a(q)); + if x == 1 { return 1; } + y := sub_cover(p, tt_b(q)); + if y == 1 { return 1; } + if x == -1 || y == -1 { return -1; } + return 0; + } + if tt_tag(q) == TT_TOP { return 1; } + if tt_tag(p) == TT_BOT { return 1; } + // p、q 皆 product(∩-链)或单字面:p 的字面集须覆盖 q 的每个字面 + bufp := lits_copy(p); + pn := g_ty_lits_count; + if lits_contradictory_buf(bufp, pn) == 1 { return 1; } // p = ∅ ⊆ 一切 + bufq := lits_copy(q); + qn := g_ty_lits_count; + qi : ., mut = 0; + loop { + if qi >= qn { break; } + lq := r64(bufq, qi * 8); + covered : ., mut = 0; + pi : ., mut = 0; + loop { + if pi >= pn { break; } + lp := r64(bufp, pi * 8); + li := lit_implies(lp, lq); + if li == 1 { covered = 1; break; } + // 预算耗尽/未覆盖面必须**上抛**(P0 终审 Critical 3:早期 `== 1` 把 -1 当「未覆盖」 + // → 判定结果被吞成 0 并缓存,违反 spec §3.3「禁止当 0 用」) + if li == -1 { return -1; } + pi = pi + 1; + } + if covered == 0 { return 0; } + qi = qi + 1; + } + return 1; +} + +// ─── 顶层:包含(假设-判定 + memo + 预算)─── +fn ty_sub_core(a: int, b: int) -> int { + slot := ty_memo_slot(a, b); + if slot < 0 { return -1; } // 预算耗尽(memo 查找本身计数) + st := ty_memo_state(slot); + if st == 2 { return 1; } // 进行中 = 假设成立(余归纳) + if st == 1 { return 1; } + if st == 0 { return 0; } + ty_memo_set(slot, a, b, 2); + r := sub_cover(a, b); + if r == -1 { + // 预算耗尽的结果**不可缓存**(否则后续更大预算的调用会读到假 -1)——清槽 + w64(g_ty_memo_keys, slot * 16, -1); + w64(g_ty_memo_vals, slot * 8, 0); + } else { + ty_memo_set(slot, a, b, r); + } + return r; +} + +fn ty_sub(a: int, b: int) -> int { + // 默认预算(未显式 ty_budget_reset 时可用;零初值下 g_ty_budget_max = 0 会让每次 + // 判定都「预算耗尽」→ 满屏 -1——P0 Task 3 自测实证) + if g_ty_budget_max <= 0 { ty_budget_reset(200000); } + if a == b { return 1; } + n1 := tt_norm(a); + if n1 < 0 { return -1; } // 规范化预算耗尽(P0 终审 Important C) + n2 := tt_norm(b); + if n2 < 0 { return -1; } + if n1 == n2 { return 1; } + if tt_tag(n2) == TT_TOP { return 1; } + if tt_tag(n1) == TT_BOT { return 1; } + return ty_sub_core(n1, n2); +} + +fn ty_equiv(a: int, b: int) -> int { + x := ty_sub(a, b); + if x != 1 { return x; } + y := ty_sub(b, a); + if y != 1 { return y; } + return 1; +} + +// 可空性:DNF 下存在一个不矛盾 product +fn ty_inhabited(a: int) -> int { + if g_ty_budget_max <= 0 { ty_budget_reset(200000); } + n := tt_norm(a); + if n < 0 { return -1; } // 规范化预算耗尽 + return inh_any_at(n, 0); +} + +// 深度守卫:μ 展开的**空递归**(如 μX.X)无可终止判据——超深即 -1 + 未覆盖面 +// (登记:空递归保守判为不可判,不静默当 0/1) +fn inh_any(i: int) -> int { return inh_any_at(i, 0); } + +fn inh_any_at(i: int, depth: int) -> int { + if depth > 512 { g_ty_exhausted = 1; return -1; } + if tt_tag(i) == TT_UNION { + x := inh_any_at(tt_a(i), depth); + if x == 1 { return 1; } + y := inh_any_at(tt_b(i), depth); + if y == 1 { return 1; } + if x == -1 || y == -1 { return -1; } + return 0; + } + if tt_tag(i) == TT_MU { return inh_any_at(tt_subst(tt_b(i), tt_a(i), i), depth + 1); } + if tt_tag(i) == TT_BOT { return 0; } + ty_lits_reset(); + lit_collect(i); + // 本语言无三元运算符(`?:` 不合法——P0 Task 3 构建期实证)→ 显式分支 + if lits_contradictory() == 1 { return 0; } + return 1; +} + +// ─── 反例(witness)与穷尽性(Task 4)─── +// witness = A\B 的规范化类型项;**不可满足 → -1**(无遗漏/无反例值); +// **-2 = 未知**(预算耗尽/未覆盖面——与「不可满足」严格区分:P0 终审 Minor 指出早期 +// 二者共用 -1 会让「证明不了」被当成「确实穷尽」) +fn tt_witness(a: int, b: int) -> int { + r := tt_norm(tt_inter(a, tt_not(b))); + if r < 0 { return -2; } + if tt_tag(r) == TT_BOT { return -1; } + x := ty_inhabited(r); + if x == -1 { return -2; } + if x != 1 { return -1; } + return r; +} + +// 穷尽 witness = domain \ ⋃patterns(patterns = CONS 链) +fn ty_exhaust_witness(domain: int, patterns: int) -> int { + acc : ., mut = tt_bot(); + p : ., mut = patterns; + loop { + if p < 0 { break; } + if tt_tag(p) != TT_CONS { break; } + acc = tt_union(acc, tt_a(p)); + p = tt_b(p); + } + return tt_witness(domain, acc); +} + +fn ty_exhaustive(domain: int, patterns: int) -> int { + w := ty_exhaust_witness(domain, patterns); + if w == -2 { return -1; } // 未知(预算耗尽/未覆盖面)——不得当「穷尽」 + if g_ty_exhausted != 0 { return -1; } + if w < 0 { return 1; } // 补集空 = 穷尽 + return 0; +} + +fn ty_disjoint(a: int, b: int) -> int { + x := ty_inhabited(tt_inter(a, b)); + if x == -1 { return -1; } // 三态传播:未知不得降级为 0 + if x == 0 { return 1; } + return 0; +} + +// iface 簇已移至 iface_axis.cr(R2 P4 Task 0,链接面纯化) diff --git a/src/compiler/type_selftest.cr b/src/compiler/type_selftest.cr new file mode 100644 index 00000000..b274b849 --- /dev/null +++ b/src/compiler/type_selftest.cr @@ -0,0 +1,3595 @@ +// === type_selftest.cr === +// R2 P0:类型项引擎判定用例表 + 自测驱动(`corec selftest-types`)。 +// 判据:每例一行 PASS/FAIL,末行 "N/M type-engine cases passed",rc=0 = 全过。 +// 说明:本文件只依赖 type_terms.cr / type_engine.cr 的公开 API。 +// 用例面分期:Task 1 = P0 最小 5 例(建层);Task 3/4 扩到 spec §8 全类 +// (子类型/等价/不相交/可空/反例/穷尽性/递归/参数化/预算);**P0 终审补 8 例** +// (否定在超类型侧 / memo 扩容不挂 / 预算不吞 -1 / n 元析取 / 命名原子未知 / +// ¬μ 不下推 / 未覆盖面未知)。tests/selfhost/test_type_engine.py 的 MIN_CASES = 32。 + +fn ts_check(name: string, got: int, want: int) -> int { + if got == want { + print("PASS "); println(name); + return 0; + } + print("FAIL "); print(name); + print(": got "); print(int_str(got)); + print(" want "); println(int_str(want)); + return 1; +} + +// Task 4 端到端用例的诊断探针:check_error 只追加 g_diags(不打印),故「门是否报错」可由 +// 增量计数 + 首条新诊断码直接读——这是「接线后门真的在判」的行为证据(表侧用例只能证明表对)。 +fn ts_diag_code_at(idx: int) -> int { + if idx < 0 { return -1; } + if idx >= g_diag_count { return -1; } + return r64(g_diags, idx * DIAG_REC_SIZE); +} + +// 诊断消息探针(R2 P5 Task 4):读第 idx 条诊断的消息指针(check_error 存的就是该指针)。 +fn ts_diag_msg_at(idx: int) -> string { + if idx < 0 { return ""; } + if idx >= g_diag_count { return ""; } + return load_str_ptr(g_diags, idx * DIAG_REC_SIZE + 8); +} + +// ─── R2 P5 Task 4:AK 映射的**冻结期望表**(替代已删的 legacy 副本对照物)─── +// 与 iface_registry.cr 的 `iface_by_ty_code`(唯一生产实现)逐格对照用:本表 = 数据形态的期望, +// 不再是生产文件里的第二份实现(单源化的判据由此从「两份实现互比」变「实现 vs 冻结期望」)。 +// 依据 = P2b Task 2 已裁决的映射(含两条灰格:TY_DEX_S → AK_DEX(同值域不同表示)、 +// TY_GENERIC_PARAM → AK_NAMED(泛型参数哨兵 → 命名类,不展开));未映射码 ⇒ -1。 +fn ts_t4_ak_expected(ty: int) -> int { + if ty == TY_INT { return AK_INT; } + if ty == TY_DEX { return AK_DEX; } + if ty == TY_DEX_S { return AK_DEX; } + if ty == TY_BOOL { return AK_BOOL; } + if ty == TY_STRING { return AK_STRING; } + if ty == TY_UNIT { return AK_UNIT; } + if ty == TY_NEVER { return AK_NEVER; } + if ty == TY_CHAR { return AK_CHAR; } + if ty == TY_GENERIC_PARAM { return AK_NAMED; } + return -1; +} + +// 行号面期望:**门**(kind 非 TYP_BASE ⇒ -1,含 TYP_DYN/结构/命名行与负 ti)+ 表内分派。 +fn ts_t4_native_ak_expected(ti: int) -> int { + if ti < 0 { return -1; } + if get_type_kind(ti) != TYP_BASE { return -1; } + return ts_t4_ak_expected(get_type_data(ti)); +} + +// 宽联合探针(预算守卫用):N 层左深 union —— sub_cover 逐支递归,每支 1 步, +// N > 预算即耗尽(深 μ 链不行:参数位置的字面蕴含不递归展开,会在参数比较处短路)。 +fn tt_probe_wide_union(depth: int) -> int { + acc : ., mut = tt_atom(AK_INT, TI_INT, -1); + d : ., mut = 0; + loop { + if d >= depth { break; } + acc = tt_union(acc, tt_atom(AK_STRING, TI_STR, -1)); + d = d + 1; + } + return acc; +} + +// R2 P3 Task 0 夹具:声明单 `int` 字段 struct(a: int),返回其命名行。本通道不经 +// parser/check_all ⇒ struct 声明表照 parser.cr:1539-1560 的写槽序人工落(字段名 / +// 裸码槽 / 类型节点 / 字段数),类型节点照 parse_type 的产物形态构造。 +fn ts_unf_mk_int_struct(name: string) -> int { + sa := add_struct(name); + si_set_field_name(sa, 0, str_intern("a")); + si_set_field_type(sa, 0, TY_INT); + si_set_field_type_node(sa, 0, alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0)); + w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1); + return alloc_named_type(str_intern(name)); +} + +// 域项的析取支计数(「变体数」判据用):只按引擎项形态走 union 树,**不与实现共享计数逻辑** +// (独立重算——否则同源同错自洽假绿)。⊥/单支 → 1。 +fn ts_unf_union_leaves(t: int) -> int { + if tt_tag(t) == TT_UNION { return ts_unf_union_leaves(tt_a(t)) + ts_unf_union_leaves(tt_b(t)); } + return 1; +} + +// ─── R2 P3 Task 5 夹具(生产函数入口,见 t5.* 段注)─── + +// g_gen_map 按名字查绑定(-1 = 未绑定)——t5.f4 用(独立读取,不复用 checker 内部循环) +fn ts_gen_map_ti(name_ni: int) -> int { + i : ., mut = 0; + loop { + if i >= g_gen_map_count { return -1; } + if r64(g_gen_map_names, i * 8) == name_ni { return r64(g_gen_map_types, i * 8); } + i = i + 1; + } + return -1; +} + +// 单泛型形参 + 单约束名的 struct 声明行夹具(约束经 **parser 的生产登记函数** +// save_struct_gen_constrs 落侧表——测真实路径,不手写侧表) +fn ts_t5_mk_constr_struct(name: string, cname: string) -> int { + sa := add_struct(name); + w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1); + w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, str_intern("T5_CS_T")); + cb := alloc(8); + w64(cb, 0, str_intern(cname)); + save_struct_gen_constrs(sa, cb, 1); + return sa; +} + +fn ts_t5_mk_constr_enum(name: string, cname: string) -> int { + ea := add_enum(name); + w64(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1); + w64(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T5_CE_T")); + cb := alloc(8); + w64(cb, 0, str_intern(cname)); + save_enum_gen_constrs(ea, cb, 1); + return ea; +} + +// 接口表行夹具(零方法):t5.constr_iface_is_unknown_p3b 用(谓词 = find_iface 命中 ⇒ +// gen_constr_satisfied 必须回 -1——P3b 交接边界) +fn ts_t5_mk_iface(name: string) -> int { + grow_ifaces(g_iface_count + 1); + base := g_iface_count * ESZ_IFACEINFO; + zi : ., mut = 0; + loop { if zi >= ESZ_IFACEINFO { break; } w8(g_ifaces, base + zi, 0); zi = zi + 1; } + w64(g_ifaces, base + OFF_IF_NAME, str_intern(name)); + w64(g_ifaces, base + OFF_IF_GENERIC_COUNT, 0); + g_iface_count = g_iface_count + 1; + return g_iface_count - 1; +} + +// ─── R2 P3 Task 5 用例体(独立函数:**不得**内联进 type_selftest_run —— +/// 该函数已是巨型函数,本段 40+ 局部变量的加入会使其在 Python bootstrap 的 +/// 栈式代码生成下越界(实测:内联版 SIGSEGV rc=139;拆出后正常)。返回 = 失败数。 +/// 用例数 = 9(调用方 `total = total + 9` 须同步——增删用例两处一起改)。 +fn ts_t5_run() -> int { + fails : ., mut = 0; + // ═══ R2 P3 Task 5:泛型约束(保留 / 实例化判定 / 反例 / 实例键类型项化)═══ + // 判据面 = ① F4 形参链导航(形参节点**不连续**布局下的正/负控——旧 `pn+1` 导航的牙); + // ② 约束名解析 + 本质轴三态判定(1/0/-1);③ 用户接口约束 = **-1 不判**(P3b 交接边界: + // 「不判」≠「不满足」);④ 反例文本(失败有值 / 通过无值);⑤ 实例化点检查的**诊断去重** + // (同节点同形参只报一次)+ 正负控;⑥ 结构/枚举约束经 parser 登记函数落到侧表后可读; + // ⑦ 实例键结构忠实(异型异键 = 反折叠)+ ti 型替换在**克隆体**上的落点(形参类型节点)。 + // 夹具说明:本组不经 parser/check_all(照 t3/t4 口径人工落表),但**走生产函数**—— + // save_struct_gen_constrs / gen_inst_constr_check / infer_gen_call / gen_find_or_create_bind。 + init_types(); + ty_budget_reset(200000); + // ① F4:夹具 = 「文件首类型节点恰是另一泛型形参的 ident」+ 形参 (n: int, a: T)(类型节点 + // 插在形参节点之前 ⇒ 形参在节点表不连续)。旧导航把 param1 的 pattern 读成节点 0 ⇒ + // 凭空绑定 U(phanton)且 T 永不绑定;修复后 param1 的 pattern = 声明类型 gparam(T)。 + init_types(); // 夹具前再清一次(上面 alloc 的行使节点下标可预期:本组不依赖具体下标) + t5_u_ni := str_intern("T5_U"); + t5_u_row := alloc_type(TYP_GENERIC_PARAM, t5_u_ni, 0); + def_sym(t5_u_ni, SYM_TYPE, t5_u_row, -1); + alloc_node(EXPR_IDENT, 0, 0, 0, t5_u_ni, 0, 0, 0, 0); // 「文件首类型节点」 + t5_int_n := alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0); + t5_p0 := alloc_node(EXPR_PARAM, str_intern("n"), 0, 0, 0, TY_INT, t5_int_n, 0, 0); + t5_t_ni := str_intern("T5_T"); + t5_t_row := alloc_type(TYP_GENERIC_PARAM, t5_t_ni, 0); + def_sym(t5_t_ni, SYM_TYPE, t5_t_row, -1); + t5_t_n1 := alloc_node(EXPR_IDENT, 0, 0, 0, t5_t_ni, 0, 0, 0, 0); + t5_p1 := alloc_node(EXPR_PARAM, str_intern("a"), 0, 0, 0, 0, t5_t_n1, 0, 0); + t5_rt := alloc_node(EXPR_IDENT, 0, 0, 0, t5_t_ni, 0, 0, 0, 0); + t5_fn := alloc_node(EXPR_FN, str_intern("t5_f4_take"), t5_p0, 2, 0, t5_rt, -1, 0, 0); + t5_fi := add_func("t5_f4_take", 2, 0, t5_fn); // add_func 收**字符串**(parser 同款:tok_lx 结果),不是 ni + fi_set_generic_count(t5_fi, 1); + println("M5b"); + fi_set_generic_name(t5_fi, 0, t5_t_ni); + println("M5c"); + def_sym(str_intern("t5_f4_take"), SYM_FN, TI_UNIT, -1); + t5_arg2 := alloc_node(EXPR_ARG, alloc_node(EXPR_STRING, 0, 0, 0, str_intern("s"), 0, 0, 0, 0), 0, 0, 0, 0, 0, 0, 0); + // EXPR_ARG: a = expr, b = next(末项 b = 0 即终止符——链遍历按 `an < 0` 停,见 infer_gen_call) + ast_set_b(t5_arg2, -1); + t5_arg1 := alloc_node(EXPR_ARG, alloc_node(EXPR_INT, 0, 0, 0, 1, 0, 0, 0, 0), t5_arg2, 0, 0, 0, 0, 0, 0); + t5_calln := alloc_node(EXPR_CALL, 0, t5_arg1, 2, 0, 0, 0, 0, 0); + infer_gen_call(t5_fi, t5_calln, t5_arg1, 2); + t5_ti_bound := ts_gen_map_ti(t5_t_ni); + fails = fails + ts_check("t5.f4_later_param_declared_type", + (t5_ti_bound == TI_STR && ts_gen_map_ti(t5_u_ni) == -1), 1); + fails = fails + ts_check("t5.f4_bind_seg_recorded", + (g_gen_binds_count == 2 && r64(g_gen_binds, 0 * 8) == 1 && + r64(g_gen_binds, 1 * 8) == TI_STR && ast_int_val(t5_calln) == 1), 1); + // ② 约束名解析 + 本质轴三态 + t5_ni_int := str_intern("int"); + t5_ni_str := str_intern("string"); + t5_ni_nope := str_intern("T5_NoSuchTypeName"); + fails = fails + ts_check("t5.constr_name_resolve", + (gen_constr_type_ti(t5_ni_int) == TI_INT && gen_constr_type_ti(t5_ni_str) == TI_STR && + gen_constr_type_ti(str_intern("never")) == TI_NEVER && gen_constr_type_ti(t5_ni_nope) == -1 && + gen_constr_type_ti(-1) == -1), 1); + fails = fails + ts_check("t5.constr_essence_three_state", + (gen_constr_satisfied(t5_ni_int, TI_INT) == 1 && + // `never` **不**满足原生约束(引擎现状:AK_NEVER 是与 AK_INT 互斥的**原子**,不是 ⊥ + // ——P0 既有语义「never 行在引擎侧不是真 ⊥」(T4 报告 §7-⑨),本批如实钉住、不动) + gen_constr_satisfied(t5_ni_int, TI_NEVER) == 0 && + gen_constr_satisfied(t5_ni_int, TI_STR) == 0 && + gen_constr_satisfied(t5_ni_str, TI_INT) == 0 && + gen_constr_satisfied(t5_ni_nope, TI_INT) == -1), 1); + // ③ 用户接口约束 + **非命名实参** ⇒ -1(P3b Task 0 后仍 -1:域限定——接口方法表只对命名行 + // 可查,故 `int` 实参不判;**不是**「未交付」。命名实参的判定面见 isat.* 段与 + // tests/selfhost/test_iface_satisfies.py) + t5_if_ii := ts_t5_mk_iface("T5_Show"); + fails = fails + ts_check("t5.constr_iface_non_named_unjudged", + (find_iface(str_intern("T5_Show")) == t5_if_ii && + gen_constr_satisfied(str_intern("T5_Show"), TI_INT) == -1), 1); + // ④ 反例文本:失败非空、通过为空(**不谎报**) + fails = fails + ts_check("t5.constr_witness_text", + (str_len(gen_constr_witness_str(TI_STR, t5_ni_int)) > 0 && + str_len(gen_constr_witness_str(TI_INT, t5_ni_int)) == 0 && + str_len(gen_constr_witness_str(TI_INT, t5_ni_nope)) == 0), 1); + // ⑤⑥ 实例化点检查:结构/枚举约束经 parser 登记函数落侧表 + 判定 + 去重 + 正负控 + 接口面零动作 + t5_si := ts_t5_mk_constr_struct("T5BoxInt", "int"); + t5_ei := ts_t5_mk_constr_enum("T5EBoxInt", "int"); + t5_si2 := ts_t5_mk_constr_struct("T5BoxShow", "T5_Show"); + fails = fails + ts_check("t5.constr_side_table_readback", + (si_generic_count(t5_si) == 1 && si_gen_constr(t5_si, 0) == t5_ni_int && + si_gen_constr(t5_si, 1) == -1 && si_gen_constr(-1, 0) == -1 && + ei_generic_count(t5_ei) == 1 && ei_gen_constr(t5_ei, 0) == t5_ni_int && + ei_gen_constr(t5_ei, 4) == -1), 1); + t5_args := alloc(8); + w64(t5_args, 0 * 8, TI_STR); + t5_gnode := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 41, 3); + t5_d0 := g_diag_count; + gen_inst_constr_check(t5_gnode, t5_si, -1, t5_args, 1); + t5_d1 := g_diag_count; + gen_inst_constr_check(t5_gnode, t5_si, -1, t5_args, 1); // 同节点同形参 → 去重 + t5_d2 := g_diag_count; + t5_gnode2 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 42, 3); + gen_inst_constr_check(t5_gnode2, t5_si, -1, t5_args, 1); // 新节点 → 再报(去重不跨节点) + t5_d3 := g_diag_count; + w64(t5_args, 0 * 8, TI_INT); + t5_gnode3 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 43, 3); + gen_inst_constr_check(t5_gnode3, t5_si, -1, t5_args, 1); // 正控:满足 → 零诊断 + gen_inst_constr_check(t5_gnode3, t5_ei, -1, t5_args, 1); // 枚举侧同(满足) + t5_d4 := g_diag_count; + w64(t5_args, 0 * 8, TI_STR); + t5_gnode4 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5EBoxInt"), -1, 1, 0, 0, 0, 44, 3); + gen_inst_constr_check(t5_gnode4, -1, t5_ei, t5_args, 1); // 枚举侧违反 → 诊断 + t5_d5 := g_diag_count; + t5_gnode5 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxShow"), -1, 1, 0, 0, 0, 45, 3); + gen_inst_constr_check(t5_gnode5, t5_si2, -1, t5_args, 1); // 接口约束 = 不判 → 零诊断 + t5_d6 := g_diag_count; + fails = fails + ts_check("t5.inst_check_dedup_and_controls", + (t5_d1 - t5_d0 == 1 && t5_d2 - t5_d1 == 0 && t5_d3 - t5_d2 == 1 && + t5_d4 - t5_d3 == 0 && t5_d5 - t5_d4 == 1 && t5_d6 - t5_d5 == 0 && + ts_diag_code_at(t5_d0) == EC_TG_BOUND && + // 末条记录下标 = **调用后计数 - 1**(d5 是调用后的 g_diag_count) + ts_diag_code_at(t5_d5 - 1) == EC_TG_BOUND), 1); + // ⑦ 实例键:异型异键(反折叠)+ 同名义同行同键 + 结构忠实(复合键含结构) + t5_a_ti := alloc_named_type(str_intern("T5_A")); + t5_b_ti := alloc_named_type(str_intern("T5_B")); + t5_a2_ti := alloc_type(TYP_NAMED, str_intern("T5_A"), 0); + t5_arr_ti := alloc_type(TYP_ARRAY, TI_INT, 3); + t5_ptr_ti := alloc_type(TYP_PTR, t5_a_ti, 0); + t5_slice_ti := alloc_type(TYP_SLICE, TI_INT, 0); + fails = fails + ts_check("t5.inst_key_structural_faithful", + (str_eq(inst_key_of_ti(t5_a_ti), inst_key_of_ti(t5_b_ti)) == 0 && + str_eq(inst_key_of_ti(t5_a_ti), inst_key_of_ti(t5_a2_ti)) != 0 && + str_eq(inst_key_of_ti(TI_INT), "int") != 0 && + str_eq(inst_key_of_ti(t5_arr_ti), "[int; 3]") != 0 && + str_eq(inst_key_of_ti(t5_ptr_ti), "*T5_A") != 0), 1); + // ⑧ ti 型替换端到端:克隆体内**泛型形参位**换成实参类型节点;异键异实例、同键缓存命中。 + // 夹具形参实参 = **切片** `[int]`(键 "[int]"):ti 路径产出 EXPR_ARRAY 类型节点,名字路径 + // 只会产出 EXPR_IDENT("[int]")(非法声明名)⇒ 本判据对「ti 路径是否生效」有牙。 + // ⚠ **范围登记**:替换只覆盖**函数体内**节点——EXPR_FN 分支不克隆形参(形参类型由 + // fi_param_type 的裸码承担),故克隆体形参仍指向源节点(与实例化的既有语义一致)。 + t5_m_ni := str_intern("T5_M"); + t5_m_tynode := alloc_node(EXPR_IDENT, 0, 0, 0, t5_m_ni, 0, 0, 0, 0); + t5_m_body := alloc_node(EXPR_RETURN, t5_m_tynode, 0, 0, 0, 0, 0, 0, 0); // 体内形参位 + t5_m_p := alloc_node(EXPR_PARAM, str_intern("x"), 0, 0, 0, 0, t5_m_tynode, 0, 0); + t5_m_rt := alloc_node(EXPR_IDENT, 0, 0, 0, t5_m_ni, 0, 0, 0, 0); + t5_m_fn := alloc_node(EXPR_FN, t5_m_ni, t5_m_p, 1, 0, t5_m_rt, t5_m_body, 0, 0); + t5_m_fi := add_func("T5_M", 1, 0, t5_m_fn); + fi_set_generic_count(t5_m_fi, 1); + fi_set_generic_name(t5_m_fi, 0, t5_m_ni); + // 绑定段(人工按 checker 写入形态落:块 = [count, ti…],节点 int_val = 起始 + 1) + grow_gen_binds(g_gen_binds_count + 2); + t5_bstart := g_gen_binds_count; + w64(g_gen_binds, t5_bstart * 8, 1); + w64(g_gen_binds, (t5_bstart + 1) * 8, t5_slice_ti); + g_gen_binds_count = t5_bstart + 2; + t5_key_s := inst_key_of_ti(t5_slice_ti); + t5_f1 := gen_find_or_create_bind(t5_m_fi, t5_key_s, t5_bstart); + t5_f2 := gen_find_or_create_bind(t5_m_fi, t5_key_s, t5_bstart); // 同键 → 缓存命中 + t5_f3 := gen_find_or_create_bind(t5_m_fi, inst_key_of_ti(t5_b_ti), t5_bstart); // 异键 → 新实例 + t5_cp_ok : ., mut = 0; + if t5_f1 >= 0 { + t5_cb := ast_data(fi_ast_node(t5_f1)); // 克隆体(EXPR_FN)的 body + if t5_cb >= 0 && ast_kind(t5_cb) == EXPR_RETURN { + t5_cn := ast_a(t5_cb); // 体内形参位(源 = EXPR_IDENT(T5_M)) + if t5_cn >= 0 && ast_kind(t5_cn) == EXPR_ARRAY { + t5_cn_e := ast_a(t5_cn); // 切片元素位(源实参 = int) + if t5_cn_e >= 0 && ast_kind(t5_cn_e) == 0 && ast_type_val(t5_cn_e) == TY_INT { t5_cp_ok = 1; } + } + } + } + fails = fails + ts_check("t5.inst_ti_subst_in_clone", + (t5_f1 >= 0 && t5_f2 == t5_f1 && t5_f3 >= 0 && t5_f3 != t5_f1 && t5_cp_ok == 1), 1); + + return fails; +} + +// ═══════════════ R2 P3b Task 0:`iface_satisfies` 契约用例(isat.*)═══════════════ +// 面(详见 type_engine.cr 的契约头注):轴分派 A 形状 → C 用户接口 → B 本质轴;三态纪律 +// (-1 不得当 0/1);轴 C 谓词 = check_iface 同源(方法名在位 + 参数计数 + 返回码);域限定 = +// 命名行。**本段主判据 = ① 三态直传(不是「查不到就 0」)② 与 check_iface 逐例对拍 +// ③ 判定路径零 str_intern(.ccr STR 段守卫)④ 形状表注册/覆盖/复位语义。** + +// 接口方法条目追加(照 parser 的 interface 分支写点:mbase + name/param_count/ret_ti) +fn ts_isat_add_method(ii: int, mname: string, pc: int, rt: int) -> int { + n := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + n * ESZ_IFMETHOD; + w64(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)); + w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc); + w64(g_ifaces, mbase + OFF_IFM_RET_TI, rt); + // R2 P3b Task 6(Step 1 签名类型项化):**类型节点**槽(与生产 parser 同形:基类型节点 = + // kind 0 + type_val = TY_* 码)+ 接收者模式(0 = 无接收者,本组夹具形态)。 + w64(g_ifaces, mbase + OFF_IFM_RET_NODE, ts_ifc_base_node(rt)); + w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, 0); + pj : ., mut = 0; + loop { + if pj >= pc || pj >= MAX_IFACE_METHOD_PARAMS { break; } + w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT); + w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, ts_ifc_base_node(TY_INT)); + pj = pj + 1; + } + w64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1); + return mbase; +} + +// 基类型节点(= parser `parse_type` 的基类型产物同形:kind 0、type_val = TY_* 码; +// 签名面消费点(`sh_sig_term_of_node` → `res_type_node`)按同一分派表解析)。 +fn ts_ifc_base_node(ty: int) -> int { + return alloc_node(0, 0, 0, 0, 0, ty, 0, 0, 0); +} + +// impl 方法登记(**照 parser 的 impl 分支写点**:mangled 函数名 + g_methods 三元组 +// {type_ni, method_ni, mangled_ni})——夹具须与生产写点同形,否则对拍在两种登记面上空转。 +// R2 P3b Task 6:函数行带**真 EXPR_FN 节点**(形参链 + 类型节点;形参节点**不连续**——类型 +// 节点插在其后,照 parse_body 的分配布局)——签名面(`sh_func_sig_term`)从 AST 读参数/返回型。 +fn ts_isat_add_impl_method(tname_ni: int, mname: string, pc: int, rt: int) -> int { + mn := istr_get(tname_ni) + "." + mname; + mangled_ni := str_intern(mn); + pf : ., mut = -1; + pj : ., mut = 0; + loop { + if pj >= pc { break; } + tn := ts_ifc_base_node(TY_INT); + if pf < 0 { pf = g_ast_count; } + alloc_node(EXPR_PARAM, str_intern("p"), 0, 0, 0, TY_INT, tn, 0, 0); + pj = pj + 1; + } + rtn := ts_ifc_base_node(rt); + fn_node := alloc_node(EXPR_FN, mangled_ni, pf, pc, rt, rtn, -1, 0, 0); + fi := add_func(mn, pc, rt, fn_node); + grow_methods(g_method_count + 1); + w64(g_methods, g_method_count * 24, tname_ni); + w64(g_methods, g_method_count * 24 + 8, str_intern(mname)); + w64(g_methods, g_method_count * 24 + 16, mangled_ni); + g_method_count = g_method_count + 1; + return fi; +} + +fn ts_isat_b2i(b: bool) -> int { if b { return 1; } return 0; } + +// 用例数 = 16(调用方 `total = total + 16` 须同步——增删用例两处一起改)。 +fn ts_isat_run() -> int { + fails : ., mut = 0; + // ── 夹具:接口 `T0bShow { fn show(pc=1, rt=0) }`;命名行 T0bS(有 show)/ T0bT(无方法)── + // rt 码语义 = 映射层编码(0 = TY_INT,见 type_engine.cr 轴 C 注)。 + isat_ii := ts_t5_mk_iface("T0bShow"); + ts_isat_add_method(isat_ii, "show", 1, 0); + isat_show_ni := str_intern("show"); + isat_s_ti := alloc_named_type(str_intern("T0bS")); + isat_t_ti := alloc_named_type(str_intern("T0bT")); + ts_isat_add_impl_method(str_intern("T0bS"), "show", 1, 0); + // ① 轴 C 正例:方法在位 + 计数/返回码相符 ⇒ 1(命名行的空洞满足另见 ⑫) + fails = fails + ts_check("isat.axis_c_satisfied", + iface_satisfies(isat_s_ti, str_intern("T0bShow")), 1); + // ② 轴 C 反例:方法名不在位 ⇒ **0**(可证违反——名字面忠实;不是 -1) + fails = fails + ts_check("isat.axis_c_missing_method", + iface_satisfies(isat_t_ti, str_intern("T0bShow")), 0); + // ③ 轴 C 反例:参数计数不符 ⇒ 0 + isat_c_ti := alloc_named_type(str_intern("T0bC")); + ts_isat_add_impl_method(str_intern("T0bC"), "show", 2, 0); + fails = fails + ts_check("isat.axis_c_param_count", + iface_satisfies(isat_c_ti, str_intern("T0bShow")), 0); + // ④ 轴 C 反例:返回码不符(3 = TY_STRING)⇒ 0 + isat_r_ti := alloc_named_type(str_intern("T0bR")); + ts_isat_add_impl_method(str_intern("T0bR"), "show", 1, 3); + fails = fails + ts_check("isat.axis_c_ret_code", + iface_satisfies(isat_r_ti, str_intern("T0bShow")), 0); + // ⑤ 域限定:非命名行(原生 / dyn / 泛型形参)⇒ **不判**(无方法表身份;绝不因「查不到」报 0) + isat_dyn_ti := alloc_type(TYP_DYN, 0, 0); + isat_gp_ti := alloc_type(TYP_GENERIC_PARAM, str_intern("T0bU"), 0); + fails = fails + ts_check("isat.axis_c_non_named_unjudged", + ts_isat_b2i(iface_satisfies(TI_INT, str_intern("T0bShow")) == -1 && + iface_satisfies(isat_dyn_ti, str_intern("T0bShow")) == -1 && + iface_satisfies(isat_gp_ti, str_intern("T0bShow")) == -1), 1); + // ⑥ 轴 B(本质轴):= gen_constr_satisfied 引擎面(迁移到统一入口后逐例同结论) + fails = fails + ts_check("isat.axis_b_essence", + ts_isat_b2i(iface_satisfies(TI_INT, str_intern("int")) == 1 && + iface_satisfies(TI_INT, str_intern("string")) == 0 && + iface_satisfies(TI_INT, str_intern("T0bNoSuchName")) == -1), 1); + // ⑦ 轴 A(横切形状):注册 ⊤_SEQUENCE 名 ⇒ 切片行 1 / 原生行 0;**复位后同键 ⇒ -1** + // (复位断言防「条目泄漏成全局态」;lookup 负键守卫) + // R2 P4 Task 3 重定:形状表在 init_types 尾部已有**生产注册面**(6 条 D17 名) + // ——本行 = 第 7 条(slot 6),计数基数由 0 改 6;注册/查询/判定语义零变化。 + isat_seq_ti := alloc_type(TYP_SLICE, TI_INT, 0); + isat_shape_name := str_intern("T0bShapeSeq"); + isat_n_pre := iface_shape_count(); + isat_slot := iface_shape_register(isat_shape_name, tt_top_k(AK_SEQUENCE)); + fails = fails + ts_check("isat.axis_a_shape", + ts_isat_b2i(isat_slot == isat_n_pre && iface_shape_count() == isat_n_pre + 1 && + iface_shape_lookup(isat_shape_name) >= 0 && + iface_satisfies(isat_seq_ti, isat_shape_name) == 1 && + iface_satisfies(TI_INT, isat_shape_name) == 0), 1); + // ⑧ 轴优先级:同名既是形状又是接口 ⇒ **形状优先**(TI_INT 在形状面可判 0,在用户轴恒 -1 + // ⇒ 结果 0 即证明走了轴 A;零方法接口在用户轴本会走 1 的路径也一并在 ⑫ 覆盖) + isat_dup_ii := ts_t5_mk_iface("T0bDup"); + fails = fails + ts_check("isat.axis_order_shape_first", + ts_isat_b2i(find_iface(str_intern("T0bDup")) == isat_dup_ii && + iface_shape_register(str_intern("T0bDup"), tt_top_k(AK_SEQUENCE)) >= 0 && + iface_satisfies(TI_INT, str_intern("T0bDup")) == 0), 1); + // ⑨ 轴优先级:同名既是接口又是原生类型名 ⇒ **接口优先**(= 现状 gen_constr_satisfied 的 + // find_iface 先行;非命名实参 ⇒ -1,**不做**本质轴判定)——P3a 零行为变化面 + ts_t5_mk_iface("int"); + fails = fails + ts_check("isat.axis_order_iface_over_essence", + ts_isat_b2i(iface_satisfies(TI_INT, str_intern("int")) == -1), 1); + // ⑩ 与 check_iface **逐例对拍**(谓词同源的最强守门:三形态 = 满足/计数不符/返回码 + // 不符;缺方法形态在 ② 与 ⑪ 分别钉住 0 与零驻留表增长) + isat_eq := -1; + if ts_isat_b2i(check_iface(str_intern("T0bS"), isat_ii)) == iface_satisfies(isat_s_ti, str_intern("T0bShow")) { + if ts_isat_b2i(check_iface(str_intern("T0bC"), isat_ii)) == iface_satisfies(isat_c_ti, str_intern("T0bShow")) { + if ts_isat_b2i(check_iface(str_intern("T0bR"), isat_ii)) == iface_satisfies(isat_r_ti, str_intern("T0bShow")) { + isat_eq = 1; + } + } + } + fails = fails + ts_check("isat.predicate_same_as_check_iface", isat_eq, 1); + // ⑪ 判定路径**零 str_intern**(.ccr STR 段守卫):缺失方法名(= 判定 0 的路径)不得把 + // 新串追加进驻留表——`type_has_method` 的 `str_intern("T.m")` 形态在此会增长表。 + isat_zz_ti := alloc_named_type(str_intern("T0bZz")); + isat_before := g_str_count; + iface_satisfies(isat_zz_ti, str_intern("T0bShow")); + fails = fails + ts_check("isat.no_str_intern_on_missing", + ts_isat_b2i(g_str_count == isat_before), 1); + // ⑫ 空洞满足:零方法接口 + 命名行 ⇒ 1;非命名行 ⇒ -1(域限定先于空循环) + isat_empty_ii := ts_t5_mk_iface("T0bEmpty"); + fails = fails + ts_check("isat.empty_iface_vacuous", + ts_isat_b2i(find_iface(str_intern("T0bEmpty")) == isat_empty_ii && + iface_satisfies(isat_t_ti, str_intern("T0bEmpty")) == 1 && + iface_satisfies(TI_INT, str_intern("T0bEmpty")) == -1), 1); + // ⑬ 泛型应用行:查方法走**基名**(decl_name_of_ti 的 TYP_GENERIC_APPLY 分支) + grow_gen_apply_data(g_gen_apply_data_count + 2); + isat_ga_s := g_gen_apply_data_count; + w64(g_gen_apply_data, isat_ga_s * 8, 1); + w64(g_gen_apply_data, (isat_ga_s + 1) * 8, TI_INT); + g_gen_apply_data_count = isat_ga_s + 2; + isat_ga_ti := alloc_type(TYP_GENERIC_APPLY, isat_s_ti, isat_ga_s); + fails = fails + ts_check("isat.generic_apply_base_name", + iface_satisfies(isat_ga_ti, str_intern("T0bShow")), 1); + // ⑭ 三态 + 预算隔离:不可译行(越界 ti)/负键 ⇒ -1(**不得**当 0/1);且**成功判定后** + // 预算窗口必须干净(g_ty_steps 归零 + 上限 = 本层窗口常量;memo 不复用跨查询历史—— + // P0 终审 Critical 3 的口径,同 type_equal_engine/gen_constr_satisfied) + ty_budget_reset(200000); + isat_bad_ti := g_type_count + 7; + // 两条引擎路径:**节点同一快路径**(0 步)+ **非同一比较**(sub_cover 计步 ⇒ 出口复位可观测: + // 缺出口复位时 g_ty_steps > 0 即红——MUT4 实证过「只用快路径 = 该断言空转」) + isat_ok := iface_satisfies(TI_STR, str_intern("string")); // 轴 B 快路径(勿用 int——⑨ 已声明同名接口) + isat_no := iface_satisfies(TI_INT, str_intern("string")); // 轴 B 非同一 ⇒ 计步 + fails = fails + ts_check("isat.three_state_budget_isolated", + ts_isat_b2i(isat_ok == 1 && isat_no == 0 && g_ty_steps == 0 && g_ty_budget_max == IFACE_SAT_BUDGET && + iface_satisfies(isat_bad_ti, str_intern("int")) == -1 && + iface_satisfies(TI_INT, isat_bad_ti) == -1 && + iface_satisfies(-1, 0) == -1 && iface_satisfies(TI_INT, -1) == -1 && + iface_satisfies_term(-1, tt_top()) == -1 && iface_satisfies_term(TI_INT, -1) == -1 && + g_ty_exhausted == 0), 1); + // ⑮ 形状表语义:覆盖(同名重注册不增长)+ 负键守卫 + 复位(count=0 ⇒ 同键未命中) + isat_n := iface_shape_count(); + fails = fails + ts_check("isat.shape_registry_semantics", + ts_isat_b2i(iface_shape_register(isat_shape_name, tt_top_k(AK_SEQUENCE)) == isat_slot && + iface_shape_count() == isat_n && + iface_shape_register(-1, tt_top()) == -1 && + iface_shape_register(isat_shape_name, -1) == -1 && + iface_shape_lookup(-1) == -1 && + iface_shape_lookup(str_intern("T0bNoShape")) == -1), 1); + iface_shape_reset(); + fails = fails + ts_check("isat.shape_registry_reset", + ts_isat_b2i(iface_shape_count() == 0 && iface_shape_lookup(isat_shape_name) == -1 && + iface_satisfies(isat_seq_ti, isat_shape_name) == -1), 1); + return fails; +} + +// ─── R2 P3b Task 2 用例体(独立函数:**不得**内联进 type_selftest_run——同 ts_t5_run 的 +// 栈式代码生成上限理由;用例数 = **15**,调用方 `total = total + 15` 须同步)。 +// 判据面 = ① 六条横切形状项(序列/只读/可写/可索引/可迭代/product)的**满足判定**; +// ② 两个**消费点换位的等价前提**(索引兜底门 ↔ IP_INDEX 许可集;range 固定性 ↔ TYP_ARRAY) +// 的**全类型行枚举**。 +// **两路纪律**(Task 0 M4 教训:只走节点同一快路径的断言 = 空转):每例除「判定值」外加一条 +// 「非同一比较」断言(实参项 != 形状项 ⇒ 结果不可能由 ty_sub 的 `a == b` 快路径给出); +// 两条枚举例的枚举规模单独断言(防零行遍历/真空通过)。 +fn ts_x2_b(b: bool) -> int { if b { return 1; } return 0; } + +fn ts_x2_run() -> int { + fails : ., mut = 0; + // ── 夹具(行面)── + x2_arr := alloc_type(TYP_ARRAY, TI_INT, 3); // 固定长序列(固定性位 = N = 3) + x2_slice := alloc_type(TYP_SLICE, TI_INT, 0); // 视图(固定性位 = -1) + x2_ro_view := alloc_type(TYP_REF, x2_slice, 0); // &[int](只读视图) + x2_rw_view := alloc_type(TYP_REF, x2_slice, 1); // &mut [int](可写视图) + grow_gen_apply_data(g_gen_apply_data_count + 1); // 元组行:字段列(product 面的行) + x2_tup_start := g_gen_apply_data_count; + w64(g_gen_apply_data, x2_tup_start * 8, TI_INT); + g_gen_apply_data_count = x2_tup_start + 1; + x2_tup := alloc_type(TYP_TUPLE, 1, x2_tup_start); + x2_named := alloc_named_type(str_intern("X2ShapeN")); + + // ① 序列接口:数组 + 切片(皆 AK_SEQUENCE)满足;原生/只读视图/命名行不满足;**非同一**断言 + fails = fails + ts_check("x2.seq_accepts_body", + ts_x2_b(iface_satisfies_term(x2_arr, sh_shape_seq()) == 1 && + iface_satisfies_term(x2_slice, sh_shape_seq()) == 1 && + sh_term_of_ti(x2_arr) != sh_shape_seq() && sh_term_of_ti(x2_slice) != sh_shape_seq()), 1); + fails = fails + ts_check("x2.seq_rejects_non_body", + ts_x2_b(iface_satisfies_term(TI_INT, sh_shape_seq()) == 0 && + iface_satisfies_term(x2_ro_view, sh_shape_seq()) == 0 && + iface_satisfies_term(x2_named, sh_shape_seq()) == 0), 1); + // ② 只读序列:本体(数组/切片)+ 只读视图满足(视图面由 AK_REF **协变槽**判定 = 元素非 ⊤ + // 也成立,即「只读 = 协变」的兑现);可写视图**不**满足(mut 标记等式语义 = Task 1 裁决) + fails = fails + ts_check("x2.ro_accepts_body_and_ro_view", + ts_x2_b(iface_satisfies_term(x2_arr, sh_shape_seq_ro()) == 1 && + iface_satisfies_term(x2_slice, sh_shape_seq_ro()) == 1 && + iface_satisfies_term(x2_ro_view, sh_shape_seq_ro()) == 1 && + iface_satisfies_term(x2_rw_view, sh_shape_seq_ro()) == 0 && + sh_term_of_ti(x2_ro_view) != sh_shape_seq_ro()), 1); + // ③ 可写序列:本体满足(切片可写 = T1 探针 pSliceW `s[0] = 9` rc=0 实证);**拒绝只读视图** + fails = fails + ts_check("x2.rw_accepts_body_rejects_ro_view", + ts_x2_b(iface_satisfies_term(x2_arr, sh_shape_seq_rw()) == 1 && + iface_satisfies_term(x2_slice, sh_shape_seq_rw()) == 1 && + iface_satisfies_term(x2_ro_view, sh_shape_seq_rw()) == 0 && + iface_satisfies_term(TI_INT, sh_shape_seq_rw()) == 0), 1); + // ④ 可写**视图**形状不可表达(登记面,三态纪律):ref(mut=1, ⊤ₖ(SEQ)) ⇒ **-1**(不变槽遇 ⊤ = + // 未覆盖面),**不得**当 0/1(改成 0/1 的突变即红)。uncovered 位须**直调引擎**读 + // (`iface_satisfies_term` 出口做预算复位 = 清该位——本层读不到,非「未置位」)。 + ty_budget_reset(200000); + x2_rw_shape := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(1), tt_cons(tt_top_k(AK_SEQUENCE), tt_nil()))); + x2_rw_s := iface_satisfies_term(x2_rw_view, x2_rw_shape); + ty_budget_reset(200000); + x2_rw_direct := ty_sub(sh_term_of_ti(x2_rw_view), x2_rw_shape); + x2_rw_unc := ty_uncovered(); + fails = fails + ts_check("x2.rw_view_unexpressible", + ts_x2_b(x2_rw_s == -1 && x2_rw_direct == -1 && x2_rw_unc == 1), 1); + ty_budget_reset(200000); + // ⑤ 可索引:字符串正控(checker 既有串下标结果分支)+ 序列;原生负控 + fails = fails + ts_check("x2.indexable_str_and_seq", + ts_x2_b(iface_satisfies_term(TI_STR, sh_shape_indexable()) == 1 && + iface_satisfies_term(x2_arr, sh_shape_indexable()) == 1 && + iface_satisfies_term(x2_slice, sh_shape_indexable()) == 1 && + iface_satisfies_term(TI_INT, sh_shape_indexable()) == 0), 1); + // ⑥ 可迭代(首版)+ product:序列满足迭代;元组行满足 product;原生/命名行皆否 + fails = fails + ts_check("x2.iterable_and_product", + ts_x2_b(iface_satisfies_term(x2_arr, sh_shape_iterable()) == 1 && + iface_satisfies_term(x2_slice, sh_shape_iterable()) == 1 && + iface_satisfies_term(TI_INT, sh_shape_iterable()) == 0 && + iface_satisfies_term(x2_tup, sh_shape_product()) == 1 && + iface_satisfies_term(TI_INT, sh_shape_product()) == 0 && + iface_satisfies_term(x2_named, sh_shape_product()) == 0), 1); + // ⑦ **消费点换位等价(索引门)**:可索引形状满足集 == IP_INDEX 许可集——全类型行枚举。 + // checker 索引兜底门换位后行为保持的判据;枚举规模单独断言(反真空:行数/许可行数下界) + x2_n_rows : ., mut = 0; + x2_bad : ., mut = 0; + x2_n_perm : ., mut = 0; + x2_n_undec : ., mut = 0; + x2_i : ., mut = 0; + loop { + if x2_i >= g_type_count { break; } + x2_n_rows = x2_n_rows + 1; + x2_s := iface_satisfies_term(x2_i, sh_shape_indexable()); + x2_o := iface_permits(iface_kind_of(x2_i), IP_INDEX); + if x2_s == -1 { + x2_n_undec = x2_n_undec + 1; + } else { + if x2_s == 1 { x2_n_perm = x2_n_perm + 1; } + if (x2_s == 1) != (x2_o == 1) { x2_bad = x2_bad + 1; } + } + x2_i = x2_i + 1; + } + fails = fails + ts_check("x2.indexable_matches_ops", + ts_x2_b(x2_n_rows == g_type_count && x2_n_rows >= 15 && x2_bad == 0 && + x2_n_perm >= 3 && x2_n_undec == 0 && get_type_kind(TI_STR) == TYP_BASE), 1); + // ⑧ **消费点换位等价(range 分支)**:`形状满足 ∧ 固定性位 ≥ 0` == `kind == TYP_ARRAY`—— + // 全类型行枚举(固定长行与视图行**两类皆须出现**,否则枚举面真空) + x2_bad2 : ., mut = 0; + x2_n_fixed : ., mut = 0; + x2_n_view : ., mut = 0; + x2_j : ., mut = 0; + loop { + if x2_j >= g_type_count { break; } + x2_s2 := iface_satisfies_term(x2_j, sh_shape_seq()); + if x2_s2 != -1 { + x2_fixed : ., mut = 0; + if x2_s2 == 1 { + if sh_seq_fixed_len_of_ti(x2_j) >= 0 { x2_fixed = 1; } + } + x2_k : ., mut = 0; + if get_type_kind(x2_j) == TYP_ARRAY { x2_k = 1; } + if x2_fixed != x2_k { x2_bad2 = x2_bad2 + 1; } + if x2_s2 == 1 { + if x2_fixed == 1 { x2_n_fixed = x2_n_fixed + 1; } else { x2_n_view = x2_n_view + 1; } + } + } + x2_j = x2_j + 1; + } + fails = fails + ts_check("x2.range_fixedness_matches_kind", + ts_x2_b(x2_bad2 == 0 && x2_n_fixed >= 1 && x2_n_view >= 1), 1); + // ⑨ 形状项的**名字面**(轴 A 生产入口):六条规范项经注册表可按名判定(= Step 1「条目」的 + // 注册面兑现);复位后同键 ⇒ -1(无泄漏)。 + // R2 P4 Task 3 重钉(裁决 1 + D17):**生产注册面**同例断言——`iface_shape_builtin_init()` + // 显式重跑(幂等)后,六个 D17 名可按名查询且与 sh_shape_*() 构造点**同项**; + // 随后照旧注册六个自测名(同名覆盖语义不受影响,计数 = 6 + 6)。 + iface_shape_builtin_init(); + x2_p1 := iface_shape_lookup(str_intern("sequence")); + x2_p2 := iface_shape_lookup(str_intern("sequence_ro")); + x2_p3 := iface_shape_lookup(str_intern("sequence_rw")); + x2_p4 := iface_shape_lookup(str_intern("indexable")); + x2_p5 := iface_shape_lookup(str_intern("iterable")); + x2_p6 := iface_shape_lookup(str_intern("product")); + fails = fails + ts_check("x2.shape_builtin_names", ts_x2_b( + iface_shape_count() == 6 && x2_p1 == sh_shape_seq() && x2_p2 == sh_shape_seq_ro() && + x2_p3 == sh_shape_seq_rw() && x2_p4 == sh_shape_indexable() && + x2_p5 == sh_shape_iterable() && x2_p6 == sh_shape_product() && + iface_satisfies(x2_arr, str_intern("sequence")) == 1 && + iface_satisfies(TI_INT, str_intern("sequence")) == 0), 1); + x2_n0 := iface_shape_count(); + x2_r1 := iface_shape_register(str_intern("X2NameSeq"), sh_shape_seq()); + x2_r2 := iface_shape_register(str_intern("X2NameIdx"), sh_shape_indexable()); + x2_r3 := iface_shape_register(str_intern("X2NameRo"), sh_shape_seq_ro()); + x2_r4 := iface_shape_register(str_intern("X2NameRw"), sh_shape_seq_rw()); + x2_r5 := iface_shape_register(str_intern("X2NameItr"), sh_shape_iterable()); + x2_r6 := iface_shape_register(str_intern("X2NameProd"), sh_shape_product()); + fails = fails + ts_check("x2.axis_a_registered", + ts_x2_b(x2_r1 >= 0 && x2_r2 >= 0 && x2_r3 >= 0 && x2_r4 >= 0 && x2_r5 >= 0 && x2_r6 >= 0 && + iface_shape_count() == x2_n0 + 6 && + iface_satisfies(x2_arr, str_intern("X2NameSeq")) == 1 && + iface_satisfies(x2_arr, str_intern("X2NameIdx")) == 1 && + iface_satisfies(x2_ro_view, str_intern("X2NameRo")) == 1 && + iface_satisfies(x2_ro_view, str_intern("X2NameRw")) == 0 && + iface_satisfies(x2_tup, str_intern("X2NameProd")) == 1 && + iface_satisfies(TI_INT, str_intern("X2NameSeq")) == 0), 1); + iface_shape_reset(); + // R2 P4 Task 3 重钉:复位守门口径不变(count = 0 + 旧键 -1);**新增**生产注册面的 + // 「复位后可重建」断言(生产重置点 = reset_frontend_state 清 count,随 init_types 尾部 + // 的 iface_shape_builtin_init 重建 ⇒ 长驻进程每请求一致)。 + x2_restored : ., mut = 0; + if iface_shape_count() == 0 && iface_satisfies(x2_arr, str_intern("X2NameSeq")) == -1 { + iface_shape_builtin_init(); + if iface_shape_count() == 6 && iface_shape_lookup(str_intern("sequence")) == sh_shape_seq() { + x2_restored = 1; + } + } + fails = fails + ts_check("x2.shape_no_leak_after_reset", + ts_x2_b(x2_restored == 1), 1); + // ⑩ 零 str_intern(.ccr STR 段守卫)——R2 P4 Task 3 重定(裁决 1): + // (a) **判定/查询路径零新增驻留**:形状项构造(六构造点)+ 按名查询 + 注册幂等重跑 + // (iface_shape_builtin_init 重复调用)皆不得增长 g_strs; + // (b) **初始化/注册面的固定增量**:增量 = 逐名注册的 6 个 D17 名(在 init_types 首次 + // 调用时一次性支付——裁决 1 明示接受 STR 段增长),此后幂等 ⇒ 本处以「(a) 零增长 + + // 六名可查(= 名字已驻留且 ni 解析正确)」两式联立钉住。 + // 注:T1..T2 期的绝对口径「构造路径恒零驻留」在初始化面已不成立(原文保留于 + // iface_registry.cr 头注),本重定即该翻转的落地面。 + x2_strs := g_str_count; + x2_t1 := sh_shape_seq(); + x2_t2 := sh_shape_seq_ro(); + x2_t3 := sh_shape_seq_rw(); + x2_t4 := sh_shape_indexable(); + x2_t5 := sh_shape_iterable(); + x2_t6 := sh_shape_product(); + x2_p1b := iface_shape_lookup(str_intern("sequence")); + x2_p2b := iface_shape_lookup(str_intern("sequence_ro")); + x2_p3b := iface_shape_lookup(str_intern("sequence_rw")); + x2_p4b := iface_shape_lookup(str_intern("indexable")); + x2_p5b := iface_shape_lookup(str_intern("iterable")); + x2_p6b := iface_shape_lookup(str_intern("product")); + iface_shape_builtin_init(); // 幂等重跑(不得再增长驻留表) + fails = fails + ts_check("x2.no_str_intern", + ts_x2_b(g_str_count == x2_strs && x2_t1 >= 0 && x2_t2 >= 0 && x2_t3 >= 0 && + x2_t4 >= 0 && x2_t5 >= 0 && x2_t6 >= 0 && + x2_p1b == x2_t1 && x2_p2b == x2_t2 && x2_p3b == x2_t3 && + x2_p4b == x2_t4 && x2_p5b == x2_t5 && x2_p6b == x2_t6), 1); + // ⑪ 三态纪律(形状面):越界行/负键/不可译行 ⇒ **-1**(不得当 0/1);成功判定后预算窗口干净 + ty_budget_reset(200000); + x2_ok := iface_satisfies_term(x2_arr, sh_shape_seq()); // 非同一 ⇒ 计步(非快路径) + fails = fails + ts_check("x2.uncovered_not_folded", + ts_x2_b(x2_ok == 1 && g_ty_steps == 0 && g_ty_budget_max == IFACE_SAT_BUDGET && + iface_satisfies_term(-1, sh_shape_seq()) == -1 && + iface_satisfies_term(g_type_count + 7, sh_shape_seq()) == -1 && + iface_satisfies_term(TI_INT, -1) == -1 && + iface_satisfies(x2_arr, -1) == -1 && iface_satisfies(-1, 0) == -1 && + g_ty_exhausted == 0), 1); + // ⑫ 用户接口形状项(R2 P3b Task 6 Step 1 起**可建**):零方法接口 ⇒ **空 product**(≥0, + // 空洞满足的载体);未声明的接口名 ⇒ -1(不得当 0/1)。判断面:命名行 + 零方法 = 满足 1 + // (空洞);原生行 = 域限定 -1。 + x2_ii := ts_t5_mk_iface("X2ShapeIface"); + fails = fails + ts_check("x2.iface_shape_term_built", + ts_x2_b(find_iface(str_intern("X2ShapeIface")) == x2_ii && + sh_iface_shape_term(str_intern("X2ShapeIface")) >= 0 && + sh_iface_shape_term(str_intern("X2NoSuchIface")) == -1 && + iface_satisfies(x2_named, str_intern("X2ShapeIface")) == 1 && + iface_satisfies(TI_INT, str_intern("X2ShapeIface")) == -1), 1); + // ⑬ 形状项与**行**的桥接关系(形状判定 = 行桥接 + 引擎包含,两层都不可少):切片的项 + // 除固定性位外与数组项同形 ⇒ 两者同判(固定性**不**入形状判定——语义判定不看 N) + x2_same : ., mut = 0; + x2_ta := sh_term_of_ti(x2_arr); + x2_ts := sh_term_of_ti(x2_slice); + if x2_ta >= 0 && x2_ts >= 0 && x2_ta != x2_ts { + if ty_sub(x2_ta, x2_ts) == 1 && ty_sub(x2_ts, x2_ta) == 1 { x2_same = 1; } + } + fails = fails + ts_check("x2.fixedness_not_in_shape_judgment", + ts_x2_b(x2_same == 1 && sh_seq_fixed_len_of_ti(x2_arr) == 3 && + sh_seq_fixed_len_of_ti(x2_slice) == -1 && sh_seq_fixed_len_of_ti(TI_INT) == -1), 1); + return fails; +} + +// ═══════════════ R2 P3b Task 6:impl 契约用例(ifc.*)═══════════════ +// 面(详见 ty_shadow.cr 的 sh_sig_term_of_ti / sh_iface_shape_term 注 + type_engine.cr 的 +// iface_user_satisfies 注):① **签名类型项化**——接口方法条目的类型**节点**槽是签名的忠实 +// 来源,判定按**类型**(命名/泛型应用/接收者模式/元数)而非裸码;② **接口形状项** = 方法集 +// (product of fn,b 槽 = 方法名)建项与不可建面;③ **mangling 退役**——方法解析走 g_methods +// 表、判定路径零 str_intern;④ 三态纪律(不可判 ⇒ -1,绝不当 0/1)。 +// 夹具须与生产写点同形(parser 的 interface/impl 分支):接口方法条目 = 节点槽 + 接收者模式; +// impl 函数行 = 真 EXPR_FN(形参链 + 类型节点,形参节点**不连续**)。 +// 用例数 = 14(调用方 `total = total + 14` 须同步——增删用例两处一起改)。 + +// 命名类型夹具(结构声明 + SYM_TYPE 登记 + 命名行——供 res_type_node 的 EXPR_IDENT / +// EXPR_GENERIC_APPLY 两分支解析)。 +fn ts_ifc_mk_named(name: string) -> int { + ni := str_intern(name); + add_struct(name); + ti := alloc_named_type(ni); + def_sym(ni, SYM_TYPE, ti, -1); + return ti; +} + +// 签名类型**节点**夹具:命名型(EXPR_IDENT,int_val = 名字 ni) +fn ts_ifc_named_node(name: string) -> int { + return alloc_node(EXPR_IDENT, 0, 0, 0, str_intern(name), 0, 0, 0, 0); +} + +// 签名类型节点:序列(EXPR_ARRAY;n = 长度,0 = 视图)——实参根单槽契约照 parser 产物 +fn ts_ifc_seq_node(elem_node: int, n: int) -> int { + return alloc_node(EXPR_ARRAY, elem_node, 0, 0, n, 0, 0, 0, 0); +} + +// 签名类型节点:泛型应用(EXPR_GENERIC_APPLY;a = 基名 ni、b = 首实参节点(**连续**)、c = 个数) +fn ts_ifc_apply_node(base_name: string, arg_node: int) -> int { + return alloc_node(EXPR_GENERIC_APPLY, str_intern(base_name), arg_node, 1, 0, 0, 0, 0, 0); +} + +// 接口方法条目(节点面可控:pnodes = 参数节点缓冲(可 -1 = 全无节点);rnode = 返回型节点; +// smode = 接收者模式(0 = 无接收者,1/2/3 = self/&self/&mut self)) +fn ts_ifc_add_method_n(ii: int, mname: string, pc: int, smode: int, n0: int, n1: int, n2: int, n3: int, rnode: int) -> int { + n := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + n * ESZ_IFMETHOD; + w64(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)); + w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc); + w64(g_ifaces, mbase + OFF_IFM_RET_TI, 0); + w64(g_ifaces, mbase + OFF_IFM_RET_NODE, rnode); + w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, smode); + pj : ., mut = 0; + loop { + if pj >= pc || pj >= MAX_IFACE_METHOD_PARAMS { break; } + pn : ., mut = -1; + if pj == 0 { pn = n0; } + if pj == 1 { pn = n1; } + if pj == 2 { pn = n2; } + if pj == 3 { pn = n3; } + w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT); + w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, pn); + pj = pj + 1; + } + w64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1); + return mbase; +} + +// impl 侧函数行(节点面可控;mode0 = 首参接收者模式(≠0 时该参无节点——接收者槽约定))。 +// 返回 = 函数行号。 +fn ts_ifc_add_impl(tname_ni: int, mname: string, pc: int, mode0: int, n0: int, n1: int, rnode: int) -> int { + mn := istr_get(tname_ni) + "." + mname; + mangled_ni := str_intern(mn); + pf : ., mut = -1; + pj : ., mut = 0; + loop { + if pj >= pc { break; } + pn2 : ., mut = -1; + m : ., mut = 0; + if pj == 0 { m = mode0; } + if m == 0 { + if pj == 0 { pn2 = n0; } + if pj == 1 { pn2 = n1; } + } + if pf < 0 { pf = g_ast_count; } + alloc_node(EXPR_PARAM, str_intern("p"), 0, 0, m, TY_INT, pn2, 0, 0); + pj = pj + 1; + } + fn_node := alloc_node(EXPR_FN, mangled_ni, pf, pc, 0, rnode, -1, 0, 0); + fi := add_func(mn, pc, 0, fn_node); + grow_methods(g_method_count + 1); + w64(g_methods, g_method_count * 24, tname_ni); + w64(g_methods, g_method_count * 24 + 8, str_intern(mname)); + w64(g_methods, g_method_count * 24 + 16, mangled_ni); + g_method_count = g_method_count + 1; + return fi; +} + +fn ts_ifc_run() -> int { + fails : ., mut = 0; + // ── 夹具:4 个命名类型(A/B 同构异名——名义 vs 结构面)+ 3 个 impl 宿主 ── + ifc_a_ti := ts_ifc_mk_named("IfcA"); + ts_ifc_mk_named("IfcB"); + ts_ifc_mk_named("IfcBox"); + ifc_a_n := ts_ifc_named_node("IfcA"); + ifc_b_n := ts_ifc_named_node("IfcB"); + ifc_int_n := ts_ifc_base_node(TY_INT); + // 泛型应用节点:`IfcBox[IfcA]` / `IfcBox[IfcB]`(实参根单槽连续) + ifc_ap_a := ts_ifc_apply_node("IfcBox", ifc_a_n); + ifc_ap_b := ts_ifc_apply_node("IfcBox", ifc_b_n); + // 序列节点:`[int; 3]` / `[int; 4]` + ifc_seq3 := ts_ifc_seq_node(ifc_int_n, 3); + ifc_seq4 := ts_ifc_seq_node(ifc_int_n, 4); + // 接口:IfcI0 { m(x: IfcA) -> IfcB }(无接收者;参数/返回皆命名型) + ifc_ii0 := ts_t5_mk_iface("IfcI0"); + ts_ifc_add_method_n(ifc_ii0, "m", 1, 0, ifc_a_n, -1, -1, -1, ifc_b_n); + // 命中:同签名 + ifc_h_ti := ts_ifc_mk_named("IfcH"); + ts_ifc_add_impl(str_intern("IfcH"), "m", 1, 0, ifc_a_n, -1, ifc_b_n); + // 参数类型不符(IfcB ← IfcA) + ifc_p_ti := ts_ifc_mk_named("IfcP"); + ts_ifc_add_impl(str_intern("IfcP"), "m", 1, 0, ifc_b_n, -1, ifc_b_n); + // 返回类型不符(int ← IfcB) + ifc_r_ti := ts_ifc_mk_named("IfcR"); + ts_ifc_add_impl(str_intern("IfcR"), "m", 1, 0, ifc_a_n, -1, ifc_int_n); + + // ① 签名类型项化(参数面):命名参数型按**类型**判定——同型 1 / 异型 0(旧态两侧皆码 0 ⇒ 恒等) + fails = fails + ts_check("ifc.sig_named_param_bound", + ts_isat_b2i(iface_satisfies(ifc_h_ti, str_intern("IfcI0")) == 1 && + iface_satisfies(ifc_p_ti, str_intern("IfcI0")) == 0), 1); + // ② 签名类型项化(返回面):命名返回型 vs 原生返回型 ⇒ 0;同命名型 ⇒ 1(编码面闭合的直接判据) + fails = fails + ts_check("ifc.sig_named_ret_bound", + ts_isat_b2i(iface_satisfies(ifc_r_ti, str_intern("IfcI0")) == 0 && + iface_satisfies(ifc_h_ti, str_intern("IfcI0")) == 1), 1); + // ③ 泛型应用实参面:`IfcBox[IfcA]` vs `IfcBox[IfcB]` ⇒ 0;同实参 ⇒ 1 + // (旧态:应用行裸码塌缩为 0 ⇒ 静默判等;规范形展开基名 + 实参链 ⇒ 异实参异节点) + ifc_ii1 := ts_t5_mk_iface("IfcI1"); + ts_ifc_add_method_n(ifc_ii1, "m", 1, 0, ifc_ap_a, -1, -1, -1, ifc_int_n); + ifc_g_ti := ts_ifc_mk_named("IfcG"); + ts_ifc_add_impl(str_intern("IfcG"), "m", 1, 0, ifc_ap_a, -1, ifc_int_n); + ifc_g2_ti := ts_ifc_mk_named("IfcG2"); + ts_ifc_add_impl(str_intern("IfcG2"), "m", 1, 0, ifc_ap_b, -1, ifc_int_n); + fails = fails + ts_check("ifc.sig_apply_arg_bound", + ts_isat_b2i(iface_satisfies(ifc_g_ti, str_intern("IfcI1")) == 1 && + iface_satisfies(ifc_g2_ti, str_intern("IfcI1")) == 0), 1); + // ④ 接收者模式是判定维度:iface `&self`(2)vs impl `self`(1)/`&mut self`(3)⇒ 0;同模式 ⇒ 1 + ifc_ii2 := ts_t5_mk_iface("IfcI2"); + ts_ifc_add_method_n(ifc_ii2, "m", 1, 2, -1, -1, -1, -1, ifc_int_n); + ifc_m1_ti := ts_ifc_mk_named("IfcM1"); + ts_ifc_add_impl(str_intern("IfcM1"), "m", 1, 1, -1, -1, ifc_int_n); + ifc_m2_ti := ts_ifc_mk_named("IfcM2"); + ts_ifc_add_impl(str_intern("IfcM2"), "m", 1, 2, -1, -1, ifc_int_n); + ifc_m3_ti := ts_ifc_mk_named("IfcM3"); + ts_ifc_add_impl(str_intern("IfcM3"), "m", 1, 3, -1, -1, ifc_int_n); + fails = fails + ts_check("ifc.sig_receiver_mode", + ts_isat_b2i(iface_satisfies(ifc_m2_ti, str_intern("IfcI2")) == 1 && + iface_satisfies(ifc_m1_ti, str_intern("IfcI2")) == 0 && + iface_satisfies(ifc_m3_ti, str_intern("IfcI2")) == 0), 1); + // ⑤ 元数(链长):iface 1 参 vs impl 2 参 ⇒ 0(链形不同);impl 0 参 ⇒ 0 + ifc_w_ti := ts_ifc_mk_named("IfcW"); + ts_ifc_add_impl(str_intern("IfcW"), "m", 2, 0, ifc_a_n, ifc_int_n, ifc_b_n); + ifc_w0_ti := ts_ifc_mk_named("IfcW0"); + ts_ifc_add_impl(str_intern("IfcW0"), "m", 0, 0, -1, -1, ifc_b_n); + fails = fails + ts_check("ifc.sig_arity_chain", + ts_isat_b2i(iface_satisfies(ifc_w_ti, str_intern("IfcI0")) == 0 && + iface_satisfies(ifc_w0_ti, str_intern("IfcI0")) == 0), 1); + // ⑥ N **不入**签名身份(P3a 不变量):`[int;3]` vs `[int;4]` ⇒ 1(长度面由常量档承担) + ifc_ii3 := ts_t5_mk_iface("IfcI3"); + ts_ifc_add_method_n(ifc_ii3, "m", 1, 0, ifc_seq3, -1, -1, -1, ifc_int_n); + ifc_n_ti := ts_ifc_mk_named("IfcN"); + ts_ifc_add_impl(str_intern("IfcN"), "m", 1, 0, ifc_seq4, -1, ifc_int_n); + fails = fails + ts_check("ifc.sig_len_not_in_identity", + ts_isat_b2i(iface_satisfies(ifc_n_ti, str_intern("IfcI3")) == 1), 1); + // ⑦ 命名行**名义**(不展开):IfcA 与 IfcB 结构同构(皆零字段)但不可互换 ⇒ 0 + // (判定不落引擎的 AK_NAMED 展开面——签名身份按**行**(同名字同行)) + fails = fails + ts_check("ifc.sig_named_nominal_not_structural", + ts_isat_b2i(iface_satisfies(ifc_p_ti, str_intern("IfcI1")) == 0 && + iface_satisfies(ifc_g_ti, str_intern("IfcI1")) == 1), 1); + // ⑧ 接口形状项 = 方法集(product of fn):成员数 = 方法数、成员 b 槽 = 方法名、 + // 链长 = 1 + 参数数;越界成员 ⇒ -1 + ifc_ii4 := ts_t5_mk_iface("IfcI4"); + ts_ifc_add_method_n(ifc_ii4, "m", 1, 0, ifc_a_n, -1, -1, -1, ifc_b_n); + ts_ifc_add_method_n(ifc_ii4, "n", 2, 0, ifc_a_n, ifc_int_n, -1, -1, ifc_int_n); + ifc_shp := sh_iface_shape_term(str_intern("IfcI4")); + ifc_m0 := sh_shape_member_at(ifc_shp, 0); + ifc_m1 := sh_shape_member_at(ifc_shp, 1); + ifc_chain_ok : ., mut = 0; + if ifc_m0 >= 0 && ifc_m1 >= 0 { + c0 := tt_c(ifc_m0); + c1 := tt_c(ifc_m1); + // 链长(1 + pc):cons(ret, cons(p…)) ⇒ 2 元链(pc=1)/ 3 元链(pc=2) + if tt_tag(c0) == TT_CONS && tt_tag(tt_b(c0)) == TT_CONS && tt_tag(tt_b(tt_b(c0))) == TT_NIL { + if tt_tag(c1) == TT_CONS && tt_tag(tt_b(c1)) == TT_CONS { + if tt_tag(tt_b(tt_b(c1))) == TT_CONS && tt_tag(tt_b(tt_b(tt_b(c1)))) == TT_NIL { ifc_chain_ok = 1; } + } + } + } + fails = fails + ts_check("ifc.shape_product_of_fn", + ts_isat_b2i(ifc_shp >= 0 && tt_tag(ifc_shp) == TT_ATOM && tt_a(ifc_shp) == AK_PRODUCT && + ifc_m0 >= 0 && tt_tag(ifc_m0) == TT_ATOM && tt_a(ifc_m0) == AK_FN && + tt_b(ifc_m0) == str_intern("m") && tt_b(ifc_m1) == str_intern("n") && + ifc_chain_ok == 1 && sh_shape_member_at(ifc_shp, 2) == -1 && + sh_shape_member_at(ifc_shp, -1) == -1), 1); + // ⑨ 形状项不可建面:参数数越界(9 > MAX_IFACE_METHOD_PARAMS)⇒ -1(未覆盖面,不得当 0/1); + // 未声明接口名 ⇒ -1 + ifc_ii5 := ts_t5_mk_iface("IfcI5"); + ts_ifc_add_method_n(ifc_ii5, "m", 9, 0, -1, -1, -1, -1, ifc_int_n); + fails = fails + ts_check("ifc.shape_build_unjudged", + ts_isat_b2i(sh_iface_shape_term(str_intern("IfcI5")) == -1 && + sh_iface_shape_term(str_intern("IfcNope")) == -1 && + iface_satisfies(ifc_h_ti, str_intern("IfcI5")) == -1), 1); + // ⑩ mangling 退役:判定/查表路径**零 str_intern**(缺失方法 = 判定 0 的路径也不得驻留新串 + // ——旧 `type_has_method` 的 `str_intern("T.m")` 形态在此会增长表) + ifc_zz_ti := ts_ifc_mk_named("IfcZz"); + ifc_strs := g_str_count; + ifc_p1 := iface_satisfies(ifc_zz_ti, str_intern("IfcI0")); + ifc_p2 := type_has_method(str_intern("IfcZz"), str_intern("m")); + fails = fails + ts_check("ifc.mangling_zero_str_intern", + ts_isat_b2i(ifc_p1 == 0 && ifc_p2 == false && g_str_count == ifc_strs), 1); + // ⑪ 方法解析 = 表查询(`type_has_method` ⇔ `iface_find_method >= 0`):在位 true / 缺失 false + fails = fails + ts_check("ifc.type_has_method_table", + ts_isat_b2i(type_has_method(str_intern("IfcH"), str_intern("m")) == true && + type_has_method(str_intern("IfcZz"), str_intern("m")) == false && + iface_find_method(str_intern("IfcH"), str_intern("m")) >= 0 && + iface_find_method(str_intern("IfcZz"), str_intern("m")) < 0), 1); + // ⑫ 同源对拍(check_iface bool 面 ⇔ iface_satisfies 三态面 的 1 侧):三形态逐例相等 + ifc_eq : ., mut = 0; + if ts_isat_b2i(check_iface(str_intern("IfcH"), ifc_ii0)) == iface_satisfies(ifc_h_ti, str_intern("IfcI0")) { + if ts_isat_b2i(check_iface(str_intern("IfcP"), ifc_ii0)) == iface_satisfies(ifc_p_ti, str_intern("IfcI0")) { + if ts_isat_b2i(check_iface(str_intern("IfcR"), ifc_ii0)) == iface_satisfies(ifc_r_ti, str_intern("IfcI0")) { + ifc_eq = 1; + } + } + } + fails = fails + ts_check("ifc.check_iface_same_source", ifc_eq, 1); + // ⑬ 三态(域限定与不可判):非命名行(原生/泛型形参/dyn)⇒ -1;零方法接口 + 命名行 ⇒ 1 + // (空洞满足);未知接口名 ⇒ -1 + ifc_gp_ti := alloc_type(TYP_GENERIC_PARAM, str_intern("IfcU"), 0); + ifc_dyn_ti := alloc_type(TYP_DYN, 0, 0); + ifc_ii6 := ts_t5_mk_iface("IfcI6"); + fails = fails + ts_check("ifc.three_state_unjudged", + ts_isat_b2i(iface_satisfies(TI_INT, str_intern("IfcI0")) == -1 && + iface_satisfies(ifc_gp_ti, str_intern("IfcI0")) == -1 && + iface_satisfies(ifc_dyn_ti, str_intern("IfcI0")) == -1 && + iface_satisfies(ifc_h_ti, str_intern("IfcI6")) == 1 && + iface_satisfies(ifc_h_ti, str_intern("IfcNope")) == -1 && + iface_satisfies(-1, str_intern("IfcI0")) == -1), 1); + // ⑭ 空 product 的形状面:零方法接口的形状项链为空(tt_nil)——成员/链形判据 + ifc_shp6 := sh_iface_shape_term(str_intern("IfcI6")); + fails = fails + ts_check("ifc.shape_empty_product", + ts_isat_b2i(ifc_shp6 >= 0 && tt_tag(ifc_shp6) == TT_ATOM && tt_a(ifc_shp6) == AK_PRODUCT && + tt_c(ifc_shp6) >= 0 && tt_tag(tt_c(ifc_shp6)) == TT_NIL && + sh_shape_member_at(ifc_shp6, 0) == -1 && ifc_a_ti >= 0), 1); + return fails; +} + +// ═══════════ R2 P5 Task 3:命名面判定化(身份链 + 可判定面加强)用例段 ═══════════ +// 判据面(行为/结构断言,**不依赖 legacy 存活**——影子层下线后本段仍是回归网组件; +// 源级探针面见 tests/selfhost/test_named_face.py): +// ① 链形态(**双构造点逐位同构**:NAMED/PARAM = [名字令牌];APPLY = [基名令牌, 实参项…]); +// ② 同链 ⇒ 1 / 链异 ⇒ 0(同名/异名命名;同/异实参泛型应用;嵌套应用); +// ③ 混类(命名 × 原生)⇒ 0(= legacy 跨 kind 同结论 = false); +// ④ 域外 ⇒ -1(空链 / 令牌位错位 / 实参位 μ 变元 / NEVER·DYN 混类)——三态纪律 +// (不得近似成 0/1); +// ⑤ 清零判据(二进制内):命名面经 type_equal = 引擎自决 + **零 ICE04 诊断**(T4:回落 +// 计数随 legacy 删除,断言形态由「计数零增量」升级为「未知诊断缺席」); +// ⑥ **残留面**(计划停条件②)= 参数链**不变槽**的元素非同形(`[NA;2]` vs `[NB;2]`): +// T3 时未动(可判定但属 `tt_list_variance_at` 不变槽,须单独裁决)⇒ **T3b 已关闭** +// (`te_elem_cmp` 三态;断言迁往 `ts_t3b_run` 段——本段只剩同元素对照)。 +// 夹具 = 人造行(alloc_named_type/alloc_type),不进生产侧表语义面,只服务本段断言。 +fn ts_t3n_run() -> int { + fails : ., mut = 0; + ty_budget_reset(200000); + n_a := alloc_named_type(str_intern("T3NA")); + n_b := alloc_named_type(str_intern("T3NB")); + na_ni := get_type_data(n_a); + ta := sh_term_of_ti(n_a); + tb := sh_term_of_ti(n_b); + // ① 链形态:b 槽 = 行号(标注;D21 的 atom_of 消费者),c = [令牌(name_ni)] + fails = fails + ts_check("t3n.chain_shape", + (ta >= 0 && tt_a(ta) == AK_NAMED && tt_b(ta) == n_a && + tt_c(ta) >= 0 && tt_tag(tt_c(ta)) == TT_CONS && + tt_a(tt_c(ta)) == sh_name_token(na_ni) && + tt_b(tt_c(ta)) >= 0 && tt_tag(tt_b(tt_c(ta))) == TT_NIL), 1); + // ① D21 契约不回退:atom_of 仍读 b(命名行 = 本行;应用行 = 基型行,规范形) + // ② 同名 ⇒ 同行(named_dedup)⇒ 同节点 ⇒ 等价 1;异名 ⇒ 0(双向) + n_a2 := alloc_named_type(str_intern("T3NA")); + fails = fails + ts_check("t3n.same_name_same_row", + (n_a2 == n_a && sh_term_of_ti(n_a2) == ta && ty_equiv(ta, sh_term_of_ti(n_a2)) == 1), 1); + fails = fails + ts_check("t3n.diff_name_diff", + (ty_sub(ta, tb) == 0 && ty_sub(tb, ta) == 0 && ty_equiv(ta, tb) == 0), 1); + // ③ 混类:命名 × 原生 ⇒ 0(名义型与异类原子不交) + fails = fails + ts_check("t3n.named_vs_base_zero", + (ty_equiv(ta, sh_term_of_ti(TI_INT)) == 0 && ty_equiv(ta, sh_term_of_ti(TI_STR)) == 0 && + ty_equiv(sh_term_of_ti(TI_STR), ta) == 0), 1); + // ④ 域外:NEVER/DYN 混类 ⇒ -1 + 未覆盖面位置位(⊥/⊤ 语义未建模,登记) + ty_budget_reset(200000); + dn := ty_equiv(ta, sh_term_of_ti(TI_NEVER)); + unc1 := ty_uncovered(); + ty_budget_reset(200000); + dd := ty_equiv(ta, sh_term_of_ti(TI_DYN)); + unc2 := ty_uncovered(); + fails = fails + ts_check("t3n.never_dyn_out_of_domain", + (dn == -1 && dd == -1 && unc1 == 1 && unc2 == 1), 1); + // ④ 域外:空链(无身份 = 未覆盖面②原形态)⇒ -1 + ty_budget_reset(200000); + dnc := ty_equiv(tt_atom(AK_NAMED, -1, -1), ta); + fails = fails + ts_check("t3n.no_chain_out_of_domain", (dnc == -1 && ty_uncovered() == 1), 1); + // ④ 域外:令牌位放类型项 ⇒ -1(位置错位不猜) + ty_budget_reset(200000); + mis1 := ty_equiv(tt_atom(AK_NAMED, -1, tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil())), ta); + fails = fails + ts_check("t3n.token_pos_mismatch_out", (mis1 == -1 && ty_uncovered() == 1), 1); + // ④ 域外:实参位 μ 变元 ⇒ -1(实参位只收类型项;令牌位同节点 ⇒ 比较推进到实参位) + bad_arg := tt_atom(AK_NAMED, n_a, tt_cons(sh_name_token(na_ni), tt_cons(tt_var(0), tt_nil()))); + good_arg := tt_atom(AK_NAMED, n_a, tt_cons(sh_name_token(na_ni), tt_cons(sh_term_of_ti(TI_INT), tt_nil()))); + ty_budget_reset(200000); + mis2 := ty_equiv(bad_arg, good_arg); + fails = fails + ts_check("t3n.arg_var_out_of_domain", (mis2 == -1 && ty_uncovered() == 1), 1); + // ⑤ 清零判据(二进制内):命名面经 type_equal = **引擎自决**——R2 P5 Task 4 后回落计数 + // 本体已删(legacy 一并删除)⇒ 断言升级为「自决 + **零 ICE04 诊断**」:诊断缺席 = 引擎 + // 真判(0/1),而不是「未知被静默当 false」(P-A 的牙齿 = t4.ice04_* 三例)。 + t3n_d0 := g_diag_count; + eqf : ., mut = 0; + if !type_equal(n_a, n_b) { + if g_diag_count == t3n_d0 { eqf = 1; } + } + fails = fails + ts_check("t3n.decided_no_indeterminate", (eqf == 1 && ty_sub(ta, tb) == 0), 1); + // ② 泛型应用:规范形 b = 基型行 + 链 [基名令牌, 实参项…] + grow_gen_apply_data(g_gen_apply_data_count + 2); + gs1 := g_gen_apply_data_count; + w64(g_gen_apply_data, gs1 * 8, 1); + w64(g_gen_apply_data, (gs1 + 1) * 8, TI_INT); + g_gen_apply_data_count = gs1 + 2; + ga1 := alloc_type(TYP_GENERIC_APPLY, n_a, gs1); + ga2 := alloc_type(TYP_GENERIC_APPLY, n_a, gs1); // **另一行**,同基型同实参 + grow_gen_apply_data(g_gen_apply_data_count + 2); + gs2 := g_gen_apply_data_count; + w64(g_gen_apply_data, gs2 * 8, 1); + w64(g_gen_apply_data, (gs2 + 1) * 8, TI_STR); + g_gen_apply_data_count = gs2 + 2; + ga3 := alloc_type(TYP_GENERIC_APPLY, n_a, gs2); // 同基型**异实参** + t_ga1 := sh_term_of_ti(ga1); + t_ga2 := sh_term_of_ti(ga2); + t_ga3 := sh_term_of_ti(ga3); + fails = fails + ts_check("t3n.apply_canonical_b", + (t_ga1 >= 0 && tt_a(t_ga1) == AK_NAMED && tt_b(t_ga1) == n_a && + tt_atom_of_term(t_ga1) == n_a && + tt_c(t_ga1) >= 0 && tt_tag(tt_c(t_ga1)) == TT_CONS && + tt_a(tt_c(t_ga1)) == sh_name_token(na_ni) && + tt_a(tt_b(tt_c(t_ga1))) == sh_term_of_ti(TI_INT)), 1); + fails = fails + ts_check("t3n.apply_two_rows_same_args", + (ga1 != ga2 && t_ga1 == t_ga2 && ty_equiv(t_ga1, t_ga2) == 1), 1); + fails = fails + ts_check("t3n.apply_diff_args_zero", + (ty_sub(t_ga1, t_ga3) == 0 && ty_sub(t_ga3, t_ga1) == 0), 1); + fails = fails + ts_check("t3n.apply_vs_bare_diff", + (ty_sub(t_ga1, ta) == 0 && ty_sub(ta, t_ga1) == 0), 1); + // ② 双构造点逐位同构(计划停条件③守门):同型两构造 ⇒ **同节点** + fails = fails + ts_check("t3n.two_points_isomorphic", + (sh_sig_term_of_ti(n_a) == ta && sh_sig_term_of_ti(ga1) == t_ga1), 1); + // ② 嵌套应用:Box[Box[int]] 两处(外层实参分别指向同实参的**不同行**)⇒ 同节点 + grow_gen_apply_data(g_gen_apply_data_count + 2); + gs3 := g_gen_apply_data_count; + w64(g_gen_apply_data, gs3 * 8, 1); + w64(g_gen_apply_data, (gs3 + 1) * 8, ga1); + g_gen_apply_data_count = gs3 + 2; + ga4 := alloc_type(TYP_GENERIC_APPLY, n_a, gs3); + grow_gen_apply_data(g_gen_apply_data_count + 2); + gs4 := g_gen_apply_data_count; + w64(g_gen_apply_data, gs4 * 8, 1); + w64(g_gen_apply_data, (gs4 + 1) * 8, ga2); + g_gen_apply_data_count = gs4 + 2; + ga5 := alloc_type(TYP_GENERIC_APPLY, n_a, gs4); + fails = fails + ts_check("t3n.nested_apply_same", + (ga4 != ga5 && sh_term_of_ti(ga4) == sh_term_of_ti(ga5)), 1); + // ② 可选面(union):NA ⊂ NA?(注入面)⇒ 1;NA? vs NB?(内层异名)⇒ 0 + opt_a := tt_union(ta, sh_null_term()); + opt_b := tt_union(tb, sh_null_term()); + fails = fails + ts_check("t3n.optional_named", + (ty_sub(ta, opt_a) == 1 && ty_sub(opt_a, opt_b) == 0), 1); + // ④ 域外:union 面含**无链**命名原子(未覆盖面②原形态)⇒ 仍 -1(加强不得把 + // 未范化/域外并集面误判成 0——计划 Step 1 的 union 负控) + ty_budget_reset(200000); + u_out := ty_equiv(tt_union(tt_atom(AK_NAMED, -1, -1), sh_null_term()), + tt_union(tt_atom(AK_NAMED, -2, -1), sh_null_term())); + fails = fails + ts_check("t3n.union_out_of_domain", (u_out == -1 && ty_uncovered() == 1), 1); + // ⑥ 残留面**已关闭**(R2 P5 Task 3b:不变槽元素三态 te_elem_cmp)——断言迁移到 + // `t3b.inv_slot_seq_named_diff_zero`(本段不再钉「-1」;翻转向量见 p5-task3b 报告) + // ⑥ 对照:同元素(不同行)⇒ 1(N 不入身份 + 同链 ⇒ 快路径) + arr_a := sh_seq_term(n_a, 2); + arr_a2 := sh_seq_term(n_a, 2); + fails = fails + ts_check("t3n.same_elem_seq_equiv", ty_equiv(arr_a, arr_a2), 1); + return fails; +} + +// ═══════════ R2 P5 Task 3b:不变槽元素三态(te_elem_cmp)用例段 ═══════════ +// 关闭 P5 Task 3 §6 登记的残留面(交接条目即本段 RED 面)。判据六面: +// ① 主形态:序列/指针/元组/嵌套的**不变槽**元素 = 异名命名原子 ⇒ 引擎自决 **0** +// (双向)+ **零未覆盖面**(= 红态计数面:改前 unknown_engine=1 replace_unknown=1); +// ② 不误伤等价面:同元素(另一行)/ N 面(`[T;2]` vs `[T;3]`)/ 同令牌 ⇒ **1**; +// ③ 令牌位(AK_SUM 链槽 0/1 = 枚举名/变体名令牌)按**节点同一性**:异名 ⇒ 0、同名 ⇒ 1; +// ④ 域外守卫:链元素**既非令牌也非类型项**(μ 变元)⇒ 仍 **-1 + 未覆盖面**(不近似成 0); +// ⑤ 负控(报告 §6.1 点名的两例):(a) 同名令牌 ⇒ **同一节点**(DAG 去重 ⇒ 结构上不存在 +// 「同名不同节点」,节点同一性比较不可能把同名令牌判不同);(b) 令牌 × 原生 unit 项 +// **同形**(AK_UNIT ∧ b ≥ 0)——`ty_equiv` 判**等**(令牌碰撞类的实证),`te_elem_cmp` +// 令牌先判 ⇒ 0(绝不产生假等;失效方向选择见 type_engine.cr 该函数头注); +// ⑥ 二进制面清零判据:数组行经 `type_equal` = **引擎自决 + 零 ICE04 诊断**(T4 重钉: +// 回落计数随 legacy 删除,断言形态改为「未知诊断缺席」)。 +// 夹具 = 人造行(alloc_named_type/alloc_type,同名 ⇒ 同名去重行);不进生产侧表语义面。 +fn ts_t3b_run() -> int { + fails : ., mut = 0; + ty_budget_reset(200000); + n_a := alloc_named_type(str_intern("T3NA")); + n_b := alloc_named_type(str_intern("T3NB")); + t_a := sh_term_of_ti(n_a); + t_b := sh_term_of_ti(n_b); + t_int := sh_term_of_ti(TI_INT); + // ① 序列元素(AK_SEQUENCE 链槽 0):异名 ⇒ 0 双向 + 零未覆盖面 + arr_a := alloc_type(TYP_ARRAY, n_a, 2); + arr_b := alloc_type(TYP_ARRAY, n_b, 2); + ty_budget_reset(200000); + ab := ty_sub(sh_term_of_ti(arr_a), sh_term_of_ti(arr_b)); + ba := ty_sub(sh_term_of_ti(arr_b), sh_term_of_ti(arr_a)); + fails = fails + ts_check("t3b.inv_slot_seq_named_diff_zero", + ts_isat_b2i(ab == 0 && ba == 0 && ty_uncovered() == 0), 1); + // ① 指针元素(AK_PTR 链槽 0)⇒ 0 + ty_budget_reset(200000); + pb := ty_sub(tt_atom(AK_PTR, -1, tt_cons(t_a, tt_nil())), + tt_atom(AK_PTR, -1, tt_cons(t_b, tt_nil()))); + fails = fails + ts_check("t3b.inv_slot_ptr_named_diff_zero", + ts_isat_b2i(pb == 0 && ty_uncovered() == 0), 1); + // ① 元组字段位(AK_PRODUCT 链;第 2 字段同项 = 对照组)⇒ 0 + ty_budget_reset(200000); + pr := ty_sub(tt_atom(AK_PRODUCT, -1, tt_cons(t_a, tt_cons(t_int, tt_nil()))), + tt_atom(AK_PRODUCT, -1, tt_cons(t_b, tt_cons(t_int, tt_nil())))); + fails = fails + ts_check("t3b.inv_slot_prod_named_diff_zero", + ts_isat_b2i(pr == 0 && ty_uncovered() == 0), 1); + // ① 嵌套(元素 = 序列项 ⇒ 经 te_elem_cmp 的类型项面递归):[[NA;2];2] vs [[NB;2];2] ⇒ 0 + nest_a := alloc_type(TYP_ARRAY, alloc_type(TYP_ARRAY, n_a, 2), 2); + nest_b := alloc_type(TYP_ARRAY, alloc_type(TYP_ARRAY, n_b, 2), 2); + ty_budget_reset(200000); + ns := ty_sub(sh_term_of_ti(nest_a), sh_term_of_ti(nest_b)); + fails = fails + ts_check("t3b.inv_slot_nested_seq_diff_zero", + ts_isat_b2i(ns == 0 && ty_uncovered() == 0), 1); + // ② 等价面不被压成 0:N 面([NA;2] vs [NA;3] ⇒ 1)+ 同元素**另一行** ⇒ 1 + ty_budget_reset(200000); + n_eq := ty_equiv(sh_term_of_ti(arr_a), sh_term_of_ti(alloc_type(TYP_ARRAY, n_a, 3))); + r_eq := ty_equiv(sh_term_of_ti(arr_a), sh_term_of_ti(alloc_type(TYP_ARRAY, n_a, 2))); + fails = fails + ts_check("t3b.equiv_face_not_crushed", + ts_isat_b2i(n_eq == 1 && r_eq == 1 && ty_uncovered() == 0), 1); + // ③ 令牌位:AK_SUM 链 = [枚举名令牌, 变体名令牌](皆不变槽)——异变体名 ⇒ 0 + e1 := str_intern("T3bE1"); + v1 := str_intern("T3bV1"); + v2 := str_intern("T3bV2"); + sum_v1 := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(sh_name_token(v1), tt_nil()))); + sum_v2 := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(sh_name_token(v2), tt_nil()))); + ty_budget_reset(200000); + sv := ty_sub(sum_v1, sum_v2); + fails = fails + ts_check("t3b.token_slot_variant_diff_zero", + ts_isat_b2i(sv == 0 && ty_uncovered() == 0), 1); + // ③ 同令牌 ⇒ 同节点 ⇒ 1(同枚举同变体两处构造) + sum_v1x := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(sh_name_token(v1), tt_nil()))); + fails = fails + ts_check("t3b.token_slot_same_one", + ts_isat_b2i(sum_v1 == sum_v1x && ty_equiv(sum_v1, sum_v1x) == 1), 1); + // ⑤(a) 负控:同名令牌 ⇒ 同一节点(DAG 去重)——「同名不同节点」结构上不可达; + // mut 标记同式(同值同节点、异值异节点) + fails = fails + ts_check("t3b.token_same_name_same_node", + ts_isat_b2i(sh_name_token(e1) == sh_name_token(e1) && + sh_name_token(e1) != sh_name_token(v1) && + sh_ref_mut_marker(0) == sh_ref_mut_marker(0) && + sh_ref_mut_marker(0) != sh_ref_mut_marker(1)), 1); + // ⑤(b) 负控:令牌 × 原生 unit 项同形 ⇒ ty_equiv 判**等**(碰撞实证); + // te_elem_cmp 令牌先判 ⇒ 0(无假等);unit × unit ⇒ 同节点 ⇒ 1 + u_item := sh_term_of_ti(TI_UNIT); + tok_e1 := sh_name_token(e1); + ty_budget_reset(200000); + coll := ty_equiv(u_item, tok_e1); + fails = fails + ts_check("t3b.unit_token_shape_collision_guard", + ts_isat_b2i(coll == 1 && te_elem_cmp(u_item, tok_e1) == 0 && + te_elem_cmp(u_item, u_item) == 1), 1); + // ⑤(b) 补:unit 项在**非令牌型**元素位上按类型项比较 ⇒ [unit] vs [int] = 0; + // [unit] vs [unit](N 不同)⇒ 1 + ty_budget_reset(200000); + u_i := ty_sub(sh_seq_term(TI_UNIT, 2), sh_seq_term(TI_INT, 2)); + u_u := ty_equiv(sh_seq_term(TI_UNIT, 2), sh_seq_term(TI_UNIT, 3)); + fails = fails + ts_check("t3b.unit_elem_type_item_face", + ts_isat_b2i(u_i == 0 && u_u == 1 && ty_uncovered() == 0), 1); + // ④ 域外守卫的牙:链元素既非令牌也非类型项(μ 变元)⇒ -1 + 未覆盖面 + ty_budget_reset(200000); + dom := ty_sub(tt_atom(AK_SEQUENCE, -1, tt_cons(tt_var(0), tt_nil())), + tt_atom(AK_SEQUENCE, -1, tt_cons(t_int, tt_nil()))); + fails = fails + ts_check("t3b.elem_out_of_domain_stays_minus1", + ts_isat_b2i(dom == -1 && ty_uncovered() == 1), 1); + // ② 只读 ref 元素(协变槽)+ mut 标记槽回归钉:&NA vs &NB ⇒ 0;& 与 &mut ⇒ 0 + ref_a := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(t_a, tt_nil()))); + ref_b := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(t_b, tt_nil()))); + ref_m := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(1), tt_cons(t_a, tt_nil()))); + ty_budget_reset(200000); + rb := ty_sub(ref_a, ref_b); + rm := ty_sub(ref_a, ref_m); + fails = fails + ts_check("t3b.ref_elem_and_marker", + ts_isat_b2i(rb == 0 && rm == 0 && ty_uncovered() == 0), 1); + // ⑥ 清零判据(二进制内):数组行经 type_equal = 引擎自决 + 零 ICE04 诊断(T4 重钉: + // 回落计数已随 legacy 删除 ⇒ 断言形态 = 「未知诊断缺席」) + t3b_d0 := g_diag_count; + dec : ., mut = 0; + if !type_equal(arr_a, arr_b) { + if g_diag_count == t3b_d0 { dec = 1; } + } + fails = fails + ts_check("t3b.decided_no_indeterminate_arr", dec, 1); + return fails; +} + +// ═══════════ R2 P5 Task 4:残留 -1 政策 P-A(ICE04 硬错)用例段 ═══════════ +// 判据面(**不依赖 legacy 存活**——它就是 legacy 删除后的替身证据;影子层下线后照常): +// ① 桥接缺口(行号越界 ⇒ 该行译不成类型项)⇒ **判 false + ICE04 恰 1 条**(旧行为 = 回落 +// legacy + g_replace_bridge 计数;两件旧物均已删除); +// ② 引擎 -1(未覆盖面:命名原子身份链 × NEVER 原子 ⇒ 域外,`te_named_pair` 的 NEVER/DYN +// 守卫)⇒ **判 false + ICE04**,且措辞 ≠ 桥接缺口措辞(成因可读 = 归因不混); +// ③ 决定面**零误报**:引擎自决(0/1)与同一行快路径一律不产 ICE04——三态纪律的牙齿 = +// 「未知 ⇒ 诊断」而「确定 ⇒ 无诊断」(P-A 若被改回「静默 false」= ①②转红;若被改成 +// 「全报」= ③转红)。 +// 诊断是**故意**触发的:本段结束时复位 g_diag_count(不留进后续用例的增量基线)。 +fn ts_t4_run() -> int { + fails : ., mut = 0; + // ① 桥接缺口(ti 越界 = 无项) + t4_d0 := g_diag_count; + t4_b1 : ., mut = 0; + if !type_equal(g_type_count + 100, TI_INT) { t4_b1 = 1; } + t4_d1 := g_diag_count; + t4_m_bridge := ts_diag_msg_at(t4_d0); + fails = fails + ts_check("t4.ice04_bridge_gap_hard_error", + (t4_b1 == 1 && t4_d1 == t4_d0 + 1 && ts_diag_code_at(t4_d0) == EC_ICE_TY_INDET && + str_len(t4_m_bridge) > 0), 1); + // ② 引擎 -1(未覆盖面):命名原子(T3 身份链)× NEVER 原子 = **域外**(`te_named_pair` + // 的 NEVER/DYN 守卫 ⇒ -1 + g_ty_uncovered,见 t3n.never_dyn_out_of_domain)——这是 + // 桥接**能译**(两条目皆 ≥ 0)但引擎判不了的形态:与 ① 的桥接缺口是**两个不同出口**。 + // (反例形态记:AK_NEVER × AK_DYN 是**已判**面(⊥ ⊆ ⊤ 不反向 ⇒ 0),不产 -1。) + t4_na := alloc_named_type(str_intern("T4IndetNamed")); + t4_d2 := g_diag_count; + t4_b2 : ., mut = 0; + if !type_equal(t4_na, TI_NEVER) { t4_b2 = 1; } + t4_d3 := g_diag_count; + t4_m_engine := ts_diag_msg_at(t4_d2); + fails = fails + ts_check("t4.ice04_engine_uncovered_hard_error", + (t4_b2 == 1 && t4_d3 == t4_d2 + 1 && ts_diag_code_at(t4_d2) == EC_ICE_TY_INDET && + str_len(t4_m_engine) > 0 && str_eq(t4_m_bridge, t4_m_engine) == 0), 1); + // ③ 决定面零误报(引擎自决 0 与同一行快路径各自零诊断) + t4_d4 := g_diag_count; + t4_ok : ., mut = 0; + if !type_equal(TI_INT, TI_STR) { // 引擎自决 0 + if type_equal(TI_INT, TI_INT) { // 同一行快路径(自反) + if g_diag_count == t4_d4 { t4_ok = 1; } + } + } + fails = fails + ts_check("t4.ice04_decided_no_false_positive", t4_ok, 1); + g_diag_count = t4_d4; // 复位(本段的 ICE04 是故意触发的) + return fails; +} + +// ═══════════ R2 P4 Task 2:TYPE 段内容面(装填 / 序列化 / dedup / 确定性)═══════════ +// 用例面:① 段体缓冲 ↔ 内存表逐字段 roundtrip(行表 24B/条 + 项表 40B/条 + 长度公式); +// ② dedup 重建(同构项 = 同节点;b 标注区分固定长度;DAG 无同 (tag,a..d) 重行); +// ③ **确定性**(D13 的牙):判定历史扰动(ty_sub/ty_equiv 触发规范化向项表追加新项) +// 后重装填 ⇒ 段体逐字节同(「直接序列化活表」的旧实现必在此变——见 ccr_types.cr 头注); +// ④ 不可译行 ⇒ 装填拒绝(save 侧拒落盘,三态纪律)。 +// 夹具说明:本组自建干净类型表(init_types 9 原生行 + 4 行可选/定长数组),不经 +// parser/check_all;**必须最后运行**(populate 复位项层/引擎/桥接三面缓存)。 +fn ts_ccr_roundtrip_ok() -> int { + rc := g_type_count; + tc := tt_count(); + if g_ccr_type_seg_len != 4 + rc * ESZ_TYPE_ROW + 4 + tc * ESZ_TYPE_TERM_DISK { return 0; } + if buf_read_u32(g_ccr_type_seg, 0) != rc { return 0; } + pos : ., mut = 4; + i : ., mut = 0; + loop { + if i >= rc { break; } + if buf_read_i64(g_ccr_type_seg, pos) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 8) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 16) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA) { return 0; } + pos = pos + ESZ_TYPE_ROW; + i = i + 1; + } + if buf_read_u32(g_ccr_type_seg, pos) != tc { return 0; } + pos = pos + 4; + j : ., mut = 0; + loop { + if j >= tc { break; } + if buf_read_i64(g_ccr_type_seg, pos) != tt_tag(j) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 8) != tt_a(j) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 16) != tt_b(j) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 24) != tt_c(j) { return 0; } + if buf_read_i64(g_ccr_type_seg, pos + 32) != tt_d(j) { return 0; } + pos = pos + ESZ_TYPE_TERM_DISK; + j = j + 1; + } + if pos != g_ccr_type_seg_len { return 0; } + return 1; +} + +fn ts_ccr_dedup_ok(opt_a: int, opt_b: int, arr3: int, arr4: int) -> int { + // 同构 ⇒ 同节点(两个 `int?` 行 → 同一 union 项);b 标注(固定长度)区分 ⇒ 异节点 + if sh_term_of_ti(opt_a) != sh_term_of_ti(opt_b) { return 0; } + if sh_term_of_ti(arr3) == sh_term_of_ti(arr4) { return 0; } + // DAG 不变量:表内无两个同 (tag,a..d) 的行(tt_term 去重——重建路径的牙) + n := tt_count(); + i : ., mut = 0; + loop { + if i >= n { break; } + j : ., mut = i + 1; + loop { + if j >= n { break; } + if tt_tag(i) == tt_tag(j) && tt_a(i) == tt_a(j) && tt_b(i) == tt_b(j) && + tt_c(i) == tt_c(j) && tt_d(i) == tt_d(j) { return 0; } + j = j + 1; + } + i = i + 1; + } + return 1; +} + +fn ts_ccr_purity_ok() -> int { + n0 := g_ccr_type_seg_len; + cp := alloc(n0 + 8); + _dyncpy(g_ccr_type_seg, n0, cp); + // R2 P4 Task 3:IFACE 段同批纳入(D13 的承诺面 = **两段**皆类型表/接口表的纯函数) + i0 := g_ccr_iface_seg_len; + ip := alloc(i0 + 8); + _dyncpy(g_ccr_iface_seg, i0, ip); + // 判定历史扰动:跨项判定 + 规范化(向项表追加新项——旧「直接序列化活表」必在此变) + pa := tt_atom(AK_INT, TI_INT, -1); + pb := tt_atom(AK_STRING, TI_STR, -1); + if ty_sub(tt_union(pa, pb), pa) != 0 { println("ct.seg_determinism: perturbation ty_sub != 0"); return 0; } + if ty_disjoint(pa, pb) != 1 { println("ct.seg_determinism: perturbation ty_disjoint != 1"); return 0; } + if ty_equiv(tt_union(pa, tt_union(pb, pa)), tt_union(pa, pb)) != 1 { + println("ct.seg_determinism: perturbation ty_equiv != 1"); return 0; + } + // 唯一标注项(**保证**向项表追加新项——判定扰动可能与既有项同构而零新增, + // 突变控制实证:仅判定扰动的版本对「跳过装填」突变不转红,本项补上该牙) + uq := tt_atom(AK_DEX, 900001, tt_cons(pa, tt_nil())); + if tt_tag(uq) != TT_ATOM || tt_b(uq) != 900001 { + println("ct.seg_determinism: unique-term fixture failed"); return 0; + } + // 重装填(项层/引擎/桥接复位 → 行序重建)⇒ 段体逐字节同 + if ccr_seg_prepare_save() != 0 { println("ct.seg_determinism: rebuild prepare failed"); return 0; } + if g_ccr_type_seg_len != n0 { + print("ct.seg_determinism: buffer length changed "); print(int_str(n0)); + print(" -> "); println(int_str(g_ccr_type_seg_len)); + return 0; + } + k : ., mut = 0; + loop { + if k >= n0 { break; } + if load8(g_ccr_type_seg, k) != load8(cp, k) { + print("ct.seg_determinism: buffer byte mismatch at "); println(int_str(k)); + return 0; + } + k = k + 1; + } + // IFACE 段同判(R2 P4 Task 3):重装填后逐字节同(形状重注册/签名装填皆纯函数) + if g_ccr_iface_seg_len != i0 { + print("ct.seg_determinism: iface buffer length changed "); print(int_str(i0)); + print(" -> "); println(int_str(g_ccr_iface_seg_len)); + return 0; + } + k2 : ., mut = 0; + loop { + if k2 >= i0 { break; } + if load8(g_ccr_iface_seg, k2) != load8(ip, k2) { + print("ct.seg_determinism: iface buffer byte mismatch at "); println(int_str(k2)); + return 0; + } + k2 = k2 + 1; + } + return 1; +} + +fn ts_ccr_run() -> int { + fails : ., mut = 0; + init_types(); // 干净表(9 原生行——不受前段夹具影响) + // R2 P4 Task 3:接口表同批清零(段体含 IFACE 五小节 ⇒ 上游 ifc/isat 夹具的**死节点** + // (EXPR_IDENT 指向已被 init_types 作废的命名行 ⇒ res_type_node 落 dyn 行)会令签名项 + // 不可建 ⇒ 装填拒绝。本组语义 = 「干净类型表 + 干净接口表」的段体往返夹具。 + g_iface_count = 0; + g_impl_for_count = 0; + g_method_count = 0; + iface_shape_reset(); + iface_shape_builtin_init(); + opt_a := alloc_type(TYP_OPTIONAL, TI_INT, 0); + opt_b := alloc_type(TYP_OPTIONAL, TI_INT, 0); + arr3 := alloc_type(TYP_ARRAY, TI_INT, 3); + arr4 := alloc_type(TYP_ARRAY, TI_INT, 4); + prep := ccr_seg_prepare_save(); + fails = fails + ts_check("ct.seg_prepare", prep, 0); + if prep == 0 { + fails = fails + ts_check("ct.seg_roundtrip", ts_ccr_roundtrip_ok(), 1); + fails = fails + ts_check("ct.seg_dedup_identity", ts_ccr_dedup_ok(opt_a, opt_b, arr3, arr4), 1); + fails = fails + ts_check("ct.seg_determinism", ts_ccr_purity_ok(), 1); + } else { + fails = fails + 3; // 后续三例无法运行 ⇒ 计失败(不得静默少算) + } + // ④ 不可译行(子行越界)⇒ 装填拒绝(save 拒落盘)——**最后**(污染类型表) + bad_row := alloc_type(TYP_ARRAY, 999999, 0); + rej := ccr_type_populate(); + if bad_row < 0 { rej = 1; } // 夹具未建成 ⇒ 不算通过(防假绿) + fails = fails + ts_check("ct.seg_reject_untranslatable", rej, -1); + return fails; +} + +// ═══════════ R2 P4 Task 3:IFACE 段内容面(扩列 / 命名化 / 签名项化 / 段体往返)═══════════ +// 用例面(7 例):① 段体五小节与内存表逐字段一致 + 行走完 == 缓冲长度(无尾随); +// ② 签名项槽(ret_term/param_terms)== 活算值(装填与签名来源零漂移;未用槽 = -1); +// ③ 形状名生产注册面(六名可按名查询且与 sh_shape_*() 构造点**同项**);④ 条目表扩列 +// (16 行 + 三新行 ops = 0 + 逐名可读);⑤ 判定面零变化(全类型行枚举无新映射 + +// TYP_NULL/TYP_OPTIONAL 行仍 -1);⑥ 不可建签名(dyn 位图行入签名)⇒ 装填拒绝 +// (save 拒落盘)——**最后**(污染接口表)。 +// 夹具/边界:本组自建**干净接口表**(g_iface_count/g_impl_for_count/g_method_count 清零 + +// 形状表 reset + 生产注册面重跑)——上游 ts_ifc_run 的夹具含 9 形参接口(不可建面), +// 不清零会令装填拒绝(设计如此:populate 不静默跳过)。**必须最后运行**(populate 复位三面缓存)。 +fn ts_ccr2_row_ok(b: string, p: int, i: int) -> int { + eo := i * ESZ_IFACE_ENTRY; + if buf_read_i32(b, p) != r64(g_iface_entries, eo + OFF_IE_AK) { return 0; } + if buf_read_i32(b, p + 4) != r64(g_iface_entries, eo + OFF_IE_TI) { return 0; } + if buf_read_i32(b, p + 8) != r64(g_iface_entries, eo + OFF_IE_NAME) { return 0; } + if buf_read_i32(b, p + 12) != r64(g_iface_entries, eo + OFF_IE_LIT) { return 0; } + if buf_read_i64(b, p + 16) != r64(g_iface_entries, eo + OFF_IE_OPS) { return 0; } + return 1; +} + +fn ts_ccr2_seg_walk_ok() -> int { + b := g_ccr_iface_seg; + n := g_ccr_iface_seg_len; + if n <= 0 { return 0; } + // ① 原生条目(计数 == 常量 == 表计数;逐行 5 字段 == g_iface_entries) + if buf_read_u32(b, 0) != IFACE_ENTRY_COUNT { return 0; } + if buf_read_u32(b, 0) != g_iface_entry_count { return 0; } + p : ., mut = 4; + ei : ., mut = 0; + loop { + if ei >= IFACE_ENTRY_COUNT { break; } + if ts_ccr2_row_ok(b, p, ei) != 1 { return 0; } + p = p + 24; + ei = ei + 1; + } + // ② 横切形状 + if buf_read_u32(b, p) != g_iface_shape_count { return 0; } + p = p + 4; + si : ., mut = 0; + loop { + if si >= g_iface_shape_count { break; } + if buf_read_i32(b, p) != r64(g_iface_shape_names, si * 8) { return 0; } + if buf_read_i32(b, p + 4) != r64(g_iface_shape_terms, si * 8) { return 0; } + p = p + 8; + si = si + 1; + } + // ③ 用户接口(头 16B + 方法 80B:项槽先于裸码槽——实施取声明行序,见 ccr_types.cr 头注) + if buf_read_u32(b, p) != g_iface_count { return 0; } + p = p + 4; + fi : ., mut = 0; + loop { + if fi >= g_iface_count { break; } + fo := fi * ESZ_IFACEINFO; + mc := r64(g_ifaces, fo + OFF_IF_METHOD_COUNT); + if buf_read_i32(b, p) != r64(g_ifaces, fo + OFF_IF_NAME) { return 0; } + if buf_read_i32(b, p + 4) != mc { return 0; } + if buf_read_i32(b, p + 8) != r64(g_ifaces, fo + OFF_IF_GENERIC_COUNT) { return 0; } + p = p + 16; + mj : ., mut = 0; + loop { + if mj >= mc { break; } + mo := fo + OFF_IF_METHODS + mj * ESZ_IFMETHOD; + if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_NAME) { return 0; } + if buf_read_i32(b, p + 4) != r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT) { return 0; } + if buf_read_i32(b, p + 8) != r64(g_ifaces, mo + OFF_IFM_SELF_MODE) { return 0; } + if buf_read_i32(b, p + 12) != r64(g_ifaces, mo + OFF_IFM_RET_TERM) { return 0; } + p = p + 16; + kq : ., mut = 0; + loop { + if kq >= MAX_IFACE_METHOD_PARAMS { break; } + if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + kq * 8) { return 0; } + p = p + 4; + kq = kq + 1; + } + qq : ., mut = 0; + loop { + if qq >= MAX_IFACE_METHOD_PARAMS { break; } + if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_PARAM_TYPES + qq * 8) { return 0; } + p = p + 4; + qq = qq + 1; + } + mj = mj + 1; + } + fi = fi + 1; + } + // ④ impl 边 + if buf_read_u32(b, p) != g_impl_for_count { return 0; } + p = p + 4; + gi : ., mut = 0; + loop { + if gi >= g_impl_for_count { break; } + if buf_read_i32(b, p) != r64(g_impl_for, gi * 16) { return 0; } + if buf_read_i32(b, p + 4) != r64(g_impl_for, gi * 16 + 8) { return 0; } + p = p + 8; + gi = gi + 1; + } + // ⑤ 方法表 + if buf_read_u32(b, p) != g_method_count { return 0; } + p = p + 4; + mi : ., mut = 0; + loop { + if mi >= g_method_count { break; } + if buf_read_i32(b, p) != r64(g_methods, mi * 24) { return 0; } + if buf_read_i32(b, p + 4) != r64(g_methods, mi * 24 + 8) { return 0; } + if buf_read_i32(b, p + 8) != r64(g_methods, mi * 24 + 16) { return 0; } + p = p + 12; + mi = mi + 1; + } + if p != n { return 0; } // 行走完 == 缓冲长度(五小节自洽 + 无尾随字节) + return 1; +} + +fn ts_ccr2_sig_slots_ok() -> int { + ii : ., mut = 0; + loop { + if ii >= g_iface_count { break; } + mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT); + mi : ., mut = 0; + loop { + if mi >= mc { break; } + mo := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD; + // 返回槽 == 活算(签名的唯一来源 = sh_iface_sig_ret_term) + if r64(g_ifaces, mo + OFF_IFM_RET_TERM) != sh_iface_sig_ret_term(ii, mi) { return 0; } + pc := r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT); + pj : ., mut = 0; + loop { + if pj >= MAX_IFACE_METHOD_PARAMS { break; } + if pj < pc { + if r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != sh_iface_sig_param_term(ii, mi, pj) { return 0; } + } else { + if r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != -1 { return 0; } // 未用槽 = -1 + } + pj = pj + 1; + } + mi = mi + 1; + } + ii = ii + 1; + } + return 1; +} + +fn ts_ccr2_shape_names_ok() -> int { + // 六生产名可查 + 与构造点**同项**(节点同一——形状项 = 唯一数据面) + if iface_shape_count() < 6 { return 0; } + if iface_shape_lookup(str_intern("sequence")) != sh_shape_seq() { return 0; } + if iface_shape_lookup(str_intern("sequence_ro")) != sh_shape_seq_ro() { return 0; } + if iface_shape_lookup(str_intern("sequence_rw")) != sh_shape_seq_rw() { return 0; } + if iface_shape_lookup(str_intern("indexable")) != sh_shape_indexable() { return 0; } + if iface_shape_lookup(str_intern("iterable")) != sh_shape_iterable() { return 0; } + if iface_shape_lookup(str_intern("product")) != sh_shape_product() { return 0; } + return 1; +} + +fn ts_ccr2_entries_ok() -> int { + // 扩列 13 → 16:三新行可查 + ops = 0(纯信息面)+ 16 行逐名(注册面固定增量) + if iface_count() != 16 { return 0; } + if iface_entry(AK_NULL) < 0 { return 0; } + if iface_entry(AK_SUM) < 0 { return 0; } + if iface_entry(AK_FN) < 0 { return 0; } + if iface_ops(AK_NULL) != 0 { return 0; } + if iface_ops(AK_SUM) != 0 { return 0; } + if iface_ops(AK_FN) != 0 { return 0; } + if iface_ti_of(AK_NULL) != -1 { return 0; } // 无规范行(类级) + if iface_ti_of(AK_SUM) != -1 { return 0; } + if iface_ti_of(AK_FN) != -1 { return 0; } + // 逐名(16 行:8 原生 + 5 结构/命名 + 3 新行) + if iface_name_of(AK_INT) != str_intern("int") { return 0; } + if iface_name_of(AK_DEX) != str_intern("dex") { return 0; } + if iface_name_of(AK_STRING) != str_intern("string") { return 0; } + if iface_name_of(AK_BOOL) != str_intern("bool") { return 0; } + if iface_name_of(AK_UNIT) != str_intern("unit") { return 0; } + if iface_name_of(AK_NEVER) != str_intern("never") { return 0; } + if iface_name_of(AK_CHAR) != str_intern("char") { return 0; } + if iface_name_of(AK_DYN) != str_intern("dyn") { return 0; } + if iface_name_of(AK_PRODUCT) != str_intern("product") { return 0; } + if iface_name_of(AK_SEQUENCE) != str_intern("sequence") { return 0; } + if iface_name_of(AK_REF) != str_intern("ref") { return 0; } + if iface_name_of(AK_PTR) != str_intern("ptr") { return 0; } + if iface_name_of(AK_NAMED) != str_intern("named") { return 0; } + if iface_name_of(AK_NULL) != str_intern("null") { return 0; } + if iface_name_of(AK_SUM) != str_intern("sum") { return 0; } + if iface_name_of(AK_FN) != str_intern("fn") { return 0; } + if iface_name_of(-1) != -1 { return 0; } // 负键守卫(不得与行 0 混同) + if iface_name_of(9999) != -1 { return 0; } + return 1; +} + +fn ts_ccr2_kind_domain_ok() -> int { + // 判定面零变化(扩列只加「可查询性」):全类型行枚举——三新原子不得被任何行命中 + // (若未来给某行新映射到 AK_NULL/AK_SUM/AK_FN,本用例转红 = 有意的闸) + i : ., mut = 0; + loop { + if i >= g_type_count { break; } + k := iface_kind_of(i); + if k == AK_NULL || k == AK_SUM || k == AK_FN { return 0; } + i = i + 1; + } + return 1; +} + +fn ts_ccr2_run() -> int { + fails : ., mut = 0; + // 生产重置路径的接线钉(先于夹具):`reset_frontend_state` 清形状表 count + + // `check_all` 首行的 init_types ⇒ 六名重建——**不经**显式 iface_shape_builtin_init + // (本断言 = 「init_types 尾部调用」的牙;若该调用缺失/被挪走 ⇒ 本条转红)。 + iface_shape_reset(); + init_types(); // 干净类型表(9 原生行)+ 生产形状注册面 + fails = fails + ts_check("ct2.shape_face_from_init_types", + (iface_shape_count() == 6 && + iface_shape_lookup(str_intern("sequence")) == sh_shape_seq() && + iface_shape_lookup(str_intern("product")) == sh_shape_product()), 1); + g_iface_count = 0; // 干净接口表(上游 ts_ifc_run 夹具含不可建面——不清零即拒装填) + g_impl_for_count = 0; + g_method_count = 0; + iface_shape_reset(); + iface_shape_builtin_init(); // 生产注册面(6 名)——populate 会重跑(幂等) + // 夹具:命名行 + null/可选行(判定面显式钉)+ 1 接口(self 接收者 + 命名型形参) + // + impl 边 + 方法表(生产写点同形:parser 的 interface/impl 分支) + ts_ifc_mk_named("Ct2Named"); + ct2_null := alloc_type(TYP_NULL, 0, 0); + ct2_opt := alloc_type(TYP_OPTIONAL, TI_INT, 0); + ct2_n := ts_ifc_named_node("Ct2Named"); + ct2_int_n := ts_ifc_base_node(TY_INT); + ct2_ii := ts_t5_mk_iface("Ct2Iface"); + ts_ifc_add_method_n(ct2_ii, "a", 1, 1, -1, -1, -1, -1, ct2_int_n); // fn a(self) -> int + ts_ifc_add_method_n(ct2_ii, "b", 1, 0, ct2_n, -1, -1, -1, ct2_int_n); // fn b(x: Ct2Named) -> int + grow_impl_for(1); + w64(g_impl_for, 0, str_intern("Ct2Iface")); + w64(g_impl_for, 8, str_intern("Ct2Named")); + g_impl_for_count = 1; + grow_methods(1); + w64(g_methods, 0, str_intern("Ct2Named")); + w64(g_methods, 8, str_intern("b")); + w64(g_methods, 16, str_intern("Ct2Named.b")); + g_method_count = 1; + prep := ccr_seg_prepare_save(); + fails = fails + ts_check("ct2.seg_prepare", prep, 0); + if prep == 0 { + fails = fails + ts_check("ct2.iface_seg_walk", ts_ccr2_seg_walk_ok(), 1); + fails = fails + ts_check("ct2.iface_sig_slots", ts_ccr2_sig_slots_ok(), 1); + fails = fails + ts_check("ct2.shape_names_production", ts_ccr2_shape_names_ok(), 1); + } else { + fails = fails + 3; // 后续三例无法运行 ⇒ 计失败(不得静默少算) + } + fails = fails + ts_check("ct2.entry_table_16_named", ts_ccr2_entries_ok(), 1); + fails = fails + ts_check("ct2.kind_of_no_new_mapping", + (ts_ccr2_kind_domain_ok() == 1 && iface_kind_of(ct2_null) == -1 && iface_kind_of(ct2_opt) == -1), 1); + // ⑥ 不可建签名(dyn 位图行入签名:sh_sig_term_of_ti 对 TYP_DYN 回 -1)⇒ 装填拒绝 + // (save 拒落盘——三态纪律:不得把缺项签名静默写成 -1 落盘)——**最后**(污染接口表) + ct2_dyn_n := ts_ifc_base_node(TI_DYN); // parser 对 `dyn` 正产 type_val = TI_DYN + ts_ifc_add_method_n(ct2_ii, "c", 0, 0, -1, -1, -1, -1, ct2_dyn_n); + rej2 := ccr_iface_populate(); + fails = fails + ts_check("ct2.reject_unbuildable_sig", rej2, -1); + return fails; +} + +// ═══════════ R2 P6 Task 2(E-10):¬ 面同类原子身份判据的邻域覆盖(9 例)═══════════ +// 牙齿分工(计划 Task 2 Step 4 的四向 + 本批补钉): +// ① 同身份 ⇒ **仍相交**(防「一律判不交」的过度加强); +// ② μ 变元作**字面** ⇒ **仍 -1 + 未覆盖面**(走 lit_implies 既有域外路径,三态不得稀释); +// ③④ `⊤ₖ` 两方向 ⇒ **不变(0)**〔**语义钉子**:防将来扩域/重排把 `⊤ₖ` 误当身份原子 +// (`tt_top_k` 的 c 槽 = 0 非 -1,仅靠空链守卫拦不住);既有 `neg.supertype*` 走的是 +// **¬×¬** 分支,不覆盖本面 ⇒ 补此两钉〕; +// ⑤ 跨枚举**同名**变体 ⇒ 1(复刻 P3 Task 1 令牌碰撞类;身份含枚举名); +// ⑥ AK_UNIT 类**令牌字面**(非 AK_SUM)⇒ 维持 0(钉类域检查); +// ⑦ `AK_NAMED` ⇒ 维持 **-1 + 未覆盖面**(P0 终审 Important B 在册政策,本批不翻转); +// ⑧ 空链 / 链元素域外(手造畸形项)⇒ **P1-a:0**(裁定口径:域外逐位不变); +// ⑨ 参数化构造子面(D4)⇒ 维持 0:`[int]` vs ¬`[int ∪ string]` 双向 —— 类域守卫的**稳定牙齿** +// (无守卫时元素项不同 ⇒ te_chain_cmp 回 0 ⇒ 误判 1 = **假不交**)。 +// 夹具 = 纯合成(`str_intern` + `sh_name_token` + `tt_atom/tt_cons/tt_nil/tt_union`)⇒ 不依赖 +// g_types / `sh_term_of_ti` / `init_types()`(与调用序列中其它段解耦)。 +// 用例数 = **11**(调用方 `total = total + 11` 须同步——增删用例两处一起改)。 +fn ts_t2_neg_run() -> int { + fails : ., mut = 0; + e1 := str_intern("T2NegE1"); + v1 := str_intern("T2NegV1"); + v2 := str_intern("T2NegV2"); + e2 := str_intern("T2NegE2"); + sum_11 := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(sh_name_token(v1), tt_nil()))); + sum_21 := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e2), tt_cons(sh_name_token(v1), tt_nil()))); + i_atom := tt_atom(AK_INT, TI_INT, -1); + s_atom := tt_atom(AK_STRING, TI_STR, -1); + // ① 同身份(同枚举同变体,两处构造 ⇒ DAG 去重同节点)⇒ lp ⊆ ¬x 不成立 ⇒ 0 + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_same_identity_not_disjoint", + ts_isat_b2i(ty_sub(sum_11, tt_not(sum_11)) == 0 && ty_uncovered() == 0), 1); + // ② μ 变元作字面(无原子类)⇒ -1 + 未覆盖面(既有域外路径,不受本批改动影响) + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_var_literal_stays_minus1", + ts_isat_b2i(ty_sub(tt_var(0), tt_not(sum_11)) == -1 && ty_uncovered() == 1), 1); + // ③ ⊤ₖ 在左:类顶 ⊆ ¬原子 ⇒ 假(类顶含该类全部值)⇒ 0 + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_topk_left_unchanged", + ts_isat_b2i(ty_sub(tt_top_k(AK_SUM), tt_not(sum_11)) == 0 && ty_uncovered() == 0), 1); + // ④ ⊤ₖ 在右:原子 ⊆ ¬类顶 ⇒ 假 ⇒ 0 + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_topk_right_unchanged", + ts_isat_b2i(ty_sub(sum_11, tt_not(tt_top_k(AK_SUM))) == 0 && ty_uncovered() == 0), 1); + // ⑤ 跨枚举同名变体:链首令牌(枚举名)不同 ⇒ 异身份 ⇒ 1;同项 ⇒ 0(不得混同) + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_cross_enum_same_name", + ts_isat_b2i(ty_sub(sum_21, tt_not(sum_11)) == 1 && ty_sub(sum_11, tt_not(sum_21)) == 1 && + ty_sub(sum_11, tt_not(sum_11)) == 0 && ty_uncovered() == 0), 1); + // ⑩ **同枚举异变体**(本批暴露首版实现缺陷的那条):AK_SUM 槽 1 = **变体令牌**,而 + // `te_chain_cmp` 的槽 ≥1 走**实参位规则**(`te_arg_cmp → ty_equiv`)⇒ 两个不同令牌 + // 结构判等(b 槽只是标注)⇒ 误判「同链」。本判据走**逐元素令牌同一性** ⇒ 1。 + // (三例 `t3.*_over_claim` 与该例同族:同枚举 T3Color 的 T3Blue vs T3Red。) + sum_12b := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(sh_name_token(v2), tt_nil()))); + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_same_enum_diff_variant", + ts_isat_b2i(ty_sub(sum_12b, tt_not(sum_11)) == 1 && ty_sub(sum_11, tt_not(sum_12b)) == 1 && + ty_sub(sum_11, tt_not(sum_11)) == 0 && ty_uncovered() == 0), 1); + // ⑥ AK_UNIT 类令牌字面(非 AK_SUM)⇒ 类域外 ⇒ 维持 0 + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_token_literal_not_identity_face", + ts_isat_b2i(ty_sub(sh_name_token(v1), tt_not(sh_name_token(v2))) == 0 && ty_uncovered() == 0), 1); + // ⑦ AK_NAMED ⇒ 维持 -1 + 未覆盖面(P0 Important B / named.not_disjoint 在册;本批不动) + ty_budget_reset(200000); + named_nc := tt_atom(AK_NAMED, 7, -1); + fails = fails + ts_check("t2.neg_named_stays_minus1", + ts_isat_b2i(ty_sub(named_nc, tt_not(sum_11)) == -1 && ty_uncovered() == 1), 1); + // ⑧ 身份链不可判的手造畸形项 ⇒ 维持 0(P1-a 钉;两条断言分别钉两个守卫: + // 空链(无身份)与链元素域外(非令牌)——无守卫时都会落成 d == 0 ⇒ 误判「异身份」⇒ 1) + ty_budget_reset(200000); + mu_chain := tt_atom(AK_SUM, -1, tt_cons(sh_name_token(e1), tt_cons(tt_var(0), tt_nil()))); + empty_chain := tt_atom(AK_SUM, -1, -1); + fails = fails + ts_check("t2.neg_chain_elem_out_of_domain_pin", + ts_isat_b2i(ty_sub(empty_chain, tt_not(sum_11)) == 0 && + ty_sub(mu_chain, tt_not(sum_11)) == 0 && ty_uncovered() == 0), 1); + // ⑪ AK_REF 的 **mut 标记位是令牌**(`sh_ref_mut_marker`)⇒ 链形与 AK_SUM 同族(元素皆令牌) + // ⇒ **类域守卫的稳定牙齿**:无守卫时 `&int` vs `&mut int` 在槽 0 令牌异节点 ⇒ 误判 + // 「异身份」⇒ 1 = **假不交**(D4 的 REF 面;语义上两者值集关系未建模,保守回 0)。 + ref_r := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(i_atom, tt_nil()))); + ref_m := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(1), tt_cons(i_atom, tt_nil()))); + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_ref_mut_marker_face_stays_zero", + ts_isat_b2i(ty_sub(ref_r, tt_not(ref_m)) == 0 && ty_sub(ref_m, tt_not(ref_r)) == 0 && + ty_uncovered() == 0), 1); + // ⑨ 参数化构造子(D4):`[int]` vs ¬`[int ∪ string]` 双向 ⇒ 0 —— 类域守卫的稳定牙齿 + // (无守卫时元素项不同 ⇒ te_chain_cmp 回 0 ⇒ 误判 1 = 假不交;语义上 `[int] ⊆ [int∪string]`) + seq_i := tt_atom(AK_SEQUENCE, -1, tt_cons(i_atom, tt_nil())); + seq_u := tt_atom(AK_SEQUENCE, -1, tt_cons(tt_union(i_atom, s_atom), tt_nil())); + ty_budget_reset(200000); + fails = fails + ts_check("t2.neg_param_face_stays_zero", + ts_isat_b2i(ty_sub(seq_i, tt_not(seq_u)) == 0 && ty_sub(seq_u, tt_not(seq_i)) == 0 && + ty_uncovered() == 0), 1); + return fails; +} + +fn type_selftest_run() -> int { + fails : ., mut = 0; + total : ., mut = 0; + + // --- P0 最小用例 --- + a_int := tt_atom(AK_INT, TI_INT, -1); + a_str := tt_atom(AK_STRING, TI_STR, -1); + a_bool := tt_atom(AK_BOOL, TI_BOOL, -1); + + total = total + 1; fails = fails + ts_check("unit.bot_sub_int", ty_sub(tt_bot(), a_int), 1); + total = total + 1; fails = fails + ts_check("unit.int_sub_str", ty_sub(a_int, a_str), 0); + total = total + 1; fails = fails + ts_check("unit.int_sub_int", ty_sub(a_int, a_int), 1); + total = total + 1; fails = fails + ts_check("unit.dedup", (tt_union(a_int, a_str) == tt_union(a_int, a_str)), 1); + total = total + 1; fails = fails + ts_check("unit.notnot", (tt_not(tt_not(a_int)) == a_int), 1); + + // --- 规范化(Task 2):De Morgan / 否定下推 / 分配律 / 幂等 --- + // 探针:(int ∪ string) ∩ bool —— 分配律必须把它展成 union(两层分配) + probe := tt_inter(tt_union(a_int, a_str), a_bool); + total = total + 1; fails = fails + ts_check("norm.demorgan", + (tt_norm(tt_not(tt_union(a_int, a_str))) == tt_norm(tt_inter(tt_not(a_int), tt_not(a_str)))), 1); + total = total + 1; fails = fails + ts_check("norm.nnf_neg_push", + tt_tag(tt_norm(tt_not(tt_union(a_int, a_str)))), TT_INTER); + total = total + 1; fails = fails + ts_check("norm.dnf_shape", tt_is_dnf(tt_norm(probe)), 1); + total = total + 1; fails = fails + ts_check("norm.distributed", tt_tag(tt_norm(probe)), TT_UNION); + total = total + 1; fails = fails + ts_check("norm.idempotent", + (tt_norm(tt_norm(probe)) == tt_norm(probe)), 1); + + // --- 判定(Task 3):子类型 / 等价 / 不相交 / 可空 / ⊤ₖ / 预算三态 --- + u_is := tt_union(a_int, a_str); + total = total + 1; fails = fails + ts_check("sub.union_right", ty_sub(a_int, u_is), 1); + total = total + 1; fails = fails + ts_check("sub.union_left_neg", ty_sub(u_is, a_int), 0); + total = total + 1; fails = fails + ts_check("sub.inter", ty_sub(tt_inter(a_int, a_str), a_int), 1); + // 非平凡吸收:int ∪ (str ∪ int) ≡ int ∪ str(需引擎判定,非构造律) + total = total + 1; fails = fails + ts_check("equiv.absorb", + ty_equiv(tt_union(a_int, tt_union(a_str, a_int)), tt_union(a_int, a_str)), 1); + total = total + 1; fails = fails + ts_check("disjoint.atoms", ty_disjoint(a_int, a_str), 1); + total = total + 1; fails = fails + ts_check("disjoint.same", ty_disjoint(a_int, a_int), 0); + total = total + 1; fails = fails + ts_check("inh.atom", ty_inhabited(a_int), 1); + total = total + 1; fails = fails + ts_check("inh.contra", ty_inhabited(tt_inter(a_int, tt_not(a_int))), 0); + total = total + 1; fails = fails + ts_check("inh.mixed_atoms", + ty_inhabited(tt_inter(a_int, tt_atom(AK_SEQUENCE, -1, -1))), 0); + total = total + 1; fails = fails + ts_check("topk.sub", ty_sub(a_int, tt_top_k(AK_INT)), 1); + total = total + 1; fails = fails + ts_check("topk.neg", ty_sub(a_str, tt_top_k(AK_INT)), 0); + // 预算守卫:深递归项 + 极小预算 → 三态 -1(禁止与「不成立」混淆) + ty_budget_reset(64); + total = total + 1; fails = fails + ts_check("budget.unknown", ty_sub(tt_probe_wide_union(300), a_int), -1); + total = total + 1; fails = fails + ts_check("budget.flag", ty_exhausted(), 1); + ty_budget_reset(200000); + + // --- 反例(witness)+ 穷尽性(Task 4)--- + w := tt_witness(a_int, a_str); + total = total + 1; fails = fails + ts_check("witness.nonempty", (w >= 0), 1); + total = total + 1; fails = fails + ts_check("witness.sub_of_a", ty_sub(w, a_int), 1); + total = total + 1; fails = fails + ts_check("witness.not_sub_of_b", ty_sub(w, a_str), 0); + total = total + 1; fails = fails + ts_check("witness.empty", tt_witness(a_int, a_int), -1); + + dom := tt_union(tt_union(a_int, a_str), a_bool); + pats := tt_cons(a_int, tt_cons(a_str, tt_nil())); + total = total + 1; fails = fails + ts_check("exhaust.incomplete", ty_exhaustive(dom, pats), 0); + miss := ty_exhaust_witness(dom, pats); + total = total + 1; fails = fails + ts_check("exhaust.witness_bool", ty_equiv(miss, a_bool), 1); + total = total + 1; fails = fails + ts_check("exhaust.complete", + ty_exhaustive(dom, tt_cons(a_int, tt_cons(a_str, tt_cons(a_bool, tt_nil())))), 1); + + // --- 递归 + 参数化(Task 4)--- + rec_abs := tt_mu(0, tt_union(a_int, tt_var(0))); + total = total + 1; fails = fails + ts_check("rec.absorb", ty_equiv(rec_abs, a_int), 1); + rec_list := tt_mu(0, tt_union(a_int, tt_atom(AK_PRODUCT, -1, tt_cons(tt_var(0), tt_nil())))); + total = total + 1; fails = fails + ts_check("rec.inh", ty_inhabited(rec_list), 1); + total = total + 1; fails = fails + ts_check("rec.sub_self", ty_sub(rec_list, rec_list), 1); + seq_i := tt_atom(AK_SEQUENCE, -1, tt_cons(a_int, tt_nil())); + seq_s := tt_atom(AK_SEQUENCE, -1, tt_cons(a_str, tt_nil())); + total = total + 1; fails = fails + ts_check("param.same", ty_sub(seq_i, seq_i), 1); + // **R2 P5 Task 3b 翻转钉**(原 `param.unknown_p0`,期望 -1):不变槽元素三态落地后 + // 同类异参(`[int]` vs `[str]`,元素 = 异原生类)由「未知 -1」变「确定不同 **0**」。 + // 语义正确性:不变元素槽下 `[int] ⊄ [str]`(元素类不同 ⇒ 确定);与 spec §2.2 的 + // `sequence<⊤>` 正向面**无冲突**——后者由 `tt_top_k(AK_SEQUENCE)` 的 TOP_K 分支判 + // (`[T] <: sequence<⊤>` 恒 1,与元素比较无关;守门例见 x2.shape_* 段)。 + // 对齐面:legacy `type_equal([int],[str]) = false` = 0(替换门同结论,影子 old_* = 0)。 + total = total + 1; fails = fails + ts_check("param.elem_class_diff_zero", ty_sub(seq_i, seq_s), 0); + // 未覆盖面位断言前**显式重置**(全局粘滞位,前面案例碰过 ¬μ 会误过——修复复审 N5); + // T3b 后本触发器改用**仍属域外**的面(μ 变元作序列元素 ⇒ te_elem_cmp 域外守卫) + ty_budget_reset(200000); + seq_mu := tt_atom(AK_SEQUENCE, -1, tt_cons(tt_var(0), tt_nil())); + d_dummy := ty_sub(seq_i, seq_mu); // 重置后重新触发一次未覆盖面(域外元素) + total = total + 1; fails = fails + ts_check("param.uncovered_flag", ty_uncovered(), 1); + + // --- P0 终审补例(三个 Critical + 两条 Important 各配一例回归)--- + // C1:否定在超类型侧 —— ¬A ⊆ ¬B ⟺ B ⊆ A(方向反会让两侧同时成立) + total = total + 1; fails = fails + ts_check("neg.supertype", + ty_sub(tt_not(seq_i), tt_not(tt_top_k(AK_SEQUENCE))), 0); + total = total + 1; fails = fails + ts_check("neg.supertype_rev", + ty_sub(tt_not(tt_top_k(AK_SEQUENCE)), tt_not(seq_i)), 1); + // C3:预算耗尽不得被吞成 0(三态必须上抛)——**预算窗口扫描**守「exhausted ⇒ -1」 + // 不变量(单点预算 1 会被规范化预算满足,守不住 lit_implies 吞并面——修复复审 N2) + ok_sw : ., mut = 1; + bi : ., mut = 1; + loop { + if bi > 12 { break; } + ty_budget_reset(bi); + r_sw := ty_sub(tt_not(seq_i), tt_not(tt_top_k(AK_SEQUENCE))); + if ty_exhausted() == 1 && r_sw != -1 { ok_sw = 0; } + bi = bi + 1; + } + ty_budget_reset(200000); + total = total + 1; fails = fails + ts_check("budget.no_swallow_sweep", ok_sw, 1); + // witness 的三态:-1 = 不可满足 / -2 = 未知(预算耗尽),ty_exhaustive 对 -2 → -1 + // (dom/pats 在此处重新构造:本块位于「反例+穷尽性」节之前) + dom_u := tt_union(tt_union(a_int, a_str), a_bool); + pats_u := tt_cons(a_int, tt_cons(a_str, tt_nil())); + ty_budget_reset(1); + total = total + 1; fails = fails + ts_check("witness.unknown_budget", tt_witness(a_int, a_str), -2); + total = total + 1; fails = fails + ts_check("exhaust.unknown_budget", ty_exhaustive(dom_u, pats_u), -1); + ty_budget_reset(200000); + // C2:memo 表扩容不挂(1200 互异对逼出装填因子守卫 + 重建重放;到得了下一行 = 未死循环) + d2 : ., mut = 0; + last := 0; + loop { + if d2 >= 1200 { break; } + last = ty_sub(tt_mu(d2, a_int), a_int); + d2 = d2 + 1; + } + total = total + 1; fails = fails + ts_check("memo.overflow_survived", last, 1); + // A:n 元析取(≥3)是合法 DNF(左深嵌套 union) + total = total + 1; fails = fails + ts_check("dnf.n_ary", + tt_is_dnf(tt_norm(tt_inter(tt_union(a_int, a_str), tt_union(a_bool, a_int)))), 1); + // B:命名原子不展开 → 未知;且不得断言与它互斥 + named_t := tt_atom(AK_NAMED, 7, -1); + a_prod := tt_atom(AK_PRODUCT, -1, -1); + total = total + 1; fails = fails + ts_check("named.unknown", ty_sub(named_t, a_prod), -1); + total = total + 1; fails = fails + ts_check("named.not_disjoint", ty_disjoint(named_t, a_prod), 0); + // D:¬μ 不静默改写为 μ¬(保留为字面 + 未覆盖面) + total = total + 1; fails = fails + ts_check("neg.mu_kept", + tt_tag(tt_norm(tt_not(tt_mu(0, a_int)))), TT_NOT); + + // --- 索引扩容守门(Task 1 遗留:判据规模 << 初始容量 → 扩容/重建路径无常规覆盖)--- + ref := tt_atom(AK_SEQUENCE, -1, tt_cons(a_int, tt_nil())); + before := tt_count(); + g_tt_index_cap = 0; // 直接逼出 grow_tt_index → 重建索引(tt_reindex)+ 重放插入 + grow_tt_index(2); + after := tt_atom(AK_SEQUENCE, -1, tt_cons(a_int, tt_nil())); + total = total + 1; fails = fails + ts_check("grow.index_rebuilt", (after == ref), 1); + d : ., mut = 0; + loop { + if d >= 40 { break; } + item := tt_atom(AK_PRODUCT, -1, tt_cons(tt_mu(d, a_int), tt_nil())); + d = d + 1; + } + // 40 个互异项建两遍:第二遍必须**全部命中**已有项(DAG 去重幂等)——精确断言 + // (早期版本用 `>= before+40`,既弱又不精确;P0 终审 Minor 指出) + c_after_first := tt_count(); + d4 : ., mut = 0; + loop { + if d4 >= 40 { break; } + item2 := tt_atom(AK_PRODUCT, -1, tt_cons(tt_mu(d4, a_int), tt_nil())); + d4 = d4 + 1; + } + total = total + 1; fails = fails + ts_check("grow.dedup_40_idempotent", (tt_count() - c_after_first), 0); + total = total + 1; fails = fails + ts_check("grow.dedup_after_rebuild", + (tt_atom(AK_PRODUCT, -1, tt_cons(tt_mu(0, a_int), tt_nil())) == + tt_atom(AK_PRODUCT, -1, tt_cons(tt_mu(0, a_int), tt_nil()))), 1); + + // --- P1 桥接(checker ti → 引擎项,R2 P1 Task 1;映射表 = 计划 Task 1 表)--- + // 前置:本通道不经 check_all(),g_types 未初始化 → 显式 init_types()(原生 9 项 + // 占下标 0..8,用户类型自 9 起——桥接用例的 alloc_type 依赖该布局)。 + // 预算:前段用例累积 g_ty_steps(仅显式 ty_budget_reset 归零),重置到干净窗口 + // (P0 修复复审 N5 同款卫生)。 + init_types(); + ty_budget_reset(200000); + t_int := sh_term_of_ti(TI_INT); + total = total + 1; fails = fails + ts_check("bridge.int_atom", tt_tag(t_int), TT_ATOM); + total = total + 1; fails = fails + ts_check("bridge.int_ak", tt_a(t_int), AK_INT); + t_arr := sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 3)); + total = total + 1; fails = fails + ts_check("bridge.arr_seq", tt_a(t_arr), AK_SEQUENCE); + t_arr3 := sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 4)); + total = total + 1; fails = fails + ts_check("bridge.len_not_identity", + ty_equiv(t_arr, t_arr3), 1); // N 不入身份(R1 裁决)——3 与 4 等价 + total = total + 1; fails = fails + ts_check("bridge.cache_hit", (sh_term_of_ti(TI_INT) == t_int), 1); + // 原生序守门:checker 下标序 ≠ 引擎 AK 序——TI_BOOL=2/TI_STR=3 vs AK_STRING=2/AK_BOOL=3 + // (计划注释「AK_* 与 TI_* 前 7 项 1:1」**不成立**,按下标直通会 bool↔string 静默错标) + total = total + 1; fails = fails + ts_check("bridge.str_ak", tt_a(sh_term_of_ti(TI_STR)), AK_STRING); + total = total + 1; fails = fails + ts_check("bridge.bool_ak", tt_a(sh_term_of_ti(TI_BOOL)), AK_BOOL); + total = total + 1; fails = fails + ts_check("bridge.dyn_ak", tt_a(sh_term_of_ti(TI_DYN)), AK_DYN); + // M3(Task 1 评审):native 分派 = 读表本体(kind == TYP_BASE 且 data == TY_*)——**全表覆盖** + // (M1 后它成为主路径,此前只有 row 8 可达且无用例)。行 0..7 逐行对照,不抽样: + // 任一行错位(含 bool↔str 互换回归)即红。 + // ⚠️ **row 7 陷阱**:TI_DYN 行 kind = TYP_DYN 而 **data = 0(== TY_INT!)**——若实现先比 + // data 再判 kind,dyn 会静默译成 AK_INT。故 ① 断言 sh_native_ak(TI_DYN) == -1(dyn 必走 + // kind 分支,绝不进 native 路径),② 项本身仍 = AK_DYN。两断言合起来把「先 data 后 kind」 + // 的写法钉死在红。 + acc_nat : ., mut = 1; + ri : ., mut = 0; + loop { + if ri >= 8 { break; } + want_ak : ., mut = -1; + if ri == TI_INT { want_ak = AK_INT; } + else if ri == TI_DEX { want_ak = AK_DEX; } + else if ri == TI_BOOL { want_ak = AK_BOOL; } + else if ri == TI_STR { want_ak = AK_STRING; } + else if ri == TI_UNIT { want_ak = AK_UNIT; } + else if ri == TI_NEVER { want_ak = AK_NEVER; } + else if ri == TI_CHAR { want_ak = AK_CHAR; } + got_ak := sh_native_ak(ri); + if ri == TI_DYN { + if got_ak != -1 { acc_nat = 0; } // dyn:kind 分支,native 必 -1 + } else if got_ak != want_ak { acc_nat = 0; } + ri = ri + 1; + } + total = total + 1; fails = fails + ts_check("bridge.native_row_table", acc_nat, 1); + total = total + 1; fails = fails + ts_check("bridge.native_dyn_kind_branch", + (sh_native_ak(TI_DYN) == -1 && tt_a(sh_term_of_ti(TI_DYN)) == AK_DYN), 1); + // TY_DEX_S 占位行(row 8):M1 后走 native 路径(旧实现走通用路径),两侧同 = AK_DEX + total = total + 1; fails = fails + ts_check("bridge.native_dex_s", + (sh_native_ak(TI_DEX_S) == AK_DEX && tt_a(sh_term_of_ti(TI_DEX_S)) == AK_DEX), 1); + // 参数链(内层项)+ 两份同类分支不得互串(PTR/REF 是两条独立分支) + t_ptr := sh_term_of_ti(alloc_type(TYP_PTR, TI_INT, 0)); + // M2(Task 1 评审):class 断言与参数链**并列**——原用例只断参数链,整支误译成 + // SEQUENCE/REF 也会绿(对照 ref_inner 已断 class) + total = total + 1; fails = fails + ts_check("bridge.ptr_ak", tt_a(t_ptr), AK_PTR); + total = total + 1; fails = fails + ts_check("bridge.ptr_inner", + tt_c(t_ptr), tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil())); + // P3 Task 1(断言更新,用例数不变):AK_REF 链 = [mut 标记项, 元素项](mut 入链 = 判定 + // 维度)⇒ 本用例守的「REF 链含元素项」改断链**第二项**(标记项由 t1.var_mut_* 单列)。 + // 旧断言(链首即元素)随 mut 入链的布局裁决更新——原因见 ty_shadow.cr 的 sh_ref_mut_marker。 + t_ref := sh_term_of_ti(alloc_type(TYP_REF, TI_STR, 0)); + total = total + 1; fails = fails + ts_check("bridge.ref_inner", + (tt_a(t_ref) == AK_REF && tt_b(tt_c(t_ref)) == tt_cons(tt_atom(AK_STRING, TI_STR, -1), tt_nil())), 1); + t_slice := sh_term_of_ti(alloc_type(TYP_SLICE, TI_STR, 0)); + total = total + 1; fails = fails + ts_check("bridge.slice_seq", + (tt_a(t_slice) == AK_SEQUENCE && tt_c(t_slice) == tt_cons(tt_atom(AK_STRING, TI_STR, -1), tt_nil())), 1); + + // --- R2 P5 Task 2(D19/D21/D22/D23):DFNode 类型面**单槽化** --- + // 形态 = 两槽(OFF_DF_TK 项引用 + OFF_DF_AUX 辅码)+ 派生码(sh_dfn_code_of_slots); + // P4 的 `t4.tk_*` 用例保留(语义未变者原地;清单扩大/复合行走辅码者重钉)。 + // 负控的牙齿 = 每个「本该 -1」的用例都同时断言**直译路径非 -1**(证明拒绝出自 + // 分类门而非「该行不可译」——否则门形同虚设也能绿)。 + total = total + 1; fails = fails + ts_check("t4.tk_const_int", + (sh_tk_term_of_code(IR_CONST, TI_INT) == sh_term_of_ti(TI_INT) && + tt_a(sh_tk_term_of_code(IR_CONST, TI_INT)) == AK_INT), 1); + total = total + 1; fails = fails + ts_check("t4.tk_binary_dex", + (sh_tk_term_of_code(IR_BINARY, TI_DEX) == sh_term_of_ti(TI_DEX) && + tt_a(sh_tk_term_of_code(IR_BINARY, TI_DEX)) == AK_DEX), 1); + total = total + 1; fails = fails + ts_check("t4.tk_const_dex_s_row8", + (sh_tk_term_of_code(IR_CONST, TI_DEX_S) == sh_term_of_ti(TI_DEX_S) && + tt_a(sh_tk_term_of_code(IR_CONST, TI_DEX_S)) == AK_DEX), 1); + // 负控 ①:IR_BOUNDS_CHECK 的 tk 是旗标(1 = 动态上限),而 1 数值上 = TI_DEX 行 + // ——直译有值 ⇒ 只能靠分类门挡住(凭空安 dex 项 = 本用例钉死的形态)。 + total = total + 1; fails = fails + ts_check("t4.tk_bounds_flag1_rejected", + (sh_tk_term_of_code(IR_BOUNDS_CHECK, 1) == -1 && sh_term_of_ti(1) >= 0), 1); + // 负控 ②:IR_DEREF 的 tk 是访问宽度(8B),8 数值上 = TI_DEX_S 行(同款陷阱)。 + total = total + 1; fails = fails + ts_check("t4.tk_deref_width8_rejected", + (sh_tk_term_of_code(IR_DEREF, 8) == -1 && sh_term_of_ti(TI_DEX_S) >= 0), 1); + // 负控 ③(重钉:P5 T2 起 IR_CALL/IR_ALLOC 入类型行面 ⇒ 换成「面外」op): + // 「行合法(0 = TI_INT)但 op 不在任何类型/辅码面」——面外 op 不得因 tk 恰好合法而放行。 + total = total + 1; fails = fails + ts_check("t4.tk_unlisted_op_valid_row_rejected", + (sh_tk_term_of_code(IR_STORE, TI_INT) == -1 && + sh_tk_term_of_code(IR_RETURN, TI_INT) == -1 && + sh_tk_term_of_code(IR_MAKE_ENUM, TI_INT) == -1 && + sh_term_of_ti(TI_INT) >= 0), 1); + // 负控 ④:行号越界 / 负值 ⇒ -1(不得当 0/1 用;不得读越界项表)。 + total = total + 1; fails = fails + ts_check("t4.tk_row_range_rejected", + (sh_tk_term_of_code(IR_CONST, g_type_count) == -1 && + sh_tk_term_of_code(IR_BINARY, g_type_count + 7) == -1 && + sh_tk_term_of_code(IR_CONST, -1) == -1), 1); + // 查询纯度(P5 T2 引入 / **P5 T5 重钉**:原断言 = 「本函数不扰动影子对账的 hits 计数」, + // 影子通道下线后该计数已不存在 ⇒ 改钉**查询纯度**这一同源性质):`sh_tk_term_of_code` + // 是**纯查询**——不写槽出参、不置 D23 失败位(诊断调用不得污染落盘闸;有副作用的生产 + // 路径一律走 sh_tk_split);且对同一行返回**同一项**(缓存语义)。 + // 牙齿:必须取**非原生行**(原生行走 sh_native_ak 快路径,本就不进缓存 ⇒ 拿 TI_INT 测 + // 等于恒真断言)。TYP_DYN 行首次直译建项入缓存、二次直译命中同项。 + t4_dyn_row := alloc_type(TYP_DYN, 0, 0); + t4_dyn_term := sh_term_of_ti(t4_dyn_row); // 预热:首次(miss)入桥接缓存 + t4_fail_0 := g_tk_face_fail; + sh_tk_split(IR_CONST, g_type_count); // 槽出参置哨兵(越界行 ⇒ term=-1/aux=越界行号) + t4_sent_t := g_sh_slot_term; + t4_sent_a := g_sh_slot_aux; + t4_fail_before := g_tk_face_fail; + sk4 := sh_tk_term_of_code(IR_CONST, t4_dyn_row); // 查询路径(缓存命中) + total = total + 1; fails = fails + ts_check("t4.tk_query_purity_and_cache_hit", + ((t4_dyn_term >= 0 && sk4 == t4_dyn_term && + g_sh_slot_term == t4_sent_t && g_sh_slot_aux == t4_sent_a && + g_tk_face_fail == t4_fail_before)), 1); + g_tk_face_fail = t4_fail_0; // 复位(哨兵行的 D23 位 = 本用例产物,不留给后续) + + // ── R2 P5 Task 2 新例 ①:原子行契约(D21)── + // TT_ATOM ∧ b ≥ 0 ⇒ b;union / cons / nil / 负 / 越界 ⇒ **-1**(显式,不得近似)。 + t5s_ptr_row := alloc_type(TYP_PTR, TI_INT, 0); + t5s_ptr_term := sh_term_of_ti(t5s_ptr_row); + total = total + 1; fails = fails + ts_check("p5t2.atom_of_contract", + (tt_atom_of_term(sh_term_of_ti(TI_INT)) == TI_INT && + tt_atom_of_term(t5s_ptr_term) == -1 && // 复合项:AK_PTR 的 b = -1 + tt_atom_of_term(tt_nil()) == -1 && + tt_atom_of_term(tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil())) == -1 && + tt_atom_of_term(-1) == -1 && + tt_atom_of_term(tt_count() + 9) == -1), 1); + + // ── 新例 ②:类型行面 —— 改走 split,断言**两槽**(项 + 空辅码)+ 互斥 ── + sh_tk_split(IR_BINARY, TI_STR); + t5s_bin_str_term := g_sh_slot_term; + t5s_bin_str_aux := g_sh_slot_aux; + total = total + 1; fails = fails + ts_check("p5t2.slot_type_face_atomic", + (t5s_bin_str_term == sh_term_of_ti(TI_STR) && t5s_bin_str_aux == 0 && + sh_dfn_code_of_slots(t5s_bin_str_term, t5s_bin_str_aux) == TI_STR), 1); + + // ── 新例 ③:类型行面扩面(Task 0 再裁的 5 个 op 正控)── + sh_tk_split(IR_ALLOC, TI_UNIT); t5s_a1 := g_sh_slot_term; t5s_a1x := g_sh_slot_aux; + sh_tk_split(IR_CALL, TI_INT); t5s_a2 := g_sh_slot_term; t5s_a2x := g_sh_slot_aux; + sh_tk_split(IR_LOAD, TI_INT); t5s_a3 := g_sh_slot_term; t5s_a3x := g_sh_slot_aux; + sh_tk_split(IR_I2F, TI_DEX); t5s_a4 := g_sh_slot_term; t5s_a4x := g_sh_slot_aux; + sh_tk_split(IR_F2I, TI_DEX); t5s_a5 := g_sh_slot_term; t5s_a5x := g_sh_slot_aux; + total = total + 1; fails = fails + ts_check("p5t2.slot_type_face_extended", + (t5s_a1 == sh_term_of_ti(TI_UNIT) && t5s_a1x == 0 && tt_a(t5s_a1) == AK_UNIT && + t5s_a2 == sh_term_of_ti(TI_INT) && t5s_a2x == 0 && + t5s_a3 == sh_term_of_ti(TI_INT) && t5s_a3x == 0 && + t5s_a4 == sh_term_of_ti(TI_DEX) && t5s_a4x == 0 && tt_a(t5s_a4) == AK_DEX && + t5s_a5 == sh_term_of_ti(TI_DEX) && t5s_a5x == 0 && tt_a(t5s_a5) == AK_DEX), 1); + + // ── 新例 ④:复合行 ⇒ 走辅码(F2 裁决;码保真,派生码 ≡ 旧混用码)── + // 牙齿:先证该行**可译**(sh_term_of_ti ≥ 0)——否则「term = -1」可能出自不可译, + // 门形同虚设也能绿。 + sh_tk_split(IR_BINARY, t5s_ptr_row); + total = total + 1; fails = fails + ts_check("p5t2.slot_composite_row_to_aux", + (t5s_ptr_term >= 0 && g_sh_slot_term == -1 && g_sh_slot_aux == t5s_ptr_row && + sh_dfn_code_of_slots(g_sh_slot_term, g_sh_slot_aux) == t5s_ptr_row), 1); + // 同款:数组行(AK_SEQUENCE 的 b = 长度或 -1 ⇒ 复合)。 + t5s_arr_row := alloc_type(TYP_ARRAY, TI_INT, 3); + sh_tk_split(IR_CONST, t5s_arr_row); + total = total + 1; fails = fails + ts_check("p5t2.slot_array_row_to_aux", + (sh_term_of_ti(t5s_arr_row) >= 0 && g_sh_slot_term == -1 && + g_sh_slot_aux == t5s_arr_row && + sh_dfn_code_of_slots(g_sh_slot_term, g_sh_slot_aux) == t5s_arr_row), 1); + + // ── 新例 ⑤:辅码面 —— 旗标 / 宽度 / 计数(含负码)原码保真 ── + sh_tk_split(IR_BOUNDS_CHECK, 1); t5s_f1t := g_sh_slot_term; t5s_f1a := g_sh_slot_aux; + sh_tk_split(IR_BOUNDS_CHECK, 0); t5s_f0t := g_sh_slot_term; t5s_f0a := g_sh_slot_aux; + sh_tk_split(IR_DEREF, 8); t5s_d8t := g_sh_slot_term; t5s_d8a := g_sh_slot_aux; + sh_tk_split(IR_STORE_PTR, 4); t5s_d4t := g_sh_slot_term; t5s_d4a := g_sh_slot_aux; + sh_tk_split(IR_SPAWN, -1); t5s_spt := g_sh_slot_term; t5s_spa := g_sh_slot_aux; + total = total + 1; fails = fails + ts_check("p5t2.slot_aux_face_codes", + (t5s_f1t == -1 && t5s_f1a == 1 && sh_dfn_code_of_slots(t5s_f1t, t5s_f1a) == 1 && + t5s_f0t == -1 && t5s_f0a == 0 && sh_dfn_code_of_slots(t5s_f0t, t5s_f0a) == 0 && + t5s_d8t == -1 && t5s_d8a == 8 && sh_dfn_code_of_slots(t5s_d8t, t5s_d8a) == 8 && + t5s_d4t == -1 && t5s_d4a == 4 && + t5s_spt == -1 && t5s_spa == -1 && sh_dfn_code_of_slots(t5s_spt, t5s_spa) == -1), 1); + // 数值陷阱复证:辅码 1 / 8 直译确有其项(TI_DEX / TI_DEX_S 行)——分类门才是拦阻者。 + total = total + 1; fails = fails + ts_check("p5t2.aux_trap_rows_translatable", + (sh_term_of_ti(1) >= 0 && tt_a(sh_term_of_ti(1)) == AK_DEX && + sh_term_of_ti(TI_DEX_S) >= 0 && tt_a(sh_term_of_ti(TI_DEX_S)) == AK_DEX && + sh_tk_term_of_code(IR_STORE_PTR, TI_DEX_S) == -1), 1); + + // ── 新例 ⑥:面外 op ⇒ 两槽皆空、派生码 0(含 LOAD_ENUM_TAG = Task 0 再裁「无」)── + sh_tk_split(IR_STORE, 0); t5s_n1t := g_sh_slot_term; t5s_n1a := g_sh_slot_aux; + sh_tk_split(IR_LOAD_ENUM_TAG, 0); t5s_n2t := g_sh_slot_term; t5s_n2a := g_sh_slot_aux; + sh_tk_split(IR_LABEL, 0); t5s_n3t := g_sh_slot_term; t5s_n3a := g_sh_slot_aux; + total = total + 1; fails = fails + ts_check("p5t2.slot_none_face_empty", + (t5s_n1t == -1 && t5s_n1a == 0 && sh_dfn_code_of_slots(t5s_n1t, t5s_n1a) == 0 && + t5s_n2t == -1 && t5s_n2a == 0 && t5s_n3t == -1 && t5s_n3a == 0), 1); + + // ── 新例 ⑦:派生码契约(辅码优先 / 无项⇒0 / 不可逆⇒-1 显式)── + total = total + 1; fails = fails + ts_check("p5t2.code_derivation_contract", + (sh_dfn_code_of_slots(sh_term_of_ti(TI_STR), 0) == TI_STR && + sh_dfn_code_of_slots(-1, 0) == 0 && + sh_dfn_code_of_slots(-1, 7) == 7 && + sh_dfn_code_of_slots(t5s_ptr_term, 0) == -1 && // 复合项入项槽 = 无法派生(显式响亮) + sh_dfn_code_of_slots(sh_term_of_ti(TI_INT), 9) == 9), 1); // 辅码优先(互斥由拆分器保证) + + // ── 新例 ⑧:互斥不变量(D22-②)逐 op 复核(面 0 / 面 1 / 面 2 各取代表)── + sh_tk_split(IR_CONST, TI_STR); t5s_m1 := (g_sh_slot_term >= 0 && g_sh_slot_aux == 0); + sh_tk_split(IR_DEREF, 8); t5s_m2 := (g_sh_slot_term == -1 && g_sh_slot_aux != 0); + sh_tk_split(IR_STORE, 0); t5s_m3 := (g_sh_slot_term == -1 && g_sh_slot_aux == 0); + t5s_m_ok := 1; + if t5s_m1 == 0 || t5s_m2 == 0 || t5s_m3 == 0 { t5s_m_ok = 0; } + total = total + 1; fails = fails + ts_check("p5t2.slot_mutual_exclusion", t5s_m_ok, 1); + + // ── 新例 ⑨:F1 修正(IR_HOTPATCH_ROUTE 的类型面 = 无;显式 0 实参 ⇒ 两槽皆空)── + sh_tk_split(IR_HOTPATCH_ROUTE, 0); + total = total + 1; fails = fails + ts_check("p5t2.hotpatch_route_no_face", + (g_sh_slot_term == -1 && g_sh_slot_aux == 0 && + sh_dfn_code_of_slots(g_sh_slot_term, g_sh_slot_aux) == 0), 1); + + // ── 新例 ⑩:D23 建项失败位(越界类型面行 ⇒ 计数 +1;复合行**不**置位)── + t5s_fail_before := g_tk_face_fail; + sh_tk_split(IR_CONST, g_type_count); + t5s_fail_after := g_tk_face_fail; + t5s_out_t := g_sh_slot_term; // 出参须**即时**取(下一次 split 会覆写) + t5s_out_a := g_sh_slot_aux; + sh_tk_split(IR_BINARY, t5s_ptr_row); // 复合行 = 域内可译 ⇒ 不得置位 + t5s_fail_final := g_tk_face_fail; + g_tk_face_fail = 0; // 复位(不污染后续落盘闸用例) + total = total + 1; fails = fails + ts_check("p5t2.d23_build_fail_bit", + (t5s_fail_after == t5s_fail_before + 1 && t5s_fail_final == t5s_fail_after && + t5s_out_t == -1 && t5s_out_a == g_type_count), 1); + // TYP_NAMED:AK_NAMED + 行号存 b 槽(引擎不展开 → 判定 UNKNOWN = P1 预期未覆盖面) + // ⚠️ 本行**刻意走裸分配**(P2a Task 1 后 8 个生产分配点已收敛到 alloc_named_type): + // 它构造的是人造 TYP_NAMED 行(键 1201 不对应任何真名),登记进生产侧表会用假键污染 + // name→ti 映射(自测将来若被并入编译路径即静默错型)。裸分配 = 不登记 = 不污染。 + named_ti := alloc_type(TYP_NAMED, 1201, 0); + t_named := sh_term_of_ti(named_ti); + total = total + 1; fails = fails + ts_check("bridge.named_ak_b", + (tt_a(t_named) == AK_NAMED && tt_b(t_named) == named_ti), 1); + // TYP_TUPLE 实读布局(checker.cr:118 type_equal 分支 + :2041+ 字段访问 t.N): + // data = 字段数,extra = 字段 ti 在 g_gen_apply_data 的起始下标(8B/元素) + grow_gen_apply_data(g_gen_apply_data_count + 2); + tup_start := g_gen_apply_data_count; + w64(g_gen_apply_data, tup_start * 8, TI_INT); + w64(g_gen_apply_data, (tup_start + 1) * 8, TI_STR); + g_gen_apply_data_count = g_gen_apply_data_count + 2; + t_tup := sh_term_of_ti(alloc_type(TYP_TUPLE, 2, tup_start)); + total = total + 1; fails = fails + ts_check("bridge.tuple_product", tt_a(t_tup), AK_PRODUCT); + total = total + 1; fails = fails + ts_check("bridge.tuple_fields", + tt_c(t_tup), tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_cons(tt_atom(AK_STRING, TI_STR, -1), tt_nil()))); + // 缓存语义:新 ti 恰入表 1 条(其元素 = 原生快路径,不入表);同 ti 二次调用 = 命中 + // (**P5 T5 重钉**:原断言 = hits 计数恰 +1;影子 hits 通道已随影子层下线 ⇒ 改钉 + // 「命中 ⇒ 同项 + 不新增条目」——同一语义的占用面证据,不再依赖计数器)。 + e_before := sh_map_entries(); + arr_ti2 := alloc_type(TYP_ARRAY, TI_STR, 9); + t_arr2 := sh_term_of_ti(arr_ti2); + total = total + 1; fails = fails + ts_check("bridge.entries_delta", (sh_map_entries() - e_before), 1); + e_hit_before := sh_map_entries(); + t_arr2b := sh_term_of_ti(arr_ti2); + total = total + 1; fails = fails + ts_check("bridge.cache_hit_same_term_no_new_entry", + (t_arr2b == t_arr2 && sh_map_entries() == e_hit_before), 1); + // 缓存扩容/重建守门(P0「扩容路径判据不可达」教训同款):上面仅 8 条 << 初始容量 + // 1024 → 装填因子守卫/重建路径**不可达**。用 600 个互异 ti(extra=N 各不同 = 互异 + // 类型表行,正是「N 不入身份」下 600 行同项的典型规模)逼出一次扩容重建(守卫 + // (entries+1)*2 >= cap 在 entries=511 时触发);再验两件事:①重放守恒(条目数恰 + // +600,不多不少)②重建**前**已入表的条目仍命中(重放把旧表条目全搬过去了)。 + g_before := sh_map_entries(); + gi : ., mut = 0; + loop { + if gi >= 600 { break; } + sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 1000 + gi)); + gi = gi + 1; + } + total = total + 1; fails = fails + ts_check("bridge.grow_rehash", + (sh_map_entries() == g_before + 600 && sh_term_of_ti(arr_ti2) == t_arr2), 1); + total = total + 1; fails = fails + ts_check("bridge.grow_cap_doubled", (g_term_map_cap >= 2048), 1); + // 第二次重建守门(Step 4b ②,Task 1 评审挂账:「>1024 条目第二次重建无实测」): + // 上一例只推过第一条守卫线(entries=511 → cap 1024→2048);本例再入 1100 条互异 ti + // 把条目推过第二条守卫线(entries=1023 → cap 2048→4096)——断三件事:①重放守恒(恰 + // +1100,不多不少)②cap ≥ 4096(**证明**第二次重建真的发生,不是「读码判安全」) + // ③首批(第一次重建前入表)条目仍命中(两次重放都把它搬过去了)。 + g2_before := sh_map_entries(); + gj : ., mut = 0; + loop { + if gj >= 1100 { break; } + sh_term_of_ti(alloc_type(TYP_ARRAY, TI_INT, 5000 + gj)); + gj = gj + 1; + } + total = total + 1; fails = fails + ts_check("bridge.grow_rehash2", + (sh_map_entries() == g2_before + 1100 && g_term_map_cap >= 4096 && sh_term_of_ti(arr_ti2) == t_arr2), 1); + + // --- F1(R2 P2a Task 1):同名 TYP_NAMED 归一行 —— P1 影子对拍 9/9 差异根因消除 --- + // 根因:同一类型名(struct 字面量 / 泛型应用基型在不同**出现点**)各建一行 → 桥接按行 + // 建原子(AK_NAMED 的 b 槽 = 行号)→ 引擎视两行为互异命名类型 → 判不了(9 条 unknown)。 + // 修法(用户裁决 = 根治):建表去重,唯一分配点收敛到 alloc_named_type。 + n1 := alloc_named_type(str_intern("DedupProbe")); + n2 := alloc_named_type(str_intern("DedupProbe")); + total = total + 1; fails = fails + ts_check("f1.same_name_one_row", (n1 == n2), 1); + total = total + 1; fails = fails + ts_check("f1.row_count", named_dedup_rows(str_intern("DedupProbe")), 1); + n3 := alloc_named_type(str_intern("DedupProbe2")); + total = total + 1; fails = fails + ts_check("f1.diff_name_diff_row", (n3 != n1), 1); + // 引擎侧:同名两行归一后,桥接的同一性成立(P1 的 9 unknown 根因消除) + total = total + 1; fails = fails + ts_check("f1.bridge_same_term", + (sh_term_of_ti(n1) == sh_term_of_ti(n2)), 1); + // 扩容/重建覆盖(P0/P1 血泪教训:装填守卫/重建重放路径判据不可达 = 挂死风险只靠读码): + // 入 1700 个互异**真名**(键域 = str_intern 分配的名字下标,与生产同域)推过**两道** + // 守卫线——count=511 → cap 1024→2048,count=1023 → 2048→4096。断四件事:①两次重建都 + // 真的发生(cap≥4096,非「读码判安全」)②重放守恒(条目计数恰 +1700,不多不少—— + // 重放漏条目/重复计数即红)③首条(重建前入表)仍归**原行**(重放把旧条目全搬过去了) + // ④该名字行数仍为 1(重建不产生重复行 = 去重不因扩容失效)。 + c_before := g_named_dedup_count; + f1i : ., mut = 0; + loop { + if f1i >= 1700 { break; } + alloc_named_type(str_intern("DedupGrow" + int_str(f1i))); + f1i = f1i + 1; + } + total = total + 1; fails = fails + ts_check("f1.dedup_rehash_survived", + (g_named_dedup_cap >= 4096 && g_named_dedup_count == c_before + 1700 && alloc_named_type(str_intern("DedupProbe")) == n1 && named_dedup_rows(str_intern("DedupProbe")) == 1), 1); + + // --- F2(R2 P2a Task 2):数组长度 N 迁为「常量档长度约束」 --- + // 背景(P1 findings §6.F2):身份判等曾把 N 与元素判等绑在一起(N 属身份);引擎侧按 + // R1 裁决 **N 不入身份** → 直接替换判定会让 `[int;4]` → `[int;3]` **静默通过**。 + // 修法(用户裁决 = 落常量档长度约束):N 迁出身份 → 具名约束 array_len_constraint_ok + // (判定点显式补检);拒绝语义保持(异长仍拒、同长仍过)。本组即该约束的守门用例。 + // 默认预算窗口(ty_equiv 用例计数)——本组之前的用例只碰过裸分配/侧表,未跑判定。 + ty_budget_reset(200000); + f2_arr3 := alloc_type(TYP_ARRAY, TI_INT, 3); + f2_arr3b := alloc_type(TYP_ARRAY, TI_INT, 3); // **另一行**同长:约束不可是 ti 同一性 + f2_arr4 := alloc_type(TYP_ARRAY, TI_INT, 4); + total = total + 1; fails = fails + ts_check("f2.same_len_ok", + array_len_constraint_ok(f2_arr3, f2_arr3b), 1); + total = total + 1; fails = fails + ts_check("f2.diff_len_reject", + array_len_constraint_ok(f2_arr4, f2_arr3), 0); + // 引擎侧:N 不入身份(spec §5.1;与 bridge.len_not_identity 同义,此处另立一行) + total = total + 1; fails = fails + ts_check("f2.engine_len_agnostic", + ty_equiv(sh_term_of_ti(f2_arr4), sh_term_of_ti(f2_arr3)), 1); + // 结构下钻(N 不在顶层数组位也要拦住——否则身份去 N 后这些位置静默放宽): + // ① 数组元素位 `[[int;3];2]`;② 泛型应用实参位 `G<[int;3]>`;③ 指针元素位 `*[int;3]`; + // ④ 元组字段位 `(int, [int;3])`。每例双断:同结构同长 → 1 / 同结构异长 → 0(防误拒)。 + f2_outer3 := alloc_type(TYP_ARRAY, f2_arr3, 2); + f2_outer4 := alloc_type(TYP_ARRAY, f2_arr4, 2); + total = total + 1; fails = fails + ts_check("f2.nested_elem_reject", + (array_len_constraint_ok(f2_outer3, f2_outer3) == 1 && array_len_constraint_ok(f2_outer4, f2_outer3) == 0), 1); + grow_gen_apply_data(g_gen_apply_data_count + 4); + f2_ga_s1 := g_gen_apply_data_count; + w64(g_gen_apply_data, f2_ga_s1 * 8, 1); + w64(g_gen_apply_data, (f2_ga_s1 + 1) * 8, f2_arr3); + f2_ga_s2 := f2_ga_s1 + 2; + w64(g_gen_apply_data, f2_ga_s2 * 8, 1); + w64(g_gen_apply_data, (f2_ga_s2 + 1) * 8, f2_arr4); + g_gen_apply_data_count = f2_ga_s2 + 2; + // base 走**裸分配**(人造行不入侧表——P1 桥接夹具同款约定,防假键污染 name→ti) + f2_base := alloc_type(TYP_NAMED, 1202, 0); + f2_ga3 := alloc_type(TYP_GENERIC_APPLY, f2_base, f2_ga_s1); + f2_ga4 := alloc_type(TYP_GENERIC_APPLY, f2_base, f2_ga_s2); + total = total + 1; fails = fails + ts_check("f2.genapply_arg_reject", + (array_len_constraint_ok(f2_ga3, f2_ga3) == 1 && array_len_constraint_ok(f2_ga4, f2_ga3) == 0), 1); + f2_pt3 := alloc_type(TYP_PTR, f2_arr3, 0); + f2_pt4 := alloc_type(TYP_PTR, f2_arr4, 0); + total = total + 1; fails = fails + ts_check("f2.ptr_elem_reject", + (array_len_constraint_ok(f2_pt3, f2_pt3) == 1 && array_len_constraint_ok(f2_pt4, f2_pt3) == 0), 1); + grow_gen_apply_data(g_gen_apply_data_count + 4); + f2_tp_s1 := g_gen_apply_data_count; + w64(g_gen_apply_data, f2_tp_s1 * 8, TI_INT); + w64(g_gen_apply_data, (f2_tp_s1 + 1) * 8, f2_arr3); + f2_tp_s2 := f2_tp_s1 + 2; + w64(g_gen_apply_data, f2_tp_s2 * 8, TI_INT); + w64(g_gen_apply_data, (f2_tp_s2 + 1) * 8, f2_arr4); + g_gen_apply_data_count = f2_tp_s2 + 2; + f2_tup3 := alloc_type(TYP_TUPLE, 2, f2_tp_s1); + f2_tup4 := alloc_type(TYP_TUPLE, 2, f2_tp_s2); + total = total + 1; fails = fails + ts_check("f2.tuple_field_reject", + (array_len_constraint_ok(f2_tup3, f2_tup3) == 1 && array_len_constraint_ok(f2_tup4, f2_tup3) == 0), 1); + // 负控:非数组对(含单侧数组)长度面无约束 → 恒满足(防「一律拒绝」的退化实现) + total = total + 1; fails = fails + ts_check("f2.nonarray_no_constraint", + (array_len_constraint_ok(f2_arr3, TI_INT) == 1 && array_len_constraint_ok(TI_INT, f2_arr3) == 1), 1); + // Task 4 Step 5c(Task 2 评审 M2 收口):① 补齐约束的 REF / SLICE 两个递归位(此前已钉 + // 数组元素 / 泛型实参 / 指针元素 / 元组字段四位,下钻面 6 位里缺这两位);② 把**站点接线** + // 纳入自测面——站点的措辞由 type_compat_strict 的**三态**分派(1 = 兼容 / 0 = 身份不匹配 + // → 原措辞 / -1 = 身份通过但长度约束违反 → 专属措辞),故 0 与 -1 的**区分**本身就是判据 + // (把两路并成一路 = 措辞面回归,且会把「长度不满足」误报成普通类型不匹配)。 + // REF 位注:约束只看 data(元素),**不**比 extra(mut)——mut 归身份判定(头注「同形」限定)。 + f2_rf3 := alloc_type(TYP_REF, f2_arr3, 0); + f2_rf4 := alloc_type(TYP_REF, f2_arr4, 0); + total = total + 1; fails = fails + ts_check("f2.ref_elem_reject", + (array_len_constraint_ok(f2_rf3, f2_rf3) == 1 && array_len_constraint_ok(f2_rf4, f2_rf3) == 0), 1); + f2_sl3 := alloc_type(TYP_SLICE, f2_arr3, 0); + f2_sl4 := alloc_type(TYP_SLICE, f2_arr4, 0); + total = total + 1; fails = fails + ts_check("f2.slice_elem_reject", + (array_len_constraint_ok(f2_sl3, f2_sl3) == 1 && array_len_constraint_ok(f2_sl4, f2_sl3) == 0), 1); + // 站点三态(type_compat_strict 恒「先 type_equal 再约束」,站点调用面不变): + // ① 同结构同长 → 1;② 异结构(数组 vs 基类型)→ 0(身份面否决,原措辞);③ 同结构异长 + // → -1(**只有**这一路走长度措辞;且它证明 type_equal 已放行 = 引擎身份确实 N-free)。 + total = total + 1; fails = fails + ts_check("t3.strict_dispatch_ok", + type_compat_strict(f2_arr3, f2_arr3b), 1); + total = total + 1; fails = fails + ts_check("t3.strict_dispatch_identity", + type_compat_strict(f2_arr3, TI_INT), 0); + total = total + 1; fails = fails + ts_check("t3.strict_dispatch_len", + type_compat_strict(f2_arr4, f2_arr3), -1); + + // --- R2 P2a Task 3:判定替换(引擎为判定权威 + unknown 政策 + N 不回身份)--- + // ① N 面(Task 2/3 评审裁决「N 不得回身份」):`type_equal`(引擎判定)对**同结构异长** + // 判 true——N 不在身份内;拒绝语义由 array_len_constraint_ok 独立承担(第二断)。 + // 两断合起来 = 该不变量的双钉:引擎不放宽成身份 → 但拒绝不丢。 + // ② unknown 政策(R2 P5 Task 3 重钉 → T4 终态):两个**互异命名型**行 → 桥接身份链 ⇒ + // 引擎**自决 0**(不再未知 ⇒ 零回落);T4 后回落本体已删 ⇒ 断言 = 「自决 + 零未知 + // 诊断(ICE04 缺席)」。**不得静默当 0/1** 的牙齿在 t4.ice04_* 三例(真未知面)。 + // ③ 同一行快路径:不触发引擎/回落(同型自反恒 true)。 + ty_budget_reset(200000); + t3_arr3 := alloc_type(TYP_ARRAY, TI_INT, 3); + t3_arr4 := alloc_type(TYP_ARRAY, TI_INT, 4); + t3_ok : ., mut = 0; + if type_equal(t3_arr3, t3_arr4) { + if array_len_constraint_ok(t3_arr4, t3_arr3) == 0 { t3_ok = 1; } + } + total = total + 1; fails = fails + ts_check("t3.engine_len_not_identity", t3_ok, 1); + // 行 1203/1204 = 人造行(不进生产侧表语义面;alloc_named_type 仅为取得互异 named 行) + t3_na := alloc_named_type(str_intern("T3NamedA")); + t3_nb := alloc_named_type(str_intern("T3NamedB")); + // **R2 P5 Task 3 重钉 / T4 终态**:命名面接引擎(身份链)后本形态 = 引擎**自决 0** + // (= legacy 同值),不再回落——T4 后回落计数本体亦删 ⇒ 断言形态 = 「行为(false)+ + // 引擎直判 0 + **零未知诊断**(ICE04 缺席 = 引擎真判而非未知被静默当 false)」。 + t3_d0 := g_diag_count; + t3_ub : ., mut = 0; + if !type_equal(t3_na, t3_nb) { + if g_diag_count == t3_d0 { t3_ub = 1; } + } + total = total + 1; fails = fails + ts_check("t3.named_face_decided_no_indeterminate", + (t3_ub == 1 && ty_equiv(sh_term_of_ti(t3_na), sh_term_of_ti(t3_nb)) == 0), 1); + // 桥接缺口(sh_term_of_ti 译不成项:行号越界)→ **P-A 硬错**(T4 重钉:旧行为 = 回落 + // legacy + g_replace_bridge 计数;现行为 = 判 false + ICE04 诊断恰 1 条)。措辞/成因面 + // 由 t4.ice04_* 段复核(本例只钉「行为 + 码」)。 + t3_bf_d0 := g_diag_count; + t3_bf : ., mut = 0; + if !type_equal(g_type_count + 100, TI_INT) { + if g_diag_count == t3_bf_d0 + 1 { + if ts_diag_code_at(t3_bf_d0) == EC_ICE_TY_INDET { t3_bf = 1; } + } + } + total = total + 1; fails = fails + ts_check("t3.bridge_gap_ice04_hard_error", t3_bf, 1); + g_diag_count = t3_bf_d0; // 本诊断是**故意**触发的(复位:不留进后续用例的增量基线) + ty_budget_reset(200000); + + // --- R2 P2a Task 3 评审 Critical:桥接缓存**随类型表重置失效**(sh_map_reset)--- + // 机制:判定路径**无条件**调 sh_term_of_ti(R2 P2a Task 3 起;此前仅 --type-shadow 下译项, + // 该旗标随 P5 T5 影子层下线删除)→ 桥接缓存 + // key = ti 本体,而 init_types() 清空重建类型表(**行号空间复用**)→ 陈旧的 ti→term 命中 + // 即返回 = 两个不同类型被判等(长驻进程静默漏报;评审复现:corelsp 同 URI 两次 didOpen)。 + // 用例 ① 重置后缓存确实清空(entries 0 + cap 0 = 惰性重建);② 行号复用场景下**不得**返回 + // 陈旧项——返回的必须是新类型的项(元素 AK 由 int 变 bool)。② 是 ① 的行为级对偶: + // 只查计数不查返回值的断言在「清了计数但表还活着」的错法下会假绿。 + init_types(); // 干净起点(同时建立缓存基线) + t3c_tiA := alloc_type(TYP_ARRAY, TI_INT, 2); // 行号 T + t3c_termA := sh_term_of_ti(t3c_tiA); // 缓存 {T → seq(int)} + t3c_entries_mid := sh_map_entries(); + init_types(); // 类型表 + 桥接缓存一并失效 + t3c_entries_after := sh_map_entries(); + total = total + 1; fails = fails + ts_check("t3c.map_reset_clears_bridge", + (t3c_entries_mid >= 1 && t3c_entries_after == 0 && g_term_map_cap == 0), 1); + t3c_tiB := alloc_type(TYP_ARRAY, TI_BOOL, 2); // 同一行号 T(复用) + t3c_termB := sh_term_of_ti(t3c_tiB); + t3c_ok : ., mut = 0; + if t3c_tiB == t3c_tiA { // 场景成立:行号确实复用 + if t3c_termB != t3c_termA { // 未返回陈旧项 + if tt_a(tt_a(tt_c(t3c_termA))) == AK_INT { + if tt_a(tt_a(tt_c(t3c_termB))) == AK_BOOL { t3c_ok = 1; } + } + } + } + total = total + 1; fails = fails + ts_check("t3c.no_stale_term_after_reset", t3c_ok, 1); + + // --- R2 P2b Task 1:本质条目表 + `iface_*` 查询 API(零消费者建层;表 = 静态数据)--- + // 前置:表由 init_types() 尾部建立(本通道不经 check_all → 与 P1 桥接段同款显式调用); + // 显式重建 = 干净起点(类型表行号空间复位,结构行自 9 起)。 + // 覆盖面口径:**16 条目**(R2 P4 Task 3 扩列:13 → +AK_NULL +AK_SUM +AK_FN)× 两向 + // (ak→ti 行 / 行→ak 类)+ 12 个 API 逐签名;8 原生逐条目不抽样。扩列 = 纯信息面 + // (三新行 ops = 0、ti_row = -1)——判定面零变化由 `ct2.kind_of_no_new_mapping`(全类型 + // 行枚举)承担,本行只钉「可查询性」面。 + init_types(); + total = total + 1; fails = fails + ts_check("iface.count", iface_count(), 16); + // 逐原生条目:ak → 规范行 + **AK↔TI 下标不 1:1** 的显式守卫(bool/string 互换即红) + total = total + 1; fails = fails + ts_check("iface.int_ti", iface_ti_of(AK_INT), TI_INT); + total = total + 1; fails = fails + ts_check("iface.dex_ti", iface_ti_of(AK_DEX), TI_DEX); + total = total + 1; fails = fails + ts_check("iface.str_ti", iface_ti_of(AK_STRING), TI_STR); + total = total + 1; fails = fails + ts_check("iface.bool_ti", iface_ti_of(AK_BOOL), TI_BOOL); + total = total + 1; fails = fails + ts_check("iface.unit_ti", iface_ti_of(AK_UNIT), TI_UNIT); + total = total + 1; fails = fails + ts_check("iface.never_ti", iface_ti_of(AK_NEVER), TI_NEVER); + total = total + 1; fails = fails + ts_check("iface.char_ti", iface_ti_of(AK_CHAR), TI_CHAR); + total = total + 1; fails = fails + ts_check("iface.dyn_ti", iface_ti_of(AK_DYN), TI_DYN); + // 反向:结构/命名条目无「规范行」(类级)→ 恒 -1(若哪天被填成某行 = 类级/行级混同) + total = total + 1; fails = fails + ts_check("iface.struct_ti_none", + (iface_ti_of(AK_PRODUCT) == -1 && iface_ti_of(AK_SEQUENCE) == -1 && iface_ti_of(AK_REF) == -1 && + iface_ti_of(AK_PTR) == -1 && iface_ti_of(AK_NAMED) == -1), 1); + // 行号 → 原子类:8 原生经**类型表本体**(kind == TYP_BASE && data == TY_*)判,不按行号猜 + total = total + 1; fails = fails + ts_check("iface.kind_int", iface_kind_of(TI_INT), AK_INT); + total = total + 1; fails = fails + ts_check("iface.kind_dex", iface_kind_of(TI_DEX), AK_DEX); + total = total + 1; fails = fails + ts_check("iface.kind_str", iface_kind_of(TI_STR), AK_STRING); + total = total + 1; fails = fails + ts_check("iface.kind_bool", iface_kind_of(TI_BOOL), AK_BOOL); + total = total + 1; fails = fails + ts_check("iface.kind_unit", iface_kind_of(TI_UNIT), AK_UNIT); + total = total + 1; fails = fails + ts_check("iface.kind_never", iface_kind_of(TI_NEVER), AK_NEVER); + total = total + 1; fails = fails + ts_check("iface.kind_char", iface_kind_of(TI_CHAR), AK_CHAR); + total = total + 1; fails = fails + ts_check("iface.kind_dyn", iface_kind_of(TI_DYN), AK_DYN); + total = total + 1; fails = fails + ts_check("iface.kind_oob", iface_kind_of(g_type_count + 7), -1); + total = total + 1; fails = fails + ts_check("iface.kind_neg", iface_kind_of(-1), -1); + // 结构/命名行 → 类(逐 kind 一条构造子;配对着写以钉死「非同类混判」) + ifc_ptr := alloc_type(TYP_PTR, TI_INT, 0); + ifc_ref := alloc_type(TYP_REF, TI_STR, 0); + total = total + 1; fails = fails + ts_check("iface.kind_ptr_ref", + (iface_kind_of(ifc_ptr) == AK_PTR && iface_kind_of(ifc_ref) == AK_REF), 1); + ifc_arr := alloc_type(TYP_ARRAY, TI_INT, 3); + ifc_slice := alloc_type(TYP_SLICE, TI_INT, 0); + total = total + 1; fails = fails + ts_check("iface.kind_seq", + (iface_kind_of(ifc_arr) == AK_SEQUENCE && iface_kind_of(ifc_slice) == AK_SEQUENCE), 1); + ifc_tup := alloc_type(TYP_TUPLE, 0, 0); + total = total + 1; fails = fails + ts_check("iface.kind_product", iface_kind_of(ifc_tup), AK_PRODUCT); + ifc_named := alloc_named_type(str_intern("IfaceNamedProbe")); + ifc_gp := alloc_type(TYP_GENERIC_PARAM, str_intern("IfaceGP"), 0); + ifc_ga := alloc_type(TYP_GENERIC_APPLY, ifc_named, 0); + total = total + 1; fails = fails + ts_check("iface.kind_named", + (iface_kind_of(ifc_named) == AK_NAMED && iface_kind_of(ifc_gp) == AK_NAMED && + iface_kind_of(ifc_ga) == AK_NAMED), 1); + // 条目定位(含越界/负键:不得把「无此原子」与行 0 混同) + total = total + 1; fails = fails + ts_check("iface.entry_lookup", + (iface_entry(AK_INT) >= 0 && iface_entry(AK_INT) < iface_count() && iface_entry(AK_NAMED) >= 0 && + iface_entry(-1) == -1 && iface_entry(9999) == -1), 1); + // TY_* 码 → 原子类(逐项语义分派;含两条已裁决灰格:DEX_S 同值域不同表示、GENERIC_PARAM 哨兵) + total = total + 1; fails = fails + ts_check("iface.ty_code_natives", + (iface_by_ty_code(TY_INT) == AK_INT && iface_by_ty_code(TY_DEX) == AK_DEX && + iface_by_ty_code(TY_BOOL) == AK_BOOL && iface_by_ty_code(TY_STRING) == AK_STRING && + iface_by_ty_code(TY_UNIT) == AK_UNIT && iface_by_ty_code(TY_NEVER) == AK_NEVER && + iface_by_ty_code(TY_CHAR) == AK_CHAR), 1); + total = total + 1; fails = fails + ts_check("iface.ty_code_gray", + (iface_by_ty_code(TY_DEX_S) == AK_DEX && iface_by_ty_code(TY_GENERIC_PARAM) == AK_NAMED), 1); + total = total + 1; fails = fails + ts_check("iface.ty_code_unknown", iface_by_ty_code(999), -1); + // 字面量定型(查表入口 = 条目 lit_code 列;接线后 = infer_expr 的 5 处唯一真源, + // 端到端对拍见本文件末段 `lit.infer_*`) + total = total + 1; fails = fails + ts_check("iface.lit_int", iface_lit_ti(EXPR_INT), TI_INT); + total = total + 1; fails = fails + ts_check("iface.lit_dex", iface_lit_ti(EXPR_DEX), TI_DEX); + total = total + 1; fails = fails + ts_check("iface.lit_str", iface_lit_ti(EXPR_STRING), TI_STR); + total = total + 1; fails = fails + ts_check("iface.lit_bool", iface_lit_ti(EXPR_BOOL), TI_BOOL); + total = total + 1; fails = fails + ts_check("iface.lit_char", iface_lit_ti(EXPR_CHAR), TI_CHAR); + total = total + 1; fails = fails + ts_check("iface.lit_none", iface_lit_ti(EXPR_IDENT), -1); + total = total + 1; fails = fails + ts_check("iface.lit_ak", + (iface_lit_ak(EXPR_INT) == AK_INT && iface_lit_ak(EXPR_DEX) == AK_DEX && + iface_lit_ak(EXPR_STRING) == AK_STRING && iface_lit_ak(EXPR_BOOL) == AK_BOOL && + iface_lit_ak(EXPR_CHAR) == AK_CHAR), 1); + // 负键 = 条目的「无字面量」哨兵 ⇒ 必须先行拒绝(否则 -1 命中无字面量条目) + total = total + 1; fails = fails + ts_check("iface.lit_neg", iface_lit_ak(-1), -1); + // 类型项 → 原子类(单一原子 / ⊤ₖ;复合与越界 → -1) + ifc_term_atom := tt_atom(AK_INT, TI_INT, -1); + total = total + 1; fails = fails + ts_check("iface.of_term_atom", iface_of_term(ifc_term_atom), AK_INT); + total = total + 1; fails = fails + ts_check("iface.of_term_topk", iface_of_term(tt_top_k(AK_SEQUENCE)), AK_SEQUENCE); + total = total + 1; fails = fails + ts_check("iface.of_term_compound", iface_of_term(tt_union(a_int, a_str)), -1); + total = total + 1; fails = fails + ts_check("iface.of_term_oob", + (iface_of_term(-1) == -1 && iface_of_term(tt_count() + 9) == -1), 1); + // 未知原子 → 空许可集(不得给「看起来有许可」的位);位下标越界 → 0(不得回绕成全位命中) + total = total + 1; fails = fails + ts_check("iface.ops_unknown", iface_ops(-1), 0); + total = total + 1; fails = fails + ts_check("iface.ops_unknown_ak", iface_ops(9999), 0); + total = total + 1; fails = fails + ts_check("iface.permits_bad_op", + (iface_permits(AK_INT, -1) == 0 && iface_permits(AK_INT, 63) == 0 && iface_permits(9999, OP_ADD) == 0), 1); + + // --- R2 P2b Task 2:`iface_kind_of` 单源化——桥接分派(sh_native_ak/sh_base_ak)与注册表合一 --- + // 判据 = **全表逐格**(非抽样):生产入口对**冻结期望表**逐格相同。**R2 P5 Task 4 重定**: + // 改动前实现的字面拷贝(sh_base_ak_legacy/sh_native_ak_legacy)已随 legacy 面删除 ⇒ 对照物 + // 改为 `ts_t4_ak_expected`/`ts_t4_native_ak_expected`(**本文件的冻结期望**,不再是生产文件里 + // 的第二份实现——单源化的收益自此由数据钉住)。只比「两版相等」会漏两类错——入口被换成 + // 第三个实现、两版一起错 ⇒ 另加③~⑥的**显式**断言(反真空/灰格/门/下标不 1:1)。 + // ① TY 码面:全部 9 个 TY_* 码(含 TY_DEX_S / TY_GENERIC_PARAM 两条已裁决灰格)+ 未映射码 + t2_codes := alloc(10 * 8); + w64(t2_codes, 0, TY_INT); w64(t2_codes, 8, TY_DEX); + w64(t2_codes, 16, TY_BOOL); w64(t2_codes, 24, TY_STRING); + w64(t2_codes, 32, TY_UNIT); w64(t2_codes, 40, TY_NEVER); + w64(t2_codes, 48, TY_CHAR); w64(t2_codes, 56, TY_GENERIC_PARAM); + w64(t2_codes, 64, TY_DEX_S); w64(t2_codes, 72, 999); + t2_ty_bad : ., mut = 0; + t2_i : ., mut = 0; + loop { + if t2_i >= 10 { break; } + t2_code := r64(t2_codes, t2_i * 8); + if iface_by_ty_code(t2_code) != ts_t4_ak_expected(t2_code) { t2_ty_bad = t2_ty_bad + 1; } + t2_i = t2_i + 1; + } + total = total + 1; fails = fails + ts_check("iface.by_ty_code_all_codes", t2_ty_bad, 0); + // ② 行号面全表:逐 ti ∈ [0, g_type_count)(含 TI_DYN 行、TI_DEX_S 占位行与上方结构/命名行) + t2_ti_bad : ., mut = 0; + t2_j : ., mut = 0; + loop { + if t2_j >= g_type_count { break; } + if sh_native_ak(t2_j) != ts_t4_native_ak_expected(t2_j) { t2_ti_bad = t2_ti_bad + 1; } + t2_j = t2_j + 1; + } + total = total + 1; fails = fails + ts_check("iface.native_ak_all_rows", t2_ti_bad, 0); + // ③ **反真空哨兵**(两处 loop 是本 Task 的判据本体,不得空转假绿): + // ⓐ 扫描计数:码面 10 个键全扫过(且解码真读回写入值);行面恰为 g_type_count 行且 ≥ 9; + // ⓑ 比较是活的:两个**已知互补**的格必须给出**不同**结果(未映射码 → -1 而 TY_INT → AK_INT)。 + total = total + 1; fails = fails + ts_check("iface.scan_coverage", + (t2_i == 10 && r64(t2_codes, 56) == TY_GENERIC_PARAM && r64(t2_codes, 64) == TY_DEX_S && + t2_j == g_type_count && t2_j >= 9), 1); + total = total + 1; fails = fails + ts_check("iface.compare_is_live", + (iface_by_ty_code(999) == ts_t4_ak_expected(TY_INT)), 0); + // ④ 生产入口**确实在委托**(逐项钉死;含两条灰格与未映射码) + total = total + 1; fails = fails + ts_check("iface.bridge_delegates", + (sh_base_ak(TY_INT) == AK_INT && sh_base_ak(TY_STRING) == AK_STRING && sh_base_ak(TY_BOOL) == AK_BOOL && + sh_base_ak(TY_DEX_S) == AK_DEX && sh_base_ak(TY_GENERIC_PARAM) == AK_NAMED && sh_base_ak(999) == -1 && + sh_native_ak(TI_INT) == AK_INT && sh_native_ak(TI_STR) == AK_STRING), 1); + // ⑤ **原门保留**(委托不得把 sh_native_ak 变成 iface_kind_of):DYN 行/结构行/负号 → -1, + // 而同时注册表侧对 DYN 行回 AK_DYN——两函数契约不同,各自钉一条。 + total = total + 1; fails = fails + ts_check("iface.bridge_gate_kept", + (sh_native_ak(TI_DYN) == -1 && sh_native_ak(ifc_arr) == -1 && sh_native_ak(ifc_named) == -1 && + sh_native_ak(-1) == -1 && iface_kind_of(TI_DYN) == AK_DYN), 1); + // ⑥ 语义分派守卫(P1 血泪:AK/TI 下标不 1:1)——若哪天有人「按下标直传」,本行必红 + total = total + 1; fails = fails + ts_check("iface.dispatch_no_index_shortcut", + (iface_ti_of(AK_STRING) != AK_STRING && iface_ti_of(AK_BOOL) != AK_BOOL && TI_STR != TI_BOOL), 1); + + // --- R2 P2b Task 3:字面量定型查表接线(infer_expr 的 5 个字面量分支 → iface_lit_ti)--- + // 判据 = **接线端到端**:用与 parser 同族的 `alloc_node` 构造 5 个字面量 AST 节点,逐 kind 比 + // `infer_expr(节点)` 与改动前的字面常量(= 内联 if 链 `checker.cr:1896-1901` 的逐格转录)。 + // 为什么不能只看表侧(`iface.lit_*`):表对而**线错**(接线点写错键,如 EXPR_INT 行调 + // `iface_lit_ti(EXPR_DEX)`)在表侧用例下**全绿**——只有经 infer_expr 真跑才暴露。 + // 本段为**本 Task 的判据本体**,其非真空性由实施报告记录的**突变控制**(翻转表格 ⇒ 本段 + // `lit.infer_int` 必红 ⇒ 复位)实证,非仅「跑过一遍」。 + li_int := alloc_node(EXPR_INT, -1, -1, -1, 42, TY_INT, -1, 0, 0); + li_dex := alloc_node(EXPR_DEX, -1, -1, -1, 314, TY_DEX, -1, 0, 0); + li_str := alloc_node(EXPR_STRING, -1, -1, -1, str_intern("iface_lit_probe"), TY_STRING, -1, 0, 0); + li_bool := alloc_node(EXPR_BOOL, -1, -1, -1, 1, TY_BOOL, -1, 0, 0); + li_char := alloc_node(EXPR_CHAR, -1, -1, -1, 65, TY_CHAR, -1, 0, 0); + // 反真空哨兵:构造出的节点确实带预期 kind(防 alloc_node 参数错位 ⇒ 「拿错节点比错值」假绿) + total = total + 1; fails = fails + ts_check("lit.node_kinds", + (ast_kind(li_int) == EXPR_INT && ast_kind(li_dex) == EXPR_DEX && ast_kind(li_str) == EXPR_STRING && + ast_kind(li_bool) == EXPR_BOOL && ast_kind(li_char) == EXPR_CHAR), 1); + total = total + 1; fails = fails + ts_check("lit.infer_int", infer_expr(li_int), TI_INT); + total = total + 1; fails = fails + ts_check("lit.infer_dex", infer_expr(li_dex), TI_DEX); + total = total + 1; fails = fails + ts_check("lit.infer_str", infer_expr(li_str), TI_STR); + total = total + 1; fails = fails + ts_check("lit.infer_bool", infer_expr(li_bool), TI_BOOL); + total = total + 1; fails = fails + ts_check("lit.infer_char", infer_expr(li_char), TI_CHAR); + // 端到端 ↔ 表 对拍:接线后 infer_expr 的结果必须**逐 kind 等于表查表结果**(表 = 唯一真源; + // 若哪天有人把某行改回硬编码常量,本行仍绿但 `lit.infer_*` 亦绿——真正的守门是实施报告的 + // 突变控制:改表格 ⇒ infer_* 红 ⇒ 该行确在读表) + total = total + 1; fails = fails + ts_check("lit.infer_is_table", + (infer_expr(li_int) == iface_lit_ti(EXPR_INT) && infer_expr(li_dex) == iface_lit_ti(EXPR_DEX) && + infer_expr(li_str) == iface_lit_ti(EXPR_STRING) && infer_expr(li_bool) == iface_lit_ti(EXPR_BOOL) && + infer_expr(li_char) == iface_lit_ti(EXPR_CHAR)), 1); + // 短路顺序(接线硬口径:**逐字保持**)——EXPR_NONE 转发行夹在 EXPR_INT 与 EXPR_DEX 之间: + // ① 带内层值的 wrapper:在 EXPR_INT 行不命中(kind=0)⇒ 走转发行 → 内层类型; + // ② 空 wrapper(a = -1):转发行不命中 ⇒ 落函数中部的 EXPR_NONE 分支 → TI_UNIT。 + li_wrap := alloc_node(EXPR_NONE, li_int, -1, -1, 0, 0, -1, 0, 0); + li_wrap_empty := alloc_node(EXPR_NONE, -1, -1, -1, 0, 0, -1, 0, 0); + total = total + 1; fails = fails + ts_check("lit.none_forwards", infer_expr(li_wrap), TI_INT); + total = total + 1; fails = fails + ts_check("lit.none_empty_unit", infer_expr(li_wrap_empty), TI_UNIT); + // 负节点先行拒绝(infer_expr 头行;接线不得把它挤掉) + total = total + 1; fails = fails + ts_check("lit.neg_node_unit", infer_expr(-1), TI_UNIT); + + // --- R2 P2b Task 4:操作许可位集接线(ops 列逐格;消费者 = checker.cr 的三个门)--- + // 判据口径 = **与改动前的判定一字等价**(逐格全表对拍,非抽样):把「改动前的谓词」内联进 + // 本段,逐 ak × 逐 op 与 `iface_permits` 对拍。三组关键等价(改动前原文 = checker.cr 算术门 + // :1959 / 逻辑门 :1969 / 条件门 :2274 + :2385): + // ① ADD..MOD 许可 ⟺ ak ∈ {AK_INT, AK_DEX}(门**只含 int/dex**;PTR/STRING 不在内 ⇒ + // `*T + *T`/`"a" - "b"` 仍 error[TB01]) + // ② OP_AND/OP_OR 许可 ⟺ ak ∈ {AK_INT, AK_BOOL}(dex 拒——`1.5 && true` 现状 TC01) + // ③ IP_COND ⟺ ak ∈ {AK_INT, AK_BOOL};IP_COND_BOOL ⟺ ak == AK_BOOL(`while 1` 现状 TC04) + // 反真空:另立「许可为 1 的格数」哨兵(全 0 表 ⇒ 计数 0 必红)+ 条目面一致性 + 端到端段。 + o4_aks := alloc(13 * 8); // 13 类枚举(顺序 = 条目表声明序) + w64(o4_aks, 0, AK_INT); w64(o4_aks, 8, AK_DEX); w64(o4_aks, 16, AK_STRING); + w64(o4_aks, 24, AK_BOOL); w64(o4_aks, 32, AK_UNIT); w64(o4_aks, 40, AK_NEVER); + w64(o4_aks, 48, AK_CHAR); w64(o4_aks, 56, AK_DYN); w64(o4_aks, 64, AK_PRODUCT); + w64(o4_aks, 72, AK_SEQUENCE); w64(o4_aks, 80, AK_REF); w64(o4_aks, 88, AK_PTR); + w64(o4_aks, 96, AK_NAMED); + o4_ops := alloc(5 * 8); // 算术族 5 op(门消费面:OP_ADD..OP_MOD) + w64(o4_ops, 0, OP_ADD); w64(o4_ops, 8, OP_SUB); w64(o4_ops, 16, OP_MUL); + w64(o4_ops, 24, OP_DIV); w64(o4_ops, 32, OP_MOD); + // ① 算术门:逐 ak × 逐 op 对拍「许可 ⟺ int|dex」 + o4_arith_bad : ., mut = 0; + o4_arith_yes : ., mut = 0; + o4_i : ., mut = 0; + loop { + if o4_i >= 13 { break; } + o4_ak := r64(o4_aks, o4_i * 8); + o4_j : ., mut = 0; + loop { + if o4_j >= 5 { break; } + o4_op := r64(o4_ops, o4_j * 8); + o4_want : ., mut = 0; + if o4_ak == AK_INT || o4_ak == AK_DEX { o4_want = 1; } + if iface_permits(o4_ak, o4_op) != o4_want { o4_arith_bad = o4_arith_bad + 1; } + if iface_permits(o4_ak, o4_op) == 1 { o4_arith_yes = o4_arith_yes + 1; } + o4_j = o4_j + 1; + } + o4_i = o4_i + 1; + } + total = total + 1; fails = fails + ts_check("ops.arith_gate_eq_legacy", o4_arith_bad, 0); + total = total + 1; fails = fails + ts_check("ops.arith_permit_count", o4_arith_yes, 10); // = 2 ak × 5 op + // ② 逻辑族:逐 ak × 逐 op(OP_AND/OP_OR)对拍「许可 ⟺ int|bool」——谓词取 OP_AND(不另设 IP_LOGIC) + o4_logic_bad : ., mut = 0; + o4_logic_yes : ., mut = 0; + o4_k : ., mut = 0; + loop { + if o4_k >= 13 { break; } + o4_ak2 := r64(o4_aks, o4_k * 8); + o4_want2 : ., mut = 0; + if o4_ak2 == AK_INT || o4_ak2 == AK_BOOL { o4_want2 = 1; } + if iface_permits(o4_ak2, OP_AND) != o4_want2 { o4_logic_bad = o4_logic_bad + 1; } + if iface_permits(o4_ak2, OP_OR) != o4_want2 { o4_logic_bad = o4_logic_bad + 1; } + if iface_permits(o4_ak2, OP_AND) == 1 { o4_logic_yes = o4_logic_yes + 1; } + o4_k = o4_k + 1; + } + total = total + 1; fails = fails + ts_check("ops.logic_gate_eq_legacy", o4_logic_bad, 0); + total = total + 1; fails = fails + ts_check("ops.logic_permit_count", o4_logic_yes, 2); // int + bool + // ③ 条件族:IP_COND = {int, bool};IP_COND_BOOL = {bool}(**两条规则现状不同,不得合并**) + o4_cond_bad : ., mut = 0; + o4_cond_yes : ., mut = 0; + o4_condb_yes : ., mut = 0; + o4_m : ., mut = 0; + loop { + if o4_m >= 13 { break; } + o4_ak3 := r64(o4_aks, o4_m * 8); + o4_want3 : ., mut = 0; + if o4_ak3 == AK_INT || o4_ak3 == AK_BOOL { o4_want3 = 1; } + o4_want4 : ., mut = 0; + if o4_ak3 == AK_BOOL { o4_want4 = 1; } + if iface_permits(o4_ak3, IP_COND) != o4_want3 { o4_cond_bad = o4_cond_bad + 1; } + if iface_permits(o4_ak3, IP_COND_BOOL) != o4_want4 { o4_cond_bad = o4_cond_bad + 1; } + if iface_permits(o4_ak3, IP_COND) == 1 { o4_cond_yes = o4_cond_yes + 1; } + if iface_permits(o4_ak3, IP_COND_BOOL) == 1 { o4_condb_yes = o4_condb_yes + 1; } + o4_m = o4_m + 1; + } + total = total + 1; fails = fails + ts_check("ops.cond_gate_eq_legacy", o4_cond_bad, 0); + total = total + 1; fails = fails + ts_check("ops.cond_permit_count", o4_cond_yes * 10 + o4_condb_yes, 21); // 2 与 1 + // ④ 全许可面(现状**无拒绝路径** ⇒ 本批不接线、只登记为 P3 旋钮):比较 6 op / 一元 4 op / AS + // 对全部 13 类置 1——逐格对拍(计数 = 13 × 11 = 143) + o4_all_bad : ., mut = 0; + o4_all_yes : ., mut = 0; + o4_n : ., mut = 0; + loop { + if o4_n >= 13 { break; } + o4_ak4 := r64(o4_aks, o4_n * 8); + o4_n2 : ., mut = 0; + loop { + if o4_n2 >= 6 { break; } + o4_cop := OP_EQ + o4_n2; // OP_EQ..OP_GE 连续取值(6..11) + if iface_permits(o4_ak4, o4_cop) != 1 { o4_all_bad = o4_all_bad + 1; } + if iface_permits(o4_ak4, o4_cop) == 1 { o4_all_yes = o4_all_yes + 1; } + o4_n2 = o4_n2 + 1; + } + o4_n3 : ., mut = 0; + loop { + if o4_n3 >= 4 { break; } + o4_uop := IP_UOP_BIAS + 1 + o4_n3; // UOP_NEG..UOP_DEREF → 21..24 + if iface_permits(o4_ak4, o4_uop) != 1 { o4_all_bad = o4_all_bad + 1; } + if iface_permits(o4_ak4, o4_uop) == 1 { o4_all_yes = o4_all_yes + 1; } + o4_n3 = o4_n3 + 1; + } + if iface_permits(o4_ak4, IP_AS) != 1 { o4_all_bad = o4_all_bad + 1; } + if iface_permits(o4_ak4, IP_AS) == 1 { o4_all_yes = o4_all_yes + 1; } + o4_n = o4_n + 1; + } + total = total + 1; fails = fails + ts_check("ops.all_permit_eq_legacy", o4_all_bad, 0); + total = total + 1; fails = fails + ts_check("ops.all_permit_count", o4_all_yes, 143); // 13 × 11 + // ⑤ 容器/方法面(Task 5 的消费格;本批登记):索引/字段/方法各恰两类 + total = total + 1; fails = fails + ts_check("ops.index_permit_set", + (iface_permits(AK_SEQUENCE, IP_INDEX) == 1 && iface_permits(AK_STRING, IP_INDEX) == 1 && + iface_permits(AK_INT, IP_INDEX) == 0 && iface_permits(AK_REF, IP_INDEX) == 0 && + iface_permits(AK_SEQUENCE, IP_INDEX_RANGE) == 1 && iface_permits(AK_STRING, IP_INDEX_RANGE) == 0), 1); + total = total + 1; fails = fails + ts_check("ops.field_permit_set", + (iface_permits(AK_NAMED, IP_FIELD) == 1 && iface_permits(AK_PRODUCT, IP_FIELD) == 1 && + iface_permits(AK_INT, IP_FIELD) == 0 && iface_permits(AK_SEQUENCE, IP_FIELD) == 0), 1); + total = total + 1; fails = fails + ts_check("ops.method_permit_set", + (iface_permits(AK_DYN, IP_METHOD) == 1 && iface_permits(AK_NAMED, IP_METHOD) == 1 && + iface_permits(AK_INT, IP_METHOD) == 0 && iface_permits(AK_PTR, IP_METHOD) == 0), 1); + // ⑥ 条目面一致性(逐条目:`iface_ops(ak)` 必须回读该行的 ops 列;防「列未填/查错行」)。 + // R2 P4 Task 3 重定:扩列三行(AK_NULL/AK_SUM/AK_FN)= **纯信息面**(ops = 0 是设计值, + // 非「空集残留」)⇒ 零 ops 行 = **恰三行且逐名可指**(允许集显式列举,其余行仍须非 0 + // ——T1..T2 口径保持)。 + o4_row_bad : ., mut = 0; + o4_zero : ., mut = 0; + o4_r : ., mut = 0; + loop { + if o4_r >= g_iface_entry_count { break; } + o4_akr := r64(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_AK); + if iface_ops(o4_akr) != r64(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_OPS) { o4_row_bad = o4_row_bad + 1; } + if iface_ops(o4_akr) == 0 { + if o4_akr == AK_NULL || o4_akr == AK_SUM || o4_akr == AK_FN { o4_zero = o4_zero + 1; } + else { o4_row_bad = o4_row_bad + 1; } + } + o4_r = o4_r + 1; + } + total = total + 1; fails = fails + ts_check("ops.entry_row_consistent", + (o4_row_bad == 0 && o4_zero == 3), 1); + // ⑦ 端到端(经真 infer_expr 走三个门;**行为证据**——表侧用例证明不了「线对」): + // ptr 操作数 = `&int 字面量` 的构造节点(EXPR_UNARY/UOP_REF,非 ident 分支 ⇒ 不触借用检查) + o4_ref := alloc_node(EXPR_UNARY, li_int, -1, UOP_REF, 0, 0, -1, 0, 0); + o4_ref2 := alloc_node(EXPR_UNARY, li_int, -1, UOP_REF, 0, 0, -1, 0, 0); + o4_b_add_int := alloc_node(EXPR_BINARY, li_int, li_int, OP_ADD, 0, 0, -1, 0, 0); + o4_b_sub_str := alloc_node(EXPR_BINARY, li_str, li_str, OP_SUB, 0, 0, -1, 0, 0); + o4_b_add_str := alloc_node(EXPR_BINARY, li_str, li_str, OP_ADD, 0, 0, -1, 0, 0); + o4_b_ptrptr := alloc_node(EXPR_BINARY, o4_ref, o4_ref2, OP_ADD, 0, 0, -1, 0, 0); + o4_b_ptrdiff := alloc_node(EXPR_BINARY, o4_ref, o4_ref2, OP_SUB, 0, 0, -1, 0, 0); + o4_b_ptradd := alloc_node(EXPR_BINARY, o4_ref, li_int, OP_ADD, 0, 0, -1, 0, 0); + o4_b_dex := alloc_node(EXPR_BINARY, li_dex, li_int, OP_ADD, 0, 0, -1, 0, 0); + o4_b_logic := alloc_node(EXPR_BINARY, li_bool, li_int, OP_AND, 0, 0, -1, 0, 0); + o4_b_logic_bad := alloc_node(EXPR_BINARY, li_dex, li_bool, OP_AND, 0, 0, -1, 0, 0); + o4_b_eq := alloc_node(EXPR_BINARY, li_str, li_int, OP_EQ, 0, 0, -1, 0, 0); + // 反真空哨兵:构造节点确实带预期 kind/op(防 alloc_node 参数错位 ⇒ 假绿) + total = total + 1; fails = fails + ts_check("ops.node_kinds", + (ast_kind(o4_b_add_int) == EXPR_BINARY && ast_c(o4_b_add_int) == OP_ADD && + ast_kind(o4_ref) == EXPR_UNARY && ast_c(o4_ref) == UOP_REF), 1); + // 正控:int 算术 / 串拼接(早退规则)/ 指针算术 + int / dex 支配 / 逻辑 int 侧 / 比较不校验 + o4_m0 := g_diag_count; + o4_r_add := infer_expr(o4_b_add_int); + total = total + 1; fails = fails + ts_check("ops.infer_add_int", + o4_r_add * 100 + (g_diag_count - o4_m0), TI_INT * 100); + o4_m1 := g_diag_count; + o4_r_cat := infer_expr(o4_b_add_str); + total = total + 1; fails = fails + ts_check("ops.infer_add_str_concat", + o4_r_cat * 100 + (g_diag_count - o4_m1), TI_STR * 100); + o4_m2 := g_diag_count; + o4_r_pa := infer_expr(o4_b_ptradd); + // 结果 = `*T` 行本身(T = int):**不**与另一次 `infer_expr(o4_ref)` 的行号比——alloc_type + // 是裸分配器(不去重,每次调用追加新行)⇒ 两次推断的行号必然不同(比行号 = 永久红)。 + o4_pa_ok : ., mut = 0; + if get_type_kind(o4_r_pa) == TYP_PTR && get_type_data(o4_r_pa) == TI_INT { + if g_diag_count - o4_m2 == 0 { o4_pa_ok = 1; } + } + total = total + 1; fails = fails + ts_check("ops.infer_ptr_add_int", o4_pa_ok, 1); + o4_m3 := g_diag_count; + o4_r_dx := infer_expr(o4_b_dex); + total = total + 1; fails = fails + ts_check("ops.infer_dex_add", + o4_r_dx * 100 + (g_diag_count - o4_m3), TI_DEX * 100); + o4_m4 := g_diag_count; + o4_r_lg := infer_expr(o4_b_logic); + total = total + 1; fails = fails + ts_check("ops.infer_logic_int_ok", + o4_r_lg * 100 + (g_diag_count - o4_m4), TI_BOOL * 100); + o4_m5 := g_diag_count; + o4_r_eq := infer_expr(o4_b_eq); + total = total + 1; fails = fails + ts_check("ops.infer_cmp_unchecked", + o4_r_eq * 100 + (g_diag_count - o4_m5), TI_BOOL * 100); // 现状宽松面:比较不校验(登记) + // 负控(**本批最易放宽的三条**):string 算术 / ptr+ptr / dex 逻辑 → 必须仍报错(码不变) + o4_m6 := g_diag_count; + o4_r_bad1 := infer_expr(o4_b_sub_str); + total = total + 1; fails = fails + ts_check("ops.infer_sub_str_diag", g_diag_count - o4_m6, 1); + total = total + 1; fails = fails + ts_check("ops.infer_sub_str_code", ts_diag_code_at(o4_m6), EC_TB_ADD); + total = total + 1; fails = fails + ts_check("ops.infer_sub_str_result", o4_r_bad1, TI_INT); + o4_m7 := g_diag_count; + o4_r_bad2 := infer_expr(o4_b_ptrptr); + total = total + 1; fails = fails + ts_check("ops.infer_ptr_add_ptr_diag", g_diag_count - o4_m7, 1); + total = total + 1; fails = fails + ts_check("ops.infer_ptr_add_ptr_code", ts_diag_code_at(o4_m7), EC_TB_ADD); + total = total + 1; fails = fails + ts_check("ops.infer_ptr_add_ptr_result", o4_r_bad2, TI_INT); + o4_m8 := g_diag_count; + o4_r_bad3 := infer_expr(o4_b_logic_bad); + total = total + 1; fails = fails + ts_check("ops.infer_logic_dex_diag", g_diag_count - o4_m8, 1); + total = total + 1; fails = fails + ts_check("ops.infer_logic_dex_code", ts_diag_code_at(o4_m8), EC_TC_IF_COND); + // 早退规则保留(结果规则):指针差 = `*T - *T` → int(不经门、不报错) + o4_m9 := g_diag_count; + o4_r_pd := infer_expr(o4_b_ptrdiff); + total = total + 1; fails = fails + ts_check("ops.infer_ptr_diff", + o4_r_pd * 100 + (g_diag_count - o4_m9), TI_INT * 100); + + // --- R2 P2b Task 5:容器面接线(**唯一接线点** = 索引兜底拒绝 → IP_INDEX 查表)--- + // 三层判据: + // ① **集等价**(保语义硬口径,全类型行枚举非抽样):门的可达集 =「kind ∉ {ARRAY, SLICE} ∧ + // ti ≠ TI_STR」= 结果分支后的落空集;表的拒绝集与之**逐行相等**(`permits == 1 ⟺ 可达 + // 集外`,双向都数)⇒ 接线不改变任何一行的判定。**这是本 Task 接线等价性的本体证据**; + // 非真空性由实施报告的突变控制承担(给 AK_INT 加 IP_INDEX ⇒ 本例必红)。 + // ② **端到端经真 infer_expr**(三个结果分支保留 + 兜底门在判):诊断增量 + 码 + 结果类型。 + // ③ 反真空哨兵:枚举覆盖行类直方图非退化 + 构造节点 kind/op 正确。 + // 覆盖行类自备(不依赖前序段落的残余行):PTR/REF/TUPLE 各一行,供 ③ 的直方图计数 + ix_row_ptr := alloc_type(TYP_PTR, TI_INT, 0); + ix_row_ref := alloc_type(TYP_REF, TI_INT, 0); + ix_row_tup := alloc_type(TYP_TUPLE, 0, 0); + ix_li0 := alloc_node(EXPR_INT, -1, -1, -1, 0, TY_INT, -1, 0, 0); // 索引值 0(串下标须在界内,避免 F 面诊断) + ix_arr3 := alloc_node(EXPR_ARRAY, -1, 3, -1, 0, 0, -1, 0, 0); // 数组字面量 len=3(无元素) + ix_arr0 := alloc_node(EXPR_ARRAY, -1, 0, -1, 0, 0, -1, 0, 0); // 空数组字面量 len=0 + ix_idx_arr := alloc_node(EXPR_INDEX, ix_arr0, li_int, -1, 0, 0, -1, 0, 0); + ix_idx_str := alloc_node(EXPR_INDEX, li_str, ix_li0, -1, 0, 0, -1, 0, 0); + ix_idx_int := alloc_node(EXPR_INDEX, li_int, li_int, -1, 0, 0, -1, 0, 0); + ix_idx_bool := alloc_node(EXPR_INDEX, li_bool, li_int, -1, 0, 0, -1, 0, 0); + ix_ref := alloc_node(EXPR_UNARY, li_int, -1, UOP_REF, 0, 0, -1, 0, 0); + ix_idx_ptr := alloc_node(EXPR_INDEX, ix_ref, li_int, -1, 0, 0, -1, 0, 0); + ix_range := alloc_node(EXPR_RANGE, li_int, li_int, -1, 0, 0, -1, 0, 0); + ix_idx_arr_range := alloc_node(EXPR_INDEX, ix_arr0, ix_range, -1, 0, 0, -1, 0, 0); + ix_idx_str_range := alloc_node(EXPR_INDEX, li_str, ix_range, -1, 0, 0, -1, 0, 0); + ix_idx_arr_oob := alloc_node(EXPR_INDEX, ix_arr3, li_int, -1, 0, 0, -1, 0, 0); + ix_idx_range_oob := alloc_node(EXPR_INDEX, ix_arr3, ix_range, -1, 0, 0, -1, 0, 0); + total = total + 1; fails = fails + ts_check("idx.node_kinds", + (ast_kind(ix_idx_arr) == EXPR_INDEX && ast_kind(ix_arr3) == EXPR_ARRAY && ast_b(ix_idx_arr) == li_int && + ast_kind(ix_range) == EXPR_RANGE && ast_kind(ix_idx_ptr) == EXPR_INDEX && ast_c(ix_ref) == UOP_REF), 1); + // ① 集等价(逐行):perm == 1 ⟺ 该行**不**属于「结果分支已处理集」{kind ARRAY, kind SLICE, ti == TI_STR} + ix_bad : ., mut = 0; + ix_n_rows : ., mut = 0; + ix_n_seq : ., mut = 0; + ix_n_str : ., mut = 0; + ix_n_named : ., mut = 0; + ix_n_prod : ., mut = 0; + ix_n_ref : ., mut = 0; + ix_n_ptr : ., mut = 0; + ix_n_dyn : ., mut = 0; + ix_i : ., mut = 0; + loop { + if ix_i >= g_type_count { break; } + ix_k := get_type_kind(ix_i); + ix_ak := iface_kind_of(ix_i); + ix_reach : ., mut = 1; // 1 = 到达兜底门(结果分支都不命中) + if ix_k == TYP_ARRAY || ix_k == TYP_SLICE || ix_i == TI_STR { ix_reach = 0; } + ix_perm := iface_permits(ix_ak, IP_INDEX); + ix_want : ., mut = 1; + if ix_reach == 1 { ix_want = 0; } + if ix_perm != ix_want { ix_bad = ix_bad + 1; } + if ix_k == TYP_ARRAY || ix_k == TYP_SLICE { ix_n_seq = ix_n_seq + 1; } + if ix_i == TI_STR { ix_n_str = ix_n_str + 1; } + if ix_ak == AK_NAMED { ix_n_named = ix_n_named + 1; } + if ix_ak == AK_PRODUCT { ix_n_prod = ix_n_prod + 1; } + if ix_ak == AK_REF { ix_n_ref = ix_n_ref + 1; } + if ix_ak == AK_PTR { ix_n_ptr = ix_n_ptr + 1; } + if ix_ak == AK_DYN { ix_n_dyn = ix_n_dyn + 1; } + ix_n_rows = ix_n_rows + 1; + ix_i = ix_i + 1; + } + total = total + 1; fails = fails + ts_check("idx.gate_deny_covers_fallback", ix_bad, 0); + // ③ 反真空:扫满全表 + 每类至少一行 + **STR 行恰 1 行**(`ti == TI_STR` ⇔ AK_STRING 的等价性 + // 依赖「TYP_BASE 行唯一分配点 = init_types」——该行数若变,本例先红) + total = total + 1; fails = fails + ts_check("idx.scan_coverage", + (ix_n_rows == g_type_count && ix_n_rows >= 15 && ix_n_seq >= 2 && ix_n_str == 1 && + ix_n_named >= 1 && ix_n_prod >= 1 && ix_n_ref >= 1 && ix_n_ptr >= 1 && ix_n_dyn >= 1 && + get_type_kind(ix_row_ptr) == TYP_PTR && get_type_kind(ix_row_ref) == TYP_REF && + get_type_kind(ix_row_tup) == TYP_TUPLE), 1); + // ② 端到端:结果分支保留(数组 → int、串 → int;含 OOB/F11 两个既有诊断仍发) + ix_m0 := g_diag_count; + ix_r_arr := infer_expr(ix_idx_arr); + total = total + 1; fails = fails + ts_check("idx.infer_arr_ok", ix_r_arr * 100 + (g_diag_count - ix_m0), TI_INT * 100); + ix_m1 := g_diag_count; + ix_r_str := infer_expr(ix_idx_str); + total = total + 1; fails = fails + ts_check("idx.infer_str_ok", ix_r_str * 100 + (g_diag_count - ix_m1), TI_INT * 100); + ix_m2 := g_diag_count; + ix_r_asc := infer_expr(ix_idx_arr_range); + ix_asc_ok : ., mut = 0; + if get_type_kind(ix_r_asc) == TYP_SLICE && get_type_data(ix_r_asc) == TI_INT { + if g_diag_count - ix_m2 == 0 { ix_asc_ok = 1; } + } + total = total + 1; fails = fails + ts_check("idx.infer_range_slice", ix_asc_ok, 1); + // 兜底门在判:非容器行 → TK01(码/结果逐字不变) + ix_m3 := g_diag_count; + ix_r_int := infer_expr(ix_idx_int); + total = total + 1; fails = fails + ts_check("idx.infer_int_denied_diag", g_diag_count - ix_m3, 1); + total = total + 1; fails = fails + ts_check("idx.infer_int_denied_code", ts_diag_code_at(ix_m3), EC_TK_INDEX); + total = total + 1; fails = fails + ts_check("idx.infer_int_denied_result", ix_r_int, TI_INT); + ix_m4 := g_diag_count; + infer_expr(ix_idx_bool); + total = total + 1; fails = fails + ts_check("idx.infer_bool_denied_diag", g_diag_count - ix_m4, 1); + total = total + 1; fails = fails + ts_check("idx.infer_bool_denied_code", ts_diag_code_at(ix_m4), EC_TK_INDEX); + ix_m5 := g_diag_count; + infer_expr(ix_idx_ptr); + total = total + 1; fails = fails + ts_check("idx.infer_ptr_denied_diag", g_diag_count - ix_m5, 1); + total = total + 1; fails = fails + ts_check("idx.infer_ptr_denied_code", ts_diag_code_at(ix_m5), EC_TK_INDEX); + // 登记面(现状宽松,本 Task 断言「不动」):串 range 索引静默 → unit(range 分支的非数组落空) + ix_m6 := g_diag_count; + ix_r_sr := infer_expr(ix_idx_str_range); + total = total + 1; fails = fails + ts_check("idx.infer_str_range_unit", ix_r_sr * 100 + (g_diag_count - ix_m6), TI_UNIT * 100); + // 结果规则原地保留的**正证据**(防「接线把分支合并掉」):F2 越界 + F11 切片界仍发 + ix_m7 := g_diag_count; + infer_expr(ix_idx_arr_oob); + total = total + 1; fails = fails + ts_check("idx.infer_arr_oob_diag", g_diag_count - ix_m7, 1); + total = total + 1; fails = fails + ts_check("idx.infer_arr_oob_code", ts_diag_code_at(ix_m7), EC_R_OOB); + ix_m8 := g_diag_count; + infer_expr(ix_idx_range_oob); + total = total + 1; fails = fails + ts_check("idx.infer_range_oob_diag", g_diag_count - ix_m8, 1); + total = total + 1; fails = fails + ts_check("idx.infer_range_oob_code", ts_diag_code_at(ix_m8), EC_TK_SLICE_BOUNDS); + + // --- R2 P2b Task 6:`res_type_node`/`res_call_type` 双份 TY→TI 映射合一(单表 ty_code_to_ti)--- + // 三层判据: + // ① 表侧逐码(含**表内不兜底**:TY_DEX_S/未知/负码 → -1); + // ② 两处基型分支**端到端**(`alloc_node(0,…,tv)` 构造基型节点 → 经真 res_type_node/res_call_type + // 比返回,= 改动前 inline 链的逐格转录); + // ③ 反真空哨兵(构造节点 kind/tv 回读正确;负节点先行拒绝)+ **两表唯一差异格** NEVER 的双向断言 + // (A → TI_NEVER、B → TI_UNIT——探针实测该格在调用位点**可达**,差异显式保留不静默合一)。 + // 非真空性(表变 ⇒ ②段必变)由实施报告的突变控制承担:表侧用例只能证明表对,证明不了线对。 + total = total + 1; fails = fails + ts_check("t6.ty_code_head", + (ty_code_to_ti(TY_INT) == TI_INT && ty_code_to_ti(TY_DEX) == TI_DEX && + ty_code_to_ti(TY_BOOL) == TI_BOOL && ty_code_to_ti(TY_STRING) == TI_STR && + ty_code_to_ti(TY_UNIT) == TI_UNIT), 1); + total = total + 1; fails = fails + ts_check("t6.ty_code_tail", + (ty_code_to_ti(TY_NEVER) == TI_NEVER && ty_code_to_ti(TY_CHAR) == TI_CHAR), 1); + // 码 7 = TY_GENERIC_PARAM 与 TI_DYN 的数值撞车格(spec §2.4 现场):现状两表原文原样保留 + total = total + 1; fails = fails + ts_check("t6.ty_code_dyn7", + (ty_code_to_ti(TI_DYN) == TI_DYN && ty_code_to_ti(TY_GENERIC_PARAM) == TI_DYN), 1); + // **表内不兜底**:TY_DEX_S(8) 不入表(计划注「不属两表并集」)+ 未知/负码 → -1 + total = total + 1; fails = fails + ts_check("t6.ty_code_domain", + (ty_code_to_ti(TY_DEX_S) == -1 && ty_code_to_ti(-1) == -1 && ty_code_to_ti(999) == -1), 1); + // ② 端到端:基型节点逐码(含 7 = dyn 码位——parser.cr:98 对 `dyn` 类型名正产此码) + t6_int := alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0); + t6_dex := alloc_node(0, 0, 0, 0, 0, TY_DEX, 0, 0, 0); + t6_bool := alloc_node(0, 0, 0, 0, 0, TY_BOOL, 0, 0, 0); + t6_str := alloc_node(0, 0, 0, 0, 0, TY_STRING, 0, 0, 0); + t6_unit := alloc_node(0, 0, 0, 0, 0, TY_UNIT, 0, 0, 0); + t6_never := alloc_node(0, 0, 0, 0, 0, TY_NEVER, 0, 0, 0); + t6_char := alloc_node(0, 0, 0, 0, 0, TY_CHAR, 0, 0, 0); + t6_dyn := alloc_node(0, 0, 0, 0, 0, TI_DYN, 0, 0, 0); + t6_dex_s := alloc_node(0, 0, 0, 0, 0, TY_DEX_S, 0, 0, 0); + t6_oob := alloc_node(0, 0, 0, 0, 0, 999, 0, 0, 0); + total = total + 1; fails = fails + ts_check("t6.node_kinds", + (ast_kind(t6_never) == 0 && ast_type_val(t6_never) == TY_NEVER && + ast_type_val(t6_dyn) == TI_DYN && ast_type_val(t6_dex_s) == TY_DEX_S), 1); + total = total + 1; fails = fails + ts_check("t6.R_codes", + (res_type_node(t6_int) == TI_INT && res_type_node(t6_dex) == TI_DEX && + res_type_node(t6_bool) == TI_BOOL && res_type_node(t6_str) == TI_STR && + res_type_node(t6_unit) == TI_UNIT && res_type_node(t6_never) == TI_NEVER && + res_type_node(t6_char) == TI_CHAR), 1); + total = total + 1; fails = fails + ts_check("t6.C_codes", + (res_call_type(t6_int, -1) == TI_INT && res_call_type(t6_dex, -1) == TI_DEX && + res_call_type(t6_bool, -1) == TI_BOOL && res_call_type(t6_str, -1) == TI_STR && + res_call_type(t6_unit, -1) == TI_UNIT && res_call_type(t6_char, -1) == TI_CHAR), 1); + // **两表唯一语义差**:NEVER 格(A 有 / B 无 ⇒ B 落 TI_UNIT)——差异不得被合一抹平 + total = total + 1; fails = fails + ts_check("t6.never_cell_diff", + (res_type_node(t6_never) == TI_NEVER && res_call_type(t6_never, -1) == TI_UNIT), 1); + // 两表**共有**的 7 码格(dyn):均 → TI_DYN(B2 探针:`x : dyn = 5; x.nosuch()` 的 N08 依赖本格) + total = total + 1; fails = fails + ts_check("t6.dyn_cell_both", + (res_type_node(t6_dyn) == TI_DYN && res_call_type(t6_dyn, -1) == TI_DYN), 1); + // 域外码(TY_DEX_S/未知)两表均落 TI_UNIT(= 改动前的尾回落;表内无兜底) + total = total + 1; fails = fails + ts_check("t6.dex_s_oob_unit", + (res_type_node(t6_dex_s) == TI_UNIT && res_call_type(t6_dex_s, -1) == TI_UNIT && + res_type_node(t6_oob) == TI_UNIT && res_call_type(t6_oob, -1) == TI_UNIT), 1); + total = total + 1; fails = fails + ts_check("t6.neg_node_unit", + (res_type_node(-1) == TI_UNIT && res_call_type(-1, -1) == TI_UNIT), 1); + + // ═══ R2 P3 Task 0:引擎展开层(named/struct/enum/generic-apply → 结构项)═══ + // 判据面 = ① 正向形态:字段**声明序** / 变体数 / 泛型实参代入;② **名义不变量双钉** + // (等价面零漂移:展开项结构相等 ∧ 桥接项 ty_equiv 仍 -1 ∧ type_equal 仍 false); + // ③ 缺口上抛(不可展开 → -1,**不得**静默判否);④ 展开缓存语义(命中计数 / 失败不入表); + // ⑤ 域/模式项在引擎穷尽性上的**端到端**前哨(Task 3 判据的种子)。 + // 夹具全部人造行(本通道不经 parser/check_all):声明表照 parser 的写槽序落,类型节点照 + // parse_type 的产物形态构造(基型节点 kind=0 + type_val=TY_*;EXPR_IDENT = 泛型形参/ + // 命名类型;EXPR_ARRAY = `[T;N]`)。 + init_types(); // 干净起点(类型行号空间复位;原生 9 行在前) + ty_budget_reset(200000); + + // 夹具 ①:struct UnfBoxU[T] { val: T }(泛型字段 = 形参 ⇒ 实参代入判据)+ 两个应用行 + // 形参照 collect_decls(checker.cr:941-950)登记为符号——**生产路径 struct 泛型形参必在 + // 符号表**(res_type_node 经 find_gsym 解析形参名),本通道照此登记 = 走真实解析路径。 + unf_gp := str_intern("UnfTGparamU"); + def_sym(unf_gp, SYM_TYPE, alloc_type(TYP_GENERIC_PARAM, unf_gp, 0), -1); + unf_box_sa := add_struct("UnfBoxU"); + w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1); + w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, unf_gp); + si_set_field_name(unf_box_sa, 0, str_intern("val")); + si_set_field_type(unf_box_sa, 0, 0); // 裸码槽 = parser 对非基型的塌缩值(0 = TY_INT;本层不读) + si_set_field_type_node(unf_box_sa, 0, + alloc_node(EXPR_IDENT, -1, -1, -1, unf_gp, 0, -1, 0, 0)); + w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1); + unf_box_ti := alloc_named_type(str_intern("UnfBoxU")); + // 应用行 `UnfBoxU[int]` / `UnfBoxU[string]`(g_gen_apply_data: [count, arg]) + grow_gen_apply_data(g_gen_apply_data_count + 2); + unf_gas1 := g_gen_apply_data_count; + w64(g_gen_apply_data, unf_gas1 * 8, 1); + w64(g_gen_apply_data, (unf_gas1 + 1) * 8, TI_INT); + g_gen_apply_data_count = unf_gas1 + 2; + unf_ga_int := alloc_type(TYP_GENERIC_APPLY, unf_box_ti, unf_gas1); + grow_gen_apply_data(g_gen_apply_data_count + 2); + unf_gas2 := g_gen_apply_data_count; + w64(g_gen_apply_data, unf_gas2 * 8, 1); + w64(g_gen_apply_data, (unf_gas2 + 1) * 8, TI_STR); + g_gen_apply_data_count = unf_gas2 + 2; + unf_ga_str := alloc_type(TYP_GENERIC_APPLY, unf_box_ti, unf_gas2); + unf_ta := sh_struct_term(unf_ga_int); + unf_tb := sh_struct_term(unf_ga_str); + total = total + 1; fails = fails + ts_check("unf.ga_arg_subst", + (tt_a(unf_ta) == AK_PRODUCT && tt_c(unf_ta) == tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil()) && + tt_c(unf_tb) == tt_cons(tt_atom(AK_STRING, TI_STR, -1), tt_nil()) && unf_ta != unf_tb), 1); + // 未应用形态:泛型形参保持**名义**(res_type_node 取形参行 ⇒ AK_NAMED)——不得凭空代入 int + unf_tbare := sh_struct_term(unf_box_ti); + total = total + 1; fails = fails + ts_check("unf.ga_unapplied_nominal", + (unf_tbare >= 0 && tt_a(unf_tbare) == AK_PRODUCT && tt_a(tt_a(tt_c(unf_tbare))) != AK_INT), 1); + + // 夹具 ②:struct UnfP1U { a: int, b: string }(字段**声明序**;含基型两种) + unf_p1_sa := add_struct("UnfP1U"); + si_set_field_name(unf_p1_sa, 0, str_intern("a")); + si_set_field_type(unf_p1_sa, 0, TY_INT); + si_set_field_type_node(unf_p1_sa, 0, alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0)); + si_set_field_name(unf_p1_sa, 1, str_intern("b")); + si_set_field_type(unf_p1_sa, 1, TY_STRING); + si_set_field_type_node(unf_p1_sa, 1, alloc_node(0, 0, 0, 0, 0, TY_STRING, 0, 0, 0)); + w64(g_structs, unf_p1_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 2); + unf_p1_ti := alloc_named_type(str_intern("UnfP1U")); + unf_tp1 := sh_struct_term(unf_p1_ti); + total = total + 1; fails = fails + ts_check("unf.struct_field_order", + (tt_a(unf_tp1) == AK_PRODUCT && + tt_c(unf_tp1) == tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_cons(tt_atom(AK_STRING, TI_STR, -1), tt_nil()))), 1); + // 夹具 ③:struct UnfP2U { x: [int;2] }——字段**裸码槽 = 0(= TY_INT)而类型节点 = 数组**: + // 「只读裸码槽」的实现会把该字段静默误判为 int(本用例即其守门;裸码塌缩 = parser 事实) + unf_p2_sa := add_struct("UnfP2U"); + unf_arrn := alloc_node(EXPR_ARRAY, alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0), -1, -1, 2, 0, -1, 0, 0); + si_set_field_name(unf_p2_sa, 0, str_intern("x")); + si_set_field_type(unf_p2_sa, 0, 0); + si_set_field_type_node(unf_p2_sa, 0, unf_arrn); + w64(g_structs, unf_p2_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1); + unf_p2_ti := alloc_named_type(str_intern("UnfP2U")); + unf_tp2 := sh_struct_term(unf_p2_ti); + total = total + 1; fails = fails + ts_check("unf.struct_field_node_not_code", + (tt_a(unf_tp2) == AK_PRODUCT && tt_a(tt_a(tt_c(unf_tp2))) == AK_SEQUENCE), 1); + + // 夹具 ④:两个**同形不同名** struct(UnfS1U{a:int} / UnfS2U{a:int})——本 Task 的风险边界, + // 三断 = 双钉:① 展开项(满足面)**结构相等**(同形 ⇒ 同项 = 满足判定的设计意图); + // ② 桥接项(等价面)仍 -1(AK_NAMED 原子不展开,**不得**当 0/1);③ type_equal 仍 false + // 且回落 legacy 恰 +1(不静默)。 + unf_s1_ti := ts_unf_mk_int_struct("UnfS1U"); + unf_s2_ti := ts_unf_mk_int_struct("UnfS2U"); + unf_e1 := sh_struct_term(unf_s1_ti); + unf_e2 := sh_struct_term(unf_s2_ti); + total = total + 1; fails = fails + ts_check("unf.nominal_structural_equal", + (unf_e1 >= 0 && unf_e2 >= 0 && ty_equiv(unf_e1, unf_e2) == 1), 1); + // **R2 P5 Task 3 重钉**(翻转向量表:`unf.nominal_equiv_atomic_unknown` → + // `unf.nominal_equiv_atomic_decided`):命名面接引擎身份链后,**异名同形**两个命名型的 + // 桥接项判 **0 = 确定不同**(旧断 -1 = 未覆盖面;legacy 同结论 = false ⇒ 行为同值, + // 差异 = 引擎自决而非回落)。展开项(满足面)结构相等断(上一例)逐字未动——「等价面 + // 不展开」的边界仍由该例把住(同形不同名 ⇒ 桥接项 0,展开项 1)。 + total = total + 1; fails = fails + ts_check("unf.nominal_equiv_atomic_decided", + ty_equiv(sh_term_of_ti(unf_s1_ti), sh_term_of_ti(unf_s2_ti)), 0); + // T3 重钉(零回落)→ **T4 终态**:回落计数已随 legacy 删除 ⇒ 断言 = 「判 false + 零 + // 未知诊断(ICE04 缺席 = 引擎真判 0,而非未知被静默当 false)」。 + unf_d0 := g_diag_count; + unf_eqf : ., mut = 0; + if !type_equal(unf_s1_ti, unf_s2_ti) { + if g_diag_count == unf_d0 { unf_eqf = 1; } + } + total = total + 1; fails = fails + ts_check("unf.nominal_type_equal_false", unf_eqf, 1); + + // 夹具 ⑤:enum UnfColorU { UnfRedU, UnfGreenU, UnfBlueU }(tag-only,3 变体) + unf_col_ei := add_enum("UnfColorU"); + ei_set_variant_name(unf_col_ei, 0, str_intern("UnfRedU")); + ei_set_variant_name(unf_col_ei, 1, str_intern("UnfGreenU")); + ei_set_variant_name(unf_col_ei, 2, str_intern("UnfBlueU")); + w64(g_enums, unf_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3); + unf_col_ti := alloc_named_type(str_intern("UnfColorU")); + unf_dom := sh_enum_domain_term(unf_col_ti); + total = total + 1; fails = fails + ts_check("unf.enum_domain_count", + (unf_dom >= 0 && ts_unf_union_leaves(unf_dom) == 3), 1); + unf_vr := sh_variant_term(unf_col_ti, str_intern("UnfRedU")); + unf_vg := sh_variant_term(unf_col_ti, str_intern("UnfGreenU")); + unf_vb := sh_variant_term(unf_col_ti, str_intern("UnfBlueU")); + total = total + 1; fails = fails + ts_check("unf.enum_variant_in_domain", + (ty_sub(unf_vr, unf_dom) == 1 && ty_sub(unf_vg, unf_dom) == 1 && ty_sub(unf_vb, unf_dom) == 1 && + sh_variant_term(unf_col_ti, str_intern("UnfNoSuchVariantU")) == -1), 1); + // 变体身份**含枚举名**(引擎只比 a/c 两槽 ⇒ 身份必须入参数链):异枚举**同名变体**不得被判等价 + unf_dup_ei := add_enum("UnfDupU"); + ei_set_variant_name(unf_dup_ei, 0, str_intern("UnfRedU")); + w64(g_enums, unf_dup_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1); + unf_dup_ti := alloc_named_type(str_intern("UnfDupU")); + unf_vdup := sh_variant_term(unf_dup_ti, str_intern("UnfRedU")); + total = total + 1; fails = fails + ts_check("unf.enum_variant_identity_scoped", + (unf_vdup >= 0 && ty_sub(unf_vdup, unf_vr) != 1 && ty_sub(unf_vr, unf_vdup) != 1), 1); + // 空枚举:域 = ⊥(无值可取——**登记语义**:空域在补集语义下恒穷尽,Task 3 须显式裁决) + unf_emp_ei := add_enum("UnfEmptyU"); + w64(g_enums, unf_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0); + unf_emp_ti := alloc_named_type(str_intern("UnfEmptyU")); + total = total + 1; fails = fails + ts_check("unf.enum_empty_domain_bot", tt_tag(sh_enum_domain_term(unf_emp_ti)), TT_BOT); + + // 夹具 ⑥:enum UnfOptU[T] { UnfNoneU, UnfSomeU(T) }(tag + payload 混合)——域/模式项在 + // 引擎穷尽性上的**端到端**前哨(Task 3 判据的种子)。payload 槽照 parser 落裸码(非基型 + // 塌缩为 0 ⇒ 本层**不读**它:payload 不入变体项,见 ty_shadow.cr 展开段未覆盖面注记)。 + unf_opt_ei := add_enum("UnfOptU"); + w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1); + w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("UnfTOptU")); + ei_set_variant_name(unf_opt_ei, 0, str_intern("UnfNoneU")); + ei_set_variant_name(unf_opt_ei, 1, str_intern("UnfSomeU")); + ei_set_variant_type_count(unf_opt_ei, 1, 1); + ei_set_variant_type(unf_opt_ei, 1, 0, TY_INT); // 裸码槽(parser 事实;本层不读) + w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2); + unf_opt_ti := alloc_named_type(str_intern("UnfOptU")); + unf_odom := sh_enum_domain_term(unf_opt_ti); + unf_onone := sh_variant_term(unf_opt_ti, str_intern("UnfNoneU")); + unf_osome := sh_variant_term(unf_opt_ti, str_intern("UnfSomeU")); + total = total + 1; fails = fails + ts_check("unf.exhaust_cover_all", + ty_exhaustive(unf_odom, tt_cons(unf_onone, tt_cons(unf_osome, tt_nil()))), 1); + total = total + 1; fails = fails + ts_check("unf.exhaust_missing_one", + ty_exhaustive(unf_odom, tt_cons(unf_onone, tt_nil())), 0); + // 缺变体 witness:不可空且 ⊆ 缺失变体。**登记**:原始 witness = 各析取支之并(**含空析取支** + // ——norm 不做空支净化)⇒ Task 3 若当「具体反例值」用,须先取有住户的支(或引擎加净化)。 + unf_w2 := ty_exhaust_witness(unf_odom, tt_cons(unf_onone, tt_nil())); + total = total + 1; fails = fails + ts_check("unf.exhaust_witness_subset", + (unf_w2 >= 0 && ty_inhabited(unf_w2) == 1 && ty_sub(unf_w2, unf_osome) == 1), 1); + + // 展开缓存:新 ti 恰入表 1 条;二次调用恰命中 1 次且返回**同项**;**失败不缓存**(-1 不入表) + unf_fresh := ts_unf_mk_int_struct("UnfCacheU"); + unf_ce0 := sh_unf_entries(); + unf_ch0 := sh_unf_hits(); + unf_ct1 := sh_struct_term(unf_fresh); + unf_ce1 := sh_unf_entries(); + unf_ct2 := sh_struct_term(unf_fresh); + unf_fail_e0 := sh_unf_entries(); + sh_struct_term(g_type_count + 7); + total = total + 1; fails = fails + ts_check("unf.cache_hit_and_no_neg_cache", + ((unf_ce1 - unf_ce0) == 1 && (sh_unf_hits() - unf_ch0) == 1 && unf_ct2 == unf_ct1 && + (sh_unf_entries() - unf_fail_e0) == 0), 1); + + // 缺口上抛(**不得静默判否**):行号越界 / 未声明的名字行 / 裸泛型形参行 / 非枚举变体名 → -1 + unf_plain_ti := alloc_named_type(str_intern("UnfNoDeclU")); + unf_loose_gp := alloc_type(TYP_GENERIC_PARAM, str_intern("UnfLooseTU"), 0); + total = total + 1; fails = fails + ts_check("unf.neg_paths", + (sh_struct_term(g_type_count + 7) == -1 && sh_enum_domain_term(g_type_count + 7) == -1 && + sh_struct_term(-1) == -1 && sh_variant_term(-1, 0) == -1 && sh_variant_term(unf_col_ti, -1) == -1 && + sh_struct_term(unf_plain_ti) == -1 && sh_enum_domain_term(unf_plain_ti) == -1 && + sh_struct_term(unf_loose_gp) == -1), 1); + + // 接口面负键守门(P3b Task 0 后:`iface_satisfies` 已交付,本用例 = **域外输入守卫**—— + // 形状项构造仍为 Task 6 占位(-1),负键/未知名键一律三态 -1,**不得**被消费方当 0/1 用) + total = total + 1; fails = fails + ts_check("unf.iface_stub_three_state", + (sh_iface_shape_term(-1) == -1 && iface_satisfies(-1, 0) == -1 && iface_satisfies(TI_INT, 9999) == -1), 1); + + // ═══ R2 P3 Task 1:定长退役收口(array/slice 方向 + 变型表 + N 读取面守门)═══ + // 判据面 = ① 方向:`[T;N] <: [T]`(固定 → 视图 = 拓宽,放行)/ `[T] ⊄ [T;N]`(视图 → 固定, + // 无法证 len==N ⇒ 拒;**确定 0 而非 -1**);② 变型:只读协变通过 / 可写不变拒绝(AK_REF + // 槽语义)+ mut 标记成为判定维度(`&T` ≢ `&mut T` 双向);③ **N 读取面守门**:身份路径与 + // 子类型路径零 N(`[int;3]` 与 `[int;4]` 的项除 b 槽外逐位相同),拒绝**唯一**来源 = + // 常量档约束;④ 嵌套位(指针元素 / 元组字段 / 泛型实参 / 数组元素)逐位同律;⑤ 动态档登记 + // (视图运行期长度不可证 ⇒ 决定 0,不得以 -1 冒充决定)。 + // 层归属(计划编排列「方向 4」于引擎步内):固定性存于序列项 **b 槽(标注,不入引擎比较)** + // ⇒ 方向**不在**引擎 ty_sub 面,而在 checker 的 array_len_constraint_ok(参序 =(源,目标)) + // ——与 P2a「N 迁出身份、拒绝由常量档承担」同一构造;故本组的方向断言直接打该函数与站点 + // 三态包装 type_compat_strict(引擎侧只保留「N 不入身份/子类型」的守门)。 + ty_budget_reset(200000); + t1_arr3 := alloc_type(TYP_ARRAY, TI_INT, 3); + t1_arr4 := alloc_type(TYP_ARRAY, TI_INT, 4); + t1_sl := alloc_type(TYP_SLICE, TI_INT, 0); + t1_sl2 := alloc_type(TYP_SLICE, TI_INT, 0); // 另一行同形切片:方向面不得靠 ti 同一性 + // ① 方向(顶层):固定→视图 放行;视图→固定 拒(0 = 决定,非 -1);视图→视图 同元素放行 + total = total + 1; fails = fails + ts_check("t1.dir_arr_to_slice_ok", + array_len_constraint_ok(t1_arr3, t1_sl), 1); + total = total + 1; fails = fails + ts_check("t1.dir_slice_to_arr_reject", + array_len_constraint_ok(t1_sl, t1_arr3), 0); + total = total + 1; fails = fails + ts_check("t1.dir_slice_slice_ok", + array_len_constraint_ok(t1_sl, t1_sl2), 1); + // ② N 不入身份(同固定性异 N):引擎等价 1 ∧ 常量档拒 0(双钉 = 「N 迁移」不变量) + total = total + 1; fails = fails + ts_check("t1.dir_fixed_two_lengths_identity", + (ty_equiv(sh_term_of_ti(t1_arr3), sh_term_of_ti(t1_arr4)) == 1 && + array_len_constraint_ok(t1_arr4, t1_arr3) == 0), 1); + // ③ 嵌套位逐位同律(负控:反向拓宽必须仍放行——防「一律拒绝」的退化实现) + t1_pt_sl := alloc_type(TYP_PTR, t1_sl, 0); + t1_pt_arr := alloc_type(TYP_PTR, t1_arr3, 0); + total = total + 1; fails = fails + ts_check("t1.dir_nested_ptr", + (array_len_constraint_ok(t1_pt_sl, t1_pt_arr) == 0 && array_len_constraint_ok(t1_pt_arr, t1_pt_sl) == 1), 1); + t1_out_sl := alloc_type(TYP_ARRAY, t1_sl, 2); // [[int];2] + t1_out_arr := alloc_type(TYP_ARRAY, t1_arr3, 2); // [[int;3];2] + total = total + 1; fails = fails + ts_check("t1.dir_nested_elem", + (array_len_constraint_ok(t1_out_sl, t1_out_arr) == 0 && array_len_constraint_ok(t1_out_arr, t1_out_sl) == 1), 1); + grow_gen_apply_data(g_gen_apply_data_count + 4); + t1_tp_s1 := g_gen_apply_data_count; + w64(g_gen_apply_data, t1_tp_s1 * 8, TI_INT); + w64(g_gen_apply_data, (t1_tp_s1 + 1) * 8, t1_sl); + t1_tp_s2 := t1_tp_s1 + 2; + w64(g_gen_apply_data, t1_tp_s2 * 8, TI_INT); + w64(g_gen_apply_data, (t1_tp_s2 + 1) * 8, t1_arr3); + g_gen_apply_data_count = t1_tp_s2 + 2; + t1_tup_sl := alloc_type(TYP_TUPLE, 2, t1_tp_s1); // (int, [int]) + t1_tup_arr := alloc_type(TYP_TUPLE, 2, t1_tp_s2); // (int, [int;3]) + total = total + 1; fails = fails + ts_check("t1.dir_nested_tuple", + (array_len_constraint_ok(t1_tup_sl, t1_tup_arr) == 0 && array_len_constraint_ok(t1_tup_arr, t1_tup_sl) == 1), 1); + grow_gen_apply_data(g_gen_apply_data_count + 4); + t1_ga_s1 := g_gen_apply_data_count; + w64(g_gen_apply_data, t1_ga_s1 * 8, 1); + w64(g_gen_apply_data, (t1_ga_s1 + 1) * 8, t1_sl); + t1_ga_s2 := t1_ga_s1 + 2; + w64(g_gen_apply_data, t1_ga_s2 * 8, 1); + w64(g_gen_apply_data, (t1_ga_s2 + 1) * 8, t1_arr3); + g_gen_apply_data_count = t1_ga_s2 + 2; + t1_base := alloc_type(TYP_NAMED, 1203, 0); // 人造基型行(裸分配,防假键污染) + t1_ga_sl := alloc_type(TYP_GENERIC_APPLY, t1_base, t1_ga_s1); + t1_ga_arr := alloc_type(TYP_GENERIC_APPLY, t1_base, t1_ga_s2); + total = total + 1; fails = fails + ts_check("t1.dir_nested_genapply", + (array_len_constraint_ok(t1_ga_sl, t1_ga_arr) == 0 && array_len_constraint_ok(t1_ga_arr, t1_ga_sl) == 1), 1); + // ④ 站点三态(专属措辞 = -1 路):拓宽 1;视图→固定 -1(身份放行、方向拒);对称核 = 现状 + total = total + 1; fails = fails + ts_check("t1.strict_widen_ok", + type_compat_strict(t1_arr3, t1_sl), 1); + total = total + 1; fails = fails + ts_check("t1.strict_view_to_fixed", + type_compat_strict(t1_sl, t1_arr3), -1); + total = total + 1; fails = fails + ts_check("t1.strict_sym_no_direction", + (type_compat_sym(t1_sl, t1_arr3) == 1 && type_compat_sym(t1_arr3, t1_sl) == 1), 1); + // ⑤ 变型:只读协变通过 / 可写不变拒绝(AK_REF 槽 1 条件变型);表外构造子默认不变 + t1_u := tt_union(tt_atom(AK_INT, TI_INT, -1), tt_atom(AK_STRING, TI_STR, -1)); // int ∪ string + t1_rv_ro := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil()))); + t1_rv_ro_w := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(0), tt_cons(t1_u, tt_nil()))); + t1_rv_mut := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(1), tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil()))); + t1_rv_mut_w := tt_atom(AK_REF, -1, tt_cons(sh_ref_mut_marker(1), tt_cons(t1_u, tt_nil()))); + total = total + 1; fails = fails + ts_check("t1.var_readonly_covariant", + (ty_sub(t1_rv_ro, t1_rv_ro_w) == 1 && ty_sub(t1_rv_ro_w, t1_rv_ro) == 0), 1); + // 不变槽非同形的判定**语义** = 「不是子类型」;引擎回 **-1**(未覆盖面登记——不变槽的 + // 「确定不等价 ⇒ 0」加强需先有「链元素皆类型项」不变量,见 type_engine.cr 注记)⇒ 断 + // 「两向皆不得判 1」= 不变性的可执行内容;生产面(checker type_equal)在此回落 legacy + // 得 false = 实拒(pE 探针实测 rc=1)。 + total = total + 1; fails = fails + ts_check("t1.var_mut_invariant_reject", + (ty_sub(t1_rv_mut, t1_rv_mut_w) != 1 && ty_sub(t1_rv_mut_w, t1_rv_mut) != 1), 1); + total = total + 1; fails = fails + ts_check("t1.var_seq_elem_invariant", + ty_sub(tt_atom(AK_SEQUENCE, -1, tt_cons(tt_atom(AK_INT, TI_INT, -1), tt_nil())), + tt_atom(AK_SEQUENCE, -1, tt_cons(t1_u, tt_nil()))) != 1, 1); + // ⑥ mut 标记入链 = 判定维度(走真实桥接):`&T` ≢ `&mut T` 双向;同 mut 同元素仍等价 + t1_ref_ro := alloc_type(TYP_REF, TI_INT, 0); + t1_ref_ro2 := alloc_type(TYP_REF, TI_INT, 0); + t1_ref_mut := alloc_type(TYP_REF, TI_INT, 1); + total = total + 1; fails = fails + ts_check("t1.var_mut_marker_dimension", + (ty_equiv(sh_term_of_ti(t1_ref_ro), sh_term_of_ti(t1_ref_mut)) == 0 && + ty_equiv(sh_term_of_ti(t1_ref_mut), sh_term_of_ti(t1_ref_ro)) == 0 && + ty_equiv(sh_term_of_ti(t1_ref_ro), sh_term_of_ti(t1_ref_ro2)) == 1 && + tt_b(tt_a(tt_c(sh_term_of_ti(t1_ref_mut)))) == 1), 1); + // ⑦ N 读取面守门:身份路径(ty_equiv)+ **子类型路径(ty_sub)**零 N;唯一拒绝来源 = 常量档 + total = total + 1; fails = fails + ts_check("t1.n_face_gate", + (type_equal(t1_arr3, t1_arr4) && + ty_sub(sh_term_of_ti(t1_arr3), sh_term_of_ti(t1_arr4)) == 1 && + array_len_constraint_ok(t1_arr4, t1_arr3) == 0), 1); + // ⑧ 动态档登记:切片运行期长度不可证 ⇒ 视图→固定是**决定**(0),不得回 -1 冒充未知; + // 异长固定档同理(常量档拒绝也在决定面)。 + total = total + 1; fails = fails + ts_check("t1.dyn_tier_decided", + (array_len_constraint_ok(t1_sl, t1_arr3) == 0 && array_len_constraint_ok(t1_sl2, t1_arr4) == 0 && + array_len_constraint_ok(t1_arr3, t1_arr4) == 0), 1); + // ⑨ **嵌套位**的身份不变量 + 站点三态(本批实测发现的回归面:序列项的固定性位 b 会经 + // 嵌套元素位的**节点同一性**泄进身份 ⇒ 必须由引擎的「忽略 b 的序列项相等」挡住—— + // type_engine.cr 的 tt_type_elem_same/tt_seq_same)。断三件事: + // ① 嵌套 N:`type_equal([[int;3];2], [[int;4];2])` 仍 true **且不触发 unknown 回落** + // (引擎自决 = 身份路径零 N 的强式;旧实现靠 legacy 回落兜底,措辞随之退化); + // ② 嵌套固定性同样**不入身份**(`ty_equiv` 两向 1); + // ③ 站点层:嵌套 视图→固定 = -1(专属措辞路)、嵌套 固定→视图 = 1(拓宽保持)。 + t1_out_arr4 := alloc_type(TYP_ARRAY, t1_arr4, 2); // [[int;4];2] + // T4 终态:断言从「零回落计数」改为「零未知诊断」(ICE04 缺席 = 引擎真判身份路径) + t1_d0 := g_diag_count; + t1_nest_ok : ., mut = 0; + if type_equal(t1_out_arr, t1_out_arr4) { if g_diag_count == t1_d0 { t1_nest_ok = 1; } } + total = total + 1; fails = fails + ts_check("t1.n_face_gate_nested", + (t1_nest_ok == 1 && array_len_constraint_ok(t1_out_arr4, t1_out_arr) == 0), 1); + total = total + 1; fails = fails + ts_check("t1.fixedness_not_identity_nested", + ty_equiv(sh_term_of_ti(t1_out_sl), sh_term_of_ti(t1_out_arr)), 1); + total = total + 1; fails = fails + ts_check("t1.dir_nested_strict", + (type_compat_strict(t1_out_sl, t1_out_arr) == -1 && type_compat_strict(t1_out_arr, t1_out_sl) == 1), 1); + + // ═══ R2 P3 Task 3:match 穷尽性真判定(补集空性 + 具体变体反例)═══ + // 判据面 = ① 引擎三态:全覆盖 / 缺一 / 无臂 / 通配吸收 / 单变体;② **反例的具体值** = + // 缺失**变体名**(逐变体覆盖位命名——引擎 witness 原始形态含空析取支,与缺失变体按 + // ty_equiv 不等,本组第 3 例把该事实与消费点命名一并钉死);③ 域守卫与不可映射守卫 + // (-1 = 不判,不得当 0/1);④ 空枚举域 = 空洞穷尽(登记语义,与计划原文「= 0」的偏差 + // 见任务报告);⑤ payload 变体混合 / 限定名解析 / 重复臂 / 泛型应用行。 + // 夹具照 Task 0 先例人工落行(写槽序 = parser 的;payload 裸码槽照 parser 事实落)。 + init_types(); + ty_budget_reset(200000); + t3_col_ei := add_enum("T3Color"); + ei_set_variant_name(t3_col_ei, 0, str_intern("T3Red")); + ei_set_variant_name(t3_col_ei, 1, str_intern("T3Green")); + ei_set_variant_name(t3_col_ei, 2, str_intern("T3Blue")); + w64(g_enums, t3_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3); + t3_col_ti := alloc_named_type(str_intern("T3Color")); + t3_r := sh_match_variant_term(t3_col_ti, 0); + t3_g := sh_match_variant_term(t3_col_ti, 1); + t3_b := sh_match_variant_term(t3_col_ti, 2); + // ① 全覆盖 = 1(模式项与域项同一构造 ⇒ 补集空) + total = total + 1; fails = fails + ts_check("t3.cover_all", + sh_match_exhaustive(t3_col_ti, tt_cons(t3_r, tt_cons(t3_g, tt_cons(t3_b, tt_nil()))), 0), 1); + // ② 缺一变体 = 0;反例 = **缺失变体名**(引擎判据 + 覆盖位命名两路并用) + t3_ab := tt_cons(t3_r, tt_cons(t3_g, tt_nil())); + total = total + 1; fails = fails + ts_check("t3.missing_one_verdict", + sh_match_exhaustive(t3_col_ti, t3_ab, 0), 0); + t3_w := ty_exhaust_witness(sh_enum_domain_term(t3_col_ti), t3_ab); + t3_cx := sh_match_first_missing(sh_match_bit(0) + sh_match_bit(1), ei_variant_count(t3_col_ei)); + t3_cx_named : ., mut = 0; + if t3_cx == 2 { + if str_eq(istr_get(ei_variant_name(t3_col_ei, t3_cx)), "T3Blue") != 0 { t3_cx_named = 1; } + } + // 引擎 witness 原始形态 = 含空析取支的并(Task 0 §5-②):本批**实测**(三态)= + // ty_sub(w, 缺失变体) = 1(w ⊆ b)∧ ty_sub(反方向) = **-1**(未覆盖面——空析取支让反向 + // 结构比较落空)∧ ty_equiv = **-1**(**不是 0**:Task 0 报告记「不等」为 `== 0`,实测为 + // -1 = 未覆盖面,此处按实测钉死;-1 不得当 0 用的三态纪律在此具体化)∧ 可空 = 1。 + // 故「反例给具体值」由**覆盖位命名**承担(引擎 witness 项在其上不可作显示名)。 + total = total + 1; fails = fails + ts_check("t3.counterexample_named_variant", + (t3_w >= 0 && ty_inhabited(t3_w) == 1 && ty_sub(t3_w, t3_b) == 1 && t3_cx_named), 1); + // ─── R2 P5 Task 3b 翻转钉(原合取式 `... && ty_equiv(t3_w, t3_b) == -1` 的拆解)─── + // 变体项 = AK_SUM 链 [枚举名令牌, 变体名令牌](两枚**身份令牌**)⇒ 不变槽三态落地后, + // 令牌按节点同一性判**确定不同 0**(改前 = -1 + 未覆盖面)。t3_w = 含两枚**空析取支** + // 的并:q1 = R∩¬R∩¬G、q2 = G∩¬R∩¬G(皆 ∅)、q3 = B∩¬R∩¬G(= B)。翻转链(实测): + // `ty_sub(B, q1)` 改前 -1(lit_implies(B,R) 走令牌不变槽 ⇒ 未知)→ 改后 0; + // ⇒ 外层并分支 `x := sub_cover(B, q1∪q2)` 改前 -1、改后 0; + // ⇒ 整式 `ty_sub(B, t3_w)` 改前 **-1**(x = -1 上抛)改后 **0**(x = 0 ∧ y = 0)。 + // ⚠ **登记面(T3b 揭开、R2 P6 Task 2 已收口)**:`ty_sub(B, q3) = 0` —— `sub_cover` 的 + // product 规则(须逐字面覆盖)撞上 ¬ 面「同类原子 ⇒ 不判交」规则(`lit_implies(B, ¬R)`: + // lp/¬x 同类 AK_SUM ⇒ 原 `ak_disjoint` 回 0 ⇒ 判「不含」)——语义上 `B ⊆ ¬R` **成立** + // (异变体值集不交)。T3b 只是撤掉了原先掩盖它的 -1(把该过判暴露为可观测);**Task 2 + // 在 `lit_implies` 的 ¬ 面加「同类 ⇒ 身份判据」**(`atom_same_class_disjoint`,域 = AK_SUM + // 变体项)⇒ `B ⊆ ¬R`/`B ⊆ ¬G` 转 1 ⇒ 下两例 + 单点最小复现**转 1 并重钉**(本批唯一的 + // 行为方向变化;域外面与 `AK_NAMED` 维持原语义,见 type_engine.cr 该函数头注)。 + total = total + 1; fails = fails + ts_check("t3.witness_reverse_over_claim", ty_sub(t3_b, t3_w), 1); + total = total + 1; fails = fails + ts_check("t3.witness_equiv_over_claim", ty_equiv(t3_w, t3_b), 1); + // 同一收口面的**最小复现**(单点断言:`B ⊆ B ∩ ¬R` ⟺ `B ⊆ ¬R`) + total = total + 1; fails = fails + ts_check("t3.product_neg_over_claim", + ty_sub(t3_b, tt_inter(t3_b, tt_not(t3_r))), 1); + // ③ 通配/绑定 = ⊤:吸收一切(引擎侧 ⊤ 语义;checker 侧映射见 EXPR_MATCH) + total = total + 1; fails = fails + ts_check("t3.wildcard_absorbs", + sh_match_exhaustive(t3_col_ti, tt_cons(tt_top(), tt_nil()), 0), 1); + // ④ 单变体枚举:有臂 = 1 / 无臂 = 0(空链 ⇒ 补集 = 全域) + t3_only_ei := add_enum("T3Only"); + ei_set_variant_name(t3_only_ei, 0, str_intern("T3One")); + w64(g_enums, t3_only_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1); + t3_only_ti := alloc_named_type(str_intern("T3Only")); + t3_one := sh_match_variant_term(t3_only_ti, 0); + total = total + 1; fails = fails + ts_check("t3.single_variant_covered", + sh_match_exhaustive(t3_only_ti, tt_cons(t3_one, tt_nil()), 0), 1); + total = total + 1; fails = fails + ts_check("t3.no_arms_not_exhaustive", + (sh_match_exhaustive(t3_only_ti, tt_nil(), 0) == 0 && sh_match_first_missing(0, 1) == 0), 1); + // ⑤ 域守卫与不可映射守卫:非枚举 scrutinee / 不可映射模式 ⇒ -1(**不判**,不得当 0/1) + t3_s_ti := ts_unf_mk_int_struct("T3StrU"); + total = total + 1; fails = fails + ts_check("t3.domain_guard_unknown", + (sh_match_exhaustive(TI_INT, tt_nil(), 0) == -1 && sh_match_exhaustive(t3_s_ti, tt_nil(), 0) == -1 && + sh_match_exhaustive(-1, tt_nil(), 0) == -1 && sh_enum_domain_term(t3_s_ti) == -1), 1); + total = total + 1; fails = fails + ts_check("t3.unmappable_guard_unknown", + sh_match_exhaustive(t3_col_ti, tt_cons(t3_r, tt_cons(t3_g, tt_cons(t3_b, tt_nil()))), 1), -1); + // ⑥ 空枚举域 = ⊥:补集语义下**空洞穷尽**(无值可漏 ⇒ 无反例;计划原文「= 0」的偏差登记) + t3_emp_ei := add_enum("T3Empty"); + w64(g_enums, t3_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0); + t3_emp_ti := alloc_named_type(str_intern("T3Empty")); + total = total + 1; fails = fails + ts_check("t3.empty_domain_vacuous", + (sh_match_exhaustive(t3_emp_ti, tt_nil(), 0) == 1 && sh_match_first_missing(0, 0) == -1), 1); + // ⑦ payload 变体与 tag 变体混合(payload 不入项 = 变体身份粒度;覆盖语义不受影响) + t3_mix_ei := add_enum("T3Mix"); + ei_set_variant_name(t3_mix_ei, 0, str_intern("T3None")); + ei_set_variant_name(t3_mix_ei, 1, str_intern("T3Some")); + ei_set_variant_type_count(t3_mix_ei, 1, 1); + ei_set_variant_type(t3_mix_ei, 1, 0, TY_INT); + w64(g_enums, t3_mix_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2); + t3_mix_ti := alloc_named_type(str_intern("T3Mix")); + t3_none := sh_match_variant_term(t3_mix_ti, 0); + t3_some := sh_match_variant_term(t3_mix_ti, 1); + total = total + 1; fails = fails + ts_check("t3.payload_mixed_cover", + (t3_none >= 0 && t3_some >= 0 && t3_none != t3_some && + sh_match_exhaustive(t3_mix_ti, tt_cons(t3_some, tt_cons(t3_none, tt_nil())), 0) == 1 && + sh_match_exhaustive(t3_mix_ti, tt_cons(t3_none, tt_nil()), 0) == 0 && + sh_match_first_missing(sh_match_bit(0), 2) == 1), 1); + // ⑧ 模式名字解析:限定名 `Enum.Variant` 与裸名同判;异枚举前缀 / 未声明名 ⇒ -1(不可映射); + // 类别分类:通配/绑定 = ⊤、枚举模式 = 变体、字面量/负节点 = 不可映射 + t3_pat_q := alloc_node(EXPR_ENUMPAT, str_intern("T3Color.T3Blue"), 0, 0, 0, 0, 0, 0, 0); + t3_pat_b := alloc_node(EXPR_ENUMPAT, str_intern("T3Blue"), 0, 0, 0, 0, 0, 0, 0); + t3_pat_f := alloc_node(EXPR_ENUMPAT, str_intern("T3Mix.T3Blue"), 0, 0, 0, 0, 0, 0, 0); + t3_pat_n := alloc_node(EXPR_ENUMPAT, str_intern("T3Nope"), 0, 0, 0, 0, 0, 0, 0); + t3_pat_w := alloc_node(EXPR_WILDCARD, 0, 0, 0, 0, 0, 0, 0, 0); + t3_pat_l := alloc_node(EXPR_INT, 0, 0, 0, 5, TY_INT, 0, 0, 0); + t3_pat_i := alloc_node(EXPR_IDENT, 0, 0, 0, str_intern("t3bind"), 0, 0, 0, 0); + total = total + 1; fails = fails + ts_check("t3.pat_name_resolution", + (sh_match_pat_variant(t3_col_ti, t3_pat_q) == 2 && sh_match_pat_variant(t3_col_ti, t3_pat_b) == 2 && + sh_match_pat_variant(t3_col_ti, t3_pat_f) == -1 && sh_match_pat_variant(t3_col_ti, t3_pat_n) == -1 && + sh_match_pat_variant(t3_col_ti, t3_pat_w) == -1 && sh_match_pat_variant(t3_s_ti, t3_pat_b) == -1), 1); + total = total + 1; fails = fails + ts_check("t3.pat_kind_classification", + (sh_match_pat_kind(t3_pat_w) == 1 && sh_match_pat_kind(t3_pat_i) == 1 && + sh_match_pat_kind(t3_pat_q) == 2 && sh_match_pat_kind(t3_pat_l) == 0 && + sh_match_pat_kind(-1) == 0), 1); + // ⑨ 重复臂:覆盖面无贡献(补集仍空 = 穷尽;覆盖位去重在 checker 侧收集时做—— + // 「位已置 ⇒ 该臂冗余」由行为集钉死) + total = total + 1; fails = fails + ts_check("t3.dup_arm_complement", + (sh_match_exhaustive(t3_only_ti, tt_cons(t3_one, tt_cons(t3_one, tt_nil())), 0) == 1 && + sh_match_first_missing(sh_match_bit(0), 3) == 1), 1); + // ⑩ 泛型应用行(`T3GOpt[int]`):域可展开 + 全覆盖 = 1(变体身份与实参无关) + t3_go_ei := add_enum("T3GOpt"); + w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1); + w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T3GT")); + ei_set_variant_name(t3_go_ei, 0, str_intern("T3GN")); + ei_set_variant_name(t3_go_ei, 1, str_intern("T3GS")); + ei_set_variant_type_count(t3_go_ei, 1, 1); + ei_set_variant_type(t3_go_ei, 1, 0, 0); + w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2); + t3_go_ti := alloc_named_type(str_intern("T3GOpt")); + grow_gen_apply_data(g_gen_apply_data_count + 2); + t3_gas := g_gen_apply_data_count; + w64(g_gen_apply_data, t3_gas * 8, 1); + w64(g_gen_apply_data, (t3_gas + 1) * 8, TI_INT); + g_gen_apply_data_count = t3_gas + 2; + t3_go_ga := alloc_type(TYP_GENERIC_APPLY, t3_go_ti, t3_gas); + t3_gn := sh_match_variant_term(t3_go_ga, 0); + t3_gs := sh_match_variant_term(t3_go_ga, 1); + total = total + 1; fails = fails + ts_check("t3.generic_apply_domain", + (t3_gn >= 0 && t3_gs >= 0 && + sh_match_exhaustive(t3_go_ga, tt_cons(t3_gn, tt_cons(t3_gs, tt_nil())), 0) == 1 && + sh_match_exhaustive(t3_go_ga, tt_cons(t3_gn, tt_nil()), 0) == 0), 1); + + // ═══ R2 P3 Task 4:联合/可选(`T?` = `T ∪ null`;退役内建 Option 注册)═══ + // 判据面 = ① 项层:`T?` 行译作 union(内层项, null 原子项)——与**独立构造**的并项等价; + // ② 子类型/不相交/可空:None ⊆ T?、Some(1) ⊆ int?、null ⊥ int(与 unit/never 亦不相交)、 + // null 有值(单点域);③ null 行规范化(同一项,两行不分裂);④ 判定点注入的**方向性** + // (T ⊆ T? 放行 / T? ⊄ T 拒绝——soundness 面);⑤ match 域面(两分支:有值 + None)与 + // 反例命名、通配吸收、不可映射守卫、非可选域零变化;⑥ 枚举载荷**类型节点列**(T0 交接 ①: + // 裸码塌缩为 0 = TY_INT,节点列才是载荷真值——本组以「码说是 int、节点说是 string」的 + // 反向夹具把节点列的读取钉死);⑦ 退役面(Option 无命名行、AK_NULL 无注册表条目)。 + // 夹具:行 = alloc_type/alloc_named_type;枚举表照 parser 写槽序人工落(两列都写)。 + init_types(); + ty_budget_reset(200000); + t4_null := alloc_type(TYP_NULL, 0, 0); // None 的类型行(每出现点一行) + t4_null2 := alloc_type(TYP_NULL, 0, 0); // 第二个出现点(规范化判据用) + t4_int_opt := alloc_type(TYP_OPTIONAL, TI_INT, 0); + t4_str_opt := alloc_type(TYP_OPTIONAL, TI_STR, 0); + t4_intt := sh_term_of_ti(TI_INT); + t4_nullt := sh_null_term(); + t4_optt := sh_term_of_ti(t4_int_opt); + // ① 项层:`int?` 项 = int ∪ null(与独立构造的并项等价),且 **不等于** 裸 int + total = total + 1; fails = fails + ts_check("t4.opt_term_is_union", + (ty_equiv(t4_optt, tt_union(t4_intt, t4_nullt)) == 1 && + ty_equiv(t4_optt, t4_intt) == 0 && ty_sub(t4_intt, t4_optt) == 1), 1); + // ② 子类型/不相交/可空 + total = total + 1; fails = fails + ts_check("t4.none_sub_optional", + (ty_sub(t4_nullt, t4_optt) == 1 && ty_sub(t4_nullt, sh_term_of_ti(t4_str_opt)) == 1 && + ty_sub(t4_optt, t4_nullt) == 0 && ty_sub(t4_optt, t4_intt) == 0), 1); + total = total + 1; fails = fails + ts_check("t4.some_sub_optional", + (ty_equiv(sh_term_of_ti(alloc_type(TYP_OPTIONAL, TI_INT, 0)), t4_optt) == 1), 1); + total = total + 1; fails = fails + ts_check("t4.null_disjoint_and_inhabited", + (ty_disjoint(t4_nullt, t4_intt) == 1 && ty_disjoint(t4_nullt, sh_term_of_ti(TI_UNIT)) == 1 && + ty_disjoint(t4_nullt, sh_term_of_ti(TI_NEVER)) == 1 && ty_disjoint(t4_nullt, t4_nullt) == 0 && + ty_inhabited(t4_nullt) == 1), 1); + // ③ null 行规范化:两个出现点译成**同一项**(节点同一),且互判包含 + total = total + 1; fails = fails + ts_check("t4.null_row_canonical", + (sh_term_of_ti(t4_null) == sh_term_of_ti(t4_null2) && + ty_sub(sh_term_of_ti(t4_null), sh_term_of_ti(t4_null2)) == 1), 1); + // ④ 判定点注入(type_compat_strict):T ⊆ T? 放行、反向拒绝、异型拒绝;同型走身份路径 + total = total + 1; fails = fails + ts_check("t4.sub_injection_asymmetry", + (type_compat_strict(TI_INT, t4_int_opt) == 1 && type_compat_strict(t4_int_opt, TI_INT) == 0 && + type_compat_strict(TI_STR, t4_int_opt) == 0 && type_compat_strict(t4_null, t4_int_opt) == 1 && + type_compat_strict(t4_int_opt, t4_int_opt) == 1 && type_compat_strict(TI_INT, t4_str_opt) == 0), 1); + // ⑤ 注册表/许可:null 与可选行都**不是**单一原子类 ⇒ -1(门全拒)。 + // R2 P4 Task 3 重定(原判据「AK_NULL 无条目」随扩列翻转):AK_NULL **有条目** + // (第 14 行,可查询性面)但 ops = 0 ⇒ 操作门仍全拒——保守面由「无条目」换位到 + // 「条目 + 零许可位」(同一行为、更可查询);条目数 13 → 16(扩列硬值)。 + total = total + 1; fails = fails + ts_check("t4.null_no_ops_no_entry", + (iface_kind_of(t4_null) == -1 && iface_kind_of(t4_int_opt) == -1 && + iface_permits(-1, OP_ADD) == 0 && iface_entry(AK_NULL) >= 0 && + iface_ops(AK_NULL) == 0 && iface_permits(AK_NULL, OP_ADD) == 0 && iface_count() == 16), 1); + // ⑥ AK_NULL 互斥公理全表(13 类逐类):除 AK_DYN(⊤ 相容规则)与自身外皆不相交 + t4_dj : ., mut = 0; + t4_k : ., mut = 0; + t4_aks := alloc(13 * 8); + w64(t4_aks, 0 * 8, AK_INT); w64(t4_aks, 1 * 8, AK_DEX); w64(t4_aks, 2 * 8, AK_STRING); + w64(t4_aks, 3 * 8, AK_BOOL); w64(t4_aks, 4 * 8, AK_UNIT); w64(t4_aks, 5 * 8, AK_NEVER); + w64(t4_aks, 6 * 8, AK_CHAR); w64(t4_aks, 7 * 8, AK_DYN); w64(t4_aks, 8 * 8, AK_PRODUCT); + w64(t4_aks, 9 * 8, AK_SUM); w64(t4_aks, 10 * 8, AK_SEQUENCE); w64(t4_aks, 11 * 8, AK_REF); + w64(t4_aks, 12 * 8, AK_PTR); + loop { + if t4_k >= 13 { break; } + ka := r64(t4_aks, t4_k * 8); + want : ., mut = 1; + if ka == AK_DYN { want = 0; } // AK_DYN = ⊤:与一切相容(含 null)——登记 + if ak_disjoint(AK_NULL, ka) != want { t4_dj = t4_dj + 1; } + t4_k = t4_k + 1; + } + total = total + 1; fails = fails + ts_check("t4.ak_null_disjoint_table", + (t4_dj == 0 && ak_disjoint(AK_NULL, AK_NULL) == 0 && ak_disjoint(AK_NULL, AK_NAMED) == 1), 1); + // ⑦ match 域面:可选域 = 两分支(有值 = Some 模式 / null = None 模式) + t4_some_pat := alloc_node(EXPR_ENUMPAT, str_intern("Some"), 0, 0, 0, 0, 0, 0, 0); + t4_none_pat := alloc_node(EXPR_ENUMPAT, str_intern("None"), 0, 0, 0, 0, 0, 0, 0); + t4_other_pat := alloc_node(EXPR_ENUMPAT, str_intern("Red"), 0, 0, 0, 0, 0, 0, 0); + t4_qual_pat := alloc_node(EXPR_ENUMPAT, str_intern("Option.Some"), 0, 0, 0, 0, 0, 0, 0); + t4_some_t := sh_match_opt_term(t4_int_opt, sh_match_opt_pat(t4_int_opt, t4_some_pat)); + t4_none_t := sh_match_opt_term(t4_int_opt, sh_match_opt_pat(t4_int_opt, t4_none_pat)); + t4_full := tt_cons(t4_some_t, tt_cons(t4_none_t, tt_nil())); + total = total + 1; fails = fails + ts_check("t4.match_opt_two_branch_cover", + (t4_some_t == t4_intt && t4_none_t == t4_nullt && + sh_match_exhaustive(t4_int_opt, t4_full, 0) == 1 && + sh_match_exhaustive(t4_int_opt, tt_cons(t4_some_t, tt_nil()), 0) == 0 && + sh_match_exhaustive(t4_int_opt, tt_cons(t4_none_t, tt_nil()), 0) == 0), 1); + total = total + 1; fails = fails + ts_check("t4.match_opt_counterexample_naming", + (sh_match_first_missing(sh_match_bit(0), 2) == 1 && + sh_match_first_missing(sh_match_bit(1), 2) == 0 && + sh_match_first_missing(0, 2) == 0), 1); + total = total + 1; fails = fails + ts_check("t4.match_opt_wildcard_and_guards", + (sh_match_exhaustive(t4_int_opt, tt_cons(tt_top(), tt_nil()), 0) == 1 && + sh_match_exhaustive(t4_int_opt, t4_full, 1) == -1 && + sh_match_exhaustive(TI_INT, t4_full, 0) == -1 && + sh_match_domain_term(TI_INT) == -1 && sh_match_domain_term(t4_str_opt) >= 0), 1); + total = total + 1; fails = fails + ts_check("t4.opt_pat_mapping", + (sh_match_opt_pat(t4_int_opt, t4_some_pat) == 0 && sh_match_opt_pat(t4_int_opt, t4_none_pat) == 1 && + sh_match_opt_pat(t4_int_opt, t4_other_pat) == -1 && sh_match_opt_pat(t4_int_opt, t4_qual_pat) == -1 && + sh_match_opt_pat(TI_INT, t4_none_pat) == -1), 1); + total = total + 1; fails = fails + ts_check("t4.opt_domain_leaf_count", + (ts_unf_union_leaves(sh_match_domain_term(t4_int_opt)) == 2 && + ts_unf_union_leaves(sh_match_domain_term(t4_str_opt)) == 2), 1); + // ⑧ 枚举载荷类型节点列(T0 交接 ① 的消费面:sh_variant_payload_term) + t4_p_ei := add_enum("T4Payload"); + ei_set_variant_name(t4_p_ei, 0, str_intern("T4Tag")); + // 变体 1:**裸码槽 = 0(= TY_INT,旧布局的全部信息)而节点列 = string** —— 节点列才是真值 + ei_set_variant_name(t4_p_ei, 1, str_intern("T4Str")); + ei_set_variant_type_count(t4_p_ei, 1, 1); + ei_set_variant_type(t4_p_ei, 1, 0, TY_INT); + ei_set_variant_type_node(t4_p_ei, 1, 0, alloc_node(0, 0, 0, 0, 0, TY_STRING, 0, 0, 0)); + w64(g_enums, t4_p_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2); + t4_p_ti := alloc_named_type(str_intern("T4Payload")); + total = total + 1; fails = fails + ts_check("t4.payload_node_beats_bare_code", + (sh_variant_payload_term(t4_p_ti, str_intern("T4Str")) == sh_term_of_ti(TI_STR) && + ty_disjoint(sh_variant_payload_term(t4_p_ti, str_intern("T4Str")), t4_intt) == 1 && + sh_variant_payload_term(t4_p_ti, str_intern("T4Tag")) == -1 && + sh_variant_payload_term(t4_p_ti, str_intern("T4Nope")) == -1), 1); + // ⑨ 载荷泛型代入:`enum T4GP[T] { T4S(T) }` 的 apply 行取实参项;两实例不同 + t4_g_ei := add_enum("T4GP"); + w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1); + w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T4GT")); + ei_set_variant_name(t4_g_ei, 0, str_intern("T4GS")); + ei_set_variant_type_count(t4_g_ei, 0, 1); + ei_set_variant_type(t4_g_ei, 0, 0, TY_INT); + ei_set_variant_type_node(t4_g_ei, 0, 0, alloc_node(EXPR_IDENT, 0, 0, 0, str_intern("T4GT"), 0, 0, 0, 0)); + w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1); + t4_g_ti := alloc_named_type(str_intern("T4GP")); + grow_gen_apply_data(g_gen_apply_data_count + 2); + t4_gs := g_gen_apply_data_count; + w64(g_gen_apply_data, t4_gs * 8, 1); + w64(g_gen_apply_data, (t4_gs + 1) * 8, TI_INT); + g_gen_apply_data_count = t4_gs + 2; + t4_g_ga := alloc_type(TYP_GENERIC_APPLY, t4_g_ti, t4_gs); + grow_gen_apply_data(g_gen_apply_data_count + 2); + t4_gs2 := g_gen_apply_data_count; + w64(g_gen_apply_data, t4_gs2 * 8, 1); + w64(g_gen_apply_data, (t4_gs2 + 1) * 8, TI_STR); + g_gen_apply_data_count = t4_gs2 + 2; + t4_g_ga2 := alloc_type(TYP_GENERIC_APPLY, t4_g_ti, t4_gs2); + total = total + 1; fails = fails + ts_check("t4.payload_generic_subst", + (sh_variant_payload_term(t4_g_ga, str_intern("T4GS")) == sh_term_of_ti(TI_INT) && + sh_variant_payload_term(t4_g_ga2, str_intern("T4GS")) == sh_term_of_ti(TI_STR) && + sh_variant_payload_term(t4_g_ga, str_intern("T4GS")) != sh_variant_payload_term(t4_g_ga2, str_intern("T4GS"))), 1); + // ⑩ 退役面:`Option` 名在类型表**零行**(内建注册已退役;用户声明才建行) + total = total + 1; fails = fails + ts_check("t4.option_not_registered", + (named_dedup_rows(str_intern("Option")) == 0 && find_gsym(str_intern("Option")) < 0), 1); + + // R2 P3 Task 5 段(用例体在 ts_t5_run——见该函数头注「不得内联」) + total = total + 9; fails = fails + ts_t5_run(); + + // R2 P3b Task 0 段(用例体在 ts_isat_run——同上,不得内联) + total = total + 16; fails = fails + ts_isat_run(); + + // R2 P3b Task 2 段(用例体在 ts_x2_run——同上,不得内联;16 例 = 原 15 + R2 P4 + // Task 3 新增 x2.shape_builtin_names) + total = total + 16; fails = fails + ts_x2_run(); + // R2 P3b Task 6:impl 契约(签名类型项化 / 形状项 / mangling 退役 / 三态)——见 ts_ifc_run + total = total + 14; fails = fails + ts_ifc_run(); + + // R2 P5 Task 3:命名面判定化(身份链 + 可判定面加强 + 域外守卫)——见 ts_t3n_run + // (残留面 1 例已迁至 t3b 段 ⇒ 本段 19 → 18) + total = total + 18; fails = fails + ts_t3n_run(); + + // R2 P5 Task 3b:不变槽元素三态(te_elem_cmp)——见 ts_t3b_run(13 例) + total = total + 13; fails = fails + ts_t3b_run(); + + // R2 P5 Task 4:残留 -1 政策 P-A(ICE04 硬错;桥接缺口 / 引擎未覆盖面 / 决定面零误报) + // ——见 ts_t4_run(3 例) + total = total + 3; fails = fails + ts_t4_run(); + + // R2 P6 Task 2(E-10):¬ 面同类原子身份判据邻域覆盖——见 ts_t2_neg_run(9 例) + total = total + 11; fails = fails + ts_t2_neg_run(); + + // R2 P4 Task 2:TYPE 段内容面(装填/roundtrip/dedup/确定性/拒绝面)——见 ts_ccr_run + // R2 P4 Task 3:IFACE 段内容面(五小节往返/签名项槽/形状名注册面/条目扩列/判定面 + // 零变化/不可建签名拒绝)——见 ts_ccr2_run(**必须最后**:populate 复位项层/引擎/ + // 桥接三面缓存;两段共用同一装填入口) + // (R2 P4 Task 4 的 7 例已在前段 bridge 节内联——见 t4.tk_* 注释。) + total = total + 5; fails = fails + ts_ccr_run(); + total = total + 8; fails = fails + ts_ccr2_run(); + + print(int_str(total - fails)); print("/"); print(int_str(total)); println(" type-engine cases passed"); + if fails != 0 { return 1; } + return 0; +} diff --git a/src/compiler/type_terms.cr b/src/compiler/type_terms.cr new file mode 100644 index 00000000..525e55ba --- /dev/null +++ b/src/compiler/type_terms.cr @@ -0,0 +1,422 @@ +// === type_terms.cr === +// R2 P0:类型项表(集合语义的类型项 DAG) +// 语义见 spec §1:τ ::= ⊥ | ⊤ | ⊤ₖ | τ₁∪τ₂ | τ₁∩τ₂ | ¬τ | μX.τ | X | k(τ₁…τₙ) +// 条目 48B {tag, a, b, c, d, hash};同构项共享(DAG),索引 = 开放寻址哈希表 g_tt_index。 +// 本文件只做「表示 + 构造去重」;判定在 type_engine.cr,规范化(NNF/DNF)在 Task 2。 +// +// 哈希面注记(与计划代码的偏差逐条见 Task 1 报告): +// ① 本语言无位异或算子(lexer 无 caret/CARET token,ast.cr OP_* 无 BXOR), +// FNV-1a 的 `h ^ byte` 改用本仓库既有 FNV-1 加法折叠(dyn_arr.cr hash_bytes 同族: +// h = h * prime + byte)——确定性、与插入顺序无关,正是 DAG 去重所需的键性质。 +// ② 槽位一律经 tt_mod 取非负(计划同日同款修正;负数取模 = 负下标 → 探针越界 → +// 扩容后去重静默失效,原生压力测试实证)。 +// ③ 键 = 五字段全比,哈希不入键(只定位槽位)——见 tt_term 内注记。 + +// ─── 标签常量(TT_*;与 ast.cr 的 TI_* 类型 kind 无关——本层是类型项语言)─── +TT_BOT : int = 0; TT_TOP : int = 1; TT_TOP_K : int = 2; +TT_UNION : int = 3; TT_INTER : int = 4; TT_NOT : int = 5; +TT_MU : int = 6; TT_ATOM : int = 7; TT_VAR : int = 8; +TT_NIL : int = 9; TT_CONS : int = 10; + +// 条目 = 48B,六字段各 8B(与 g_ir_entries 同风格:字节缓冲 + 偏移常量) +ESZ_TYPE_TERM : int = 48; +OFF_TT_TAG : int = 0; OFF_TT_A : int = 8; OFF_TT_B : int = 16; +OFF_TT_C : int = 24; OFF_TT_D : int = 32; OFF_TT_HASH : int = 40; + +// 盘上项记录(TYPE 段,R2 P4 Task 2 / D12):5 × i64 = 40B {tag,a,b,c,d}—— +// 内存 48B 记录**去掉 hash 槽**(哈希 = 五字段的纯函数,落盘 = 冗余 + 第二真源; +// 加载侧由 tt_hash5 重算)。字段宽度 8B = 与内存槽逐位同源(无截断面;D12 的 +// 「40B/条」= 5×8B,不是 5×i32)。 +ESZ_TYPE_TERM_DISK : int = 40; + +// ─── 访问器(i = 类型项行号;调用方保证 0 ≤ i < tt_count())─── +fn tt_tag(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG); } +fn tt_a(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A); } +fn tt_b(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B); } +fn tt_c(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C); } +fn tt_d(i: int) -> int { return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D); } +fn tt_count() -> int { return g_type_term_count; } + +// ─── D21 契约:`TT_ATOM ∧ b ≥ 0 ⇒ b`(b 槽即该原子的类型行);其余(union/product/μ/ +// 空链/负)⇒ **-1 = 非单一原子**(不得近似、不得回 0)。R2 P6 Task 3 自 +// `ty_shadow.cr:sh_atom_of_term` **整函数迁入**(单一真源;原位置 = 桥接层,而 +// corearch 清单不含 ty_shadow.cr ⇒ `load_ccr` 的项索引一致性校验与 corearch 回读 +// 通道无此函数可用)。语义/返回域与迁移前逐字同(唯一改点 = 函数名,调用点同步)。 +fn tt_atom_of_term(term: int) -> int { + if term < 0 || term >= tt_count() { return -1; } + if tt_tag(term) != TT_ATOM { return -1; } + if tt_b(term) < 0 { return -1; } + return tt_b(term); +} + +// ─── 结构哈希(DAG 去重的键:五字段 fold,与插入顺序无关)─── +fn tt_hash5(tag: int, a: int, b: int, c: int, d: int) -> int { + h : ., mut = 1469598103934665603; + slot : ., mut = 0; + loop { + if slot >= 5 { break; } + v : ., mut = tag; + if slot == 1 { v = a; } else if slot == 2 { v = b; } + else if slot == 3 { v = c; } else if slot == 4 { v = d; } + k : ., mut = 0; + loop { + if k >= 8 { break; } + byte : ., mut = v % 256; + if byte < 0 { byte = -byte; } + h = h * 1099511628211 + byte; + v = v / 256; + k = k + 1; + } + slot = slot + 1; + } + return h; +} + +// 非负槽位(**不要写 h - (h/cap)*cap**:i64 除法向零截断,h 为负时该式得负下标 → +// 探针越界 → 去重静默失效;2026-09-10 P0 Task 1 原生压力测试实证扩容后 dedup 全灭, +// 计划同日同款修正 = 本函数)。 +// 先取余再取绝对值:|h % cap| < cap ≤ 2^63,取负不溢出(cap 恒为 2 的幂,见 +// grow_tt_index);本仓库先例 = str_intern 的 `pos := h % cap; if pos < 0 { pos = -pos; }`。 +fn tt_mod(h: int, cap: int) -> int { + m : ., mut = h % cap; + if m < 0 { m = 0 - m; } + return m; +} + +// ─── 扩容(本文件自持,**不置 dyn_arr.cr**)─── +// 计划 Step 4 把两函数放在 dyn_arr.cr(照 grow_types 先例),实测该位置破坏 +// corearch 单元:dyn_arr.cr 属双 concat 共享面(build_selfhost_native.py 的 +// common_files),而 type_terms.cr 当时只入 corec 清单——共享文件里引用引擎符号 = +// corearch 解析期 `Undefined name: ESZ_TYPE_TERM / tt_reindex` 硬失败(实测)。 +// 引擎自持扩容 + 常量,共享面零改动 → **corearch 不受影响**(其清单无 checker/引擎层)。 +// **corelsp 更正(R2 P1 Task 2 评审 M3)**:自 Task 2 起 checker 的判定入口引用 +// 桥接层(ty_shadow.cr)→ corelsp 清单必须链接 引擎 + 桥接层,本文件已在其中 +// (build_selfhost_native.py 的 corelsp_files);「corelsp 不受影响」的旧半句已不成立。 +fn grow_type_terms(needed: int) { + if needed < g_type_term_cap { return; } + nc : ., mut = g_type_term_cap * 2; + if nc < 256 { nc = 256; } + if nc < needed { nc = needed + 64; } + nb := alloc(nc * ESZ_TYPE_TERM); + _dyncpy(g_type_terms, g_type_term_cap * ESZ_TYPE_TERM, nb); + g_type_terms = nb; + g_type_term_cap = nc; +} + +fn grow_tt_index(needed: int) { + // 开放寻址索引:容量取 2 的幂、**≥ 2×现有项数**(重建式扩容,见 tt_reindex)。 + // 「≥ 2×项数」不是可选项:tt_reindex 的重放插入**没有**装填因子守卫,若新表按 + // needed 定得过小(如调用方传 2、而现有项已 >512),重放时探测永不落空 = 死循环 + // (P0 自测守门用例实证:`g_tt_index_cap = 0; grow_tt_index(2)` 直接挂死)。 + if needed < g_tt_index_cap { return; } + nc : ., mut = 1024; + need2 : ., mut = needed; + if (g_type_term_count + 1) * 2 > need2 { need2 = (g_type_term_count + 1) * 2; } + loop { if nc >= need2 { break; } nc = nc * 2; } + nb := alloc(nc * 8); + i : ., mut = 0; + loop { if i >= nc { break; } w64(nb, i * 8, -1); i = i + 1; } + g_tt_index = nb; + g_tt_index_cap = nc; + g_tt_index_count = 0; + tt_reindex(); +} + +// ─── 索引重建(grow_tt_index 扩容后调用:清表 + 重放全部项的开放寻址插入)─── +fn tt_reindex() { + i : ., mut = 0; + loop { + if i >= g_type_term_count { break; } + h := r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH); + cap := g_tt_index_cap; + p : ., mut = tt_mod(h, cap); + loop { + if r64(g_tt_index, p * 8) < 0 { break; } + p = p + 1; if p >= cap { p = 0; } + } + w64(g_tt_index, p * 8, i); + g_tt_index_count = g_tt_index_count + 1; + i = i + 1; + } +} + +// ─── 项层复位(R2 P4 Task 2:D13 确定性装填 与 loader 重建 的共点)─── +// 计数/索引/惰性 memo 全清:g_tt_index_cap = 0 ⇒ 下一次 tt_term 触发 +// grow_tt_index(2)(惰性重建空表 + tt_reindex 重放 0 行);g_tt_top_ok/g_tt_nil_ok +// = 0 ⇒ 惰性 memo 重取(重建出的表里若已有 ⊤/nil 项,tt_term 去重会命中同一节点)。 +// **不释放缓冲**(cap 保留——重建/加载复用已分配空间,且 next grow 判定依赖 cap)。 +// 本函数另含**引擎面**复位(预算/memo/lits——同属「项行号语义」的状态): +// type_engine.cr **不入 corearch 清单**(其两条既有诊断会触发 project-mode +// `error[`=0 门,见 build_selfhost_native.py 的 backend_support_files 注记), +// 故此处直写全局(等价于 ty_budget_reset(200000) + ty_lits_reset():steps/budget/ +// exhausted/uncovered + memo 表与 cap(cap 必须清——grow_ty_memo 有 `needed < cap +// → return` 早退,只清 ok 位会复用旧表,照该函数 N1 修复注记)+ 字面缓冲计数)。 +// 调用方(ccr_types.cr 装填 / ccr_io.cr loader)另须复位桥接面 +// (sh_map_reset/sh_unf_map_reset,corec 侧)——陈旧 ti→term 缓存持有旧行号语义。 +fn tt_layer_reset() { + g_type_term_count = 0; + g_tt_index_cap = 0; + g_tt_index_count = 0; + g_tt_top_ok = 0; + g_tt_nil_ok = 0; + tt_budget_state_reset(); + g_ty_memo_ok = 0; + g_ty_memo_cap = 0; + g_ty_lits_count = 0; +} + +// 预算/未覆盖位复位(**不动项表**):--dump-types 的 probe 在表已载入后跑项层 +// 原语(tt_norm——规范化计步),两侧(写侧进程/读侧进程)须从同一计数起点出发 +// 才能对拍消耗与结果。 +fn tt_budget_state_reset() { + g_ty_steps = 0; + g_ty_budget_max = 200000; + g_ty_exhausted = 0; + g_ty_uncovered = 0; +} + +// 同构比较(五字段全比——DAG 键;哈希相同与否不参与,见 tt_term 内注记) +fn tt_same(i: int, j: int) -> int { + if tt_tag(i) != tt_tag(j) { return 0; } + if tt_a(i) != tt_a(j) { return 0; } + if tt_b(i) != tt_b(j) { return 0; } + if tt_c(i) != tt_c(j) { return 0; } + if tt_d(i) != tt_d(j) { return 0; } + return 1; +} + +// ─── 构造/复用(DAG 去重的唯一入口)─── +fn tt_term(tag: int, a: int, b: int, c: int, d: int) -> int { + if g_tt_index_cap <= 0 { grow_tt_index(2); } + h := tt_hash5(tag, a, b, c, d); + ret : ., mut = -1; + loop { + // 装填因子守卫:探测前扩容(扩容 = 重建索引,故探测位置不会中途失配); + // 扩容后重试同一轮(loop 而非递归——避免深递归与重复增长)。 + if (g_type_term_count + 1) * 2 >= g_tt_index_cap { grow_tt_index((g_type_term_count + 1) * 2); } + cap := g_tt_index_cap; + p : ., mut = tt_mod(h, cap); + loop { + idx := r64(g_tt_index, p * 8); + if idx < 0 { break; } + // 键比较 = 五字段全比(DAG 键);**不**把「存储哈希 == 重算哈希」并入键: + // 该等式只在 i64 回绕语义下恒真(x86-64 后端成立),而 bootstrap 解释器 + // 为任意精度整数(interpreter.py 二元运算无回绕)——重算 h 不回绕、落盘 + // 哈希经 w64/r64 截断到 i64,等式恒假 → 去重静默退化为「只插不查」。 + // 哈希在本层的唯一职责 = 槽位定位(插入与 tt_reindex 重放同用), + // 故键只认五字段;碰撞(含上述截断差)仅令探测链变长,不改变结果。 + if tt_tag(idx) == tag && tt_a(idx) == a && tt_b(idx) == b && + tt_c(idx) == c && tt_d(idx) == d { ret = idx; break; } + p = p + 1; if p >= cap { p = 0; } + } + if ret >= 0 { break; } + // 未命中:追加新项,写入本轮探测到的空槽(轮内索引未被改动) + grow_type_terms(g_type_term_count + 1); + i := g_type_term_count; + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG, tag); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A, a); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B, b); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C, c); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D, d); + w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH, h); + g_type_term_count = i + 1; + w64(g_tt_index, p * 8, i); + g_tt_index_count = g_tt_index_count + 1; + ret = i; + break; + } + return ret; +} + +// ─── 构造 API ─── +// 惰性 memo:0 = 未建 + ready 位(零初值惯例——全局 `= -1` 初值在 bootstrap +// 后端会被降级为 0,见 globals.cr 声明段注记) +fn tt_bot() -> int { return tt_term(TT_BOT, 0, 0, 0, 0); } + +fn tt_top() -> int { + if g_tt_top_ok == 0 { g_tt_top = tt_term(TT_TOP, 0, 0, 0, 0); g_tt_top_ok = 1; } + return g_tt_top; +} + +fn tt_top_k(k: int) -> int { return tt_term(TT_TOP_K, k, 0, 0, 0); } + +fn tt_union(a: int, b: int) -> int { + if a == b { return a; } + if tt_tag(a) == TT_BOT { return b; } + if tt_tag(b) == TT_BOT { return a; } + if tt_tag(a) == TT_TOP || tt_tag(b) == TT_TOP { return tt_top(); } + return tt_term(TT_UNION, a, b, 0, 0); +} + +fn tt_inter(a: int, b: int) -> int { + if a == b { return a; } + if tt_tag(a) == TT_TOP { return b; } + if tt_tag(b) == TT_TOP { return a; } + if tt_tag(a) == TT_BOT || tt_tag(b) == TT_BOT { return tt_bot(); } + return tt_term(TT_INTER, a, b, 0, 0); +} + +fn tt_not(a: int) -> int { + if tt_tag(a) == TT_BOT { return tt_top(); } + if tt_tag(a) == TT_TOP { return tt_bot(); } + if tt_tag(a) == TT_NOT { return tt_a(a); } // 双重否定免费(构造面代数律) + return tt_term(TT_NOT, a, 0, 0, 0); +} + +fn tt_mu(v: int, body: int) -> int { return tt_term(TT_MU, v, body, 0, 0); } +fn tt_var(v: int) -> int { return tt_term(TT_VAR, v, 0, 0, 0); } + +fn tt_nil() -> int { + if g_tt_nil_ok == 0 { g_tt_nil = tt_term(TT_NIL, 0, 0, 0, 0); g_tt_nil_ok = 1; } + return g_tt_nil; +} + +fn tt_cons(head: int, tail: int) -> int { return tt_term(TT_CONS, head, tail, 0, 0); } +fn tt_atom(ak: int, ti: int, params: int) -> int { return tt_term(TT_ATOM, ak, ti, params, 0); } + +// ─── NNF(否定下推)与 DNF 规范化(R2 P0 Task 2)─── +// 语义(spec §1.3):规范化输出 = DNF(union of products;product = 字面集合)。 +// 本层只做「形态规整」——语义判定(覆盖/等价/可空/反例)在 type_engine.cr。 + +// 参数链逐元素 NNF(ATOM 的 params = CONS 链;-1 = 无参) +fn tt_nnf_list(p: int) -> int { + if p < 0 { return -1; } + if tt_tag(p) != TT_CONS { return tt_nnf(p); } + return tt_cons(tt_nnf(tt_a(p)), tt_nnf_list(tt_b(p))); +} + +// 正位置 NNF:结构不变,否定下推(¬(∪) → ∩(¬)、¬(∩) → ∪(¬)、¬¬ 消,均在负位置函数里) +fn tt_nnf(i: int) -> int { + t := tt_tag(i); + if t == TT_NOT { return tt_nnf_neg(tt_a(i)); } + if t == TT_UNION { return tt_union(tt_nnf(tt_a(i)), tt_nnf(tt_b(i))); } + if t == TT_INTER { return tt_inter(tt_nnf(tt_a(i)), tt_nnf(tt_b(i))); } + if t == TT_MU { return tt_mu(tt_a(i), tt_nnf(tt_b(i))); } + if t == TT_ATOM { return tt_atom(tt_a(i), tt_b(i), tt_nnf_list(tt_c(i))); } + if t == TT_CONS { return tt_nnf_list(i); } + return i; // ⊥ / ⊤ / ⊤ₖ / VAR / NIL:字面,原样 +} + +// 负位置 NNF:计算 ¬i 的 NNF +fn tt_nnf_neg(i: int) -> int { + t := tt_tag(i); + if t == TT_BOT { return tt_top(); } + if t == TT_TOP { return tt_bot(); } + if t == TT_NOT { return tt_nnf(tt_a(i)); } // ¬¬i = i + if t == TT_UNION { return tt_inter(tt_nnf_neg(tt_a(i)), tt_nnf_neg(tt_b(i))); } + if t == TT_INTER { return tt_union(tt_nnf_neg(tt_a(i)), tt_nnf_neg(tt_b(i))); } + if t == TT_MU { + // ¬μX.F ≠ μX.¬F(后者需要 ν 绑定)——**不静默做非等价改写**(P0 终审 Important D): + // 保留 ¬(μ…) 为字面(tt_is_literal 接受)+ 登记未覆盖面;判定侧走保守路径(不覆盖) + g_ty_uncovered = 1; + return tt_not(i); + } + return tt_not(i); // 字面取反:¬ATOM / ¬VAR / ¬⊤ₖ +} + +// 参数链逐元素 DNF(参数内的分配律同样要做) +fn tt_dnf_list(p: int) -> int { + if p < 0 { return -1; } + if tt_tag(p) != TT_CONS { return tt_dnf(p); } + return tt_cons(tt_dnf(tt_a(p)), tt_dnf_list(tt_b(p))); +} + +// DNF:∩ 分配到 ∪ 上((A∪B)∩C → (A∩C)∪(B∩C));叶子 = product(∩-链)或字面 +// 全程计预算;-1 = 预算耗尽(上抛,不得当 0/形态用) +fn tt_dnf(i: int) -> int { + if tt_step() == -1 { return -1; } + t := tt_tag(i); + if t == TT_UNION { + a1 := tt_dnf(tt_a(i)); + if a1 < 0 { return -1; } + b1 := tt_dnf(tt_b(i)); + if b1 < 0 { return -1; } + return tt_union(a1, b1); + } + if t == TT_INTER { + l := tt_dnf(tt_a(i)); + if l < 0 { return -1; } + r := tt_dnf(tt_b(i)); + if r < 0 { return -1; } + // 分配律必须**两支都展开**:(A∪B)∩C → (A∩C) ∪ (B∩C)。 + // 只展开一支 = 静默丢项(P0 Task 2 自测实证:norm.distributed got INTER want UNION + // ——计划骨架同样写错,本实现已修)。 + if tt_tag(l) == TT_UNION { + x := tt_dnf(tt_inter(tt_a(l), r)); + if x < 0 { return -1; } + y := tt_dnf(tt_inter(tt_b(l), r)); + if y < 0 { return -1; } + return tt_union(x, y); + } + if tt_tag(r) == TT_UNION { + x := tt_dnf(tt_inter(l, tt_a(r))); + if x < 0 { return -1; } + y := tt_dnf(tt_inter(l, tt_b(r))); + if y < 0 { return -1; } + return tt_union(x, y); + } + return tt_inter(l, r); + } + if t == TT_MU { + b1 := tt_dnf(tt_b(i)); + if b1 < 0 { return -1; } + return tt_mu(tt_a(i), b1); + } + if t == TT_ATOM { return tt_atom(tt_a(i), tt_b(i), tt_dnf_list(tt_c(i))); } + return i; +} + +// 预算步进(**规范化也计入**——P0 终审 Important C 实证:∩ 分配律在 n 元联合下 +// 词项数 2^n 爆炸(n=16 → 29 万项)而 exhausted 恒 0,预算形同虚设)。 +fn tt_step() -> int { + if g_ty_budget_max <= 0 { g_ty_budget_max = 200000; } + g_ty_steps = g_ty_steps + 1; + if g_ty_steps > g_ty_budget_max { g_ty_exhausted = 1; return -1; } + return 0; +} + +// 规范化入口(NNF → DNF;DAG 去重使「同形同项」免费)——-1 = 预算耗尽(调用方必须上抛) +fn tt_norm(i: int) -> int { + n := tt_nnf(i); + if n < 0 { return -1; } + return tt_dnf(n); +} + +// ─── 形态判定(自测断言与引擎前置检查共用)─── +fn tt_is_literal(i: int) -> int { + t := tt_tag(i); + if t == TT_ATOM || t == TT_VAR || t == TT_TOP_K || t == TT_BOT || t == TT_TOP { return 1; } + if t == TT_NOT { + it := tt_tag(tt_a(i)); + // NOT(MU) 亦为字面:¬μ 不下推动(见 tt_nnf_neg 的 MU 分支) + if it == TT_ATOM || it == TT_VAR || it == TT_TOP_K || it == TT_MU { return 1; } + } + return 0; +} + +fn tt_is_product_chain(i: int) -> int { + if tt_tag(i) == TT_INTER { + if tt_tag(tt_a(i)) == TT_UNION || tt_tag(tt_b(i)) == TT_UNION { return 0; } + if tt_is_product_chain(tt_a(i)) == 0 { return 0; } + return tt_is_product_chain(tt_b(i)); + } + return tt_is_literal(i); +} + +// DNF 成员:union 的子项 = product 或字面(不得再嵌 union) +fn tt_is_dnf_member(i: int) -> int { + if tt_tag(i) == TT_UNION { return 0; } + if tt_tag(i) == TT_INTER { return tt_is_product_chain(i); } + return tt_is_literal(i); +} + +fn tt_is_dnf(i: int) -> int { + // union 是**左深嵌套**(tt_union 不扁平化)→ 必须递归接受 n 元析取: + // 早期版本对子项调 tt_is_dnf_member(要求子项非 union)→ ≥3 析取的合法 DNF 被误判 0 + // (P0 终审 Important A 实证:(A|B)∩(C|D) 规范化为 4 析取 → 判 0)。 + if tt_tag(i) == TT_UNION { + if tt_is_dnf(tt_a(i)) == 0 { return 0; } + return tt_is_dnf(tt_b(i)); + } + return tt_is_dnf_member(i); +} diff --git a/src/arch/linux/ld/elf.cr b/src/format/elf/elf.cr similarity index 86% rename from src/arch/linux/ld/elf.cr rename to src/format/elf/elf.cr index 9fdeedb4..b8393c80 100644 --- a/src/arch/linux/ld/elf.cr +++ b/src/format/elf/elf.cr @@ -1,6 +1,11 @@ -// === backend/x86_64/elf.cr === +// === src/format/elf/elf.cr(格式轴:ELF 容器/段/phdr + elf_gen + allocator body; +// 波 1 Task 1 迁入;_start 发射序 = OS 轴 src/os/linux/entry.cr,Task 2 迁出; +// 函数帧(尺寸核算 + 序言/尾声/函数尾附加块)= 架构轴 src/arch/x86_64/frame.cr, +// Task 3 抽出——本文件零帧尺寸计算(H2))=== // Direct ELF binary output for x86-64 using the new resolve+emit interface. // Depends on: x86_64/instr.cr (instr_size, emit_instr, g2_*) +// Depends on: x86_64/frame.cr (pf_frame_size, pf_frame_overhead, pf_prologue, +// pf_epilogue——Phase 2 dry-run 与 Phase 3 两处调用点;帧公式单源) // Depends on: backend/resolve.cr (res_labels) // ── ELF constants (x86-64) ── @@ -837,8 +842,8 @@ fn elf2_hdr(buf: string, code_end: int, total_sz: int) { data_sz, 1073741824, 4096); // memsz = 1 GiB virtual bump heap } -// ── Emit _start code, return total bytes ── -g_call_main_pos : int, mut; // set by emit_start, used for patching call main +// ── _start 面共享状态(发射序本体 = OS 轴 src/os/linux/entry.cr)── +g_call_main_pos : int, mut; // set by emit_start (OS 轴 entry.cr), used for patching call main gv_argc : int, mut; // IR var index for g_rt_argc (or -1) gv_argv : int, mut; // IR var index for g_rt_argv_ptr (or -1) @@ -855,128 +860,10 @@ gv_hp_inflight : int, mut = -1; g_heap_expand_call_pos : int, mut = -1; g_alloc_gl_jmp_pos : int, mut = -1; // Part 1 jl .Lglobal displacement position (patched at Part 8) -fn emit_start(buf: string, pos: int) -> int { - cp : ., mut = pos; - // mov rdi, [rsp] — load argc (rdi=7) - cp = cp + emit_rex(buf, cp, 1, 0, 0, 0); - e2_w8(buf, cp, 139); cp = cp + 1; - cp = cp + emit_modrm(buf, cp, 0, 7, 4); // [rsp] via SIB - cp = cp + emit_sib(buf, cp, 0, 4, 4); - - // lea rsi, [rsp+8] — pointer to argv (rsi=6, no REX extension) - cp = cp + emit_rex(buf, cp, 1, 0, 0, 0); - e2_w8(buf, cp, 141); cp = cp + 1; - cp = cp + emit_modrm(buf, cp, 1, 6, 4); // [rsp+disp8] - cp = cp + emit_sib(buf, cp, 0, 4, 4); - e2_w8(buf, cp, 8); cp = cp + 1; - - if gv_argc >= 0 { - // lea r10, [rip + 0] (placeholder, patched in Phase 3) - rip_pos := cp + 3; - cp = cp + e2_lr(buf, cp, 0); - grow_rip_patch(g_x86_rip_patch_count + 1); - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos); - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argc); - g_x86_rip_patch_count = g_x86_rip_patch_count + 1; - // mov [r10], rdi — store argc - cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); - e2_w8(buf, cp, 137); cp = cp + 1; - cp = cp + emit_modrm(buf, cp, 0, 7, 10%8); - } - - if gv_argv >= 0 { - rip_pos2 := cp + 3; - cp = cp + e2_lr(buf, cp, 0); - grow_rip_patch(g_x86_rip_patch_count + 1); - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2); - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv); - g_x86_rip_patch_count = g_x86_rip_patch_count + 1; - // mov [r10], rsi — store argv - cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); - e2_w8(buf, cp, 137); cp = cp + 1; - cp = cp + emit_modrm(buf, cp, 0, 6, 10%8); - } - - // Initialize g_current_arena = -1 (no arena active) - if gv_current_arena >= 0 { - rip_ca := cp + 3; - cp = cp + e2_lr(buf, cp, 0); // lea r10, [rip+0] - grow_rip_patch(g_x86_rip_patch_count + 1); - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_ca); - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena); - g_x86_rip_patch_count = g_x86_rip_patch_count + 1; - // mov qword [r10], -1 — REX.WB + 0xC7 /0 - cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); - e2_w8(buf, cp, 199); cp = cp + 1; // 0xC7 MOV r/m64, imm32 - cp = cp + emit_modrm(buf, cp, 0, 0, 10%8); - e2_w32(buf, cp, -1); cp = cp + 4; // immediate = -1 (0xFFFFFFFF) - } - - // Write compile-time constant global initializers (g_ir_globals[i]. - // init_val, i64 at +16; 0 = none — BSS is already zero). - // g_current_arena is also covered by the explicit init above; the - // LET-based init below writes it again with the same value (harmless). - gi0 : ., mut = 0; - loop { if gi0 >= g_ir_global_count { break; } - iv := r64(g_ir_globals, gi0 * 24 + 16); - gvv0 := r64(g_ir_globals, gi0 * 24 + 8); - if iv != 0 && gvv0 >= 0 { - // lea r10, [rip+0] — rip_patch for the global's BSS slot - rip_pos_g := cp + 3; - cp = cp + e2_lr(buf, cp, 0); - grow_rip_patch(g_x86_rip_patch_count + 1); - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_g); - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gvv0); - g_x86_rip_patch_count = g_x86_rip_patch_count + 1; - if iv >= -2147483647 - 1 && iv <= 2147483647 { - // mov qword [r10], imm32 (sign-extended) — 49 C7 02 imm32 - w8(buf, cp, 73); w8(buf, cp+1, 199); w8(buf, cp+2, 2); - e2_w32(buf, cp+3, iv); cp = cp + 7; - } else { - // mov rax, imm64; mov [r10], rax - w8(buf, cp, 72); w8(buf, cp+1, 184); e2_w64(buf, cp+2, iv); cp = cp + 10; - w8(buf, cp, 73); w8(buf, cp+1, 137); w8(buf, cp+2, 2); cp = cp + 3; - } - } - gi0 = gi0 + 1; } - - g_call_main_pos = cp; - cp = cp + e2_call(buf, cp, 0); // call main - - // mov edi, eax - e2_w8(buf, cp, 137); cp = cp + 1; - cp = cp + emit_modrm(buf, cp, 3, 0, 7); - - // mov eax, 60 (sys_exit) - e2_w8(buf, cp, 184); cp = cp + 1; // 0xB8 MOV r, imm32 (rax) - cp = cp + e2_w32(buf, cp, 60); - - // syscall - e2_w8(buf, cp, 15); cp = cp + 1; - e2_w8(buf, cp, 5); cp = cp + 1; - - return cp - pos; -} - -fn emit_start_size() -> int { - sz : ., mut = sz_start_body(); - if gv_argc >= 0 { sz = sz + sz_start_argv_save(); } - if gv_argv >= 0 { sz = sz + sz_start_argv_save(); } - // g_current_arena init: lea(7) + rex+mov+modrm+imm32(7) = 14 bytes - if gv_current_arena >= 0 { sz = sz + 14; } - // Constant global initializers: lea r10(7) + mov imm32(7) = 14 bytes, - // or mov rax imm64 + mov [r10],rax = 20 bytes for large values. - gi0s : ., mut = 0; - loop { if gi0s >= g_ir_global_count { break; } - ivs := r64(g_ir_globals, gi0s * 24 + 16); - gvvs := r64(g_ir_globals, gi0s * 24 + 8); - if ivs != 0 && gvvs >= 0 { - if ivs >= -2147483647 - 1 && ivs <= 2147483647 { sz = sz + 14; } - else { sz = sz + 20; } - } - gi0s = gi0s + 1; } - return sz; -} +// ── _start 发射序(emit_start/emit_start_size)已整函数迁 OS 轴 +// src/os/linux/entry.cr(x86 实例化波 1 Task 2——跨轴调用经 module.cr 三轴回退 +// 链 / 组合根 _import.cr 的 `import entry` 解析;本文件保留调用点与下方共享 +// 全局声明——单扁平编译单元内解析,无重复声明)── // ── Main ELF generation ── fn elf_gen(buf: string) -> int { @@ -987,6 +874,16 @@ fn elf_gen(buf: string) -> int { gi = gi + 1; } grow_is_global(g_ir_var_count); + // 内置体名索引扫描段(g_ni_* 赋值):**保留原位**——本段整体参数化 = + // 波 2 面(x86 实例化设计 §5 波 2)。syscall3/syscall4 二项消费方 = + // instr.cr emit_instr 内置体分派链,其**发射序**已归 OS 轴 + // src/os/linux/syscall.cr(波 1 Task 6 抽取——sys_syscall3_stub/ + // sys_syscall4_stub:rax 号 + rdi/rsi/rdx(/r10) 参数序 + 0F 05 + 回存); + // 本段只负责"名字 → 索引",与发射面解耦。**注**:本段 ≠ syscall 序全集 + // ——elf.cr 另有三处手写 syscall 序列(emit_heap_expand mmap / worker + // exit / clone,syscall1/5/6 形,非内置体径)= OS 轴收编面,见 TODO #10 + // (波 2 / 实例 B 前必办——实例 B 承诺"换轴零改动"要求 Linux ABI 字节 + // 不留在格式轴)。 g_ni_syscall3 = -1; g_ni_syscall4 = -1; g_ni_load8 = -1; g_ni_store8 = -1; g_ni_load64 = -1; g_ni_load_str_ptr = -1; g_ni_store_str_ptr = -1; g_ni_get_arg = -1; g_ni_w64 = -1; g_ni_dyncpy = -1; g_ni_r64 = -1; @@ -1085,21 +982,22 @@ fn elf_gen(buf: string) -> int { g_x86_func_off_count = g_x86_func_off_count + 1; vc2 := r64(g_ir_func_var_count, fi * 8); + vs2 := r64(g_ir_func_var_start, fi * 8); pc2 := r64(g_ir_func_param_count, fi * 8); + // int 多字 M1(Task 1):识别潜在多字变量并计入帧尺寸(tag 区)。 + // tag 数为 0 → pf_frame_size 与旧公式逐字节一致(快路径零变化)。 + mw_setup_tags(fi, vs2, vc2); fsz := r64(g_x86_func_code_sz, fi * 8); - // SysV 16 字节对齐(发现 11):call 后 rsp%16=8; - // opt≥1 有 6 个 push(rbx,r12-15,rbp)→ rsp%16=8 → size 需 ≡8 (mod 16); - // opt<1 有 1 个 push(rbp)→ rsp%16=0 → size 需 ≡0 (mod 16)。 - g_x86_emit_stack_size = vc2 * 8; - if (g_x86_emit_stack_size % 16 == 0 && g_opt_level >= 1) || - (g_x86_emit_stack_size % 16 == 8 && g_opt_level < 1) { - g_x86_emit_stack_size = g_x86_emit_stack_size + 8; - } - total_code = total_code + sz_push_rbp() + sz_mov_rbp_rsp(); - if g_opt_level >= 1 { total_code = total_code + 18; } // push rbx,r12-r15(9) + pop r15-r12,rbx(9) + // 帧尺寸/帧相关字节 = frame.cr 单源(H2):SysV 16 字节对齐(发现 11) + // ——call 后 rsp%16=8;opt≥1 有 6 个 push(rbx,r12-15,rbp)→ rsp%16=8 + // → size 需 ≡8 (mod 16);opt<1 有 1 个 push(rbp)→ rsp%16=0 → ≡0。 + // 本 dry-run 与 Phase 3 的 sub rsp 立即数**调用同一函数**(pf_frame_size + // ——双源合流);帧相关字节总数 = pf_frame_overhead(须与 pf_prologue/ + // pf_epilogue 实际发射一致)。本文件零帧尺寸计算(H2 纪律)。 + g_x86_emit_stack_size = pf_frame_size(vc2); ss_dry := g_x86_emit_stack_size; - total_code = total_code + sz_sub_rsp(ss_dry); + total_code = total_code + pf_frame_overhead(ss_dry); reg_pc2 : ., mut = pc2; if reg_pc2 > 6 { reg_pc2 = 6; } stack_pc2 : ., mut = pc2 - 6; @@ -1107,8 +1005,6 @@ fn elf_gen(buf: string) -> int { total_code = total_code + reg_pc2 * sz_save_param(); total_code = total_code + stack_pc2 * sz_save_stack_param(); total_code = total_code + fsz; - total_code = total_code + sz_add_rsp(ss_dry) + sz_pop_rbp() + sz_ret(); - if g_opt_level >= 1 { total_code = total_code + 9; } // pop r15,r14,r13,r12,rbx fi = fi + 1; } rd_sz := g2_rodata_sz(); @@ -1212,96 +1108,23 @@ fi = 0; loop { if fi >= g_ir_func_count { break; } g2_init(); g_current_func_var_start = vs; vi := 0; loop { if vi >= vc { break; } g2_slot(vs + vi); vi = vi + 1; } + // int 多字 M1(Task 1):潜在多字变量识别 + tag 字节偏移表(g2_tag_off)。 + // 帧尺寸 = pf_frame_size(vc)(含 tag 区,16 对齐规则与 Phase 2 dry run + // 同源);tag 数为 0 → 与旧布局逐字节一致。 + mw_setup_tags(fi, vs, vc); // SysV 16 字节对齐(发现 11):与 dry run 相同的对齐规则 - g_x86_emit_stack_size = vc * 8; - if (g_x86_emit_stack_size % 16 == 0 && g_opt_level >= 1) || - (g_x86_emit_stack_size % 16 == 8 && g_opt_level < 1) { - g_x86_emit_stack_size = g_x86_emit_stack_size + 8; - } + g_x86_emit_stack_size = pf_frame_size(vc); // Init label state for single-pass backpatching (-1 = not yet seen) li2 : ., mut = 0; loop { if li2 >= g_label_count { break; } grow_label_poses(li2 + 1); w64(g_label_poses, li2*8, -1); li2 = li2 + 1; } g_pending_count = 0; - // Save callee-saved registers (pushed before rbp setup → at [rbp+8..48]) - if g_opt_level >= 1 { - w8(buf, cp, 83); cp = cp + 1; // push rbx - w8(buf, cp, 65); w8(buf, cp+1, 84); cp = cp + 2; // push r12 - w8(buf, cp, 65); w8(buf, cp+1, 85); cp = cp + 2; // push r13 - w8(buf, cp, 65); w8(buf, cp+1, 86); cp = cp + 2; // push r14 - w8(buf, cp, 65); w8(buf, cp+1, 87); cp = cp + 2; // push r15 - } - // frame - w8(buf, cp, 85); cp = cp + 1; // push rbp - w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 229); cp = cp + 3; // mov rbp, rsp - g_x86_sub_rsp_pos = cp; - if g_x86_emit_stack_size > 0 { - if g_x86_emit_stack_size > 127 { - w8(buf, cp, 72); w8(buf, cp+1, 129); w8(buf, cp+2, 236); - e2_w32(buf, cp+3, 0); cp = cp + 7; - } else { - w8(buf, cp, 72); w8(buf, cp+1, 131); w8(buf, cp+2, 236); w8(buf, cp+3, 0); cp = cp + 4; - } - } - // Save register and caller-stack params into this function's slots. - pi := 0; loop { if pi >= pc { break; } - po2 := -(vs + pi + 1 - g_current_func_var_start) * 8; // force stack slot, ignore reg alloc - pty := irv_type(vs + pi); - if pty == TI_DEX && pi < 6 { - // float 参数在 XMM:movsd [rbp+po2], xmm{frn}(SysV) - // frn = 第 pi 个参数前的 float 参数数 - frn : ., mut = 0; - fj : ., mut = 0; - loop { if fj >= pi { break; } - if irv_type(vs + fj) == TI_DEX { frn = frn + 1; } - fj = fj + 1; } - if frn < 8 { - // movsd [rbp+po2], xmm{frn} — F2 0F 11 /rn(mod=01, rm=5) - w8(buf, cp, 242); w8(buf, cp+1, 15); w8(buf, cp+2, 17); - w8(buf, cp+3, 64 + frn * 8 + 5); w8(buf, cp+4, po2); cp = cp + 5; - } else { - // 9+ binary64 参数在栈上(边缘场景,位置布局简化处理) - caller_off := 16 + (pi - 6) * 8; - if g_opt_level >= 1 { caller_off = caller_off + 40; } - cp = cp + e2_sd_load(buf, cp, caller_off); - cp = cp + e2_sd_store(buf, cp, po2); - } - } else if pi >= 6 { - caller_off := 16 + (pi - 6) * 8; - if g_opt_level >= 1 { caller_off = caller_off + 40; } - if pty == TI_DEX { - cp = cp + e2_sd_load(buf, cp, caller_off); - cp = cp + e2_sd_store(buf, cp, po2); - } else { - cp = cp + e2_ld(buf, cp, 10, caller_off); - cp = cp + e2_st(buf, cp, 10, po2); - } - } else { - if pi == 0 { cp = cp + e2_st(buf, cp, 7, po2); } - if pi == 1 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 117); w8(buf, cp+3, po2); cp = cp + 4; } - if pi == 2 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 85); w8(buf, cp+3, po2); cp = cp + 4; } - if pi == 3 { w8(buf, cp, 72); w8(buf, cp+1, 137); w8(buf, cp+2, 77); w8(buf, cp+3, po2); cp = cp + 4; } - if pi == 4 { w8(buf, cp, 76); w8(buf, cp+1, 137); w8(buf, cp+2, 69); w8(buf, cp+3, po2); cp = cp + 4; } - if pi == 5 { w8(buf, cp, 76); w8(buf, cp+1, 137); w8(buf, cp+2, 77); w8(buf, cp+3, po2); cp = cp + 4; } - } - pi = pi + 1; } - - // Load register-allocated parameters from stack to callee-saved regs - if g_opt_level >= 1 { - pi2 : ., mut = 0; - loop { if pi2 >= pc || pi2 >= 6 { break; } - pri := get_reg_for_var(vs + pi2); - if pri >= 0 { - pso2 := -(vs + pi2 + 1 - g_current_func_var_start) * 8; // force stack slot - // mov reg, [rbp+offset] — load from stack to allocated register - cp = cp + emit_rex(buf, cp, 1, pri/8, 0, 0); - e2_w8(buf, cp, 139); cp = cp + 1; // 0x8B MOV r64, r/m64 - cp = cp + emit_modrm(buf, cp, 1, pri%8, 5); // [rbp+disp8] - e2_w8(buf, cp, pso2); cp = cp + 1; - } - pi2 = pi2 + 1; } - } + // ── 序言 + 形参落槽 + 寄存器参数装载(frame.cr pf_prologue)── + // 帧 setup(opt≥1 六寄存器保存 → push rbp/mov rbp,rsp → sub rsp 占位 + // 立即数)+ 形参落槽(XMM/int/栈参 + tag 卫生③参数行清 tag)+ 寄存器 + // 分配参数装载。帧序唯一发射器(H2 ③);ss = pf_frame_size 结果。 + cp = pf_prologue(vs, pc, g_x86_emit_stack_size, buf, cp); // function body — emit instructions g_x86_func_frame_start = cp; // absolute buffer pos of body start @@ -1309,52 +1132,74 @@ fi = 0; loop { if fi >= g_ir_func_count { break; } ii := 0; loop { if ii >= ic { break; } inst_idx := ist + ii; - sz := emit_instr(inst_idx, buf, cp); + // HIT 表模式(M1 Task 2):表映射 op 走 emit_instr_tabled; + // -1(未映射/形态不支持/无表)落旧路径 emit_instr——混合模式。 + // int 多字 M1(Task 2):tagged int add/sub 需溢出跳(jo)—— + // 表路径(hit_ev 降低不知 tag/无 jo 事件)整条排除落旧路径。 + jo_before := g_x86_mw_jo_count; // Task 3:站点回跳点记录 + oc_before := g_x86_mw_oc_count; // Task 4:2L 操作数站点回跳点记录 + sz : ., mut = -1; + if hit_table_active() != 0 && mw_int_arith_jo_needed(inst_idx) == 0 { + sz = emit_instr_tabled(inst_idx, buf, cp); + } + if sz < 0 { sz = emit_instr(inst_idx, buf, cp); } + // Task 3:本指令若发射了 jo(至多 1 站点/指令——add/sub 单点), + // 其回跳点 = 快路径 e2_st 之后的指令尾(绝对位置 cp+sz)——慢路径 + // 块跳回后从此继续(跳走即跳过该 store;dest 由块自行回存)。 + if g_x86_mw_jo_count > jo_before { + w64(g_x86_mw_jo_resume, (g_x86_mw_jo_count - 1) * 8, cp + sz); + } + // Task 4:本指令若新建了 2L 操作数站点(至多 1/指令),其块回跳点 + // 同样 = 指令尾(tag 清 0 之后——2L 块自写 tag 不受影响)。 + if g_x86_mw_oc_count > oc_before { + w64(g_x86_mw_oc_resume, (g_x86_mw_oc_count - 1) * 8, cp + sz); + } cp = cp + sz; ii = ii + 1; } - // patch RETURNs to epilogue - epi_pos := cp; - print(" rets: "); print(int_str(g_x86_ret_patch_count)); println(""); - rpi := 0; loop { if rpi >= g_x86_ret_patch_count { break; } - jmp_pos := r64(g_x86_ret_patch_pos, rpi * 8); - rel := epi_pos - (jmp_pos + 5); - w32(buf, jmp_pos + 1, rel); - rpi = rpi + 1; } - g_x86_ret_patch_count = 0; - - // Patch prologue sub rsp with actual stack size - if save_ss > 0 { - ss3 := save_ss; - if ss3 > 127 { - e2_w32(buf, g_x86_sub_rsp_pos + 3, ss3); - } else { - w8(buf, g_x86_sub_rsp_pos + 3, ss3); - } - } + // ── 尾声 + 函数尾附加块(frame.cr pf_epilogue)── + // RETURN patch → sub rsp 立即数回填(save_ss = 本函数帧字节)→ + // epilogue(add rsp/opt≥1 逆序 pop/pop rbp/ret)→ jo 慢路径块 + + // 2L 操作数块(函数尾冷区)。帧序唯一发射器(H2 ③)。 + cp = pf_epilogue(save_ss, buf, cp); + fi = fi + 1; } - // epilogue (emit with correct stack size, no placeholder) - if save_ss > 0 { - if save_ss > 127 { - w8(buf, cp, 72); w8(buf, cp+1, 129); w8(buf, cp+2, 196); - e2_w32(buf, cp+3, save_ss); cp = cp + 7; + // ── M2a Task 2:事件流 rel32 回填(回填表 kind 0 = 事件流位置)── + // jump 事件于事件字节发射期登记(instr.cr hit_rel_add);目标 = 事件序 → + // 发射期位置表(g_hit_ev_pos,边发射边记)解析为绝对字节位置后回填: + // disp = 目标位置 - (rel 字段位置 + 4)(rel32——与旧路径 label 回填同公式)。 + // kind 1/2(函数起点 / 外部符号)= Task 4/5 消费——回填未实现:出现即大声 + // 拒(防御——现事件集(jump/branch 前)无 func/extern rel 步可达登记)。 + // 守卫重构(v7 Task 1 注记——编译器巨型函数尾既有代码生成缺陷规避): + // 计数先读入局部 + 外层 if 包裹,等价于原 `hri := 0; loop { if hri >= + // g_hit_rel_count ...`(循环体不改计数;reset 恒执行)——原形状在巨型 + // 函数尾控制流下经自举编译器生成的边界读 = 未初始化栈槽(dir 目录构建 + // corearch 的 stage1 场景 segv;concat 构建因陈旧栈值恰为 0 而潜伏), + // 重构后边界读落已写局部。语义零变化;缺陷 = 编译器层潜在面(待核注记)。 + hri2 : ., mut = g_hit_rel_count; + if hri2 > 0 { + hri : ., mut = 0; + loop { + if hri >= hri2 { break; } + rk := r64(g_hit_rel, hri * 24); + rp := r64(g_hit_rel, hri * 24 + 8); + rt := r64(g_hit_rel, hri * 24 + 16); + if rk == 0 { + if rt >= 0 && rt < g_hit_ev_pos_count { + tp := r64(g_hit_ev_pos, rt * 8); + w32(buf, rp, tp - (rp + 4)); } else { - w8(buf, cp, 72); w8(buf, cp+1, 131); w8(buf, cp+2, 196); w8(buf, cp+3, save_ss); cp = cp + 4; + print(" MISSING event position for rel target ordinal "); + println(int_str(rt)); } - } - if g_opt_level >= 1 { - // Pops in REVERSE order: rbp, r15, r14, r13, r12, rbx - w8(buf, cp, 93); cp = cp + 1; // pop rbp - w8(buf, cp, 65); w8(buf, cp+1, 95); cp = cp + 2; // pop r15 - w8(buf, cp, 65); w8(buf, cp+1, 94); cp = cp + 2; // pop r14 - w8(buf, cp, 65); w8(buf, cp+1, 93); cp = cp + 2; // pop r13 - w8(buf, cp, 65); w8(buf, cp+1, 92); cp = cp + 2; // pop r12 - w8(buf, cp, 91); cp = cp + 1; // pop rbx } else { - w8(buf, cp, 93); cp = cp + 1; // pop rbp + print(" BAD rel kind "); + print(int_str(rk)); + println(" (func/extern backfill = Task 4/5)"); } - w8(buf, cp, 195); cp = cp + 1; // ret - fi = fi + 1; } + hri = hri + 1; } + } + g_hit_rel_count = 0; // ── _init_globals ── w8(buf, cp, 85); cp = cp + 1; @@ -1565,6 +1410,23 @@ fi = 0; loop { if fi >= g_ir_func_count { break; } loop { if cp % 8 == 0 { break; } w8(buf, cp, 0); cp = cp + 1; } si = si + 1; } + // ── HIT const pool(M1 Task 3 合成层:lower 常量池 → rodata)── + // 池引用(mov r64, [rip+disp32] 占位)在函数发射期间记录(instr.cr 编码器); + // 字符串区定稿后回填 disp(池区紧随字符串,8B/槽、8 对齐不破)。 + hpr : ., mut = 0; + loop { if hpr >= g_hit_pool_patch_count { break; } + pp := r64(g_hit_pool_patch, hpr * 16); + pk := r64(g_hit_pool_patch, hpr * 16 + 8); + rel := cp + pk * 8 - (pp + 7); + w32(buf, pp + 3, rel); + hpr = hpr + 1; } + g_hit_pool_patch_count = 0; + hpk : ., mut = 0; + loop { if hpk >= g_hit_pool_count { break; } + w64(buf, cp + hpk * 8, r64(g_hit_pool, hpk * 8)); + hpk = hpk + 1; } + cp = cp + g_hit_pool_count * 8; + // ── Recompute bss_va after all code emitted ── // total_code from Phase 2 underestimates; use actual cp for precise calculation. // +1 ensures BSS is on a different page from code. diff --git a/src/arch/linux/ld/ld.cr b/src/format/elf/ld.cr similarity index 99% rename from src/arch/linux/ld/ld.cr rename to src/format/elf/ld.cr index 1ae6f3ae..e8164610 100644 --- a/src/arch/linux/ld/ld.cr +++ b/src/format/elf/ld.cr @@ -1,4 +1,4 @@ -// === arch/linux/ld/ld.cr === +// === format/elf/ld.cr === // Core dynamic ELF linker — mold-inspired chunk architecture. // // Chunks (kind dispatch): diff --git a/src/arch/linux/ld/resolve.cr b/src/format/elf/resolve.cr similarity index 100% rename from src/arch/linux/ld/resolve.cr rename to src/format/elf/resolve.cr diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr new file mode 100644 index 00000000..d3d95cf2 --- /dev/null +++ b/src/lattice/ent_kernel.cr @@ -0,0 +1,1068 @@ +// === ent_kernel.cr === +// corearch 范式无关内核——语义判定引擎 + 条目/区间数据面(内核抽取 Task 1, +// 2026-09-10 自 regalloc.cr 逐函数纯搬——函数体零改动;计划: +// docs/superpowers/plans/2026-09-10-corearch-kernel-extraction.md)。 +// 范式无关:不读值内容、无寄存器名/ABI 常量/编码知识——判定作用于条目类别 +// (位置 + 配方 + 存在);资源域(寄存器/槽的代数)= 实例侧声明。 +// 机器侧(x86 实例)= regalloc.cr:CAG alloc_registers + g_opt_meta 写入 +// (meta_set/append/remove/meta_reg_assign_total)+ 注入钩子(cir debug 测试通道)。 +// 同 concat 单编译单元(corearch concat)互调——跨文件引用无需声明头。 +// 目录分层(2026-09-10):本文件位于 src/lattice/(格层 = 图→格→编码三层链的 +// 层名)——语义内核不属 x86 实例目录(原实例目录已于 x86 实例化波 1 Task 1 +// 拆为三轴 src/arch/x86_64 + src/format/elf + src/os/linux);import ent_kernel +// 解析经 module.cr 回退链的 src/lattice/ 条目(src/compiler 与组合根两侧共享)。 +// B 通道中立化(内核完备 Task 3,2026-09-10):判定输入真相源 = 位置登记表 +// {entry→loc}(kern_loc_assign/clear/of——实例写 g_opt_meta 后经 kern API 成对 +// 登记/清除——双份同步责任在实例);本文件读通道 = 登记表(meta_reg_for_var +// = 登记表查询——var 级驻留,历史名保留——调用点 = rl_rule2_func + regalloc.cr +// 注入探针)。g_opt_meta 本身 = 实例 emit 面私有结构(instr.cr get_reg_for_var +// 消费),本文件零代码引用(中立性 guard B 组绿判据)。 +// +// 本文件函数集 = 内核接口面(现名即 API——不加 kern_ 包装层,YAGNI 裁决, +// 内核抽取 Task 3)。内核 API 面按职责族: +// 数据面 :grow_live_ranges/live_range_slot/live_first/live_last/compute_live_ranges +// grow_entries/grow_func_entry_meta/ent_off/ent_var/ent_def/ent_live_start/ +// ent_live_end/ent_home/ent_flags/entry_start/entry_count/compute_entries +// 位置登记:kern_loc_assign/kern_loc_clear/kern_loc_of({entry→loc} 不透明位置 +// 登记表——判定输入统一形态,实例经 API 写入;loc_registry_reset = +// 条目重建同点清表) +// 判定服务:entries_coexist/coexist_version_conflicts/coexist_home_conflicts/ +// verify_regalloc_consistency/regalloc_verify_all(规则①②合成—— +// regalloc_verify_all = 设计 spec §3.3 输出 API kern_verify_all 的 +// 现名,注记见函数位;违反诊断措辞 = 中性文本层(Task 4))+ 判定 +// 诊断(rl_rec_lt/rl_merge_sort/rl_print_loc/rl_func_name/rl_report_*) +// 诊断通道:dump_entries_summary/dump_coexist_summary(cir 调试 dump 载体) +// + ir_op_kind_name(--dump-entries kind= 定值种类名,Task 2 迁入) +// 实例侧(x86 实例机器函数)= regalloc.cr:CAG alloc_registers + g_opt_meta 写入 +// (meta_set/append/remove/meta_reg_assign_total)+ 注入钩子(cir debug 测试通道) +// ——写入点成对调 kern 登记/清除(双份同步纪律,Task 3 配对点 = phase 5 + +// meta_set_reg/meta_remove_var,见 regalloc.cr 注记)。 +// 注册契约最小面(2026-09-10 内核抽取 Task 3 + 内核完备 Task 4):实例声明表 +// g_instance_decl(corearch.cr 声明——corearch 侧数据)——行 = {id, name +// str_idx, opt_min, opt_max, allow_table, allow_link, needs_alloc, needs_verify, +// needs_eviction, needs_call_sites} × n(x86 实例 + 表模式路径声明,行数据 = 0/0); +// needs_eviction/needs_call_sites = 判定③④ 的**形式声明字段**——引擎不实现 +// (用户裁决,设计 spec §3.2:静态放置无驱逐事件 + callee-saved 平凡满足; +// 字段存在 + 文档即契约完整性,needs_* = 1 的未来实例出现时 = 引擎扩展的 +// 注册契约演进点);引导 = corearch_main 读 flag → instance_select() 查表选 +// 实例 → 决策逐点查询活动实例行(拒绝门/表管线门/O2 职责门)。判定输入 +// 中立化(B 通道——位置登记表 + home 字段独立遍,2026-09-10 完成,见登记表 +// 节)/ 双向契约输出面(kern_verify_all 现名即 API 注记)/ home 回填生产 = +// 蓝图后续步骤(范围克制)。 +// 区头注随搬逐字保留(其中「判定区」「alloc 区」互指现分居本文件/regalloc.cr)。 +// ------------------------------------------------------------------ +// v6 数据基础:存在区间推导(NOD 节点序 [first_ref, last_ref]——节点序 = +// 指令序 index-aligned(F5),坐标语义同前;A 通道中立化 2026-09-10) +// ------------------------------------------------------------------ +// compute_live_ranges 填充全局 g_ir_live_ranges(表全局声明在 globals.cr), +// alloc_registers 改读本表——与原内联 iv_buf 构建逻辑逐行一致(行为不变)。 +// 表布局:每函数一段,段内每「函数内 var」16B(first_ref/last_ref 各 8B, +// 函数内节点序 = 节点窗口内坐标);func_i 段起始 = Σ var_count[0..func_i), +// 不乘固定稠密系数(live_range_slot 即该前缀累计;16B 记录 + grow 风格同 +// g_ir_slice_lens)。 + +fn grow_live_ranges(needed: int) { + if needed < g_live_range_cap { return; } + nc := g_live_range_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * 16); _dyncpy(g_ir_live_ranges, g_live_range_cap * 16, nb); + g_ir_live_ranges = nb; g_live_range_cap = nc; +} + +fn live_range_slot(func_i: int, var_i: int) -> int { + // 函数内 var 索引 = var_i − var_start[func_i];func_i 段起始 = 前缀 var_count 累计 + off : ., mut = 0; + fi : ., mut = 0; + loop { if fi >= func_i { break; } + off = off + r64(g_ir_func_var_count, fi * 8); + fi = fi + 1; } + return (off + (var_i - r64(g_ir_func_var_start, func_i * 8))) * 16; +} + +fn live_first(func_i: int, var_i: int) -> int { + if func_i < 0 || var_i < 0 { return -1; } + return r64(g_ir_live_ranges, live_range_slot(func_i, var_i)); +} + +fn live_last(func_i: int, var_i: int) -> int { + if func_i < 0 || var_i < 0 { return -1; } + return r64(g_ir_live_ranges, live_range_slot(func_i, var_i) + 8); +} + +// 存在区间推导:填充 g_ir_live_ranges。语义 = 原 alloc_registers 内联 iv_buf +// 构建(:210-241):逐函数逐节点扫描(节点字段读 = 对象面 nod_*——A 通道 +// 中立化 2026-09-10,同 index 数值与线性流时代 iri_* 逐字节同),dest/s1/s2 +// 落在 [vs, vs+vc) 内则扩展 [first_ref,last_ref](首见写 first,之后推进 +// last);未使用 var 两条均为 -1。 +fn compute_live_ranges() { + total : ., mut = 0; + fi : ., mut = 0; + loop { if fi >= g_ir_func_count { break; } + total = total + r64(g_ir_func_var_count, fi * 8); + fi = fi + 1; } + grow_live_ranges(total); + g_live_range_count = total; + // 首遍写 -1(区间未知 = -1,同 alloc_registers 的 iv_buf 初始化语义) + zi : ., mut = 0; + loop { if zi >= total { break; } + w64(g_ir_live_ranges, zi * 16, -1); + w64(g_ir_live_ranges, zi * 16 + 8, -1); + zi = zi + 1; } + // 逐函数逐节点扫描:段偏移 = 运行中前缀累计(与 live_range_slot 前缀一致)。 + // 函数窗口源 = g_ir_func_instr_start/count——值 = NOD 节点坐标(v7 节点序 + // = 指令序 index-aligned,窗口 = 函数节点范围 [start, start+count); + // loader 侧 REG root span 派生、构建侧区域快照,双进程同值——F3 注记, + // A 通道中立化 2026-09-10)。节点字段读全走对象面 nod_*(同 index—— + // 数值与线性流时代 iri_* 逐字节同,见语义对象节)。 + seg : ., mut = 0; + fi = 0; + loop { + if fi >= g_ir_func_count { break; } + ic := r64(g_ir_func_instr_count, fi * 8); + ist := r64(g_ir_func_instr_start, fi * 8); + vc := r64(g_ir_func_var_count, fi * 8); + vs := r64(g_ir_func_var_start, fi * 8); + if vc > 0 { + ii : ., mut = 0; + loop { + if ii >= ic { break; } + inst := ist + ii; + d := nod_dest(inst); s1 := nod_s1(inst); s2 := nod_s2(inst); + if d >= vs && d < vs + vc { + lv := d - vs; + st := (seg + lv) * 16; + if r64(g_ir_live_ranges, st) < 0 { w64(g_ir_live_ranges, st, ii); } + w64(g_ir_live_ranges, st + 8, ii); + } + if s1 >= vs && s1 < vs + vc { + lv := s1 - vs; + st := (seg + lv) * 16; + if r64(g_ir_live_ranges, st) < 0 { w64(g_ir_live_ranges, st, ii); } + w64(g_ir_live_ranges, st + 8, ii); + } + if s2 >= vs && s2 < vs + vc { + lv := s2 - vs; + st := (seg + lv) * 16; + if r64(g_ir_live_ranges, st) < 0 { w64(g_ir_live_ranges, st, ii); } + w64(g_ir_live_ranges, st + 8, ii); + } + ii = ii + 1; + } + } + seg = seg + vc; + fi = fi + 1; + } + // v6 Task 2:条目版本化——对全部函数切分版本条目(逐函数升序, + // compute_entries 的 func_i==0 分支负责整表重建;本函数幂等可重跑)。 + // 与 opt 门控解耦:compute_live_ranges 自身在 cir --dump-entries 与 + // alloc_registers(O2)两处被调,条目表随算随新。 + ef : ., mut = 0; + loop { + if ef >= g_ir_func_count { break; } + compute_entries(ef); + ef = ef + 1; + } +} + +// ------------------------------------------------------------------ +// v6 条目版本化:变量 × 定值点切分版本条目 +// ------------------------------------------------------------------ +// Core IR 非 SSA——变量可多次定值。定值识别(GC 批 2 扩权,格式定稿 §4.1 +// 「dest ≥ 0 = 定值点」): +// · 每个写变量槽的指令 = 该变量的定值点——IR_ALLOC(局部槽初定值,interp +// 置零)、IR_ALLOC_ARRAY/IR_ALLOC_STRUCT(数组/结构变量诞生,无初值声明 +// 不发 IR_ALLOC,如 `a : [int; N];`——见 ir_gen EXPR_LET 路径)、以及全部 +// producer(CONST/BINARY/CALL/LOAD 族/DEREF/…,dest = 产出变量)。 +// · IR_STORE 形态 ρ(s1):=ρ(s2)——定值目标在 s1 不在 dest(dest 恒 -1,见 +// docs/ir-op-semantics.md §2.1),单列规则。 +// · 例外(dest ≥ 0 但非变量定值,扫 IR 全集勘定 GC 批 2): +// IR_STORE_INDEX_VAR —— dest = 被存值源(M[s1+8·s2] := ρ(dest),值槽不写) +// IR_STORE_PTR —— dest = runtime base 标注(provenance_verify.cr +// 改写:无结果指令用 dest 携带基址变量,源语义) +// IR_DYN_DISPATCH —— dest = _dyncall 占位 var(分发不写槽) +// 每个定值切分一个新版本条目。 +// 版本存在区间(全局指令序闭区间,与 Task 1 区间表同语义、坐标 +instr_start): +// 版本 j = [def_j, min(def_{j+1}−1, last_ref)](末版 = [def_k, last_ref]; +// last_ref 恒 ≥ 末定值——定值写自身计入引用)。 +// 无定值但有引用的变量(函数参数、无写临时值等)→ 单条目 def_instr=−1、 +// 区间 = [first_ref, last_ref];从未引用(first_ref=−1)跳过。 +// 版本号不落盘:同 var 条目按 def_instr 升序(单遍扫描即升序),版本序 = 组内序号。 +// 调用前置:compute_live_ranges() 已先行(截断用 last_ref),func_i 升序调用 +// (func_i==0 重置全局计数——整表重建,重复调用幂等)。 + +fn grow_entries(needed: int) { + if needed < g_entry_cap { return; } + nc : ., mut = g_entry_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + nb := alloc(nc * ESZ_ENTRY); _dyncpy(g_ir_entries, g_entry_cap * ESZ_ENTRY, nb); + g_ir_entries = nb; g_entry_cap = nc; +} + +fn grow_func_entry_meta(needed: int) { + if needed < g_ir_func_entry_cap { return; } + nc : ., mut = g_ir_func_entry_cap * 2; if nc < 64 { nc = 64; } if nc < needed { nc = needed + 64; } + sz := nc * 8; + n1 := alloc(sz); _dyncpy(g_ir_func_entry_start, g_ir_func_entry_cap * 8, n1); g_ir_func_entry_start = n1; + n2 := alloc(sz); _dyncpy(g_ir_func_entry_count, g_ir_func_entry_cap * 8, n2); g_ir_func_entry_count = n2; + g_ir_func_entry_cap = nc; +} + +// 条目表字段访问器(24B/条、4B 字段 LE;字段布局注释见 globals.cr)。 +// 读统一走 buf_read_i32(ccr_io.cr,真实函数体带符号扩展)——不能调 r32: +// Python bootstrap 的 StackAsmGen 把名为 r32 的调用内联成 `mov eax,[rdi+rsi]` +// (零扩展 32 位读),bootstrap 产物里负值(-1 home/def)会读成 4294967295 +// (x86_64_stack_asm.py:242;r32 源码里的符号修正逻辑只在自举产物中生效)。 +// 写侧 w32 内联为 `mov [rdi+rsi],edx`(低 32 位存储)——补码语义正确,可用。 +fn ent_off(e: int) -> int { return e * ESZ_ENTRY; } +fn ent_var(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_VAR); } +fn ent_def(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_DEF); } +fn ent_live_start(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_LS); } +fn ent_live_end(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_LE); } +fn ent_home(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_HOME); } +fn ent_flags(e: int) -> int { return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_FLAGS); } + +// 函数条目段界(全局条目下标;compute_entries 已跑过才有效) +fn entry_start(func_i: int) -> int { + if func_i < 0 || func_i >= g_ir_func_count { return 0; } + return r64(g_ir_func_entry_start, func_i * 8); +} + +fn entry_count(func_i: int) -> int { + if func_i < 0 || func_i >= g_ir_func_count { return 0; } + return r64(g_ir_func_entry_count, func_i * 8); +} + +fn compute_entries(func_i: int) -> int { + if func_i == 0 { + g_entry_count = 0; + // 条目表整代重建——旧登记全失效(B 通道中立化 2026-09-10:登记 = + // 本代条目语义——新代条目复用旧槽位,不清 = 陈旧 loc 误读;实例登记 + // 只发生在本代 compute 完成之后 = phase 5/注入,时序安全) + loc_registry_reset(); + } + // 函数窗口源同 compute_live_ranges(F3 注记,2026-09-10):ist/ic = + // g_ir_func_instr_start/count——值 = NOD 节点坐标(节点序 = 指令序 + // index-aligned);节点字段读 = 对象面 nod_*(A 通道中立化)。 + ic := r64(g_ir_func_instr_count, func_i * 8); + ist := r64(g_ir_func_instr_start, func_i * 8); + vc := r64(g_ir_func_var_count, func_i * 8); + vs := r64(g_ir_func_var_start, func_i * 8); + cnt : ., mut = 0; + if ic > 0 && vc > 0 { + // 每「函数内 var」一条最近打开条目(-1 = 未打开):定值序列切割 O(1) 收口 + prev : string, mut = alloc(vc * 8); + pz : ., mut = 0; + loop { + if pz >= vc { break; } + w64(prev, pz * 8, -1); + pz = pz + 1; + } + // 单遍扫描:定值点 = IR_STORE 的目标槽(s1=var)∪ 其余指令 dest≥0 + // 命中函数段(格式定稿 §4.1——GC 批 2 扩权,见本区头:ALLOC_ARRAY/ + // ALLOC_STRUCT 等内存对象诞生 + 全部 producer 直写并入版本切分; + // STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 的 dest 非定值,不判) + ii : ., mut = 0; + loop { + if ii >= ic { break; } + inst := ist + ii; + op := nod_op(inst); + dv : ., mut = -1; + if op == IR_STORE { + s1 := nod_s1(inst); + if s1 >= vs && s1 < vs + vc { dv = s1; } + } else if op != IR_STORE_INDEX_VAR && op != IR_STORE_PTR && op != IR_DYN_DISPATCH { + d := nod_dest(inst); + if d >= vs && d < vs + vc { dv = d; } + } + if dv >= 0 { + lv := dv - vs; + last_global : ., mut = -1; + ll := live_last(func_i, dv); + if ll >= 0 { last_global = ist + ll; } + // 收口上一版本:end = min(def−1, last_ref)(last_ref ≥ 次定值,恒取 def−1) + pe := r64(prev, lv * 8); + if pe >= 0 { + pend : ., mut = inst - 1; + if last_global >= 0 && last_global < pend { pend = last_global; } + w32(g_ir_entries, pe * ESZ_ENTRY + OFF_ENTRY_LE, pend); + } + // 开新版本:区间端点暂定 = 定值点..last_ref,末版直接成立 + grow_entries(g_entry_count + 1); + eo := g_entry_count * ESZ_ENTRY; + w32(g_ir_entries, eo + OFF_ENTRY_VAR, dv); + w32(g_ir_entries, eo + OFF_ENTRY_DEF, inst); + w32(g_ir_entries, eo + OFF_ENTRY_LS, inst); + w32(g_ir_entries, eo + OFF_ENTRY_LE, last_global); + w32(g_ir_entries, eo + OFF_ENTRY_HOME, -1); + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0); + w64(prev, lv * 8, g_entry_count); + g_entry_count = g_entry_count + 1; + cnt = cnt + 1; + } + ii = ii + 1; + } + // 无定值但有引用的 var(参数/单写临时值等):单条目 def=-1 + lv2 : ., mut = 0; + loop { + if lv2 >= vc { break; } + if r64(prev, lv2 * 8) < 0 { + gv := vs + lv2; + ff := live_first(func_i, gv); + ll := live_last(func_i, gv); + if ff >= 0 && ll >= 0 { + grow_entries(g_entry_count + 1); + eo := g_entry_count * ESZ_ENTRY; + w32(g_ir_entries, eo + OFF_ENTRY_VAR, gv); + w32(g_ir_entries, eo + OFF_ENTRY_DEF, -1); + w32(g_ir_entries, eo + OFF_ENTRY_LS, ist + ff); + w32(g_ir_entries, eo + OFF_ENTRY_LE, ist + ll); + w32(g_ir_entries, eo + OFF_ENTRY_HOME, -1); + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0); + g_entry_count = g_entry_count + 1; + cnt = cnt + 1; + } + } + lv2 = lv2 + 1; + } + } + grow_func_entry_meta(func_i + 1); + w64(g_ir_func_entry_start, func_i * 8, g_entry_count - cnt); + w64(g_ir_func_entry_count, func_i * 8, cnt); + return cnt; +} + +// (2026-09-10 内核抽取 Task 2 自 regalloc.cr 迁入——本函数唯一调用方 = +// dump_entries_summary(--dump-entries kind= 字段映射);机器侧零引用。 +// 定值指令种类名(--dump-entries kind= 字段):定值点 = dest≥0 producer +// opcode ∪ IR_STORE(s1)(本区头规则)——只列会作为定值点出现的 opcode, +// 未列 opcode 回退数字(不该出现;出现即探明新定值形态的信号)。 +fn ir_op_kind_name(op: int) -> string { + if op == IR_ALLOC { return "ALLOC"; } + if op == IR_ALLOC_STRUCT { return "ALLOC_STRUCT"; } + if op == IR_ALLOC_ARRAY { return "ALLOC_ARRAY"; } + if op == IR_STORE { return "STORE"; } + if op == IR_CONST { return "CONST"; } + if op == IR_LOAD { return "LOAD"; } + if op == IR_LOAD_FIELD { return "LOAD_FIELD"; } + if op == IR_LOAD_INDEX { return "LOAD_INDEX"; } + if op == IR_LOAD_INDEX_VAR { return "LOAD_INDEX_VAR"; } + if op == IR_BINARY { return "BINARY"; } + if op == IR_UNARY { return "UNARY"; } + if op == IR_CALL { return "CALL"; } + if op == IR_CALL_EXTERN { return "CALL_EXTERN"; } + if op == IR_HOTPATCH_ROUTE { return "HOTPATCH_ROUTE"; } + if op == IR_MAKE_ENUM { return "MAKE_ENUM"; } + if op == IR_REF { return "REF"; } + if op == IR_DEREF { return "DEREF"; } + if op == IR_LOAD_ENUM_TAG { return "LOAD_ENUM_TAG"; } + if op == IR_SLICE { return "SLICE"; } + if op == IR_ADDR_INDEX { return "ADDR_INDEX"; } + if op == IR_SPAWN { return "SPAWN"; } + if op == IR_AWAIT { return "AWAIT"; } + if op == IR_ARENA_NEW { return "ARENA_NEW"; } + if op == IR_DYN_PACK { return "DYN_PACK"; } + if op == IR_DYN_TAG { return "DYN_TAG"; } + if op == IR_DYN_VAL { return "DYN_VAL"; } + if op == IR_LAZY_THUNK { return "LAZY_THUNK"; } + if op == IR_LAZY_FORCE { return "LAZY_FORCE"; } + if op == IR_FNADDR { return "FNADDR"; } + if op == IR_I2F { return "I2F"; } + if op == IR_F2I { return "F2I"; } + if op == IR_PHI { return "PHI"; } + return "OP" + int_str(op); +} + +// --dump-entries 调试通道输出(cir 命令调用,Task 2 测试载体): +// 每函数一段、一行一条目;坐标 = 全局指令序/全局变量索引(与表内一致), +// v = 组内版本序(1-based),kind = 定值指令种类(def≥0 = producer opcode +// 名,GC 批 2 后不再止于 ALLOC/STORE;def=-1 → "-")。 +fn dump_entries_summary() { + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + es := entry_start(fi); + ec := entry_count(fi); + print("== entries func "); print(int_str(fi)); print(" ("); + print(istr_get(r64(g_ir_func_name_idx, fi * 8))); print("): "); + println(int_str(ec)); + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + gv := ent_var(e); + ord : ., mut = 1; + ej : ., mut = es; + loop { + if ej >= e { break; } + if ent_var(ej) == gv { ord = ord + 1; } + ej = ej + 1; + } + print(" e "); print(int_str(e)); + print(" var "); print(int_str(gv)); + vn := get_ir_var_name(gv); + if str_len(vn) > 0 { print(" name="); print(vn); } + print(" v "); print(int_str(ord)); + print(" def "); print(int_str(ent_def(e))); + d := ent_def(e); + if d >= 0 { + // d = 定值点 = 全局节点坐标(A 通道中立化:对象面 nod_op 同 + // index——线性流读点 F2 清,2026-09-10) + print(" kind="); print(ir_op_kind_name(nod_op(d))); + } else { + print(" kind=-"); + } + print(" live "); print(int_str(ent_live_start(e))); print(".."); + print(int_str(ent_live_end(e))); + print(" home "); print(int_str(ent_home(e))); + print(" flags "); println(int_str(ent_flags(e))); + ei = ei + 1; + } + fi = fi + 1; + } +} + +// ===== v6 Task 3:共存推导(存在区间相交;不落盘——D3 判定现算)===== +// 格层语义:共存 = 对称关系(无传递性,最弱理论)。条目的 live 区间为 +// 闭区间 [ls, le](全局指令序坐标——v6 NOD 坐标同源)。 + +// 单对查询:两条目存在区间相交 ⟺ ls1 ≤ le2 && ls2 ≤ le1(闭区间)。 +// e1/e2 = 全局条目索引;func_i 保留兼容调用约定(跨函数指令区间天然 +// 不重叠,无需按函数过滤)。判定四条之共存互斥的输入。 +fn entries_coexist(func_i: int, e1: int, e2: int) -> int { + // GC-1(M-5):无上界校验收口——e1/e2 ≥ g_entry_count 属越界索引 + // (条目表容量 ≥ 计数:槽后区域读到零填充/缓冲外 → 判定垃圾或崩溃), + // 防御性返回 0,风格与访问器约定一致(判定原语化前补)。 + if e1 < 0 || e2 < 0 || e1 >= g_entry_count || e2 >= g_entry_count { return 0; } + if e1 == e2 { return 0; } + s1 := ent_live_start(e1); n1 := ent_live_end(e1); + s2 := ent_live_start(e2); n2 := ent_live_end(e2); + if s1 < 0 || s2 < 0 { return 0; } + if s1 <= n2 && s2 <= n1 { return 1; } + return 0; +} + +// 同 var 跨版本冲突数(数据自检):版本按定值点切割,构造保证 +// 版本 j 区间终点 = 版本 j+1 定值点 −1 < 其起点 → 相邻版本必不交。 +// 返回 0 = 版本化正确(任何 >0 = 版本切割 bug)。O(n) 每 var 组。 +fn coexist_version_conflicts(func_i: int) -> int { + es := entry_start(func_i); + ec := entry_count(func_i); + conflicts : ., mut = 0; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + gv := ent_var(e); + // 找同 var 的下一版本条目(版本按定值序相邻) + ej : ., mut = ei + 1; + loop { + if ej >= ec { break; } + if ent_var(es + ej) == gv { + if entries_coexist(func_i, e, es + ej) != 0 { + conflicts = conflicts + 1; + } + break; + } + ej = ej + 1; + } + ei = ei + 1; + } + return conflicts; +} + +// 同 home 组内冲突数(判定输入雏形——共存互斥:同槽条目不共存)。 +// home = -1(未分配)不参与。真实成本注记(评审 I-2):home ≥ 0 时对每函数 +// 做 O(E²) 相等探测(与组大小 k 无关;当前无回填路径 = 内层不进入 = O(E))。 +// 触发点 = 判定进入消费端(分配器/自检回填 home 后)——届时须按 v6 格式定稿 +// §4.2 per-group sweep O(k log k) 重写,不得直接复用本函数(ledger 门禁注记)。 +fn coexist_home_conflicts(func_i: int) -> int { + es := entry_start(func_i); + ec := entry_count(func_i); + conflicts : ., mut = 0; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e1 := es + ei; + h1 := ent_home(e1); + if h1 >= 0 { + ej : ., mut = ei + 1; + loop { + if ej >= ec { break; } + e2 := es + ej; + if ent_home(e2) == h1 { + if entries_coexist(func_i, e1, e2) != 0 { + conflicts = conflicts + 1; + } + } + ej = ej + 1; + } + } + ei = ei + 1; + } + return conflicts; +} + +// --dump-coexist 调试通道(cir 命令调用,Task 3 测试载体): +// 每函数输出版本冲突数(应 0)与同 home 组内冲突数(未分配时应 0)。 +fn dump_coexist_summary() { + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + print("== coexist func "); print(int_str(fi)); print(" ("); + print(istr_get(r64(g_ir_func_name_idx, fi * 8))); print("):"); + print(" ver_conf "); print(int_str(coexist_version_conflicts(fi))); + print(" home_conf "); println(int_str(coexist_home_conflicts(fi))); + fi = fi + 1; + } +} +// ===== v6 Task 5:判定消费最小闭环(一致性自检——共 specs/ +// regalloc-consistency.corespec 规约)===== +// 分配结果:实例侧 g_opt_meta 的 OPT_KEY_REG_ASSIGN 对(var_idx → 位置——x86 +// 寄存器号;instr.cr g2_slot/get_reg_for_var 发射时按对落寄存器 = emit 面)。 +// 判定消费 = 位置登记表(B 通道中立化 2026-09-10:实例写 g_opt_meta 后经 +// kern_loc_* 登记——判定经登记表消费「实际编码的分配」的一致视图——本文件 +// 零 g_opt_meta 代码引用,布局知识留实例侧)。 +// (注:本文件旧区头「IR 操作数改写为负编码」是过时设计残留——实现已改为 +// 元数据表 + 后端 g2_slot 查询,判定按实现走。) +// 版本级 vs 变量级对齐(衔接决策 b):alloc_registers(CAG 升级后仍)是变量级 +// 单位置分配(粒度论证见 alloc 区头:meta/后端/.ccr seam = var 级单位置),条目 +// 是版本级。判定把分配结果按「条目所属 var」投影到版本条目上——同 var 多版本 = +// 同位置组内多条(版本按定值切割互不共存,sweep 自然校验);两条目同位置且共存 +// = 违反。变量级表述(同寄存器两 var 的窗口相交)与条目级表述在变量级分配下 +// 等价(一 var 的版本区间并 = 其整活跃窗口),但条目级表述在分配器将来升级为 +// 条目级(版本各自落位置)后无需改动——判定原样消费。③④(驱逐配对/调用点 +// 失效)按 CAG 范围控制留 TODO:本分配器无动态驱逐(静态放置失败 = 栈驻留 +// home 保留,判定③无事件)、只用 callee-saved(调用点契约 ④ 平凡满足)—— +// 随 CAG 批次裁定:③④ 的判定实现留待 spill/调用点解锁引入时,规约已先行落 +// 文档(spec/regalloc-consistency.corespec R3/R4——文件名引用保留注记)。 +// ③④ 形式声明(内核完备 Task 4,2026-09-10):判定引擎不实现(用户裁决, +// 设计 spec §3.2 引用)——能力承载 = 实例声明表 needs_eviction/ +// needs_call_sites 字段(corearch.cr g_instance_decl 行扩展,x86/表模式路径 +// 行 = {0, 0}——静态放置无驱逐事件 + callee-saved 平凡满足)。 + +// 规则违反诊断打印上限(rl_report_rule1/2 防病理刷屏,计数不封顶)——唯一 +// 使用方 = 本文件判定函数(rl_rule2_func/verify_regalloc_consistency);机器 +// 侧零引用(2026-09-10 内核抽取 Task 2 自 regalloc.cr 迁入)。 +RPT_MAX : int = 8; // 规则违反诊断每函数每规则打印上限(计数不封顶) + +// ===== 位置登记表(B 通道中立化——内核完备 Task 3,2026-09-10)===== +// 判定输入 = 实例分配结果的统一形态——{entry → loc} 登记。loc = 不透明整数: +// 内核只做相等/排序/同值互斥,无寄存器号/域分类/编码合成概念(设计 spec +// §2.2 修正——home 面 = 条目 home 字段独立遍,不复用合成,见 verify 规则①)。 +// 写侧 = 实例(alloc_registers phase 5 + 注入钩子——写 g_opt_meta 后经本 API +// 成对登记/清除——双份同步责任在实例,配对点注记见 regalloc.cr);读侧 = +// 判定(规则① 登记表遍 kern_loc_of + meta_reg_for_var var 级驻留查询)。 +// 生命周期:条目表整代重建(compute_entries func_i==0)使全部登记失效 → 同点 +// loc_registry_reset()。表 = 每条目 4B 槽(entry_idx * 4;-1 = 未登记),按 +// 条目下标 O(1) 寻址(读走 buf_read_i32 带符号扩展——w32/buf_read_i32 同 +// ent_* 族约定,见 ent_var 注)。 +// 命名注记(Task 4 措辞批——Task 3 评审 Minor 2 候选):旧名 g_loc_reg_buf +// 的 "reg" 缩写 = 寄存器时代措辞(内核零经典概念原则)——改全词 registry +// (登记表——与 loc_registry_reset 命名族一致;registry 无寄存器语义)。 +g_loc_registry_buf : string, mut; // 登记表缓冲(4B/槽——槽 e = 条目 e 的 loc 或 -1) +g_loc_registry_cap : int, mut; // 登记表容量(槽数) + +fn loc_registry_reset() { + // 条目重建点调用(compute_entries func_i==0)——已登记区整清 -1 + // (O(cap) 一次/代;新增长槽已在 kern_loc_assign -1 初始化) + zi : ., mut = 0; + loop { + if zi >= g_loc_registry_cap { break; } + w32(g_loc_registry_buf, zi * 4, -1); + zi = zi + 1; + } +} + +fn kern_loc_assign(entry_idx: int, loc: int) { + if entry_idx < 0 { return; } + if entry_idx >= g_loc_registry_cap { + nc : ., mut = g_loc_registry_cap * 2; + if nc < 64 { nc = 64; } + if nc <= entry_idx { nc = entry_idx + 64; } + nb := alloc(nc * 4); + _dyncpy(g_loc_registry_buf, g_loc_registry_cap * 4, nb); + zi : ., mut = g_loc_registry_cap; + loop { + if zi >= nc { break; } + w32(nb, zi * 4, -1); // 新槽 -1 初始化(未登记 = -1 哨兵) + zi = zi + 1; + } + g_loc_registry_buf = nb; + g_loc_registry_cap = nc; + } + w32(g_loc_registry_buf, entry_idx * 4, loc); +} + +fn kern_loc_clear(entry_idx: int) { + if entry_idx < 0 || entry_idx >= g_loc_registry_cap { return; } + w32(g_loc_registry_buf, entry_idx * 4, -1); +} + +fn kern_loc_of(entry_idx: int) -> int { + if entry_idx < 0 || entry_idx >= g_loc_registry_cap { return -1; } + return buf_read_i32(g_loc_registry_buf, entry_idx * 4); +} + +// var 级驻留查询(判定读通道——B 通道中立化 2026-09-10 函数体等价改写:旧 = +// 直读 g_opt_meta 实例布局 → 现 = 登记表查询——kern_loc_of 逐条目)。var 级 +// 单位置分配(regalloc.cr 分配粒度决策)⇒ 同 var 全版本条目同 loc 登记—— +// 任一已登记条目 = 驻留、loc = 该条目 loc(与旧投影语义精确等价:var 分配 +// ⇔ 全条目登记,配对点见 regalloc.cr phase 5 注记)。历史函数名保留(现名 +// 即 API):调用点 = rl_rule2_func 驻留过滤 + regalloc.cr 注入探针 +// try_inject_read_gap(探针读同一内核函数 = 经登记表读——判定与探针单真源, +// 注入路径配对保持登记与 g_opt_meta 同步,红路径测试 = test_live_ranges +// check_regalloc_violations/read_gap_nonfunc0)。 +// 窗口搜索注记:var 的条目只存在于所属函数的条目段——先按 var 窗口定位函数 +// 再扫该段(避免全表扫描;与 rl_rule2_func 既有 per-var 条目收集同界)。 +fn meta_reg_for_var(var_idx: int) -> int { + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + vs := r64(g_ir_func_var_start, fi * 8); + vc := r64(g_ir_func_var_count, fi * 8); + if var_idx >= vs && var_idx < vs + vc { + es := entry_start(fi); + ec := entry_count(fi); + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_var(e) == var_idx { + loc := kern_loc_of(e); + if loc >= 0 { return loc; } + } + ei = ei + 1; + } + return -1; // 窗口内无登记条目 = 非驻留 + } + fi = fi + 1; + } + return -1; // 不在任何函数 var 窗口(全局 var 等)——恒非驻留 +} + +// 16B 记录比较(归并用):key = (loc, entry 的 live_start),均升序 +fn rl_rec_lt(buf: string, a: int, b: int) -> int { + la := r64(buf, a * 16); lb := r64(buf, b * 16); + if la != lb { if la < lb { return 1; } return 0; } + ea := r64(buf, a * 16 + 8); eb := r64(buf, b * 16 + 8); + if ent_live_start(ea) < ent_live_start(eb) { return 1; } + return 0; +} + +// 自底向上归并排序(16B 记录 {loc:8, entry:8})——O(k log k), +// v6 格式定稿 §4.2 sweep 门禁(同位置组内检查不得逐对 O(E²))。 +fn rl_merge_sort(buf: string, n: int) { + if n <= 1 { return; } + tmp := alloc(n * 16); + width : ., mut = 1; + loop { + if width >= n { break; } + left : ., mut = 0; + loop { + if left >= n { break; } + mid : ., mut = left + width; if mid > n { mid = n; } + right : ., mut = left + width * 2; if right > n { right = n; } + i : ., mut = left; j : ., mut = mid; k : ., mut = left; + loop { + if i >= mid || j >= right { break; } + if rl_rec_lt(buf, i, j) != 0 { + w64(tmp, k * 16, r64(buf, i * 16)); + w64(tmp, k * 16 + 8, r64(buf, i * 16 + 8)); + i = i + 1; + } else { + w64(tmp, k * 16, r64(buf, j * 16)); + w64(tmp, k * 16 + 8, r64(buf, j * 16 + 8)); + j = j + 1; + } + k = k + 1; + } + loop { + if i >= mid { break; } + w64(tmp, k * 16, r64(buf, i * 16)); + w64(tmp, k * 16 + 8, r64(buf, i * 16 + 8)); + i = i + 1; k = k + 1; + } + loop { + if j >= right { break; } + w64(tmp, k * 16, r64(buf, j * 16)); + w64(tmp, k * 16 + 8, r64(buf, j * 16 + 8)); + j = j + 1; k = k + 1; + } + left = right; + } + zi : ., mut = 0; + loop { + if zi >= n { break; } + w64(buf, zi * 16, r64(tmp, zi * 16)); + w64(buf, zi * 16 + 8, r64(tmp, zi * 16 + 8)); + zi = zi + 1; + } + width = width * 2; + } +} + +// 打印位置值(Task 4 措辞中性化——不透明 loc 直印,去 "reg N"/"home slot N" +// 分类措辞:值域分类 = 实例事务,内核只印不透明整数)。规则① 两遍(登记表遍/ +// 条目 home 字段遍)= 两条独立同值互斥遍(B 通道中立化 2026-09-10)——报告 +// 格式相同、遍来源不印(Task 3 遍来源标记参数 = 仅诊断措辞面,Task 4 随分类 +// 措辞同撤);判据只消费规则号/条目/loc 值,诊断可读性由 entries/var/区间/ +// loc 全量信息承载。无域偏移合成。 +fn rl_print_loc(loc: int) { + print("loc "); print(int_str(loc)); +} + +fn rl_func_name(fi: int) { + print("func "); print(int_str(fi)); print(" ("); + print(istr_get(r64(g_ir_func_name_idx, fi * 8))); print(")"); +} + +// 规则 ① 违反诊断(打印上限 RPT_MAX 防病理刷屏,计数不封顶)。 +// 前缀措辞中性化(Task 4 输出面批):旧前缀 "regalloc-consistency" = x86 时代 +// 检查名——改检查域语义名 "entry-consistency"(条目版本表一致性——规则① +// 同值互斥 + 规则② 读覆盖);spec 文件名引用保留于本文件判定区头注 +// (spec/regalloc-consistency.corespec R3/R4——规约文档名不变)。 +fn rl_report_rule1(func_i: int, loc: int, e1: int, e2: int) { + print("entry-consistency: "); rl_func_name(func_i); + print(": rule 1 violation: entries "); print(int_str(e1)); + print(" (var "); print(int_str(ent_var(e1))); print(") and "); print(int_str(e2)); + print(" (var "); print(int_str(ent_var(e2))); print(") both at "); + rl_print_loc(loc); + print(", coexist ["); print(int_str(ent_live_start(e1))); print(".."); + print(int_str(ent_live_end(e1))); print("] x ["); print(int_str(ent_live_start(e2))); + print(".."); print(int_str(ent_live_end(e2))); println("]"); +} + +// 规则① 同值互斥遍(归并段扫——sweep 门禁):rec = {loc:8, entry:8} 记录按 +// (loc, live_start) 归并排序(rl_merge_sort)——同 loc 连续段内维持最大 +// live_end:max_end ≥ 当前 ls ⟹ 区间相交 = 违反(rl_report_rule1 报告)。 +// cap_base = 前遍已打印数——RPT_MAX 按规则合并封顶(两遍共享 = 与单遍合成 +// 打印同界;Task 3 遍来源标记参数随 Task 4 措辞批同撤——两遍同报告格式)。 +fn rl_rule1_sweep(func_i: int, rec: string, cnt: int, cap_base: int) -> int { + viol : ., mut = 0; + rl_merge_sort(rec, cnt); + run_start : ., mut = 0; + loop { + if run_start >= cnt { break; } + run_end : ., mut = run_start + 1; + loop { + if run_end >= cnt { break; } + if r64(rec, run_end * 16) != r64(rec, run_start * 16) { break; } + run_end = run_end + 1; + } + maxj : ., mut = -1; + zi : ., mut = run_start; + loop { + if zi >= run_end { break; } + e := r64(rec, zi * 16 + 8); + if maxj >= 0 { + mej := r64(rec, maxj * 16 + 8); + if ent_live_end(mej) >= ent_live_start(e) { + if cap_base + viol < RPT_MAX { + rl_report_rule1(func_i, r64(rec, zi * 16), mej, e); + } + viol = viol + 1; + } + } + if maxj < 0 || ent_live_end(e) > ent_live_end(r64(rec, maxj * 16 + 8)) { + maxj = zi; + } + zi = zi + 1; + } + run_start = run_end; + } + return viol; +} + +// 规则 ② 违反诊断(前缀中性化同 rl_report_rule1——"entry-consistency"; +// 正文语义词 = 读覆盖(read ... not covered by any active version entry), +// Task 4 措辞批保留)。 +fn rl_report_rule2(func_i: int, gv: int, inst: int) { + print("entry-consistency: "); rl_func_name(func_i); + print(": rule 2 violation: read of var "); print(int_str(gv)); + vn := get_ir_var_name(gv); + if str_len(vn) > 0 { print(" name="); print(vn); } + print(" at instr "); print(int_str(inst)); + println(" not covered by any active version entry"); +} + +// 规则 ②(框架):寄存器驻留变量的读点必须有活跃版本覆盖。 +// 读点 = 指令 i 以 v 为源操作数(IR_STORE 的 s1 是写目标,排除)——只扫 s1/s2 +// 源列。dest 列不入扫,两层理由: +// · 定值指令的 dest = 本指令产出(版本起点),无读旧值语义; +// · 例外(GC 批 2 勘定,见本区头):STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 的 +// dest ≥ 0 是真实读点(被存值源/基址标注/占位,非定值)——其值 var 的 +// last_ref 已含本指令(compute_live_ranges 三列同扫),版本区间止于 +// last_ref 恒覆盖,义务内无缺口(注入截断只落被扫读点;dest 独读的漏报 = +// 框架边界,驱逐机制落地时按需扩列)。 +// 覆盖义务边界 = 首个版本化定值之后——GC 批 2 已按格式定稿 §4.1 把定值识别 +// 扩为 dest≥0 全定值(含 ALLOC_ARRAY/ALLOC_STRUCT 内存对象诞生),义务前读 +// 窗口(旧「非版本化定值供给」区)自动纳入:版本区间自定值点起连续覆盖至 +// last_ref,义务内读点恒有版本(构造不变量)——本检查零改动,承诺已验证。 +fn rl_rule2_func(func_i: int, vs: int, vc: int, nst: int, nc: int, es: int, ec: int) -> int { + // 签名措辞中性化(Task 2 F3,2026-09-10——值不变):窗口参数 = 语义对象 + // 空间切片——vs/vc = 变量窗口(全局 var 命名空间 [vs, vs+vc))、nst/nc = + // 节点窗口(函数在 NOD 节点坐标空间的 [nst, nst+nc),源 = 函数窗口表 + // g_ir_func_instr_start/count——节点序 = 指令序 index-aligned,见 + // compute_live_ranges F3 注记)。调用方位置传参,重命名零行为影响。 + violations : ., mut = 0; + lv : ., mut = 0; + loop { + if lv >= vc { break; } + gv := vs + lv; + if meta_reg_for_var(gv) < 0 { lv = lv + 1; continue; } + // 收集该 var 的版本条目(def ≥ 0;表内按定值点升序)——先数再收集 + m : ., mut = 0; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_var(e) == gv && ent_def(e) >= 0 { m = m + 1; } + ei = ei + 1; + } + if m <= 0 { lv = lv + 1; continue; } // 无版本化定值:义务范围为空 + vers : string, mut = alloc(m * 8); + vj : ., mut = 0; + ei = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_var(e) == gv && ent_def(e) >= 0 { + w64(vers, vj * 8, e); + vj = vj + 1; + } + ei = ei + 1; + } + // 逐节点升序扫读点(读点 = 节点坐标——F1:判定② 读点扫描点,A 通道 + // 中立化 2026-09-10 后 = 对象面 nod_op/nod_s1/nod_s2 同 index 读取, + // 数值与线性流时代 iri_* 逐字节同);版本游标单调推进(版本区间自 def + // 起无缝相接)。 + // 坐标注意(评审 F1 修复):ent_def/ent_live_end 存全局节点坐标 + // (nst+局部,compute_entries 写入 inst = nst + ii)——比较必须用 + // inst 不得用局部 ii;func 0 上 nst=0 使 ii == inst 掩盖该错配,非 + // func 0 函数上规则 ② 会失明(只漏报不误报;回归 = test_live_ranges + // check_regalloc_read_gap_nonfunc0)。 + cursor : ., mut = 0; + first_def := r64(vers, 0 * 8); + ii : ., mut = 0; + loop { + if ii >= nc { break; } + inst := nst + ii; + op := nod_op(inst); + rd : ., mut = 0; + s1 := nod_s1(inst); + s2 := nod_s2(inst); + if s1 == gv && op != IR_STORE { rd = 1; } + if s2 == gv { rd = 1; } + if rd != 0 && inst >= ent_def(first_def) { + // 推进游标至最后一个 def ≤ inst 的版本 + loop { + if cursor + 1 >= m { break; } + nx := r64(vers, (cursor + 1) * 8); + if ent_def(nx) > inst { break; } + cursor = cursor + 1; + } + cv := r64(vers, cursor * 8); + if ent_live_end(cv) < inst { + if violations < RPT_MAX { rl_report_rule2(func_i, gv, inst); } + violations = violations + 1; + } + } + ii = ii + 1; + } + lv = lv + 1; + } + return violations; +} + +// 判定实现(0 = 一致 1 = 违反)——消费条目表(存在区间/home 字段)+ 位置 +// 登记表(分配结果统一形态——B 通道中立化 2026-09-10,见登记表节)。 +// precondition:alloc_registers() 已跑(条目表随算随新、登记已配对)。 +fn verify_regalloc_consistency(func_i: int) -> int { + if func_i < 0 || func_i >= g_ir_func_count { return 0; } + es := entry_start(func_i); + ec := entry_count(func_i); + if ec <= 0 { return 0; } + // 函数窗口源同 compute_live_ranges(F3 注记,2026-09-10):g_ir_func_instr_* + // 值 = NOD 节点坐标(节点序 = 指令序 index-aligned);ic/ist 传 rl_rule2_func + // 的 nst/nc 节点窗口参数(位置传参)。规则② 节点字段读 = 对象面 nod_*。 + ic := r64(g_ir_func_instr_count, func_i * 8); + ist := r64(g_ir_func_instr_start, func_i * 8); + vc := r64(g_ir_func_var_count, func_i * 8); + vs := r64(g_ir_func_var_start, func_i * 8); + + // 规则 ①:同值互斥两遍(B 通道中立化 2026-09-10——设计 spec §2.2 修正: + // 位置 = 不透明整数,内核无 reg/home 值域分类与编码合成概念)—— + // (1) 登记表遍:kern_loc_of(e) ≥ 0 的条目按 loc 分组(实例分配输出的 + // 统一登记——现 = 寄存器面;实例若把 home 也登记,合成发生在实例 + // 登记动作内,内核只见不透明值); + // (2) home 字段遍:ent_home ≥ 0 条目按 home 值分组(条目字段独立遍, + // -1 未分配哨兵跳过——不复用合成)。 + // 遍序 = 登记表遍先(与现合成排序同序:reg loc 段升序全部先于 home 段—— + // 输出逐字节同);两遍各自 (loc, live_start) 归并排序后同值互斥 sweep。 + // 排他注记(与现 else-if 合成语义逐条等价):已登记条目不进 home 遍—— + // 双态条目(已登记 + home ≥ 0)不可达(注入路径先清登记再写 home;真实 + // 分配不回填 home),排他 = 现语义 reg 优先的保持(防双态假冲突)。 + reg_cnt : ., mut = 0; + home_cnt : ., mut = 0; + ei : ., mut = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_live_start(e) >= 0 && ent_live_end(e) >= 0 { + if kern_loc_of(e) >= 0 { reg_cnt = reg_cnt + 1; } + else if ent_home(e) >= 0 { home_cnt = home_cnt + 1; } + } + ei = ei + 1; + } + rule1_viol : ., mut = 0; + // 登记表遍(先) + if reg_cnt > 1 { + rec : string, mut = alloc(reg_cnt * 16); + ci : ., mut = 0; + ei = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_live_start(e) >= 0 && ent_live_end(e) >= 0 { + loc := kern_loc_of(e); + if loc >= 0 { + w64(rec, ci * 16, loc); + w64(rec, ci * 16 + 8, e); + ci = ci + 1; + } + } + ei = ei + 1; + } + rule1_viol = rl_rule1_sweep(func_i, rec, reg_cnt, 0); + } + // home 字段遍(独立遍——cap_base = 登记表遍已打印数(RPT_MAX 合并封顶)) + if home_cnt > 1 { + rec : string, mut = alloc(home_cnt * 16); + ci : ., mut = 0; + ei = 0; + loop { + if ei >= ec { break; } + e := es + ei; + if ent_live_start(e) >= 0 && ent_live_end(e) >= 0 { + h := ent_home(e); + if h >= 0 { + w64(rec, ci * 16, h); + w64(rec, ci * 16 + 8, e); + ci = ci + 1; + } + } + ei = ei + 1; + } + rule1_viol = rule1_viol + rl_rule1_sweep(func_i, rec, home_cnt, rule1_viol); + } + + // 规则 ②(框架):寄存器驻留变量的读点活跃版本覆盖 + rule2_viol : ., mut = 0; + if vc > 0 && ic > 0 { + rule2_viol = rl_rule2_func(func_i, vs, vc, ist, ic, es, ec); + } + + if rule1_viol > 0 || rule2_viol > 0 { return 1; } + return 0; +} + +// 全函数自检(O2 构建路径调用点):0 = 全部一致;违反时打印诊断并返回违反 +// 函数数。成功静默(自检通过 = 不打扰构建输出)。 +// 判定结果输出 API 注记(设计 spec §3.3 双向输出面——Task 4 注释层确认): +// 本函数 = 文档名 kern_verify_all()——violations 计数 + 违反诊断打印(实例 +// 自取)。现名 regalloc_verify_all = x86 时代历史名保留(现名即 API 裁决—— +// 不加 kern_ 包装层,YAGNI);"判定结果回传实例" = 本函数 + 证据查询面 +// (entries_coexist/coexist_version_conflicts/coexist_home_conflicts——实例 +// 自取),内核不写实例结构。 +fn regalloc_verify_all() -> int { + bad : ., mut = 0; + fi : ., mut = 0; + loop { + if fi >= g_ir_func_count { break; } + bad = bad + verify_regalloc_consistency(fi); + fi = fi + 1; + } + return bad; +} + +// ================================================================== +// 语义对象模型(内核完备 Task 1,2026-09-10):对象面 API + NOD 对象留存。 +// 对象集 = v7 载体内容的进程内形态(内核持有——配方可读承诺的载体:对象面 +// 访问器读内核缓冲而非文件重扫;裁决注:对象存储 (a) 而非文件重扫 (b)—— +// 内存成本 28B(语义) + 16B(邻接) 每节点,见 Task 1 报告): +// 图节点:g_v7_nod_sem(28B 语义字段——盘 36B 记录剥离邻接的镜像,offset +// 布局同盘)+ g_v7_nod_meta(邻接域 {first_edge, edge_count}, +// 16B/节点——EDG 段载入守卫与对象面 nod_edge_first/count 同源) +// 图边: g_v7_edges/g_v7_edge_count(EDG 段载入缓冲——声明见 ccr_io.cr, +// 8B/条 {to_nod, kind} 打包,文件序 = 节点运行序) +// 条目面:ent_* 族(本文件既有);区域面 = REG 行(g_sgs)遍历——Task 0 +// 定夺:对象面不含 region_of_nod(区域面仅 REG 行遍历)。 +// loader(load_ccr,ccr_io.cr)载入对象 + 守卫;NOD→g_ir_instrs 线性重建 +// 移实例(build_linear_schedule——regalloc.cr,调度 = 实例事务)。 +// A 通道中立化(内核完备 Task 2,2026-09-10):本文件零线性流 accessor +// (iri_*/g_ir_instrs)代码引用——compute_live_ranges/compute_entries/ +// dump_entries_summary/rl_rule2_func 的节点字段读(原 4 使用点)全走本对象 +// 面 nod_*(同 index 机械替换——F5 节点序 = 指令序)。节点面喂入 = +// loader(corearch 侧)或 populate_nod_objects(ccr_io.cr corec 写侧—— +// compute 前单遍 g_ir_instrs → g_v7_nod_sem 镜像,与 loader 解析逐字节对称; +// 写侧载体裁决 2026-09-10)。本文件访问器 = 内核接口面(读缓冲;调用方 +// 保证界内——ent_* 同约)。字段读走 buf_read_*(ccr_io.cr 真实函数体带符号 +// 扩展——r32 在 bootstrap 产物里零扩展,负值会读错,见 ent_var 注);无界 +// 检查与 ent_* 约定一致。 +ESZ_NOD_SEM : int = 32; // 内存对象记录 32B(= 盘 40B 剥离 first_edge/edge_count;R2 P6 T3:+4B 项索引) +ESZ_NOD_META : int = 16; // 邻接域记录 16B {first_edge i64 @0, edge_count i64 @8} +OFF_NS_OP : int = 0; // 语义字段 offset(布局镜像盘记录:s1 = 8B 占 +8..+16) +OFF_NS_DEST : int = 4; +OFF_NS_S1 : int = 8; +OFF_NS_S2 : int = 16; +OFF_NS_S3 : int = 20; +OFF_NS_TK : int = 24; +// R2 P6 Task 3(β):TYPE 段**文件空间**项索引(i32;-1 = 无项)。盘记录 +28, +// 邻接域自 +28/+32 顺移 +32/+36(语义区在前惯例自 v5 保持)。写侧 = 保存期 +// 单遍装填(ccr_types.cr——文件空间只在 ccr_type_populate 重建后成立); +// 读侧 = loader 直读 + TYPE 段解析后的域/一致性硬校验(ccr_io.cr load 尾)。 +OFF_NS_ITEM : int = 28; + +g_v7_nod_sem : string, mut; // NOD 语义字段对象缓冲(ESZ_NOD_SEM/节点——loader 载入) +g_v7_nod_meta : string, mut; // NOD 邻接域缓冲(ESZ_NOD_META/节点——EDG 守卫暂存同源) +g_v7_nod_count : int, mut; // 对象缓冲记录数(= NOD 段节点数——load 后可用) + +// 节点面访问器(n = NOD 节点坐标 0..g_v7_nod_count-1):语义字段读 +// g_v7_nod_sem、邻接索引读 g_v7_nod_meta——数值与 loader 解析(buf_read_*) +// 逐字节同(u32 字段 buf_read_u32 / 有符号 buf_read_i32 / s1 buf_read_i64)。 +fn nod_op(n: int) -> int { return buf_read_u32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_OP); } +fn nod_dest(n: int) -> int { return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_DEST); } +fn nod_s1(n: int) -> int { return buf_read_i64(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_S1); } +fn nod_s2(n: int) -> int { return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_S2); } +fn nod_s3(n: int) -> int { return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_S3); } +fn nod_tk(n: int) -> int { return buf_read_u32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_TK); } +// R2 P6 Task 3(β):项索引读访问器——**i32**(-1 = 无项必须读到 -1;码槽的 +// -1 在盘上是 u32 0xFFFFFFFF,两者语义不同,不得混用读法)。 +fn nod_item(n: int) -> int { return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_ITEM); } +fn nod_edge_first(n: int) -> int { return r64(g_v7_nod_meta, n * ESZ_NOD_META); } +fn nod_edge_count(n: int) -> int { return r64(g_v7_nod_meta, n * ESZ_NOD_META + 8); } + +// 边面访问器(e = 全局边下标 0..g_v7_edge_count-1):读 EDG 缓冲 8B 记录 +// {to_nod u32 @0, kind u32 @4}(loader 打包 {to, kind×2^32} 的低/高 32 位)。 +fn v7_edge_to(e: int) -> int { return buf_read_u32(g_v7_edges, e * 8); } +fn v7_edge_kind(e: int) -> int { return buf_read_u32(g_v7_edges, e * 8 + 4); } + +// 配方查询(薄封装):节点 n 的配方输入集 = 出边遍历——邻接索引区间 +// [nod_edge_first(n), nod_edge_first(n) + nod_edge_count(n))(v7 邻接约定: +// 节点 i 出边连续段,first_edge = 前缀累计),区间内逐边读 +// v7_edge_to/v7_edge_kind → 目标节点。节点无出边 = 空区间(count = 0)。 +// 补体注记(内核完备 Task 4——Task 1 评审 M-1 悬空注落实):Core 无闭包/ +// 迭代器——遍历动作封装在函数体内,现消费动作 = 配方边记录行输出(对象面 +// 诊断通道 dump_object_surface 调用方——函数化收敛:该通道内联遍历 = 本函数 +// 体搬移,dump 输出逐字节同,判据 = test_ccr_v7 对象面断言 + --dump-objects +// 语料 byte-compare)。纯值消费方(未来验证器)= 访问器直读模式(M-1 注记: +// 访问器齐备后遍历即查询)。 +fn nod_inputs(n: int) { + last : ., mut = nod_edge_first(n) + nod_edge_count(n); + ej : ., mut = nod_edge_first(n); + loop { + if ej >= last { break; } + print("edge "); print(int_str(n)); + print(" to "); print(int_str(v7_edge_to(ej))); + print(" kind "); println(int_str(v7_edge_kind(ej))); + ej = ej + 1; + } +} diff --git a/src/lsp/analysis.cr b/src/lsp/analysis.cr index 108cd061..9e2edba1 100644 --- a/src/lsp/analysis.cr +++ b/src/lsp/analysis.cr @@ -109,6 +109,13 @@ fn analysis_type_node_name(node: int, depth: int) -> string { out = out + analysis_type_node_name(ast_a(node), depth + 1); return out; } + if k == EXPR_OPTIONAL { + // R2 P3 Task 4:`T?` 的悬停/补全显示(内层名 + "?") + out : string, mut = ""; + out = out + analysis_type_node_name(ast_a(node), depth + 1); + out = out + "?"; + return out; + } if k == EXPR_ARRAY { out : string, mut = "["; out = out + analysis_type_node_name(ast_a(node), depth + 1); diff --git a/src/os/linux/callseq.cr b/src/os/linux/callseq.cr new file mode 100644 index 00000000..99fe30d2 --- /dev/null +++ b/src/os/linux/callseq.cr @@ -0,0 +1,220 @@ +// ══════════════════════════════════════════════════════════════ +// src/os/linux/callseq.cr —— OS 轴:SysV AMD64 调用约定序列 +// (寄存器参数分派 / 栈参压栈 / 栈清理 / 返回值 / 直接调用) +// +// x86 实例化波 1 Task 5 抽取(H1 波 1 最大风险之一——emit_instr 内联段落): +// emit_instr(src/arch/x86_64/instr.cr)中 IR_CALL / IR_CALL_EXTERN / +// IR_SPAWN / IR_RETURN 四分支的调用序列此前全部内联在巨函数里,本文件把它们 +// 抽为 5 个入口(发射序逐字节 verbatim——波 1 判据 = stage 链 byte-identical): +// ① cs_args_dispatch —— 寄存器参数分派(int: rdi,rsi,rdx,rcx,r8,r9; +// binary64: xmm0-7,int/float 各自独立编号)。**三处同源合流**:IR_CALL +// 主路径 / IR_CALL_EXTERN / IR_SPAWN 三处逐字节相同的分派段(差异核对 +// 见下),抽取后三调用点共用本入口。 +// ② cs_arg_on_stack / cs_stack_count / cs_stack_args —— 栈参判定 + 压栈 +// (右到左压;第 7 个 int / 第 9 个 binary64 起才压)。判定单入口 +// (cs_arg_on_stack)——计数(cs_stack_count,供清理立即数)与发射 +// (cs_stack_args)共用同一规则,防判定/计数双源漂移。 +// ③ cs_stack_cleanup —— call 后 add rsp 恢复(≤127B 用 4B 立即数形, +// 否则 7B 形)。 +// ④ cs_ret_value —— IR_RETURN 值序列(TI_DEX → xmm0;global → RIP +// 读 rax;局部 → rax 装载 + int 多字 tag 读路径 A)+ ret_patch 登记 + +// jmp 占位(尾声回填)。 +// ⑤ cs_call_direct —— 通用调用路径(按名查 g_x86_func_offsets → e2_call; +// 未找到 → g_x86_ext_rel_* 外部位)+ 返回值存(d ≥ 0)。 +// +// 归属裁决(设计 §1 "ABI = 交叉轴"):ABI = 架构轴寄存器集 × OS 轴调用约定 +// ——本文件 = 调用约定的 OS 轴落点;寄存器名/编码原语(e2_*/emit_*/g2_slot/ +// irv_type/TI_DEX)经架构轴 src/arch/x86_64/instr.cr(concat 扁平单元 / +// module.cr 三轴回退链解析,见组合根 _import.cr `import callseq`)。 +// 与 x86_64/instr.cr 的关系:instr.cr = 指令编码原语 + emit_instr 分派; +// 本文件 = 序列算法(多指令粘合序)——不新增任何编码原语。 +// +// 抽取纪律(H1):cp/pos/buf 三参数显式传递(无环境态假设)——buf = 字节 +// 缓冲、pos = 本 IR 指令的绝对缓冲起点、cp = 相对 pos 的已发字节数;返回 +// **新 cp**(相对量)。调用点形如 `cp = cs_xxx(buf, pos, cp, ...)`(与 +// emit_instr 内部约定一致;注:同波 frame.cr pf_* 取绝对位置约定,两者按 +// 各自宿主函数风格定形,见 frame.cr 头注)。 +// +// **三处同源合流差异核对结论**(抽取时逐处比对——不得静默合并): +// ✔ cs_args_dispatch:IR_CALL / IR_CALL_EXTERN / IR_SPAWN 三处发射字节 +// 逐字节相同(同一 e2_sd_load_x 序号 + e2_load_var 序列 + 同一 int 寄存器 +// 映射表 rdi(7),rsi(6),rdx(2),rcx(1),r8(8),r9(9)),合并等价。 +// ✔ cs_stack_args / cs_stack_cleanup:IR_CALL 与 IR_SPAWN 两处逐字节相同, +// 合并等价;**IR_CALL_EXTERN 不调用二者**(预存行为:extern 路径只做寄存器 +// 分派,无栈参压栈/栈清理——波 1 逐字节保持,不改判据;>6 int 参 extern +// 调用的语义缺口见 TODO 挂账,属波 2 参数化面)。 +// ✔ cs_call_direct:IR_CALL 专有(IR_HOTPATCH_ROUTE 形式近似但语义不同—— +// 恒外部位占位、无 g_x86_func_offsets 查名——保持分立原位)。 +// ✔ cs_ret_value:IR_RETURN 值序列专有;IR_CALL/IR_HOTPATCH_ROUTE 用 +// e2_store_ret(d ≥ 0 门)、IR_SPAWN 另有 TI_DEX/整数两分派、 +// IR_CALL_EXTERN 恒 e2_st(rax)(无 dex 分支、无 d 门)——差异面各自保留 +// 原位,不在本任务抽界内。 +// +// Depends on: x86_64/instr.cr(e2_*/emit_rex/emit_modrm/g2_slot/istr_get/ +// str_eq/str_intern)、x86_64/frame.cr(g2_tag_off——tag 读路径 A +// 的 tag 字节偏移)、x86_64/tag2l.cr(e2_mw_t8——tag 测试字节)、 +// 共享全局(g_x86_func_offsets/g_x86_call_patch_*/g_x86_ext_rel_*/ +// g_x86_ret_patch_*/g_x86_rip_patch_*/g_x86_is_global)。 +// ══════════════════════════════════════════════════════════════ + +// ── ① 寄存器参数分派(IR_CALL 主路径 / IR_CALL_EXTERN / IR_SPAWN 三处同源)── +// SysV AMD64:int 用 ir(0-5 → rdi,rsi,rdx,rcx,r8,r9),binary64 用 fr +// (0-7 → xmm0-7),各自独立编号(标准答案)。第一遍按位置顺序(左到右)发 +// 寄存器装载;超限者(第 7 int / 第 9 binary64 起)不在此处发射——留待 +// cs_stack_args(调用点自行决定是否压栈:IR_CALL/IR_SPAWN 压,EXTERN 不压)。 +// 返回新 cp。 +fn cs_args_dispatch(buf: string, pos: int, cp: int, fa: int, ac: int) -> int { + ir_cnt : ., mut = 0; fr_cnt : ., mut = 0; + ai := 0; + loop { if ai >= ac { break; } + pt := irv_type(fa + ai); + if pt == TI_DEX { + if fr_cnt < 8 { + cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + ai), fr_cnt); + fr_cnt = fr_cnt + 1; + } + } else { + if ir_cnt < 6 { + r := -1; + if ir_cnt == 0 { r = 7; } if ir_cnt == 1 { r = 6; } if ir_cnt == 2 { r = 2; } + if ir_cnt == 3 { r = 1; } if ir_cnt == 4 { r = 8; } if ir_cnt == 5 { r = 9; } + cp = cp + e2_load_var(buf, pos+cp, r, fa + ai); + ir_cnt = ir_cnt + 1; + } + } + ai = ai + 1; } + return cp; +} + +// ── ② 栈参:判定 / 计数 / 压栈(IR_CALL + IR_SPAWN 两处同源)── +// 第 i 个实参是否超限落栈(SysV:int 第 7 个起、binary64 第 9 个起)。 +// 判定 = 类型 × 该类型在 i 之前的实参个数(与提取前内联段同一规则)。 +// 1 = 落栈,0 = 走寄存器。 +fn cs_arg_on_stack(fa: int, i: int) -> int { + ic2 : ., mut = 0; fc2 : ., mut = 0; + j2 : ., mut = 0; + loop { if j2 >= i { break; } + if irv_type(fa + j2) == TI_DEX { fc2 = fc2 + 1; } else { ic2 = ic2 + 1; } + j2 = j2 + 1; } + if irv_type(fa + i) == TI_DEX { + if fc2 >= 8 { return 1; } + } else { + if ic2 >= 6 { return 1; } + } + return 0; +} + +// 实际压栈参数个数(cs_stack_cleanup 的 add rsp 立即数 = count*8)。 +// 与 cs_stack_args 同规则(cs_arg_on_stack)——两处计数恒等。 +fn cs_stack_count(fa: int, ac: int) -> int { + n : ., mut = 0; i : ., mut = 0; + loop { if i >= ac { break; } + if cs_arg_on_stack(fa, i) > 0 { n = n + 1; } + i = i + 1; } + return n; +} + +// 栈参压栈(右到左压——保持栈上实参位置顺序:调用时 [rsp] = 第 7 int / 第 9 +// binary64)。int → r10 装载 + push r10(41 52);binary64 → xmm0 装载 + +// e2_push_xmm0(sub rsp,8 + movsd [rsp],xmm0)。返回新 cp。 +fn cs_stack_args(buf: string, pos: int, cp: int, fa: int, ac: int) -> int { + stack_ai : ., mut = ac - 1; + loop { + if stack_ai < 0 { break; } + if cs_arg_on_stack(fa, stack_ai) > 0 { + if irv_type(fa + stack_ai) == TI_DEX { + cp = cp + e2_sd_load_x(buf, pos+cp, g2_slot(fa + stack_ai), 0); + cp = cp + e2_push_xmm0(buf, pos+cp); + } else { + cp = cp + e2_load_var(buf, pos+cp, 10, fa + stack_ai); + e2_w8(buf, pos+cp, 65); e2_w8(buf, pos+cp+1, 82); cp = cp + 2; // push r10 + } + } + stack_ai = stack_ai - 1; + } + return cp; +} + +// ── ③ 栈清理(IR_CALL + IR_SPAWN 两处同源)── +// call 后恢复 rsp:add rsp, stack_count*8——字节数 ≤127 → 4B 立即数形 +// (48 83 C4 imm8),否则 7B 形(48 81 C4 imm32)。返回新 cp。 +fn cs_stack_cleanup(buf: string, pos: int, cp: int, stack_count: int) -> int { + if stack_count > 0 { + stack_bytes := stack_count * 8; + if stack_bytes <= 127 { + e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 131); + e2_w8(buf, pos+cp+2, 196); e2_w8(buf, pos+cp+3, stack_bytes); cp = cp + 4; + } else { + e2_w8(buf, pos+cp, 72); e2_w8(buf, pos+cp+1, 129); e2_w8(buf, pos+cp+2, 196); + e2_w32(buf, pos+cp+3, stack_bytes); cp = cp + 7; + } + } + return cp; +} + +// ── ④ IR_RETURN 值序列 + ret_patch 登记 + jmp 占位 ── +// s1 < 0(无值)→ 只登记 ret_patch(尾声回填 jmp → epilogue)。 +// 返回新 cp。 +fn cs_ret_value(buf: string, pos: int, cp: int, s1: int) -> int { + if s1 >= 0 { + if irv_type(s1) == TI_DEX { + // binary64 返回:movsd xmm0, [slot](SysV 返回值在 XMM0,apx 快路径) + cp = cp + e2_sd_load(buf, pos+cp, g2_slot(s1)); + } else if r64(g_x86_is_global, s1 * 8) != 0 { + // Global: load via RIP-relative into rax + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + cp = cp + e2_lr(buf, pos+cp, 0); // lea r10, [rip+0] + // mov rax, [r10] — REX.WB + 0x8B + cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 10/8); e2_w8(buf, pos+cp, 139); cp = cp + 1; cp = cp + emit_modrm(buf, pos+cp, 0, 0, 10%8); + } else { + cp = cp + e2_ld(buf, pos+cp, 0, g2_slot(s1)); + // int 多字 M1(Task 4)tag 读路径 A:return 的 tagged 局部值 + // ——tag=1(槽 = 2-limb 指针)→ 解指针取低 limb → rax(exit + // 低 8 位 = 数学值低字节;不解 = 指针低字节 = 错值)。tag=0 + // → 快值原样。行内(je rel8 跨 3B deref——tag 字节测试)。 + tg := g2_tag_off(s1); + if tg != -1 { + cp = cp + e2_mw_t8(buf, pos+cp, tg); + w8(buf, pos+cp, 116); w8(buf, pos+cp+1, 3); cp = cp + 2; // je +3 + w8(buf, pos+cp, 72); w8(buf, pos+cp+1, 139); w8(buf, pos+cp+2, 0); cp = cp + 3; // mov rax, [rax] + } + } + } + // record position for caller to patch jmp → epilogue + grow_ret_patch(g_x86_ret_patch_count + 1); w64(g_x86_ret_patch_pos, g_x86_ret_patch_count * 8, pos + cp); + g_x86_ret_patch_count = g_x86_ret_patch_count + 1; + cp = cp + e2_jmp(buf, pos+cp, 0); + return cp; +} + +// ── ⑤ 通用调用路径(IR_CALL 的 s3 ≥ 0 分支)── +// 按函数名(interned string 索引)查 g_x86_func_offsets: +// 命中 → e2_call 直接相对调用(目标 = 代码起点 176 + 函数体偏移) +// 未命中 → 外部位登记(g_x86_ext_rel_*,动态链接期解析)+ call 占位 +// 两者都先登记 call 补丁(g_x86_call_patch_*,名字 → 后置回填实际 cp)。 +// 末尾 d ≥ 0 → e2_store_ret(int → rax / dex → xmm0)。返回新 cp。 +fn cs_call_direct(buf: string, pos: int, cp: int, s3: int, d: int) -> int { + fn2 := istr_get(s3); + to := -1; tf := 0; + loop { if tf >= g_x86_func_off_count { break; } if str_eq(istr_get(r64(g_x86_func_offsets, tf*16)), fn2) != 0 { to = r64(g_x86_func_offsets, tf*16+8); break; } tf = tf + 1; } + // Record call position for post-emission patching + grow_call_patch(g_x86_call_patch_count + 1); + w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp); + w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern(fn2)); + g_x86_call_patch_count = g_x86_call_patch_count + 1; + if to >= 0 { + cp = cp + e2_call(buf, pos+cp, (176 + to) - (pos + cp + 5)); + } else { + // Unknown function: emit external relocation (for dynamic linking) + grow_ext_rel(g_x86_ext_rel_count + 1); + w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp + 1); + w64(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, s3); + g_x86_ext_rel_count = g_x86_ext_rel_count + 1; + cp = cp + e2_call(buf, pos+cp, 0); + } + if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + return cp; +} diff --git a/src/os/linux/entry.cr b/src/os/linux/entry.cr new file mode 100644 index 00000000..36dfe1ed --- /dev/null +++ b/src/os/linux/entry.cr @@ -0,0 +1,136 @@ +// === src/os/linux/entry.cr(OS 轴:Linux 进程入口/启动集成;波 1 Task 2 自 +// src/format/elf/elf.cr 整函数迁入——emit_start/emit_start_size 逐字节 verbatim)=== +// _start 发射序:argc/argv 自初始栈装载 → g_rt_argc/g_rt_argv_ptr 存(RIP 补丁登记) +// → g_current_arena = -1 初始化 → 编译期常量全局初值环(imm32/imm64 两式) +// → call main(位置登记 g_call_main_pos 供重定位)→ exit(60) syscall。 +// 外部耦合(共享全局——声明留格式轴 elf.cr,扁平编译单元内单次声明): +// gv_argc / gv_argv / gv_current_arena —— elf_gen 扫描段(src/format/elf/elf.cr) +// 赋值;g_call_main_pos —— 本文件写(call main 位置登记)、elf.cr 补丁段读。 +// 消费方 = 本文件 + elf.cr 补丁段 + instr.cr get_arg(gv_argv)。跨轴引用经 +// module.cr 三轴回退链 / 组合根 _import.cr 的 `import entry` 解析(与 concat +// 清单 os_linux_files 双注册)。 +// Depends on: x86_64/instr.cr(emit_rex/emit_modrm/emit_sib/e2_*)、 +// x86_64/sizes.cr(sz_start_body/sz_start_argv_save) + +fn emit_start(buf: string, pos: int) -> int { + cp : ., mut = pos; + // mov rdi, [rsp] — load argc (rdi=7) + cp = cp + emit_rex(buf, cp, 1, 0, 0, 0); + e2_w8(buf, cp, 139); cp = cp + 1; + cp = cp + emit_modrm(buf, cp, 0, 7, 4); // [rsp] via SIB + cp = cp + emit_sib(buf, cp, 0, 4, 4); + + // lea rsi, [rsp+8] — pointer to argv (rsi=6, no REX extension) + cp = cp + emit_rex(buf, cp, 1, 0, 0, 0); + e2_w8(buf, cp, 141); cp = cp + 1; + cp = cp + emit_modrm(buf, cp, 1, 6, 4); // [rsp+disp8] + cp = cp + emit_sib(buf, cp, 0, 4, 4); + e2_w8(buf, cp, 8); cp = cp + 1; + + if gv_argc >= 0 { + // lea r10, [rip + 0] (placeholder, patched in Phase 3) + rip_pos := cp + 3; + cp = cp + e2_lr(buf, cp, 0); + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argc); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + // mov [r10], rdi — store argc + cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); + e2_w8(buf, cp, 137); cp = cp + 1; + cp = cp + emit_modrm(buf, cp, 0, 7, 10%8); + } + + if gv_argv >= 0 { + rip_pos2 := cp + 3; + cp = cp + e2_lr(buf, cp, 0); + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + // mov [r10], rsi — store argv + cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); + e2_w8(buf, cp, 137); cp = cp + 1; + cp = cp + emit_modrm(buf, cp, 0, 6, 10%8); + } + + // Initialize g_current_arena = -1 (no arena active) + if gv_current_arena >= 0 { + rip_ca := cp + 3; + cp = cp + e2_lr(buf, cp, 0); // lea r10, [rip+0] + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_ca); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + // mov qword [r10], -1 — REX.WB + 0xC7 /0 + cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8); + e2_w8(buf, cp, 199); cp = cp + 1; // 0xC7 MOV r/m64, imm32 + cp = cp + emit_modrm(buf, cp, 0, 0, 10%8); + e2_w32(buf, cp, -1); cp = cp + 4; // immediate = -1 (0xFFFFFFFF) + } + + // Write compile-time constant global initializers (g_ir_globals[i]. + // init_val, i64 at +16; 0 = none — BSS is already zero). + // g_current_arena is also covered by the explicit init above; the + // LET-based init below writes it again with the same value (harmless). + gi0 : ., mut = 0; + loop { if gi0 >= g_ir_global_count { break; } + iv := r64(g_ir_globals, gi0 * 24 + 16); + gvv0 := r64(g_ir_globals, gi0 * 24 + 8); + if iv != 0 && gvv0 >= 0 { + // lea r10, [rip+0] — rip_patch for the global's BSS slot + rip_pos_g := cp + 3; + cp = cp + e2_lr(buf, cp, 0); + grow_rip_patch(g_x86_rip_patch_count + 1); + w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_g); + w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gvv0); + g_x86_rip_patch_count = g_x86_rip_patch_count + 1; + if iv >= -2147483647 - 1 && iv <= 2147483647 { + // mov qword [r10], imm32 (sign-extended) — 49 C7 02 imm32 + w8(buf, cp, 73); w8(buf, cp+1, 199); w8(buf, cp+2, 2); + e2_w32(buf, cp+3, iv); cp = cp + 7; + } else { + // mov rax, imm64; mov [r10], rax + w8(buf, cp, 72); w8(buf, cp+1, 184); e2_w64(buf, cp+2, iv); cp = cp + 10; + w8(buf, cp, 73); w8(buf, cp+1, 137); w8(buf, cp+2, 2); cp = cp + 3; + } + } + gi0 = gi0 + 1; } + + g_call_main_pos = cp; + cp = cp + e2_call(buf, cp, 0); // call main + + // mov edi, eax + e2_w8(buf, cp, 137); cp = cp + 1; + cp = cp + emit_modrm(buf, cp, 3, 0, 7); + + // mov eax, 60 (sys_exit) + e2_w8(buf, cp, 184); cp = cp + 1; // 0xB8 MOV r, imm32 (rax) + cp = cp + e2_w32(buf, cp, 60); + + // syscall + e2_w8(buf, cp, 15); cp = cp + 1; + e2_w8(buf, cp, 5); cp = cp + 1; + + return cp - pos; +} + +fn emit_start_size() -> int { + sz : ., mut = sz_start_body(); + if gv_argc >= 0 { sz = sz + sz_start_argv_save(); } + if gv_argv >= 0 { sz = sz + sz_start_argv_save(); } + // g_current_arena init: lea(7) + rex+mov+modrm+imm32(7) = 14 bytes + if gv_current_arena >= 0 { sz = sz + 14; } + // Constant global initializers: lea r10(7) + mov imm32(7) = 14 bytes, + // or mov rax imm64 + mov [r10],rax = 20 bytes for large values. + gi0s : ., mut = 0; + loop { if gi0s >= g_ir_global_count { break; } + ivs := r64(g_ir_globals, gi0s * 24 + 16); + gvvs := r64(g_ir_globals, gi0s * 24 + 8); + if ivs != 0 && gvvs >= 0 { + if ivs >= -2147483647 - 1 && ivs <= 2147483647 { sz = sz + 14; } + else { sz = sz + 20; } + } + gi0s = gi0s + 1; } + return sz; +} diff --git a/src/os/linux/syscall.cr b/src/os/linux/syscall.cr new file mode 100644 index 00000000..df189a27 --- /dev/null +++ b/src/os/linux/syscall.cr @@ -0,0 +1,70 @@ +// ══════════════════════════════════════════════════════════════ +// src/os/linux/syscall.cr —— OS 轴:Linux syscall 约定(内置体发射序) +// (rax 号 + rdi/rsi/rdx(/r10) 参数序 + 0F 05 陷入 + rax 回存) +// +// x86 实例化波 1 Task 6 抽取:emit_instr(src/arch/x86_64/instr.cr)内置体 +// 分派链中 syscall3 / syscall4 两分支的发射序列此前内联在巨函数里,本文件 +// 把二者抽为两个入口(发射序逐字节 verbatim——波 1 判据 = stage 链 +// byte-identical): +// sys_syscall3_stub —— syscall3(num, a, b, c):通用装载器(cs_args_dispatch) +// 已按 SysV 把 4 个 int 实参放 rdi(号)/rsi/rdx/rcx,此处转位为 Linux +// syscall 约定 rax=号 / rdi,rsi,rdx=前三参 → 0F 05 → rax 回存(d ≥ 0)。 +// sys_syscall4_stub —— syscall4(num, a, b, c, d):第 4 参经 r10(**x86-64 +// syscall 约定:第 4 个非编号参数在 r10;rcx 被 syscall 指令用作返回 +// 地址**)——I-2 修复史随迁(wait4 rusage EFAULT,见函数内注)。 +// +// 归属裁决(设计 §1 "ABI = 交叉轴";§2 OS 轴文件表列 syscall.cr):syscall +// 约定 = OS 轴(Linux:rax 号 + rdi/rsi/rdx/r10 参数序);编码原语 +// (e2_mov/e2_w8/e2_store_ret)与内置体名索引/分派链(g_ni_syscall3/g_ni_ +// syscall4、`if s3 ==` 链)留架构轴 src/arch/x86_64/instr.cr(concat 扁平 +// 单元 / module.cr 三轴回退链解析,见组合根 _import.cr `import syscall`)。 +// 与 instr.cr 的关系:instr.cr = 指令编码原语 + emit_instr 分派;本文件 = +// syscall 发射序——不新增任何编码原语。 +// +// 抽取纪律(H1):cp/pos/buf 三参数显式传递(无环境态假设)——buf = 字节 +// 缓冲、pos = 本 IR 指令的绝对缓冲起点、cp = 相对 pos 的已发字节数;返回 +// **新 cp**(相对量)——与同波 callseq.cr cs_* 同约定(frame.cr pf_* 取绝对 +// 位置约定,按各自宿主函数风格定形,见 callseq.cr 头注)。d = 返回值目标 +// 变量(< 0 = 无回存)。 +// +// 内置体名索引扫描段(elf_gen 的 `str_eq(ns, "syscall3")` 段——g_ni_syscall3/ +// g_ni_syscall4 赋值)**保留原位**(src/format/elf/elf.cr,该处加注指向本 +// 文件):扫描段整体参数化 = 波 2 面(设计 §5——本任务只抽发射两函数)。 +// +// Depends on: x86_64/instr.cr(e2_mov/e2_w8/e2_store_ret——经 e2_st/e2_sd_store +// 分派)、共享全局(g_ni_syscall3/g_ni_syscall4——globals.cr 声明、 +// elf.cr 扫描段赋值、instr.cr 分派链消费)。 +// ══════════════════════════════════════════════════════════════ + +// ── syscall3(num, a, b, c) —— 三个非编号参数 ── +// 转位(rax ← rdi, rdi ← rsi, rsi ← rdx, rdx ← rcx)后 0F 05;返回值 rax +// 按 d 的类型回存(int 槽 / binary64 槽,见 e2_store_ret)。返回新 cp。 +fn sys_syscall3_stub(buf: string, pos: int, cp: int, d: int) -> int { + cp = cp + e2_mov(buf, pos+cp, 0, 7); + cp = cp + e2_mov(buf, pos+cp, 7, 6); + cp = cp + e2_mov(buf, pos+cp, 6, 2); + cp = cp + e2_mov(buf, pos+cp, 2, 1); + // syscall: 2-byte 0x0F 0x05 + e2_w8(buf, pos+cp, 15); e2_w8(buf, pos+cp+1, 5); cp = cp + 2; + if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + return cp; +} + +// ── syscall4(num, a, b, c, d) —— 第四个非编号参数经 r10 ── +// syscall4(num, a, b, c, d)——第 4 参 d 经 r10 传递(x86-64 syscall +// 约定:第 4 参在 r10;rcx 被 syscall 指令用作返回地址)。 +// I-2:wait4 的 rusage 此前无第 4 参通道——通用装载器把第 4 参放 +// rcx(ir_cnt=3)、第 5 参放 r8(ir_cnt=4),syscall 读 r10 = 残留 +// 垃圾 → 内核 EFAULT 或写错地址 → 退出码传播不可靠。 +// 返回新 cp。 +fn sys_syscall4_stub(buf: string, pos: int, cp: int, d: int) -> int { + cp = cp + e2_mov(buf, pos+cp, 0, 7); // rax = rdi — syscall number + cp = cp + e2_mov(buf, pos+cp, 7, 6); // rdi = rsi — arg1 + cp = cp + e2_mov(buf, pos+cp, 6, 2); // rsi = rdx — arg2 + cp = cp + e2_mov(buf, pos+cp, 2, 1); // rdx = rcx — arg3 + cp = cp + e2_mov(buf, pos+cp, 10, 8); // r10 = r8 — arg4 + // syscall: 2-byte 0x0F 0x05 + e2_w8(buf, pos+cp, 15); e2_w8(buf, pos+cp+1, 5); cp = cp + 2; + if d >= 0 { cp = cp + e2_store_ret(buf, pos+cp, d); } + return cp; +} diff --git a/src/stdlib/toml.cr b/src/stdlib/toml.cr index fc53b30c..20244c72 100644 --- a/src/stdlib/toml.cr +++ b/src/stdlib/toml.cr @@ -150,6 +150,112 @@ fn extract_toml_name(content: string) -> string { return toml_get_str(content, "name"); } +// Hex digit value (0-15), -1 if not a hex digit. +fn _toml_hex_digit(c: int) -> int { + if c >= 48 && c <= 57 { return c - 48; } + if c >= 65 && c <= 70 { return c - 55; } // 'A'-'F' + if c >= 97 && c <= 102 { return c - 87; } // 'a'-'f' + return -1; +} + +// Extract an int array value: key = [a, b, c](项支持十进制与 0x/0X 十六进制, +// 非负整数;值写入 out,每项 4B 小端)。供 HIT 表 opcode = [0x48, 0x29] 类 +// 条目使用(见 src/arch/x86_64/core-x86.toml)。 +// Returns item count on success(遇 ']' 收尾);-1 = key 缺 / 值非数组 / +// 项格式错 / 项数超过 max_items(不静默截断)。 +fn toml_get_int_list(content: string, key: string, out: string, max_items: int) -> int { + slen := str_len(content); + klen := str_len(key); + pos : ., mut = 0; + loop { + if pos >= slen { return -1; } + if !_is_at_line_start(content, pos) { + pos = _next_line_start(content, pos); + continue; + } + spos := _skip_whitespace(content, pos); + if spos + klen > slen { + pos = _next_line_start(content, pos); + continue; + } + sub := str_sub(content, spos, klen); + if str_eq(sub, key) == 0 { + pos = _next_line_start(content, pos); + continue; + } + eq_pos := _skip_whitespace(content, spos + klen); + if load8(content, eq_pos) != 61 { // 非 '=' 的行不算命中 + pos = _next_line_start(content, pos); + continue; + } + val_pos := _skip_whitespace(content, eq_pos + 1); + if load8(content, val_pos) != 91 { // 值非 '[' 开头 → 非数组,继续找下一条 + pos = _next_line_start(content, pos); + continue; + } + // Parse list items + p : ., mut = val_pos + 1; + n : ., mut = 0; + loop { + if p >= slen { return -1; } + // Skip whitespace (incl. newlines: lists may span lines) + ch : ., mut = 0; + loop { + if p >= slen { return -1; } + ch = load8(content, p); + if ch == 32 || ch == 9 || ch == 10 || ch == 13 { + p = p + 1; + } else { break; } + } + if ch == 93 { break; } // ']' → list end(此处 return n) + if ch == 44 { p = p + 1; continue; } // ',' — 宽容跳过分隔符 + if n >= max_items { return -1; } // 项数超限(不静默截断) + v : ., mut = 0; + // Hex item: 0x/0X prefix + if ch == 48 && p + 1 < slen && + (load8(content, p + 1) == 120 || load8(content, p + 1) == 88) { + p = p + 2; + if p >= slen { return -1; } + hd := _toml_hex_digit(load8(content, p)); + if hd < 0 { return -1; } + v = hd; + p = p + 1; + loop { + if p >= slen { break; } + ch = load8(content, p); + hd2 := _toml_hex_digit(ch); + if hd2 < 0 { break; } + v = v * 16 + hd2; + p = p + 1; + } + } else { + // Decimal item(须以数字开头) + if ch < 48 || ch > 57 { return -1; } + v = ch - 48; + p = p + 1; + loop { + if p >= slen { break; } + ch = load8(content, p); + if ch >= 48 && ch <= 57 { + v = v * 10 + (ch - 48); + p = p + 1; + } else { break; } + } + } + // Item terminator must be ws / ',' / ']' + if ch != 32 && ch != 9 && ch != 10 && ch != 13 && + ch != 44 && ch != 93 { return -1; } + store8(out, n * 4 + 0, v % 256); + store8(out, n * 4 + 1, (v / 256) % 256); + store8(out, n * 4 + 2, (v / 65536) % 256); + store8(out, n * 4 + 3, (v / 16777216) % 256); + n = n + 1; + } + return n; + } + return -1; +} + // Memory layout configuration struct MemLayout { stack_size: int, diff --git a/src/arch/linux/ld/Core.toml b/src/targets/x86_64-linux/Core.toml similarity index 100% rename from src/arch/linux/ld/Core.toml rename to src/targets/x86_64-linux/Core.toml diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux/_import.cr new file mode 100644 index 00000000..0488501e --- /dev/null +++ b/src/targets/x86_64-linux/_import.cr @@ -0,0 +1,80 @@ +// _import.cr — imports for the corearch backend project +import cli +import fmt +import io +import toml +import ast +import globals +import dyn_arr +import hit +// 降低层(HIT M2):instr.cr 的表编码器消费 lower_to_core.cr 的事件流 +// (hit_rel_add/hit_ev_*/hit_pool_patch_add/HIT_EV_*/g_hit_*)——本文件原先只 +// import hit 而漏 lower_to_core,致 ld project-mode 单元 15×N06+16×N01 静默未 +// 定义(含 TA01 级联;与 c138c44c「compiler 单元缺 ent_kernel」同类)。经 +// src/arch/hit/ 跨树回退命中(与 hit 同机制、同目录)。 +import lower_to_core +// R2 P3 Task 7 收官修复(2026-09-12):此处原 `import monomorph`——P3 Task 5 给 monomorph.cr +// 加的实例键类型项化(inst_key_of_ti/inst_type_node_of_ti 族)引入 checker/parser 层符号 +// (get_type_kind/get_type_data/get_type_extra/alloc_node/alloc_type),project-mode corearch +// 单元集不含该层 ⇒ 构建 33×error[N06] 静默未定义(rc=0 + 产物照出 ⇒ stage 链互测不可见); +// concat 面 build_selfhost_native.py 的 backend_support_files 已同批迁出,唯本文件漏改。 +// 与 concat 面同一证据:monomorph 在 corearch 链接集**零代码引用**(全部定义符只出现在 +// globals.cr 的注释里),单态化本就是前端 ir_gen 期动作。删除本行 = 两面清单重新对齐; +// 该缺陷的机械守卫 = tests/selfhost/test_backend_bootstrap.py 的 project-mode error[ 门 +// (TODO #31:该套件未挂 CI,收官全量枚举才跑到)。若将来自复活本文件,须先让它不依赖 +// checker/parser 层符号。 +// R2 P4 Task 2(TYPE 段内容面读回):ccr_io.cr 的 load 侧重建类型项表——调 +// tt_hash5/grow_tt_index/tt_reindex/tt_layer_reset(type_terms.cr)与 --dump-types +// 通道的项层原语(tt_norm/tt_is_dnf/tt_is_literal)。**必须先于 ccr_io 入本清单** +// (常量可见性:ESZ_TYPE_TERM/OFF_TT_*/TT_*——若漏列本行,project-mode corearch +// 单元即 N06 静默未定义,B.6 同族;concat 面 = backend_support_files 头部同位置)。 +// **type_engine.cr(TF01 收口 2026-09-13 起入本清单)**:原阻塞 = 该文件带两条诊断 +// (lits_copy 类型洗白 TF01 + ty_memo_slot_no_grow loop-落空误报)会触发本单元的 +// project-mode `error[`=0 门(test_backend_bootstrap);两条已修(lits_copy 返回型改 +// string;落空分析入 checker.cr)⇒ 判定原语(ty_sub 族)跨进程同值面解阻 +// (T2 报告的收口路径 (a))。位置 = type_terms 之后、ccr_io 之前(同 concat 面)。 +import type_terms +import type_engine +import ccr_io +import ent_kernel +// 内核完备 Task 1(调度重建移实例):build_linear_schedule(regalloc.cr—— +// 实例侧调度重建)须进自举 stage 链编译集——本文件(自举 project 入口 +// main.cr 的共享导入)与 build_selfhost_native.py corearch concat 双注册 +// (regalloc 原 = concat 独有;main.cr 的 load 后调用依赖本导入)。 +import regalloc +import sizes +import instr +// 架构轴 tag2l(x86 实例化波 1 Task 4 落位):int 多字 M1 的 tag 表 owner +// (mw_setup_tags——elf.cr Phase 2/3 各一次)+ mw 族纯编码(jo 溢出跳/慢路径块/ +// 2L 操作数块/tag 卫生助手——自 instr.cr 整函数迁出)+ 表发射门 +// mw_int_arith_jo_needed。与 frame.cr 同轴双向引用(裁定②可接受——frame 的 +// pf_epilogue 调本文件 e2_mw_*,本文件经 frame 的 g2_tag_off 读表)。project-mode +// 单元须经本清单收编(回退链命中 src/arch/x86_64/;concat 面 = arch_x86_64_files)。 +import tag2l +// 架构轴 frame(x86 实例化波 1 Task 3 落位):函数帧单源——pf_frame_size +// (Phase 2 dry-run 与 Phase 3 sub rsp 立即数共用)+ pf_prologue/pf_epilogue +// (帧发射序自 format/elf/elf.cr 抽出)+ g2_tag_off(自 instr.cr 迁入,H3)。 +// project-mode 单元须经本清单收编(回退链命中 src/arch/x86_64/;concat 面 = +// arch_x86_64_files)。 +import frame +import resolve +import elf +import ld +// OS 轴(x86 实例化波 1 Task 2 落位):_start 发射序 emit_start/emit_start_size +// 自 format/elf/elf.cr 整函数迁 src/os/linux/entry.cr——elf.cr 的调用点跨轴引它, +// project-mode 单元须经本清单收编(回退链命中 src/os/linux/;concat 面 = os_linux_files)。 +import entry +// OS 轴(x86 实例化波 1 Task 5 落位):SysV AMD64 调用约定序列——cs_args_dispatch +// (寄存器参数分派,IR_CALL/EXTERN/SPAWN 三处同源合流)/cs_stack_args(+ 判定 +// cs_arg_on_stack/计数 cs_stack_count)/cs_stack_cleanup/cs_ret_value(IR_RETURN +// 值序列 + tag 读路径 A)/cs_call_direct——自 instr.cr emit_instr 内联段落抽出。 +// instr.cr 的四分支调用点跨轴引它,project-mode 单元须经本清单收编(回退链命中 +// src/os/linux/;concat 面 = os_linux_files)。 +import callseq +// OS 轴(x86 实例化波 1 Task 6 落位):Linux syscall 约定发射序——sys_syscall3_stub +// (rax 号 + rdi/rsi/rdx 参数序 + 0F 05 + 回存)/sys_syscall4_stub(第 4 参经 r10 +// ——I-2 wait4 EFAULT 修复史随迁)——自 instr.cr emit_instr 内置体分派链抽出 +// (syscall3/4 两分支改调)。内置体名索引扫描段留 elf.cr 原位(波 2 参数化面)。 +// instr.cr 的分派链跨轴引它,project-mode 单元须经本清单收编(回退链命中 +// src/os/linux/;concat 面 = os_linux_files)。 +import syscall diff --git a/src/arch/linux/ld/main.cr b/src/targets/x86_64-linux/main.cr similarity index 79% rename from src/arch/linux/ld/main.cr rename to src/targets/x86_64-linux/main.cr index dee96e01..b6b8d7a5 100644 --- a/src/arch/linux/ld/main.cr +++ b/src/targets/x86_64-linux/main.cr @@ -1,6 +1,20 @@ -// === arch/linux/ld/main.cr === +// === targets/x86_64-linux/main.cr === // Backend project entry: .ccr → ELF/assembly/SO // Self-hosted counterpart of src/compiler/corearch.cr +// +// 活入口注记(2026-09-10 内核完备 Task 1 评审 I-1 修正——原「死代码」注记驳斥): +// 本文件 = 组合根(x86_64-linux target)project-mode 构建的活入口—— +// corec build src/targets/x86_64-linux +// 以本文件 main:160 → corearch_main :31 → load_ccr :64 → build_linear_schedule +// :71 为链,backend_bootstrap stage 链全程经此入口自举(tests/selfhost/ +// test_backend_bootstrap.py project mode:corec build <目录> 的入口 = 该目录 +// main.cr——与 concat 清单并列的独立构建机制;Task 1 偏差 ① 实证并接线)。 +// 入口二元性:corearch concat 清单入口 = src/compiler/corearch.cr(wrapper +// corearch_main),组合根 project-mode 入口 = 本文件——两条均活、均已接线(load 后 +// 调 build_linear_schedule)。内核抽取 Task 4 原注记「零 concat 引用 → 实际入口 +// = corearch.cr → 删除挂账」仅核对 concat 清单而误判 project-mode 入口;两入口 +// 未来如需合并/删除,须同步双入口接线与 _import.cr 导入集(现 load 后行为同构; +// 表模式/调试通道的 flag 注册分歧 = TODO #6,留波 2 收敛——本注记仅措辞同步)。 fn init_backend_arrays() { g_x86_var_count = 0; g_x86_stack_size = 0; g_x86_func_idx = 0; g_x86_is_enum_count = 0; @@ -51,6 +65,11 @@ fn corearch_main() -> int { if r != 0 { println("error: invalid .ccr file"); return 1; } init_backend_arrays(); + // 内核完备 Task 1(调度重建移实例):loader 只产语义对象——线性流重建 = + // 实例事务(build_linear_schedule——本文件所在自举 stage 链 = 本函数实际 + // 消费方之一:load 后、elf_gen 前必须重建,产物逐字节同移出前)。 + build_linear_schedule(); + emit_so := cli_has("shared"); link_val := cli_get("link"); out_path := cli_get("output"); diff --git a/tests/bootstrap/test_pipeline.py b/tests/bootstrap/test_pipeline.py index 83fec080..6ec728ae 100644 --- a/tests/bootstrap/test_pipeline.py +++ b/tests/bootstrap/test_pipeline.py @@ -326,11 +326,18 @@ def test_raw(name, src, expected): from corec.syntax.tokens import TokenType def check_integer_literals(): - tokens = Lexer('0x1f 0o17 0b1010 1_000').tokenize() + tokens = Lexer('0x1f 0o17 0b1010').tokenize() values = [int(t.lexeme) for t in tokens[:-1]] - if values != [31, 15, 10, 1000]: + if values != [31, 15, 10]: print(f'[FAIL] integer literal forms: got {values}') return False + for bad in ('1_000', '0x1_0', '10f32', '1_000.5'): + try: + Lexer(bad).tokenize() + except SyntaxError: + continue + print(f'[FAIL] rejected form was accepted: {bad}') + return False try: Lexer('0x').tokenize() except SyntaxError: @@ -338,7 +345,7 @@ def check_integer_literals(): else: print('[FAIL] empty prefixed integer was accepted') return False - print('[PASS] integer literal forms: hex/octal/binary/separators') + print('[PASS] integer literal forms: hex/octal/binary; separators/suffixes rejected') return True if not check_integer_literals(): diff --git a/tests/hit/smoke_add.cr b/tests/hit/smoke_add.cr new file mode 100644 index 00000000..ca849ed6 --- /dev/null +++ b/tests/hit/smoke_add.cr @@ -0,0 +1,7 @@ +// === tests/hit/smoke_add.cr === +// HIT M1 冒烟:减法(sub 事件直通;const 走常量池 load)。期望 exit 2(7-5)。 +fn main() -> int { + a := 7; + b := 5; + return a - b; +} diff --git a/tests/hit/smoke_mem.cr b/tests/hit/smoke_mem.cr new file mode 100644 index 00000000..587d486b --- /dev/null +++ b/tests/hit/smoke_mem.cr @@ -0,0 +1,15 @@ +// === tests/hit/smoke_mem.cr === +// HIT M1 冒烟:内存读写 + 加法链(add → sub 反减;store/load 事件)。 +// 期望 exit 5(6+3=9,9-4=5)。 +// +// 注(与 plan Task 4 草案差异):原草案载体含 `a & b`(位与)——Core 源语言 +// 无位与/位或运算符(& 为引用取址;&&/|| 恒短路分支化 = IR_BRANCH,超 M1 直线 +// 子集),位与经 && 亦不可达 IR_BINARY(OP_AND)。载体改为链式加减 + 内存读写; +// and→nand 合成为 IR 层完备性代码(M2 位运算符落地后启用)。 +fn main() -> int { + a: ., mut = 6; + b := 3; + a = a + b; + a = a - 4; + return a; +} diff --git a/tests/probes/README.md b/tests/probes/README.md new file mode 100644 index 00000000..617754fb --- /dev/null +++ b/tests/probes/README.md @@ -0,0 +1,79 @@ +# 行为探针语料(判定面回归网腿 ② 的载体) + +**来源**:R2 P5 各任务在 `/tmp` 侧的探针语料**实拷入仓**(R2 P6 Task 1 / 交接包 **E-13**)。 + +> **子目录 `warm/`(#60 T3 新增,7 档)**:**暖态腿**专用语料,**不进本目录的 29 档清单** +> (本 runner 是非递归 glob,不会收集它)。判据、定路径要求与用法见 `warm/README.md` 与 +> `tools/baseline/warm_leg.sh` 头注。 +**性质**:**不是**编译器编译单元(不在任何清单内、不被任何套件收集)——它们是 `check` 面的**行为断言输入**, +由 `tools/baseline/probes_run.sh` 逐档跑(`clean-cache` → `check` → 记 rc + 日志)。 + +## 用法 + +```bash +bash tools/baseline/probes_run.sh ./build/corec /tmp/probes_now # 当前二进制 +bash tools/baseline/probes_run.sh /tmp/p6-baseline/corec /tmp/probes_base # 冻结基线 +diff -rq /tmp/probes_now /tmp/probes_base # 两态零差异 = 判定面行为未变 +``` + +**rc 判读**:本目录语料**大量为负例**(期望 rc=1)——`rc=1` 是**预期行为**(诊断正确拒绝), +不是失败。判据 = **两态逐档 rc + 日志零差异**(与 P5 记录值比),不是「全 rc=0」。 + +## 清点(**29 档** = `/tmp/p5t0/probes` 11 + `/tmp/p5t3/probes` 18) + +> **U-4 收口**:P5 台账曾记「30 档」——**这是假象**:迁移期 runner 的 +> `for f in /tmp/p5t3b/probes/*.cr` 在零匹配目录上迭代**字面 glob**,产生伪条目 +> `_tmp_p5t3b_probes_*.cr`(rc=1,log = `error: cannot read …/*.cr`),两态恒等 ⇒ 该条对拍是 +> **空洞证据**。**正式值 = 29**(R2 P6 T0 §3.2);`/tmp/p5t3b/probes` 是 T3b 的**输出**目录 +> (21 log + 6 dump),**不是**语料目录。此后文档一律写 29。 + +| # | 入仓文件 | 原路径(`/tmp`) | 出处 | 形态 | P6 起点 rc | +|---|---|---|---|---|---| +| 1 | `p_annots.cr` | `/tmp/p5t0/probes/p_annots.cr` | P5 T0 §2(表 A 探针) | `@no_bounds_check` **语句**形态(`IR_NO_BOUNDS_CHECK`/`IR_FAST` **零 IR** — F4 登记) | 0 | +| 2 | `p_annots2.cr` | 同目录 | P5 T0 §2.3(F4) | `@no_bounds_check()` **括号**形态(唯一可达形态) | 0 | +| 3 | `p_dex.cr` | 同目录 | P5 T0 §2(表 A) | dex 算术 + `as int`(`IR_I2F`/`IR_F2I`/`IR_BINARY` 行值为 `TI_FLOAT`) | 0 | +| 4 | `p_dyn.cr` | 同目录 | 同上 | `dyn` 槽(`TYP_DYN` 行面) | 0 | +| 5 | `p_enum.cr` | 同目录 | 同上 | 枚举构造 + match(`IR_MAKE_ENUM`/`IR_LOAD_ENUM_TAG`) | 0 | +| 6 | `p_ffi.cr` | 同目录 | 同上 | `extern "C" fn`(字面量 ABI 形态)——**期望 rc=1**(该形态不可解析) | 1 | +| 7 | `p_ffi2.cr` | 同目录 | 同上 | `extern fn`(`IR_CALL_EXTERN` 可达形态) | 0 | +| 8 | `p_go.cr` | 同目录 | 同上 | `go work()`(`IR_SPAWN`/`IR_YIELD`/`IR_AWAIT`) | 0 | +| 9 | `p_i2f.cr` | 同目录 | 同上 | int→dex 返回位(`IR_I2F` 恒 `TI_FLOAT` 行) | 0 | +| 10 | `p_opt.cr` | 同目录 | 同上 | `T?` + `Some` + `?` 解包(可选表示面) | 0 | +| 11 | `p_spawn.cr` | 同目录 | P5 T0 §2(`IR_SPAWN` 的 `aux=-1` 辅码面) | `go i 0..3 f(i)`(动态 spawn 计数) | 0 | +| 12 | `n02_diff_named.cr` | `/tmp/p5t3/probes/n02_diff_named.cr` | P5 T3(表 D d2) | **异名同形**命名互赋(命名面判定化主例) | 1 | +| 13 | `n03_apply_twice.cr` | 同目录 | P5 T3(表 D d3b) | **同实参泛型应用两次实例化**(必须判**等**) | 0 | +| 14 | `n04_apply_diff_args.cr` | 同目录 | P5 T3 | 异实参应用(判不等) | 1 | +| 15 | `n05_recursive.cr` | 同目录 | P5 T3(表 D d4) | 递归命名 `struct Node { next: *Node }` | 1 | +| 16 | `n09_t_vs_int.cr` | 同目录 | P5 T3(表 D d9) | 泛型形参位 `T` vs `int` | 0 | +| 17 | `n10_named_vs_int.cr` | 同目录 | P5 T3(混类收紧面) | 命名 × 原生(混类 ⇒ 0) | 1 | +| 18 | `n12_alias.cr` | 同目录 | P5 T3 | 类型别名一层透明 | 0 | +| 19 | `n13_arr_diff_named.cr` | 同目录 | **P5 T3b**(§1 残留面) | `[NA;2]` vs `[NB;2]`(不变槽元素三态) | 1 | +| 20 | `n14_optional_diff_named.cr` | 同目录 | P5 T3 | 可选异名 `NA?` vs `NB?` | 1 | +| 21 | `n15_apply_named_arg.cr` | 同目录 | P5 T3 | 应用含命名实参 | 1 | +| 22 | `n16_nested_apply.cr` | 同目录 | P5 T3 | 嵌套应用 `Box[Box[int]]` 两次实例化 | 0 | +| 23 | `n17_ptr_named.cr` | 同目录 | **P5 T3b** | `*NA` vs `*NB` | 1 | +| 24 | `n18_tuple_named.cr` | 同目录 | **P5 T3b** | 元组含异名 `(NA,int)` vs `(NB,int)` | 1 | +| 25 | `n19_ref_named.cr` | 同目录 | P5 T3 | `&NA` vs `&NB`(引用元素位) | 0 | +| 26 | `n20_apply_named_vs_int.cr` | 同目录 | P5 T3(混类) | `Box[NA]` vs `Box[int]` | 1 | +| 27 | `n21_t_assign_int.cr` | 同目录 | P5 T3(混类) | `T` 赋 `int` | 1 | +| 28 | `n22_named_struct_field.cr` | 同目录 | P5 T3 | 命名结构体字段位 | 0 | +| 29 | `n23_enum_named.cr` | 同目录 | P5 T3 | 异枚举同名变体(身份域) | 0 | + +**起点 rc 分布**:**17×0 / 12×1**(R2 P6 T0 实跑,与 P5 T7 `/tmp/p5t7/probes_post/rc.txt` 的 29 条实档**逐档同**)。 + +**逐档 sha256 交叉核**:入仓 29 档 sha 与 T0 的 `/tmp/p6t0/probes_manifest.txt` **29/29 逐条同** +(实拷、未改写——**不要**在探针源里加注释:逐字节保真才能复现 rc/日志)。 + +## 运行条件(不得含糊) + +1. **变异态证据不在本目录**:T3b 的 `n13/n17/n18` 在**变异二进制**(M1..M4)下的转红证据 + (`/tmp/p5t3b/probes/*.M{1..4}.log`、`.M2.dump`,共 15 件)需**先重造变异二进制**才能复跑 + ——配方见 `p5-task3b-report.md` §3.3,**不随语料入仓**(登记,不假装可复跑)。 +2. **影子通道产物不可复现**:T3b 的 42 行 `[type-shadow…]` 摘要行(21 log × 2)**无二进制可产** + (通道已随 P5 T5 删除)⇒ **不得**引用为证据(三态纪律:不可产出的读数 = 无读数)。 +3. **红态复现**(`n13/n17/n18` 的 pre-T3b 行为)须用**冻结基线**(其仍带影子旗标)—— + R2 P6 T0 §3.3 已实测:冻结基线 × 三档 + `--type-shadow*` ⇒ 与 `p5t3b` 的 `red.log`/`red.dump` + **逐字节同**(log 归一化 dump 路径行后)。 +4. **cwd = 仓库根**(runner 已 `cd`;`/tmp` cwd 会改变相对 import 解析域 ⇒ 假 rc)。 +5. **探针语料只增不减**:新增/删除须同步 `tools/baseline/probes_run.sh` 的 `PROBE_TOTAL` 与本表 + (计数不符 = runner 硬失败)。 diff --git a/tests/probes/n02_diff_named.cr b/tests/probes/n02_diff_named.cr new file mode 100644 index 00000000..9c66c716 --- /dev/null +++ b/tests/probes/n02_diff_named.cr @@ -0,0 +1,8 @@ +struct NA { x: int } +struct NB { x: int } +fn main() -> int { + a : ., mut = NA { x: 1 }; + b : ., mut = NB { x: 2 }; + a = b; + return a.x; +} diff --git a/tests/probes/n03_apply_twice.cr b/tests/probes/n03_apply_twice.cr new file mode 100644 index 00000000..090e82cb --- /dev/null +++ b/tests/probes/n03_apply_twice.cr @@ -0,0 +1,6 @@ +struct Box[T] { v: T } +fn g(a: Box[int], b: Box[int]) -> int { + a = b; + return a.v; +} +fn main() -> int { return 0; } diff --git a/tests/probes/n04_apply_diff_args.cr b/tests/probes/n04_apply_diff_args.cr new file mode 100644 index 00000000..fe820c49 --- /dev/null +++ b/tests/probes/n04_apply_diff_args.cr @@ -0,0 +1,6 @@ +struct Box[T] { v: T } +fn g(a: Box[int], b: Box[str]) -> int { + a = b; + return a.v; +} +fn main() -> int { return 0; } diff --git a/tests/probes/n05_recursive.cr b/tests/probes/n05_recursive.cr new file mode 100644 index 00000000..b315c648 --- /dev/null +++ b/tests/probes/n05_recursive.cr @@ -0,0 +1,7 @@ +struct Node { val: int, next: *Node } +fn main() -> int { + a : ., mut = Node { val: 1, next: 0 }; + b : ., mut = Node { val: 2, next: 0 }; + a = b; + return a.val; +} diff --git a/tests/probes/n09_t_vs_int.cr b/tests/probes/n09_t_vs_int.cr new file mode 100644 index 00000000..431a7d77 --- /dev/null +++ b/tests/probes/n09_t_vs_int.cr @@ -0,0 +1,6 @@ +fn h[T](a: T, b: T) -> T { return a; } +fn main() -> int { + x := h(1, 2); + y : int = x; + return y; +} diff --git a/tests/probes/n10_named_vs_int.cr b/tests/probes/n10_named_vs_int.cr new file mode 100644 index 00000000..a2884a22 --- /dev/null +++ b/tests/probes/n10_named_vs_int.cr @@ -0,0 +1,7 @@ +struct NA { x: int } +fn main() -> int { + a : NA = NA { x: 1 }; + b : int = 2; + a = b; + return a.x; +} diff --git a/tests/probes/n12_alias.cr b/tests/probes/n12_alias.cr new file mode 100644 index 00000000..6b17c3a2 --- /dev/null +++ b/tests/probes/n12_alias.cr @@ -0,0 +1,7 @@ +type X = int; +fn main() -> int { + a : X = 1; + b : int = 2; + a = b; + return a; +} diff --git a/tests/probes/n13_arr_diff_named.cr b/tests/probes/n13_arr_diff_named.cr new file mode 100644 index 00000000..7f349df2 --- /dev/null +++ b/tests/probes/n13_arr_diff_named.cr @@ -0,0 +1,8 @@ +struct NA { x: int } +struct NB { x: int } +fn main() -> int { + a : [NA; 2] = [NA { x: 1 }, NA { x: 2 }]; + b : [NB; 2] = [NB { x: 3 }, NB { x: 4 }]; + a = b; + return 0; +} diff --git a/tests/probes/n14_optional_diff_named.cr b/tests/probes/n14_optional_diff_named.cr new file mode 100644 index 00000000..cde2f237 --- /dev/null +++ b/tests/probes/n14_optional_diff_named.cr @@ -0,0 +1,8 @@ +struct NA { x: int } +struct NB { x: int } +fn main() -> int { + a : NA? = NA { x: 1 }; + b : NB? = NB { x: 2 }; + a = b; + return 0; +} diff --git a/tests/probes/n15_apply_named_arg.cr b/tests/probes/n15_apply_named_arg.cr new file mode 100644 index 00000000..33611e84 --- /dev/null +++ b/tests/probes/n15_apply_named_arg.cr @@ -0,0 +1,6 @@ +struct NA { x: int } +struct NB { x: int } +struct Box[T] { v: T } +fn g(a: Box[NA], b: Box[NA]) -> int { a = b; return a.v.x; } +fn h(a: Box[NA], b: Box[NB]) -> int { a = b; return a.v.x; } +fn main() -> int { return 0; } diff --git a/tests/probes/n16_nested_apply.cr b/tests/probes/n16_nested_apply.cr new file mode 100644 index 00000000..3388c116 --- /dev/null +++ b/tests/probes/n16_nested_apply.cr @@ -0,0 +1,3 @@ +struct Box[T] { v: T } +fn g(a: Box[Box[int]], b: Box[Box[int]]) -> int { a = b; return 0; } +fn main() -> int { return 0; } diff --git a/tests/probes/n17_ptr_named.cr b/tests/probes/n17_ptr_named.cr new file mode 100644 index 00000000..6cf1c3e9 --- /dev/null +++ b/tests/probes/n17_ptr_named.cr @@ -0,0 +1,4 @@ +struct NA { x: int } +struct NB { x: int } +fn g(a: *NA, b: *NB) -> int { a = b; return 0; } +fn main() -> int { return 0; } diff --git a/tests/probes/n18_tuple_named.cr b/tests/probes/n18_tuple_named.cr new file mode 100644 index 00000000..aa3b7390 --- /dev/null +++ b/tests/probes/n18_tuple_named.cr @@ -0,0 +1,8 @@ +struct NA { x: int } +struct NB { x: int } +fn main() -> int { + a : ., mut = (NA { x: 1 }, 2); + b : ., mut = (NB { x: 3 }, 4); + a = b; + return 0; +} diff --git a/tests/probes/n19_ref_named.cr b/tests/probes/n19_ref_named.cr new file mode 100644 index 00000000..37a57c49 --- /dev/null +++ b/tests/probes/n19_ref_named.cr @@ -0,0 +1,11 @@ +struct NA { x: int } +struct NB { x: int } +fn g(a: &NA, b: &NB) -> int { return 0; } +fn main() -> int { + x : ., mut = NA { x: 1 }; + y : ., mut = NB { x: 2 }; + p := &x; + q := &y; + g(p, q); + return 0; +} diff --git a/tests/probes/n20_apply_named_vs_int.cr b/tests/probes/n20_apply_named_vs_int.cr new file mode 100644 index 00000000..3f8aa9fc --- /dev/null +++ b/tests/probes/n20_apply_named_vs_int.cr @@ -0,0 +1,4 @@ +struct NA { x: int } +struct Box[T] { v: T } +fn g(a: Box[NA], b: Box[int]) -> int { a = b; return 0; } +fn main() -> int { return 0; } diff --git a/tests/probes/n21_t_assign_int.cr b/tests/probes/n21_t_assign_int.cr new file mode 100644 index 00000000..62847044 --- /dev/null +++ b/tests/probes/n21_t_assign_int.cr @@ -0,0 +1,5 @@ +fn h[T](a: T, b: int) -> int { + a = b; + return 0; +} +fn main() -> int { return h(1, 2); } diff --git a/tests/probes/n22_named_struct_field.cr b/tests/probes/n22_named_struct_field.cr new file mode 100644 index 00000000..4b1c6928 --- /dev/null +++ b/tests/probes/n22_named_struct_field.cr @@ -0,0 +1,8 @@ +struct NA { x: int } +fn take(v: NA) -> int { return v.x; } +fn main() -> int { + a : ., mut = NA { x: 1 }; + b : ., mut = NA { x: 2 }; + a = b; + return take(a); +} diff --git a/tests/probes/n23_enum_named.cr b/tests/probes/n23_enum_named.cr new file mode 100644 index 00000000..0437380a --- /dev/null +++ b/tests/probes/n23_enum_named.cr @@ -0,0 +1,8 @@ +enum EA { X, Y } +enum EB { X, Y } +fn main() -> int { + a : ., mut = EA.X; + b : ., mut = EB.X; + a = b; + return 0; +} diff --git a/tests/probes/p_annots.cr b/tests/probes/p_annots.cr new file mode 100644 index 00000000..996bab02 --- /dev/null +++ b/tests/probes/p_annots.cr @@ -0,0 +1,9 @@ +fn add(a: int, b: int) -> int { return a + b; } +fn main() -> int { + @no_bounds_check; + @fast; + @unroll(4); + @section("hot"); + r := @inline(add)(3, 4); + return r; +} diff --git a/tests/probes/p_annots2.cr b/tests/probes/p_annots2.cr new file mode 100644 index 00000000..82938462 --- /dev/null +++ b/tests/probes/p_annots2.cr @@ -0,0 +1,7 @@ +fn add(a: int, b: int) -> int { return a + b; } +fn main() -> int { + @no_bounds_check(); + @fast(); + r := @inline(add)(3, 4); + return r; +} diff --git a/tests/probes/p_dex.cr b/tests/probes/p_dex.cr new file mode 100644 index 00000000..07d1f12b --- /dev/null +++ b/tests/probes/p_dex.cr @@ -0,0 +1,7 @@ +fn main() -> int { + d := 1 + 0.5; + n := d as int; + i : int = 7; + e := i + 0.25; + return n; +} diff --git a/tests/probes/p_dyn.cr b/tests/probes/p_dyn.cr new file mode 100644 index 00000000..d8aa0b98 --- /dev/null +++ b/tests/probes/p_dyn.cr @@ -0,0 +1,6 @@ +fn main() -> int { + cond : int = 1; + x : dyn = 0; + if cond != 0 { x = 42; } else { x = "hello"; } + return 0; +} diff --git a/tests/probes/p_enum.cr b/tests/probes/p_enum.cr new file mode 100644 index 00000000..b74cdd7f --- /dev/null +++ b/tests/probes/p_enum.cr @@ -0,0 +1,5 @@ +enum Color { Red, Green, Blue } +fn main() -> int { + x := Green(); + return match x { Red => 0, Green => 1, Blue => 2, }; +} diff --git a/tests/probes/p_ffi.cr b/tests/probes/p_ffi.cr new file mode 100644 index 00000000..ccc2f6a4 --- /dev/null +++ b/tests/probes/p_ffi.cr @@ -0,0 +1,5 @@ +extern "C" fn putchar(c: int) -> int; +fn main() -> int { + putchar(65); + return 0; +} diff --git a/tests/probes/p_ffi2.cr b/tests/probes/p_ffi2.cr new file mode 100644 index 00000000..08dd3e04 --- /dev/null +++ b/tests/probes/p_ffi2.cr @@ -0,0 +1,5 @@ +extern fn putchar(c: int) -> int; +fn main() -> int { + putchar(65); + return 0; +} diff --git a/tests/probes/p_go.cr b/tests/probes/p_go.cr new file mode 100644 index 00000000..52de8aec --- /dev/null +++ b/tests/probes/p_go.cr @@ -0,0 +1,6 @@ +fn work() -> int { return 1; } +fn main() -> int { + f := go work(); + r := await f; + return r; +} diff --git a/tests/probes/p_i2f.cr b/tests/probes/p_i2f.cr new file mode 100644 index 00000000..6dc8631d --- /dev/null +++ b/tests/probes/p_i2f.cr @@ -0,0 +1,6 @@ +fn half() -> dex { return 0.5; } +fn main() -> int { + x : dex = half(); + y := 3 + x; + return 0; +} diff --git a/tests/probes/p_opt.cr b/tests/probes/p_opt.cr new file mode 100644 index 00000000..d8a12a42 --- /dev/null +++ b/tests/probes/p_opt.cr @@ -0,0 +1,6 @@ +fn main() -> int { + x : int? = Some(5); + y := x?; + z : int? = None; + return y; +} diff --git a/tests/probes/p_spawn.cr b/tests/probes/p_spawn.cr new file mode 100644 index 00000000..bf876b66 --- /dev/null +++ b/tests/probes/p_spawn.cr @@ -0,0 +1,5 @@ +fn square(x: int) -> int { return x * x; } +fn main() -> int { + arr := go i 0..3 square(i); + return arr[0] + arr[1] + arr[2]; +} diff --git a/tests/probes/warm/README.md b/tests/probes/warm/README.md new file mode 100644 index 00000000..8c07f940 --- /dev/null +++ b/tests/probes/warm/README.md @@ -0,0 +1,46 @@ +# 暖态探针语料(#60 批 T3:判据网「暖态腿」的载体) + +**来源**:#60 批 T1 的定路径探针矩阵(`/tmp/fct4/task1-report.md` §3.3,2026-09-15 实跑实拷入仓)。 +**性质**:**不是**编译器编译单元(不被 `tests/suite/`、`src/ci/run.sh`、`tools/baseline/probes_run.sh` +的任何清单收集——那些都是非递归 glob)——它们是 `tools/baseline/warm_run.sh`(= `warm_leg.sh +--with-corpus`)与 `tests/selfhost/test_warm_cache_gate.py` 的**两态断言输入**。 + +## 设计要点:**定路径** + +缓存键 = `源路径::函数名`,指纹 = 纯 AST。把语料拷到唯一 temp 路径(既有套件的普遍写法) +⇒ 键唯一 ⇒ **结构性无暖态** ⇒ 腿恒绿而空洞(T1 方法学发现;突变 M3 实证:缺陷在场时 +唯一路径版腿 `FAIL=0` + 「暖态生效档数 = 0」)。故本语料**必须按入仓路径就地编译**, +两次编译(冷 = `clean-cache` 后首跑;暖 = 紧接二跑)用**同一路径**。 + +## 清点(**7 档**,只增不减;改动须同步 `tools/baseline/warm_leg.sh` 的 `WARM_TOTAL` 与本表) + +| # | 文件 | 形态 | 判据(冷 → 暖) | 出处 | +|---|---|---|---|---| +| 1 | `tu03_load.cr` | 外部指针解引用(读):`p := 4096 as *int; return *p;` | **rc=1 + `TU03` → rc=1 + `TU03`**(暖 rc=0 = **缺陷回归**) | T1 §3.3 / `test_pointer_safety.py:131-138` 形态 | +| 2 | `tu03_store.cr` | 同(写):`*p = 1;` | 同上 | T1 §3.3 | +| 3 | `tk01_oob_load.cr` | 指针越界(读):`&arr[0] + 1` | rc=1 + `TK01` → rc=1 + `TK01` | T1 §3.3 | +| 4 | `tk01_oob_store.cr` | 指针越界(写) | 同上 | T1 §3.3 | +| 5 | `tk01_width.cr` | 宽度跨界:`(&arr[1]) as *[int; 2]` | 同上 | T1 §3.3 | +| 6 | `plain.cr` | **正控**(普通程序) | rc=0 → rc=0 ∧ **`plain.cr::main.cir` 条目存在 ∧ 二跑真命中**(防腿恒绿空洞) | T2 正控 | +| 7 | `alloc_witness.cr` | **见证面**(数组字面量 + 取址 ⇒ ir_gen 期分配复合行) | rc=0 → rc=0 ∧ **自档 `main` 无条目**(#60 T2 见证规则) | T2 §3.2 | + +**旁面对照的意义**:`tk01_*` 三档走 `g_pts`/辅码宽度(**每次构建重算**),不依赖 ir_gen 期类型行 +⇒ 暖态**本就不该**变脸;它们的绿 = 缺陷面被精确锁定在「读生成期类型行」的判定(T1 E1 旁证)。 + +## 用法 + +```bash +bash tools/baseline/warm_run.sh ./build/corec /tmp/warm_now # 当前二进制(牙齿层) +bash tools/baseline/warm_run.sh /tmp//corec /tmp/warm_before # pre-fix ⇒ **必红**(腿有牙) +``` + +**判读**:`WARM LEG DONE(…):7 档 · **暖态生效(≥1 真命中)档数 = N** · FAIL=M` +—— `FAIL>0` = 冷/暖分歧(缺陷回归)或期望值不命中;**`暖态生效档数 = 0` = 空洞警报** +(腿绿但根本没走到暖态,例如路径不固定/缓存被关),二者都不可接受。 + +## 与既有 runner 的关系(#60 T3 落位) + +- `tools/baseline/warm_leg.sh`:腿本体(两层:广度层 = 父 runner 传入清单;牙齿层 = 本语料)。 +- `tools/baseline/warm_run.sh`:牙齿层专用入口(= `--with-corpus`)。 +- `parity_run.sh` / `probes_run.sh`:各加**广度层**调用(同一清单单一真源 `/corpus.tsv`); + 其 `logs/` 产物**格式不变**(历史对拍可比性保持),暖态结果落 `/warm/`。 diff --git a/tests/probes/warm/alloc_witness.cr b/tests/probes/warm/alloc_witness.cr new file mode 100644 index 00000000..adeac092 --- /dev/null +++ b/tests/probes/warm/alloc_witness.cr @@ -0,0 +1,7 @@ +// #60 T2 见证面:数组字面量 + 取址 ⇒ ir_gen 期分配复合类型行 +// (ir_gen.cr:2584/:1403)⇒ 本函数不落缓存条目(下跑必 miss 重放)。程序本身合法。 +fn main() -> int { + arr := [10, 20, 30]; + p := &arr[1]; + return *p; +} diff --git a/tests/probes/warm/plain.cr b/tests/probes/warm/plain.cr new file mode 100644 index 00000000..a3e43f61 --- /dev/null +++ b/tests/probes/warm/plain.cr @@ -0,0 +1,4 @@ +fn main() -> int { + x := 1 + 2; + return x; +} diff --git a/tests/probes/warm/tk01_oob_load.cr b/tests/probes/warm/tk01_oob_load.cr new file mode 100644 index 00000000..79353559 --- /dev/null +++ b/tests/probes/warm/tk01_oob_load.cr @@ -0,0 +1,5 @@ +fn main() -> int { + arr := [1]; + p := &arr[0] + 1; + return *p; +} diff --git a/tests/probes/warm/tk01_oob_store.cr b/tests/probes/warm/tk01_oob_store.cr new file mode 100644 index 00000000..bb9b35bc --- /dev/null +++ b/tests/probes/warm/tk01_oob_store.cr @@ -0,0 +1,6 @@ +fn main() -> int { + arr := [1]; + p := &arr[0] + 1; + *p = 2; + return 0; +} diff --git a/tests/probes/warm/tk01_width.cr b/tests/probes/warm/tk01_width.cr new file mode 100644 index 00000000..76f5a24e --- /dev/null +++ b/tests/probes/warm/tk01_width.cr @@ -0,0 +1,6 @@ +fn main() -> int { + arr := [1, 2]; + p := (&arr[1]) as *[int; 2]; + wide := *p; + return 0; +} diff --git a/tests/probes/warm/tu03_load.cr b/tests/probes/warm/tu03_load.cr new file mode 100644 index 00000000..4cef0b7e --- /dev/null +++ b/tests/probes/warm/tu03_load.cr @@ -0,0 +1,4 @@ +fn main() -> int { + p := 4096 as *int; + return *p; +} diff --git a/tests/probes/warm/tu03_store.cr b/tests/probes/warm/tu03_store.cr new file mode 100644 index 00000000..879c2ea0 --- /dev/null +++ b/tests/probes/warm/tu03_store.cr @@ -0,0 +1,5 @@ +fn main() -> int { + p := 4096 as *int; + *p = 1; + return 0; +} diff --git a/tests/selfhost/test_agg_checks.py b/tests/selfhost/test_agg_checks.py new file mode 100644 index 00000000..3851e8a6 --- /dev/null +++ b/tests/selfhost/test_agg_checks.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""TODO #29:struct/数组字面量的「名 / 型 / 同质性」三校验(F5 同族剩余面)。 + +修复前(全部 rc=0 静默,无任何诊断): + ① struct 字段名被丢弃 —— parser 取 `fni` 后从未写入,字段值按**声明位序**绑定: + `P{b: 11, a: 22}` 静默得 a=11, b=22(**静默错值**级);未知字段 / 重复字段 / 缺字段 + 三校验均不存在。 + ② 字段类型不与声明比对 —— `P{a: 1, b: "x"}`(b: int)、`P{a: 1}`(缺 b)、 + `P{a: 1, b: 3}`(b: Q 结构体)全部静默通过。 + ③ 数组元素同质性不检查 —— `[1, "x", 3]` rc=0,且元素类型随**末**元素漂移(soundness 漏放)。 + +修复(checker 侧补齐 + 消费者按名字绑定): + · parser 把字段名 idx 写进 wrapper.b(EXPR_STRUCT 契约,见 ast.cr / parser.cr); + · checker:名字 → 声明下标(未知 TS02 / 重复 TS04 / 缺字段 TS01)+ 字段类型 vs 声明 + 比对(TS03,走 type_compat_strict 引擎判定)+ 数组元素同质(TK02); + · 值**按名字绑定**(与 Python bootstrap 的 gen_struct_lit 同语义):ir_gen 按 wrapper.b + 解出字段位存储,求值顺序仍为**源序**(c3 = 副作用顺序判据); + · 五码入 run_frontend 的**硬错误**名单(与 R002/TK05 同类)——修复前这些错误即便报出也 + 照常产出二进制(静默错产物);现在 rc=1 且不产生产物。 + · 泛型参数**未实例化**时两侧任一含 TYP_GENERIC_PARAM → 跳过类型比对(不假拒:泛型函数体 + 内的值类型、泛型结构体字段的声明类型);字段类型为结构体泛型参数的走绑定路径,结果 + TYP_GENERIC_APPLY 的实参按**参数声明序**取(旧代码按字面量字段序 = 参数序 ≠ 字段序时错位)。 + +判据: + 值 6 例(build+run,退出码 = main 返回值 & 0xFF)+ 类型 10 例(负 7:各出对应码且 rc≠0 + 且**无产物**;正 3:rc=0 且无 error[)。 +""" + +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# ---- 值判据:(名称, 源码, main 期望返回值) ---- +VALUE_CASES = [ + ( + "struct: 乱序字段按名字绑定(①主判据;修复前静默得 1122)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{b: 11, a: 22}; + return p.a * 100 + p.b; +} +""", + 2211, + ), + ( + "struct: 乱序 + 求值顺序 = 源序(副作用判据)", + """ +g : int, mut = 0; +fn nx() -> int { g = g + 1; return g; } +struct P { a: int, b: int } +fn main() -> int { + p := P{b: nx(), a: nx()}; + return p.a * 100 + p.b; +} +""", + 201, + ), + ( + "struct: 三字段逆序(错位累积面)", + """ +struct P { a: int, b: int, c: int } +fn main() -> int { + p := P{c: 3, b: 2, a: 1}; + return p.a * 100 + p.b * 10 + p.c; +} +""", + 123, + ), + ( + "struct: 乱序 + 复合字段值(嵌套字面量)", + """ +struct Q { u: int, v: int } +struct P { a: int, b: Q } +fn main() -> int { + p := P{b: Q{u: 2, v: 3}, a: 1}; + return p.a * 100 + p.b.u * 10 + p.b.v; +} +""", + 123, + ), + ( + "generic struct: 乱序字段 + 泛型参数绑定(Pair[T])", + """ +struct Pair[T] { left: T, right: T } +fn main() -> int { + p := Pair { right = 2, left = 1 }; + return p.left * 10 + p.right; +} +""", + 12, + ), + ( + "generic struct: 双参数、字段序 ≠ 参数序(实参须按参数声明序)", + """ +struct Sw[A, B] { y: B, x: A } +fn main() -> int { + s := Sw { x = 1, y = 2 }; + return s.x * 10 + s.y; +} +""", + 12, + ), + ( + "generic fn body: 乱序字面量经 monomorph 克隆仍按名字绑定(名字随克隆保留)", + """ +struct P { a: int, b: int } +fn f[T](x: T) -> int { + p := P{b: 2, a: x}; + return p.a * 10 + p.b; +} +fn main() -> int { return f(1); } +""", + 12, + ), + ( + "嵌套泛型应用字段类型 Box[Box[int]](既存载荷写坏回归——修复前假拒)", + """ +struct Box[T] { val: T } +struct Holder { b: Box[Box[int]] } +fn main() -> int { + h := Holder{b: Box{val: Box{val: 7}}}; + return h.b.val.val; +} +""", + 7, + ), +] + +# ---- 类型判据:(名称, 源码, 期望码 / "ACCEPT") ---- +CHECK_CASES = [ + # 负控:各出对应码、rc≠0、且不产生产物(硬错误门) + ( + "① 未知字段 + 缺字段(TS02 + TS01)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{c: 1, a: 2}; + return 0; +} +""", + "TS02", + ), + ( + "① 重复字段(TS04)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{a: 1, a: 2, b: 3}; + return 0; +} +""", + "TS04", + ), + ( + "① 缺字段(TS01)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{a: 1}; + return 0; +} +""", + "TS01", + ), + ( + "② 字段类型与声明不符(TS03;b: int 收 string)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{a: 1, b: "x"}; + return 0; +} +""", + "TS03", + ), + ( + "② 嵌套结构体字段错型(TS03;b: Q 收 int)", + """ +struct Q { u: int, v: int } +struct P { a: int, b: Q } +fn main() -> int { + p := P{a: 1, b: 3}; + return 0; +} +""", + "TS03", + ), + ( + "③ 数组元素异质(TK02)", + """ +fn main() -> int { + a := [1, "x", 3]; + return 0; +} +""", + "TK02", + ), + ( + "③ 数组元素异质:嵌套 vs 扁平(TK02)", + """ +fn main() -> int { + a := [[1, 2], 3]; + return 0; +} +""", + "TK02", + ), + # 正控:合法写法 rc=0 且无 error[ + ( + "正控:乱序但名/型/完整性全对 → 接受", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{b: 11, a: 22}; + return p.a * 100 + p.b; +} +""", + "ACCEPT", + ), + ( + "正控:泛型函数体内泛型参数值赋给确定类型字段(不得假拒)", + """ +struct P { a: int, b: int } +fn f[T](x: T) -> int { + p := P{a: 1, b: x}; + return p.a * 100 + p.b; +} +fn main() -> int { return f(7); } +""", + "ACCEPT", + ), + ( + "正控:同质数组 + 嵌套同形数组 → 接受", + """ +fn main() -> int { + a := [1, 2, 3]; + b := [[1, 2], [3, 4]]; + return a[0] + b[1][1]; +} +""", + "ACCEPT", + ), + ( + "正控:嵌套泛型应用在**返回位**(既存载荷写坏回归——修复前假拒 TF01)", + """ +struct Box[T] { val: T } +fn mk() -> Box[Box[int]] { return Box{val: Box{val: 1}}; } +fn main() -> int { b := mk(); return b.val.val; } +""", + "ACCEPT", + ), + ( + "负控:嵌套泛型应用**错型**仍拒(两趟修复不得引入假阴性——评审 Important #1 的反面)", + """ +struct Box[T] { val: T } +struct P { b: Box[Box[int]] } +fn main() -> int { p := P{b: Box{val: Box{val: "s"}}}; return 0; } +""", + "TS03", + ), +] + + +def _write_tmp(src, prefix): + d = tempfile.mkdtemp(prefix=prefix) + path = os.path.join(d, "t.cr") + with open(path, "w") as f: + f.write(src) + return d, path + + +def run_value_case(name, source, expect): + """build → run,退出码须等于 expect & 0xFF(main 返回值即进程退出码的低 8 位)。""" + d, src_path = _write_tmp(source, "f29_v_") + bin_path = os.path.join(d, "t.bin") + try: + b = subprocess.run( + ["nice", "-n", "19", str(COREC), "build", src_path, "-o", bin_path, "--static"], + cwd=BASE, capture_output=True, text=True, timeout=300, + ) + out = b.stdout + b.stderr + if b.returncode != 0 or not os.path.exists(bin_path): + print(f"[FAIL] {name}: build rc={b.returncode} (expect value {expect})") + print(out) + return False + r = subprocess.run([bin_path], capture_output=True, text=True, timeout=60) + want = expect & 0xFF + if r.returncode == want: + print(f"[PASS] {name}: rc={r.returncode} == {expect}") + return True + print(f"[FAIL] {name}: rc={r.returncode} want={want} (value {expect})") + return False + finally: + shutil.rmtree(d, ignore_errors=True) + + +def run_check_case(name, source, expect): + """负控:`build` 必须 rc≠0、报出期望码、且**不产生产物**(硬错误门 = 非静默); + 正控:rc=0 且无任何 error[。""" + d, src_path = _write_tmp(source, "f29_c_") + bin_path = os.path.join(d, "t.bin") + try: + r = subprocess.run( + ["nice", "-n", "19", str(COREC), "build", src_path, "-o", bin_path, "--static"], + cwd=BASE, capture_output=True, text=True, timeout=300, + ) + produced = os.path.exists(bin_path) + finally: + shutil.rmtree(d, ignore_errors=True) + + out = r.stdout + r.stderr + if expect == "ACCEPT": + ok = r.returncode == 0 and "error[" not in out + else: + ok = ( + r.returncode != 0 + and f"error[{expect}]" in out + and not produced # 硬错误:绝不产出静默错产物 + and " --> " in out # 定位诊断(行:列) + ) + if ok: + print(f"[PASS] {name}: {expect}") + return True + print(f"[FAIL] {name}: expect={expect} rc={r.returncode} produced={produced}") + print(out) + return False + + +def main(): + if not COREC.exists(): + print(f"[FAIL] missing native compiler: {COREC}") + return 1 + results = [run_value_case(*c) for c in VALUE_CASES] + results += [run_check_case(*c) for c in CHECK_CASES] + passed = sum(results) + print(f"{passed}/{len(results)} passed") + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_agg_slots.py b/tests/selfhost/test_agg_slots.py new file mode 100644 index 00000000..b1926614 --- /dev/null +++ b/tests/selfhost/test_agg_slots.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +"""F5 同族(TODO #25 姊妹条目):EXPR_STRUCT / EXPR_STRUCTPAT / EXPR_ARRAY 聚合字面量槽位。 + +根因(与已修的 EXPR_TUPLE 同源——一处槽契约、三处分支实例): + parser 的 struct 字面量 / struct 模式 / 数组字面量三分支曾把「字段值(元素)节点在 g_ast + 连续」当契约,只记首个值节点 + 个数,且**逐值后随建 wrapper 交错分配**(`fv := parse_expr(); + ast_alloc(0, fv, ...)`)。值节点是复合表达式(调用 / 嵌套字面量 / 下标)时其子树自占多槽, + 夹在相邻 wrapper 之间 ⇒ 第 2 个起槽位整体错位,消费者读到值节点的**子节点**: + ① 静默错误值(rc=0):P{a:11, b:g()} 的 b 得 0;[1, g(), 3] 的 a[1] 得 0; + ② 崩溃:[[1,2],[3,4]] 的 a[1] 被读成扁平 int 3 → a[1][0] SIGSEGV 139; + ③ soundness 漏放:[[1,2],[3,4]] 与 [5,6] 元素类型被错录为 int,类型互赋静默通过。 + 修复 = 三分支统一两趟(先解析全部值进暂存表、后统建连续 wrapper)。契约: + EXPR_STRUCT/EXPR_STRUCTPAT/EXPR_ARRAY(字面量形) = a=首 wrapper、b=个数; + wrapper(kind=EXPR_NONE)在 g_ast 中连续、wrapper.a=值节点。消费点 5 处逐 wrapper 解引用: + checker(infer_expr 前向)/ ir_gen gen_expr 与 ast_patch_node / monomorph 克隆(改「先克隆 + 值、后统建 wrapper」)/ opt(折叠)。注意类型形 [T; N] / [T] 由 parse_type 产出 + (a=内层类型节点、b=0、int_val=尺寸),不经字面量分支、不受本契约影响。 + +判据(值判据两向非空:含单槽元素反证边界与泛型实例体克隆路径;类型判据双向:期望拒的必出 + 对应码,期望收的必 rc=0 且无 error[): + 值 9 例(build+run,退出码 = main 返回值 & 0xFF)+ 类型 4 例(check)。 +""" + +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# ---- 值判据:(名称, 源码, main 期望返回值) ---- +VALUE_CASES = [ + ( + "struct: compound value in last field (F5 ①主判据)", + """ +struct P { a: int, b: int } +fn g() -> int { return 3; } +fn main() -> int { + p := P{a: 11, b: g()}; + return p.a * 100 + p.b; +} +""", + 1103, + ), + ( + "struct: both fields compound (F5 ①主判据)", + """ +struct P { a: int, b: int } +fn g() -> int { return 3; } +fn h() -> int { return 4; } +fn main() -> int { + p := P{a: g(), b: h()}; + return p.a * 10 + p.b; +} +""", + 34, + ), + ( + "struct: 3 fields, compound in middle (错位累积面)", + """ +struct P { a: int, b: int, c: int } +fn g() -> int { return 5; } +fn main() -> int { + p := P{a: 1, b: g(), c: 2}; + return p.a * 100 + p.b * 10 + p.c; +} +""", + 152, + ), + ( + "struct: nested struct literal as field value", + """ +struct Q { u: int, v: int } +struct P { a: int, b: Q } +fn main() -> int { + p := P{a: 1, b: Q{u: 2, v: 3}}; + return p.a * 100 + p.b.u * 10 + p.b.v; +} +""", + 123, + ), + ( + "struct: single-slot fields only (反证边界——旧交错顺序下此形本已正确)", + """ +struct P { a: int, b: int } +fn main() -> int { + p := P{a: 11, b: 22}; + return p.a * 100 + p.b; +} +""", + 1122, + ), + ( + "generic fn body: struct literal cloned by monomorph", + """ +struct P { a: int, b: int } +fn f[T](x: T) -> int { + p := P{a: 1, b: x}; + return p.a * 100 + p.b; +} +fn main() -> int { + return f(7); +} +""", + 107, + ), + ( + "array: compound element in middle (F5 ③主判据)", + """ +fn g() -> int { return 7; } +fn main() -> int { + a := [1, g(), 3]; + return a[0] * 10 + a[1]; +} +""", + 17, + ), + ( + "array: nested array element read (修复前 a[1][0] SIGSEGV)", + """ +fn main() -> int { + a := [[1, 2], [3, 4]]; + return a[0][0] * 10 + a[1][1]; +} +""", + 14, + ), + ( + "array: repeat form [value; N] 共享值节点", + """ +fn g() -> int { return 4; } +fn main() -> int { + a := [g() + 1; 3]; + return a[0] + a[1] + a[2]; +} +""", + 15, + ), +] + +# ---- 类型判据:(名称, 源码, 期望):"ACCEPT" / "TA01" ---- +CHECK_CASES = [ + ( + "soundness: nested vs flat array literal rejected (F5 ③)", + """ +fn main() -> int { + x : ., mut = [[1, 2], [3, 4]]; + y : ., mut = [5, 6]; + x = y; + return 0; +} +""", + "TA01", + ), + ( + "array element count mismatch rejected", + """ +fn main() -> int { + x : ., mut = [1, 2, 3]; + y : ., mut = [5, 6]; + x = y; + return 0; +} +""", + "LEN", + ), + ( + "same-shape nested arrays accepted (反证边界)", + """ +fn main() -> int { + x : ., mut = [[1, 2], [3, 4]]; + y : ., mut = [[5, 6], [7, 8]]; + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "array type form [T; N] + literal accepted (类型形不受字面量契约影响)", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + return a[2]; +} +""", + "ACCEPT", + ), +] + + +def _write_tmp(src, prefix): + d = tempfile.mkdtemp(prefix=prefix) + path = os.path.join(d, "t.cr") + with open(path, "w") as f: + f.write(src) + return d, path + + +def run_value_case(name, source, expect): + """build → run,退出码须等于 expect & 0xFF(main 返回值即进程退出码的低 8 位)。""" + d, src_path = _write_tmp(source, "f5agg_v_") + bin_path = os.path.join(d, "t.bin") + try: + b = subprocess.run( + ["nice", "-n", "19", str(COREC), "build", src_path, "-o", bin_path, "--static"], + cwd=BASE, capture_output=True, text=True, timeout=300, + ) + out = b.stdout + b.stderr + if b.returncode != 0 or not os.path.exists(bin_path): + print(f"[FAIL] {name}: build rc={b.returncode} (expect value {expect})") + print(out) + return False + r = subprocess.run([bin_path], capture_output=True, text=True, timeout=60) + want = expect & 0xFF + if r.returncode == want: + print(f"[PASS] {name}: rc={r.returncode} == {expect}") + return True + print(f"[FAIL] {name}: rc={r.returncode} want={want} (value {expect})") + return False + finally: + shutil.rmtree(d, ignore_errors=True) + + +def run_check_case(name, source, expect): + d, src_path = _write_tmp(source, "f5agg_c_") + try: + r = subprocess.run( + ["nice", "-n", "19", str(COREC), "check", src_path], + cwd=BASE, capture_output=True, text=True, timeout=120, + ) + finally: + shutil.rmtree(d, ignore_errors=True) + + out = r.stdout + r.stderr + if expect == "ACCEPT": + ok = r.returncode == 0 and "error[" not in out + elif expect == "TA01": + ok = r.returncode == 1 and "error[TA01]" in out and "Assignment type mismatch" in out + elif expect == "LEN": + ok = r.returncode == 1 and "error[TA01]" in out and "Array length constraint" in out + else: + ok = False + if ok: + print(f"[PASS] {name}: {expect}") + return True + print(f"[FAIL] {name}: expect={expect} rc={r.returncode}") + print(out) + return False + + +def main(): + if not COREC.exists(): + print(f"[FAIL] missing native compiler: {COREC}") + return 1 + results = [run_value_case(*c) for c in VALUE_CASES] + results += [run_check_case(*c) for c in CHECK_CASES] + passed = sum(results) + print(f"{passed}/{len(results)} passed") + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_backend_bootstrap.py b/tests/selfhost/test_backend_bootstrap.py index eae253e5..5d70de88 100644 --- a/tests/selfhost/test_backend_bootstrap.py +++ b/tests/selfhost/test_backend_bootstrap.py @@ -10,7 +10,8 @@ BUILD = BASE / "build" COREC = BUILD / "corec" COREARCH = BUILD / "corearch" -BACKEND_SOURCE = BASE / "src" / "arch" / "linux" / "ld" +# 组合根(三轴组合的 target triple 命名)——project-mode 入口 = 该目录 main.cr +BACKEND_SOURCE = BASE / "src" / "targets" / "x86_64-linux" def run_checked(args: list[str], label: str, env: dict[str, str]) -> bool: @@ -23,7 +24,20 @@ def run_checked(args: list[str], label: str, env: dict[str, str]) -> bool: text=True, timeout=180, ) + out = (result.stdout or "") + (result.stderr or "") if result.returncode == 0: + # 守卫②(project-mode 面):rc=0 但构建日志带 error[ 诊断 = 失败。 + # self-hosted corec 对未定义函数/名(error[N06]/N01)只打印诊断、**不置 rc**, + # 产物照常生成 → 互测面(stage 链 byte-identical)完全不可见(TODO #6 ③ 的 + # 事发层)。bootstrap concat 面的同类门见 build_selfhost_native.py + # guard_build_log(该门只能看 concat 日志,看不到本入口)。 + diag = [ln for ln in out.splitlines() if "error[" in ln] + if diag: + print(f"[FAIL] {label}: exit 0 but build log carries " + f"{len(diag)} error[ diagnostic line(s):") + for ln in diag[:40]: + print(f" {ln}") + return False print(f"[PASS] {label}") return True print(f"[FAIL] {label}: exit {result.returncode}") diff --git a/tests/selfhost/test_cache_identity.py b/tests/selfhost/test_cache_identity.py new file mode 100644 index 00000000..ab4e95b6 --- /dev/null +++ b/tests/selfhost/test_cache_identity.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +"""TODO #5 回归:cir 缓存缺编译器身份 ⇒ 跨编译器重建不失效(2026-09-11 修复)。 + +背景(RED,修复前实测,pre-fix 二进制 sha256 f0f00d7d…): + 缓存键 = 源路径::函数名,头部指纹 = magic/格式版本/纯 AST 指纹(只覆盖目标源) + ——**无编译器身份分量**。二进制重建后字符串驻留序(g_strs intern 序)漂移, + 而键与指纹一字未变 ⇒ 旧条目被命中,旧序索引被当活产物使用。 + 实测(手工,pre-fix):把 alpha 条目内 var[0]/var[2] 的 name_ni(2 ↔ 146) + 互换(=「另一个编译器写下的」索引序)后重跑——条目原样未被重写(命中), + dump 通道随之错位:`binary 24 = a + b` → `24 = 20 + b`、`dest=22` → `dest=a`, + rc=0 静默污染。(同族第二实例 = 同一二进制冷/热缓存 dump 渲染差异,见 + TODO #5 末条,非本单。) + +修复:缓存头写入**运行中编译器自身 ELF 的内容哈希**(/proc/self/exe 全文件 + 单趟乘加哈希,cir_cache.cr cir_compiler_identity/cir_hash_running_binary), + 装载时比对:不等 = cache miss = 无害重建。身份取不到(无 procfs/读取失败) + 则两端点均关缓存(绝不落「身份 0」条目——那是同一静默类的退化复活)。 + 头部布局 v16→v17 整体后移 8B(magic/ver/identity/fp/sig/name_len/name…)。 + 与 CIR_CACHE_VER 的分工:VER = 手工粗粒度(格式/行为代),身份 = 自动细粒度 + (编译器内容驱动)——两者都必须匹配才命中。 + +判据(5 组 7 例): + ① identity_semantics:条目身份字段 == FNV-1(运行中编译器 ELF 全文件) 且 ver==17 + ② same_identity_hit:同身份条目仍命中(正常路径无退化)——改条目内**被装载 + 跳过**的“函数名字节”(canary),重跑后字节原样留存(命中 = 从不重写) + ③ foreign_identity_rejected:身份字段被改(+载荷 name_ni 互换,模拟异序)→ + 重跑后条目身份被改写回当前身份、载荷被重建(= 判为 miss 并整体重算) + ④ foreign_entry_fully_discarded(差分):「条目缺失」与「条目毒化(身份+载荷)」 + 两次运行的 stdout dump 逐字节相同 + 重建出的条目逐字节相同 ⇒ 外来条目被 + 完整丢弃、以全新产物替代(修复前该差分必然非零:毒化条目被当命中沿用) + ⑤ cross_binary_ab:两份**内容不同**的编译器二进制共用一个缓存目录(同一份 + 源码构建出的新二进制 = 同内容副本 + 追加一字节)——A 写的条目不被 B 接受: + canary 被重写抹除 + 条目身份改写为 B;反向亦然。 + (人工另以真实第二编译器 corec2 = `corec build src/compiler/main.cr` 自举 + 产物验证同判据:身份 3584… vs -3398…,条目被改写为运行方身份。) +""" + +import os +import pathlib +import shutil +import struct +import subprocess +import tempfile + +BASE = pathlib.Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +CACHE_DIR = BASE / ".core" / "cache" / "cir" + +VER_EXPECTED = 17 +FNV_OFFSET = -3750763034362895579 # FNV-1 64 offset basis(signed i64) +FNV_PRIME = 1099511628211 +M64 = 1 << 64 + +PROBE = """// cache identity probe (tests/selfhost/test_cache_identity.py) +fn alpha(a: int, b: int) -> int { + x := a + b; + y := x * 2; + z := y - a; + return z; +} + +fn beta(p: int) -> int { + m := p + 1; + n := m * m; + return n; +} + +fn main() -> int { + return alpha(3, 4) + beta(5); +} +""" + + +def fnv1_64(data: bytes) -> int: + """cir_hash_running_binary 的 Python 独立实现(8 字节小端字 + 余尾逐字节 + + 末尾混入总长;见 cir_cache.cr 该函数头注)。""" + h = FNV_OFFSET + n = len(data) + i = 0 + while i + 8 <= n: + w = int.from_bytes(data[i:i + 8], "little", signed=True) + h = (h * FNV_PRIME + w) % M64 + i += 8 + while i < n: + h = (h * FNV_PRIME + data[i]) % M64 + i += 1 + h = (h * FNV_PRIME + n) % M64 + return h - M64 if h >= (1 << 63) else h + + +def identity_of(path: pathlib.Path) -> int: + return fnv1_64(path.read_bytes()) + + +def layout(d: bytes): + """按头部版本给出字段偏移(v17 = 本修复;v16 = 修复前布局,用于 RED 对照)。""" + ver = struct.unpack_from(" int: + info = parse_entry(p) + if info["var_base"] < 0: + return -1 + return struct.unpack_from("= 0: + base = info["var_base"] + a = struct.unpack_from(" bytes: + """把条目内被装载跳过的“函数名字节”改成同长 canary;返回 canary。""" + info = parse_entry(path) + d = bytearray(info["raw"]) + n = len(info["name"]) + canary = b"X" * n + d[info["name_off"]:info["name_off"] + n] = canary + path.write_bytes(bytes(d)) + return canary + + +def strip_dot_line(stdout: str, dot: pathlib.Path) -> str: + """剔除只含输出路径的行(两次运行同一路径时本就相同;防御性过滤)。""" + return "\n".join(l for l in stdout.splitlines() if str(dot) not in l) + + +def run_corec(compiler: pathlib.Path, src: pathlib.Path, dot: pathlib.Path): + """corec cir(cwd=BASE:import 解析与缓存目录都在仓根——见 CLAUDE.md)。""" + return subprocess.run( + [str(compiler), "cir", str(src), "-o", str(dot)], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def entry_path(src: pathlib.Path, fn_name: str) -> pathlib.Path: + return CACHE_DIR / f"{str(src).replace('/', '_')}::{fn_name}.cir" + + +def main(): + failures = [] + checks = 0 + + fd, src_name = tempfile.mkstemp(suffix="_cacheid_probe.cr") + src = pathlib.Path(src_name) + with os.fdopen(fd, "w") as f: + f.write(PROBE) + tmpdir = pathlib.Path(tempfile.mkdtemp(prefix="core_cacheid_")) + dot = tmpdir / "probe.cir" + alpha = entry_path(src, "alpha") + + def cleanup_cache(): + # 只清本探针 key(前缀 = 探针源路径的 sanitize 形态)的条目,不牵动他者。 + prefix = str(src).replace("/", "_") + for p in CACHE_DIR.glob(f"{prefix}::*.cir"): + try: + p.unlink() + except FileNotFoundError: + pass + + def check(name, cond, detail=""): + nonlocal checks + checks += 1 + print(f"[{'PASS' if cond else 'FAIL'}] {name}{(' — ' + detail) if detail else ''}") + if not cond: + failures.append(name) + + try: + if not COREC.exists(): + print("[FAIL] 缺 build/corec(先跑 build_selfhost_native.py)") + return 1 + expect_id = identity_of(COREC) + cleanup_cache() + + # ① 身份语义:条目身份 = 运行中编译器 ELF 全文件哈希 + 版本 v17 + r = run_corec(COREC, src, dot) + if r.returncode != 0 or not alpha.exists(): + print(f"[FAIL] 探针首次编译失败 rc={r.returncode} alpha_exists={alpha.exists()}\n{r.stdout}{r.stderr}") + return 1 + info = parse_entry(alpha) + check("identity_semantics", + info["ver"] == VER_EXPECTED and info["identity"] == expect_id, + f"ver={info['ver']} identity={info['identity']} expect={expect_id}") + + # ② 同身份命中:改「被装载跳过」的函数名字节,重跑必须原样留存 + canary = set_name_canary(alpha) + r = run_corec(COREC, src, dot) + now = parse_entry(alpha) + check("same_identity_hit", + r.returncode == 0 and now["name"] == canary, + f"name_canary={'kept(命中)' if now['name'] == canary else 'rewritten(miss)'}") + + # ③ 异身份拒绝:身份+载荷毒化 → 条目被改写回当前身份、载荷被重建 + v0_clean = var_name_ni(alpha, 0) + v2_clean = var_name_ni(alpha, 2) + if v0_clean == v2_clean: + print("[FAIL] 探针要求 var[0]/var[2] 名索引不同") + return 1 + poison(alpha, identity=True, payload=True) + r = run_corec(COREC, src, dot) + after = parse_entry(alpha) + check("foreign_identity_rejected", + r.returncode == 0 and after["identity"] == expect_id + and var_name_ni(alpha, 0) == v0_clean and var_name_ni(alpha, 2) == v2_clean, + f"identity_after={after['identity']}(==当前 ⇒ miss 重算)") + + # ④ 差分:条目缺失 vs 条目毒化 —— dump/重建条目必须逐字节相同 + alpha.unlink() + r_x = run_corec(COREC, src, dot) + dot_x, entry_x = dot.read_bytes(), alpha.read_bytes() + poison(alpha, identity=True, payload=True) + r_y = run_corec(COREC, src, dot) + dot_y, entry_y = dot.read_bytes(), alpha.read_bytes() + check("foreign_entry_fully_discarded", + r_x.returncode == 0 and r_y.returncode == 0 and dot_x == dot_y + and entry_x == entry_y + and strip_dot_line(r_x.stdout, dot) == strip_dot_line(r_y.stdout, dot), + "条目缺失 ≡ 条目毒化(dump + 重建条目逐字节同)") + + # ⑤ 跨二进制 A/B:内容不同的编译器不共享条目(canary 判据与版本无关) + compiler_b = tmpdir / "corec_rebuilt" + shutil.copyfile(COREC, compiler_b) + os.chmod(compiler_b, 0o755) + with open(compiler_b, "ab") as fb: + fb.write(b"\x00") # 重建后的新编译器(语义同、内容异) + id_b = identity_of(compiler_b) + if id_b == expect_id: + print("[FAIL] 副本身份与本体相同(内容哈希失效)") + return 1 + run_corec(COREC, src, dot) # A 写条目 + a_ident = parse_entry(alpha)["identity"] + canary = set_name_canary(alpha) + r_b = run_corec(compiler_b, src, dot) # B 读 A 的条目 + after_b = parse_entry(alpha) + r_a = run_corec(COREC, src, dot) # A 再读 B 的条目 + after_a = parse_entry(alpha) + check("cross_binary_ab", + r_b.returncode == 0 and r_a.returncode == 0 + and after_b["name"] != canary # B 拒绝并重写(canary 被抹) + and after_b["identity"] == id_b + and after_a["identity"] == expect_id, + f"A写={a_ident} → B读后={after_b['identity']}(=B身份, canary " + f"{'gone' if after_b['name'] != canary else 'KEPT!'}) → A读后={after_a['identity']}") + + print(f"{checks - len(failures)}/{checks} passed") + for f in failures: + print(f" FAILED: {f}") + return 0 if not failures else 1 + finally: + cleanup_cache() + try: + src.unlink() + except FileNotFoundError: + pass + shutil.rmtree(tmpdir, ignore_errors=True) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py new file mode 100644 index 00000000..81dfdd05 --- /dev/null +++ b/tests/selfhost/test_ccr_types.py @@ -0,0 +1,2296 @@ +#!/usr/bin/env python3 +"""R2 P4 Task 1/2/3:`.ccr` TYPE=7 / IFACE=8 段(机制面 + TYPE/IFACE 内容面)断言套件。 + +本套件 = **机制面**(Task 1:段表/版本/loader 三闸)+ **TYPE 内容面**(Task 2: +行表 + 项 DAG 序列化 / D13 确定性装填 / corearch 读回重建 / 判定原语跨进程同值) ++ **IFACE 内容面**(Task 3:五小节 = 原生条目 16 行 + 形状名 + 接口签名项化 + +impl 边 + 方法表 / corearch 读回 / 跨段引用域 / 扩列与命名化守门)。 + +Task 1(机制面): + ① 段表 = 8 段、tag 序 = 1..8、offset 连续、末段尾 == 文件大小; + ② header: version = 9、seg_count = 8、reserved = 0; + ③ TYPE(7)/IFACE(8) **皆为内容面**(R2 P4 Task 2/3 起——空壳期已退役); + ④ loader 负分支(byte mutation,逐个打;全部要求 corearch rc≠0 且**不得 + 静默当空表**——三态纪律 C.5-3): + · version := 7/8 → 拒(D10:旧 v7/v8 文件在版本闸整类拒收); + · **结构性合法的 v7(6 段)文件整体重建** → 拒(D10 的正面证据: + 非「字节破烂」被拒,而是合法旧文件被版本闸拒); + · seg_cnt := 6(末段 size 扩到 EOF——前闸全过)→ 拒(D11 必备集); + · 段 tag ∉ 1..8(tag := 9)/ 重复 tag(7 → 6)→ 拒(闸界 + 规范序); + · 段体截断(文件 −1B)→ 拒(越界)。 + ⑤ 端到端:corec build --static + corearch --elf 双路径 rc=0 + 产物 rc=42 + + 产物 .ccr 的 TYPE/IFACE 段非空(内容面入真实构建路径); + ⑥ 冷/热两态:既有段 + TYPE + IFACE 段体逐字节同(语料 = add/main——ir_gen 不 + 新增类型行 ⇒ 行表缓存不变量;见 T2 用例 ⑮ 的分语料拆解)。 + +Task 2(TYPE 内容面): + ⑦ 段结构(两小节计数/长度自洽、行表前 9 行 = 原生行块);⑧ 段内行表/项表 == + corec 侧 dump(内存真值);⑨ corearch 读回逐行对拍(含 probe 行——判定原语 + 跨进程同值);⑩ DAG 语义(无重行 + 引用槽拓扑 + rowterm dedup 实证);⑪ + `--dump-types` 零产物影响(带/不带 flag 的 .ccr 逐字节同);⑫ loader 负分支 + (引用槽拓扑违规 / 计数越界 / 截断 / 尾随字节);⑬ 标注槽不误拒(ATOM b := + 5000 接受;负控 = 真引用槽同值拒绝);⑭ 冷/冷两次编译 TYPE 逐字节同;⑮ + 冷/热分语料拆解(缓存不变量语料 TYPE 同;ptr_arith 热态行表 = 冷态前缀 + + 项表/probe 同 + SYM 悬挂引用实测登记)。 + +Task 3(IFACE 内容面):见文件末「R2 P4 Task 3:IFACE 内容面」节(⑯..㉕)。 + +R2 P4 Task 4(DFNode.TK 迁升)+ R2 P5 Task 2(单槽化)见文件末同名节(㉖..㉛ / ㉜..㊲): +内存 DF 记录 72B = 9 槽;P5 T2 起两槽语义对调并分离——`OFF_DF_TK`(40) = **类型项引用** +(类型面唯一真源)、`OFF_DF_AUX`(64) = **辅码**(旗标/宽度/不可逆行的原码)。 +`.ccr` NOD **40B**(R2 P6 Task 3:32B 语义区 + 8B 邻接;+28 = 项索引 i32——TYPE 段 +文件空间项索引,-1 = 无项;`tk` 槽仍是派生码)与 `.cir` 快照布局**不变**(`CIR_CACHE_VER` 保持 17;盘面只承载 +**派生码**——两槽都是 (opcode, 码) 的纯函数,落盘 = 第二真源 + 进程内索引悬空, +实测见 cir_cache.cr 头注)。派生码逐节点 ≡ 单槽化前的混用码(D22-①)。 + +字节真相 = docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md +(v8 = v7 段表架构的加法扩展——D9/D10;文件名/测试名保留「v7」字样): + [0] magic u32 = 0x31524343 ("CCR1") + [4] version u32 = 9 + [8] seg_count u32 = 8 + [12] reserved u32 = 0 + [16] 段表 8 × 12B {tag u32, offset u32, size u32}(规范序 tag 1..8) + [112] 段体(tag 升序连续): + STR(1) / SYM(2) / NOD(3) / ENT(4) / REG(5) / EDG(6) / TYPE(7) / IFACE(8) + TYPE(7) = [row_count u32] [row_count × 24B {kind,data,extra} i64×3] + [term_count u32] [term_count × 40B {tag,a..d} i64×5](哈希不落盘) + IFACE(8) = 五小节(R2 P4 Task 3,D14;字段宽度见下): + ① [native_count u32 = 16] [× 24B {ak i32, ti_row i32, name_ni i32, lit_code i32, + ops i64}] + ② [shape_count u32] [× 8B {name_ni i32, term i32}] + ③ [iface_count u32] [× {name_ni i32, method_count i32, generic_count i32, pad i32} + + method_count × 80B {name_ni i32, param_count i32, self_mode i32, ret_term i32, + param_terms[8] i32, param_codes[8] i32}] + ④ [impl_count u32] [× 8B {trait_ni i32, type_ni i32}] + ⑤ [method_count u32] [× 12B {type_ni i32, method_ni i32, mangled_ni i32}] +""" +import os +import re +import shutil +import struct +import subprocess +import sys + +BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# 段表/对象面解析器唯一真源 = test_ccr_v7.py(同目录;test_mw_task1.py 先例) +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from test_ccr_v7 import parse_object_dump # noqa: E402 +COREC = os.path.join(BASE, 'build/corec') +COREARCH = os.path.join(BASE, 'build/corearch') + +MAGIC = 0x31524343 # "CCR1" +VER = 9 # v9 = v7 段表架构的加法扩展(D10)——R2 P6 Task 3:NOD 记录 36→40B(+28 项索引) +SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE +HEADER_TABLE = 16 + 8 * 12 # 112 +TYPE_TAG = 7 +IFACE_TAG = 8 +ESZ_TYPE_ROW = 24 # 类型行表记录:{kind,data,extra} i64×3(D12) +ESZ_TYPE_TERM = 40 # 项 DAG 记录:{tag,a,b,c,d} i64×5(哈希不落盘,加载侧重算) +IFACE_NATIVE_N = 16 # R2 P4 Task 3 扩列硬值(13 → 16;见 ccr_io.cr 注) +IFACE_ENTRY_DISK = 24 +IFACE_SHAPE_DISK = 8 +IFACE_METHOD_DISK = 80 +IFACE_IMPL_DISK = 8 +IFACE_GMETHOD_DISK = 12 +# D17 的六个形状名(生产注册面;corec 侧 iface_shape_builtin_init 逐名 str_intern) +IFACE_SHAPE_NAMES = ('sequence', 'sequence_ro', 'sequence_rw', + 'indexable', 'iterable', 'product') +# 项引用槽字段表(tag → 槽下标;未列 tag = 无引用槽——标注/保留槽不得按 +# 「< 自身行号」校验):TT_UNION/TT_INTER/TT_CONS = a,b;TT_NOT = a; +# TT_MU = b(a = 绑定变量);TT_ATOM = c(参数链头;a = ak、b = 标注)。 +# 与 ccr_io.cr:ccr_type_term_ref_ok 同域(独立复述——防同源同错自洽假绿)。 +TERM_REFS = {3: (0, 1), 4: (0, 1), 5: (0,), 6: (1,), 7: (2,), 10: (0, 1)} + +# 旧格式(v7)参照值——D10 的拒收面 +V7_SEG_TAGS = [1, 2, 3, 4, 5, 6] +V7_HEADER_TABLE = 16 + 6 * 12 # 88 +V7_VER = 7 + + +class CcrFile: + """`.ccr` 全段解析(8 段规范序契约;违反 = AssertionError)。""" + + def __init__(self, data: bytes): + self.d = data + assert len(data) >= HEADER_TABLE, \ + f"file too small for header+8-seg table: {len(data)}" + (magic, ver, seg_count, reserved) = struct.unpack_from('<4I', data, 0) + assert magic == MAGIC, f"bad magic {magic:#x}" + assert ver == VER, f"expected version {VER}, got {ver}" + assert seg_count == 8, f"expected 8 segments, got {seg_count}" + assert reserved == 0, f"reserved != 0: {reserved}" + self.segs = {} + cur = HEADER_TABLE # 段体紧随段表 + for i, tag in enumerate(SEG_TAGS): + (t, off, size) = struct.unpack_from('<3I', data, 16 + i * 12) + assert t == tag, f"row {i}: expected tag {tag}, got {t}" + assert off == cur, f"tag {tag}: expected offset {cur}, got {off}" + assert off + size <= len(data), f"tag {tag}: body out of file" + self.segs[tag] = (off, size) + cur = off + size + assert cur == len(data), \ + f"segments end at {cur} of {len(data)} bytes" + self.fsize = len(data) + + def body(self, tag: int) -> bytes: + off, size = self.segs[tag] + return self.d[off:off + size] + + def count(self, tag: int) -> int: + """段体首字段(各段自带计数 u32)。""" + (n,) = struct.unpack_from(' str: + r = subprocess.run([COREC, 'ccr', src, '-o', out], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, f"corec ccr failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def read_ccr(path: str) -> bytes: + with open(path, 'rb') as fh: + return fh.read() + + +def arch_elf(ccr_in: str, elf_out: str, must_fail: bool = False): + r = subprocess.run([COREARCH, ccr_in, '--elf', '--static', '-o', elf_out], + capture_output=True, text=True, cwd=BASE, timeout=180) + if must_fail: + assert r.returncode != 0, \ + f"corearch accepted mutated file: rc={r.returncode} " \ + f"(silent-accept = 三态纪律违反)" + assert 'invalid' in (r.stdout + r.stderr), \ + f"expected invalid-.ccr error, got: {r.stdout!r} {r.stderr!r}" + else: + assert r.returncode == 0, \ + f"corearch load failed: rc={r.returncode} {r.stdout!r} {r.stderr!r}" + return r + + +def _write(path: str, data: bytes) -> str: + with open(path, 'wb') as fh: + fh.write(data) + return path + + +def _v7_structure_ok(data: bytes) -> bool: + """独立 v7 结构校验(6 段规范序/连续/尾部贴合)——证明重建文件 = 合法 + v7 而非「字节破烂」(拒绝必须归因于版本闸,不是畸形)。""" + if len(data) < V7_HEADER_TABLE: + return False + (magic, ver, seg_cnt, rsv) = struct.unpack_from('<4I', data, 0) + if magic != MAGIC or ver != V7_VER or seg_cnt != 6 or rsv != 0: + return False + cur = V7_HEADER_TABLE + for i, tag in enumerate(V7_SEG_TAGS): + (t, off, size) = struct.unpack_from('<3I', data, 16 + i * 12) + if t != tag or off != cur or off + size > len(data): + return False + cur = off + size + return cur == len(data) + + +def _rebuild_v7(data: bytes) -> bytes: + """把 v8 文件重建成**字节级合法**的 v7 文件:header 版本 7/6 段 + 段表 + offset 重算(88 起)+ 前六段段体原样(v8 的加法扩展不改前六段体)。""" + bodies = [CcrFile(data).body(t) for t in V7_SEG_TAGS] + out = bytearray(struct.pack('<4I', MAGIC, V7_VER, 6, 0)) + cur = V7_HEADER_TABLE + for i, tag in enumerate(V7_SEG_TAGS): + out += struct.pack('<3I', tag, cur, len(bodies[i])) + cur += len(bodies[i]) + for b in bodies: + out += b + return bytes(out) + + +def _prog_src() -> str: + return ("fn add(a: int, b: int) -> int { return a + b; }\n" + "fn main() -> int { return add(40, 2); }\n") + + +def _shell_fixture(name: str): + """产一个 fixture .ccr 并返回 (ccr_path, data)。调用方负责 cleanup。""" + src_path = os.path.join(BASE, 'build', f'test_p4t1_{name}.cr') + ccr_path = os.path.join(BASE, 'build', f'test_p4t1_{name}.ccr') + for p in (src_path, ccr_path): + try: + os.unlink(p) + except FileNotFoundError: + pass + with open(src_path, 'w') as fh: + fh.write(_prog_src()) + corec_ccr(src_path, ccr_path) + return src_path, ccr_path + + +def test_p4t1_layout_eight_segments(): + """① 段表 = 8 段规范序(tag 1..8)+ offset 连续 + 末段尾 == 文件大小; + header version = 8 / seg_count = 8 / reserved = 0。""" + src_path, ccr_path = _shell_fixture('layout') + try: + c = CcrFile(read_ccr(ccr_path)) + assert c.fsize > HEADER_TABLE + # 前六段非空(既有内容面不因加段塌陷) + for tag in (1, 2, 3, 4, 5, 6): + assert c.segs[tag][1] > 4, f"segment {tag} unexpectedly empty" + # TYPE(7) = 内容面(R2 P4 Task 2 起):至少原生 9 行 + 项段 + tbody = c.body(TYPE_TAG) + assert len(tbody) >= 4 + 9 * ESZ_TYPE_ROW + 4 + ESZ_TYPE_TERM, \ + f"TYPE body {len(tbody)}B < native 9-row minimum" + # IFACE(8) = 内容面(R2 P4 Task 3 起;空壳期已退役——原 SHELL_TAGS 断言 + # 由 `test_p4t3_iface_segment_layout` 的五小节结构断言取代) + ibody = c.body(IFACE_TAG) + assert len(ibody) >= 4 + IFACE_NATIVE_N * IFACE_ENTRY_DISK + 4 + 6 * IFACE_SHAPE_DISK, \ + f"IFACE body {len(ibody)}B < native 16 rows + 6 shapes minimum" + finally: + for p in (src_path, ccr_path): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_p4t1_loader_rejects_valid_v7_file(): + """④-a D10 正面证据:**结构性合法的 6 段 v7 文件**(段表重新算过 offset、 + 段体原样)→ corearch 必须拒(版本闸整类拒收)——不得静默当空表读成 + 「两段缺席 = 空」。 + 非平凡性控制:先以独立校验器确认重建文件 = 合法 v7(否则「拒绝」可能只是 + 畸形字节的副产品)。""" + src_path, ccr_path = _shell_fixture('v7rej') + bad = ccr_path + '.v7old' + try: + v7 = _rebuild_v7(read_ccr(ccr_path)) + assert _v7_structure_ok(v7), "rebuilt file is not a structurally valid v7" + # 段体逐字节来自 v8 文件前六段(加法扩展不改前六段)——合法性自证 + c = CcrFile(read_ccr(ccr_path)) + for i, tag in enumerate(V7_SEG_TAGS): + off, size = struct.unpack_from('<3I', v7, 16 + i * 12)[1:] + assert v7[off:off + size] == c.body(tag), f"seg {tag} body drifted" + _write(bad, v7) + arch_elf(bad, os.path.join(BASE, 'build/test_p4t1_v7old.out'), + must_fail=True) + finally: + for p in (src_path, ccr_path, bad, + os.path.join(BASE, 'build/test_p4t1_v7old.out')): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_p4t1_loader_rejects_missing_type_iface(): + """④-b D11:段表被改成 6 段且末段 size 扩到 EOF(tag 序/连续/越界三闸 + 全过)→ 拒绝必须来自**必备集**(have7/have8 == 0),不得静默当空表。""" + src_path, ccr_path = _shell_fixture('segsix') + bad = ccr_path + '.six' + try: + data = bytearray(read_ccr(ccr_path)) + c = CcrFile(bytes(data)) + edg_off, _ = c.segs[6] + struct.pack_into(' 4, \ + f"tag {tag} body collapsed to shell size {c.segs[tag][1]}B" + finally: + for p in (src_path, ccr_path): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_p4t1_loader_rejects_truncated_body(): + """④-e 段体截断(文件 −1B)→ 越界拒绝(末段 size 不再被文件容纳)。""" + src_path, ccr_path = _shell_fixture('trunc') + bad = ccr_path + '.cut' + try: + data = read_ccr(ccr_path) + _write(bad, data[:-1]) + arch_elf(bad, os.path.join(BASE, 'build/test_p4t1_cut.out'), + must_fail=True) + finally: + for p in (src_path, ccr_path, bad, + os.path.join(BASE, 'build/test_p4t1_cut.out')): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_p4t1_end_to_end_shells(): + """⑤ 全链不回归:corec build --static(走 corec→.ccr→corearch) + 与 corec ccr + corearch --elf 两路径产物行为正确(rc=42);且真实构建路径 + 产出的 .ccr 带**非空 TYPE 段**(内容面入盘——R2 P4 Task 2 起)。""" + src_path, ccr_path = _shell_fixture('e2e') + elf1 = os.path.join(BASE, 'build/test_p4t1_e2e1.out') + elf2 = os.path.join(BASE, 'build/test_p4t1_e2e2.out') + try: + r = subprocess.run([COREC, 'build', src_path, '-o', elf1, '--static'], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, f"corec build failed: {r.stdout}\n{r.stderr}" + # build 路径的 .ccr(elf1 + '.ccr')——TYPE 段非空 + 两小节自洽 + bccr = elf1 + '.ccr' + try: + rows, terms = parse_type_segment(CcrFile(read_ccr(bccr)).body(TYPE_TAG)) + assert len(rows) >= 9 and len(terms) > 0, \ + f"build-path TYPE segment empty: {len(rows)} rows {len(terms)} terms" + finally: + try: + os.unlink(bccr) + except FileNotFoundError: + pass + arch_elf(ccr_path, elf2) + for p in (elf1, elf2): + os.chmod(p, 0o755) + rr = subprocess.run([p], capture_output=True, timeout=10) + assert rr.returncode == 42, \ + f"{os.path.basename(p)} exit {rr.returncode} != 42" + finally: + for p in (src_path, ccr_path, elf1, elf2): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_p4t1_cold_warm_shell_stability(): + """⑥ 冷/热两态:TYPE/IFACE 段体逐字节同。语料 = add/main(ir_gen 不新增 + 类型行 ⇒ 行表缓存不变量——分语料拆解见 T2 用例 ⑮)。 + 命中证据 = 暖运行不重写缓存文件(mtime 不变);同时复核既有五段(2..6) + 冷/热逐字节同(继承 test_ccr_v7 口径),STR 差异不入本判据。""" + cache_dir = os.path.join(BASE, '.core', 'cache') + cir_dir = os.path.join(cache_dir, 'cir') + src_path, ccr_path = _shell_fixture('cw') + out_cold = os.path.join(BASE, 'build/test_p4t1_cw_cold.ccr') + out_warm = os.path.join(BASE, 'build/test_p4t1_cw_warm.ccr') + try: + shutil.rmtree(cache_dir, ignore_errors=True) + corec_ccr(src_path, out_cold) + snap = {os.path.join(cir_dir, f): os.stat( + os.path.join(cir_dir, f)).st_mtime_ns + for f in os.listdir(cir_dir)} + assert snap, "cold run did not populate cir cache" + corec_ccr(src_path, out_warm) + for p, ns in snap.items(): + assert os.stat(p).st_mtime_ns == ns, \ + f"warm run rewrote cache file {p} (not a full hit?)" + cc = CcrFile(read_ccr(out_cold)) + cw = CcrFile(read_ccr(out_warm)) + for tag in (2, 3, 4, 5, 6, TYPE_TAG, IFACE_TAG): + assert cc.body(tag) == cw.body(tag), \ + f"segment {tag} drifted cold→warm" + finally: + for p in (src_path, ccr_path, out_cold, out_warm): + try: + os.unlink(p) + except FileNotFoundError: + pass + shutil.rmtree(cache_dir, ignore_errors=True) + + +# ═════════════════════ R2 P4 Task 2:TYPE 内容面 ═════════════════════ + +P4T2_ROW_RE = re.compile(r'^row (\d+) kind (-?\d+) data (-?\d+) extra (-?\d+)$') +P4T2_TERM_RE = re.compile( + r'^term (\d+) tag (-?\d+) a (-?\d+) b (-?\d+) c (-?\d+) d (-?\d+)$') +P4T2_ROWT_RE = re.compile(r'^rowterm (\d+) (-?\d+)$') +P4T2_PROBE_RE = re.compile(r'^probe: (.*)$') + + +def parse_type_dump(text: str): + """解析 `--dump-types` 输出(corec 与 corearch **同一条打印路径**,行格式 + 契约见 ccr_io.cr:ccr_type_surface_dump): + + rows: N + row i kind K data D extra E (i = 0..N-1) + terms: M + term i tag T a A b B c C d D (i = 0..M-1) + probe: n N sub1 X sub0 Y unknown Z digest H + [rowterms: N / rowterm i T] (corec-only 节——本解析器忽略) + + 返回 (rows, terms, probe, rowterms);行号必须无洞(契约)。""" + rows, terms, rowterms = {}, {}, {} + probe = None + for ln in text.splitlines(): + m = P4T2_ROW_RE.match(ln) + if m: + rows[int(m.group(1))] = (int(m.group(2)), int(m.group(3)), int(m.group(4))) + continue + m = P4T2_TERM_RE.match(ln) + if m: + terms[int(m.group(1))] = tuple(int(m.group(k)) for k in (2, 3, 4, 5, 6)) + continue + m = P4T2_ROWT_RE.match(ln) + if m: + rowterms[int(m.group(1))] = int(m.group(2)) + continue + m = P4T2_PROBE_RE.match(ln) + if m: + probe = m.group(1) + assert rows, "dump has no 'row' lines" + assert terms, "dump has no 'term' lines" + assert probe is not None, "dump has no 'probe' line" + assert sorted(rows) == list(range(len(rows))), "row ids not contiguous" + assert sorted(terms) == list(range(len(terms))), "term ids not contiguous" + return ([(rows[i]) for i in range(len(rows))], + [(terms[i]) for i in range(len(terms))], + probe, rowterms) + + +def parse_type_segment(body: bytes): + """TYPE 段字节 → (rows, terms)(D12 布局直解——独立于 dump 的第三方实现; + 行走完必须恰等于段体长度 = 无尾随字节)。""" + (rc,) = struct.unpack_from(' str: + """corec ccr --dump-types(写侧内存真值 dump)→ stdout。""" + r = subprocess.run([COREC, 'ccr', src, '-o', out, '--dump-types'], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, \ + f"corec ccr --dump-types failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def arch_dump(ccr_in: str) -> str: + """corearch --dump-types(读侧载入重建后的 dump)→ stdout。""" + r = subprocess.run([COREARCH, ccr_in, '--dump-types'], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, \ + f"corearch --dump-types failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def type_seg(data: bytes) -> bytes: + return CcrFile(data).body(TYPE_TAG) + + +def _resize_type_tail(data: bytes, delta: int) -> bytes: + """TYPE 段体尾增(+)/删(-) |delta| 字节,并同步段表(TYPE size、IFACE offset) + ——产出「段表仍自洽、TYPE 内容被破坏」的文件(拒收必须归因于内容闸)。""" + d = bytearray(data) + (t_off, t_size) = struct.unpack_from('<2I', d, 16 + 6 * 12 + 4) + struct.pack_into(' 0: + return bytes(d[:cut]) + b'\x00' * delta + bytes(d[cut:]) + return bytes(d[:cut + delta]) + bytes(d[cut:]) + + +def sym_type_refs(data: bytes): + """SYM 段里全部类型行引用(globals type + func var_decls type)——检查载体 + 自洽(引用域必须 < TYPE 段行数;T2 用例 ⑮ 的根因量化用)。""" + c = CcrFile(data) + off, size = c.segs[2] + end = off + size + p = off + refs = [] + (gc,) = struct.unpack_from('= 9, f"row table too small: {len(rows)}" + assert len(terms) > 0, "term DAG empty" + for i in range(7): # 原生 int/dex/bool/string/unit/never/char + assert rows[i][0] == rows[0][0] and rows[i][1] == i, \ + f"native row {i} drifted: {rows[i]}" + for i, t in enumerate(terms): # D12 加载侧同域不变量 + assert 0 <= t[0] <= 10, f"term {i} tag out of 0..10: {t[0]}" + assert all(v >= -1 for v in t[1:]), f"term {i} field < -1: {t}" + for slot in TERM_REFS.get(t[0], ()): + r = t[1 + slot] + assert r == -1 or r < i, \ + f"term {i} ref slot {slot} = {r} not < own row (topology)" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t2_rows_and_terms_match_corec_dump(): + """⑧ 段内行表/项表 == corec 侧内存表(dump 打印内存真值)——写侧装填 + + 序列化的保真链:内存 → 缓冲 → 文件(Python 直解字节)。""" + src_path, ccr_path = _shell_fixture('t2rows') + try: + dump = corec_dump(src_path, ccr_path) + d_rows, d_terms, _probe, d_rowt = parse_type_dump(dump) + b_rows, b_terms = parse_type_segment(type_seg(read_ccr(ccr_path))) + assert [tuple(r) for r in b_rows] == d_rows, "file rows != memory rows" + assert [tuple(t) for t in b_terms] == d_terms, "file terms != memory terms" + assert len(d_rowt) == len(d_rows), \ + f"rowterm map size {len(d_rowt)} != rows {len(d_rows)}" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t2_corearch_readback_parity(): + """⑨ 读回证据(spec §6.3 + 判定原语跨进程同值):corearch `--dump-types` + 与 corec 侧 dump 的共享节(rows/terms/probe)逐行一致——载入段重建的行表 / + 项 DAG / 引擎判定结果与写侧进程逐位同。""" + src_path, ccr_path = _shell_fixture('t2par') + try: + corec_out = corec_dump(src_path, ccr_path) + arch_out = arch_dump(ccr_path) + c_rows, c_terms, c_probe, c_rowt = parse_type_dump(corec_out) + a_rows, a_terms, a_probe, a_rowt = parse_type_dump(arch_out) + assert c_rows == a_rows, "read-back row table drifted" + assert c_terms == a_terms, "read-back term DAG drifted" + assert c_probe == a_probe, \ + f"probe diverged across processes: {c_probe!r} vs {a_probe!r}" + assert 'digest' in c_probe and 'norm_unknown' in c_probe and 'lit_ok' in c_probe + assert c_rowt and not a_rowt, "rowterm section must be corec-only" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t2_dag_dedup_topology(): + """⑩ DAG 语义(ptr_arith 语料——含 &arr[i]/&x 的指针行): + ① 项表无同 (tag,a..d) 重行(tt_term 去重的不变量,重建路径的牙); + ② rowterm 映射:重复构造的指针行全部命中**同一项**(dedup 实证)且 `[int;5]` + 与 `*int` 命中不同项;③ 引用槽 < 自身行号 + 引用值在项表域内。""" + src = os.path.join(BASE, 'tests/suite/ptr_arith.cr') + ccr = os.path.join(BASE, 'build/test_p4t2_dedup.ccr') + try: + dump = corec_dump(src, ccr) + d_rows, d_terms, _probe, d_rowt = parse_type_dump(dump) + b_rows, b_terms = parse_type_segment(type_seg(read_ccr(ccr))) + assert [tuple(t) for t in b_terms] == d_terms + assert len(set(b_terms)) == len(b_terms), "duplicate term rows (no dedup)" + for i, t in enumerate(b_terms): + for slot in TERM_REFS.get(t[0], ()): + r = t[1 + slot] + assert r == -1 or r < i, f"term {i} ref {r} not < own row" + assert all(0 <= v < len(b_terms) for v in d_rowt.values()), \ + "rowterm index out of term table" + assert len(d_rowt) == len(d_rows) + # dedup 实证:ptr_arith 的 5 个 `*int` 行(&arr[2]/&x 等)→ 同一项 + from collections import Counter + cnt = Counter(d_rowt.values()) + assert cnt.most_common(1)[0][1] >= 2, \ + f"no term shared by >=2 rows (dedup not exercised): {cnt}" + assert len(set(d_rowt.values())) >= 2, "all rows map to one term?" + finally: + try: + os.unlink(ccr) + except FileNotFoundError: + pass + + +def test_p4t2_dump_flag_zero_artifact_effect(): + """⑪ `--dump-types` 是**只读通道**:带/不带 flag 的 .ccr 逐字节同——probe 的 + 规范化建项发生在段体缓冲构造**之后**,不得泄入序列化面。""" + src_path, ccr_path = _shell_fixture('t2flag') + with_dump = ccr_path + '.dump' + try: + corec_ccr(src_path, ccr_path) # 不带 flag + corec_dump(src_path, with_dump) # 带 flag + assert read_ccr(ccr_path) == read_ccr(with_dump), \ + "--dump-types changed the emitted .ccr" + finally: + _cleanup(src_path, ccr_path, with_dump) + + +def test_p4t2_loader_rejects_topology_violation(): + """⑫-a 子项引用拓扑违规(自引用 / 前向引用 / 远越界)⇒ 拒绝。""" + src = os.path.join(BASE, 'tests/suite/ptr_arith.cr') + ccr = os.path.join(BASE, 'build/test_p4t2_topo.ccr') + try: + corec_dump(src, ccr) + data = read_ccr(ccr) + rows, terms = parse_type_segment(type_seg(data)) + t_off, _ = CcrFile(data).segs[TYPE_TAG] + base = t_off + 4 + len(rows) * ESZ_TYPE_ROW + 4 + # 挑一个带引用槽的项(CONS/UNION/INTER/NOT/MU/ATOM-c) + idx = next(i for i, t in enumerate(terms) if TERM_REFS.get(t[0])) + slot = TERM_REFS[terms[idx][0]][0] + for bad_val in (idx, idx + 1, len(terms) + 1000): + d = bytearray(data) + struct.pack_into('warm on a cache-invariant corpus" + # ② ptr_arith:冷/热行表前缀关系 + 项表/probe 同值 + shutil.rmtree(cache_dir, ignore_errors=True) + out_cold = corec_dump(pa, pa_cold) # 冷(no cache) + out_warm = corec_dump(pa, pa_warm) # 热(全命中) + dc, dw = read_ccr(pa_cold), read_ccr(pa_warm) + rc_rows, rc_terms = parse_type_segment(type_seg(dc)) + rw_rows, rw_terms = parse_type_segment(type_seg(dw)) + assert rc_terms == rw_terms, \ + f"term DAG drifted cold->warm ({len(rc_terms)} vs {len(rw_terms)})" + assert len(rw_rows) <= len(rc_rows), "warm row table larger than cold?" + assert rw_rows == rc_rows[:len(rw_rows)], \ + "warm row table is not a prefix of the cold row table" + pc = [ln for ln in out_cold.splitlines() if ln.startswith('probe:')] + pw = [ln for ln in out_warm.splitlines() if ln.startswith('probe:')] + assert pc == pw and pc, "probe diverged cold->warm" + # 悬挂引用量化(登记,不修):两侧引用均不出冷态行表 + for tag, name in ((dc, 'cold'), (dw, 'warm')): + refs = sym_type_refs(tag) + assert max(refs) < len(rc_rows), \ + f"{name}: SYM type ref outside cold row table" + d_cold = sum(1 for r in sym_type_refs(dc) if r >= len(rc_rows)) + d_warm = sum(1 for r in sym_type_refs(dw) if r >= len(rw_rows)) + print(f" [info] ptr_arith SYM dangling type refs: cold={d_cold} " + f"warm={d_warm} (rows cold={len(rc_rows)} warm={len(rw_rows)}; " + f"root cause = .cir cache hit skips ir_gen alloc_type)") + assert d_cold == 0, f"cold .ccr has {d_cold} dangling type refs" + finally: + _cleanup(src_path, ccr_path, cold, warm, pa_cold, pa_warm, + pa_cold + '.d', pa_warm + '.d') + + +# ═════════════════════ R2 P4 Task 3:IFACE 内容面 ═════════════════════ +# 字节布局 = 文件头 docstring(D14 五小节;方法记录字段序取计划「声明行」序: +# {name_ni, param_count, self_mode, ret_term, param_terms[8], param_codes[8]})。 + +P4T3_NATIVE_RE = re.compile(r'^native (\d+) ak (-?\d+) ti (-?\d+) name (-?\d+) lit (-?\d+) ops (-?\d+)$') +P4T3_SHAPE_RE = re.compile(r'^shape (\d+) name (-?\d+) term (-?\d+)$') +P4T3_IFACE_RE = re.compile(r'^iface (\d+) name (-?\d+) methods (\d+) generics (-?\d+)$') +P4T3_METHOD_RE = re.compile(r'^method (\d+) (\d+) name (-?\d+) params (\d+) self (-?\d+) ret (-?\d+)$') +P4T3_MPARAM_RE = re.compile(r'^mparam (\d+) (\d+) (\d+) code (-?\d+) term (-?\d+)$') +P4T3_IMPL_RE = re.compile(r'^impl (\d+) trait (-?\d+) type (-?\d+)$') +P4T3_GMETHOD_RE = re.compile(r'^gmethod (\d+) type (-?\d+) method (-?\d+) mangled (-?\d+)$') +P4T3_PROBE_RE = re.compile( + r'^ifaceprobe: slots (\d+) built (\d+) unbuilt (\d+) bad (\d+) digest (-?\d+)$') +P4T3_SIG_RE = re.compile(r'^ifacesig (\d+) (\d+) ret (-?\d+) pc (\d+)$') +P4T3_SIGP_RE = re.compile(r'^ifacesigp (\d+) (\d+) (\d+) (-?\d+)$') + + +def parse_iface_segment(body: bytes): + """IFACE 段字节 → (natives, shapes, ifaces, impls, methods) + (D14 布局直解——独立于 dump 的第三方实现;行走完必须恰等于段体长度)。 + natives = [(ak, ti, name_ni, lit, ops)];shapes = [(name_ni, term)]; + ifaces = [(name_ni, method_count, generic_count, pad, [(mname, pc, self_mode, + ret_term, param_terms(8), param_codes(8))])];impls = [(trait_ni, type_ni)]; + methods = [(type_ni, method_ni, mangled_ni)]。""" + p = 0 + (nat,) = struct.unpack_from(' str: + r = subprocess.run([COREC, 'ccr', src, '-o', out, '--dump-ifaces'], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, \ + f"corec ccr --dump-ifaces failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def arch_dump_ifaces(ccr_in: str) -> str: + r = subprocess.run([COREARCH, ccr_in, '--dump-ifaces'], + capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, \ + f"corearch --dump-ifaces failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def str_table(data: bytes): + """STR 段 → 串列表(ni → 串——跨段引用域断言的独立解析面)。""" + off, size = CcrFile(data).segs[1] + p = off + (n,) = struct.unpack_from(' bytes: + return CcrFile(data).body(IFACE_TAG) + + +# 夹具源:2 接口(self 接收者 + &self + 带参方法)+ 1 结构 + impl 块 +# (g_impl_for 边 + g_methods 方法表 + 接口方法签名项——五小节全非空) +_T3_SRC = ("interface Show { fn show(self) -> int; }\n" + "interface Pair2 { fn first(&self) -> int; fn second(self, x: int) -> int; }\n" + "struct S { a: int }\n" + "impl Show for S { fn show(self: S) -> int { return 2; } }\n" + "fn main() -> int { s := S { a = 2 }; return s.a; }\n") + + +def _t3_fixture(name: str): + src_path = os.path.join(BASE, 'build', f'test_p4t3_{name}.cr') + ccr_path = os.path.join(BASE, 'build', f'test_p4t3_{name}.ccr') + for p in (src_path, ccr_path): + try: + os.unlink(p) + except FileNotFoundError: + pass + with open(src_path, 'w') as fh: + fh.write(_T3_SRC) + corec_ccr(src_path, ccr_path) + return src_path, ccr_path + + +def _resize_iface_tail(data: bytes, delta: int) -> bytes: + """IFACE 段体尾增(+)/删(-) |delta| 字节(段表有尾随量可容纳时用——本段是末段, + 增删会改变文件长度;调用方负责只做「可容纳」的破坏)。""" + d = bytearray(data) + (i_off, i_size) = struct.unpack_from('<2I', d, 16 + 7 * 12 + 4) + struct.pack_into(' 0: + return bytes(d[:cut]) + b'\x00' * delta + bytes(d[cut:]) + return bytes(d[:cut + delta]) + bytes(d[cut:]) + + +def test_p4t3_iface_segment_layout(): + """⑯ 五小节结构自洽:计数/长度逐小节与内存真值一致(native 16 == 扩列硬值、 + 形状 6 == D17 生产注册面、接口/方法/impl/方法表逐条 == dump)+ 行走完 == 段体 + 长度(无尾随字节)+ 逐记录不变量(ak ∈ 0..15、ti_row ∈ {-1} ∪ 行域、name_ni + 入 STR 域、term 入 TYPE 项域、self_mode ∈ 0..3)。""" + src_path, ccr_path = _t3_fixture('layout') + try: + data = read_ccr(ccr_path) + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + names = str_table(data) + rows, terms = parse_type_segment(type_seg(data)) + # ① 原生条目:扩列硬值 + 三新行(AK_NULL=15 / AK_SUM=9 / AK_FN=13)纯信息面 + assert len(natives) == IFACE_NATIVE_N, f"native count {len(natives)} != 16" + aks = [r[0] for r in natives] + assert len(set(aks)) == len(aks), f"duplicate ak rows: {aks}" + new_rows = {r[0]: r for r in natives if r[0] in (15, 9, 13)} + assert set(new_rows) == {15, 9, 13}, f"missing extended rows: {aks}" + assert new_rows[15][4] == 0 and new_rows[9][4] == 0 and new_rows[13][4] == 0, \ + "extended rows must be pure-info (ops == 0)" + assert new_rows[15][3] == -1 and new_rows[9][3] == -1 and new_rows[13][3] == -1, \ + "extended rows must have no lit_code" + # ② 形状:6 条 D17 生产名(按 STR 解析,逐名) + assert len(shapes) == 6, f"shape count {len(shapes)} != 6" + sh_names = sorted(names[nm] for nm, _ in shapes) + assert sh_names == sorted(IFACE_SHAPE_NAMES), f"shape names drifted: {sh_names}" + # ③ 用户接口:夹具 2 接口 + Pair2 双方法;签名项槽逐条入域 + assert len(ifaces) == 2, f"iface count {len(ifaces)} != 2" + for nm, mc, gc, pad, meths in ifaces: + assert 0 <= nm < len(names) + assert mc == len(meths) and 0 < mc <= 16 + for (mname, pc, sm, rt, pterms, pcodes) in meths: + assert 0 <= mname < len(names) + assert 0 <= pc <= 8 and 0 <= sm <= 3 + assert rt == -1 or 0 <= rt < len(terms), f"ret term {rt} out of domain" + for t in pterms: + assert t == -1 or 0 <= t < len(terms), f"param term {t} out of domain" + for c in pcodes: + assert c >= -1 + # ④ impl 边 + ⑤ 方法表:逐条 ni 入 STR 域 + assert len(impls) >= 1, "fixture has no impl edge" + assert len(methods) >= 1, "fixture has no g_methods rows" + for tr, ty in impls: + assert 0 <= tr < len(names) and 0 <= ty < len(names) + for ty, mn, mg in methods: + assert 0 <= ty < len(names) and 0 <= mn < len(names) and 0 <= mg < len(names) + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_bytes_match_corec_dump(): + """⑰ 段内五小节 == corec 侧 dump(内存真值)——写侧装填 + 序列化保真链 + (内存 → 缓冲 → 文件,Python 直解字节逐字段对拍)。""" + src_path, ccr_path = _t3_fixture('rows') + try: + dump = corec_dump_ifaces(src_path, ccr_path) + d = parse_iface_dump(dump) + data = read_ccr(ccr_path) + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + assert [tuple(r) for r in natives] == [tuple(r) for r in d['natives']], \ + "file natives != memory natives" + assert shapes == d['shapes'], "file shapes != memory shapes" + assert impls == d['impls'], "file impls != memory impls" + assert methods == d['gmethods'], "file g_methods != memory g_methods" + # ③ 用户接口(含方法 80B 记录逐字段:项槽/裸码槽分别对拍) + d_if = d['ifaces'] + d_me = [m for m in d['method']] + d_mp = {(i, j, k): (c, t) for (i, j, k, c, t) in d['mparam']} + assert len(ifaces) == len(d_if), "iface count drift" + for i, (nm, mc, gc, pad, meths) in enumerate(ifaces): + assert (nm, mc, gc) == d_if[i], f"iface {i} header drift" + for j, (mname, pc, sm, rt, pterms, pcodes) in enumerate(meths): + mrow = next(m for m in d_me if m[0] == i and m[1] == j) + assert (mname, pc, sm, rt) == mrow[2:], f"method {i}.{j} drift" + for k in range(8): + assert (pcodes[k], pterms[k]) == d_mp[(i, j, k)], \ + f"method {i}.{j} param {k} slot drift" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_corearch_readback_parity(): + """⑱ 读回证据(spec §6.3):corearch `--dump-ifaces` 与 corec 侧 dump 的共享节 + 逐行一致(条目表/形状表/接口表/impl 边/方法表 + ifaceprobe 行——跨段项索引在 + 重建项表上解析同值);corec-only 的活算节(ifacesig*)只在写侧存在。""" + src_path, ccr_path = _t3_fixture('par') + try: + corec_out = corec_dump_ifaces(src_path, ccr_path) + arch_out = arch_dump_ifaces(ccr_path) + c = parse_iface_dump(corec_out) + a = parse_iface_dump(arch_out) + for key in ('natives', 'shapes', 'ifaces', 'method', 'mparam', 'impls', 'gmethods'): + assert c[key] == a[key], f"read-back {key} drifted" + assert c['probe'] == a['probe'], \ + f"ifaceprobe diverged across processes: {c['probe']} vs {a['probe']}" + assert c['probe'][3] == 0, f"cross-segment probe bad != 0: {c['probe']}" + assert c['sig'] and not a['sig'], "ifacesig section must be corec-only" + assert c['sigp'] and not a['sigp'], "ifacesigp section must be corec-only" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_signature_itemization_matches_live(): + """⑲ 签名项化:段内 ret_term/param_terms == corec 侧**活算**签名项 + (sh_iface_sig_ret_term/sh_iface_sig_param_term——缓冲构造后独立读点)⇒ + 装填与签名来源零漂移;未用形参槽 = -1;接收者槽 = unit 占位项(>= 0)。""" + src_path, ccr_path = _t3_fixture('sig') + try: + dump = corec_dump_ifaces(src_path, ccr_path) + d = parse_iface_dump(dump) + data = read_ccr(ccr_path) + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + for i, (nm, mc, gc, pad, meths) in enumerate(ifaces): + for j, (mname, pc, sm, rt, pterms, pcodes) in enumerate(meths): + live_ret, live_pc = d['sig'][(i, j)] + assert live_pc == pc, f"live param count {live_pc} != file {pc}" + assert rt == live_ret, \ + f"method {i}.{j} ret term {rt} != live {live_ret}" + for k in range(8): + if k < pc: + live = d['sigp'][(i, j, k)] + assert pterms[k] == live, \ + f"method {i}.{j} param {k} term {pterms[k]} != live {live}" + assert live >= 0, f"live signature item unbuildable at {i}.{j}.{k}" + else: + assert (i, j, k) not in d['sigp'], \ + "live section must not print unused param slots" + assert pterms[k] == -1, "unused param term slot must be -1" + # 签名项确实**非空**(防「全 -1 空签名」假绿):至少一个有效项 >= 0 + assert any(t >= 0 for _, _, _, _, ms in ifaces for m in ms for t in m[4]), \ + "no effective signature terms in fixture" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_shape_name_registration(): + """⑳ 形状命名化(裁决 1 + D17):六生产名入段(name_ni → STR 解析逐名)+ + 形状项在 TYPE 项域内(跨段引用域);`shape` 行的 term 与接口轴 A 的 + 形状项同域(判定 `arr <: 形状项` 语义见 selftest `x2.shape_builtin_names`)。""" + src_path, ccr_path = _t3_fixture('shape') + try: + data = read_ccr(ccr_path) + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + names = str_table(data) + rows, terms = parse_type_segment(type_seg(data)) + got = {names[nm]: term for nm, term in shapes} + assert sorted(got) == sorted(IFACE_SHAPE_NAMES), f"shape names: {sorted(got)}" + for nm, term in shapes: + assert 0 <= term < len(terms), f"shape {nm} term {term} out of TYPE domain" + # 形状项结构(与 iface_registry.cr 的构造点语义独立对拍):顶层展开收集原子类 + # ——TT_TOP_K(2) → a = 原子类;TT_UNION(3) → 两操作数;TT_ATOM(7) → a = 原子类。 + # 期望值 = 六形状的**语义定义**(D17/spec §2.2):序列本体 = ⊤_SEQUENCE; + # 只读 = 序列 ∪ ref 视图(AK_REF=11);可索引 = 序列 ∪ 字符串(AK_STRING=2); + # product = ⊤_PRODUCT(AK_PRODUCT=8)。 + AK_STRING, AK_PRODUCT, AK_SEQUENCE, AK_REF = 2, 8, 10, 11 + exp = {'sequence': sorted([AK_SEQUENCE]), + 'sequence_ro': sorted([AK_SEQUENCE, AK_REF]), + 'sequence_rw': sorted([AK_SEQUENCE]), + 'indexable': sorted([AK_SEQUENCE, AK_STRING]), + 'iterable': sorted([AK_SEQUENCE]), + 'product': sorted([AK_PRODUCT])} + + def top_classes(ti): + tag, a, b = terms[ti][0], terms[ti][1], terms[ti][2] + if tag == 2: # TT_TOP_K + return [a], tag + if tag == 7: # TT_ATOM + return [a], tag + if tag == 3: # TT_UNION:两操作数各递归(各为 TOP_K/ATOM) + out = [] + for op in (a, b): + assert 0 <= op < len(terms), f"union operand {op} out of domain" + sub, _ = top_classes(op) + out.extend(sub) + return sorted(out), tag + raise AssertionError(f"unexpected shape term tag {tag}") + + for nm, term in shapes: + classes, tag = top_classes(term) + assert classes == exp[names[nm]], \ + f"shape {names[nm]} classes {classes} != expected {exp[names[nm]]}" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_cross_process_term_probe(): + """㉑ 跨段引用域探针:`ifaceprobe` 行的 slots == Σ(方法数 × (1 + pc))、 + built + unbuilt == slots、bad == 0,且逐槽 **解析摘要跨进程同值** + (corec 与 corearch 两侧 digest 同 ⇒ 段内项索引在重建项表上解析到同构项)。""" + src_path, ccr_path = _t3_fixture('probe') + try: + corec_out = corec_dump_ifaces(src_path, ccr_path) + arch_out = arch_dump_ifaces(ccr_path) + c = parse_iface_dump(corec_out) + a = parse_iface_dump(arch_out) + data = read_ccr(ccr_path) + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + exp_slots = sum(1 + m[1] for _, _, _, _, ms in ifaces for m in ms) + for probe, tag in ((c['probe'], 'corec'), (a['probe'], 'corearch')): + slots, built, unbuilt, bad, digest = probe + assert slots == exp_slots >= 1, \ + f"{tag}: probe slots {slots} != expected {exp_slots}" + assert built + unbuilt == slots, f"{tag}: built+unbuilt != slots" + assert bad == 0, f"{tag}: cross-segment probe bad {bad} != 0" + assert c['probe'][4] == a['probe'][4], \ + f"probe digest diverged: {c['probe']} vs {a['probe']}" + finally: + _cleanup(src_path, ccr_path) + + +def test_p4t3_loader_rejects_iface_mutations(): + """㉒ loader 负分支(跨段引用域 + 结构闸 + 尾随字节,逐个 byte mutation + → 必须拒绝,不得静默接受): + · native_count := 15(≠ 扩列硬值) + · 形状行的 term 越 TYPE 项域(term := tt+1) + · 接口方法 ret_term 越 TYPE 项域 + · 方法名的 name_ni 越 STR 串数 + · 段体尾插 4B 垃圾(五小节行走完 ≠ 段体长) + · 段体尾截 4B(计数 × 记录尺寸 > 段余量) + """ + src_path, ccr_path = _t3_fixture('mut') + try: + data = read_ccr(ccr_path) + c = CcrFile(data) + i_off, i_size = c.segs[IFACE_TAG] + natives, shapes, ifaces, impls, methods = parse_iface_segment(iface_seg(data)) + rows, terms = parse_type_segment(type_seg(data)) + names = str_table(data) + sh_off = i_off + 4 + len(natives) * IFACE_ENTRY_DISK + if_off = sh_off + 4 + len(shapes) * IFACE_SHAPE_DISK + # ③ 第一节:夹具首接口首方法记录基址 + rec = if_off + 4 + 16 + muts = {} + d = bytearray(data) + struct.pack_into('warm" + assert type_seg(dc) == type_seg(dw), "TYPE drifted cold->warm" + for tag in (2, 3, 4, 5, 6): + assert CcrFile(dc).body(tag) == CcrFile(dw).body(tag), \ + f"segment {tag} drifted cold->warm" + finally: + _cleanup(src_path, ccr_path, cold, warm) + shutil.rmtree(cache_dir, ignore_errors=True) + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +# ═══ R2 P4 Task 4 + R2 P5 Task 2:DFNode 类型面(双槽 → 单槽化)㉖..㉛ / ㉜..㊳ ═══ +# 观察通道 = `corec cir --dump-tk-terms`(hidden flag;只读、不产产物)。 +# 独立复述(防同源同错自洽假绿):本节的分类表/slot 规则按**规格文本**在 Python 侧重写, +# 不复用编译器内部表——IR 常量取 ast.cr 的公开编号,TT_* / AK_* 取引擎常量语义。 +# R2 P5 Task 2(D19)后的槽语义:dump 的 `tk` 列 = **派生码**(= 单槽化前的混用码,逐节点 +# 不变);`term` 列 = 类型项引用(-1 = 无项);`aux` 列 = 辅码(0 = 无)。互斥:term ≥ 0 +# 与 aux ≠ 0 不同时成立。 +IR_CONST = 1 # ast.cr:549 +IR_BINARY = 2 # ast.cr:550 +IR_CALL = 4 # ast.cr:552(P5 T2 入类型行面:调用点传 call_ti) +IR_ALLOC = 6 # ast.cr:554(同) +IR_LOAD = 10 # ast.cr:558(同) +IR_DEREF = 25 # ast.cr:573(tk = 访问宽度,非行号) +IR_STORE_PTR = 26 # ast.cr:574(同上) +IR_SPAWN = 27 # ast.cr:576(tk = spawn_count;-1 = 动态) +IR_BOUNDS_CHECK = 30 # ast.cr:579(tk = 0/1 动态上限旗标) +IR_HOTPATCH_ROUTE = 39 # ast.cr:587(F1 修正后 tk 恒 0 = 无面) +IR_I2F = 49 # ast.cr:596(恒 TI_DEX 行) +IR_F2I = 50 # ast.cr:597(同) +TT_ATOM = 7 # type_terms.cr 的 TT_* 标签 +AK_INT, AK_DEX, AK_STRING, AK_BOOL, AK_UNIT, AK_PTR = 0, 1, 2, 3, 4, 8 +# 类型行面(可建项)op 集 = 规格表;辅码面 op 集 = 规格表;其余 = 无面。 +MINT_OPS = {IR_CONST, IR_BINARY, IR_ALLOC, IR_CALL, IR_LOAD, IR_I2F, IR_F2I} +AUX_OPS = {IR_DEREF, IR_STORE_PTR, IR_SPAWN, IR_BOUNDS_CHECK, IR_HOTPATCH_ROUTE} + +# 复合行夹具(F2 面):BINARY 的操作数行是指针行(AK_PTR 项:b = -1 ⇒ 不可逆)—— +# 该节点的项槽必须为 -1、辅码槽 = 原行号,派生码 ≡ 行号(单槽化前该槽的值)。 +COMPOSITE_FIXTURE = """// R2 P5 Task 2 composite-row probe +fn add1(p: *int) -> int { return *p + 1; } +fn main() -> int { + arr := [10, 20, 30]; + p := &arr[1]; + q := p + 1; + if *q != 30 { return 1; } + return add1(p); +} +""" + +# #60 T2:**可缓存**夹具(无 ir_gen 期类型行分配 ⇒ `main` 有条目)——㊲ 重钉用 +# (复合行的可达性见 ㊲ 注:复合 mint 行的唯一来源 = ir_gen 分配行 ⇒ 见证下不落盘)。 +PLAIN_FIXTURE = """// R2 P5 Task 2 disk-code probe (T2 重钉:可缓存形态) +fn add(a: int, b: int) -> int { return a + b; } +fn main() -> int { + x := add(1, 2); + if x != 3 { return 1; } + return 0; +} +""" + +# 探针源(内联夹具;覆盖清单内 4 个类型行 + 三个陷阱形态): +# IR_DEREF / IR_STORE_PTR 的 tk=8(= 访问宽度,数值恰是 TI_DEX_S 行)、 +# IR_BOUNDS_CHECK 的 tk=0/1(旗标;1 数值恰是 TI_DEX 行)、 +# IR_CALL 的 tk ∈ {int,bool,str,unit}(合法行但后端不按 ti 分派)。 +TK_FIXTURE = """// R2 P4 Task 4 tk-slot probe +fn main() -> int { + arr := [10, 20, 30, 40, 50]; + p := &arr[2]; + if *p != 30 { return 1; } + q : ., mut = 42; + qp := &q; + *qp = 99; + s := "hi"; + b := true; + d := 1.5; + x := 40 + 2; + if b { x = x + 1; } + lo : ., mut = 1; + hi : ., mut = 3; + sl := arr[lo..hi]; + if sl[0] != 20 { return 2; } + if x > d { return 3; } + if s != "hi" { return 4; } + return x; +} +""" + + +def corec_cir(src: str, out: str, extra=None) -> str: + """corec cir(cwd=BASE:import 解析与缓存目录都在仓根)。""" + cmd = [COREC, 'cir', src, '-o', out] + if extra: + cmd += list(extra) + r = subprocess.run(cmd, capture_output=True, text=True, cwd=BASE, timeout=180) + assert r.returncode == 0, \ + f"corec cir failed rc={r.returncode}: {r.stdout}\n{r.stderr}" + return r.stdout + + +def parse_tk_dump(text: str): + """`--dump-tk-terms` 输出 → (header dict, rows)。行格式见 dump.cr 该函数注: + [df-tk-terms] nodes= with_term= bad_term= aux_nonzero= face_fail= terms= rows= mint=… + \\t\\t\\t\\t\\t\\t\\t\\t + rows = 9 元组 (node, op, tk, term, tag, a, b, c, aux)。""" + hdr = None + rows = [] + for line in text.splitlines(): + if line.startswith('[df-tk-terms]'): + m = re.match(r'\[df-tk-terms\] nodes=(\d+) with_term=(\d+) ' + r'bad_term=(\d+) aux_nonzero=(\d+) face_fail=(-?\d+) ' + r'terms=(\d+) rows=(\d+) mint=([0-9,]+)', line) + assert m, f"malformed tk-terms header: {line!r}" + hdr = {'nodes': int(m.group(1)), 'with_term': int(m.group(2)), + 'bad_term': int(m.group(3)), 'aux_nonzero': int(m.group(4)), + 'face_fail': int(m.group(5)), 'terms': int(m.group(6)), + 'rows': int(m.group(7)), + 'mint': {int(x) for x in m.group(8).split(',')}} + continue + if hdr is None: + continue + f = line.split('\t') + if len(f) != 9: + continue + rows.append(tuple(int(x) for x in f)) + assert hdr is not None, "no [df-tk-terms] header in output" + return hdr, rows + + +def tk_fixture(name: str, text: str = None): + """产一个探针源并返回路径。调用方负责 cleanup。""" + src_path = os.path.join(BASE, 'build', f'test_p4t4_{name}.cr') + for p in (src_path,): + try: + os.unlink(p) + except FileNotFoundError: + pass + with open(src_path, 'w') as fh: + fh.write(TK_FIXTURE if text is None else text) + return src_path + + +def _tk_dump_of(src: str, name: str): + dot = os.path.join(BASE, 'build', f'test_p4t4_{name}.cir') + out = corec_cir(src, dot, extra=['--dump-tk-terms']) + os.unlink(dot) + return parse_tk_dump(out) + + +def cir_entry_nodes(path: str): + """独立解析 .cir 快照的节点小节 → (ver, nodes, stride_ok)。 + 布局(cir_cache.cr 写侧,v17/v18 同): + magic/ver/identity/fp/sig/name_len(6×8) + name(name_len) + var_count(8) + var_count×24 | node_count(8) + node_count×64 | …""" + d = open(path, 'rb').read() + ver = struct.unpack_from(' 0, "vacuous: no node carries a term" + assert hdr['aux_nonzero'] > 0, "vacuous: no node carries an aux code" + + saw = {'const_rows': set(), 'binary_rows': set()} + traps = {'deref_w8': 0, 'store_w8': 0, 'bounds_flag1': 0, 'bounds_flag0': 0, + 'call_row': 0, 'spawn_dyn': 0} + for (n, op, tk, term, tag, a, b, c, aux) in rows: + # ── 派生码不变量(D22-①,逐节点)── + if term >= 0: + assert aux == 0, f"node {n}: term {term} and aux {aux} both set" + assert tag == TT_ATOM and b == tk, \ + f"node {n}: term {term} not ATOM(row) — tag={tag} b={b} tk={tk}" + # D21 契约的另一半:入项槽的必须是**行项**(b ≥ 0 = 行号)。复合行项 + # (AK_PTR/AK_SEQUENCE 的 b = -1)若漏进项槽,此处必红(突变 M1 实证: + # 只查 b == tk 会被「tk 也被算成 -1」自洽掩盖 ⇒ 必须显式查 b ≥ 0)。 + assert b >= 0, \ + f"node {n}: non-row item in term slot (tag={tag} b={b} term={term})" + assert tk == b, f"node {n}: derived code {tk} != row {b}" + elif aux != 0: + assert tk == aux, f"node {n}: derived code {tk} != aux {aux}" + else: + assert tk == 0, f"node {n}: empty slots but code {tk} != 0" + # ── 分类表逐面 ── + if op in MINT_OPS: + if term == -1: + assert aux == tk, f"node {n}: mint op lost its code (aux={aux})" + if op == IR_CONST: + saw['const_rows'].add((tk, a)) + elif op == IR_BINARY: + saw['binary_rows'].add((tk, a)) + else: + assert term == -1, \ + f"node {n}: non-mint op {op} got term {term}" + assert aux == tk, f"node {n}: non-mint op {op} aux {aux} != code {tk}" + if op == IR_DEREF and tk == 8: + traps['deref_w8'] += 1 + if op == IR_STORE_PTR and tk == 8: + traps['store_w8'] += 1 + if op == IR_BOUNDS_CHECK and tk == 1: + traps['bounds_flag1'] += 1 + if op == IR_BOUNDS_CHECK and tk == 0: + traps['bounds_flag0'] += 1 + if op == IR_CALL and tk in (0, 2, 3, 4): + traps['call_row'] += 1 + if op == IR_SPAWN and tk == -1: + traps['spawn_dyn'] += 1 + # 正控(类型行的项内容:a = AK_*,b = 行号) + assert (0, AK_INT) in saw['const_rows'], saw['const_rows'] + assert (2, AK_BOOL) in saw['const_rows'], saw['const_rows'] + assert (3, AK_STRING) in saw['const_rows'], saw['const_rows'] + assert (8, AK_DEX) in saw['const_rows'], \ + f"dex_s row (8) not mapped to AK_DEX: {saw['const_rows']}" + assert saw['binary_rows'], "no binary node carries a term" + # 陷阱非空:宽度 8 / 旗标 1 / 旗标 0 必须逐条在夹具里出现过(否则用例空转)。 + # IR_CALL 已入类型行面(P5 T2)⇒ 不再计入陷阱。 + assert traps['deref_w8'] >= 1 and traps['store_w8'] >= 1, traps + assert traps['bounds_flag1'] >= 1 and traps['bounds_flag0'] >= 1, traps + finally: + _cleanup(src) + + +def test_p4t4_tk_slot_dex_binary_corpus(): + """㉗ 语料面(dex_test.cr,仓内文件):IR_BINARY 的 TI_DEX 行(后端 `ti == TI_DEX` + 分派 SSE2 的那支)⇒ 项 = dex 原子;IR_CONST 的 TI_DEX/DEX_S 两行都归 AK_DEX + (同值域不同表示——ty_shadow.cr 已裁决的灰格)。""" + src = os.path.join(BASE, 'tests', 'suite', 'dex_test.cr') + hdr, rows = _tk_dump_of(src, 'dex') + assert hdr['bad_term'] == 0, f"bad_term={hdr['bad_term']}" + bin_dex = [r for r in rows if r[1] == IR_BINARY and r[2] == 1] + const_dex = [r for r in rows if r[1] == IR_CONST and r[2] == 1] + const_dex_s = [r for r in rows if r[1] == IR_CONST and r[2] == 8] + assert bin_dex, "no IR_BINARY with TI_DEX in corpus (vacuous)" + assert const_dex and const_dex_s, "no dex/dex_s const in corpus (vacuous)" + for r in bin_dex + const_dex + const_dex_s: + assert r[3] >= 0 and r[4] == TT_ATOM and r[5] == AK_DEX and r[6] == r[2], \ + f"dex row not mapped to dex atom: {r}" + + +def ccr_nod_rows(data: bytes): + """独立解析 NOD 段(**40B/条**——R2 P6 Task 3 起:32B 语义区 + 8B 邻接; + 字段序见 test_ccr_v7.py 的同名解析;此处只取 op 与 tk 两列并按 40B 步长硬断)。""" + return [(r[0], r[1]) for r in ccr_nod_records(data)] + + +def ccr_nod_records(data: bytes): + """40B/条独立解析 → `[(op, tk, item), …]`(item = +28 i32;-1 = 无项)。 + 硬断 = 步长恒等式(段体 = 4 + n×40)+ 邻接域前缀累计不变量。""" + b = CcrFile(data).body(3) + n = struct.unpack_from(' 0 and hdr_c['with_term'] > 0, hdr_c + assert hdr_c['bad_term'] == 0 and hdr_w['bad_term'] == 0, (hdr_c, hdr_w) + assert hdr_c['nodes'] == hdr_w['nodes'], (hdr_c, hdr_w) + assert hdr_c['aux_nonzero'] == hdr_w['aux_nonzero'], (hdr_c, hdr_w) + assert hdr_w['face_fail'] == 0, f"loader counted a D23 failure: {hdr_w}" + assert hdr_w['rows'] <= hdr_c['rows'], f"warm rows grew: {hdr_c} vs {hdr_w}" + missing = idx_drift = 0 + for rc_, rw_ in zip(rows_c, rows_w): + assert rc_[0:3] == rw_[0:3] and rc_[8] == rw_[8], \ + f"derived-code face drifted cold->warm: {rc_} vs {rw_}" + if rc_[3] >= 0 and rw_[3] >= 0: + assert rc_[4:8] == rw_[4:8], f"item content drifted: {rc_} vs {rw_}" + if rc_[3] != rw_[3]: + idx_drift += 1 + elif rc_[3] >= 0 and rw_[3] == -1: + assert rw_[2] >= hdr_w['rows'], \ + f"item lost for an in-range row {rw_[2]} < rows {hdr_w['rows']}: {rw_}" + missing += 1 + else: + assert rw_[3] == rc_[3], f"warm-only item: {rc_} vs {rw_}" + assert missing == hdr_c['with_term'] - hdr_w['with_term'], \ + (missing, hdr_c, hdr_w) + finally: + _cleanup(src, dot) + shutil.rmtree(cache_dir, ignore_errors=True) + + +def test_p4t4_dump_flag_zero_artifact_effect(): + """㉚ `--dump-tk-terms` 只读:带/不带 flag 两次**冷**运行的产物与 stdout(剔除 + dump 节)逐字节同(新增通道不泄入产物/输出面)。两轮之间清缓存 = 避开既有的 + 冷/热渲染差异(TODO #5 末条,非本任务面)。""" + src = tk_fixture('flag') + dot = os.path.join(BASE, 'build', 'test_p4t4_flag.cir') + cache_root = os.path.join(BASE, '.core', 'cache') + try: + shutil.rmtree(cache_root, ignore_errors=True) + out_flag = corec_cir(src, dot, extra=['--dump-tk-terms']) + dot_flag = open(dot, 'rb').read() + shutil.rmtree(cache_root, ignore_errors=True) + out_plain = corec_cir(src, dot) + dot_plain = open(dot, 'rb').read() + assert dot_flag == dot_plain, "dump flag changed the .cir artifact" + stripped = "\n".join(l for l in out_flag.splitlines() + if not l.startswith('[df-tk-terms]') + and not re.match(r'^-?\d+\t', l)) + assert stripped == "\n".join(out_plain.splitlines()), \ + "dump flag changed stdout beyond the dump section" + finally: + _cleanup(src, dot) + shutil.rmtree(cache_root, ignore_errors=True) + + +def test_p4t4_cir_snapshot_layout_unbumped(): + """㉛ `.cir` 快照布局未变(CIR_CACHE_VER 保持 17 的证据面):条目版本位 == 17; + 节点小节 = 8B 计数 + n×64B(8 字段)——即「项槽不落盘、装载侧重派生」的格式 + 事实;>0 节点保证非空转。""" + src = tk_fixture('snap') + dot = os.path.join(BASE, 'build', 'test_p4t4_snap.cir') + cache_dir = os.path.join(BASE, '.core', 'cache', 'cir') + try: + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + corec_cir(src, dot) + entries = sorted(os.listdir(cache_dir)) + assert entries, "no cir entries written" + total_nodes = 0 + for f in entries: + ver, nodes, ok = cir_entry_nodes(os.path.join(cache_dir, f)) + assert ver == 17, f"{f}: CIR_CACHE_VER {ver} != 17 (unbumped expected)" + assert ok, f"{f}: node section (64B stride) runs past EOF" + total_nodes += len(nodes) + assert total_nodes > 0, "vacuous: no nodes parsed from snapshots" + finally: + _cleanup(src, dot) + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + + +# ═══════════════ R2 P5 Task 2(D19 单槽化):㉜..㊲ ═══════════════ + +def test_p5t2_composite_row_goes_to_aux(): + """㉜ F2 面(仓内语料 ptr_ref_first.cr + 自足夹具):BINARY 的操作数行是**复合行** + (指针/引用/数组——项是 AK_PTR/AK_SEQUENCE,b 槽为 -1 等标注 ⇒ atom_of 不可逆) + ⇒ 项槽 = -1、辅码槽 = 原行号、派生码 ≡ 行号(单槽化前该槽的混用码)。 + 非空断言:该形态必须在两处语料里都出现过(ptr_ref_first 是 T0 实测的 BINARY tk=10 + 指针行;夹具自证 node=23 形态)。""" + corpus = os.path.join(BASE, 'tests', 'suite', 'ptr_ref_first.cr') + fixture = tk_fixture('comp', COMPOSITE_FIXTURE) + try: + for src, tag in ((corpus, 'prf'), (fixture, 'comp')): + hdr, rows = _tk_dump_of(src, tag) + assert hdr['bad_term'] == 0 and hdr['face_fail'] == 0, hdr + # 直接反证:项槽里**不得**出现非行项(b < 0 的结构性项 = 复合行漏进项槽)。 + # (突变 M1 实证:只查「派生码 == b」会被「tk 也变成 -1」自洽掩盖。) + for (n, op, tk, term, tagv, a, b, c, aux) in rows: + assert not (term >= 0 and tagv == TT_ATOM and b < 0), \ + f"{tag} node {n}: non-row item in term slot (term={term} b={b})" + comp = [r for r in rows if r[1] in MINT_OPS and r[3] == -1 and r[8] > 0] + assert comp, f"no composite-row node in {tag} (vacuous)" + for (n, op, tk, term, tagv, a, b, c, aux) in comp: + assert aux == tk and tk >= 0, \ + f"{tag} node {n}: composite row code lost (tk={tk} aux={aux})" + assert 0 <= tk < hdr['terms'], f"{tag} node {n}: row {tk} out of range" + finally: + _cleanup(fixture) + + +def test_p5t2_mutual_exclusion_invariant(): + """㉝ 互斥不变量(D22-②)全节点扫描:任一节点不得同时有 项 ≥ 0 与 辅 ≠ 0; + 且两槽的**占用面都必须非空**(否则是空转)——同时守住派生码逐节点一致。""" + corpus = os.path.join(BASE, 'tests', 'suite', 'ptr_ref_first.cr') + fixture = tk_fixture('mutex') + try: + for src, tag in ((corpus, 'prf'), (fixture, 'mutex')): + hdr, rows = _tk_dump_of(src, tag) + assert hdr['with_term'] > 0 and hdr['aux_nonzero'] > 0, hdr + for (n, op, tk, term, tagv, a, b, c, aux) in rows: + assert not (term >= 0 and aux != 0), \ + f"{tag} node {n}: both slots set (term={term} aux={aux})" + finally: + _cleanup(fixture) + + +def test_p5t2_f1_hotpatch_route_no_face(): + """㉞ F1 修正(仓内语料 hotpatch_test.cr):IR_HOTPATCH_ROUTE 的类型面 = 「无」 + ⇒ 显式第 6 实参 0 ⇒ 两槽皆空、派生码 0。非空断言:语料里必须真有该 op + (修复前该槽是残留寄存器值——3 调用语料实测得 4,纯属垃圾)。""" + src = os.path.join(BASE, 'tests', 'suite', 'hotpatch_test.cr') + hdr, rows = _tk_dump_of(src, 'hot') + hp = [r for r in rows if r[1] == IR_HOTPATCH_ROUTE] + assert hp, "no IR_HOTPATCH_ROUTE node in corpus (vacuous)" + for (n, op, tk, term, tagv, a, b, c, aux) in hp: + assert tk == 0 and term == -1 and aux == 0, \ + f"node {n}: hotpatch-route carries tk={tk} term={term} aux={aux}" + + +def test_p5t2_cold_warm_composite_symmetry(): + """㉟ 复合行语料的冷/暖两态:**派生码面**(op, tk, aux)逐行相同(≤ 这是发射面 + 与盘面的唯一真值面);项槽允许**单方向**缺失——暖进程类型表更小时(缓存命中跳过 + 该函数的 IR 生成,而 ir_gen 自身 `alloc_type` 新行)冷态存在的行在暖进程不存在 + ⇒ 项不可重建(term -1)。本用例把该**预存缓存面局限**钉成精确形态:任一冷/暖 + 项槽差异都必须满足「暖 = -1 ∧ 码 ≥ 暖行数」,其余面逐行严格相等。""" + cache_dir = os.path.join(BASE, '.core', 'cache') + src = tk_fixture('cwc', COMPOSITE_FIXTURE) + dot = os.path.join(BASE, 'build', 'test_p4t4_cwc.cir') + try: + shutil.rmtree(cache_dir, ignore_errors=True) + out_c = corec_cir(src, dot, extra=['--dump-tk-terms']) + hdr_c, rows_c = parse_tk_dump(out_c) + out_w = corec_cir(src, dot, extra=['--dump-tk-terms']) + hdr_w, rows_w = parse_tk_dump(out_w) + assert hdr_c['nodes'] == hdr_w['nodes'], (hdr_c, hdr_w) + assert hdr_c['bad_term'] == 0 and hdr_w['bad_term'] == 0, (hdr_c, hdr_w) + assert hdr_c['aux_nonzero'] == hdr_w['aux_nonzero'], (hdr_c, hdr_w) + assert hdr_c['mint'] == hdr_w['mint'] == MINT_OPS, (hdr_c, hdr_w) + assert hdr_w['face_fail'] == 0, \ + f"loader reported a D23 failure (should be emit-only): {hdr_w}" + assert hdr_w['rows'] <= hdr_c['rows'], f"warm rows grew: {hdr_c} vs {hdr_w}" + assert hdr_w['terms'] <= hdr_c['terms'], f"warm terms grew: {hdr_c} vs {hdr_w}" + assert hdr_c['with_term'] >= hdr_w['with_term'], (hdr_c, hdr_w) + seen_missing = 0 + for rc_, rw_ in zip(rows_c, rows_w): + assert (rc_[0], rc_[1], rc_[2], rc_[8]) == (rw_[0], rw_[1], rw_[2], rw_[8]), \ + f"derived code drifted cold->warm: {rc_} vs {rw_}" + if rc_[3] >= 0 and rw_[3] >= 0: + # 项**内容**逐节点同(索引可漂移——进程内表偏移) + assert rc_[4:8] == rw_[4:8], f"item content drifted: {rc_} vs {rw_}" + elif rc_[3] >= 0 and rw_[3] == -1: + assert rw_[2] >= hdr_w['rows'], \ + f"term lost for an in-range row {rw_[2]} < rows {hdr_w['rows']}: {rw_}" + seen_missing += 1 + else: + assert rw_[3] == rc_[3], f"warm-only item: {rc_} vs {rw_}" + # #60 T2 收口:上述「暖态丢项」局限的**成因**(命中跳过 ir_gen 而该函数 alloc_type + # 新行)已由见证规则消除(分配行的函数不再有条目 ⇒ 必重放)。本夹具实测 0 —— 钉死, + # 防回退(若再出现,说明见证被绕过或行表分叉,属 T2 面回归)。 + assert seen_missing == 0, \ + f"warm run lost {seen_missing} term(s): 行表分叉(#60 T2 见证规则应已闭合)" + comp = [r for r in rows_c if r[1] in MINT_OPS and r[3] == -1 and r[8] > 0] + assert comp, "vacuous: no composite row in cold dump" + finally: + _cleanup(src, dot) + shutil.rmtree(cache_dir, ignore_errors=True) + + +def test_p5t2_ccr_nod_composite_parity(): + """㊱ 复合行语料的 `.ccr` 面:NOD (op, tk) 与图 (op, tk) 逐位置相同——码没有被 + 项槽吞掉(派生码 ≡ 旧混用码的 .ccr 侧证据)。""" + src = tk_fixture('nodc', COMPOSITE_FIXTURE) + ccr_path = os.path.join(BASE, 'build', 'test_p4t4_nodc.ccr') + dot = os.path.join(BASE, 'build', 'test_p4t4_nodc.cir') + try: + hdr, rows = _tk_dump_of(src, 'nodc') + corec_ccr(src, ccr_path) + nod = ccr_nod_rows(read_ccr(ccr_path)) + assert len(nod) == len(rows), f"NOD rows {len(nod)} != DF nodes {len(rows)}" + for i, (nr, dr) in enumerate(zip(nod, rows)): + assert nr == (dr[1], dr[2]), f"node {i}: NOD {nr} != graph {(dr[1], dr[2])}" + assert any(r[1] in MINT_OPS and r[8] > 0 for r in rows), \ + "vacuous: no composite row in fixture" + finally: + _cleanup(src, ccr_path, dot) + + +def test_p5t2_snapshot_disk_code_preserved(): + """㊲ `.cir` 快照盘面承载**派生码**(不是项引用、不是辅码):盘上 mint 行的 (op, tk) + 必须全部落在活体 dump 的派生码集内(若盘上落的是 -1 项槽或 0,这里必红)。 + + **#60 T2 重钉(死亡证据 = 本批能力变更,非放宽判据)**:原判据对象 = **复合行** + (mint ∧ aux>0)。实核(101 档全语料扫描 + 夹具矩阵):复合 mint 行的唯一来源 = + ir_gen 期分配复合类型行的函数(ir_gen.cr:1403/1409/2584/2641/2951)⇒ 依 T2 见证 + 规则(main.cr miss 分支:分配过类型行 ⇒ 不写条目)这类函数**结构上不落盘** ⇒ 原判据 + 在见证下**可达主体为空**(原夹具 COMPOSITE_FIXTURE 实测:comp={(2,13)} 全缺)。重钉 + 为可达形态:夹具改为**可缓存**(PLAIN_FIXTURE),断言对象 = 「带码 mint 行」全集; + 复合子面待 (c)/(d)(快照承载类型行)恢复可缓存后按原判据复位 —— 见 TODO #60。 + 抑制面**不在此处静默放宽**:由紧随其后的 ㊳ 单独钉住。""" + cache_dir = os.path.join(BASE, '.core', 'cache', 'cir') + src = tk_fixture('snapc', PLAIN_FIXTURE) + dot = os.path.join(BASE, 'build', 'test_p4t4_snapc.cir') + try: + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + hdr, rows = _tk_dump_of(src, 'snapc') + corec_cir(src, dot) # 冷跑:写快照(上一步已建缓存,此处为命中/补写面) + disk = [] + for f in sorted(os.listdir(cache_dir)): + ver, nodes, ok = cir_entry_nodes(os.path.join(cache_dir, f)) + assert ver == 17 and ok, f"{f}: version/stride drifted" + disk += [(nd[0], nd[5]) for nd in nodes] + assert disk, "vacuous: no nodes parsed from snapshots" + mint = set((r[1], r[2]) for r in rows if r[1] in MINT_OPS and r[2] > 0) + assert mint, "vacuous: no coded mint row in fixture" + missing = mint - set(disk) + assert not missing, f"mint derived code lost on disk: {missing}" + finally: + _cleanup(src, dot) + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + + +def test_p5t2_row_allocating_fn_not_snapshotted(): + """㊳(#60 T2 能力变更钉)**见证规则**:ir_gen 期对「快照**不载**的共享面」(类型行表) + 有副作用的函数 ⇒ **不写快照条目** ⇒ 下跑必 miss ⇒ 重放全部生成期副作用(「宁可 miss + 不可静默」,main.cr miss 分支)。夹具 = 原 ㊲ 的 COMPOSITE_FIXTURE(数组字面量 + 取址: + ir_gen.cr:2584 / :1403 / :1409 分配复合行 ⇒ main 命中见证)。 + · ① `main` **无**条目(见证生效;修复前有 —— 原 ㊲ 正因它落盘才可达); + · ② `add1` **有**条目 ∧ 条目总数 > 1 = 抑制是**选择性**的(非整档放弃缓存); + · ③ 冷/暖两态同形:本夹具的逐行派生码/项对称由 ㉟(test_p5t2_cold_warm_composite_ + symmetry)钉住 —— 本用例只管**条目面**,不重复。""" + cache_dir = os.path.join(BASE, '.core', 'cache', 'cir') + src = tk_fixture('snapc_alloc', COMPOSITE_FIXTURE) + out1 = os.path.join(BASE, 'build', 'test_p4t4_snapc_alloc.ccr') + try: + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + corec_ccr(src, out1) + ents = sorted(e.split('::')[-1] for e in os.listdir(cache_dir)) + assert ents, "vacuous: no snapshot entry at all" + assert 'main.cir' not in ents, \ + f"row-allocating fn was snapshotted (witness rule broken): {ents}" + assert 'add1.cir' in ents, f"non-selective: add1 entry missing: {ents}" + assert len(ents) > 1, f"non-selective: single entry only: {ents}" + finally: + _cleanup(src, out1) + shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) + + +# ═══════════ R2 P6 Task 3(β):NOD 盘面项索引(+28 i32)+ 版本 9 ═══════════ +# 口径:`item` = **TYPE 段文件空间**项索引(-1 = 无项);写侧 = corec 的 +# ccr_types.cr:ccr_nod_item_populate(保存期由盘上派生码经桥接层重派生),读侧 = +# load_ccr 的 TYPE 段后**一致性硬校验**(域外/不一致 ⇒ 拒绝)+ corearch +# `--dump-nod-items` 读回通道。`tk` 槽语义/字节不变(派生码,D22-①)。 + +def _nod_items_dump(ccr_path: str): + """`corearch F.ccr --dump-nod-items` → (hdr, rows)。 + 行格式契约见 corearch.cr:dump_nod_items: + `[nod-items] nodes= with_item= terms= rows=` + `item op code idx atom ` + rows = [(node, op, code, idx, atom), …]。""" + r = subprocess.run([COREARCH, ccr_path, '--dump-nod-items'], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, \ + f"corearch --dump-nod-items rc={r.returncode}: {r.stdout!r} {r.stderr!r}" + hdr = None + rows = [] + for ln in r.stdout.splitlines(): + if ln.startswith('[nod-items]'): + m = re.match(r'\[nod-items\] nodes=(\d+) with_item=(\d+) ' + r'terms=(\d+) rows=(\d+)', ln) + assert m, f"malformed nod-items header: {ln!r}" + hdr = {'nodes': int(m.group(1)), 'with_item': int(m.group(2)), + 'terms': int(m.group(3)), 'rows': int(m.group(4))} + continue + p = ln.split() + if p and p[0] == 'item': + assert len(p) == 10 and p[2] == 'op' and p[4] == 'code' \ + and p[6] == 'idx' and p[8] == 'atom', f"malformed item line: {ln!r}" + rows.append(tuple(int(p[k]) for k in (1, 3, 5, 7, 9))) + assert hdr is not None, "nod-items header missing" + assert len(rows) == hdr['nodes'], \ + f"rows {len(rows)} != header nodes {hdr['nodes']}" + return hdr, rows + + +def test_p6t3_version_gate_rejects_v8(): + """㊳ 版本闸:v9 接受 / **v8 拒收**(D10 先例——旧格式整类拒收,不得静默)。 + 非平凡性控制:未补丁版必须 rc=0 + 出产物(否则「拒绝」可能只是畸形字节的副产品)。""" + src = tk_fixture('p6t3ver') + ccr_path = os.path.join(BASE, 'build', 'test_p6t3_ver.ccr') + bad = ccr_path + '.v8' + out = os.path.join(BASE, 'build', 'test_p6t3_ver.out') + try: + corec_ccr(src, ccr_path) + arch_elf(ccr_path, out) # v9 正控(非平凡性) + data = bytearray(read_ccr(ccr_path)) + struct.pack_into(' 0`(非空转)· ③ **非退化**:至少一节点 `idx != code`(项 DAG 序 ≠ + 行序——否则「回读通道其实是把码打印两遍」的假绿不可辨)。 + 写侧若误用 emit 期活表索引(而非保存期重派生),① 必红。""" + for src_name in ('nod', 'comp'): + src = tk_fixture('p6t3rt_' + src_name, + TK_FIXTURE if src_name == 'nod' else COMPOSITE_FIXTURE) + ccr_path = os.path.join(BASE, 'build', f'test_p6t3_rt_{src_name}.ccr') + try: + corec_ccr(src, ccr_path) + hdr, rows = _nod_items_dump(ccr_path) + assert hdr['with_item'] > 0, f"{src_name}: vacuous (no item-bearing node)" + mism = [r for r in rows if r[3] >= 0 and r[4] != r[2]] + assert not mism, f"{src_name}: {len(mism)} item/code mismatch, e.g. {mism[:3]}" + with_i = [r for r in rows if r[3] >= 0] + assert any(r[3] != r[2] for r in with_i), \ + f"{src_name}: every idx == code — read-back may be degenerate" + for (n, op, code, idx, atom) in rows: + if idx >= 0: + assert 0 <= idx < hdr['terms'], f"node {n}: idx {idx} out of term space" + assert atom == code, f"node {n}: atom {atom} != code {code}" + finally: + _cleanup(src, ccr_path) + + +def test_p6t3_items_match_memory_face(): + """㊶ 两通道面一致:`(item ≥ 0) ⟺ (内存项槽 ≥ 0)` 逐节点 + 计数相等 + (`--dump-tk-terms` 的 `term` 列 = emit 期分类结果;`item` = 保存期同判定的投影)。 + 同时锁 D22-①:**盘上 tk 列 ≡ 内存派生码列**(全节点,含辅码面/复合行)。""" + src = tk_fixture('p6t3face', COMPOSITE_FIXTURE) + ccr_path = os.path.join(BASE, 'build', 'test_p6t3_face.ccr') + dot = os.path.join(BASE, 'build', 'test_p6t3_face.cir') + try: + hdr, rows = _tk_dump_of(src, 'face') # (node, op, tk(派生码), term, tag, a, b, c, aux) + corec_ccr(src, ccr_path) + recs = ccr_nod_records(read_ccr(ccr_path)) + assert len(recs) == len(rows), f"nodes {len(recs)} != mem rows {len(rows)}" + w_mem = sum(1 for r in rows if r[3] >= 0) + nhdr, nrows = _nod_items_dump(ccr_path) + assert nhdr['with_item'] == w_mem, \ + f"with_item {nhdr['with_item']} != memory with_term {w_mem}" + for i, ((op, tk, item), r) in enumerate(zip(recs, rows)): + assert (op, tk) == (r[1], r[2]), \ + f"node {i}: NOD (op,tk)=({op},{tk}) != memory derived ({r[1]},{r[2]})" + assert (item >= 0) == (r[3] >= 0), \ + f"node {i}: item {item} vs memory term {r[3]} (face disagreement)" + finally: + _cleanup(src, ccr_path, dot) + + +def test_p6t3_aux_face_and_composite_preserved(): + """㊷ 辅码面 + 复合行保真:辅码面 op(`IR_BOUNDS_CHECK`/`IR_DEREF`/`IR_STORE_PTR`/ + `IR_SPAWN`/`IR_HOTPATCH_ROUTE`)**必须无项**(`item == -1`)且码原值;复合行 + (仓内语料 `ptr_ref_first.cr` 的 `IR_BINARY` 指针行)同(F2 裁决:不可逆行不建项)。 + 非空转:三类形态在语料里都必须真出现。""" + aux_ops = {IR_BOUNDS_CHECK, IR_DEREF, IR_STORE_PTR, IR_SPAWN, IR_HOTPATCH_ROUTE} + # 语料分工(各自覆盖不同形态;非空转 = 三类形态合计必须真出现): + # ptr_ref_first.cr = 复合行(IR_BINARY 指针行)+ hotpatch route 辅码; + # p_dex.cr = IR_STORE_PTR 宽度 + IR_BOUNDS_CHECK 旗标; + # p_spawn.cr = IR_SPAWN 计数(**含负码** —— 盘上以 u32 位型承载)。 + sources = [(os.path.join(BASE, 'tests', 'suite', 'ptr_ref_first.cr'), 'prf', 'comp'), + (os.path.join(BASE, 'tests', 'probes', 'p_dex.cr'), 'dex', 'aux'), + (os.path.join(BASE, 'tests', 'probes', 'p_spawn.cr'), 'spawn', 'aux')] + total_aux = 0 + for src, tag, kind in sources: + ccr_path = os.path.join(BASE, 'build', f'test_p6t3_aux_{tag}.ccr') + try: + corec_ccr(src, ccr_path) + recs = ccr_nod_records(read_ccr(ccr_path)) + total_aux += sum(1 for (op, _tk, _it) in recs if op in aux_ops) + if kind == 'comp': + comp = [r for r in recs if r[0] == IR_BINARY and r[2] == -1 and r[1] >= 0] + assert comp, f"{tag}: vacuous (no composite-row node)" + for (op, tk, item) in recs: + if op in aux_ops: + assert item == -1, \ + f"{tag}: aux-face op {op} carries item {item} (must be -1)" + finally: + _cleanup(ccr_path) + assert total_aux > 0, "vacuous: no aux-face node in any source" + + +def _patch_item(data: bytes, node: int, value: int) -> bytes: + """把第 node 个 NOD 记录的 `item` 字段(+28)改写为 value。""" + d = bytearray(data) + off, _ = CcrFile(bytes(d)).segs[3] + struct.pack_into('= 0] + assert with_i, "vacuous: no item-bearing node to patch" + n0 = with_i[0][0] + # ③ 找一个非原子项索引(tag != TT_ATOM 或 b < 0)——独立读 --dump-types + r = subprocess.run([COREARCH, ccr_path, '--dump-types'], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, "--dump-types rc!=0" + _rows, terms, _probe, _rt = parse_type_dump(r.stdout) + nonatom = [i for i, t in enumerate(terms) + if not (t[0] == TT_ATOM and t[2] >= 0)] + assert nonatom, "vacuous: no non-atom term in file" + cases = [('oob_count', hdr['terms']), ('neg2', -2), ('nonatom', nonatom[0])] + for tag, val in cases: + bad = f'{ccr_path}.{tag}' + _write(bad, _patch_item(data, n0, val)) + arch_elf(bad, out, must_fail=True) + os.unlink(bad) + finally: + _cleanup(src, ccr_path, out) + + +def test_p6t3_nod_count_overflow_rejected(): + """㊹ 段界检(40B 新尺寸下):`nod_count := n + 1` ⇒ 记录数超段体 ⇒ 拒绝 + (`(seg_end3-seg_off3)/ESZ_NOD_DISK` 界检)。正控 = 未补丁版 rc=0。""" + src = tk_fixture('p6t3cnt') + ccr_path = os.path.join(BASE, 'build', 'test_p6t3_cnt.ccr') + bad = ccr_path + '.cnt' + out = os.path.join(BASE, 'build', 'test_p6t3_cnt.out') + try: + corec_ccr(src, ccr_path) + arch_elf(ccr_path, out) + data = bytearray(read_ccr(ccr_path)) + off, size = CcrFile(bytes(data)).segs[3] + (n,) = struct.unpack_from(' 所属节点(数据/state 前向——DAG 拓扑不变量)。 + +幽灵边修复(Task 0 盘点注 A 裁决——播种修复)后的 EDG 内容语义: + g_df_var_producer 槽对全部 var 下标恒 -1 播种(未定值 var——全局/参数/ + 0 字面量槽——不产数据边);EDG 数据边 = 纯 def-use(定值节点 → 消费节点, + 平行边不合并);state 边(kind=1)不变。测试期望 = 表一「去幽灵后」形态 + (期望值派生:对现 v6 线性 NOD 流按 df_connect_srcs 语义重放(Python 侧 + 独立模型)+ 幽灵边去除 + state 链按现状(不受播种修复影响)——实现前 + 从当前编译器产物导出并锁定,见 test_v7_edge_content_small_program)。 + +Task 2 条目期望(Python 独立模型 + 手算对照——均按 regalloc.cr compute_entries +实现语义转写,见 test_v7_ent_* 的派生说明;loader 校验面 = 同文件内自洽: +evr ≥ 1 / ev 命名空间 / els < ele ≤ instr_cnt / def ≥ 0 → ed == els / SYM +func 块对照——reject 测试逐个 byte mutation 打)。 +Task 3 新增(2026-09-10): + · test_v7_cache_hit_restore_path——cache-hit 恢复路径回归(冷/暖五段体逐 + 字节相同 + STR 前缀关系 + corearch 行为一致 + hybrid 单函数改动产物 == + 全冷编译 byte-identical);Minor 3 注记见该测试 docstring。 + · test_v7_loader_rejects_ent_var_foreign_block(R1)/ _ent_var_namespace_oob / + _ent_def_nod_oob / _ent_def_ne_live_start(R2)——ENT 校验负分支补面。 + +Task 3 判据重定(2026-09-11 效应/纯度修正批——见文件末「Task 3 新增三」段): + 触及 state 边/纯度的改动**必然改** `.ccr` 的 NOD/EDG(Task 1/2 实测:extern/ + spawn/yield 入链 ⇒ EDG kind=1 边 +N)⇒ 旧判据「与旧版逐字节相同」退役,替代 + 判据 = 语义零变化 + 边集语义断言 + 自举稳定(连续两次编译产物一致;TODO.md)。 + 本文件承载边集语义断言三条:① 可证纯调用无 kind=1 入边(test_v7_edge_ + semantics_pure_call_untouched);② 效应调用四类(store 体/extern/不可解析 + builtin/间接 IR_DYN_DISPATCH)必有 kind=1 入边(..._effect_call_in_chain); + ③ 每函数链独立——.ccr 层零跨函数 kind=1 边 + byte mutation 咬合控制 + (test_v7_state_chain_per_function_independence)。期望表(EXPECT_DATA 41 / + EXPECT_STATE 13)经 Task 1/2/3 实测**未变**:fixture(PROBE_SRC)只含 + pure_add 调用(可证纯 ⇒ 不入链)与 store 族,无新入链 opcode ⇒ 不重锁。 +""" +import os +import shutil +import struct +import subprocess +import tempfile + +BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +COREC = os.path.join(BASE, 'build/corec') +COREARCH = os.path.join(BASE, 'build/corearch') + +MAGIC = 0x31524343 # "CCR1" +V7 = 9 # R2 P6 Task 3:v9(v7 段表架构的加法扩展,D10)——常量名/文件名从旧 + # (「v7」= 段表架构代号;改名的引用面 40+ 处、收益为零) +SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE +NOD_REC = 40 +ENT_REC = 28 +REG_REC = 24 +EDG_REC = 8 +HEADER_TABLE = 16 + 8 * 12 # 112 + +# 源程序 span 内(pure_add + main 两函数,节点 0..44)期望 EDG 内容——见 +# test_v7_edge_content_small_program 的派生说明。 +EXPECT_SRC_SPAN = 45 # 源函数节点范围 [0, 45):pure_add 0..5、main 5..45 +# 数据边(kind=0)= {from: [to...]}——纯 def-use(表一「去幽灵后」); +# 播种修复前这些集合还混有幽灵边 0→X(参数读/0 字面量槽/自环 (0,0))—— +# 断言精确集合即回归网:幽灵边回归 = 集合多出边,立即失败。 +EXPECT_DATA = { + 0: [3], 1: [2], + 5: [43], + 6: [16, 24, 27, 30, 39], + 7: [9, 11, 13, 15, 16], + 8: [9], 10: [11], 12: [13], 14: [15], + 17: [19, 23, 24, 29, 30], + 18: [19], 20: [22], 21: [22], + 24: [26, 34], 25: [26], 26: [27], + 28: [31, 33], 30: [31], + 32: [38, 40, 41, 42], + 35: [36], 36: [37], 37: [38], 39: [40], 40: [41], +} +# state 边(kind=1)= STORE 族副作用链(程序序,创建序前向)——播种修复 +# 不触及 state 链,期望 = 现状(buggy dump 的 dashed 集逐条核对一致)。 +EXPECT_STATE = { + 9: [11], 11: [13], 13: [15], 15: [16], 16: [19], 19: [22], + 22: [24], 24: [27], 27: [31], 31: [33], 33: [34], 34: [38], 38: [41], +} +assert sum(len(v) for v in EXPECT_DATA.values()) == 41, "expectation table drift" +assert sum(len(v) for v in EXPECT_STATE.values()) == 13, "expectation table drift" + + +class V7File: + """Parse a v7 .ccr file into its segments. Raises AssertionError on layout + violations (mirrors load_ccr's segment-table contract).""" + + def __init__(self, data: bytes): + self.d = data + assert len(data) >= HEADER_TABLE, f"file too small for v7 header+table: {len(data)}" + (magic, ver, seg_count, reserved) = struct.unpack_from('<4I', data, 0) + assert magic == MAGIC, f"bad magic {magic:#x}" + assert ver == V7, f"expected version {V7}, got {ver}" + assert seg_count == 8, f"expected 8 segments, got {seg_count}" + assert reserved == 0, f"reserved != 0: {reserved}" + # Segment table: canonical order, contiguous layout + self.segs = {} + cur = HEADER_TABLE # first body follows the whole table + for i, tag in enumerate(SEG_TAGS): + (t, off, size) = struct.unpack_from('<3I', data, 16 + i * 12) + assert t == tag, f"row {i}: expected tag {tag}, got {t}" + assert off == cur, f"tag {tag}: expected offset {cur}, got {off}" + assert off + size <= len(data), f"tag {tag}: body out of file" + self.segs[tag] = (off, size) + cur = off + size + assert cur == len(data), f"segments end at {cur} of {len(data)} bytes" + self.fsize = len(data) + + def body(self, tag: int): + off, size = self.segs[tag] + return self.d[off:off + size] + + # --- segment record walkers (each body starts with its count) --- + + def str_table(self): + b = self.body(1) + (n,) = struct.unpack_from(' 所属节点(前向拓扑);kind ∈ {0=数据, 1=state}。 + 返回 edges: {node: [(to, kind), ...]}(文件序)。""" + b = self.body(6) + nod = self.nod() + n = len(nod) + (edg_cnt,) = struct.unpack_from(' i, \ + f"edge {i}->{to} violates forward invariant (to_nod <= node)" + assert kind <= 1, f"edge {i}->{to}: unknown kind {kind}" + row.append((to, kind)) + if row: + edges[i] = row + run_off += ec + assert run_off == edg_cnt, \ + f"Σ edge_count {run_off} != edg_count {edg_cnt}" + assert pos == len(b), f"EDG walk ended at {pos} of {len(b)}" + return edges + + +def corec_ccr(src: str, out: str) -> str: + """Run `corec ccr` on src (unique temp path per call → cache miss → + fresh deterministic compile); returns stdout.""" + with tempfile.NamedTemporaryFile('w', suffix='.cr', delete=False) as f: + f.write(src) + path = f.name + try: + r = subprocess.run([COREC, 'ccr', path, '-o', out], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, f"corec ccr failed rc={r.returncode}: {r.stderr}" + return r.stdout + finally: + os.unlink(path) + + +def parse_object_dump(out: str): + """Parse corearch --dump-objects output (kernel object surface channel — + Task 1 语义对象模型): lines are + `objects: ` + `nod op dest s1 s2 s3 tk fe ec ` + `edge to kind ` (one per out-edge of node i, index order) + Returns (count, {i: (op, dest, s1, s2, s3, tk, fe, ec)}, {i: [(to, kind)]}).""" + lines = out.splitlines() + assert lines and lines[0].startswith('objects: '), \ + f"dump missing count header: {lines[:3]!r}" + count = int(lines[0].split()[1]) + nodes = {} + edges = {} + for ln in lines[1:]: + p = ln.split() + if not p: + continue + if p[0] == 'nod': + idx = int(p[1]) + vals = [int(p[k + 1]) for k in range(2, len(p), 2)] + assert len(vals) == 8, f"nod line fields {vals}" + nodes[idx] = tuple(vals) + elif p[0] == 'edge': + idx = int(p[1]) + edges.setdefault(idx, []).append((int(p[3]), int(p[5]))) + return count, nodes, edges + + +def read_ccr(path: str) -> bytes: + with open(path, 'rb') as fh: + return fh.read() + + +# --- tests --- + +PROBE_SRC = ( + "fn pure_add(a: int, b: int) -> int { return a + b; }\n" + "fn main() -> int {\n" + " arr : [int; 4], mut = [0, 0, 0, 0];\n" + " i : int = 1;\n" + " v : int = 9;\n" + " arr[i] = v;\n" + " arr[0] = v + 1;\n" + " x := arr[i];\n" + " s : int = pure_add(x, v);\n" + " s = s + arr[0];\n" + " return s;\n" + "}\n") + + +# --- Task 2: ENT 实记录期望(Python 独立模型 + 手算对照)--- + +# 模型 opcode 常量(规则需要判别的四个 op——与 ast.cr 编号一致) +IR_STORE = 9 +IR_STORE_INDEX_VAR = 16 +IR_STORE_PTR = 26 +IR_DYN_DISPATCH = 44 + +# Task 3 边集语义断言用 opcode(ast.cr:532/571/572) +IR_CALL = 4 +IR_CALL_EXTERN = 45 + +ENT_SRC = ( + "fn identity(n: int) -> int { return n; }\n" + "fn main() -> int {\n" + " x := 1;\n" + " x = x + 1;\n" + " x = x + 2;\n" + " return x;\n" + "}\n") + + +def model_compute_entries(nod, funcs, gcount, regs): + """v6 §4.1 条目重建规则(regalloc.cr compute_live_ranges/compute_entries + 镜像)的 Python 独立实现——.cr 侧 compute_entries_v7 无共享代码,同为 + regalloc.cr 规则文本转写,测试即交叉验证。 + + 规则(Task 0 表二逐条): + · 每函数三列引用扫描(d/s1/s2 ∈ [vs, vs+vc) 函数 var 窗口,局部坐标 + 存 [first_ref, last_ref]——不得以 df 边替代); + · 定值点 = IR_STORE 的 s1 ∈ 窗口 ∪ 其余 op(STORE_INDEX_VAR/STORE_PTR/ + DYN_DISPATCH 排除)的 dest ∈ 窗口; + · 版本切分:上一版本收口 end = min(def−1, last_ref)(dest/s1 列含定值 + 自身 → last_ref ≥ 次定值 → 恒 def−1);末版 LE = last_ref(全局坐标); + · 从未定值但有引用的 var(参数等):def=-1 条目,区间 = [first_ref, + last_ref](差异①实现语义——loader 只校验 els < ele ≤ instr_cnt); + · 全局 var(SYM 前缀 0..G-1)不在任何函数窗口 → 恒无条目(差异②); + · home/flags 恒 -1/0;版本 = 文件序同 var 组内 1-based 序数。 + 返回 (rows, blocks): + rows: [(var_id, version, def_nod, live_start, live_end_halfopen, + home, flags)](文件序) + blocks: [(start_index, count)] per func(文件序块界) + """ + defs_out = [] # entries in creation order (def-point order per func) + blocks = [] + row_no = 0 # global entry counter across funcs (== file order) + var_start = gcount + for k, f in enumerate(funcs): + root = regs[f['root_region']] + ist, ex = root[2], root[3] + ic = ex - ist + vc = f['var_count'] + vs = var_start + var_start += vc + ent0 = row_no + if ic > 0 and vc > 0: + first = [-1] * vc + last = [-1] * vc + for ii in range(ic): + inst = ist + ii + op, d, s1, s2, s3, tk, fe, ec = nod[inst] + for col in (d, s1, s2): + if vs <= col < vs + vc: + lv = col - vs + if first[lv] < 0: + first[lv] = ii + last[lv] = ii + prev = [-1] * vc # open-version entry id per local var + for ii in range(ic): + inst = ist + ii + op, d, s1, s2, s3, tk, fe, ec = nod[inst] + dv = -1 + if op == IR_STORE: + if vs <= s1 < vs + vc: + dv = s1 + elif op != IR_STORE_INDEX_VAR and op != IR_STORE_PTR and \ + op != IR_DYN_DISPATCH: + if vs <= d < vs + vc: + dv = d + if dv >= 0: + lv = dv - vs + last_global = ist + last[lv] if last[lv] >= 0 else -1 + pe = prev[lv] + if pe >= 0: + pend = inst - 1 + if last_global >= 0 and last_global < pend: + pend = last_global + # closure: overwrite LE of the still-open version + row = defs_out[pe] + defs_out[pe] = (row[0], row[1], row[2], row[3], pend, + row[5], row[6]) + defs_out.append((dv, 1, inst, inst, last_global, -1, 0)) + prev[lv] = len(defs_out) - 1 + row_no += 1 + for lv2 in range(vc): + if prev[lv2] < 0 and first[lv2] >= 0 and last[lv2] >= 0: + defs_out.append((vs + lv2, 1, -1, ist + first[lv2], + ist + last[lv2], -1, 0)) + row_no += 1 + blocks.append((ent0, row_no - ent0)) + # version = 同 var 文件序组内 1-based 序数 + seen = {} + rows = [] + for (var_id, _, dn, ls, le, ho, fl) in defs_out: + ord_ = seen.get(var_id, 0) + 1 + seen[var_id] = ord_ + rows.append((var_id, ord_, dn, ls, le + 1, ho, fl)) + return rows, blocks + + +def model_func_ranges(rows, blocks, funcs, gcount): + """从模型 rows/blocks 派生 SYM/REG 侧期望:per-func (first_ent, last_ent, + param_ents);per-func param var 全局 id 列表(前 param_count 个 decl)。""" + out = [] + vs = gcount + for k, f in enumerate(funcs): + st, cnt = blocks[k] + fe = st if cnt else -1 + le = st + cnt - 1 if cnt else -1 + pes = [] + for p in range(f['param_count']): + pvar = vs + p + pid = -1 + for i in range(st, st + cnt): + if rows[i][0] == pvar and rows[i][2] == -1: + pid = i + pes.append(pid) + out.append((fe, le, pes)) + vs += f['var_count'] + return out + + +def test_v7_ent_hand_expected_small(): + """ENT 实记录 + 字段语义手算期望(Task 2 测试载体——已知小程序 + identity/main,文件前两函数 = 节点 0..17,每函数首节点 = arena_new): + + 手算(v6 §4.1 规则 + regalloc 实现语义——差异① def=-1 条目区间 = + [first_ref, last_ref] 闭区间、盘上 live_end 半开 = +1;版本 = 组内 1-based): + + identity func0 nodes[0:4) decls ['n','_arena'](n = var 窗口首行): + n0 ARENA_NEW d=_arena;n1 RETURN s1=n;n2 ARENA_RESET s1=_arena; + n3 RETURN -1 + → _arena: def@0,refs {0(d), 2(s1)} → 版本 1 [0,2] 闭 → 盘 (0, 3) + → n: 无定值,ref@1(s1) → def=-1 条目 (ls 1, le 2) ← 差异① 参数条目 + main func1 nodes[4:17) decls ['_arena','x','int','int','bin','int','bin']: + _arena def@4 refs{4,15} → (4, 16) + x defs@5(ALLOC),7,10,13(STORE),refs{5,7,9,10,12,13,14} → 版本 1..4 + 版本 1 [5,6]→(5,7);版本 2 [7,9]→(7,10);版本 3 [10,12]→(10,13); + 版本 4 [13,14]→(13,15) + 临时(RESOLVED/BINARY dest):decl2 def@6 ref@7 → (6,8); + decl3 def@8 ref@9 → (8,10);decl4 def@9 ref@10 → (9,11); + decl5 def@11 ref@12 → (11,13);decl6 def@12 ref@13 → (12,14) + 行 = (var_decl_row, version, def_nod, live_start, live_end_半开); + home/flags 恒 -1/0。条目文件序 = 函数序块拼接:identity 块 = 文件行 0..1 + (def 升序在前,def=-1 补丁按 var 行序在后),main 块 = 行 2..11。 + """ + ccr_path = os.path.join(BASE, 'build/test_v7_ent_hand.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(ENT_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + strs = v7.str_table() + sym = v7.sym_parse() + funcs = sym['funcs'] + names = [strs[f['name']] for f in funcs] + assert names[0] == 'identity' and names[1] == 'main', names + ents = v7.ent() + # 手算表(var 以声明行序引用——与文件命名空间解耦) + exp = [ + # identity block(文件行 0..1) + (1, 1, 0, 0, 3), (0, 1, -1, 1, 2), + # main block(文件行 2..11) + (0, 1, 4, 4, 16), (1, 1, 5, 5, 7), (2, 1, 6, 6, 8), + (1, 2, 7, 7, 10), (3, 1, 8, 8, 10), (4, 1, 9, 9, 11), + (1, 3, 10, 10, 13), (5, 1, 11, 11, 13), (6, 1, 12, 12, 14), + (1, 4, 13, 13, 15), + ] + # func var 窗口起点 = globals + 前缀 var_count + vs0 = len(sym['globals']) + vs1 = vs0 + funcs[0]['var_count'] + var_of = {} + for di in range(funcs[0]['var_count']): + var_of[(0, di)] = vs0 + di + for di in range(funcs[1]['var_count']): + var_of[(1, di)] = vs1 + di + # 手算表只覆盖文件前两函数块(identity 2 + main 10 = 文件行 0..11—— + # 函数块按函数序拼接,其余函数块在其后,本测试只断言前缀 12 行) + assert len(ents) >= len(exp), \ + f"entries {len(ents)} < hand-expected {len(exp)}" + for k, (fidx, erow) in enumerate(zip([0] * 2 + [1] * 10, exp)): + evar, ever, edef, els, ele, eho, efl = ents[k] + assert evar == var_of[(fidx, erow[0])], \ + f"row {k}: var {evar} != decl-row {erow[0]} var " \ + f"{var_of[(fidx, erow[0])]}" + assert (ever, edef, els, ele) == erow[1:], \ + f"row {k} {evar}: got ({ever},{edef},{els},{ele}) " \ + f"expected {erow[1:]}" + assert eho == -1 and efl == 0, \ + f"row {k}: home/flags ({eho},{efl}) != (-1,0)" + # SYM func 记录:块界回填 = 文件行 0..1 / 2..11;param_ents 实回填 + # (identity 参数 n 的 def=-1 条目 = 文件行 1) + f0, f1 = funcs[0], funcs[1] + assert (f0['first_ent'], f0['last_ent']) == (0, 1), \ + f"identity range {f0['first_ent']}..{f0['last_ent']}" + assert f0['param_ents'] == [1], \ + f"identity param_ents {f0['param_ents']} != [1]" + assert (f1['first_ent'], f1['last_ent']) == (2, 11), \ + f"main range {f1['first_ent']}..{f1['last_ent']}" + assert f1['param_count'] == 0 + # REG 根行(SG_FUNC)first/last 与 SYM func 记录双写一致 + regs = v7.reg() + roots = [i for i, r in enumerate(regs) if r[0] == 0] + assert regs[roots[0]][4] == 0 and regs[roots[0]][5] == 1 + assert regs[roots[1]][4] == 2 and regs[roots[1]][5] == 11 + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_ent_model_replay_pure_add(): + """整文件条目 = Python 独立模型预测(模型 = §4.1/regalloc 规则转写,与 + .cr 实现无共享代码)——PROBE_SRC(pure_add 参数 def=-1 + main 全形态: + ALLOC/STORE/STORE_INDEX/STORE_INDEX_VAR carve-out 读/LAZY 族/多定值)。 + 对照面: + · ENT 段逐行 == 模型行(var_id/version/def/ls/le 半开/home/flags); + · SYM func first_ent/last_ent/param_ents == 模型块界/参数条目派生; + · REG 每行 first/last == 模型派生(根行 = 函数块含 def=-1;嵌套 = + 定值点 def ∈ [enter, exit) 的连续段,无 = -1)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_ent_model.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(PROBE_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + sym = v7.sym_parse() + funcs = sym['funcs'] + nod = v7.nod() + regs = v7.reg() + ents = v7.ent() + rows, blocks = model_compute_entries(nod, funcs, len(sym['globals']), + regs) + assert rows == ents, \ + f"ENT rows != independent model:\n file {ents[:8]}...\n model {rows[:8]}..." + # func 名对照(模型按 SYM func 序——须 = 期望序) + strs = v7.str_table() + assert strs[funcs[0]['name']] == 'pure_add' + assert strs[funcs[1]['name']] == 'main' + # SYM func 块界 + param_ents 对照 + exp_ranges = model_func_ranges(rows, blocks, funcs, + len(sym['globals'])) + for k, f in enumerate(funcs): + fe, le, pes = exp_ranges[k] + assert (f['first_ent'], f['last_ent']) == (fe, le), \ + f"func {k} ({strs[f['name']]}) first/last " \ + f"({f['first_ent']},{f['last_ent']}) != model ({fe},{le})" + assert f['param_ents'] == pes, \ + f"func {k} param_ents {f['param_ents']} != model {pes}" + # REG first/last 对照(根行 = 函数块;嵌套 = def ∈ span 连续段) + rfc = -1 + for rid, r in enumerate(regs): + kind, par, en, ex, rfe, rle = r + if kind == 0: + rfc += 1 + st, cnt = blocks[rfc] + if kind == 0: + me = st if cnt else -1 + ml = st + cnt - 1 if cnt else -1 + else: + in_span = [i for i in range(st, st + cnt) + if rows[i][2] >= en and rows[i][2] < ex] + me = min(in_span) if in_span else -1 + ml = max(in_span) if in_span else -1 + assert (rfe, rle) == (me, ml), \ + f"REG row {rid} (kind {kind}) range ({rfe},{rle}) " \ + f"!= model ({me},{ml})" + # loader 语义不变量显式断言(与 load_ccr ENT 校验同面) + instr_cnt = len(nod) + for k, (ev, evr, ed, els, ele, eho, efl) in enumerate(ents): + assert evr >= 1, f"row {k}: version {evr} < 1" + assert 0 <= ev < len(sym['globals']) + sum(fn['var_count'] + for fn in funcs), \ + f"row {k}: var_id {ev} outside namespace" + assert els < ele <= instr_cnt, \ + f"row {k}: interval [{els},{ele}) invalid vs instr_cnt" + if ed >= 0: + assert ed == els, f"row {k}: def {ed} != live_start {els}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_ent_hand_expected_pure_add_block(): + """pure_add(PROBE_SRC func0,nodes[0:5) decls ['a','b','_arena','bin']) + 条目块手算(= 差异① def=-1 参数条目形态的直接锁定): + + n0 ARENA_NEW d=_arena(decl2);n1 BINARY d=bin(decl3) s1=a s2=b; + n2 RETURN s1=bin;n3 ARENA_RESET s1=_arena;n4 RETURN -1 + → _arena def@0 refs{0,3} → 版本 1 (ls 0, le 4 半开) + → bin def@1 refs{1,2} → 版本 1 (ls 1, le 3) + → a def=-1 ref@1 → (ls 1, le 2);b def=-1 ref@1 → (ls 1, le 2) + 块 = 文件行 0..3(def 条目在前、def=-1 补丁按 var 行序 = a 先于 b); + first/last_ent = 0..3;param_ents = [2, 3](a/b 的 def=-1 条目文件行)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_ent_pure.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(PROBE_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + sym = v7.sym_parse() + funcs = sym['funcs'] + strs = v7.str_table() + assert strs[funcs[0]['name']] == 'pure_add' + ents = v7.ent() + vs0 = len(sym['globals']) + exp = [ + (vs0 + 2, 1, 0, 0, 4), (vs0 + 3, 1, 1, 1, 3), + (vs0 + 0, 1, -1, 1, 2), (vs0 + 1, 1, -1, 1, 2), + ] + got = [(ev, ever, edef, els, ele) for (ev, ever, edef, els, ele, + eho, efl) in ents[:4]] + assert got == exp, f"pure_add block:\n got {got}\n expected {exp}" + for (ev, ever, edef, els, ele, eho, efl) in ents[:4]: + assert eho == -1 and efl == 0 + f0 = funcs[0] + assert (f0['first_ent'], f0['last_ent']) == (0, 3), \ + f"pure_add range ({f0['first_ent']},{f0['last_ent']}) != (0,3)" + assert f0['param_ents'] == [2, 3], \ + f"pure_add param_ents {f0['param_ents']} != [2,3]" + # main 内多定值 var 版本切分 spot-check(手算): + # v(decl9): ALLOC@20 + STORE@22 + STORE_INDEX_VAR@24 读(carve-out) + # 值读@26/@34 → 版本1 (20,22) 版本2 (22,35) + # x(decl13): ALLOC@28 + STORE@31 → 版本1 (28,31) 版本2 (31,34) + # s(decl15): ALLOC@32 + STORE@38 + STORE@41, 末读@42 RETURN + # → 版本1 (32,38) 版本2 (38,41) 版本3 (41,43) + mvs = vs0 + funcs[0]['var_count'] # main var 窗口起点 + main = funcs[1] + # main 条目块 = SYM 实回填范围(块长 = 条目数 ≠ var_count) + block = ents[main['first_ent']:main['last_ent'] + 1] + assert len(block) >= 20, f"main block too small: {len(block)}" + by_decl = {} + for (ev, ever, edef, els, ele, eho, efl) in block: + by_decl.setdefault(ev - mvs, []).append((ever, edef, els, ele)) + assert by_decl[9] == [(1, 20, 20, 22), (2, 22, 22, 35)], \ + f"main v versions {by_decl[9]}" + assert by_decl[13] == [(1, 28, 28, 31), (2, 31, 31, 34)], \ + f"main x versions {by_decl[13]}" + assert by_decl[15] == [(1, 32, 32, 38), (2, 38, 38, 41), + (3, 41, 41, 43)], \ + f"main s versions {by_decl[15]}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def _mutate_ent_field(data, rec_idx, field_off, value, fmt=' 0, "precondition: ENT must carry real records" + ent_off, _ = v7.segs[4] + at = ent_off + 4 + rec_idx * ENT_REC + field_off + struct.pack_into(fmt, data, at, value) + return ents + + +def test_v7_loader_rejects_bad_ent_version(): + """loader ENT 实记录校验(激活面):version 字段 = 0 → 拒绝。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_entver.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + _mutate_ent_field(data, 0, 4, 0) # version := 0(evr < 1) + bad_path = ccr_path + '.v0' + with open(bad_path, 'wb') as fh: + fh.write(bytes(data)) + r = subprocess.run([COREARCH, bad_path, '--elf', '--static', + '-o', os.path.join(BASE, 'build/test_v7_entver.out')], + capture_output=True, text=True, cwd=BASE, timeout=60) + assert r.returncode != 0, \ + f"corearch accepted version-0 entry: rc={r.returncode}" + assert 'invalid' in (r.stdout + r.stderr), \ + f"expected invalid-.ccr error, got: {r.stdout!r} {r.stderr!r}" + finally: + for p in (ccr_path, ccr_path + '.v0', + os.path.join(BASE, 'build/test_v7_entver.out')): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_v7_loader_rejects_ent_liveend_oob(): + """loader ENT 区间校验:live_end(半开)越出 NOD 空间(ele > instr_cnt) + → 拒绝。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_entle.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + nod_cnt = len(v7.nod()) + _mutate_ent_field(data, 0, 16, nod_cnt + 1) # live_end := oob + bad_path = ccr_path + '.oob' + with open(bad_path, 'wb') as fh: + fh.write(bytes(data)) + r = subprocess.run([COREARCH, bad_path, '--elf', '--static', + '-o', os.path.join(BASE, 'build/test_v7_entle.out')], + capture_output=True, text=True, cwd=BASE, timeout=60) + assert r.returncode != 0, \ + f"corearch accepted live_end beyond NOD space: rc={r.returncode}" + assert 'invalid' in (r.stdout + r.stderr), \ + f"expected invalid-.ccr error, got: {r.stdout!r} {r.stderr!r}" + finally: + for p in (ccr_path, ccr_path + '.oob', + os.path.join(BASE, 'build/test_v7_entle.out')): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_v7_loader_rejects_ent_ls_ge_le(): + """loader ENT 区间校验:live_start ≥ live_end(半开区间倒置/退化)→ + 拒绝。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_entls.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + ents = _mutate_ent_field(data, 0, 16, 0) # live_end := 0 → 0 ≥ ls + bad_path = ccr_path + '.inv' + with open(bad_path, 'wb') as fh: + fh.write(bytes(data)) + r = subprocess.run([COREARCH, bad_path, '--elf', '--static', + '-o', os.path.join(BASE, 'build/test_v7_entls.out')], + capture_output=True, text=True, cwd=BASE, timeout=60) + assert r.returncode != 0, \ + f"corearch accepted inverted interval: rc={r.returncode}" + assert 'invalid' in (r.stdout + r.stderr), \ + f"expected invalid-.ccr error, got: {r.stdout!r} {r.stderr!r}" + finally: + for p in (ccr_path, ccr_path + '.inv', + os.path.join(BASE, 'build/test_v7_entls.out')): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def test_v7_loader_rejects_sym_ent_block_mismatch(): + """loader 双写对照(激活面):SYM func first_ent 与 ENT 实块界失配 → + 拒绝。byte mutation:func0 记录 first_ent 字段 +1(REG 根行对照与 + ENT 块界对照双路径同源拒绝)。""" + src = ("fn add(a: int, b: int) -> int { return a + b; }\n" + "fn main() -> int { return 42; }\n") + ccr_path = os.path.join(BASE, 'build/test_v7_entblk.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + sym = v7.sym_parse() + f0 = sym['funcs'][0] + assert f0['first_ent'] >= 0, \ + f"precondition: func0 first_ent real, got {f0['first_ent']}" + sym_off, _ = v7.segs[2] + # SYM 体:global_count(4) + globals(16B×G) + func_count(4) + func0 头 + # 24B——first_ent = 头内 +16 + g = len(sym['globals']) + at = sym_off + 4 + g * 16 + 4 + 0 * 24 + 16 + struct.pack_into(' int { return a + b; }\n" + "fn main() -> int {\n" + " s : ., mut = 0;\n" + " for i in 0..4 { s = s + i; }\n" + " return s;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_layout.ccr') + for p in (ccr_path,): + try: + os.unlink(p) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + assert v7.fsize > HEADER_TABLE + assert len(v7.str_table()) >= 2 + sym = v7.sym_parse() # validates the full SYM body layout + assert len(sym['funcs']) >= 1 and len(sym['globals']) >= 1 + n = v7.nod() + assert len(n) > 4, f"expected nodes, got {len(n)}" + assert len(v7.reg()) >= 2 + e = v7.ent() + # Task 2: ENT 实记录(corec 产条目)——函数有 var 即有条目(参数/本地 + # 引用 + _arena 定值),本程序条目数 > 0;全行字段自洽 + assert len(e) > 0, f"ENT empty — Task 2: corec should write entries: {e}" + for (ev, evr, ed, els, ele, eho, efl) in e: + assert evr >= 1 and 0 <= ev and eho == -1 and efl == 0 + assert els < ele and (ed < 0 or ed == els) + edg = v7.edg() # full EDG walk + forward/kind invariant checks + # EDG 段必落且至少一条边(每函数 arena_new→arena_reset def-use 存在) + assert sum(len(v) for v in edg.values()) >= 2, \ + f"EDG unexpectedly small: {edg}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_object_surface_recipe_readable(): + """内核语义对象模型(内核完备 Task 1)——对象面配方可读判据(设计 spec + §4.4):corearch 直载 .ccr 后经 --dump-objects 通道(对象面访问器 + nod_op/nod_dest/nod_s1-3/nod_tk + nod_edge_first/count + v7_edge_to/kind + 读内核对象缓冲)输出逐节点语义字段 + 出边遍历。断言三层: + + · 全图逐节点/逐边 == V7File 文件解析(loader 对象缓冲 = 文件语义投影 + ——语义字段与邻接域载入无损;现任务前 NOD 语义字段除 g_ir_instrs + 线性流外无任何保留——本通道 = 对象留存形态的直接证据); + · 节点出边遍历(邻接索引区间 [first, first+count) → v7_edge_to/kind) + == 文件 EDG 走查(配方输入集逐边一致——v7_edge_* 缓冲与邻接元数据 + 圆通); + · 源函数域(节点 < EXPECT_SRC_SPAN)数据边/state 边 = 表一「去幽灵后」 + 期望(EXPECT_DATA/EXPECT_STATE 复用——已知小程序期望锚)。 + + Task 4 配方查询补体(函数化收敛锚):出边遍历 = 内核 nod_inputs 薄封装 + (dump_object_surface 内联遍历收敛调用——行为零变化,上列全量输出断言 = + 收敛后封装体逐字节同的运行时证明);结构断言(本测试尾部)= fn nod_inputs + 存在 + dump 通道经 nod_inputs 出边——防悬空注复发(历史:Task 1 评审 M-1 + 曾以文档化遍历模式交付、封装推迟)。 + + 说明:dump 通道经 loader(NOD/EDG 段载入对象缓冲)→ build_linear_schedule + (调度重建移实例后 g_ir_instrs 仍须就位——dump 分支在重建后、发射前)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_objects.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(PROBE_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + nod = v7.nod() + file_edges = v7.edg() + r = subprocess.run([COREARCH, ccr_path, '--dump-objects'], + capture_output=True, text=True, cwd=BASE, + timeout=120) + assert r.returncode == 0, \ + f"corearch --dump-objects rc={r.returncode}: {r.stdout!r} {r.stderr!r}" + count, nodes, edges = parse_object_dump(r.stdout) + assert count == len(nod), \ + f"object count {count} != NOD count {len(nod)}" + assert len(nodes) == len(nod), \ + f"dumped nodes {len(nodes)} != file nodes {len(nod)}" + for i, rec in enumerate(nod): + assert i in nodes, f"node {i} missing from dump" + assert nodes[i] == rec, \ + f"node {i}: object surface {nodes[i]} != file record {rec}" + # 出边遍历 == 文件 EDG(空出边节点两侧都缺省) + for i, row in file_edges.items(): + assert edges.get(i) == row, \ + f"node {i}: traversal edges {edges.get(i)} != file EDG {row}" + assert set(edges) == set(file_edges), \ + f"traversal edge nodes {sorted(edges)} != file {sorted(file_edges)}" + # 表一复用:源函数域数据/state 边 = 去幽灵后期望 + got_data = {f: sorted(t for (t, k) in row if k == 0) + for f, row in edges.items() if f < EXPECT_SRC_SPAN} + got_state = {f: sorted(t for (t, k) in row if k == 1) + for f, row in edges.items() if f < EXPECT_SRC_SPAN} + got_data = {f: ts for f, ts in got_data.items() if ts} + got_state = {f: ts for f, ts in got_state.items() if ts} + assert got_data == EXPECT_DATA, \ + f"data edges mismatch:\n got {got_data}\n expected {EXPECT_DATA}" + assert got_state == EXPECT_STATE, \ + f"state edges mismatch:\n got {got_state}\n expected {EXPECT_STATE}" + # Task 4 配方查询补体结构锚(悬空注落实——见 docstring Task 4 节): + # 内核定义 fn nod_inputs(薄封装——查询函数落实)+ 实例通道收敛经其 + # 出边(函数化收敛)。上列运行时断言经收敛后通道 = 封装体行为证明。 + kern_src = os.path.join(BASE, 'src/lattice/ent_kernel.cr') + ktext = open(kern_src, encoding='utf-8').read() + assert 'fn nod_inputs(n: int) {' in ktext, \ + "kernel nod_inputs recipe-query wrapper missing (dangling note reverted)" + drv_src = os.path.join(BASE, 'src/compiler/corearch.cr') + dtext = open(drv_src, encoding='utf-8').read() + assert 'nod_inputs(ni)' in dtext, \ + "dump_object_surface must traverse edges via kernel nod_inputs" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_edge_content_small_program(): + """EDG 内容 = 表一「去幽灵后」形态(Task 0 注 A 裁决语义)——源程序两函数 + (pure_add/main,节点 0..44 = 文件前 45 节点——源函数先于 stdlib 模块函数 + 编译,节点编号稳定)的精确边集合: + + 期望派生(实现前锁定):对当前 v6 编译产物 NOD 线性流按 df_connect_srcs + opcode 语义 + g_df_var_producer 播种后语义(producer = 最近定值节点,未 + 定值 = -1——幽灵边缺陷修复后)重放 —— Python 侧独立模型(df 语义镜像) + 与 buggy .cir dump 交叉核对:模型边 ⊆ buggy 实发边,差集 = 幽灵边 + (全 0→X 形态:参数读/0 字面量槽/自环 (0,0))——即修复将去除的边。 + state 边(kind=1)= 现状不变(播种修复不触及 state 链机制)。 + + 断言 = 精确多重集合——幽灵边回归(如播种回退)→ 集合多出 0→X/自环边, + 立即失败。""" + ccr_path = os.path.join(BASE, 'build/test_v7_edges.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(PROBE_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + strs = v7.str_table() + sym = v7.sym_parse() + funcs = sym['funcs'] + regs = v7.reg() + # 源函数定位(SYM func 记录按声明序:pure_add = func0、main = func1) + names = [strs[f['name']] for f in funcs] + assert names[0] == 'pure_add', f"func0 = {names[0]!r}" + assert names[1] == 'main', f"func1 = {names[1]!r}" + # root region 行(kind=0)按函数序 1:1 —— span = 节点范围 + roots = [i for i, r in enumerate(regs) if r[0] == 0] + assert roots[0] == funcs[0]['root_region'] + assert roots[1] == funcs[1]['root_region'] + span_pure = (regs[roots[0]][2], regs[roots[0]][3]) # pure_add + span_main = (regs[roots[1]][2], regs[roots[1]][3]) # main + assert span_pure[1] == span_main[0] == 5 + assert span_main[1] == EXPECT_SRC_SPAN, \ + f"main span {span_main} != expected end {EXPECT_SRC_SPAN}" + assert len(v7.nod()) >= EXPECT_SRC_SPAN + + edges = v7.edg() + # 源节点域出边 = 期望集(data ∪ state 按 kind 分类) + got_data = {f: sorted(t for (t, k) in row if k == 0) + for f, row in edges.items() if f < EXPECT_SRC_SPAN} + got_state = {f: sorted(t for (t, k) in row if k == 1) + for f, row in edges.items() if f < EXPECT_SRC_SPAN} + got_data = {f: ts for f, ts in got_data.items() if ts} + got_state = {f: ts for f, ts in got_state.items() if ts} + assert got_data == EXPECT_DATA, \ + f"data edges mismatch:\n got {got_data}\n expected {EXPECT_DATA}" + assert got_state == EXPECT_STATE, \ + f"state edges mismatch:\n got {got_state}\n expected {EXPECT_STATE}" + # 域内无跨函数数据边(def-use 不出函数——全局/参数无生产者) + for f, row in edges.items(): + if f < EXPECT_SRC_SPAN: + for (t, k) in row: + if k == 0: + assert t < EXPECT_SRC_SPAN, \ + f"data edge {f}->{t} escapes source span" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_loader_rejects_version_ne_7(): + """v9-only(R2 P6 Task 3 起合法版本 = 9):version 改成 7/8/6/5(**旧 v7 六段 + 文件与 v8 文件整类拒收**——D10 的落点:不得静默当「TYPE/IFACE 缺席 = 空表」)或 + 任意非 9 → corearch 必须拒绝。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_reject.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + for bad_ver in (8, 7, 6, 5): + data = bytearray(read_ccr(ccr_path)) + struct.pack_into(' 所属节点——byte + mutation 把 node 0 唯一出边(0→3 arena def-use)的 to_nod 改成 0(自环) + → corearch 必须拒绝('invalid .ccr')。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_selfloop.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + nod = v7.nod() + assert nod[0][7] >= 1, f"node 0 has no out edges: {nod[0]}" + edg_off, _ = v7.segs[6] + # node 0 的 EDG 记录 = edg_count(4B) 后第一条:to_nod 字段 = 前 4B + struct.pack_into('= 1, f"node 0 has no out edges: {nod[0]}" + nod_off, _ = v7.segs[3] + row0_ec = nod_off + 4 + 0 * NOD_REC + 36 # edge_count field (+36 in 40B row) + struct.pack_into(' int {\n" + " s : ., mut = 0;\n" + " for i in 0..6 { s = s + i; }\n" + " return s;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_rt.ccr') + out = os.path.join(BASE, 'build/test_v7_rt') + with tempfile.NamedTemporaryFile('w', suffix='.cr', delete=False) as f: + f.write(src) + path = f.name + try: + for p in (ccr_path, out, out + '2'): + try: + os.unlink(p) + except FileNotFoundError: + pass + r = subprocess.run([COREC, 'build', path, '-o', out, '--static'], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, f"corec build failed: {r.stderr}" + os.chmod(out, 0o755) + run = subprocess.run([out], capture_output=True, text=True, timeout=10) + assert run.returncode == 15, \ + f"expected exit 15 (sum 0..5), got {run.returncode} stdout={run.stdout!r}" + assert os.path.exists(ccr_path), "corec build did not save .ccr alongside output" + data = read_ccr(ccr_path) + v7 = V7File(data) # built artifact is v7: header/segments/NOD36/EDG walk + ents = v7.ent() + # Task 2: build 链中间产物携带实条目(corearch 已 load 校验 + 发射) + assert len(ents) > 0, "built .ccr should carry real ENT records" + for (ev, evr, ed, els, ele, eho, efl) in ents: + assert evr >= 1 and eho == -1 and efl == 0 + assert els < ele and (ed < 0 or ed == els) + assert sum(len(v) for v in v7.edg().values()) >= 2 + # direct corearch invocation on the same .ccr + r = subprocess.run([COREARCH, ccr_path, '--elf', '--static', '-o', out + '2'], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, f"corearch load v7 failed: {r.stdout} {r.stderr}" + os.chmod(out + '2', 0o755) + run2 = subprocess.run([out + '2'], capture_output=True, text=True, timeout=10) + assert run2.returncode == 15, \ + f"corearch-only run expected 15, got {run2.returncode} stdout={run2.stdout!r}" + finally: + os.unlink(path) + for p in (ccr_path, out, out + '2'): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +# === Task 3(收官)迁移用例——吸收 test_ccr_v6.py 全量(v7 命名/路径); +# 删除面(version-reject / roundtrip / layout-walk)由 v7 版更强吸收,见模块 +# docstring「Task 3 吸收说明」。=== + + +def test_v7_ent_real_optmeta_absent(): + """(迁移 test_ent_real_optmeta_absent)ENT 实记录 + opt_meta 恒空面: + ENT 段携带实条目;opt_meta 仍恒 0(分配结果归位 corearch 自算,.ccr + 不传);SYM func first/last_ent 与 param_ents 实回填。Python 侧独立块 + 切分(loader 块对照同语义): + · 每函数块 = 文件序连续段(var ∈ 函数 var 窗口)——块界 == SYM + first_ent/last_ent; + · param_ents 每参数 = -1 或该参数 var 的 def=-1 条目(identity 参数 n + 未定值 → 实条目); + · REG 根行(SG_FUNC)first/last 与 SYM func 记录双写一致(实值)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_optmeta.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(ENT_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + ents = v7.ent() + assert len(ents) > 0, f"ENT empty: {ents}" + sym = v7.sym_parse() + assert sym['opt_count'] == 0, f"opt_meta not empty: {sym['opt_count']}" + funcs = sym['funcs'] + assert len(funcs) >= 2 + # Python 侧块切分(loader 块对照同语义):块序 = 函数序,块 = 连续段 + vs = len(sym['globals']) + pos = 0 + for f in funcs: + win = range(vs, vs + f['var_count']) + start = pos + while pos < len(ents) and ents[pos][0] in win: + pos += 1 + pcnt = pos - start + if pcnt == 0: + assert (f['first_ent'], f['last_ent']) == (-1, -1), f + else: + assert f['first_ent'] == start, \ + f"func first_ent {f['first_ent']} != block start {start}" + assert f['last_ent'] == start + pcnt - 1, \ + f"func last_ent {f['last_ent']} != block end {start + pcnt - 1}" + # param_ents:-1 或该参数 var 的 def=-1 条目(块内唯一) + for p in range(f['param_count']): + pvar = vs + p + pid = -1 + for i in range(start, start + pcnt): + (ev, evr, ed, els, ele, eho, efl) = ents[i] + if ev == pvar and ed == -1: + pid = i + assert f['param_ents'][p] == pid, \ + f"func param_ents[{p}] {f['param_ents'][p]} != {pid}" + vs += f['var_count'] + assert pos == len(ents), "block partition did not cover all entries" + # REG 根行 first/last 与 SYM func 记录双写一致(实值) + regs = v7.reg() + roots = [(i, r) for i, r in enumerate(regs) if r[0] == 0] + for k, (rid, r) in enumerate(roots[:len(funcs)]): + assert r[4] == funcs[k]['first_ent'], \ + f"root {rid} first_ent {r[4]} != SYM {funcs[k]['first_ent']}" + assert r[5] == funcs[k]['last_ent'], \ + f"root {rid} last_ent {r[5]} != SYM {funcs[k]['last_ent']}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_sym_reg_target_shape(): + """(迁移 test_sym_reg_target_shape)SYM 归并/REG 坐标化目标形状 + (v7 spec §3.2/§3.5 落地): + - SYM func 记录 = {name/param_count/ret_type/root_region/first_ent/last_ent} + + param_ents + var 声明区;globals 记录带 type; + - REG 记录 = {kind, parent, enter_nod, exit_nod, first_ent, last_ent}; + - 根 region(SG_FUNC)行与 func 记录 1:1,span 连续铺满 NOD 空间, + first/last = 本函数条目块;嵌套 region 条目范围 = 定值点 ∈ [enter, exit) + (文件条目序连续一段);变量命名空间行数覆盖 ENT/NOD 引用。""" + src = ("fn main() -> int {\n" + " s : ., mut = 0;\n" + " for i in 0..4 { s = s + i; }\n" + " return s;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_symreg.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + sym = v7.sym_parse() + globs = sym['globals'] + funcs = sym['funcs'] + strs = v7.str_table() + nod_cnt = len(v7.nod()) + ents = v7.ent() + regs = v7.reg() + # func 记录 + assert len(funcs) >= 1 + f = funcs[0] + assert strs[f['name']] == 'main', f"func0 is {strs[f['name']]!r}" + assert f['param_count'] == 0 + assert f['var_count'] >= f['param_count'] + decl_names = [strs[d['name']] for d in f['var_decls']] + assert len(decl_names) == f['var_count'] + # 局部变量声明随函数记录落盘(for 循环变量内部名 = for_i) + for want in ('s', 'for_i'): + assert want in decl_names, \ + f"local {want} missing from func var decls: {decl_names}" + # 全局记录:>=1 条,全局行数 = var 行序前缀(func0 var 基准) + assert len(globs) >= 1 + # REG: 根行(kind=0)与 func 1:1;span 连续铺满 NOD 空间 + roots = [(i, r) for i, r in enumerate(regs) if r[0] == 0] + assert len(roots) == len(funcs), \ + f"SG_FUNC rows {len(roots)} != func records {len(funcs)}" + prev_end = 0 + for k, (rid, r) in enumerate(roots): + assert funcs[k]['root_region'] == rid, \ + f"func {k} root_region {funcs[k]['root_region']} != row {rid}" + assert r[2] == prev_end, \ + f"root {k} enter {r[2]} != previous end {prev_end}" + prev_end = r[3] + # 根 region 条目范围 == 函数条目块(SYM func 记录同值) + assert funcs[k]['first_ent'] == r[4] and funcs[k]['last_ent'] == r[5] + assert (r[4] == -1) == (r[5] == -1) + assert prev_end == nod_cnt, \ + f"root spans end at {prev_end}, nod_count {nod_cnt}" + # 嵌套 region:first/last = def_nod ∈ [enter, exit) 的文件序连续段 + for rid, r in enumerate(regs): + if r[0] == 0: + assert r[1] == -1, f"root {rid} parent != -1: {r}" + continue + if r[1] >= 0: + p = regs[r[1]] + assert r[2] >= p[2] and r[3] <= p[3], \ + f"region {rid} escapes parent span: {r} vs {p}" + exp = {ei for ei, en in enumerate(ents) + if en[2] >= 0 and r[2] <= en[2] < r[3]} + if not exp: + assert r[4] == -1 and r[5] == -1, \ + f"empty region {rid} has range {r[4]}..{r[5]}" + else: + assert r[4] == min(exp) and r[5] == max(exp), \ + f"region {rid} range {r[4]}..{r[5]} != expected {min(exp)}..{max(exp)}" + got = set(range(r[4], r[5] + 1)) + assert got == exp, \ + f"region {rid} run not exactly the def'd-in-range set" + # 变量命名空间(globals + 各 func var_count)覆盖 ENT var id / NOD dest + total_vars = len(globs) + sum(fn['var_count'] for fn in funcs) + for en in ents: + assert 0 <= en[0] < total_vars, \ + f"ENT var_id {en[0]} outside var namespace 0..{total_vars - 1}" + for (op, dest, s1, s2, s3, tk, fe, ec) in v7.nod(): + if dest >= 0: + assert dest < total_vars, f"NOD dest {dest} outside var namespace" + assert fe >= 0 and ec >= 0 # 邻接域自洽(完整校验在 edg() 走查) + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_sym_func_shapes(): + """(迁移 test_sym_func_shapes)SYM func 记录形状:add/main 行序 = 声明序; + 参数 = var 声明区前 param_count 个(行序 = 参数序,类型 int);root_region + 行序 1:1、span 连续铺满 NOD 空间;first/last_ent 实回填(add 参数 a/b 从未 + 定值 → def=-1 条目 → param_ents = 其条目 id;main 无参;条目范围 = 块界)。""" + src = ("fn add(a: int, b: int) -> int { return a + b; }\n" + "fn main() -> int {\n" + " x := 1;\n" + " x = x + 1;\n" + " x = x + 2;\n" + " return x;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_symparams.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + sym = v7.sym_parse() + funcs = sym['funcs'] + strs = v7.str_table() + regs = v7.reg() + nod_cnt = len(v7.nod()) + ents = v7.ent() + assert strs[funcs[0]['name']] == 'add' + assert strs[funcs[1]['name']] == 'main' + # add: 参数声明 = var 声明区前 2 个(a, b, TI_INT=0) + add = funcs[0] + assert add['param_count'] == 2 + assert add['var_count'] >= 3 + pdecls = add['var_decls'][:2] + assert [strs[d['name']] for d in pdecls] == ['a', 'b'] + assert [d['type'] for d in pdecls] == [0, 0], "int param type != TI_INT" + # 条目范围/param_ents 实回填——Python 侧块切分自洽(loader 块对照同 + # 语义):add = 文件首块(func0 起点 0,含 a/b 的 def=-1 条目) + vs = len(sym['globals']) + pos = 0 + block_of = [] + for f in funcs: + win = range(vs, vs + f['var_count']) + start = pos + while pos < len(ents) and ents[pos][0] in win: + pos += 1 + block_of.append((start, pos - start)) + vs += f['var_count'] + assert pos == len(ents), "block partition did not cover all entries" + for k, f in enumerate(funcs): + st, pcnt = block_of[k] + fe = st if pcnt else -1 + le = st + pcnt - 1 if pcnt else -1 + assert (f['first_ent'], f['last_ent']) == (fe, le), \ + f"func {k} range ({f['first_ent']},{f['last_ent']}) != " \ + f"block ({fe},{le})" + # add 参数 a/b 从未定值但有引用 → def=-1 条目;param_ents 指向它们 + g0 = len(sym['globals']) + for p in range(add['param_count']): + pid = add['param_ents'][p] + assert pid >= 0, \ + f"add param_ents[{p}] == -1 (expected def=-1 entry id)" + (ev, evr, ed, els, ele, eho, efl) = ents[pid] + assert ed == -1 and ev == g0 + p, \ + f"add param_ents[{p}] -> row {pid} var {ev} (want {g0 + p})" + assert funcs[1]['param_count'] == 0 and funcs[1]['param_ents'] == [] + # 根 region(kind=0)行序 = 函数序 1:1;span 连续铺满 NOD 空间 + roots = [(i, r) for i, r in enumerate(regs) if r[0] == 0] + assert len(roots) == len(funcs) + prev_end = 0 + for k, (rid, r) in enumerate(roots): + assert funcs[k]['root_region'] == rid + assert r[2] == prev_end, f"root {k} enter {r[2]} != prev end {prev_end}" + prev_end = r[3] + # 根行 first/last = 函数块界(与 SYM 双写一致) + assert (r[4], r[5]) == (funcs[k]['first_ent'], + funcs[k]['last_ent']) + assert prev_end == nod_cnt, \ + f"root spans end at {prev_end}, nod_count {nod_cnt}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_loader_rejects_root_span_beyond_nod_space(): + """(迁移 GC-3——SYM 评审 M1)REG root span 对 NOD 空间上界校验。 + + loader 解析序 REG → NOD:root region(SG_FUNC 行)的 enter/exit 在 REG + 段解析时就回填成函数指令边界,但 instr_cnt 直到 NOD 段才可知——彼时 + 无上界校验(ENT 有 ele > instr_cnt 拒绝先例)。损坏文件把末函数 root + region 的 exit_nod 推出 NOD 空间(exit == instr_cnt + 1)→ corearch 必须 + 拒绝('invalid .ccr')。""" + src = ("fn main() -> int {\n" + " s : ., mut = 0;\n" + " for i in 0..4 { s = s + i; }\n" + " return s;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_rootspan.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + regs = v7.reg() + nod_cnt = len(v7.nod()) + # 末函数 root region(最后一个 kind==0 行):span 末 = NOD 空间末 + # (根行 span 连续铺满 NOD 空间的不变量),exit_nod += 1 → 越界 + roots = [i for i, r in enumerate(regs) if r[0] == 0] + assert roots, f"no SG_FUNC rows in REG: {regs}" + rid = roots[-1] + assert regs[rid][3] == nod_cnt, \ + f"last root exit {regs[rid][3]} != nod_count {nod_cnt}: {regs[rid]}" + reg_off, _ = v7.segs[5] + patch_at = reg_off + 4 + rid * REG_REC + 12 # exit_nod field (+12 in row) + struct.pack_into(' int { return a + b; }\n" + "fn main() -> int {\n" + " x := 1;\n" + " return x + 1;\n" + "}\n") + ccr_path = os.path.join(BASE, 'build/test_v7_varshift.ccr') + with tempfile.NamedTemporaryFile('w', suffix='.cr', delete=False) as f: + f.write(src) + path = f.name + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + r = subprocess.run([COREC, 'ccr', path, '-o', ccr_path, + '--inject-var-shift'], + capture_output=True, text=True, cwd=BASE, timeout=120) + out = r.stdout + r.stderr + assert r.returncode != 0, \ + f"save accepted misaligned var block (rc=0):\n{out}" + assert 'inject-var-shift: precondition' not in out, \ + f"hook precondition failed — test not exercising the guard:\n{out}" + assert not os.path.exists(ccr_path), \ + "save wrote a .ccr file despite rejecting" + finally: + os.unlink(path) + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +# === Task 3 新增一:ENT 负分支补面(Task 2 review R1/R2——loader 校验路径 +# 从未被现测试击中的分支逐个 byte mutation 打)=== + + +def test_v7_loader_rejects_ent_var_foreign_block(): + """Task 2 review R1——ENT↔SYM 块重推导 reject 面(ccr_io.cr :1469-1478)。 + + 现存 sym_ent_block_mismatch 只打 SYM func first_ent——REG↔SYM 对照 + (:1331-1337)先于 ENT 解析触发,块重推导路径(:1452-1478)恒空走不到。 + 本 mutation:func0(add)块首条目的 var_id 改写进 func1(main)var 窗口 + 起点 → 逐条语义校验(ev 命名空间 :1431/区间 :1432/def 关系 :1433-1436) + 全过(只动 var_id 一字段)→ 块重推导见 func0 首条 var 不在其窗口 → + pcnt=0 与 SYM first_ent 实值失配 → 拒绝。命中点唯一 = :1471-1478。""" + src = ("fn add(a: int, b: int) -> int { return a + b; }\n" + "fn main() -> int { return 42; }\n") + ccr_path = os.path.join(BASE, 'build/test_v7_entblk2.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + sym = v7.sym_parse() + funcs = sym['funcs'] + ents = v7.ent() + assert funcs[0]['first_ent'] == 0, \ + f"precondition: func0 first entry at 0, got {funcs[0]['first_ent']}" + vs1 = len(sym['globals']) + funcs[0]['var_count'] # func1 窗口起点 + assert ents[0][0] < vs1, \ + f"precondition: entry0 var {ents[0][0]} already outside func0 window" + _mutate_ent_field(data, 0, 0, vs1, '= 0: + return i + raise AssertionError("no def entry found (def_nod >= 0)") + + +def test_v7_loader_rejects_ent_def_nod_oob(): + """Task 2 review R2——ENT def_nod 越界(ccr_io.cr :1434):定值条目的 + def_nod 改写为 instr_cnt(= NOD 空间外第一个坐标)→ 拒绝。""" + src = "fn main() -> int { return 42; }\n" + ccr_path = os.path.join(BASE, 'build/test_v7_entdef.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(src, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + nod_cnt = len(v7.nod()) + idx = _first_def_entry(v7.ent()) # 定值条目(ed >= 0 才走 :1433-1436) + _mutate_ent_field(data, idx, 8, nod_cnt, ' int { return x * 2; }\n" + "fn main() -> int {\n" + " s : ., mut = 0;\n" + " for i in 0..4 { s = s + mul2(i); }\n" + " return s;\n" # Σ mul2(0..3) = 0+2+4+6 = 12 + "}\n") + src2 = src1.replace('0..4', '0..6') # Σ mul2(0..5) = 30 + + def ccr_run(out: str) -> None: + r = subprocess.run([COREC, 'ccr', src_path, '-o', out], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, f"corec ccr failed rc={r.returncode}: {r.stderr}" + + def arch_elf(ccr_in: str, elf_out: str) -> None: + r = subprocess.run([COREARCH, ccr_in, '--elf', '--static', '-o', elf_out], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, \ + f"corearch load failed: {r.stdout!r} {r.stderr!r}" + os.chmod(elf_out, 0o755) + + try: + shutil.rmtree(cache_dir, ignore_errors=True) + with open(src_path, 'w') as fh: + fh.write(src1) + ccr_run(out_cold) # 冷:全编译 + 播种缓存 + cold = read_ccr(out_cold) + # 命中证据:暖运行不重写缓存文件(miss → 重编译 → save → mtime 变) + snap = {os.path.join(cir_dir, f): os.stat( + os.path.join(cir_dir, f)).st_mtime_ns + for f in os.listdir(cir_dir)} + assert snap, "cold run did not populate cir cache" + ccr_run(out_warm) # 暖:全函数恢复路径 + for p, ns in snap.items(): + assert os.stat(p).st_mtime_ns == ns, \ + f"warm run rewrote cache file {p} (not a full hit?)" + warm = read_ccr(out_warm) + vc = V7File(cold) + vw = V7File(warm) + # 核心锁:恢复路径下 SYM/NOD/ENT/REG/EDG + TYPE/IFACE(R2 P4 Task 1 + # 空壳)七段体逐字节相同 + for tag in (2, 3, 4, 5, 6, 7, 8): + assert vc.body(tag) == vw.body(tag), \ + f"segment {tag} drifted on cache-hit restore" + # STR:暖表 = 冷表前缀(冷多出尾部 = 无引用内名二次 interning) + sc, sw = vc.str_table(), vw.str_table() + assert len(sw) <= len(sc), \ + f"warm STR {len(sw)} > cold STR {len(sc)} (restore bloat?)" + assert sc[:len(sw)] == sw, \ + "warm STR not a prefix of cold STR" + # corearch 行为一致:冷/暖双载 + 运行同退出码 + arch_elf(out_cold, elf_cold) + arch_elf(out_warm, elf_warm) + for p in (elf_cold, elf_warm): + r = subprocess.run([p], capture_output=True, timeout=10) + assert r.returncode == 12, \ + f"{p} exit {r.returncode} != 12 (cold/warm divergence)" + # hybrid:单函数改动(main 体 + 循环上界)→ 再编译(部分/全量命中 + # 视指纹失效粒度——产物断言不依赖)→ 产物 == 清缓存冷编译全文件 + with open(src_path, 'w') as fh: + fh.write(src2) + ccr_run(out_hyb) + shutil.rmtree(cache_dir, ignore_errors=True) + ccr_run(out_cold2) + assert read_ccr(out_hyb) == read_ccr(out_cold2), \ + "hybrid cache output != full-cold compile (stale graph/STR drift?)" + arch_elf(out_hyb, elf_hyb) + arch_elf(out_cold2, elf_cold2) + for p in (elf_hyb, elf_cold2): + r = subprocess.run([p], capture_output=True, timeout=10) + assert r.returncode == 30, \ + f"{p} exit {r.returncode} != 30" + finally: + for p in artifacts: + try: + os.unlink(p) + except FileNotFoundError: + pass + shutil.rmtree(cache_dir, ignore_errors=True) + + +# === Task 3 新增三:判据重定——边集语义断言(取代「.ccr 逐字节同旧版」)=== +# 时点(2026-09-11 效应/纯度修正 Task 3):`.ccr` 的 NOD/EDG 对任何触及 state 边/ +# 纯度的改动**预期会变**(Task 1/2 实测:extern/spawn/yield 由不入链改为入链 ⇒ +# EDG kind=1 边 +N)——旧判据「与旧版逐字节相同」不再适用。替代判据 =「语义零 +# 变化 + 边集语义断言 + 自举稳定(连续两次编译产物一致)」(见 TODO.md 判据条目)。 +# +# 本段三条性质各自带正/负控,且负控 = 同一 fixture 内的**真数据对照**(谓词必须 +# 在同一份产物上取反过,防「恒真/恒假」的退化实现假绿): +# ① 可证纯调用 ⇒ 无 kind=1 入边(负控 = 同文件 effect 调用必有入边); +# ② 效应调用(store 体 / extern / 不可解析 builtin / 间接调用)⇒ 必有 kind=1 +# 入边(负控 = 同文件 pure_add 调用无入边); +# ③ 每函数链独立(跨函数不连)—— .ccr 层(本判据的产物面);负控 = byte +# mutation(把 main 内一条 kind=1 边的 to_nod 改写到后一函数区间)后同一 +# 判据函数必须报出该违规(咬合证明)。 +# 期望值全部来自实现后实测(2026-09-11 探针:pure_add / id[int] 调用 state_in=[]、 +# effect/ext_probe/load64/dyn_dispatch 四类 state_in=[前驱]、全文件 329 条 kind=1 +# 边跨函数 = 0、15 个函数各自携带 kind=1 边 ⇒ ③ 非空非退化)。 + +CHAIN_SRC = ( + "fn pure_add(a: int, b: int) -> int { return a + b; }\n" + "fn id[T](x: T) -> T { return x; }\n" + "fn effect() { a : ., mut = 0; a = 1; a = 2; }\n" + "extern fn ext_probe(x: int) -> int;\n" + "struct Counter { n: int }\n" + "impl Counter {\n" + " fn bump(self) { }\n" # dyn 方法(间接调用载体) + "}\n" + "fn main() -> int {\n" + " s : ., mut = 0;\n" + " s = 1;\n" + " p := pure_add(s, 1);\n" # 可证纯调用(性质①载体) + " s = p;\n" + " q := id(s);\n" # 泛型纯调用(Task 1 前 = 意外入链的 RED 载体) + " s = s + q;\n" + " effect();\n" # store 体效应调用(②-a) + " r := ext_probe(s);\n" # extern(②-b) + " s = s + r;\n" + " b := load64(s);\n" # 不可解析 runtime builtin(②-c) + " s = s + b;\n" + " c : Counter = Counter { n: 7 };\n" + " d : dyn = c;\n" + " d.bump();\n" # 间接调用(②-d) + " s = s + 1;\n" + " return s;\n" + "}\n") + + +def _op_nodes(nod, opcode, lo=0, hi=-1): + """[lo, hi) 区间内该 opcode 的节点序号表(文件序)。""" + if hi < 0: + hi = len(nod) + return [i for i in range(lo, min(hi, len(nod))) if nod[i][0] == opcode] + + +def _cname(strs, ni): + """NOD 字段里的名字下标 → 字符串(越界 = None,防名字表外静默相等)。""" + return strs[ni] if 0 <= ni < len(strs) else None + + +def _func_span(v7, name): + """按名取函数节点区间 (lo, hi)(SYM func 记录 root_region → REG 根行 span)。""" + strs = v7.str_table() + sym = v7.sym_parse() + regs = v7.reg() + for f in sym['funcs']: + if strs[f['name']] == name: + r = regs[f['root_region']] + assert r[0] == 0, f"root_region row {f['root_region']} not SG_FUNC: {r}" + return (r[2], r[3]) + raise AssertionError(f"function {name!r} not found in SYM") + + +def _state_in(edges): + """{to: [from, ...]}——全文件 kind=1 入边索引(文件序)。""" + out = {} + for f, row in edges.items(): + for (t, k) in row: + if k == 1: + out.setdefault(t, []).append(f) + return out + + +def _reg_spans(v7): + """REG 根行(SG_FUNC)节点区间表 [enter, exit)(函数序 = SYM func 序)。""" + return [(r[2], r[3]) for r in v7.reg() if r[0] == 0] + + +def _func_of(spans, n): + """节点序号 → 函数序号(-1 = 不在任何函数区间)。""" + for k, (en, ex) in enumerate(spans): + if en <= n < ex: + return k + return -1 + + +def _chain_cross_violations(edges, spans): + """性质③判据函数:kind=1 边两端不同属一个函数区间(或落在区间外)的违规表 + [(from, to), ...](文件序)。空表 = 每函数链独立。""" + out = [] + for f in sorted(edges): + for (t, k) in edges[f]: + if k != 1: + continue + a, b = _func_of(spans, f), _func_of(spans, t) + if a < 0 or b < 0 or a != b: + out.append((f, t)) + return out + + +def test_v7_edge_semantics_pure_call_untouched(): + """性质① 可证纯调用 ⇒ **无** kind=1 入边(判据重定——取代「.ccr 字节同旧」)。 + + 正控:CHAIN_SRC 的两个可证纯调用——纯函数调用(pure_add)与**泛型纯实例**调用 + (id[int],Task 1 前 monomorph 未写 fi_ispure ⇒ 读到 0 ⇒ 意外入链的真实 RED + 载体)——必须存在且不被任何 kind=1 边指入。负控(同文件真数据对照):同一份 + 产物的 effect 调用节点必须被指入——若实现退化成「一律不入链」,负控挂;退化 + 成「一律入链」,正控挂。两断合起来才把「按真纯度入链」钉死(谓词在同一数据 + 上取过反)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_sem_pure.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(CHAIN_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + strs = v7.str_table() + nod = v7.nod() + spins = _state_in(v7.edg()) + lo, hi = _func_span(v7, 'main') + pure = [] + for callee in ('pure_add', 'id[int]'): + ids = [i for i in _op_nodes(nod, IR_CALL, lo, hi) + if _cname(strs, nod[i][4]) == callee] + assert ids, f"non-empty guard: no emitted call node for {callee}" + pure += ids + for i in pure: + assert i not in spins, \ + f"provably-pure call node {i} pierced by state chain " \ + f"(in-edges {spins.get(i)})" + # 负控:同文件 effect 调用必须有入边(谓词非恒真) + eff = [i for i in _op_nodes(nod, IR_CALL, lo, hi) + if _cname(strs, nod[i][4]) == 'effect'] + assert eff and all(i in spins for i in eff), \ + f"negative control failed: effect call must be chained — " \ + f"got {[(i, spins.get(i)) for i in eff]}" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_edge_semantics_effect_call_in_chain(): + """性质② 效应调用 ⇒ **必有** kind=1 入边(四类载体:store 体效应调用 / + extern / 不可解析 runtime builtin / 间接调用 IR_DYN_DISPATCH)。 + + 正控:四类节点各自存在(非空守卫)且逐个被 kind=1 边指入。负控(同文件真数据 + 对照):同一份产物里 pure_add 调用节点无入边——谓词非恒真。**咬合**:Task 2 + 实现前该断言在 C 层实测 RED(`chain.extern_op_pierced: got 0 want 3`——extern + 调用无入链分支);同一改动面在 .ccr 层即 EDG kind=1 边缺失。""" + ccr_path = os.path.join(BASE, 'build/test_v7_sem_effect.ccr') + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(CHAIN_SRC, ccr_path) + v7 = V7File(read_ccr(ccr_path)) + strs = v7.str_table() + nod = v7.nod() + spins = _state_in(v7.edg()) + lo, hi = _func_span(v7, 'main') + cats = [ + ('store-body effect call (IR_CALL effect)', + [i for i in _op_nodes(nod, IR_CALL, lo, hi) + if _cname(strs, nod[i][4]) == 'effect']), + ('extern call (IR_CALL_EXTERN ext_probe)', + [i for i in _op_nodes(nod, IR_CALL_EXTERN, lo, hi) + if _cname(strs, nod[i][2]) == 'ext_probe']), + ('unresolvable builtin call (IR_CALL load64)', + [i for i in _op_nodes(nod, IR_CALL, lo, hi) + if _cname(strs, nod[i][4]) == 'load64']), + ('indirect call (IR_DYN_DISPATCH)', + _op_nodes(nod, IR_DYN_DISPATCH, lo, hi)), + ] + for label, ids in cats: + assert ids, f"non-empty guard: no node for {label}" + for i in ids: + assert i in spins, \ + f"{label}: node {i} not pierced by state chain (no kind=1 in-edge)" + # 负控:同文件可证纯调用无入边(谓词非恒真) + pure = [i for i in _op_nodes(nod, IR_CALL, lo, hi) + if _cname(strs, nod[i][4]) == 'pure_add'] + assert pure and not any(i in spins for i in pure), \ + "negative control failed: pure_add call must stay unchained" + finally: + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + + +def test_v7_state_chain_per_function_independence(): + """性质③ 每函数链独立(跨函数不连)——.ccr 层(本判据的产物面;链在 + df_replay_state_chain 内每函数重置链头 ⇒ 序列化 EDG 内 kind=1 边两端必同属 + 一个函数区间)。 + + 正控:全文件 kind=1 边零跨函数(判据函数返回空表)+ 非退化守卫(链边 ≥ 3 且 + ≥ 2 个函数各自携带 kind=1 边——空集/单函数上该断言恒真,不算过)。 + 负控(咬合):byte mutation——取 main 内一条 kind=1 边的 EDG 记录,把 to_nod + 改写到后一函数区间(保持 to > from 的前向不变量,V7File 走查仍过)→ 同一判据 + 函数必须报出恰好该条跨函数边([] → [(from, to')],谓词在同一产物字节上取反)。""" + ccr_path = os.path.join(BASE, 'build/test_v7_sem_chain.ccr') + bad_path = ccr_path + '.xfn' + try: + os.unlink(ccr_path) + except FileNotFoundError: + pass + try: + corec_ccr(CHAIN_SRC, ccr_path) + data = bytearray(read_ccr(ccr_path)) + v7 = V7File(bytes(data)) + nod = v7.nod() + edges = v7.edg() + spans = _reg_spans(v7) + assert len(spans) >= 2, f"need >= 2 functions, got {spans}" + # 非退化守卫:链边非空 + ≥ 2 个函数各自携带 kind=1 边 + chain_edges = [(f, t) for f in sorted(edges) + for (t, k) in edges[f] if k == 1] + assert len(chain_edges) >= 3, \ + f"degenerate guard: only {len(chain_edges)} chain edges" + fns = {_func_of(spans, f) for (f, _) in chain_edges} + assert len(fns) >= 2, \ + f"degenerate guard: chain edges confined to functions {sorted(fns)}" + # 正控:零跨函数 + assert _chain_cross_violations(edges, spans) == [], \ + f"cross-function chain edges: {_chain_cross_violations(edges, spans)}" + # 负控(咬合):把 main 内一条 kind=1 边改写到后一函数区间 + main_k = _func_of(spans, _func_span(v7, 'main')[0]) + victim = None + for f in sorted(edges): + if _func_of(spans, f) != main_k: + continue + for (t, k) in edges[f]: + if k == 1 and _func_of(spans, t) == main_k: + victim = (f, t) + break + if victim: + break + assert victim, "precondition: no main-internal kind=1 edge found" + f, t = victim + next_k = _func_of(spans, t) + 1 # 区间连续铺满 NOD 空间 ⇒ 下一函数在前方 + assert next_k < len(spans), "precondition: main is the last function span" + t_new = spans[next_k][0] + assert t_new > f, f"mutation target {t_new} violates forward invariant (f={f})" + # EDG 记录全局序 = 节点序前缀累计;定位该边(f 行内位置) + gidx = 0 + for i in range(f): + gidx += nod[i][7] + gidx += edges[f].index((t, 1)) + edg_off, _ = v7.segs[6] + struct.pack_into(' 1GiB 堆(rt.s `heap_start: .space 1024*1024*1024`) + ③ `alloc` 耗尽返回 0(`.Lalloc_oom`)⇒ `read_file` 把 0 当 string 返回 + ④ `load_cir_cache` 首行 `str_len(0)` → `load64(0, -8)` = SIGSEGV(冷态不读快照故 rc=0) +修复(`src/compiler/cir_cache.cr::cir_read_snapshot`):快照内容对装载是**瞬态**的 + ⇒ **单一复用缓冲**(峰值 = 最大单文件尺寸,而非全体累计);open 失败/空文件/短读/ + 缓冲不可得 ⇒ -1 = cache miss(三态纪律:载入失败 ⇒ 拒绝,不得静默当空表)。 + +判据(19 例;`COREC_WARM_SELF=1` 另开 2 例自源语料 ⇒ 共 21 例): + A 冷/暖判据(base 探针)7 例:rc 双 0 · 条目已落盘 · ELF 逐字节同 · + `.ccr` **段级契约**(SYM..IFACE 七段冷=暖;STR 段冷≠暖为**预存**口径—— + P4 T1 用例已钉:暖态不重放 ir_gen 临时名的 intern ⇒ STR 表更短)· + 暖态**真命中**(条目 size+mtime 不变)· 再暖确定性 + B 变体 4 例:多串 / 0 串 / 大串(4KB 字面量)/ 多函数(24 函数) + C 可选程序 2 例:两跑产物同 + 缓存关(零条目——本修复不牵动可选面) + D **累计装载复现例**(本单 RED 的廉价复现;不依赖自源语料)6 例: + 400 条目补零至 3MB(Σ=1.2GB > 1GB 堆)⇒ 暖态 rc=0 · 产物同冷态 · + 补零条目仍为命中(未被重写)· 暖态峰值 RSS < 512MB(直接钉「不累计」性质; + 堆上限若日后上调,rc 判据会真空通过而 RSS 判据仍能抓住累计回归) + E 自源暖态(真·红态语料,`COREC_WARM_SELF=1`)2 例 + +用法:`python3 tests/selfhost/test_cir_warm_path.py`(cwd 任意;缓存与 import 解析均在仓根)。 + `COREC_BIN=/path/to/corec` 换被测编译器(RED 对照用冻结基线); + `COREC_WARM_SELF=1` 追加自源语料冷/暖跑(~3 分钟,缺省关闭以保 CI 时长)。 +""" + +import hashlib +import os +import pathlib +import shutil +import struct +import subprocess +import tempfile + +BASE = pathlib.Path(__file__).resolve().parents[2] +COREC = pathlib.Path(os.environ.get("COREC_BIN", str(BASE / "build" / "corec"))) +CACHE_DIR = BASE / ".core" / "cache" / "cir" + +# ── 探针源(无 import、无泛型、无可选 ⇒ 缓存全开)────────────────────────── +BASE_PROBE = """// warm-path probe (tests/selfhost/test_cir_warm_path.py) +fn add(a: int, b: int) -> int { + return a + b; +} + +fn scale(x: int) -> int { + return x * 3; +} + +fn pick(i: int) -> string { + if i > 0 { return "alpha"; } + if i > 1 { return "beta"; } + return "gamma"; +} + +fn main() -> int { + s : ., mut = add(1, 2); + s = s + scale(4); + t := pick(0); + return s; +} +""" + +ZERO_STR_PROBE = """// warm-path probe: 无字符串常量 +fn twice(x: int) -> int { + return x * 2; +} + +fn main() -> int { + a : ., mut = 0; + i : ., mut = 1; + loop { + if i > 8 { break; } + a = a + twice(i); + i = i + 1; + } + return a; +} +""" + +BIG_STR_PROBE = """// warm-path probe: 大字符串字面量(4KB) +fn big() -> string { + s := "%s"; + return s; +} + +fn main() -> int { + t := big(); + return 42; +} +""" % ("A" * 4096) + + +def many_funcs_probe(n: int) -> str: + parts = ["// warm-path probe: %d 函数" % n] + for i in range(n): + parts.append("fn f%d(x: int) -> int { return x + %d; }" % (i, i)) + parts.append("fn main() -> int { return f0(0) + f1(1) + f2(2); }") + return "\n".join(parts) + "\n" + + +OPTIONAL_PROBE = """// warm-path probe: 可选程序(表示面启用 ⇒ .cir 缓存关闭) +fn g() -> int? { return Some(1); } + +fn main() -> int { + x : int? = 5; + y := g(); + return match x { Some(v) => v, None => 0, }; +} +""" + +PAD_FUNCS = 400 +PAD_BYTES = 3 * 1024 * 1024 # 每条目补零到 3MB ⇒ Σ = 1.2GB > 1GB 堆 +RSS_BOUND_KB = 512 * 1024 # 暖态峰值 RSS 上限(证「读缓冲不累计」) + + +def entry_key(src: pathlib.Path) -> str: + return str(src).replace("/", "_") + + +def probe_entries(src) -> list: + return sorted(CACHE_DIR.glob(f"{entry_key(pathlib.Path(src))}::*.cir")) + + +def clean_entries(src): + for p in probe_entries(src): + try: + p.unlink() + except FileNotFoundError: + pass + + +def run_corec(args, timeout=1800): + """跑被测编译器(cwd=仓根:import 解析与 .core/cache 都在仓根)。""" + r = subprocess.run([str(COREC), *args], capture_output=True, text=True, + cwd=BASE, timeout=timeout) + return r.returncode, (r.stdout or "") + (r.stderr or "") + + +def run_corec_rss(args, timeout=1800): + """同 run_corec,另回该子进程峰值 RSS(KB)。os.wait4 ⇒ 只算本进程,不受先跑子进程污染。""" + out_path = tempfile.mktemp(prefix="core_warm_rss_") + try: + with open(out_path, "w") as fh: + p = subprocess.Popen([str(COREC), *args], stdout=fh, + stderr=subprocess.STDOUT, cwd=BASE) + _pid, status, ru = os.wait4(p.pid, 0) + p.returncode = os.waitstatus_to_exitcode(status) + return p.returncode, open(out_path).read(), ru.ru_maxrss + finally: + try: + os.unlink(out_path) + except FileNotFoundError: + pass + + +def entry_stamp(p: pathlib.Path): + st = p.stat() + return (st.st_size, st.st_mtime_ns) + + +# ── `.ccr` 段级判据 ──────────────────────────────────────────────────────── +# 冷/暖**全文件**逐字节同**不成立**且**预存**(P4 T1 用例已定口径:`STR` 段 +# 冷=1811B / 暖=1703B——暖态不重放 ir_gen 临时名(`_eq0`/`bin` 等)的 intern ⇒ +# STR 表更短;其余七段逐字节同)。本套件因此钉**段级契约**:SYM/NOD/ENT/REG/ +# EDG/TYPE/IFACE 必须冷=暖,STR 只登记尺寸(真产物 = ELF,恒逐字节同)。 +SEG_NAMES = {1: "STR", 2: "SYM", 3: "NOD", 4: "ENT", 5: "REG", + 6: "EDG", 7: "TYPE", 8: "IFACE"} +CONTRACT_TAGS = (2, 3, 4, 5, 6, 7, 8) + + +def ccr_segments(path: pathlib.Path): + d = pathlib.Path(path).read_bytes() + segs = {} + for i, tag in enumerate(sorted(SEG_NAMES)): + t, off, size = struct.unpack_from("<3I", d, 16 + i * 12) + assert t == tag, f"segment table row {i}: tag {t} != {tag}" + segs[tag] = d[off:off + size] + return segs + + +def ccr_contract_equal(cold_path: pathlib.Path, warm_path: pathlib.Path): + a, b = ccr_segments(cold_path), ccr_segments(warm_path) + bad = [SEG_NAMES[t] for t in CONTRACT_TAGS if a[t] != b[t]] + detail = (f"STR {len(a[1])}B→{len(b[1])}B(预存口径,不入判据);" + + ("其余七段逐字节同" if not bad else "契约段异: " + ",".join(bad))) + return (not bad), detail + + +def sha16(data: bytes) -> str: + return hashlib.sha256(data).hexdigest()[:16] + + +def main(): + failures = [] + checks = 0 + + def check(name, cond, detail=""): + nonlocal checks + checks += 1 + print(f"[{'PASS' if cond else 'FAIL'}] {name}{(' — ' + detail) if detail else ''}") + if not cond: + failures.append(name) + + if not COREC.exists(): + print(f"[FAIL] 缺被测编译器 {COREC}(先跑 build_selfhost_native.py)") + return 1 + + tmpdir = pathlib.Path(tempfile.mkdtemp(prefix="core_warm_")) + probes = [] + + def write_probe(name: str, text: str) -> pathlib.Path: + p = tmpdir / name + p.write_text(text) + probes.append(p) + return p + + try: + # ── A:base 探针 冷/暖判据 ──────────────────────────────────────── + src_a = write_probe("base.cr", BASE_PROBE) + out_cold, out_warm = tmpdir / "base_cold", tmpdir / "base_warm" + clean_entries(src_a) + rc_c, log_c = run_corec(["build", str(src_a), "-o", str(out_cold), "--static"]) + check("A1_cold_rc0", rc_c == 0, f"rc={rc_c}") + if rc_c != 0: + print(log_c[-2000:]) + return 1 + entries_a = probe_entries(src_a) + check("A2_cold_entries_written", len(entries_a) >= 3, f"entries={len(entries_a)}") + stamp_before = {p.name: entry_stamp(p) for p in entries_a} + rc_w, log_w = run_corec(["build", str(src_a), "-o", str(out_warm), "--static"]) + check("A3_warm_rc0", rc_w == 0, f"rc={rc_w}") + if rc_w != 0: + print(log_w[-2000:]) + return 1 + check("A4_cold_warm_elf_identical", + out_cold.read_bytes() == out_warm.read_bytes(), + f"sha256[:16]={sha16(out_warm.read_bytes())}") + ok_ccr, ccr_detail = ccr_contract_equal(str(out_cold) + ".ccr", + str(out_warm) + ".ccr") + check("A5_cold_warm_ccr_contract_segments", ok_ccr, ccr_detail) + stamp_after = {p.name: entry_stamp(p) for p in probe_entries(src_a)} + check("A6_warm_is_real_hit", stamp_before == stamp_after, + "暖态条目 (size, mtime_ns) 全等 ⇒ 真命中(非空跑)") + out_warm2 = tmpdir / "base_warm2" + rc_w2, _ = run_corec(["build", str(src_a), "-o", str(out_warm2), "--static"]) + check("A7_warm_repeat_deterministic", + rc_w2 == 0 and out_warm2.read_bytes() == out_cold.read_bytes()) + + # ── B:变体(多串 / 0 串 / 大串 / 多函数)──────────────────────── + variants = [ + ("B1_multi_string", "multi_string.cr", BASE_PROBE), + ("B2_zero_string", "zero_string.cr", ZERO_STR_PROBE), + ("B3_big_string", "big_string.cr", BIG_STR_PROBE), + ("B4_many_funcs", "many_funcs.cr", many_funcs_probe(24)), + ] + for name, fname, text in variants: + src_v = write_probe(fname, text) + oc, ow = tmpdir / (fname + ".cold"), tmpdir / (fname + ".warm") + clean_entries(src_v) + rc1, l1 = run_corec(["build", str(src_v), "-o", str(oc), "--static"]) + rc2, l2 = run_corec(["build", str(src_v), "-o", str(ow), "--static"]) + same = (rc1 == 0 and rc2 == 0 and oc.read_bytes() == ow.read_bytes()) + check(name, same, f"cold rc={rc1} warm rc={rc2}") + if not same: + print((l2 if rc2 != 0 else l1)[-1500:]) + + # ── C:可选程序(表示面 ⇒ 缓存关;本修复不牵动该面)──────────── + src_c = write_probe("optional.cr", OPTIONAL_PROBE) + clean_entries(src_c) + oc, ow = tmpdir / "opt_cold", tmpdir / "opt_warm" + rc1, l1 = run_corec(["build", str(src_c), "-o", str(oc), "--static"]) + rc2, l2 = run_corec(["build", str(src_c), "-o", str(ow), "--static"]) + check("C1_optional_two_runs_identical", + rc1 == 0 and rc2 == 0 and oc.read_bytes() == ow.read_bytes(), + f"cold rc={rc1} warm rc={rc2}") + check("C2_optional_cache_off_zero_entries", len(probe_entries(src_c)) == 0, + f"entries={len(probe_entries(src_c))}(可选面 = P4 T5 关缓存,本修复不涉及)") + + # ── D:累计装载复现例(补零条目 Σ=1.2GB > 1GB 堆)──────────────── + src_d = write_probe("pad.cr", many_funcs_probe(PAD_FUNCS)) + clean_entries(src_d) + oc, ow = tmpdir / "pad_cold", tmpdir / "pad_warm" + rc_c, log_c = run_corec(["build", str(src_d), "-o", str(oc), "--static"]) + check("D1_pad_cold_rc0", rc_c == 0, f"rc={rc_c}") + if rc_c != 0: + print(log_c[-2000:]) + return 1 + entries = probe_entries(src_d) + for p in entries: # 补零到 3MB(稀疏;装载只读声明段) + with open(p, "ab") as fh: + fh.truncate(PAD_BYTES) + total = sum(p.stat().st_size for p in entries) + check("D2_pad_total_exceeds_heap", total > (1 << 30), + f"{len(entries)} 条目 Σ={total} B > 1GiB 堆") + stamp_before = {p.name: entry_stamp(p) for p in entries} + rc_w, log_w, rss = run_corec_rss(["build", str(src_d), "-o", str(ow), "--static"]) + check("D3_pad_warm_rc0", rc_w == 0, f"rc={rc_w}(修复前此处 rc=139)") + if rc_w != 0: + print(log_w[-2000:]) + return 1 + ok_ccr, ccr_detail = ccr_contract_equal(str(oc) + ".ccr", str(ow) + ".ccr") + check("D4_pad_warm_cold_identical", + oc.read_bytes() == ow.read_bytes() and ok_ccr, + f"ELF 逐字节同;{ccr_detail}") + stamp_after = {p.name: entry_stamp(p) for p in entries} + check("D5_pad_entries_still_hits", stamp_before == stamp_after, + "补零条目 (size, mtime_ns) 全等 ⇒ 暖态走命中路径(非退化为 miss 重写)") + check("D6_pad_warm_rss_bounded", 0 < rss < RSS_BOUND_KB, + f"峰值 RSS={rss} KB < {RSS_BOUND_KB} KB(钉「读缓冲不累计」;" + f"Σ 读取载荷={total // 1024} KB)") + + # ── E:自源暖态(真·红态语料;COREC_WARM_SELF=1 才跑)──────────── + if os.environ.get("COREC_WARM_SELF") == "1": + oc, ow = tmpdir / "self_cold", tmpdir / "self_warm" + run_corec(["clean-cache"]) + rc1, l1 = run_corec(["build", "src/compiler/main.cr", "-O", "0", + "-o", str(oc), "--static"]) + rc2, l2 = run_corec(["build", "src/compiler/main.cr", "-O", "0", + "-o", str(ow), "--static"]) + check("E1_self_cold_rc0", rc1 == 0, f"rc={rc1}") + check("E2_self_warm_rc0_and_identical", + rc1 == 0 and rc2 == 0 and oc.read_bytes() == ow.read_bytes(), + f"cold rc={rc1} warm rc={rc2}(修复前暖态 rc=139)") + else: + print("[SKIP] E1/E2 自源暖态(置 COREC_WARM_SELF=1 开启;~3 分钟、1.25GB 缓存)") + + print(f"{checks - len(failures)}/{checks} passed") + for f in failures: + print(f" FAILED: {f}") + return 0 if not failures else 1 + finally: + for p in probes: + clean_entries(p) + shutil.rmtree(tmpdir, ignore_errors=True) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_compile.py b/tests/selfhost/test_compile.py index 0dca0f35..60f77944 100644 --- a/tests/selfhost/test_compile.py +++ b/tests/selfhost/test_compile.py @@ -19,27 +19,68 @@ def concat_sources(): 'src/stdlib/io.cr', 'src/stdlib/fmt.cr', 'src/stdlib/cli.cr', 'src/stdlib/toml.cr', 'src/compiler/ast.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', - 'src/compiler/checker.cr', 'src/compiler/opt.cr', 'src/compiler/ptr_analysis.cr', + 'src/compiler/checker.cr', + # R2 P0 类型项引擎(建层,与 build_selfhost_native.py 的 corec 清单同位): + # 前端单元新增三文件的源码闭包条目。 + 'src/compiler/type_terms.cr', 'src/compiler/type_engine.cr', + # R2 P2b Task 1 本质条目表 + iface_* 查询 API(引擎之后、桥接层之前——与 + # build_selfhost_native.py 的 corec 清单同位) + 'src/compiler/iface_registry.cr', + # R2 P4 Task 0:iface 满足判定簇 + iface_kind_of 移入 corec-only 的 iface_axis.cr + # (清单漂移的后果 = 本测试 resolver 的 Undefined name 家族:iface_satisfies/ + # iface_kind_of/iface_find_method/…——实测由本测试捕获,与 concat 面/self-host + # 链同源;与 build_selfhost_native.py 的 corec 清单同位) + 'src/compiler/iface_axis.cr', + # R2 P1 桥接层(ti → 类型项)——与 build_selfhost_native.py 的 corec 清单同位 + 'src/compiler/ty_shadow.cr', + # R2 P4 Task 2:TYPE(7) 段内容构造(D13 装填 + 段体缓冲 + corec-only dump 节) + # ——与 build_selfhost_native.py 的 corec 清单同位(清单漂移 = 本测试 resolver + # 的 Undefined name 家族:ccr_type_prepare_save/ccr_type_populate/ + # ccr_type_selftest_dump——实测由本测试捕获,与 concat 面/self-host 链同源)。 + 'src/compiler/ccr_types.cr', + 'src/compiler/type_selftest.cr', + # 效应/纯度修正 Task 1:纯度自测通道(main.cr 的 selftest-purity 调用它)—— + # 与 build_selfhost_native.py 的 corec 清单同位(清单漂移 = 本测试的 + # resolver 报 Undefined name,见该文件头注「清单条目现全部为现存路径」)。 + 'src/compiler/purity_selftest.cr', + 'src/compiler/opt.cr', 'src/compiler/ptr_analysis.cr', 'src/compiler/region_check.cr', 'src/compiler/provenance_verify.cr', 'src/compiler/diag.cr', 'src/compiler/ext_mgr.cr', 'src/compiler/ext_safety.cr', 'src/compiler/ir_gen.cr', 'src/compiler/pass.cr', 'src/compiler/dataflow.cr', - 'src/compiler/backend/x86_64.cr', 'src/compiler/backend/x86_64/instr.cr', - 'src/compiler/module.cr', 'src/compiler/ccr_io.cr', 'src/compiler/dump.cr', + # 原三条 `src/compiler/backend/**` 条目(x86_64.cr / x86_64/instr.cr / + # resolve.cr)已随波 1 三轴搬迁不存在,继任者居 src/arch/x86_64/*、 + # src/format/elf/*、src/os/linux/*——归属 **corearch 编译单元**,不在本清单 + # (本清单 = corec 前端单元的源码闭包)。2026-09-10 R1 Task 5 实证:把三轴 + # 文件补入本单元会缺 68 个 HIT 引擎符号(src/arch/hit/* 为独立清单段, + # lower_to_core 亦缺)——即这是刻意裁剪而非遗漏,故不补入。三条死条目 + # 删除(原被 exists 静默跳过,**拼接内容零变化**);清单条目现全部为现存 + # 路径,配下方存在性断言,杜绝再次静默漂移。 + 'src/compiler/module.cr', 'src/compiler/ccr_io.cr', + 'src/lattice/ent_kernel.cr', + 'src/compiler/dump.cr', 'src/compiler/cir_cache.cr', 'src/compiler/monomorph.cr', 'src/compiler/project.cr', 'src/compiler/interp.cr', 'src/stdlib/os.cr', - 'src/compiler/backend/resolve.cr', 'src/compiler/main.cr', ] + # 存在性断言(build_selfhost_native.py guard_manifest 同款):清单条目缺失 = + # 立即失败。原实现为 `if os.path.exists(path)` 静默跳过——路径陈旧时清单会 + # 无声缺斤短两,测试仍「全绿」(2026-09-10 R1 Task 5 发现并修正)。 + missing = [f for f in files if not os.path.exists(os.path.join(BASE, f))] + if missing: + print(f"concat_sources: {len(missing)} manifest entr(y|ies) missing:") + for f in missing: + print(f" - {f}") + sys.exit(1) + parts = [] for f in files: path = os.path.join(BASE, f) - if os.path.exists(path): - with open(path) as fh: - content = fh.read().strip() - if content: - parts.append(content) + with open(path) as fh: + content = fh.read().strip() + if content: + parts.append(content) return '\n\n'.join(parts) diff --git a/tests/selfhost/test_diag_gate.py b/tests/selfhost/test_diag_gate.py new file mode 100644 index 00000000..03c822e0 --- /dev/null +++ b/tests/selfhost/test_diag_gate.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""fail-closed 判据线(FC 批 T2):闸门反转 = 默认阻断 + 豁免登记表。 + +判据面(源 = `src/compiler/diag.cr::diag_gate_exempt`;消费点 = `main.cr` 的硬判定循环): + · 正控(9):表内 6 条 build 面豁免码(TF01/TF07/TB01/TM04/TK01/B04)⇒ 仍放行(build rc=0 + 产物); + `scope=check` 3 条(N01/N06/N11)⇒ **check 面 rc=1 不变**((C) 终局:本表对 check 面惰性)。 + · 负控(6):三类面各覆盖——语法面 P21 · 类型面 TA02/R002/TM03/TK05 · 安全检查面 TU03 + ⇒ 仍阻断(rc=1 **且零产物**)。 + · 零产物(3):前端失败无半成品 · corearch 失败删**本次** `.ccr`(stub corearch 仿真)· + 既有旧哨兵(ELF/.ccr)在失败后**原样仍在**(裁-FC-6:只删本次写下者)。 + +注:安全面用例(TU03)须 `clean-cache` 后测——`.cir` 暖缓存下该诊断会消失(**预存缺陷**, +另案登记,与本套件判据无关)。用例只增不减。 +""" +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +COREC = BASE / "build" / "corec" +TMP = Path(tempfile.mkdtemp(prefix="diag_gate_")) + + +def _no_core_dump(): + import resource + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def cc(args, ccbin=None, timeout=180): + return subprocess.run([str(ccbin or COREC)] + args, cwd=BASE, capture_output=True, + text=True, timeout=timeout, preexec_fn=_no_core_dump) + + +def clean(): + cc(["clean-cache"]) + + +def write(name, src): + p = TMP / f"{name}.cr" + p.write_text(src) + return p + + +class Tally: + def __init__(self): + self.pass_ = 0 + self.fail = 0 + + def ok(self, name, cond, detail=""): + if cond: + print(f"[PASS] {name} {detail}") + self.pass_ += 1 + else: + print(f"[FAIL] {name} {detail}") + self.fail += 1 + + +t = Tally() + +# ── 正控 1-6:build 面豁免码仍放行 ────────────────────────────────────── +def build_ok(name, src_path, want_codes=()): + out = TMP / f"{name}.bin" + for f in (out, Path(str(out) + ".ccr")): + if f.exists(): + f.unlink() + clean() + r = cc(["build", str(src_path), "-o", str(out), "--static"]) + txt = r.stdout + r.stderr + okc = all(f"error[{c}]" in txt for c in want_codes) if want_codes else True + t.ok(f"pos_{name}", r.returncode == 0 and out.exists() and okc, + f"rc={r.returncode} art={out.exists()} codes={[c for c in want_codes]}") + + +build_ok("tf01", "tests/suite/chan_test.cr", ("TF01",)) +build_ok("tf07_tb01", "tests/suite/ptr_ref_first.cr", ("TF07", "TB01")) +tm04 = write("tm04", "enum Color { Red, Green, Blue }\n" + "fn main() -> int { c := Red(); " + "return match c { Red => 1, Red => 2, Green => 3, Blue => 4, }; }\n") +build_ok("tm04", tm04, ("TM04",)) +tk01 = write("tk01", "\nfn main() -> int {\n x := 5;\n return x[0];\n}\n") +build_ok("tk01", tk01, ("TK01",)) +build_ok("b04", "tests/suite/ptr_arith.cr") + +# ── 正控 7-9:scope=check 三条 check 面不变(rc=1) ───────────────────── +for nm, f in (("n01", "src/stdlib/collections.cr"), ("n06", "src/stdlib/trace.cr"), + ("n11", "src/stdlib/fmt.cr")): + clean() + r = cc(["check", f]) + t.ok(f"pos_scope_check_{nm}", r.returncode == 1, f"check rc={r.returncode}(不变)") + +# ── 负控 1-6:三类面仍阻断 + 零产物 ───────────────────────────────────── +def neg(name, src_path, want_code): + out = TMP / f"{name}.bin" + for f in (out, Path(str(out) + ".ccr")): + if f.exists(): + f.unlink() + clean() + r = cc(["build", str(src_path), "-o", str(out), "--static"]) + txt = r.stdout + r.stderr + t.ok(f"neg_{name}", + r.returncode == 1 and not out.exists() and not Path(str(out) + ".ccr").exists() + and f"error[{want_code}]" in txt, + f"rc={r.returncode} ELF={out.exists()} ccr={Path(str(out)+'.ccr').exists()}") + + +neg("syntax_p21", BASE / "tests/suite/at_test_mini4.cr", "P21") +neg("type_ta02", write("ta02", 'fn main() -> int {\n x : int = "s";\n return 0;\n}\n'), "TA02") +neg("type_r02", write("r02", "fn main() -> int {\n arr := [1, 2, 3];\n return arr[5];\n}\n"), "R02") +neg("type_tm03", write("tm03", "enum E { A, B }\nfn main() -> int { e := A(); " + "return match e { A => 1, }; }\n"), "TM03") +neg("type_tk05", write("tk05", "fn main() -> int {\n arr := [1, 2, 3];\n" + " s := arr[0..5];\n return 0;\n}\n"), "TK05") +neg("safety_tu03", write("tu03", "fn main() -> int {\n p := 4096 as *int;\n" + " return *p;\n}\n"), "TU03") + +# ── 零产物 1:前端失败(P21)⇒ 无 ELF / 无 .ccr(并入 neg_syntax_p21 断言)── +# ── 零产物 2:corearch 失败(stub)⇒ 本次 .ccr 被删 ───────────────────── +sim = TMP / "sim" +(sim / "build").mkdir(parents=True, exist_ok=True) +shutil.copy(COREC, sim / "build" / "corec") +(sim / "build" / "corearch").write_text("#!/bin/sh\necho 'error: simulated backend failure' >&2\nexit 1\n") +(sim / "build" / "corearch").chmod(0o755) +ok_src = write("ok_src", "fn main() -> int { return 0; }\n") +out2 = TMP / "c2.bin" +for f in (out2, Path(str(out2) + ".ccr")): + if f.exists(): + f.unlink() +clean() +r = cc(["build", str(ok_src), "-o", str(out2), "--static"], ccbin=sim / "build" / "corec") +t.ok("zero_corearch_fail_deletes_new_ccr", + r.returncode == 1 and not out2.exists() and not Path(str(out2) + ".ccr").exists(), + f"rc={r.returncode} ELF={out2.exists()} ccr={Path(str(out2)+'.ccr').exists()}") + +# ── 零产物 3a/3b:既有旧哨兵在失败后原样仍在 ──────────────────────────── +out3 = TMP / "c3.bin" +out3.write_text("SENTINEL-ELF") +Path(str(out3) + ".ccr").write_text("SENTINEL-CCR") +clean() +r = cc(["build", str(TMP / "ta02.cr"), "-o", str(out3), "--static"]) +t.ok("zero_frontend_fail_keeps_sentinels", + r.returncode == 1 and out3.read_text() == "SENTINEL-ELF" + and Path(str(out3) + ".ccr").read_text() == "SENTINEL-CCR", f"rc={r.returncode}") + +out4 = TMP / "c4.bin" +out4.write_text("SENTINEL-ELF2") +clean() +r = cc(["build", str(ok_src), "-o", str(out4), "--static"], ccbin=sim / "build" / "corec") +t.ok("zero_corearch_fail_keeps_old_elf_sentinel", + r.returncode == 1 and out4.read_text() == "SENTINEL-ELF2", f"rc={r.returncode}") + +print(f"{t.pass_}/{t.pass_ + t.fail} passed") +shutil.rmtree(TMP, ignore_errors=True) +sys.exit(0 if t.fail == 0 else 1) diff --git a/tests/selfhost/test_ent_kernel_neutrality.py b/tests/selfhost/test_ent_kernel_neutrality.py new file mode 100644 index 00000000..beb087a0 --- /dev/null +++ b/tests/selfhost/test_ent_kernel_neutrality.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""ent_kernel.cr 中立性静态 guard(内核完备计划 2026-09-10 全量中立化——用户 +原则:内核零经典概念——寄存器/栈/调度/内存布局/指令任一概念不得出现)。 + +文本扫描 src/lattice/ent_kernel.cr:剥注释(// 与 /* */)与字符串字面量 +后按 identifier token 查禁。禁止 token 分组注记(守卫入回归面——A 组 Task 2 +绿、B 组 Task 3 绿): + + A 组(线性流——Task 2 范围):g_ir_instrs / g_ir_instr_count / + g_ir_instr_cap / iri_*(iri_op/iri_dest/iri_s1/iri_s2/iri_s3/iri_tk) + Task 2 前红:4 使用点 10 token——compute_live_ranges(:99 iri_dest/s1/s2)、 + compute_entries(:227-233 iri_op/s1/dest)、dump_entries_summary(:367 + iri_op)、rl_rule2_func(:677-680 iri_op/s1/s2)。中立化 = 对象面 nod_* + 同 index 机械替换(F5:v7 NOD 节点序 = 指令序 index-aligned)→ 本组绿。 + B 组(实例 meta / 位置编码——Task 3 范围):g_opt_meta 族(g_opt_meta/ + g_opt_meta_count)/ OPT_*(OPT_META_STRIDE/OPT_KEY_REG_ASSIGN)/ + LOC_HOME_BASE / E2_* / g_x86_* / x86 寄存器名 + Task 3 前红:meta_reg_for_var 直读 g_opt_meta(实例私有输出布局)+ + LOC_HOME_BASE 位置编码 seam(常量 + rl_print_loc/verify 合成引用)→ + Task 3 登记表通道切换 + 移除后转绿。**本组 Task 2 收官 = 预期红** + (中间态——A 绿 B 红在报告中记录,Task 3 绿 B)。 + +结构性守卫注记:ent_kernel.cr 双 concat 共享(corec concat 无 regalloc/ +instr/elf/hit/corearch.cr)→ 引用实例独有文件的符号 = corec 编译失败;文本 +guard 真正防的 = 共享声明文件里的实例符号(g_opt_meta/opt 常量 = globals.cr/ +ast.cr,双 concat 都有 → 结构守卫拦不住)——正是本静态 guard 的必要性。 + +实现注:必须先剥注释再扫 identifier——rl_print_loc 的 x86 寄存器注记("3=rbx, +12-15=r12-r15")在注释层,不剥则误报。字符串字面量一并剥离(非代码标识符, +扫串内文本会误报 "home slot"/"reg" 等输出措辞)。 + +NOD/EDG/ENT/REG = 文件格式语义名不拦(§4.3 例外——载体语义名非实例符号)。 +""" + +import re +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +KERNEL = BASE / "src" / "lattice" / "ent_kernel.cr" + +# --- 排除集(分组) --- + +# A 组:线性流(调度产物 = 实例侧;内核零线性流代码引用——Task 2) +A_LITERAL = {"g_ir_instrs", "g_ir_instr_count", "g_ir_instr_cap"} +A_PREFIX = ("iri_",) + +# B 组:实例符号 / 位置编码(Task 3 范围) +B_LITERAL = {"LOC_HOME_BASE"} +B_PREFIX = ("g_opt_meta", "OPT_", "E2_", "g_x86_") +B_REGNAMES = { # x86 寄存器枚举名(注释层已有 "3=rbx, 12-15=r12-r15" 记录) + "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "rip", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "eip", +} + +TOKEN_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") + + +def strip_comments_and_strings(src: str) -> str: + """剥 // 行注释、/* */ 块注释与 "..." 字符串字面量(内容以空格替位, + 保持 token 边界语义——邻接 identifier 不得因剥离而并字)。""" + out = [] + i, n = 0, len(src) + while i < n: + c = src[i] + if c == "/" and i + 1 < n and src[i + 1] == "/": + j = src.find("\n", i) + out.append(" ") + i = j if j >= 0 else n + continue + if c == "/" and i + 1 < n and src[i + 1] == "*": + j = src.find("*/", i + 2) + out.append(" ") + i = (j + 2) if j >= 0 else n + continue + if c == '"': + j = i + 1 + while j < n: + if src[j] == "\\": + j += 1 + elif src[j] == '"': + break + j += 1 + out.append(" ") + i = j + 1 + continue + out.append(c) + i += 1 + return "".join(out) + + +def scan() -> tuple: + """扫描 ent_kernel.cr 代码面。返回 ({A 组 token: count}, {B 组 token: + count})——均为空 = 中立。""" + src = KERNEL.read_text(encoding="utf-8") + code = strip_comments_and_strings(src) + found_a, found_b = {}, {} + for tok in TOKEN_RE.findall(code): + if tok in A_LITERAL or tok.startswith(A_PREFIX): + found_a[tok] = found_a.get(tok, 0) + 1 + elif (tok in B_LITERAL or tok.startswith(B_PREFIX) + or tok in B_REGNAMES): + found_b[tok] = found_b.get(tok, 0) + 1 + return found_a, found_b + + +def main() -> int: + if not KERNEL.exists(): + print(f"[FAIL] missing kernel file {KERNEL}") + return 1 + a, b = scan() + + def fmt(inv: dict) -> str: + return " ".join(f"{t}×{c}" for t, c in sorted(inv.items())) or "clean" + + if not a: + print("[PASS] neutrality A (linear-stream g_ir_instrs/iri_*): clean") + else: + print(f"[FAIL] neutrality A (linear-stream g_ir_instrs/iri_*): {fmt(a)}" + f" ← Task 2 目标(对象面 nod_* 同 index 替换)") + if not b: + print("[PASS] neutrality B (meta/LOC/OPT/E2/g_x86_*/regnames): clean") + else: + print(f"[FAIL] neutrality B (meta/LOC/OPT/E2/g_x86_*/regnames): {fmt(b)}" + f" ← Task 3 目标(登记表通道 + LOC_HOME_BASE 移除;Task 2 收官预期红)") + ok = not a and not b + print(f"{'PASS' if ok else 'FAIL'} (A clean = Task 2 gate; " + f"A+B clean = full neutrality)") + return 0 if ok else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_enum_limit.py b/tests/selfhost/test_enum_limit.py new file mode 100644 index 00000000..d6ff1be1 --- /dev/null +++ b/tests/selfhost/test_enum_limit.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""枚举/结构体容量面回归(TODO #35 → **容量批 T3 解除**)。 + +**旧态(#35 修复,2026-09-12)**:`EnumVariant` 槽区(`MAX_ENUM_VARIANTS=16` 槽 × `OFF_EV_SIZE=272`) +与 `StructInfo` 字段槽区(`MAX_STRUCT_FIELDS=16` 槽)是**定长内嵌槽区** ⇒ >16 越界写(踩 count/邻记录) +⇒ parser 写点护栏 + `error[P022]`/`error[P023]` 定位硬错(rc=1 + 无产物)。 + +**新态(容量批 T3,裁-CAP-2 (a)「记录布局迁侧表」)**:字段/变体/载荷**迁侧表** ⇒ 三面**无硬上限**; +`P022`/`P023` **退役**(零 raise);覆盖位自 `2^vi` 单 int 换为**无界位图**(`mc_*`)⇒ ≥63 变体的 +穷尽性/冗余臂判定恢复正确(旧态实测 70 变体 SIGFPE rc=136)。 + +判据(本实例实跑):≤16 与 >16 一律 **编译 + 运行值正确**(三路同证);穷尽性/冗余臂的**真错**仍 +定位硬错(TM03/TM04);越界写类旧闸的**死亡证据** = 本批能力变更(旧 17-拒绝用例转正)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 退出码 = main 返回值低 8 位(见 test_slice_bounds.py 头注)⇒ 期望值一律 < 256。 + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _write(source: str): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + return src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + src = _write(source) + out = src[:-3] + try: + built = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + if built.returncode != 0: + return f"compile-failed(rc={built.returncode}): {built.stdout}{built.stderr}" + return subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + finally: + _cleanup(src, out, out + ".ccr") + + +def case_run(name, source, expect_rc): + r = build_and_run(source) + if isinstance(r, str): + print(f"[FAIL] {name}: {r}") + return False + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc}, got {r.returncode}") + return False + print(f"[PASS] {name}: rc={r.returncode}") + return True + + +def case_reject(name, source, needles, cmd="build", forbid=()): + """拒绝面:rc != 0 + 全部 needle 命中 + 无产物(bin 与 .ccr)+ 全部 forbid 缺席。 + + **必须**同时断言 needle(定位诊断文本)——仅断言 rc != 0 会被「读回侧闸」式的兜底 + 误当通过:修复前 build 面本来就 rc=1(`.ccr` 读回闸),而写入侧的静默面(check rc=0) + 与「越界写已发生」并不因此消失。 + forbid = 流水线阶段串(如 `save .ccr`)——钉「拒绝发生在**前端**(parse 阶段诊断闸, + `main.cr` 的 `[3/5] parse...` 之后),**不进入** lower/写 .ccr/ELF」:护栏在读回侧 + (读回闸兜底)≠ 护栏在写入侧(TODO #35 的原话)。 + """ + src = _write(source) + out = src[:-3] + try: + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + blob = r.stdout + r.stderr + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默面复活): {blob}") + return False + for nd in needles: + if nd not in blob: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {nd!r}: {blob}") + return False + for fd in forbid: + if fd in blob: + print(f"[FAIL] {name}: 拒绝晚于前端——输出含流水线阶段 {fd!r}(越界写已进入 IR/载体面)") + return False + if cmd == "build": + for p in (out, out + ".ccr"): + if os.path.exists(p): + print(f"[FAIL] {name}: 拒绝编译却仍产出 {p}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles} + 无产物") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +# ─── 语料构造 ─── +def enum_src(n, extra="return 0"): + """n 变体枚举 + main(`extra` = main 体)。""" + vs = ", ".join(f"V{i}" for i in range(n)) + return f"enum E{n} {{ {vs} }}\nfn main() -> int {{ {extra}; }}\n" + + +def enum_exhaustive_src(n, pick): + """n 变体枚举 + 全臂 match(返回被选变体下标 ⇒ 逐变体可判值正确)。""" + vs = ", ".join(f"V{i}" for i in range(n)) + arms = ", ".join(f"V{i} => {i}" for i in range(n)) + return (f"enum E{n} {{ {vs} }}\n" + f"fn main() -> int {{ x := V{pick}(); return match x {{ {arms}, }}; }}\n") + + +def enum_missing_arm_src(n): + """n 变体枚举 + n-1 臂 match(缺最后一变体 ⇒ 真·非穷尽)。""" + vs = ", ".join(f"V{i}" for i in range(n)) + arms = ", ".join(f"V{i} => {i}" for i in range(n - 1)) + return (f"enum E{n} {{ {vs} }}\n" + f"fn main() -> int {{ x := V0(); return match x {{ {arms}, }}; }}\n") + + +def payload_src(n, extra="return 0"): + ps = ", ".join("int" for _ in range(n)) + return f"enum P {{ A({ps}), B }}\nfn main() -> int {{ {extra}; }}\n" + + +def struct_src(n, extra="return 0"): + fs = ", ".join(f"f{i}: int" for i in range(n)) + return f"struct S{n} {{ {fs} }}\nfn main() -> int {{ {extra}; }}\n" + + +def main(): + ok = [ + # ── 16 = 旧边界(含):三面全对(回归钉)── + case_run("enum16_exhaustive_ok", enum_exhaustive_src(16, 9), 9), + case_run("enum16_decl_only_ok", enum_src(16), 0), + case_run("payload16_ok", payload_src(16, "x := B(); return 4"), 4), + case_run("struct16_ok", + struct_src(16, "s := S16 { " + ", ".join(f"f{i}: {i}" for i in range(16)) + + " }; return s.f15"), + 15), + # ── >16(17/40):旧 P022/P023 拒绝面的**转正**(死亡证据 = 侧表解除)── + case_run("enum17_now_ok", enum_exhaustive_src(17, 13), 13), + case_run("enum40_now_ok", enum_exhaustive_src(40, 33), 33), + case_run("payload17_now_ok", payload_src(17, "x := A(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17); return match x { A(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17) => { return a17; } B => { return 0; } }"), 17), + case_run("struct17_now_ok", + struct_src(17, "s := S17 { " + ", ".join(f"f{i}: {i}" for i in range(17)) + + " }; return s.f16"), + 16), + case_run("struct40_now_ok", + struct_src(40, "s := S40 { " + ", ".join(f"f{i}: {i}" for i in range(40)) + + " }; return s.f39"), + 39), + # ── 跨记录完整性(侧表基址不重叠:宽记录 + 邻记录)── + case_run("enum40_plus_small_ok", + enum_src(40, "v := V39(); return 0") + "enum F3 { X, Y, Z }\n" + "fn g() -> int { w := Z(); return match w { X => { return 1; } Y => { return 2; } Z => { return 3; } }; }\n", 0), + # 记录碰撞敏感面(**判别性**):大枚举之后声明小枚举 ⇒ 大枚举的**判定面** + # (穷尽性)仍须正确(侧表基址不重叠的机器抓手) + case_reject("enum40_then_small_missing_arm_rejected", + enum_src(40, "v := V0(); return 0") + "enum H4 { S, T, U }\n" + "fn hm(x: E40) -> int { return match x { " + + ", ".join(f"V{i} => {{ return {i}; }}" for i in range(39)) + " }; }\n", + ["error[TM03]", "missing variant 'V39'"], cmd="check"), + case_run("enum40_then_small_then_match_big_ok", + enum_src(40, "v := V0(); return 0") + "enum H3 { S, T, U }\n" + "fn hm(x: E40) -> int { return match x { " + ", ".join(f"V{i} => {{ return {i}; }}" for i in range(40)) + " }; }\n" + "fn main3() -> int { return 0; }\n", 0), + case_run("enum40_plus_small_match_ok", + enum_src(40, "v := V7(); return 0") + "enum G3 { P, Q, R }\n" + "fn gm(x: G3) -> int { return match x { P => { return 41; } Q => { return 42; } R => { return 43; } }; }\n" + "fn main2() -> int { return 0; }\n", 0), + case_run("struct40_plus_pair_ok", + struct_src(40, "a := S40 { " + ", ".join(f"f{i}: {i}" for i in range(40)) + " }; return a.f39") + + "struct P2 { x: int, y: int }\n" + "fn h() -> int { p : ., mut = P2 { x: 7, y: 8 }; return p.y; }\n", 39), + # ── 覆盖位无界(≥63 变体):穷尽性判定仍正确 ── + case_run("enum64_exhaustive_ok", enum_exhaustive_src(64, 63), 63 % 256), + case_run("enum70_exhaustive_ok", enum_exhaustive_src(70, 69), 69), + # ── 负控:覆盖位的**真错**仍定位硬错(TM03 缺臂 / TM04 冗余臂)── + case_reject("enum64_missing_arm_rejected", enum_missing_arm_src(64), + ["error[TM03]", "missing variant 'V63'"], cmd="check"), + case_reject("enum70_missing_arm_rejected", enum_missing_arm_src(70), + ["error[TM03]", "missing variant 'V69'"], cmd="check"), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_generic_constr.py b/tests/selfhost/test_generic_constr.py new file mode 100644 index 00000000..e6b80b02 --- /dev/null +++ b/tests/selfhost/test_generic_constr.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""R2 P3 Task 5:泛型约束(保留 / 实例化判定 / monomorph 实例键类型项化)行为覆盖集。 + +判据(语义见 docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md Task 5 + +docs/superpowers/specs/2026-09-10-type-interface-unification-design.md §5.2): + + A. 约束**保留**(本批修):`struct`/`enum` 泛型约束此前写进 dummy 缓冲后丢弃(`struct Box[T: I]` + 静默无约束)⇒ 本批登记到结构/枚举侧表,并在**实例化点**(`Box[P]` 的类型解析)判定。 + B. 实例化判定(**本质轴**):约束名解析为原生/已声明类型 ⇒ `ty_sub(实参项, 约束项)` 三态; + 违反 = `error[TG02]`(软诊断:check rc=1 / build rc=0)+ **非空反例文本**。 + C. 用户接口约束(`T: I`,I = `interface` 名):**P3b Task 0 起 `iface_satisfies` 已交付** + (P3 计划附录 B.1 阻塞项解除)⇒ 结构/枚举**实例化点真判定**(可证违反 = TG02 软诊断; + 正控 = 方法在位不误报);函数调用点的接口约束仍走**既有** `check_iface` 名拼接路径 + (措辞/去重/rc 逐字未动;0 → 新措辞的切换归 Task 6 Step 3)。「不判」(-1)仍存在但 + 域收窄 = 非命名实参 / 泛型形参实参 / 签名编码不可判面(登记;覆盖集 = + test_iface_satisfies.py)。 + D. F4(形参链导航,TODO #21):`infer_gen_call` 的 `pn = pn + 1` 落在**类型节点**上 + (每个形参的类型节点在其形参节点之前分配)⇒ 后续形参的声明类型被绕过、未绑定形参被 + 凭空绑定。本批修为「前扫到下一个 EXPR_PARAM」——正控 = 旧态假拒现通过;负控 = 真不适配仍拒。 + E. monomorph 实例键**类型项化**:键由「实参名串(非原生 → "int" 兜底)」改为类型行规范结构名 + (含命名/泛型应用真身份)⇒ 两个**不同结构体**实参不再折叠成一个实例(旧态实测 `f[unit]` + 一份覆盖 A/B 两类型;新态 `f[A]`/`f[B]` 两份)。判据 = `.ccr` 的 SYM/STR 里的实例名。 + F. MAX_GENERICS = 4(本批登记,未解除):>4 泛型形参现状 = parse 期硬报(P01/TA08,非静默 + 截断),全语料最大实参 = 2 ⇒ 解除需改 struct/enum 表布局(ESZ 变更),本批不做(登记)。 + +判据口径 = 正例**三路同证**(check rc=0 ∧ ELF rc=N ∧ interp rc=N——TF01/TA01/TG02 系为 +**软诊断**,只断 build+run 会让判定面失效的突变照样全绿);负例走 `check`(有诊断即 rc=1) ++ 措辞断言 + 无产物。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="build", extra=None): + """返回 (result, out_path, src_path);cmd = build(ELF 产物)| check(仅前端)| ccr。""" + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + elif cmd == "ccr": + args = [str(COREC), "ccr", src, "-o", out + ".ccr"] + else: + args = [str(COREC), "check", src] + if extra: + args += list(extra) + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + r, out, src = _compile(source) + try: + if r.returncode != 0: + return r, None + rr = subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + return r, rr + finally: + _cleanup(src, out, out + ".ccr") + + +def run_interp(source: str): + return subprocess.run( + [str(COREC), "run", source], capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def case_dual(name, source, expect_rc): + """正例三路同证:check rc=0 ∧ ELF rc=N ∧ interp rc=N。""" + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + ri = run_interp(source) + if ri.returncode != expect_rc: + print(f"[FAIL] {name}: interp expected rc {expect_rc}, got {ri.returncode}: {ri.stdout}{ri.stderr}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}") + return True + + +def case_reject(name, source, needles, cmd="check"): + """负例:rc≠0 + 诊断针 + (build 面)无产物。""" + r, out, src = _compile(source, cmd) + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + if cmd == "build" and os.path.exists(out): + print(f"[FAIL] {name}: 拒绝编译却仍产出二进制 {out}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_ccr_names(name, source, present, absent=()): + """实例键判据:`.ccr` 的 STR 段里实例名(mangled)的**存在/不存在**断言。 + + monomorph 实例的 mangled 名 = `原名[键]`(`gen_create_instance`),落 `.ccr` STR 段; + SYM 段含函数名(ELF 无符号表 ⇒ 该判据只在 .ccr 面成立)。absent 列表把「旧态的折叠名」 + 钉死(防回退)。 + """ + r, out, src = _compile(source, cmd="ccr") + try: + if r.returncode != 0: + print(f"[FAIL] {name}: ccr rc={r.returncode}: {r.stdout}{r.stderr}") + return False + blob = open(out + ".ccr", "rb").read() + for n in present: + if n.encode() not in blob: + print(f"[FAIL] {name}: 缺实例名 {n!r}") + return False + for n in absent: + if n.encode() in blob: + print(f"[FAIL] {name}: 不该出现的实例名 {n!r} 出现了(旧折叠面复活)") + return False + print(f"[PASS] {name}: 实例名 {list(present)!r} 在 / {list(absent)!r} 不在") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +# ── 源形 ────────────────────────────────────────────────────────────── + +# A/B:本质轴约束(函数侧 + 结构侧 + 枚举侧) +# 返回型 = T(**既有语义约束**:泛型体内 `return a`(a: T)对**具体**返回型会 TF01——实测 +# 新旧一致,非本批面);调用点 int 返回 T 实例化的结果 +FUNC_INT_CONSTR = """fn f[T: int](a: T) -> T { return a; } +fn main() -> int { return f(1); } +""" +FUNC_INT_CONSTR_BAD = """fn f[T: int](a: T) -> T { return a; } +fn main() -> int { return f("s"); } +""" +STRUCT_INT_CONSTR = """struct Box[T: int] { v: T } +fn g(b: Box[int]) -> int { return b.v; } +fn main() -> int { return g(Box { v: 7 }); } +""" +STRUCT_INT_CONSTR_BAD = """struct Box[T: int] { v: T } +struct S { a: int } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +STRUCT_INT_CONSTR_STR_BAD = """struct Box[T: int] { v: T } +fn g(b: Box[string]) -> int { return 0; } +fn main() -> int { return 0; } +""" +ENUM_INT_CONSTR_BAD = """enum EBox[T: int] { V(T), N } +fn g(b: EBox[string]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 无约束泛型(对照:约束面落地不得波及无约束声明) +STRUCT_NO_CONSTR = """struct Wrap[T] { v: T } +struct S { a: int } +fn g(b: Wrap[S]) -> int { return b.v.a; } +fn main() -> int { return g(Wrap { v: S { a: 3 } }); } +""" +# C:用户接口约束 = P3b 边界(零诊断;函数侧走既有 check_iface,结构侧 = 不判) +IFACE_LEGACY_OK = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +fn f[T: Show](a: T) -> int { return 0; } +fn main() -> int { s := S { a: 1 }; return f(s); } +""" +IFACE_LEGACY_BAD = """interface Show { fn show(self) -> int; } +struct S { a: int } +fn f[T: Show](a: T) -> int { return 0; } +fn main() -> int { s := S { a: 1 }; return f(s); } +""" +STRUCT_IFACE_REJECT = """interface Show { fn show(self) -> int; } +struct Box[T: Show] { v: T } +struct S { a: int } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 结构侧正控:方法在位 ⇒ 满足(P3b Task 0 起实例化点**真判定**——本常量 = 新判定不误报的守门) +STRUCT_IFACE_ACCEPT = """interface Show { fn show(self) -> int; } +struct Box[T: Show] { v: T } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { return g(Box { v = S { a = 7 } }); } +""" +UNKNOWN_CONSTR_NAME = """fn f[T: NoSuchThing](a: T) -> int { return 0; } +fn main() -> int { return f(1); } +""" +# D:F4 形参链(旧态:后续形参的声明类型被绕过 → T 未绑定 → 假拒 TF01) +F4_LATER_PARAM = """fn foo(s: string) -> string { return s; } +fn take[T](n: int, a: T) -> T { return a; } +fn main() -> int { return take(1, 2); } +""" +F4_LATER_PARAM_BAD = """fn foo(s: string) -> string { return s; } +fn take[T](n: int, a: T) -> T { return a; } +fn main() -> int { return take(1, "s"); } +""" +# E:实例键(两个不同结构体实参) +TWO_STRUCTS = """struct A { a: int } +struct B { b: int, c: int } +fn f[T](x: T) -> int { return 1; } +fn main() -> int { p := A { a: 1 }; q := B { b: 2, c: 3 }; return f(p) + f(q); } +""" + + +def main(): + ok = [ + # ── A/B 正:本质轴约束满足(函数侧 / 结构侧)── + case_dual("func_essence_constr_pass", FUNC_INT_CONSTR, 1), + case_dual("struct_essence_constr_pass", STRUCT_INT_CONSTR, 7), + case_dual("unconstrained_generic_unaffected", STRUCT_NO_CONSTR, 3), + # ── A/B 负:约束违反(软诊断 TG02 + 反例文本)── + # 函数调用点:实参 string 不满足 `T: int` + case_reject("func_essence_constr_violated", FUNC_INT_CONSTR_BAD, + ["error[TG02]", "does not satisfy constraint 'int'", "counterexample"]), + # 结构实例化点:`Box[string]` 违反 `T: int` + case_reject("struct_inst_constr_violated", STRUCT_INT_CONSTR_STR_BAD, + ["error[TG02]", "does not satisfy constraint 'int'", "counterexample"]), + # 结构实例化点:命名类型实参(S)——**R2 P5 Task 3 重钉**(收紧面,登记): + # 命名面接引擎身份链后,「命名实参 × 原生约束」由「-1 不判」变为「0 = 确定违反」 + # ⇒ TG02 + 反例(旧断:case_dual ... 0 = 零诊断「不判」= 引擎未覆盖面②的静默面)。 + # 语义 = 与 legacy 同结论(type_equal(S, int) = false ⇒ 本就不满足);全语料 0 命中 + # (72 档 rc/日志与冻结基线逐条同)+ D26 同源对拍零差异 ⇒ 新 rc=1 面仅在本登记例 + # 内可见(build 侧 tg02 非硬名单 ⇒ 产物面不变)。见 p5-task3-report §收紧。 + case_reject("struct_inst_named_arg_rejected", STRUCT_INT_CONSTR_BAD, + ["error[TG02]", "does not satisfy constraint 'int'", "counterexample"]), + # 枚举实例化点 + case_reject("enum_inst_constr_violated", ENUM_INT_CONSTR_BAD, + ["error[TG02]", "does not satisfy constraint 'int'"]), + # ── C:用户接口约束(P3b 边界)── + # 函数侧:既有 check_iface 路径(满足 → 过) + case_dual("iface_constr_legacy_pass", IFACE_LEGACY_OK, 0), + # 函数侧:既有路径拒绝(措辞 = interface,与本质轴路径区分) + case_reject("iface_constr_legacy_reject", IFACE_LEGACY_BAD, + ["error[TG02]", "does not satisfy interface 'Show'"]), + # 结构侧:**P3b Task 0 起真判定**(`iface_satisfies` 已交付 ⇒ 可证违反 = TG02)。 + # 本用例原为「不判」钉(P3a 边界,rc=0 零诊断)——**台账:旧 rc=0 → 新 check rc=1 + + # error[TG02] + "does not satisfy interface 'Show'"**(收紧,全语料零命中;同族 + # 覆盖集 = tests/selfhost/test_iface_satisfies.py 17 例) + case_reject("struct_iface_constr_violated", STRUCT_IFACE_REJECT, + ["error[TG02]", "does not satisfy interface 'Show'"]), + # 结构侧正控:方法在位 ⇒ 满足(新判定不误报;三路同证) + case_dual("struct_iface_constr_present_accepted", STRUCT_IFACE_ACCEPT, 7), + # 约束名不是任何类型/接口 ⇒ 不判(不发明诊断) + case_dual("unknown_constr_name_unjudged", UNKNOWN_CONSTR_NAME, 0), + # ── D:F4 形参链(正控 = 修复后绑定正确;负控 = 真不适配仍拒)── + case_dual("f4_later_param_binds_correctly", F4_LATER_PARAM, 2), + case_reject("f4_later_param_type_mismatch", F4_LATER_PARAM_BAD, ["error[TF01]"], cmd="check"), + # ── E:实例键类型项化(异型异构体 → 两实例;旧态折叠为 f[unit])── + case_ccr_names("mono_two_struct_instances", TWO_STRUCTS, + present=["f[A]", "f[B]"], absent=["f[unit]"]), + ] + + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_global_init.py b/tests/selfhost/test_global_init.py new file mode 100644 index 00000000..a90d6682 --- /dev/null +++ b/tests/selfhost/test_global_init.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +"""File-scope global initializer semantics — dual path (interpreter + native ELF). + +R1 Task 4 判据(语言面收窄 §1.3 / R1 ②):文件级全局的运行期初始化在两条路径上 +语义一致—— + ① 聚合全局(`[T; N]`)无论有无初值都在运行期分配存储并写槽(存储持久); + ② 非常量初值的全局在运行期求值并写槽; + ③ 编译期标量常量初始化(ELF `_start` 常量环 / 解释器初始化阶段)两路径一致。 + +每例两条断言:编译/执行输出无 `error[` 诊断 + 退出码精确值。 +两路径都跑:`corec run`(解释器)与 `corec build --static` + 运行 ELF。 + +注意:ELF 路径走 per-function cir 增量缓存(.core/cache/cir/),缓存键不含编译器 +身份(TODO #5)——本脚本在开跑前清一次缓存,保证判据跑在当次构建的编译器上。 +""" + +import os +import subprocess +from pathlib import Path + + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" + +# (name, source, expected rc) +CASES = [ + ( + "array_global_literal", + "g : [int;2] = [8,9];\nfn main()->int{return g[1];}", + 9, + ), + ( + "array_global_no_init", + "g : [int;2];\nfn main()->int{ g[1]=7; return g[1]; }", + 7, + ), + ( + "array_global_copy", + "a : [int;2] = [8,9];\nb : [int;2] = a;\nfn main()->int{return b[1];}", + 9, + ), + ( + "array_global_unused", + "g : [int;4] = [1,2,3,4];\nfn main()->int{return 5;}", + 5, + ), + ( + "scalar_mut_global", + "x : int, mut = 5;\nfn main()->int{ x = x + 1; return x; }", + 6, + ), + ( + "scalar_const_global", + "x : int = 5;\nfn main()->int{return x;}", + 5, + ), + ( + "array_global_cross_func", + "g : [int;2] = [3,4];\nfn get()->int{return g[1];}\nfn main()->int{return get();}", + 4, + ), + ( + "array_global_two_refs", + "a : [int;2] = [8,9];\nb : [int;2] = a;\nfn main()->int{return a[0]+b[1];}", + 17, + ), + # —— 以下为同一修复面(R1 ② 判据)的扩展 pin:非字面量初值的其它形态 + callee 侧 + # 聚合访问(解释器外函数内联路径的聚合族 opcode;ELF 全局基址字段读写)。 —— + ( + "struct_global_field_read", + "struct P { x: int, y: int }\ng : P = P { x = 30, y = 12 };\nfn main()->int{return g.y;}", + 12, + ), + ( + "struct_global_field_write_callee", + "struct P { x: int, y: int }\ng : P = P { x = 1, y = 2 };\nfn set()->int{ g.y = 40; return 0; }\nfn main()->int{ set(); return g.y; }", + 40, + ), + ( + "string_global", + 'g : string = "hello";\nfn main()->int{ if str_len(g) == 5 { return 7; } return 1; }', + 7, + ), + ( + "call_init_global", + "fn make()->int{ return 33; }\ng : int, mut = make();\nfn main()->int{ return g; }", + 33, + ), + ( + "callee_array_var_index", + "g : [int;3] = [5,6,7];\nfn get(i: int)->int{ return g[i]; }\nfn main()->int{ return get(2); }", + 7, + ), + ( + "alias_array_global_literal", + "type Arr = [int;2];\ng : Arr = [3,4];\nfn main()->int{return g[1];}", + 4, + ), + ( + "alias_array_global_no_init", + "type Arr = [int;2];\ng : Arr;\nfn main()->int{ g[1] = 7; return g[1]; }", + 7, + ), +] + + +def clean_cache() -> None: + """ci 缓存键不含编译器身份(TODO #5)——判据前清缓存,避免旧 IR 冒充新编译器。 + + 返回码必须检查(Task 4 评审 Minor):清缓存失败 + 旧缓存 = 判据被静默污染 + (正是本项目大忌)——失败即报错退出,不许继续跑。 + """ + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "clean-cache"], + cwd=BASE, + capture_output=True, + text=True, + ) + if result.returncode != 0: + print(f"[FAIL] clean-cache failed (rc={result.returncode})") + for line in (result.stdout + result.stderr).strip().splitlines()[:10]: + print(f" | {line}") + raise SystemExit(1) + + +def has_diag(output: str) -> bool: + return "error[" in output + + +def run_interp(source: str): + """解释器路径:corec run ''。返回 (rc, combined_output)。""" + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "run", source], + cwd=BASE, + capture_output=True, + text=True, + ) + return result.returncode, result.stdout + result.stderr + + +def run_native(name: str, source: str): + """ELF 路径:corec build --static + 运行产物。返回 (rc, combined_output)。""" + tag = f"{name}_{os.getpid()}" # pid 后缀:并发跑同一测试不互踩(终审 Minor) + src = BUILD / f"global_init_{tag}.cr" + binary = BUILD / f"global_init_{tag}" + ccr = Path(str(binary) + ".ccr") + src.write_text(source.strip() + "\n", encoding="utf-8") + try: + built = subprocess.run( + [ + "nice", + "-n", + "19", + str(COREC), + "build", + str(src), + "-o", + str(binary), + "--static", + ], + cwd=BASE, + capture_output=True, + text=True, + ) + out = built.stdout + built.stderr + if built.returncode != 0: + return built.returncode, out + if not binary.exists(): + return 127, out + "\n[no ELF produced]" + run = subprocess.run([str(binary)], cwd=BASE, capture_output=True, text=True) + return run.returncode, out + run.stdout + run.stderr + finally: + for artifact in (src, binary, ccr): + try: + artifact.unlink() + except FileNotFoundError: + pass + + +def main() -> int: + if not COREC.exists(): + print("build/corec is missing; run `python3 build_selfhost_native.py` first") + return 1 + BUILD.mkdir(exist_ok=True) + clean_cache() + + failures = [] + for name, source, expected in CASES: + ok = True + for path, runner in (("interp", run_interp), ("elf", run_native)): + if path == "interp": + rc, out = runner(source) + else: + rc, out = runner(name, source) + diag = has_diag(out) + if diag or rc != expected: + ok = False + detail = f"rc={rc}" + if diag: + detail += " + diagnostic" + print(f"[FAIL] {name}/{path}: expected {expected}, got {detail}") + if out.strip(): + for line in out.strip().splitlines()[:10]: + print(f" | {line}") + else: + print(f"[PASS] {name}/{path}: {rc}") + if not ok: + failures.append(name) + + print(f"{len(CASES) - len(failures)}/{len(CASES)} global-init cases passed (dual path)") + return 0 if not failures else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_hit_table.py b/tests/selfhost/test_hit_table.py new file mode 100644 index 00000000..bc46f7b4 --- /dev/null +++ b/tests/selfhost/test_hit_table.py @@ -0,0 +1,1474 @@ +#!/usr/bin/env python3 +"""HIT 表模式合成层测试(M1 Task 3)——事件流降低 + 常量池 + 表模式运行闭环。 + +Task 3 = 合成层:IR 直线子集 → 4 核事件流(sub/nand/load/store): +- 表模式下 corearch 内部 = IR →(降低)→ 事件流 →(表投影)→ 字节 +- 降低规则:sub 直通;add → sub 反减(sub(a, sub(0,b)),0 = 常量池);常量 + IR_CONST → 常量池槽 load 事件(ELF rodata 写入);store/load 直通事件 +- 超出直线子集的 op(分支/调用/移位/mod/…)→ 编译报错「needs more events」 +- 判据 = 运行正确性(旧路径与表模式同 .ccr 运行 exit 一致且等于手算期望值) + —— Task 2 的逐字节对照随合成层失效(const → 常量池 load 与旧 imm 路径 + 字节不同,按计划 Task 3/4 以冒烟运行正确为判据) +- --dump-events 调试输出(事件流 dump 断言) + +注(评审挂账):源语言无位与/位或运算符(&&/|| 恒短路分支化,超 M1 直线 +子集)→ and→nand / or→德摩根 规则在 M1 无源码载体,属 IR 层完备性代码。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" + +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" +TABLE = BASE / "src" / "arch" / "x86_64" / "core-x86.toml" +HIT_SRC = BASE / "src" / "arch" / "hit" / "hit.cr" +LOWER_SRC = BASE / "src" / "arch" / "hit" / "lower_to_core.cr" +SMOKE_ADD = BASE / "tests" / "hit" / "smoke_add.cr" +SMOKE_MEM = BASE / "tests" / "hit" / "smoke_mem.cr" +OPT = "1" # 与 corec build 默认同(corec/main.cr: g_opt_level 默认 1) + +# 载体(直线子集:const/store/add/sub/load/return——无 &&/||(短路分支化超 M1)) +SUB_SRC = "fn main() -> int {\n a := 7;\n b := 5;\n return a - b;\n}\n" +SUB_RC = 2 # 7 - 5 + +ADD_SRC = "fn main() -> int {\n a := 7;\n b := 5;\n return a + b;\n}\n" +ADD_RC = 12 # 7 + 5(add → sub 反减:12 = 7 - (0 - 5)) + +MEM_SRC = """fn main() -> int { + a: ., mut = 6; + b := 3; + a = a + b; + a = a - 4; + return a; +} +""" +MEM_RC = 5 # 6+3-4(链式加减 + 内存读写,与 tests/hit/smoke_mem.cr 同源同期望) + +MOD_SRC = "fn main() -> int {\n a := 7;\n b := 3;\n return a % b;\n}\n" + + +def run_bin(binary, args): + return subprocess.run( + [str(binary)] + args, + cwd=BASE, + capture_output=True, + text=True, + timeout=300, + ) + + +def compile_ccr(tmp: Path, name: str, src: str): + """corec ccr:.cr → .ccr(仅前端,不落 corearch)。""" + src_p = tmp / f"{name}.cr" + src_p.write_text(src) + ccr_p = tmp / f"{name}.ccr" + r = run_bin(COREC, ["ccr", str(src_p), "-o", str(ccr_p), "--opt-level", OPT]) + if r.returncode != 0: + raise RuntimeError(f"corec ccr failed: {r.stdout + r.stderr}") + if not ccr_p.exists(): + raise RuntimeError(f"corec ccr produced no .ccr: {r.stdout + r.stderr}") + return ccr_p + + +def emit_bin(ccr: Path, out: Path, table: bool, dump: bool = False): + args = [str(ccr), "--elf", "--opt-level", OPT, "-o", str(out)] + if table: + args += ["--table", str(TABLE)] + if dump: + args += ["--dump-events"] + r = run_bin(COREARCH, args) + if r.returncode != 0: + raise RuntimeError(f"corearch failed ({'table' if table else 'old'}): {r.stdout + r.stderr}") + if not out.exists(): + raise RuntimeError(f"corearch produced no output: {r.stdout + r.stderr}") + return r + + +def build_both(tmp: Path, name: str, src: str): + """同一 .ccr → 旧路径 + --table 路径两 ELF;返回 (old_bytes, tab_bytes, outputs)。""" + ccr = compile_ccr(tmp, name, src) + old_out = tmp / f"{name}_old" + tab_out = tmp / f"{name}_tab" + old_r = emit_bin(ccr, old_out, table=False) + tab_r = emit_bin(ccr, tab_out, table=True) + return old_out.read_bytes(), tab_out.read_bytes(), old_r, tab_r + + +def run_elf(bin_path: Path): + r = subprocess.run([str(bin_path)], capture_output=True, text=True, timeout=60) + return r.returncode + + +def test_run_closure(tmp: Path, name: str, src: str, expected_rc: int, need_lowered: bool) -> bool: + """表模式运行闭环:两路径 exit == 手算期望值;表模式须真实经事件流发射。""" + try: + _, _, old_r, tab_r = build_both(tmp, name, src) + except RuntimeError as e: + print(f"[FAIL] {name}: {e}") + return False + out = tab_r.stdout + tab_r.stderr + # M2a Task 1:schema v2 后事件数 4 → 9(运行时核:1-4 数据 + 5-9 控制/调用/系统) + if "hit table loaded: 9 events" not in out: + print(f"[FAIL] {name}: missing 'hit table loaded: 9 events' in --table output") + print(out) + return False + if need_lowered: + m = out.find(" events emitted") + if m < 0: + print(f"[FAIL] {name}: missing 'events emitted' summary in --table output") + print(out) + return False + cnt_txt = out[:m].rsplit("hit table: ", 1)[-1].strip() + if not cnt_txt.isdigit() or int(cnt_txt) < 1: + print(f"[FAIL] {name}: events-emitted count invalid: '{cnt_txt}'") + print(out) + return False + old_rc = run_elf(tmp / f"{name}_old") + tab_rc = run_elf(tmp / f"{name}_tab") + if old_rc != expected_rc or tab_rc != expected_rc: + print(f"[FAIL] {name}: run exit codes old={old_rc} table={tab_rc} expected={expected_rc}") + return False + print(f"[PASS] {name}: old & --table ELF both exit {expected_rc}") + return True + + +def test_dump_events(tmp: Path, name: str, src: str, expect_sub_ev: bool, + expect_nand_ev: bool, expect_pool: int) -> bool: + """--dump-events:事件流可 dump(ev 行 + 常量池条目数断言)。""" + try: + ccr = compile_ccr(tmp, name, src) + dump_out = tmp / f"{name}_dump" + r = emit_bin(ccr, dump_out, table=True, dump=True) + except RuntimeError as e: + print(f"[FAIL] dump/{name}: {e}") + return False + out = r.stdout + r.stderr + ok = True + m = out.find("hit events lowered:") + if m < 0: + print(f"[FAIL] dump/{name}: missing 'hit events lowered:' summary") + ok = False + else: + cnt_txt = out[m:].split("\n", 1)[0].replace("hit events lowered:", "").strip() + if not cnt_txt.isdigit() or int(cnt_txt) < 1: + print(f"[FAIL] dump/{name}: event count invalid: '{cnt_txt}'") + ok = False + pool_marker = f"hit pool entries: {expect_pool}" + if pool_marker not in out: + print(f"[FAIL] dump/{name}: expected '{pool_marker}'") + ok = False + for probe, need in (("ev sub", expect_sub_ev), ("ev nand", expect_nand_ev)): + if need and probe not in out: + print(f"[FAIL] dump/{name}: expected '{probe}' in dump") + ok = False + if ok: + print(f"[PASS] dump/{name}: event stream + const pool dump ok") + return ok + + +def test_reject_out_of_subset(tmp: Path) -> bool: + """超 M1 直线子集 op(mod)→ 表模式拒绝编译(exit 1 + 'needs more events')。""" + try: + ccr = compile_ccr(tmp, "reject_mod", MOD_SRC) + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(TABLE), "-o", str(tmp / "reject_mod.out")]) + except RuntimeError as e: + print(f"[FAIL] reject: {e}") + return False + if r.returncode == 0: + print("[FAIL] --table on IR_BINARY(OP_MOD) should exit 1 (needs more events)") + print(r.stdout + r.stderr) + return False + if "needs more events" not in (r.stdout + r.stderr): + print("[FAIL] expected 'needs more events' error message") + print(r.stdout + r.stderr) + return False + print("[PASS] --table on out-of-subset op -> exit 1 with 'needs more events'") + return True + + +def test_smoke_files() -> bool: + """tests/hit/smoke_add.cr + smoke_mem.cr 端到端(源文件载体,Task 4 冒烟同源)。""" + ok = True + for src_p, rc, tag in ((SMOKE_ADD, 2, "smoke_add"), (SMOKE_MEM, 5, "smoke_mem")): + if not src_p.exists(): + print(f"[FAIL] {tag}: missing source {src_p}") + ok = False + continue + with tempfile.TemporaryDirectory(prefix="hit_smoke_") as td: + tmp = Path(td) + ccr_p = tmp / f"{tag}.ccr" + r = run_bin(COREC, ["ccr", str(src_p), "-o", str(ccr_p), "--opt-level", OPT]) + if r.returncode != 0 or not ccr_p.exists(): + print(f"[FAIL] {tag}: corec ccr failed: {r.stdout + r.stderr}") + ok = False + continue + out_p = tmp / tag + r2 = run_bin(COREARCH, [str(ccr_p), "--elf", "--table", str(TABLE), "-o", str(out_p)]) + if r2.returncode != 0 or not out_p.exists(): + print(f"[FAIL] {tag}: corearch --table failed: {r2.stdout + r2.stderr}") + ok = False + continue + got = run_elf(out_p) + if got != rc: + print(f"[FAIL] {tag}: table ELF exit {got}, expected {rc}") + ok = False + continue + print(f"[PASS] {tag}: --table run exit {rc}") + return ok + + +def test_table_load_missing_file(tmp: Path) -> bool: + """表文件缺 → load 失败即退(exit 1),即使给了有效 .ccr。""" + ccr = compile_ccr(tmp, "missing_tbl", SUB_SRC) + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(tmp / "no" / "such" / "table.toml")]) + if r.returncode == 0: + print("[FAIL] corearch --table should exit 1") + print(r.stdout + r.stderr) + return False + print("[PASS] corearch --table -> exit != 0") + return True + + +def test_reject_table_with_link_shared(tmp: Path) -> bool: + """M2-1: --table × --link/--shared 显式拒绝(池 mov rip disp 链接路径未测试)。""" + try: + ccr = compile_ccr(tmp, "rej_tab_link", SUB_SRC) + except RuntimeError as e: + print(f"[FAIL] reject-combo: {e}") + return False + ok = True + for extra in (["--link", "auto"], ["--link", "x.so"], ["--shared"]): + out_p = tmp / "rej_combo.out" + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(TABLE)] + extra + + ["-o", str(out_p)]) + if r.returncode == 0: + print(f"[FAIL] --table with {' '.join(extra)} should exit 1 (explicit reject)") + print(r.stdout + r.stderr) + ok = False + continue + if "cannot be combined with --link/--shared" not in r.stderr: + print(f"[FAIL] --table with {' '.join(extra)}: expected rejection msg on stderr") + print("stderr:", r.stderr) + print("stdout:", r.stdout) + ok = False + continue + print(f"[PASS] --table with {' '.join(extra)} -> exit 1 (stderr reject)") + return ok + + +def test_dump_sentinel_clean(tmp: Path) -> bool: + """M2-2a: 未用哨兵字段(store dst / load s2)= 0——dump 不得出现 255/−1 读回伪影。 + + 旧实现存 −1:自持 x86 截断除法写回单字节 0xFF → 读回 255;Python 解释地板 + 除四字节全 0xFF → 读回 0xFFFFFFFF——实现分裂,未用字段应恒存 0。 + """ + try: + ccr = compile_ccr(tmp, "dump_sentinel", MEM_SRC) + dump_out = tmp / "dump_sentinel_out" + r = emit_bin(ccr, dump_out, table=True, dump=True) + except RuntimeError as e: + print(f"[FAIL] dump/sentinel: {e}") + return False + out = r.stdout + r.stderr + if "hit pool entries: " not in out: + print("[FAIL] dump/sentinel: missing pool summary") + return False + bad = [s for s in ("dst=255", "s2=255", "dst=-1", "s2=-1") if s in out] + if bad: + print(f"[FAIL] dump/sentinel: unused-field readback artifacts {bad} in dump") + print(out) + return False + print("[PASS] dump/sentinel: unused fields (store dst / load s2) = 0, no 255/-1 artifacts") + return True + + +def test_table_bad_opcode_range(tmp: Path) -> bool: + """评审项:opcode >255 不得静默截断——表数据值域校验拒绝(exit 1)。 + + M2a Task 1 修订:不再对真实表做文本手术([0x4D, 0x29] 锚在 v1→v2 迁移后 + 消失)——改为在表尾追加一个 v1 语法事件(id 50,opcode = [0x300])。 + """ + ccr = compile_ccr(tmp, "bad_opcode_tbl", SUB_SRC) + bad = tmp / "bad_opcode.toml" + content = TABLE.read_text() + ( + "\n[[event]]\nid = 50\nname = \"badop\"\ninputs = 0\noutputs = 0\n" + "side_effect = \"pure\"\n\n[[event.proj]]\nisa = \"x86-64\"\n" + "opcode = [0x300]\nmodrm_reg_role = \"dst\"\nmodrm_rm_role = \"src2\"\n" + "rm_mode = 0\n") + bad.write_text(content) + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(bad)]) + if r.returncode == 0: + print("[FAIL] corearch --table 255> should exit 1 (no silent truncation)") + print(r.stdout + r.stderr) + return False + if "opcode byte >255" not in (r.stdout + r.stderr): + print("[FAIL] expected 'opcode byte >255' error message") + print(r.stdout + r.stderr) + return False + print("[PASS] corearch --table 255> -> exit 1 with error") + return True + + +def test_hit_lower_sources_check_clean() -> bool: + """hit.cr / lower_to_core.cr 独立 corec check 干净。""" + ok = True + for f in (HIT_SRC, LOWER_SRC): + if not f.exists(): + print(f"[FAIL] missing source: {f}") + ok = False + continue + r = run_bin(COREC, ["check", str(f)]) + if r.returncode != 0: + print(f"[FAIL] corec check {f.name} exit={r.returncode}") + print(r.stdout + r.stderr) + ok = False + else: + print(f"[PASS] corec check {f.name} -> exit 0") + return ok + + +# ════════════════════════════════════════════════════════════════ +# M2a Task 2:事件流注入测试通道(--hit-events-file)——模板解释器 v2 形态 +# 发射的对照载体。通道 = --dump-events 逆格式文本文件:`ev [dst=N] +# [s1=[pool]N] [s2=N]` 每事件一行(操作数缺省 = 从所附着指令取——附着规则见 +# lower_to_core.cr hit_inject_events 注释)+ `pool v0 v1..` 池值行。注入取代 +# 降低(跳过 lower 直接 emit);只映射到指令的某条事件才走表(未列事件指令 = +# 旧路径)——同源程序表路径 vs 旧路径逐字节对照成立面 = 每事件字节 == 旧路径 +# 该指令字节(jump = E9 rel32;sub/load/store 形态 = v2 字段发射 + disp auto)。 +# ════════════════════════════════════════════════════════════════ + +# 注(MW 里程碑后):int add/sub 定值目标 = 规则 A 恒 tagged → mw 门整条落旧 +# 路径——sub/nand 事件在现架构对真 IR 不可达(M2b Task 6/8 快路径走表通道), +# 逐字节对照载体只用非门面指令:store/load(as 槽载入)/jump/cst。 +# jump 对照载体:if(无 else)体内 store 链 + 尾 jump 指向合并标签 = if 后首个 +# 事件指令(d 的 load——事件序 4)。跳转目标 = 事件序(jump 事件 s1 域)。 +JUMP_SRC = """fn main() -> int { + a : ., mut = 10; + b := 3; + if a > b { + c := a as int; + a = c; + } + d := a as int; + return d; +} +""" +JUMP_EVENTS = ["ev load", "ev store", "ev store", "ev jump s1=4", "ev load", "ev store"] +JUMP_RC = 10 # 10>3 真 → c=10、a=10 → d=10 + +# disp 对照载体:let 初始化 store ×2 + as 槽 load/store 链 + 变量移动 store +#(事件全部 auto disp8——旧路径 e2_ld/e2_st 同规则;固定 disp32 旧发射会红) +DISP_SRC = """fn main() -> int { + a : ., mut = 10; + b := 3; + c := a as int; + a = c; + d := b as int; + e := a as int; + a = d; + return a; +} +""" +DISP_EVENTS = ["ev store", "ev store", "ev load", "ev store", "ev store", + "ev load", "ev store", "ev load", "ev store", "ev store"] +DISP_RC = 3 # a ← d ← b = 3 + +# disp32 对照载体:20 槽变量(a16+ 槽偏移 < -128 → disp32;a15 = -128 边界 disp8) +BIG_EVENTS = ["ev store"] * 20 + ["ev store"] +BIG_RC = 18 # a19 = a18 + + +def big_src() -> str: + lines = ["fn main() -> int {"] + for i in range(20): + lines.append(f" a{i} : ., mut = {i};") + lines.append(" a19 = a18;") + lines.append(" return a19;") + lines.append("}") + return "\n".join(lines) + "\n" + + +# imm 对照载体:cst 事件(fixture 表事件 11 = C7 /0 [rbp+disp] imm32——与旧路径 +# const e2_li 同字节)。夹具 = 真实表 + 追加 cst 事件(events 9 → 10)。 +CST_FIX = TABLE.read_text().replace("events = 9", "events = 10", 1) + ( + "\n[[event]]\nid = 11\nname = \"cst\"\ninputs = 2\noutputs = 1\n" + "side_effect = \"pure\"\n\n[[event.proj]]\nisa = \"x86-64\"\n" + "[[event.proj.step]]\nrex_w = 1\nopcode = [0xC7]\nmodrm_reg_digit = 0\n" + "modrm_rm_role = \"dst\"\nrm_mode = 1\nimm_size = 4\nimm_role = \"src1\"\n") + +IMM_SRC = "fn main() -> int {\n a := 63;\n return a;\n}\n" +IMM_EVENTS = ["ev cst"] +IMM_RC = 63 + +# 池对照载体:const → 池 load(M1 语义——字节与旧路径 e2_li 有意不同,判据 = 运行) +POOL_SRC = "fn main() -> int {\n a := 7;\n b := 5;\n return a - b;\n}\n" +POOL_LINES = ["pool 7 5"] +POOL_EVENTS = ["ev load s1=pool0", "ev store", "ev load s1=pool1", "ev store"] +POOL_RC = 2 # a-b 指令 = mw 门旧路径——池载入 + 初始化 store 经表 + + +def emit_inject(tmp: Path, name: str, src: str, ev_lines, pool_lines=(), + table=TABLE): + """corearch --table --hit-events-file:.cr → .ccr → 注入发射。 + 返回 (r, inj_bytes|None, out_p)。""" + ccr = compile_ccr(tmp, name, src) + ev_p = tmp / f"{name}.events" + ev_p.write_text("\n".join(list(pool_lines) + list(ev_lines)) + "\n") + out_p = tmp / f"{name}_inj" + r = run_bin(COREARCH, [str(ccr), "--elf", "--opt-level", OPT, + "--table", str(table), + "--hit-events-file", str(ev_p), "-o", str(out_p)]) + return r, (out_p.read_bytes() if out_p.exists() else None), out_p + + +def _ev_count(r) -> int: + """'hit table: N events emitted' 中 N(无 → -1)。""" + out = r.stdout + r.stderr + m = out.find(" events emitted") + if m < 0: + return -1 + txt = out[:m].rsplit("hit table: ", 1)[-1].strip() + return int(txt) if txt.isdigit() else -1 + + +def test_inject_byte_compare(tmp: Path, tag: str, src: str, ev_lines, + expected_rc: int, pool_lines=(), table=TABLE) -> bool: + """注入通道逐字节对照:同源 .ccr 旧路径 ELF vs 注入表路径 ELF 全文件一致 + + 运行 exit 一致 + 注入事件全部经表发射(计数 == 文件事件行数)。""" + try: + ccr = compile_ccr(tmp, tag, src) + old_out = tmp / f"{tag}_old" + old_r = emit_bin(ccr, old_out, table=False) + old_b = old_out.read_bytes() + r, inj_b, out_p = emit_inject(tmp, tag + "_inj", src, ev_lines, + pool_lines=pool_lines, table=table) + except RuntimeError as e: + print(f"[FAIL] inject/{tag}: {e}") + return False + ok = True + + def chk(cond, msg): + nonlocal ok + if not cond: + print(f"[FAIL] inject/{tag}: {msg}") + print(" " + (r.stdout + r.stderr).replace("\n", "\n ")[:800]) + ok = False + + chk(r.returncode == 0 and inj_b is not None, "corearch --hit-events-file failed") + chk(_ev_count(r) == len(ev_lines), + f"events-emitted {_ev_count(r)} != file lines {len(ev_lines)}(有事件落旧路径?)") + chk(inj_b == old_b, f"injected ELF != old-path ELF ({len(old_b)} vs {len(inj_b or b'')} bytes)") + if ok: + old_rc = run_elf(old_out) + inj_rc = run_elf(out_p) + chk(old_rc == expected_rc and inj_rc == expected_rc, + f"run exit old={old_rc} inj={inj_rc} expected={expected_rc}") + if ok: + print(f"[PASS] inject/{tag}: {len(ev_lines)} 事件注入发射 == 旧路径逐字节 " + f"({len(old_b)}B) 且运行 exit {expected_rc}") + return ok + + +def test_inject_pool_run(tmp: Path) -> bool: + """池注入运行闭环:const → 池 load 事件(M1 语义)——池值入 rodata、运行 + exit 与旧路径一致(字节有意不同——const 走池 vs 旧路径 imm 直载)。""" + try: + r, inj_b, out_p = emit_inject(tmp, "pool", POOL_SRC, POOL_EVENTS, + pool_lines=POOL_LINES) + except RuntimeError as e: + print(f"[FAIL] inject/pool: {e}") + return False + ok = True + + def chk(cond, msg): + nonlocal ok + if not cond: + print(f"[FAIL] inject/pool: {msg}") + print(" " + (r.stdout + r.stderr).replace("\n", "\n ")[:800]) + ok = False + + chk(r.returncode == 0 and inj_b is not None, "corearch --hit-events-file failed") + chk(_ev_count(r) == len(POOL_EVENTS), f"events-emitted != {len(POOL_EVENTS)}") + if ok: + got = run_elf(out_p) + chk(got == POOL_RC, f"pool-injected ELF exit {got}, expected {POOL_RC}") + if ok: + print(f"[PASS] inject/pool: 池注入(pool 7 5)运行 exit {POOL_RC}") + return ok + + +def test_inject_rejects(tmp: Path) -> bool: + """注入通道拒绝面:坏行语法 / 未知事件名 / 不支持事件(branch = Task 3 前 + 无指令期望它)/ 越界 jump 目标序 → exit 1 + 错误消息。""" + cases = [ + ("bad_line", JUMP_SRC, ["ev sub dst=abc"], (), "bad dst"), + ("unknown_event", JUMP_SRC, ["ev bogus"], (), "unknown event"), + ("unsupported_branch", JUMP_SRC, ["ev store", "ev branch dst=0 s1=1 s2=0"], + (), "no instruction"), + ("jump_target_oob", JUMP_SRC, ["ev load", "ev jump s1=9", "ev store"], + (), "out of range"), + ("no_table", JUMP_SRC, ["ev store"], (), "needs --table"), + ] + ok = True + for tag, src, ev_lines, pool_lines, token in cases: + try: + ccr = compile_ccr(tmp, "rej_" + tag, src) + ev_p = tmp / f"rej_{tag}.events" + ev_p.write_text("\n".join(list(pool_lines) + list(ev_lines)) + "\n") + args = [str(ccr), "--elf", "--opt-level", OPT, "-o", str(tmp / "rej.out")] + if tag != "no_table": + args = [str(ccr), "--elf", "--opt-level", OPT, "--table", str(TABLE), + "--hit-events-file", str(ev_p), "-o", str(tmp / "rej.out")] + else: + args += ["--hit-events-file", str(ev_p)] + r = run_bin(COREARCH, args) + except RuntimeError as e: + print(f"[FAIL] reject/{tag}: {e}") + ok = False + continue + out = r.stdout + r.stderr + if r.returncode == 0 or token not in out: + print(f"[FAIL] reject/{tag}: expected exit 1 + {token!r}") + print(" " + out.replace("\n", "\n ")[:600]) + ok = False + continue + print(f"[PASS] reject/{tag}: exit 1 with {token!r}") + return ok + + +# ════════════════════════════════════════════════════════════════ +# M2a Task 1:schema v2 加载 walker + 字段解析断言(TDD:v2 加载器未实现前红) +# ════════════════════════════════════════════════════════════════ +# 静态夹具(自包含,不随真实表迁移变动): +# F1 = v1 子集语法事件 1-4(兼容面:M1 表形态 = v2 子集,加载不变) +# + v2 语法事件 5-9(jump/branch/call/ret/extern——真实 x86 投影声明, +# 语义绑定 = Task 4 降低对照定稿)+ [runtime.x86] 小节 +# F2 = F1 + 事件 3 第二 proj(多 proj 条目) +# F3 = F1 + 事件 10(v2 全字段:prefix/rex 全位/3 字节 opcode/SIB/digit/imm/rel8/cond) + +V2_TABLE_HEAD = """[table] +name = "core-x86-min" +events = 9 # 运行时核:1-4 数据 + 5-9 控制/调用/系统(M2a) +""" + +# 事件 1-4:v1 子集语法(字段直置 proj 层——M2a 兼容面,等价迁移前表) +V2_EV1_4 = """ +[[event]] +id = 1 # sub +name = "sub" +arith = "sub" # 语义锚:规约层引用(加载不消费) +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +opcode = [0x4D, 0x29] # REX.W+R+B + sub r/m64,r64(v1 行内字节惯例) +modrm_reg_role = "src2" +modrm_rm_role = "dst" +rm_mode = 0 + +[[event]] +id = 2 +name = "nand" +inputs = 2 +outputs = 1 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +opcode = [0x4D, 0x21] # and r/m64,r64(nand 单步占位——M1 语义不变) +modrm_reg_role = "src2" +modrm_rm_role = "dst" +rm_mode = 0 + +[[event]] +id = 3 +name = "load" +inputs = 1 +outputs = 1 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +opcode = [0x4C, 0x8B] # REX.W+R + mov r64, r/m64(rm = rbp+disp32 槽寻址) +modrm_reg_role = "dst" +modrm_rm_role = "addr" +rm_mode = 1 + +[[event]] +id = 4 +name = "store" +inputs = 2 +outputs = 0 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +opcode = [0x4C, 0x89] # REX.W+R + mov r/m64, r64(写 [rbp+disp32] ← val) +modrm_reg_role = "val" +modrm_rm_role = "addr" +rm_mode = 1 +""" + +# 事件 5-9:v2 语法([[event.proj.step]] 步小节——多步序列表达) +V2_EV5_9 = """ +[[event]] +id = 5 +name = "jump" +inputs = 1 # 目标 = 事件流位置(IR_JUMP s1 → 标签映射) +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +# jump = E9 rel32(无条件转移;rel kind = event → 事件流位置回填表) +[[event.proj.step]] +opcode = [0xE9] +rel_role = "src1" +rel_kind = "event" +rel_size = 32 + +[[event]] +id = 6 +name = "branch" +inputs = 3 # 条件 s1(≠0 真)、真目标 s2、假目标 s3 +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +# branch = cmp + jne 两步序列:cmp r10(=条件载入), 0 → ZF=(s1==0); +# jne(ZF=0 → 条件≠0 → 真目标 s2)。假目标 = 事件流下一条(相邻跳过选型 = +# 降低层 Task 4 定稿——此处仅模板声明) +[[event.proj.step]] +rex_w = 1 # REX.W+B(rm = r10 需 B 位;/7 digit 组不得 REX.R) +rex_b = 1 +opcode = [0x83] # 83 /7 ib = cmp r/m64, imm8 +modrm_reg_digit = 7 +modrm_rm_role = "dst" +rm_mode = 0 +imm_size = 1 +imm_role = "lit" +imm_lit = 0 +cond_role = "src1" # 条件源槽(判定语义 Task 4 对照定稿) + +[[event.proj.step]] +opcode = [0x0F, 0x85] # jne rel32(ZF=0 → 真目标) +rel_role = "src2" +rel_kind = "event" +rel_size = 32 + +[[event]] +id = 7 +name = "call" +inputs = 1 # 目标 = 函数索引(.ccr SYM func 行序) +outputs = 0 # 值返回 = 调用点序列槽协议(R2/R7 算法保留代码,Task 4) +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +# call = E8 rel32(rel kind = func → 函数体事件流位置回填表) +[[event.proj.step]] +opcode = [0xE8] +rel_role = "src1" +rel_kind = "func" +rel_size = 32 + +[[event]] +id = 8 +name = "ret" +inputs = 0 +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +# ret = C3(调用栈机制 = [runtime.x86] 条目;返回序列 = R7 算法保留代码) +[[event.proj.step]] +opcode = [0xC3] + +[[event]] +id = 9 +name = "extern" +inputs = 1 # 外部符号 +outputs = 0 +side_effect = "effect" + +[[event.proj]] +isa = "x86-64" +# extern = E8 rel32 + 外部重定位标记(rel kind = extern → ext_rel 同族回填) +[[event.proj.step]] +opcode = [0xE8] +rel_role = "src1" +rel_kind = "extern" +rel_size = 32 +""" + +V2_RUNTIME = """ +[runtime.x86] +# 运行时条目最小描述(spec §6):x86-64 一档。现行约定 = SysV 寄存器传参 +# (rdi/rsi/rdx/rcx/r8/r9——elf.cr R2 参数拷贝:callee 帧槽保存,与分配器一致); +# save 集 = callee-saved rbx/r12-15;extern = call rel32 + ext_rel 符号解析 +call_mechanism = "stack" +call_ret_addr = "pushed" +call_stack_grow = "down" +param_mode = "regs" +param_regs = "rdi rsi rdx rcx r8 r9" +save_regs = "rbx r12 r13 r14 r15" +extern_entry = "call_rel32" +extern_resolve = "ext_rel" +""" + +V2_FIX = V2_TABLE_HEAD + V2_EV1_4 + V2_EV5_9 + V2_RUNTIME + +V2_F2 = V2_FIX.replace( + "[[event]]\nid = 4", + "[[event.proj]] # 事件 3 第二形态(M2a 多 proj 条目:rm_mode 0 寄存器寻址形——\n" + "isa = \"x86-64\" # 形态占位,选择语义 = 发射器 Task 9+;proj0 = M1 兼容视区)\n" + "opcode = [0x49, 0x8B]\n" + "modrm_reg_role = \"dst\"\n" + "modrm_rm_role = \"addr\"\n" + "rm_mode = 0\n" + "\n[[event]]\nid = 4", + 1, +) + +V2_SINK = """ +[[event]] +id = 10 +name = "sink" +inputs = 2 +outputs = 1 +side_effect = "effect" + +[[event.proj]] # 形态 A:rm_mode 2 SIB 族 + rex 全位 + 3 字节 opcode(域解析 +isa = "x86-64" # 载体——movbe r64,m64 真实字节;绑定 = 数据化批 3 E 族索引寻址) +rex_w = 1 +rex_r = 1 +rex_x = 1 +rex_b = 1 +opcode = [0x0F, 0x38, 0xF0] +modrm_reg_role = "dst" +modrm_rm_role = "addr" +rm_mode = 2 +sib_scale = 3 +sib_index_role = "src2" +sib_base_role = "addr" +disp_size = 4 +disp_src = "addr" + +[[event.proj]] # 形态 B:/digit + imm 字面量(C7 /0 mov r/m64, imm32 域—— +isa = "x86-64" # 绑定 = 数据化批 1 B1 imm32→槽;imm_lit = 63 字面量) +rex_w = 1 +opcode = [0xC7] +modrm_reg_digit = 0 +modrm_rm_role = "dst" +rm_mode = 1 +imm_size = 4 +imm_role = "lit" +imm_lit = 63 + +[[event.proj]] # 形态 C:前缀 + rel8 + cond(前缀域解析载体:66 90 = 2 字节 +isa = "x86-64" # nop 真实字节——消费面 = SSE/串行系 Task 12/13 预留) +prefix = [0x66] +opcode = [0x90] + +[[event.proj]] # 形态 D:rel8(jne rel8——G2 rel.size {8|32} 的 8 侧) +isa = "x86-64" +opcode = [0x75] +rel_role = "src2" +rel_kind = "event" +rel_size = 8 +cond_role = "src1" + +[[event.proj]] # 形态 E:imm64 回填 kind(movabs rax, [fnaddr] 域——B2 movabs +isa = "x86-64" # imm64 回填 kind = fnaddr;reg 嵌 opcode 字面量 = Task 12 扩展) +rex_w = 1 +opcode = [0x48, 0xB8] +imm_size = 8 +imm_kind = "fnaddr" +""" + +V2_F3 = V2_FIX.replace("events = 9", "events = 10", 1) + V2_SINK + +# 事件 10:单事件双 proj、双双用步小节(Task 1 评审项 1 锁——步小节解析循环 +# 不得越过 [[event.proj]] 头续行吸收后续投影步小节:旧缺陷 p0 步数吞并 p1 +# 的步 + 步记录重复入库,加载静默成功但表已错)。proj0/1 步字节取互异 1 字节 +# opcode(90-93),读回即可判别归属。 +V2_SECSEC = """ +[[event]] +id = 10 +name = "secsec" +inputs = 0 +outputs = 0 +side_effect = "pure" + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0x90] +[[event.proj.step]] +opcode = [0x91] + +[[event.proj]] +isa = "x86-64" +[[event.proj.step]] +opcode = [0x92] +[[event.proj.step]] +opcode = [0x93] +""" + +V2_F4 = V2_FIX.replace("events = 9", "events = 10", 1) + V2_SECSEC + + +def _write_fixture(tmp: Path, name: str, content: str) -> Path: + p = tmp / f"{name}.toml" + p.write_text(content) + return p + + +# ── schema v2 walker:独立 Python 结构校验(与 hit.cr 加载器双实现互证) ── + +_STEP_KEYS = { + "prefix", "opcode", "rex_w", "rex_r", "rex_x", "rex_b", + "modrm_reg_role", "modrm_reg_digit", "modrm_rm_role", "rm_mode", + "modrm_base_role", "sib_scale", "sib_index_role", "sib_base_role", + "disp_size", "disp_src", "imm_size", "imm_role", "imm_lit", "imm_kind", + "rel_role", "rel_kind", "rel_size", "cond_role", +} +_EVENT_KEYS = {"id", "name", "arith", "inputs", "outputs", "side_effect"} +_PROJ_KEYS = {"isa"} +_RUNTIME_KEYS = { + "call_mechanism", "call_ret_addr", "call_stack_grow", + "param_mode", "param_regs", "save_regs", "extern_entry", "extern_resolve", +} +_ROLE_NAMES = {"dst", "src1", "src2", "addr", "val", "cond"} +_REG_NAMES = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"} +_ROLE_OR_REG = _ROLE_NAMES | _REG_NAMES + + +def _val_token(s: str) -> str: + """取原始值(剥行内注释/空白)——int 列表取 [..],字符串取 ".."。""" + s = s.strip() + if s.startswith('"'): + end = s.find('"', 1) + if end < 0: + raise ValueError(f"unterminated string: {s!r}") + return s[: end + 1] + if s.startswith("["): + end = s.find("]") + if end < 0: + raise ValueError(f"unterminated list: {s!r}") + return s[: end + 1] + for i, ch in enumerate(s): + if ch in " #\t": + return s[:i] + return s + + +def _tbl_int(s: str) -> int: + s = _val_token(s) + return int(s, 0) if s.startswith(("0x", "0X")) else int(s) + + +def _tbl_int_list(s: str) -> list: + s = _val_token(s).strip() + if not (s.startswith("[") and s.endswith("]")): + raise ValueError(f"not an int list: {s!r}") + inner = s[1:-1].strip() + if not inner: + return [] + return [_tbl_int(t) for t in inner.split(",")] + + +def _tbl_str(s: str) -> str: + s = _val_token(s).strip() + if not (len(s) >= 2 and s[0] == '"' and s[-1] == '"'): + raise ValueError(f"not a string: {s!r}") + return s[1:-1] + + +def load_v2_model(text: str): + """节式解析 core-x86.toml → (table dict, events, runtime dict|None)。 + event = {fields, projs:[{fields, steps:[fields-dict...]}]};proj 无步小节 + (flat v1 子集)时 steps 为空、字段留在 proj.fields。""" + table, events, runtime = {}, [], None + cur_ev = cur_proj = cur_step = None + for raw in text.splitlines(): + line = raw.strip() + if not line or line.startswith("#"): + continue + if line == "[table]": + cur_ev = cur_proj = cur_step = None + continue + if line == "[[event]]": + cur_ev = {"fields": {}, "projs": []} + events.append(cur_ev) + cur_proj = cur_step = None + continue + if line == "[[event.proj]]": + cur_proj = {"fields": {}, "steps": []} + cur_ev["projs"].append(cur_proj) + cur_step = None + continue + if line == "[[event.proj.step]]": + cur_proj["steps"].append({}) + cur_step = cur_proj["steps"][-1] + continue + if line.startswith("[runtime.") and line.endswith("]"): + runtime = {} + cur_ev = cur_proj = cur_step = None + continue + if "=" not in line: + raise ValueError(f"malformed line: {line!r}") + k, _, v = line.partition("=") + if cur_step is not None: + cur_step[k.strip()] = v.strip() + elif cur_proj is not None: + cur_proj["fields"][k.strip()] = v.strip() + elif cur_ev is not None: + cur_ev["fields"][k.strip()] = v.strip() + elif runtime is not None: + runtime[k.strip()] = v.strip() + else: + table[k.strip()] = v.strip() + return table, events, runtime + + +def _ev(events, eid: int): + for e in events: + if _tbl_int(e["fields"]["id"]) == eid: + return e + return None + + +def _chk_step_fields(sf, where, probs): + """单步字段值域 + 关联规则(与 hit.cr v2 加载器规则镜像)。""" + for k in sf: + if k not in _STEP_KEYS: + probs.append(f"{where}: unknown key {k!r}") + # opcode:必选、1..3 字节、每字节 0..255 + if "opcode" not in sf: + probs.append(f"{where}: missing opcode") + else: + try: + op = _tbl_int_list(sf["opcode"]) + except ValueError as e: + probs.append(f"{where}: opcode parse: {e}") + op = [] + if not (1 <= len(op) <= 3): + probs.append(f"{where}: opcode count {len(op)} not 1..3") + for b in op: + if not (0 <= b <= 255): + probs.append(f"{where}: opcode byte {b} > 255") + # prefix:≤2 字节、每字节 ≤255 + if "prefix" in sf: + try: + pfx = _tbl_int_list(sf["prefix"]) + except ValueError as e: + probs.append(f"{where}: prefix parse: {e}") + pfx = [] + if len(pfx) > 2: + probs.append(f"{where}: prefix count {len(pfx)} > 2") + for b in pfx: + if not (0 <= b <= 255): + probs.append(f"{where}: prefix byte {b} > 255") + # rex 位 ∈ {0,1} + for rk in ("rex_w", "rex_r", "rex_x", "rex_b"): + if rk in sf: + try: + rv = _tbl_int(sf[rk]) + except ValueError as e: + probs.append(f"{where}: {rk} parse: {e}") + continue + if rv not in (0, 1): + probs.append(f"{where}: {rk} = {rv} not 0/1") + # modrm 存在性:任一 modrm/sib/disp 键 ⇒ reg 侧(role|digit)+ rm_role 齐备 + modrm_keys = [k for k in sf if k.startswith(("modrm_", "rm_mode", "sib_", "disp_"))] + has_role = "modrm_reg_role" in sf + has_digit = "modrm_reg_digit" in sf + if modrm_keys: + if not has_role and not has_digit: + probs.append(f"{where}: modrm keys present but no reg role/digit") + if "modrm_rm_role" not in sf: + probs.append(f"{where}: modrm keys present but no rm role") + if has_role and has_digit: + probs.append(f"{where}: reg_role and reg_digit both present") + elif "rm_mode" in sf: + probs.append(f"{where}: rm_mode without modrm roles") + # rm_mode 0..2 + 模式关联规则 + rm_mode = _tbl_int(sf["rm_mode"]) if "rm_mode" in sf else 0 + if rm_mode not in (0, 1, 2): + probs.append(f"{where}: rm_mode {rm_mode} not 0..2") + if rm_mode == 2 and ("sib_scale" not in sf or "sib_base_role" not in sf): + probs.append(f"{where}: rm_mode 2 needs sib_scale and sib_base_role") + for k in ("sib_scale", "sib_index_role", "sib_base_role"): + if k in sf and rm_mode != 2: + probs.append(f"{where}: {k} outside rm_mode 2") + if "modrm_base_role" in sf and rm_mode == 0: + probs.append(f"{where}: modrm_base_role needs rm_mode 1/2") + for k in ("disp_size", "disp_src"): + if k in sf and rm_mode == 0: + probs.append(f"{where}: {k} needs rm_mode 1/2") + if "sib_scale" in sf: + sv = _tbl_int(sf["sib_scale"]) + if sv not in (0, 1, 2, 3): + probs.append(f"{where}: sib_scale {sv} not 0..3") + # 角色名值域(base 键额外收 rip) + for k in ("modrm_reg_role", "modrm_rm_role", "disp_src", "rel_role", "cond_role"): + if k in sf: + nm = _tbl_str(sf[k]) + if nm not in _ROLE_OR_REG: + probs.append(f"{where}: {k} bad role {nm!r}") + for k in ("modrm_base_role", "sib_base_role"): + if k in sf: + nm = _tbl_str(sf[k]) + if nm not in _ROLE_OR_REG and nm != "rip": + probs.append(f"{where}: {k} bad base {nm!r}") + if "sib_index_role" in sf: + nm = _tbl_str(sf["sib_index_role"]) + if nm not in _ROLE_OR_REG: + probs.append(f"{where}: sib_index_role bad {nm!r}") + if has_digit: + dv = _tbl_int(sf["modrm_reg_digit"]) + if not (0 <= dv <= 7): + probs.append(f"{where}: reg digit {dv} not 0..7") + # disp + if "disp_size" in sf: + ds = sf["disp_size"].strip() + if ds not in ('"auto"', "1", "4"): + probs.append(f"{where}: disp_size {ds!r} not auto/1/4") + # imm 组 + if "imm_size" in sf: + iv = _tbl_int(sf["imm_size"]) + if iv not in (1, 2, 4, 8): + probs.append(f"{where}: imm_size {iv} not 1/2/4/8") + if "imm_role" not in sf and "imm_kind" not in sf: + probs.append(f"{where}: imm_size without imm_role/imm_kind") + if "imm_role" in sf: + nm = _tbl_str(sf["imm_role"]) + if nm != "lit" and nm not in _ROLE_OR_REG: + probs.append(f"{where}: imm_role bad {nm!r}") + if "imm_lit" in sf: + lv = _tbl_int(sf["imm_lit"]) + if lv < 0: + probs.append(f"{where}: imm_lit negative ({lv}) unsupported") + if "imm_kind" in sf: + nm = _tbl_str(sf["imm_kind"]) + if nm != "fnaddr": + probs.append(f"{where}: imm_kind bad {nm!r}") + elif "imm_size" not in sf or _tbl_int(sf["imm_size"]) != 8: + probs.append(f"{where}: imm_kind fnaddr needs imm_size 8") + # rel 组:三者同现、kind 域、size 域 + rel_here = [k for k in ("rel_role", "rel_kind", "rel_size") if k in sf] + if rel_here and len(rel_here) != 3: + probs.append(f"{where}: rel fields incomplete ({sorted(rel_here)})") + if "rel_kind" in sf: + nm = _tbl_str(sf["rel_kind"]) + if nm not in ("event", "func", "extern"): + probs.append(f"{where}: rel_kind bad {nm!r}") + if "rel_size" in sf: + rv = _tbl_int(sf["rel_size"]) + if rv not in (8, 32): + probs.append(f"{where}: rel_size {rv} not 8/32") + + +def v2_walk(text: str) -> list: + """schema v2 全字段结构校验 → 问题串列表(空 = 通过)。""" + probs = [] + try: + table, events, runtime = load_v2_model(text) + except ValueError as e: + return [f"structure: {e}"] + # [table] + for k in table: + if k not in ("name", "events"): + probs.append(f"table: unknown key {k!r}") + if "name" not in table: + probs.append("table: missing name") + if "events" in table: + try: + want = _tbl_int(table["events"]) + except ValueError as e: + probs.append(f"table: events parse: {e}") + want = -1 + if want != len(events): + probs.append(f"table: events = {want} but {len(events)} event sections") + # 事件:id 唯一 + 每事件 ≥1 proj + 每 proj ≥1 步 + seen = {} + for e in events: + f = e["fields"] + for k in f: + if k not in _EVENT_KEYS: + probs.append(f"event: unknown key {k!r}") + if "id" not in f: + probs.append("event: missing id") + eid = -1 + else: + try: + eid = _tbl_int(f["id"]) + except ValueError: + probs.append("event: bad id") + eid = -1 + if eid < 1: + probs.append(f"event: id {eid} < 1") + if eid in seen: + probs.append(f"event: duplicate id {eid}") + seen[eid] = True + if "name" not in f: + probs.append(f"event {eid}: missing name") + if f.get("side_effect") not in ('"pure"', '"effect"'): + probs.append(f"event {eid}: side_effect {f.get('side_effect')!r} not pure/effect") + for k in ("inputs", "outputs"): + if k in f: + try: + if _tbl_int(f[k]) < 0: + probs.append(f"event {eid}: {k} < 0") + except ValueError as e: + probs.append(f"event {eid}: {k} parse: {e}") + projs = e["projs"] + if not projs: + probs.append(f"event {eid}: no projection") + for pi, p in enumerate(projs): + pf = p["fields"] + where = f"event {eid} proj {pi}" + for k in pf: + if k not in _PROJ_KEYS | _STEP_KEYS: + probs.append(f"{where}: unknown proj key {k!r}") + isa = pf.get("isa") + if isa != '"x86-64"': + probs.append(f"{where}: isa {isa!r} not x86-64") + if p["steps"]: + for k in pf: + if k != "isa": + probs.append(f"{where}: key {k!r} outside step sections") + steps = p["steps"] + else: + # flat v1 子集:isa 以外的字段 = 单步字段 + steps = [{k: v for k, v in pf.items() if k != "isa"}] + for si, sf in enumerate(steps): + _chk_step_fields(sf, f"{where} step {si}", probs) + # runtime 小节 + if runtime is not None: + for k in runtime: + if k not in _RUNTIME_KEYS: + probs.append(f"runtime.x86: unknown key {k!r}") + for k in _RUNTIME_KEYS: + if k not in runtime: + probs.append(f"runtime.x86: missing {k}") + want = {"call_mechanism": "stack", "call_ret_addr": "pushed", + "call_stack_grow": "down", "param_mode": "regs", + "extern_entry": "call_rel32", "extern_resolve": "ext_rel"} + for k, v in want.items(): + if k in runtime and runtime[k].strip() != f'"{v}"': + probs.append(f"runtime.x86: {k} = {runtime[k]!r} (want {v!r})") + for k in ("param_regs", "save_regs"): + if k in runtime: + try: + regs = _tbl_str(runtime[k]).split() + except ValueError as e: + probs.append(f"runtime.x86: {k} parse: {e}") + regs = [] + for r in regs: + if r not in _REG_NAMES: + probs.append(f"runtime.x86: {k} unknown register {r!r}") + return probs + + +def test_v2_walker_real_table() -> bool: + """真实 core-x86.toml(v2 迁移后)walker 全字段 + 事件 5-9 存在性/形态断言。""" + text = TABLE.read_text() + probs = v2_walk(text) + if probs: + print(f"[FAIL] walker: {TABLE} schema violations ({len(probs)}):") + for p in probs[:25]: + print(" " + p) + return False + _, events, runtime = load_v2_model(text) + ids = [_tbl_int(e["fields"]["id"]) for e in events] + if ids != list(range(1, 10)): + print(f"[FAIL] walker: event ids {ids}, expected 1..9 (运行时核事件 5-9 存在)") + return False + ok = True + + def chk(cond, msg): + nonlocal ok + if not cond: + print(f"[FAIL] walker: {msg}") + ok = False + + # 事件 1-4 迁移后 legacy 字节视区 = M1 模板字节(REX 组装 + opcode 拼接序) + for eid, want in ((1, [0x4D, 0x29]), (2, [0x4D, 0x21]), (3, [0x4C, 0x8B]), (4, [0x4C, 0x89])): + ev = _ev(events, eid) + sf = ev["projs"][0]["steps"][0] + rb = 0x40 + for bit, k in ((8, "rex_w"), (4, "rex_r"), (2, "rex_x"), (1, "rex_b")): + if k in sf: + rb |= bit * _tbl_int(sf[k]) + stream = ([_tbl_int(b) for b in _tbl_int_list(sf.get("prefix", "[]"))] + if "prefix" in sf else []) + if rb != 0x40: + stream.append(rb) + stream += _tbl_int_list(sf["opcode"]) + chk(stream[:2] == want, f"event {eid} legacy stream {stream[:2]} != {want} (M1 字节等价)") + # 5 jump:E9 rel32 kind=event + ev = _ev(events, 5) + st = ev["projs"][0]["steps"][0] + chk(_tbl_int_list(st["opcode"]) == [0xE9], "jump opcode != [0xE9]") + chk(_tbl_str(st["rel_role"]) == "src1" and _tbl_str(st["rel_kind"]) == "event" + and _tbl_int(st["rel_size"]) == 32, "jump rel fields != src1/event/32") + # 6 branch:2 步(digit+imm cmp / jne rel32) + ev = _ev(events, 6) + chk(len(ev["projs"]) == 1 and len(ev["projs"][0]["steps"]) == 2, "branch != 2 steps") + s0, s1 = ev["projs"][0]["steps"] + chk(_tbl_int(s0["modrm_reg_digit"]) == 7 and _tbl_int(s0["imm_lit"]) == 0 + and _tbl_str(s0["cond_role"]) == "src1", "branch step1 digit/imm/cond fields") + chk(_tbl_int_list(s1["opcode"]) == [0x0F, 0x85] and _tbl_str(s1["rel_kind"]) == "event", + "branch step2 != jne rel32") + # 7 call:E8 kind=func;8 ret:C3 无 modrm/rel + ev = _ev(events, 7) + st = ev["projs"][0]["steps"][0] + chk(_tbl_int_list(st["opcode"]) == [0xE8] and _tbl_str(st["rel_kind"]) == "func", + "call != E8 kind=func") + ev = _ev(events, 8) + st = ev["projs"][0]["steps"][0] + chk(_tbl_int_list(st["opcode"]) == [0xC3], "ret opcode != [0xC3]") + chk(not any(k.startswith(("modrm_", "rel_", "rm_mode")) for k in st), "ret has modrm/rel keys") + # 9 extern:E8 kind=extern + ev = _ev(events, 9) + st = ev["projs"][0]["steps"][0] + chk(_tbl_int_list(st["opcode"]) == [0xE8] and _tbl_str(st["rel_kind"]) == "extern", + "extern != E8 kind=extern") + # runtime:save 集 = callee-saved rbx/r12-15(spec §6) + save = set(_tbl_str(runtime["save_regs"]).split()) + chk(save == {"rbx", "r12", "r13", "r14", "r15"}, f"runtime save_regs {save} != rbx/r12-15") + chk(_tbl_str(runtime["call_mechanism"]) == "stack" + and _tbl_str(runtime["param_mode"]) == "regs" + and _tbl_str(runtime["extern_resolve"]) == "ext_rel", "runtime mechanism fields") + if ok: + print("[PASS] walker: core-x86.toml v2 全字段 + 事件 5-9 + runtime 小节校验通过") + return ok + + +def test_v2_fixture_load_f1(tmp: Path) -> bool: + """F1 夹具:v1 子集语法事件 1-4(兼容面)+ v2 事件 5-9 + runtime → 加载 9 events + + 事件 1-4 发射闭环(v1 子集模板在 v2 加载器下运行语义不变)。""" + try: + ccr = compile_ccr(tmp, "f1_sub", SUB_SRC) + f1 = _write_fixture(tmp, "f1", V2_FIX) + out_p = tmp / "f1_sub_tab" + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(f1), "-o", str(out_p)]) + except RuntimeError as e: + print(f"[FAIL] f1: {e}") + return False + out = r.stdout + r.stderr + if r.returncode != 0 or not out_p.exists(): + print("[FAIL] f1: corearch --table failed (expect load + emit ok)") + print(out) + return False + if "hit table loaded: 9 events" not in out: + print("[FAIL] f1: missing 'hit table loaded: 9 events'") + print(out) + return False + if "hit table: " not in out: + print("[FAIL] f1: missing 'hit table: N events emitted' summary") + print(out) + return False + got = run_elf(out_p) + if got != SUB_RC: + print(f"[FAIL] f1: v1-subset events under v2 loader run exit {got}, expected {SUB_RC}") + return False + print(f"[PASS] f1: v1 子集 + v2 事件 5-9 + runtime 加载(9 events)且运行 exit {SUB_RC}") + return True + + +def test_v2_fixture_multi_proj(tmp: Path) -> bool: + """F2 夹具:事件 3 多 proj 条目 → 加载通过(proj0 = M1 兼容视区,发射不变)。""" + try: + ccr = compile_ccr(tmp, "f2_sub", SUB_SRC) + f2 = _write_fixture(tmp, "f2", V2_F2) + out_p = tmp / "f2_sub_tab" + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(f2), "-o", str(out_p)]) + except RuntimeError as e: + print(f"[FAIL] f2: {e}") + return False + out = r.stdout + r.stderr + if r.returncode != 0 or not out_p.exists(): + print("[FAIL] f2: corearch --table failed") + print(out) + return False + if "hit table loaded: 9 events" not in out: + print("[FAIL] f2: missing 'hit table loaded: 9 events'") + print(out) + return False + got = run_elf(out_p) + if got != SUB_RC: + print(f"[FAIL] f2: multi-proj load run exit {got}, expected {SUB_RC}") + return False + print(f"[PASS] f2: 多 proj 条目加载(9 events)且 proj0 发射运行 exit {SUB_RC}") + return True + + +def test_v2_fixture_sink_fields(tmp: Path) -> bool: + """F3 夹具:事件 10 v2 全字段(prefix/rex 全位/3 字节 opcode/SIB/digit/imm/rel8/cond) + → 逐字段解析通过(加载 10 events)。""" + try: + ccr = compile_ccr(tmp, "f3_sub", SUB_SRC) + f3 = _write_fixture(tmp, "f3", V2_F3) + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(f3), "-o", str(tmp / "f3.out")]) + except RuntimeError as e: + print(f"[FAIL] f3: {e}") + return False + out = r.stdout + r.stderr + if r.returncode != 0 or "hit table loaded: 10 events" not in out: + print("[FAIL] f3: v2 全字段事件加载失败(期望 10 events 通过)") + print(out) + return False + print("[PASS] f3: v2 全字段解析(prefix/rex/SIB/digit/imm/rel8/cond)加载通过") + return True + + +def test_v2_bad_field_rejects(tmp: Path) -> bool: + """坏文件拒绝面:F1 逐字段注入坏值 → exit 1 + 对应错误消息(值域校验)。""" + try: + ccr = compile_ccr(tmp, "badf_sub", SUB_SRC) + except RuntimeError as e: + print(f"[FAIL] badf: {e}") + return False + variants = [ + # (tag, 旧串, 新串, 期望错误消息, 替换次数) + ("rex bit", "rex_w = 1", "rex_w = 2", "bad rex bit", 1), + ("rel kind", 'rel_kind = "event"', 'rel_kind = "bogus"', "bad rel kind", 1), + ("rel size", "rel_size = 32", "rel_size = 16", "bad rel size", 1), + ("disp size", "opcode = [0x4C, 0x8B]", 'opcode = [0x4C, 0x8B]\ndisp_size = "wide"', + "bad disp_size", 1), + ("opcode count", "opcode = [0x4C, 0x8B]", "opcode = [0x4C, 0x8B, 0x90, 0x90]", + "opcode >3 bytes", 1), + ("prefix count", "opcode = [0x4C, 0x89]", + "opcode = [0x4C, 0x89]\nprefix = [0x66, 0xF2, 0x67]", "prefix >2 bytes", 1), + ("imm size", "imm_size = 1", "imm_size = 3", "bad imm_size", 1), + ("reg digit", "modrm_reg_digit = 7", "modrm_reg_digit = 9", "bad reg digit", 1), + ("side effect", 'side_effect = "pure"', 'side_effect = "weird"', "bad side_effect", 1), + ("sib mode", "opcode = [0x4C, 0x89]", "opcode = [0x4C, 0x89]\nsib_scale = 1", + "sib fields need rm_mode 2", 1), + ("unknown key", 'modrm_reg_role = "dst"', 'modrm_reg_role = "dst"\nmodrm_reg_rol = "dst"', + "unknown key", 1), + ("runtime value", 'call_mechanism = "stack"', 'call_mechanism = "heap"', + "bad call_mechanism", 1), + ("dup event id", "id = 8", "id = 5", "duplicate event id", 1), + ("count mismatch", "events = 9", "events = 8", "mismatch", 1), + ] + ok = True + for tag, old, new, token, cnt in variants: + if V2_FIX.count(old) < cnt: + print(f"[FAIL] badf/{tag}: anchor {old!r} not found in fixture") + ok = False + continue + p = _write_fixture(tmp, f"badf_{tag.replace(' ', '_')}", V2_FIX.replace(old, new, cnt)) + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(p)]) + if r.returncode == 0 or token not in (r.stdout + r.stderr): + print(f"[FAIL] badf/{tag}: expected exit 1 + {token!r}") + print(" " + (r.stdout + r.stderr).replace("\n", "\n ")[:600]) + ok = False + continue + print(f"[PASS] badf/{tag}: exit 1 with {token!r}") + return ok + + +def test_v2_fixture_multi_step_sections(tmp: Path) -> bool: + """F4 夹具(Task 1 评审项 1 锁):单事件 ≥2 proj 且 ≥2 proj 用步小节 → + 加载后逐 proj 步数与逐步归属正确(p0 = 2 步,不得吸收 p1 的 90-93 步)。 + + 读回通道 = corearch --dump-table(hit_dump_table_state:proj 表记录逐条 + + 每步 v2 字段区 opcode 探针(108B 整条复制后偏移 32 读)+ runtime 槽掩码 + ——评审项 4 的 v2 字段区写读一致实证一并覆盖)。旧缺陷下本测试红: + ev=10 p0=4(吞并 92/93 两步)+ 额外 'p=0 s=2/3' 步行。""" + try: + ccr = compile_ccr(tmp, "f4_sub", SUB_SRC) + f4 = _write_fixture(tmp, "f4", V2_F4) + out_p = tmp / "f4_sub_tab" + r = run_bin(COREARCH, [str(ccr), "--elf", "--table", str(f4), + "--dump-table", "-o", str(out_p)]) + except RuntimeError as e: + print(f"[FAIL] f4: {e}") + return False + out = r.stdout + r.stderr + ok = True + + def chk(cond, msg): + nonlocal ok + if not cond: + print(f"[FAIL] f4: {msg}") + print(" " + out.replace("\n", "\n ")[:800]) + ok = False + + chk(r.returncode == 0 and out_p.exists(), "corearch --table failed") + chk("hit table loaded: 10 events" in out, "missing 'hit table loaded: 10 events'") + # proj 步数归属:p0/p1 各 2——旧缺陷 p0 吞并 p1 后 p0=4 + chk("hit proj: ev=10 projs=2 p0=2 p1=2" in out, + "ev10 proj counts != 'projs=2 p0=2 p1=2'(p0 吸收 p1 步?)") + # 逐步归属 + v2 字段区读回(opb = opcode 首字节十进制:90→144 … 93→147) + for tag in ("hit step: ev=10 p=0 s=0 opb=144", "hit step: ev=10 p=0 s=1 opb=145", + "hit step: ev=10 p=1 s=0 opb=146", "hit step: ev=10 p=1 s=1 opb=147"): + chk(tag in out, f"missing {tag}") + chk("hit step: ev=10 p=0 s=2" not in out, "ev10 p0 越界吸收 p1 步(s=2 出现)") + # 既有形态不受扰:branch(ev6)两步序列 p0=2;runtime 槽位掩码读回 + chk("hit proj: ev=6 projs=1 p0=2" in out, "ev6 branch 步数 != 2") + chk("hit rt: param_regs=966" in out, "runtime param_regs 读回 != 966(rdi rsi rdx rcx r8 r9)") + chk("hit rt: save_regs=61448" in out, "runtime save_regs 读回 != 61448(rbx r12-15)") + if ok: + print("[PASS] f4: 多 proj 步小节归属正确(p0=2 p1=2 无吸收)+ v2 字段区读回一致") + return ok + + +def main(): + if not COREC.exists() or not COREARCH.exists(): + print("[FAIL] missing build/corec or build/corearch (run build_selfhost_native.py first)") + return 1 + with tempfile.TemporaryDirectory(prefix="hit_t3_") as td: + tmp = Path(td) + results = [ + test_run_closure(tmp, "sub", SUB_SRC, SUB_RC, need_lowered=True), + test_run_closure(tmp, "add", ADD_SRC, ADD_RC, need_lowered=True), + test_run_closure(tmp, "mem", MEM_SRC, MEM_RC, need_lowered=True), + # 载体与 test_run_closure 同源;事件流 dump 断言(sub 事件 + 常量池 3 条目) + test_dump_events(tmp, "dump_add", ADD_SRC, expect_sub_ev=True, expect_nand_ev=False, expect_pool=3), + test_reject_out_of_subset(tmp), + test_smoke_files(), + test_table_load_missing_file(tmp), + test_table_bad_opcode_range(tmp), + test_reject_table_with_link_shared(tmp), + test_dump_sentinel_clean(tmp), + test_hit_lower_sources_check_clean(), + # M2a Task 1:schema v2(真实表 walker + v2 夹具 + 坏文件拒绝面) + test_v2_walker_real_table(), + test_v2_fixture_load_f1(tmp), + test_v2_fixture_multi_proj(tmp), + test_v2_fixture_sink_fields(tmp), + test_v2_fixture_multi_step_sections(tmp), + test_v2_bad_field_rejects(tmp), + # M2a Task 2:事件流注入通道 + v2 形态发射(跳转/disp/imm 逐字节对照) + test_inject_byte_compare(tmp, "simple", "fn main() -> int {\n a : ., mut = 7;\n return a;\n}\n", + ["ev store"], 7), + test_inject_byte_compare(tmp, "jump", JUMP_SRC, JUMP_EVENTS, JUMP_RC), + test_inject_byte_compare(tmp, "disp", DISP_SRC, DISP_EVENTS, DISP_RC), + test_inject_byte_compare(tmp, "bigframe", big_src(), BIG_EVENTS, BIG_RC), + test_inject_byte_compare(tmp, "imm", IMM_SRC, IMM_EVENTS, IMM_RC, + table=_write_fixture(tmp, "cst_fix", CST_FIX)), + test_inject_pool_run(tmp), + test_inject_rejects(tmp), + ] + passed = sum(results) + print(f"{passed}/{len(results)} passed") + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_iface_ops.py b/tests/selfhost/test_iface_ops.py new file mode 100644 index 00000000..9e05bd1e --- /dev/null +++ b/tests/selfhost/test_iface_ops.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +"""R2 P2b Task 4/5:接口查表接线的行为回归(checker.cr ← iface_registry 的 ops 列)。 + +Task 4 接线点(三层门形状,改动前原文逐字等价): + · 算术门 `checker.cr:1959`(ANY:至少一侧许可即通过;改动前 = `lt != TI_INT && lt != TI_DEX + && rt != TI_INT && rt != TI_DEX`)——表的 ADD..MOD 格**只含 int/dex**; + · 逻辑门 `checker.cr:1969`(ALL:每侧都须许可;改动前 = `(lt≠bool ∧ lt≠int) ∨ (rt≠bool ∧ rt≠int)`) + ——表的 AND/OR 格恰 {int, bool}(**dex 不在内**); + · 条件门 `checker.cr:2274`(ONE / IP_COND = bool|int)与 `checker.cr:2385`(ONE / IP_COND_BOOL + = **仅 bool**)——两条规则现状不同,不得合并。 + +Task 5 接线点(`t5_*` 段;**唯一** = 索引面兜底拒绝 → `IP_INDEX` 查表):门体即原 +`EC_TK_INDEX` 调用(码/文案/位置逐字不变),可达集 = 数组/切片/串三个**结果分支**之后的落空集, +与表的拒绝集逐行相等(selftest `idx.gate_deny_covers_fallback` 全类型行枚举钉死)。字段 +(`IP_FIELD`)/转换(`IP_AS`)/dyn(`IP_METHOD`)三面**未接线**:前两面在现状**无任何拒绝路径** +(EXPR_FIELD/EXPR_AS 全形 rc=0,门会退化成恒真空转);dyn 面的拒绝谓词是**逐具体行的方法表 +成员判定**(int/string 候选行今日也发 N08),类级门会抑制它 = 放宽。三者均为 P3 收紧旋钮。 + +判据口径 = **保语义**(零行为变化):正控 rc=0 / 负控 rc=1 + 错误码逐字不变 / 登记面(现状 +宽松面)断言「不动」。**关键守卫**(本批最易放宽的两条,见计划 Task 4 的「结构事实」): +把 PTR/STRING 填进算术门的 ADD 格 ⇒ ANY 门下 `permits(PTR,ADD)=1` 足以放行 ⇒ `*T + *T`、 +`"a" - "b"` 从 error[TB01] 变静默 —— 本文件的负控段把这两条钉死。 + +Task 6 段(`t6_*`;`res_type_node`/`res_call_type` 双份 TY→TI 映射合一 → 单表 `ty_code_to_ti`): +8 个站点的**域逐站不同**(注册链缺 CHAR/NEVER/7、extern 链含 CHAR、形参链缺 NEVER、 +iface 返回链值域是 TY_* 码…),且这些域差异是**载荷**的(`fn f() -> char` 在注册链 = unit、 +在 extern 链 = char)。本段用「逐条 (码,行号) 多元集」把差异钉死,防后续按「表 + 尾回落」 +一刀切(= 静默改判定);两表**唯一语义差 = NEVER 格**(res_call_type 侧 = unit,探针实测可达)。 + +登记面(现状宽松 = P3 收紧清单的预备队,本批**不得**顺手改): + · 算术门「任一侧数值即可」:`1 + [int;3]` / `"a" * 2` 静默为 int(无诊断); + · 一元透传:`!arr` / `-"a"` / `*x`(x: int)原样返回操作数类型,不校验; + · 比较不校验操作数:`"a" == 1` 恒 TI_BOOL; + · `as` 恒等透传;字段落空静默(`x.a`,x 非 struct)——后两条属 Task 5 面。 +""" + +import os +import re +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃/信号相关的子进程会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _check(source: str): + fd, path = tempfile.mkstemp(suffix=".cr", dir="/tmp") + with os.fdopen(fd, "w") as f: + f.write(source) + try: + return subprocess.run( + [str(COREC), "check", path], capture_output=True, text=True, + cwd=BASE, timeout=120, preexec_fn=_no_core_dump, + ) + finally: + try: + os.unlink(path) + except FileNotFoundError: + pass + + +def case_ok(name, source): + """正控 / 登记面:rc=0 且零 error[...] 诊断。""" + r = _check(source) + out = r.stdout + r.stderr + if r.returncode != 0 or "error[" in out: + print(f"[FAIL] {name}: expected rc=0 + no diagnostics, got rc={r.returncode}\n{out}") + return False + print(f"[PASS] {name}: rc=0") + return True + + +def case_reject(name, source, code): + """负控:rc=1 + 诊断码逐字不变(码 = 改动前的码,接线不得改判定/文案路径)。""" + r = _check(source) + out = r.stdout + r.stderr + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默放行 = 放宽面复活)\n{out}") + return False + if ("error[" + code + "]") not in out: + print(f"[FAIL] {name}: rc={r.returncode} 但缺 error[{code}]\n{out}") + return False + print(f"[PASS] {name}: rc={r.returncode} + error[{code}]") + return True + + +def case_diag_lines(name, source, expect): + """Task 6 站点域判据:rc + **逐条 (码, 行号)** 多元集相等。 + + 为什么需要行号:TY→TI 的 8 个站点里有 6 处是**内联链**(注册/形参/返回/iface 返回), + 它们对同一份源码可能各报一条同码诊断——「报了几条、报在哪一行」正是区分「哪一站点产出了 + 什么类型」的观测量(只数码会两说)。 + """ + r = _check(source) + out = r.stdout + r.stderr + got = sorted(re.findall(r"error\[([A-Z0-9]+)\]:[^\n]*\n --> (\d+):", out)) + want = sorted((c, str(l)) for c, l in expect) + if r.returncode != 1 or got != want: + print(f"[FAIL] {name}: rc={r.returncode} got={got} want={want}\n{out}") + return False + print(f"[PASS] {name}: rc=1 + {got}") + return True + + +def main(): + ok = [] + # ─── 正控(许可面):三个门各自的许可路径 ─── + ok.append(case_ok("pos_int_add", "fn main() -> int { x := 1 + 2; return x; }\n")) + ok.append(case_ok("pos_int_mod", "fn main() -> int { x := 7 % 3; return x; }\n")) + ok.append(case_ok("pos_dex_add", "fn main() -> int { d := 1.5 + 1; return 0; }\n")) + ok.append(case_ok("pos_dex_dominates", "fn main() -> int { d := 1 + 1.5; return 0; }\n")) + ok.append(case_ok("pos_str_concat", 'fn main() -> int { s := "a" + "b"; return 0; }\n')) + ok.append(case_ok("pos_str_concat_right", + 'fn main() -> int { s := 1 + "b"; return 0; }\n')) + ok.append(case_ok("pos_ptr_add_int", + "fn main() -> int { x : ., mut = 5; p := &x; q := p + 1; return 0; }\n")) + ok.append(case_ok("pos_int_add_ptr", + "fn main() -> int { x : ., mut = 5; p := &x; q := 1 + p; return 0; }\n")) + ok.append(case_ok("pos_ptr_diff", + "fn main() -> int { x : ., mut = 5; p := &x; q := &x; d := p - q; return d; }\n")) + ok.append(case_ok("pos_str_index", 'fn main() -> int { s := "ab"; c := s[0]; return c; }\n')) + ok.append(case_ok("pos_tuple_field", + "fn main() -> int { t := (1, 2); a := t . 0; return a; }\n")) + ok.append(case_ok("pos_logic_int", "fn main() -> int { b := 1 && true; return 0; }\n")) + ok.append(case_ok("pos_logic_bool", "fn main() -> int { b := true && false; return 0; }\n")) + ok.append(case_ok("pos_cond_if_int", "fn main() -> int { if 1 { return 1; } return 0; }\n")) + ok.append(case_ok("pos_cond_if_bool", "fn main() -> int { if true { return 1; } return 0; }\n")) + ok.append(case_ok("pos_cond_while_bool", + "fn main() -> int { n : ., mut = 0; while n < 3 { n = n + 1; } return n; }\n")) + ok.append(case_ok("pos_range_int", "fn main() -> int { b := 1..3; return 0; }\n")) + # ─── 负控(拒绝面,码逐字不变)─── + # **关键守卫 1**:`*T + *T` —— 门集合若含 PTR(ANY 语义)即静默放行 + ok.append(case_reject("neg_ptr_add_ptr", + "fn main() -> int { x : ., mut = 5; p := &x; q := &x; r := p + q; return 0; }\n", "TB01")) + # **关键守卫 2**:`"a" - "b"` —— 串拼接许可在 :1946 早退(只认 OP_ADD),门集合若含 STRING 即放行 + ok.append(case_reject("neg_str_sub_str", + 'fn main() -> int { s := "a" - "b"; return 0; }\n', "TB01")) + ok.append(case_reject("neg_str_mul_str", + 'fn main() -> int { s := "a" * "b"; return 0; }\n', "TB01")) + ok.append(case_reject("neg_arr_add_arr", + "fn main() -> int { a := [1,2,3]; b := [4,5,6]; c := a + b; return 0; }\n", "TB01")) + ok.append(case_reject("neg_char_add_char", + "fn main() -> int { c := 'a' + 'b'; return 0; }\n", "TB01")) + ok.append(case_reject("neg_logic_dex", + "fn main() -> int { b := 1.5 && true; return 0; }\n", "TC01")) + ok.append(case_reject("neg_logic_dex_or", + "fn main() -> int { b := 1.5 || false; return 0; }\n", "TC01")) + ok.append(case_reject("neg_logic_str", + 'fn main() -> int { b := "a" && true; return 0; }\n', "TC01")) + ok.append(case_reject("neg_cond_if_arr", + "fn main() -> int { if [1,2,3] { return 1; } return 0; }\n", "TC01")) + ok.append(case_reject("neg_cond_if_dex", + "fn main() -> int { if 1.5 { return 1; } return 0; }\n", "TC01")) + # `while` 与 `if` 现状**不同**(IP_COND_BOOL 只收 bool):int 在 if 收、在 while 拒 + ok.append(case_reject("neg_cond_while_int", + "fn main() -> int { while 1 { } return 0; }\n", "TC04")) + ok.append(case_reject("neg_cond_while_dex", + "fn main() -> int { while 1.5 { } return 0; }\n", "TC04")) + ok.append(case_reject("neg_cond_while_arr", + "fn main() -> int { while [1,2,3] { } return 0; }\n", "TC04")) + ok.append(case_reject("neg_range_str_ends", + 'fn main() -> int { b := "a".."b"; return 0; }\n', "TB01")) + # ─── 登记面(现状宽松;本批断言「不动」= 保留为表的全许可格,P3 才收紧)─── + ok.append(case_ok("reg_int_add_arr", + "fn main() -> int { a := [1,2,3]; c := 1 + a; return 0; }\n")) + ok.append(case_ok("reg_str_mul_int", 'fn main() -> int { s := "a" * 2; return 0; }\n')) + ok.append(case_ok("reg_arr_index_bool", + "fn main() -> int { a := [1,2,3]; c := a[true]; return 0; }\n")) + ok.append(case_ok("reg_not_arr", + "fn main() -> int { a := [1,2,3]; b := !a; return 0; }\n")) + ok.append(case_ok("reg_neg_str", 'fn main() -> int { s := "a"; t := -s; return 0; }\n')) + ok.append(case_ok("reg_deref_int", + "fn main() -> int { x := 5; y := *x; return 0; }\n")) + ok.append(case_ok("reg_cmp_unchecked", + 'fn main() -> int { b := "a" == 1; return 0; }\n')) + ok.append(case_ok("reg_as_identity", + 'fn main() -> int { s := "a"; x := s as int; return 0; }\n')) + + # ─── R2 P2b Task 5:容器面(**唯一接线点** = 索引兜底拒绝 → IP_INDEX 查表)─── + # 接线口径 = 与改动前**一字等价**:门体即原 `EC_TK_INDEX` 调用;可达集(= 三个结果分支 + # 之后的落空集)与表的拒绝集逐行相等(selftest `idx.gate_deny_covers_fallback` 钉死)。 + # 字段/转换/dyn 三面**未接线**(无拒绝路径 / 谓词不是类级位)——本段以「现状不动」钉住, + # 防后续把 `IP_FIELD`/`IP_METHOD`/`IP_AS` 硬接成收紧或放宽。 + S5 = "struct S { a: int }\n" + # 正控:三个结果分支(数组→元素 / 切片→元素 / 数组 range→切片) + ok.append(case_ok("t5_pos_arr_index", + "fn main() -> int { a := [1,2,3]; c := a[1]; return c; }\n")) + ok.append(case_ok("t5_pos_slice_index", + "fn main() -> int { a := [1,2,3]; s := a[0..2]; c := s[1]; return c; }\n")) + ok.append(case_ok("t5_pos_arr_range", + "fn main() -> int { a := [1,2,3]; s := a[1..2]; return 0; }\n")) + # 负控:非容器行一律 TK01(码/文案逐字不变)——给表的 IP_INDEX 加任一非容器类即静默放行 + ok.append(case_reject("t5_neg_index_int", + "fn main() -> int { x := 1; c := x[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_bool", + "fn main() -> int { x := true; c := x[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_dex", + "fn main() -> int { x := 1.5; c := x[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_char", + "fn main() -> int { c := 'a'[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_ptr", + "fn main() -> int { x : ., mut = 5; p := &x; c := p[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_named", + S5 + "fn main() -> int { s := S { a: 1 }; c := s[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_tuple", + "fn main() -> int { t := (1,2); c := t[0]; return 0; }\n", "TK01")) + ok.append(case_reject("t5_neg_index_dyn", + "fn main() -> int { d : dyn = 5; c := d[0]; return 0; }\n", "TK01")) + # 登记面(现状静默;本批断言「不动」= P3 收紧面): + # range 索引的非数组落空 `return TI_UNIT` 无诊断(表 IP_INDEX_RANGE **未接线**——门恒真空转) + ok.append(case_ok("t5_reg_str_range", + 'fn main() -> int { s := "ab"; t := s[0..1]; return 0; }\n')) + ok.append(case_ok("t5_reg_slice_range", + "fn main() -> int { a := [1,2,3]; s := a[0..2]; t := s[0..1]; return 0; }\n")) + ok.append(case_ok("t5_reg_int_range", + "fn main() -> int { x := 1; t := x[0..1]; return 0; }\n")) + # 字段落空静默(EXPR_FIELD 全形 rc=0:非 struct / 越界元组位 / 指针·数组·dyn 操作数) + ok.append(case_ok("t5_reg_int_field", + "fn main() -> int { x := 1; c := x.a; return 0; }\n")) + ok.append(case_ok("t5_reg_tuple_field_oob", + "fn main() -> int { t := (1,2); c := t . 5; return 0; }\n")) + ok.append(case_ok("t5_reg_ptr_field", + S5 + "fn main() -> int { s := S { a: 1 }; p := &s; c := p.a; return 0; }\n")) + ok.append(case_ok("t5_reg_dyn_field", + "fn main() -> int { d : dyn = 5; c := d.a; return 0; }\n")) + # dyn 面:现状**有**拒绝路径(逐具体行的方法表判定)——int/string 候选行也发 N08; + # 类级 IP_METHOD 门会**抑制**它们(= 放宽)⇒ 本段把现有 N08 钉红 + ok.append(case_reject("t5_dyn_int_method_missing", + "fn main() -> int { d : dyn = 5; d.nosuch(); return 0; }\n", "N08")) + ok.append(case_reject("t5_dyn_str_method_missing", + 'fn main() -> int { d : dyn = "a"; d.nosuch(); return 0; }\n', "N08")) + ok.append(case_reject("t5_dyn_struct_method_missing", + S5 + "fn main() -> int { s := S { a: 1 }; d : dyn = s; d.nosuch(); return 0; }\n", + "N08")) + + # ─── R2 P2b Task 6:res_type_node/res_call_type 双份 TY→TI 映射合一(单表 ty_code_to_ti)─── + # 口径 = **保语义(零行为变化)**:8 个站点的**域逐站不同且载荷**(探针组 F/R/B/A/C,见报告)—— + # 本段把「站点域差异」钉死,防后续按「表 + 尾回落」一刀切(那会把 S1 的 char/never 从 unit + # 静默改成 char/never、把 S3 的 char 从 char 改成 unit 等)。行号 = 诊断所在行(区分站点)。 + # ① 两表基型分支(res_type_node / res_call_type) + ok.append(case_ok("t6_R_dyn_decl", + "fn main() -> int { x : dyn = 5; return 0; }\n")) + # res_type_node 的 7 码格 ⇒ TI_DYN(dyn 方法分派据此走 N08 路径) + ok.append(case_reject("t6_R_dyn_cell_live", + "fn main() -> int { x : dyn = 5; x.nosuch(); return 0; }\n", "N08")) + ok.append(case_ok("t6_R_char_ret", + "fn f() -> char { return 'a'; }\nfn main() -> int { c := f(); return 0; }\n")) + # ② **两表唯一差异格 = NEVER**:res_call_type 对调用位点的 `-> never` 返回 unit(现状), + # res_type_node 对类型位 `x : never = 1` 映 TI_NEVER——后者由 t6_S1/S3/S4/S6 的排除面互为镜像 + # TF01 收口(2026-09-13):`fn g[T](a: T) -> never { loop { } }` 体以**无 break 的 loop** + # 收尾 ⇒ 不落空 ⇒ 站点 5 不再误报 TF01@1(该函数语义上确为 never 函数)。本用例的关注面 = + # 调用位点(`never` 在调用位点被当 unit ⇒ @raw_int 报 TF07@2),该面**不变**。 + ok.append(case_diag_lines("t6_C_never_call_unit", + "fn g[T](a: T) -> never { loop { } }\n" + "fn main() -> int { x := g(1); y := @raw_int(x); return 0; }\n", + [("TF07", 2)])) + # 对照(同一调用位点、`-> int`)::1615 路径确实在判(unit 兜底假设下 @raw_int 必报 TF07) + ok.append(case_ok("t6_C_int_call_ctrl", + "fn g[T](a: T) -> int { return 1; }\n" + "fn main() -> int { x := g(1); y := @raw_int(x); return 0; }\n")) + ok.append(case_ok("t6_C_never_param", + "fn f[T](a: T, b: never) -> int { return 1; }\n" + "fn main() -> int { return f(1, 2); }\n")) + # ③ 同族 6 处内联链:**逐站域**(同一形在不同站点产出不同类型 ⇒ 域差异载荷) + # S1(hotpatch/普通注册链,域缺 CHAR/NEVER/7)vs S3(extern 链,域含 CHAR):同形不同果 + ok.append(case_diag_lines("t6_S1_char_unit", + "fn f() -> char { return 'a'; }\n" + "fn main() -> char { x := f(); return x; }\n", + [("TF01", 2)])) + ok.append(case_ok("t6_S3_char_covered", + "extern fn f() -> char;\nfn main() -> char { x := f(); return x; }\n")) + # TF01 收口(2026-09-13):TF01@1 = 同为「无 break 的 loop 收尾」误报面(已修)。 + # R2 P6 Task 4a(E-11)**重钉**(2026-09-14):本用例改前钉的是**误报**——`f()` 的 + # 调用推断此前回 unit(S1 注册链把 NEVER 钳成 unit,checker.cr:1491)⇒ `x` = unit ⇒ + # `main`(`-> never`)体检查在返回位报 TF01@2。E-11 落地后调用推断 = never(直调 + # 站点守卫)⇒ `x` = never ⇒ 返回位豁免(checker.cr:2234)⇒ **诊断消失、rc=0**。 + # 改前 = 误报在册 / 改后 = 正确行为在册(RED 实跑:改前 rc=1 + TF01@2;改后 rc=0)。 + # **S1 链的域差异**(`:1491` 挡 NEVER)**仍然成立**,其证据面改由 t6_S3/S4/S5/S6 四钉子 + # + `type_selftest` 的 `t6.never_cell_diff` 承担,不在本用例。 + ok.append(case_ok("t6_S1_never_unit", + "fn f() -> never { loop { } }\n" + "fn main() -> never { x := f(); return x; }\n")) + ok.append(case_diag_lines("t6_S3_never_unit", + "extern fn f() -> never;\n" + "fn main() -> never { x := f(); return x; }\n", + [("TF01", 2)])) + # R2 P6 Task 4a(E-11,裁②并入):方法调用站点的 never 透传(姊妹站点 checker.cr:2694)。 + # 改前:`s.m()` 推断 = unit(mangled "S.m" 经同一注册链钳位 :1491)⇒ 误发 TA02; + # 改后:rc=0(守卫含 `fi_generic_count(fi_m) == 0` + `sym_type == unit` + 裸码 never)。 + ok.append(case_ok("t4a_method_never_call", + "struct S { a: int }\n" + "impl S { fn m(self: S) -> never { loop { } } }\n" + "fn main() -> int { s := S { a: 1 }; x : int = s.m(); return 0; }\n")) + # 登记钉(裁④ 的泛型对齐面):泛型方法**不动**(守卫含 `fi_generic_count(fi_m) == 0`, + # 与直调泛型面「调用推断 = unit」一致)——改前改后同为 TA02。 + ok.append(case_reject("t4a_method_generic_never_unit", + "struct S { a: int }\n" + "impl S { fn m[T](self: S, v: T) -> never { loop { } } }\n" + "fn main() -> int { s := S { a: 1 }; x : int = s.m(1); return 0; }\n", + "TA02")) + # S4(`check_func` 形参链,域缺 NEVER):形参符号类型 = unit(@raw_int 只收 dex/int/never) + ok.append(case_diag_lines("t6_S4_param_never_unit", + "fn f(b: never) -> int { x := @raw_int(b); return 0; }\n" + "fn main() -> int { return 0; }\n", + [("TF07", 1)])) + ok.append(case_ok("t6_S4_param_char", + "fn f(b: char) -> int { y : char = b; return 0; }\n" + "fn main() -> int { return 0; }\n")) + # S5(`check_func` 返回链,域含 NEVER)/S1 的 never 格排除面:`-> never` 声明自身报体检查 + ok.append(case_diag_lines("t6_S5_never_covered", + "fn f() -> never { return 1; }\nfn main() -> int { return 0; }\n", + [("TF01", 1)])) + # S6(iface 返回链,值域 = parser 写入的 TY_* 码;域缺 NEVER/7)——**显式化**撞车比较 + T6_S6 = "struct S { a: int }\n" + ok.append(case_ok("t6_S6_iface_int", + "interface Show { fn show(self) -> int; }\n" + T6_S6 + + "impl S { fn show(self: S) -> int { return 1; } }\n" + "fn f[T: Show](a: T) -> int { x := a.show(); return x; }\n" + "fn main() -> int { s := S { a: 1 }; return f(s); }\n")) + # TF01 收口(2026-09-13):TF01@3 = 同为「无 break 的 loop 收尾」误报面(已修); + # 本用例的关注面 = S6 iface 返回链(域缺 NEVER ⇒ @raw_int 报 TF07@4),该面**不变**。 + ok.append(case_diag_lines("t6_S6_iface_never_unit", + "interface Show { fn show(self) -> never; }\n" + T6_S6 + + "impl S { fn show(self: S) -> never { loop { } } }\n" + "fn f[T: Show](a: T) -> int { x := a.show(); y := @raw_int(x); return 0; }\n" + "fn main() -> int { s := S { a: 1 }; return f(s); }\n", + [("TF07", 4)])) + ok.append(case_ok("t6_S6_iface_char_covered", + "interface Show { fn show(self) -> char; }\n" + T6_S6 + + "impl S { fn show(self: S) -> char { return 'a'; } }\n" + "fn f[T: Show](a: T) -> int { x := a.show(); y : char = x; return 0; }\n" + "fn main() -> int { s := S { a: 1 }; return f(s); }\n")) + + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_iface_satisfies.py b/tests/selfhost/test_iface_satisfies.py new file mode 100644 index 00000000..4d89bdc5 --- /dev/null +++ b/tests/selfhost/test_iface_satisfies.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +"""R2 P3b Task 0(P2b 交接面回补):`iface_satisfies` 契约的行为覆盖集。 + +契约(P3 计划「与 P2b 的交接面」① + 附录 B.1 #1):`iface_satisfies(t_ti, iface_ni) -> int` +三态(1 = 判定满足 / 0 = 判定违反 / -1 = 不判)——**-1 不得被读成 0/1**。本批交付: + · 轴分派:A 横切形状名(表由 Task 2 注册;本批空表 ⇒ 恒 -1)→ C 用户接口名 → B 本质轴 + (原生/已声明类型名,= P3a `gen_constr_satisfied` 的窄化口径,逐字保持); + · 轴 C 谓词 = **与 `check_iface` 同源**(方法名在位 + 参数计数 + 返回码),三态;域限定 + = 命名行(`TYP_NAMED` / `TYP_GENERIC_APPLY` 基名)——原生/`dyn`/泛型形参/复合行 ⇒ -1; + · 首个消费者 = `gen_constr_satisfied` 首分支(**1 提前返回**,0/-1 一律回落既有 + `check_iface` 名拼接路径 = 函数调用点措辞/去重/rc 逐字不动,切换归 Task 6 Step 3); + · 第二消费者 = 结构/枚举**实例化点**(`gen_inst_constr_satisfied`)——**本批新增判定**。 + +**三态纪律(本文件的主判据)**:「不判」≠「不满足」。负例段把「违反 ⇒ error[TG02]」钉死; +登记面段把「不判 ⇒ 零诊断」钉死(防后续把 -1 静默折成 0/1)。 + +**覆盖边界(R2 P3b Task 6 关闭本条的返回型面)——旧机制 = 接口签名槽是映射层编码**(parser +的 `unpack_type`:非原生类型节点一律塌缩为码 0 = `TY_INT`,与 `int` 不可区分)⇒ 旧态逐参数类型 +不可比、返回码比对语义 = **编码相等**。Task 6 起签名**类型项化**(`g_ifaces` 方法条目新增 +**类型节点**槽 + `sh_iface_shape_term` / `sh_func_sig_term` 规范项 + 引擎结构比较原语),逐参 +与返回**都按类型**判定 ⇒ 本条已闭合。**台账(收紧)**: + | 用例 | 旧 | 新 | + |---|---|---| + | `inst_encoding_limit_named_ret_rejected`(原 `..._pinned`) | rc=0 零诊断(编码面判「满足」) | check rc=1 + TG02 | + 另:接收者模式(`self`/`&self`/`&mut self`)两侧显式比较(口径 = 调用约定维度,不入类型项面) + ——同为 Task 6 收紧面,覆盖集在 `tests/selfhost/test_impl_iface.py`。 + +**判据口径**:正例三路同证(check rc=0 ∧ ELF rc=N ∧ interp rc=N);负例走 `check` +(有诊断即 rc=1)+ 措辞断言 + 无产物。TG02 为**软诊断**(check rc=1 / build rc=0 + 产物 +照出——与既有约束检查同门同码,本批未新增硬门,见 P3a 报告 §0-① 的口径)。 + +**收紧台账(旧 → 新;全语料零命中,见本批报告 §5)**: + | 用例 | 旧 | 新 | + |---|---|---| + | inst_missing_method_rejected | rc=0 零诊断 | check rc=1 + TG02 | + | inst_count_mismatch_rejected | rc=0 零诊断 | check rc=1 + TG02 | + | inst_ret_code_mismatch_rejected | rc=0 零诊断 | check rc=1 + TG02 | + | inst_enum_missing_method_rejected | rc=0 零诊断 | check rc=1 + TG02 | + | inst_multi_method_partial_rejected | rc=0 零诊断 | check rc=1 + TG02 | + | inst_second_iface_missing_rejected | rc=0 零诊断 | check rc=1 + TG02 | + 「旧」= 本批前二进制(P3a 收官 `aa54d26a`);零变化的其余用例 = 逐位同。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="check"): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + r, out, src = _compile(source, cmd="build") + try: + if r.returncode != 0: + return r, None + rr = subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + return r, rr + finally: + _cleanup(src, out, out + ".ccr") + + +def run_interp(source: str): + return subprocess.run( + [str(COREC), "run", source], capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def case_dual(name, source, expect_rc): + """正例三路同证:check rc=0 ∧ ELF rc=N ∧ interp rc=N。""" + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + ri = run_interp(source) + if ri.returncode != expect_rc: + print(f"[FAIL] {name}: interp expected rc {expect_rc}, got {ri.returncode}: {ri.stdout}{ri.stderr}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}") + return True + + +def case_reject(name, source, needles): + """负例:check rc≠0 + 诊断针 + 同源三态(同措辞在函数调用点与实例化点都成立)。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_unjudged(name, source): + """登记面:**不判**(-1)或编码面判定满足(-1 与 1 在实例化点都 = 零动作)⇒ 零诊断 + rc=0。 + + 三态纪律的正面钉:零诊断 = 「不是『不满足』」,**不得**被读作「已验证满足」。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode != 0: + print(f"[FAIL] {name}: 不判/编码面出现诊断(-1 被当 0 用?)rc={r.returncode}: {r.stdout}{r.stderr}") + return False + print(f"[PASS] {name}: 零诊断 rc=0(不判 或 编码面判定满足)") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +# ── 源形 ────────────────────────────────────────────────────────────── + +# 方法在位(固有 impl 形态;P3a 既有语义 = 结构判定,非名义声明) +IFACE_OK = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { return g(Box { v = S { a = 7 } }); } +""" +# 方法在位(`impl I for T` 形态) +IFACE_IMPL_FOR_OK = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl Show for S { fn show(self: S) -> int { return 2; } } +struct Box[T: Show] { v: T } +fn main() -> int { b := Box { v = S { a = 2 } }; return b.v.a; } +""" +# 方法缺失(**本批新增判定**) +IFACE_MISSING = """interface Show { fn show(self) -> int; } +struct S { a: int } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 参数计数不符 +IFACE_CNT_MISMATCH = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S, x: int) -> int { return x; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 返回码不符(iface `-> int` 码 0 vs impl `-> string` 码 3) +IFACE_RET_MISMATCH = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> string { return "x"; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 同一违反走**函数调用点**(既有路径;措辞应与实例化点同) +IFACE_FUNC_RET_MISMATCH = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> string { return "x"; } } +fn f[T: Show](a: T) -> int { return 0; } +fn main() -> int { s := S { a = 1 }; return f(s); } +""" +# 枚举实例化点(方法缺失) +IFACE_ENUM_MISSING = """interface Show { fn show(self) -> int; } +struct S { a: int } +enum EBox[T: Show] { V(T), N } +fn g(b: EBox[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 多方法接口:实现一半 +IFACE_PARTIAL = """interface Pair2 { fn first(self) -> int; fn second(self) -> int; } +struct S { a: int } +impl S { fn first(self: S) -> int { return 1; } } +struct Box[T: Pair2] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 两接口隔离:S 满足 Show、不满足 Name +IFACE_TWO_A = """interface Name { fn name(self) -> int; } +struct S { a: int } +impl S { fn name(self: S) -> int { return 1; } } +struct BoxA[T: Name] { v: T } +fn g(b: BoxA[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +IFACE_TWO_B = """interface Show { fn show(self) -> int; } +interface Name { fn name(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +struct BoxB[T: Name] { v: T } +fn g(b: BoxB[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 泛型形参实参(未实例化)⇒ 不判 +IFACE_GP_ARG = """interface Show { fn show(self) -> int; } +struct Box[T: Show] { v: T } +fn f[U: Show](b: Box[U]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 原生实参 ⇒ 不判(接口方法表只对命名行可查) +IFACE_NATIVE_ARG = """interface Show { fn show(self) -> int; } +struct Box[T: Show] { v: T } +fn g(b: Box[int]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 签名类型项化的直接可观测面(Task 6 Step 1/3):命名的返回型 vs int 返回 ⇒ **拒绝** +# (旧态编码面判「满足」= 静默通过;台账见文件头注) +IFACE_NAMED_RET = """interface Show { fn show(self) -> S2; } +struct S2 { b: int } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 函数调用点:满足 ⇒ 既有路径放行(正控;P3a 既有语义保持) +IFACE_FUNC_OK = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 3; } } +fn f[T: Show](a: T) -> int { return 1; } +fn main() -> int { s := S { a = 1 }; return f(s); } +""" +# 函数调用点:违反 ⇒ 既有措辞(**逐字不动**的钉) +IFACE_FUNC_MISSING = """interface Show { fn show(self) -> int; } +struct S { a: int } +fn f[T: Show](a: T) -> int { return 0; } +fn main() -> int { s := S { a = 1 }; return f(s); } +""" +# 空接口(零方法)⇒ 空洞满足(命名行) +IFACE_EMPTY = """interface Empty { } +struct S { a: int } +struct Box[T: Empty] { v: T } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { return g(Box { v = S { a = 5 } }); } +""" +# `&self` 接收者形态(计数面) +IFACE_REF_SELF = """interface Show { fn show(&self) -> int; } +struct S { a: int } +impl S { fn show(&self) -> int { return 1; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { return g(Box { v = S { a = 4 } }); } +""" + + +def main(): + ok = [ + # ── 正例(三路同证;含新判定不误报的守门)── + case_dual("inst_method_present_accepted", IFACE_OK, 7), + case_dual("inst_impl_for_form_accepted", IFACE_IMPL_FOR_OK, 2), + case_dual("inst_empty_iface_vacuous", IFACE_EMPTY, 5), + case_dual("inst_ref_self_accepted", IFACE_REF_SELF, 4), + case_dual("inst_first_iface_accepted", IFACE_TWO_A, 0), + case_dual("func_site_pass_unchanged", IFACE_FUNC_OK, 1), + # ── 负例(收紧面:实例化点新判定)── + case_reject("inst_missing_method_rejected", IFACE_MISSING, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("inst_count_mismatch_rejected", IFACE_CNT_MISMATCH, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("inst_ret_code_mismatch_rejected", IFACE_RET_MISMATCH, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("inst_enum_missing_method_rejected", IFACE_ENUM_MISSING, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("inst_multi_method_partial_rejected", IFACE_PARTIAL, + ["error[TG02]", "does not satisfy interface 'Pair2'"]), + case_reject("inst_second_iface_missing_rejected", IFACE_TWO_B, + ["error[TG02]", "does not satisfy interface 'Name'"]), + # 站点一致性:同一违反在**函数调用点**(既有路径)与实例化点**同措辞**报出 + case_reject("func_site_ret_mismatch_also_rejected", IFACE_FUNC_RET_MISMATCH, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("func_site_missing_method_unchanged", IFACE_FUNC_MISSING, + ["error[TG02]", "does not satisfy interface 'Show'"]), + # ── 登记面(不判 ⇒ 零诊断;三态纪律的正面钉——零动作而非「已验证满足」)── + case_unjudged("inst_generic_param_arg_unjudged", IFACE_GP_ARG), + case_unjudged("inst_native_arg_unjudged", IFACE_NATIVE_ARG), + # 签名类型项化(Task 6 收紧):命名返回型 vs int 返回 ⇒ 拒绝(旧态 = 编码面判「满足」) + case_reject("inst_encoding_limit_named_ret_rejected", IFACE_NAMED_RET, + ["error[TG02]", "does not satisfy interface 'Show'"]), + ] + + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_impl_iface.py b/tests/selfhost/test_impl_iface.py new file mode 100644 index 00000000..9479fdfc --- /dev/null +++ b/tests/selfhost/test_impl_iface.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python3 +"""R2 P3b Task 6:impl 契约覆盖集(spec §5.3 要求新建)。 + +交付面(P3 计划 Task 6): + · **Step 1 签名类型项化**:接口方法条目新增**类型节点**槽(`OFF_IFM_PARAM_NODES` / + `OFF_IFM_RET_NODE` / `OFF_IFM_SELF_MODE`)⇒ 签名首次可按**类型**表达与比较(旧态 = 裸 + `TY_*` 码:非原生类型一律塌缩为码 0 = TY_INT,与 int 不可区分)。 + · **Step 3 满足判定接引擎 + mangling 退役**:方法解析从「`Type.method` 字符串拼接 + `find_func`」 + 改为 `g_methods` 表查询(`iface_find_method`);判定 = 接口形状项(方法集 = product of fn, + `sh_iface_shape_term`)的**逐成员**签名项比较(`sh_func_sig_term` / `sh_iface_sig_param_term` + + 引擎结构比较原语 `tt_list_same`)。泛型形参接收者的方法调用在实例化时按具体类型查表解析 + (`CALL_FLAG_IFACE_METHOD` + monomorph 的 EXPR_CALL 克隆分支)。 + · **覆盖集**:正向 / 反向(缺方法·参数数·**参数类型**·**返回类型**·**接收者模式**)/ 多接口 / + 泛型约束交互 / 错误例(未定义接口)/ 固有方法不受影响 / 上限钉子(16 方法 · 8 参数)。 + +**语义口径(本任务裁决,逐条登记在报告)**: + ① **用户轴保持结构口径**(方法集包含);**不**改名义口径(`impl I for T` 声明即满足)—— + 采纳名义会翻转既有钉死用例(`test_generic_constr.py::iface_constr_legacy_pass` 等 5+ 例 + 正向固有 impl 形态),且属语言设计裁决(spec §0 裁决 6:语义争议停下上报)⇒ 登记 TODO。 + ② **签名身份 = P3a 既有不变量**:N(数组长度)不入签名身份(长度面由常量档承担); + 命名行按**名义**(同名字同行;引擎不展开 AK_NAMED——P0 未覆盖面②);泛型应用按 + 基名 + 实参链(规范形展开)。 + ③ **接收者模式**(self / &self / &mut self)是判定维度(调用约定,不入类型项面 ⇒ 两侧显式 + 比较)。旧态两侧皆写码 0 ⇒ 不可比。 + +**收紧台账(旧 → 新;「旧」= P3b Task 2 二进制 ./build/corec 的上一版,本文件逐例实测随报告提交)**: + | 用例 | 旧 | 新 | + |---|---|---| + | neg_named_param_type_mismatch | check rc=0 零诊断 | rc=1 + TG02 | + | neg_apply_arg_mismatch | check rc=0 零诊断 | rc=1 + TG02 | + | neg_named_ret_vs_native | check rc=0 零诊断(编码面判「满足」) | rc=1 + TG02 | + | neg_receiver_mode_mismatch | check rc=0 零诊断 | rc=1 + TG02 | + | pos_generic_body_call 运行面 | build rc=0 + **运行 rc=139**(调用目标悬空) | build rc=0 + 运行 rc=N(正确值) | + +**判据口径**:正例三路同证(check rc=0 ∧ ELF rc=N ∧ interp rc=N);负例走 `check`(rc=1 + 诊断 +针)+ 软诊断门形态(build rc=0 + 产物照出,TG02 与既有约束检查同门同码)。**例外(登记)**: +泛型体内方法调用(`pos_generic_body_call`)的解释器路径现状 = `Unexpected token in expression` +(`corec run` 的解析面不支持方法调用语句;**旧二进制同 rc=1**,非本任务引入)⇒ 该例走 +check + ELF 两路。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + + +BASE = Path(__file__).resolve().parents[2] +COREC = Path(os.environ.get("COREC_BIN", BASE / "build" / "corec")) + + +def _no_core_dump(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="check"): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + r, out, src = _compile(source, cmd="build") + try: + if r.returncode != 0: + return r, None + rr = subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + return r, rr + finally: + _cleanup(src, out, out + ".ccr") + + +def run_interp(source: str): + return subprocess.run( + [str(COREC), "run", source], capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def case_dual(name, source, expect_rc): + """正例三路同证:check rc=0 ∧ ELF rc=N ∧ interp rc=N。""" + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + ri = run_interp(source) + if ri.returncode != expect_rc: + print(f"[FAIL] {name}: interp expected rc {expect_rc}, got {ri.returncode}: {ri.stdout}{ri.stderr}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}") + return True + + +def case_elf(name, source, expect_rc): + """两路同证(check + ELF;解释器面登记为不覆盖——见文件头注)。""" + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + print(f"[PASS] {name}: ELF rc={rr.returncode}(check rc=0)") + return True + + +def case_reject(name, source, needles): + """负例:check rc≠0 + 诊断针(软诊断门形态:build 仍 rc=0 + 产物照出)。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_unjudged(name, source): + """登记面:**不判**(-1)⇒ 零诊断 + rc=0。零诊断 = 「不是『不满足』」。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode != 0: + print(f"[FAIL] {name}: 不判面出现诊断(-1 被当 0 用?)rc={r.returncode}: {r.stdout}{r.stderr}") + return False + print(f"[PASS] {name}: 零诊断 rc=0(不判)") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_hard(name, source, needles): + """硬错误钉子:check rc=1 + 无产物(build rc≠0)。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected hard error, got rc=0") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + finally: + _cleanup(src, out, out + ".ccr") + r2, out2, src2 = _compile(source, cmd="build") + try: + if r2.returncode == 0: + print(f"[FAIL] {name}: 硬错误面 build 竟然 rc=0(产物照出)") + return False + print(f"[PASS] {name}: check rc=1 + build rc={r2.returncode}(无产物)") + return True + finally: + _cleanup(src2, out2, out2 + ".ccr") + + +# ── 源形 ────────────────────────────────────────────────────────────── + +# 正向:固有 impl 形态(结构口径)——**双站点**(实例化点 `Box[S]` 注解 + 函数调用点 `f(s)`) +POS_INHERENT = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 5; } } +struct Box[T: Show] { v: T } +fn f[T: Show](a: T) -> int { return 0; } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { s := S { a = 7 }; z := f(s); return g(Box { v = s }); } +""" +# 正向:`impl I for T` 形态(双站点) +POS_IMPL_FOR = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl Show for S { fn show(self: S) -> int { return 2; } } +struct Box[T: Show] { v: T } +fn f[T: Show](a: T) -> int { return 0; } +fn main() -> int { s := S { a = 2 }; z := f(s); return s.a + z; } +""" +# 正向:命名参数类型 + 命名返回类型(签名类型项化的主要正控——旧态两侧皆码 0;双站点) +POS_NAMED_SIG = """interface Conv { fn conv(self, x: S2) -> S3; } +struct S2 { b: int } +struct S3 { c: int } +struct S { a: int } +impl S { fn conv(self: S, x: S2) -> S3 { return S3 { c = x.b + 3 }; } } +struct Box[T: Conv] { v: T } +fn f[T: Conv](a: T) -> int { return 0; } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { s := S { a = 1 }; z := f(s); return g(Box { v = s }); } +""" +# 正向:泛型应用实参(`Pair[int]` 两侧同型;双站点) +POS_APPLY_SIG = """interface Take { fn take(self, x: Pair[int]) -> int; } +struct Pair[T] { v: T } +struct S { a: int } +impl S { fn take(self: S, x: Pair[int]) -> int { return x.v; } } +struct Box[T: Take] { v: T } +fn f[T: Take](a: T) -> int { return 0; } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { s := S { a = 9 }; z := f(s); return g(Box { v = s }); } +""" +# 正向:`&self` 接收者(两侧同模式;双站点) +POS_REF_SELF = """interface Show { fn show(&self) -> int; } +struct S { a: int } +impl S { fn show(&self) -> int { return 4; } } +struct Box[T: Show] { v: T } +fn f[T: Show](a: T) -> int { return 0; } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { s := S { a = 4 }; z := f(s); return g(Box { v = s }); } +""" +# 泛型体内方法调用(mangling 退役的主用例:旧态**运行 rc=139**)——固有形态 +POS_GENERIC_BODY_CALL = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 7; } } +fn f[T: Show](a: T) -> int { x := a.show(); return x; } +fn main() -> int { s := S { a = 1 }; return f(s); } +""" +# 泛型体内方法调用——`impl I for T` 形态 +POS_GENERIC_BODY_CALL_FOR = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl Show for S { fn show(self: S) -> int { return 9; } } +fn f[T: Show](a: T) -> int { x := a.show(); return x; } +fn main() -> int { s := S { a = 1 }; return f(s); } +""" +# 固有方法不受影响(无接口参与) +POS_INHERENT_ONLY = """struct S { a: int } +impl S { fn get(self: S) -> int { return self.a; } fn add(self: S, x: int) -> int { return self.a + x; } } +fn main() -> int { s := S { a = 6 }; return s.add(1); } +""" +# 多接口隔离:S 满足 Show(另一接口 Name 未参与;双站点) +POS_MULTI_IFACE = """interface Show { fn show(self) -> int; } +interface Name { fn name(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +impl S { fn name(self: S) -> int { return 2; } } +struct Box[T: Show] { v: T } +fn f[T: Show](a: T) -> int { return 0; } +fn g(b: Box[S]) -> int { return b.v.a; } +fn main() -> int { s := S { a = 3 }; z := f(s); return g(Box { v = s }); } +""" +# 负:缺方法 +NEG_MISSING = """interface Show { fn show(self) -> int; } +struct S { a: int } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:参数计数 +NEG_ARITY = """interface Show { fn show(self) -> int; } +struct S { a: int } +impl S { fn show(self: S, x: int) -> int { return x; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:**参数类型**不符(命名型——旧态裸码面不可见) +NEG_NAMED_PARAM = """interface Show { fn m(self, x: S2) -> int; } +struct S2 { b: int } +struct S { a: int } +impl S { fn m(self: S, x: int) -> int { return x; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:**返回类型**不符(命名型 vs 原生——旧态编码面判「满足」) +NEG_NAMED_RET = """interface Show { fn m(self) -> S2; } +struct S2 { b: int } +struct S { a: int } +impl S { fn m(self: S) -> int { return 1; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:泛型应用实参不符(`Pair[string]` ← `Pair[int]`) +NEG_APPLY_ARG = """interface Take { fn take(self, x: Pair[int]) -> int; } +struct Pair[T] { v: T } +struct S { a: int } +impl S { fn take(self: S, x: Pair[string]) -> int { return 1; } } +struct Box[T: Take] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:接收者模式不符(iface `&self` vs impl `self`) +NEG_RECV_MODE = """interface Show { fn show(&self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +struct Box[T: Show] { v: T } +fn g(b: Box[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 负:两接口隔离(S 满足 Show、不满足 Name) +NEG_SECOND_IFACE = """interface Show { fn show(self) -> int; } +interface Name { fn name(self) -> int; } +struct S { a: int } +impl S { fn show(self: S) -> int { return 1; } } +struct BoxB[T: Name] { v: T } +fn g(b: BoxB[S]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 错误例:未定义接口(`impl I for T` 的声明侧校验) +ERR_UNDEFINED_IFACE = """struct S { a: int } +impl Nope for S { fn show(self: S) -> int { return 1; } } +fn main() -> int { return 0; } +""" +# 登记面:非命名实参(原生)⇒ 不判 ⇒ 零诊断 +UNJUDGED_NATIVE = """interface Show { fn show(self) -> int; } +struct Box[T: Show] { v: T } +fn g(b: Box[int]) -> int { return 0; } +fn main() -> int { return 0; } +""" +# 上限钉子:17 方法(> MAX_IFACE_METHODS=16)⇒ 硬错 +LIMIT_METHODS = "interface Many {\n" + "\n".join( + f" fn m{i}(self) -> int;" for i in range(17)) + "\n}\nfn main() -> int { return 0; }\n" +# 上限钉子:9 参数(> MAX_IFACE_METHOD_PARAMS=8)⇒ 硬错 +LIMIT_PARAMS = ("interface Wide { fn m(self, " + ", ".join(f"p{i}: int" for i in range(9)) + + ") -> int; }\nfn main() -> int { return 0; }\n") + + +def main(): + ok = [ + # ── 正向(三路同证 / 两路同证)── + case_dual("pos_inherent_impl_accepted", POS_INHERENT, 7), + case_dual("pos_impl_for_accepted", POS_IMPL_FOR, 2), + case_dual("pos_named_signature_accepted", POS_NAMED_SIG, 1), + case_dual("pos_apply_signature_accepted", POS_APPLY_SIG, 9), + case_dual("pos_ref_self_accepted", POS_REF_SELF, 4), + case_dual("pos_inherent_methods_unaffected", POS_INHERENT_ONLY, 7), + case_dual("pos_multi_iface_isolation", POS_MULTI_IFACE, 3), + # 泛型体内方法调用:**运行面**(mangling 退役的收益——旧态 rc=139) + case_elf("pos_generic_body_call_inherent", POS_GENERIC_BODY_CALL, 7), + case_elf("pos_generic_body_call_impl_for", POS_GENERIC_BODY_CALL_FOR, 9), + # ── 反向(收紧面:签名类型项化 / 接收者模式)── + case_reject("neg_missing_method", NEG_MISSING, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("neg_param_count", NEG_ARITY, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("neg_named_param_type_mismatch", NEG_NAMED_PARAM, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("neg_named_ret_vs_native", NEG_NAMED_RET, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("neg_apply_arg_mismatch", NEG_APPLY_ARG, + ["error[TG02]", "does not satisfy interface 'Take'"]), + case_reject("neg_receiver_mode_mismatch", NEG_RECV_MODE, + ["error[TG02]", "does not satisfy interface 'Show'"]), + case_reject("neg_second_iface_missing", NEG_SECOND_IFACE, + ["error[TG02]", "does not satisfy interface 'Name'"]), + # ── 错误例 / 登记面 / 上限钉子 ── + case_reject("err_undefined_interface", ERR_UNDEFINED_IFACE, + ["error[N01]", "Undefined interface 'Nope'"]), + case_unjudged("unjudged_native_arg", UNJUDGED_NATIVE), + case_hard("limit_17_methods_rejected", LIMIT_METHODS, + ["exceeds max 16 methods"]), + case_hard("limit_9_params_rejected", LIMIT_PARAMS, + ["exceeds max 8 params"]), + ] + + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_interp_parity.py b/tests/selfhost/test_interp_parity.py new file mode 100644 index 00000000..04191106 --- /dev/null +++ b/tests/selfhost/test_interp_parity.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python3 +"""TODO #11 判据:解释器 callee 内联路径 ≡ 主循环路径(≡ ELF oracle)。 + +R1 终审实测:`interp.cr` 的 callee 内联分派相对主循环缺 18 个 opcode +(4/17/18/23/24/25/26/27/28/29/30/41/42/43/44/45/48/51)——同一段源码写在 +main 里正确、写进普通函数(被内联执行)里错值 / SIGSEGV,ELF 侧均正确。 + +本判据把三条路径钉在一起(每例两条断言 + 一条 oracle 断言): + ① interp(callee) —— 内联路径(被内联的 probe()) + ② interp(main) —— 主循环路径(同一段源码,省一次 ELF 构建) + ③ elf(callee) —— ELF oracle(本修复不改动发射面) + +三类用例: + - `parity`:① == ② == ③ == 期望值(纯值语义;ELF 即 oracle) + - `loud` :①/② 必须响亮失败(非零 rc,动态分发/extern/深度守卫另有诊断), + 绝不静默 0;③ 单列期望(ELF 侧自身行为:越界 SIGILL / extern· + 深递归 SIGSEGV / 动态分发占位 no-op) + - `approx`:解释器的显式近似(IR_FNADDR 无真实地址 = 0)——只要求 ① == ② + (两路径一致的近似才不算分叉),③ 单列 + +对 parity/approx 追加「诊断奇偶性」断言:同一段源码在两种形态(包进 probe() / +内联进 main)下,前端诊断的**存在性必须相等**——前端两形态共享,诊断只出现在 +一条路径上才是分叉信号;两形态共有的既存诊断(B04 借用报错、apx dex 的 +N01/TA01)属于前端既有行为,不属本判据判域。 + +ELF 侧 SIGILL/SIGSEGV 在 subprocess.returncode 里是负信号号(-4 / -11);解释器 +侧越界陷阱与深度守卫的 rc 是 255(main 返回 -1,低 8 位)。 + +判据前清 cir 缓存(缓存键不含编译器身份,TODO #5):否则旧 IR 冒充新编译器, +本判据会被静默污染(见 test_global_init.py 同款说明)。 +""" + +import os +import resource +import subprocess +from pathlib import Path + + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" + +# 信号退出码(subprocess 语义):SIGILL = -4,SIGSEGV = -11 +SIGILL = -4 +SIGSEGV = -11 + +# (名称, 文件级前言, 函数体, 模式, 期望 interp rc, 期望 elf rc) +# 函数体 = 不含 return 类型的语句序列(两条路径同源:callee 包进 probe()、 +# main 直接内联)。 +CASES = [ + # —— 枚举族(17 MAKE_ENUM / 23 LOAD_ENUM_TAG / payload 走 11/12)—— + ( + "enum_payload_in_callee", + "enum Choice { First(int), Second(int), Third(int) }", + "c := Third(33); return match c { First(x) => x, Second(x) => x, Third(x) => x, };", + "parity", 33, 33, + ), + ( + # 构造在 callee(MAKE_ENUM)、match 在 main(LOAD_ENUM_TAG 跨函数) + "enum_construct_callee_match_main", + "enum Choice { First(int), Second(int), Third(int) }\n" + "fn make()->Choice { return Third(33); }", + "c := make(); return match c { First(x) => x, Second(x) => x, Third(x) => x, };", + "parity", 33, 33, + ), + ( + "enum_tag_variant_in_callee", + "enum Color { Red, Green, Blue }", + "c := Green(); return match c { Red => 1, Green => 2, Blue => 3, };", + "parity", 2, 2, + ), + ( + "enum_payload_tag_only_in_callee", + "enum Choice { First(int), Second(int), Third(int) }", + "c := Third(33); return match c { First(x) => 1, Second(x) => 2, Third(x) => 3, };", + "parity", 3, 3, + ), + # —— 裸指针族(18 REF / 25 DEREF / 26 STORE_PTR / 31 ADDR_INDEX)—— + ( + # TODO #11 复现原件:g:[int;3]=[5,6,7]; fn f(){ p:=&g[2]; return *p; } + "bare_ptr_deref_callee", + "g : [int;3] = [5,6,7];", + "p := &g[2]; return *p;", + "parity", 7, 7, + ), + ( + "ptr_store_callee", + "", + "x : ., mut = 5; p := &x; *p = 42; return x;", + "parity", 42, 42, + ), + ( + "addr_index_deref_callee", + "", + "a := [10,20,30]; p := &a[1]; return *p;", + "parity", 20, 20, + ), + # —— 切片 / 边界检查族(24 SLICE / 30 BOUNDS_CHECK)—— + ( + "slice_runtime_bounds_callee", + "", + "a : [int;5] = [10,20,30,40,50]; lo := 1; hi := 3; s := a[lo..hi]; return s[1];", + "parity", 30, 30, + ), + ( + "slice_literal_bounds_callee", + "", + "a : [int;5] = [10,20,30,40,50]; s := a[1..3]; return s[1];", + "parity", 30, 30, + ), + ( + # 越界:interp 陷阱 = 中止码 -1(rc 255,与主循环 return -1 同语义); + # ELF = ud2 SIGILL。两路径都非静默(rc 非 0)。 + "slice_oob_callee_loud", + "", + "a : [int;5] = [10,20,30,40,50]; lo := 1; hi := 3; s := a[lo..hi]; return s[2];", + "loud", 255, SIGILL, + ), + # —— dyn 族(43 DYN_PACK / 42 DYN_VAL / 41 DYN_TAG / 44 DYN_DISPATCH)—— + ( + "dyn_pack_val_callee", + "", + "x : dyn = 42; if x != 42 { return 1; } return 0;", + "parity", 0, 0, + ), + ( + # 动态分发:interp 响亮报错(rc 2 + 诊断,与主循环同文案);ELF 占位 no-op + "dyn_dispatch_callee_loud", + "struct Counter { n: int }\nimpl Counter {\n fn bump(self) { }\n}\n", + "c : Counter = Counter { n = 7 }; d : dyn = c; d.bump(); return 3;", + "loud", 2, 3, + ), + # —— 调用族(4 CALL:嵌套内联 / 递归 / 互递归)—— + ( + "nested_call_callee", + "fn g2()->int { return 7; }\n", + "return g2();", + "parity", 7, 7, + ), + ( + "recursion_fib_callee", + "fn fib(n: int)->int { if n < 2 { return n; } return fib(n-1) + fib(n-2); }\n", + "return fib(10);", + "parity", 55, 55, + ), + ( + "mutual_recursion_callee", + "fn is_even(n: int)->int { if n == 0 { return 1; } return is_odd(n - 1); }\n" + "fn is_odd(n: int)->int { if n == 0 { return 0; } return is_even(n - 1); }\n", + "return is_even(10);", + "parity", 1, 1, + ), + ( + # 深度守卫:无界递归必须响亮中止(rc 255 + 诊断),不挂死 + "depth_guard_runaway_loud", + "fn r(n: int)->int { return r(n + 1); }\n", + "return r(0);", + "loud", 255, SIGSEGV, + ), + # —— 并发族(27 SPAWN:range go;28 YIELD / 29 AWAIT eager 近似)—— + ( + "range_go_spawn_callee", + "fn square(x: int) -> int { return x * x; }\n", + "arr := go i 0..3 square(i); return arr[0] + arr[1] + arr[2];", + "parity", 5, 5, + ), + ( + # 19 BRANCH / 20 JUMP / 21 LABEL:callee 内的回边(主循环用 region 表、 + # callee 路径只用 label_poses——两条机制各走各的,必须同值) + "loop_in_callee", + "", + "s : ., mut = 0; for i := 0..5 { s = s + i; } return s;", + "parity", 10, 10, + ), + ( + "yield_in_callee", + "", + "yield; return 11;", + "parity", 11, 11, + ), + ( + "await_in_callee", + "", + "x := await 5; return x;", + "parity", 5, 5, + ), + # —— 45 CALL_EXTERN:响亮报错(interp rc 2 + 诊断;ELF 静态构建运行期 + # SIGSEGV——两侧都非静默)—— + ( + "extern_call_callee_loud", + "extern fn putchar(c: int) -> int;\n", + "return putchar(65);", + "loud", 2, SIGSEGV, + ), + # —— 48 FNADDR / 51 IR_APPROX —— + ( + # 解释器无真实地址(FNADDR 恒 0)——显式近似:只断言 callee ≡ main + "fnaddr_callee_approx", + "fn square(x: int) -> int { return x * x; }\n", + "p := @addr(square); if p == 0 { return 1; } return 0;", + "approx", 1, 0, + ), + ( + # IR_APPROX (51):apx 标注(`apx dex` 不是合法语法——真形式是 `dex, apx` + # 标签)。先前写成 `d : apx dex = 2.0` 时 checker 发 N01/TA01、IR_APPROX + # 根本未发射,判据形同虚设(两形态的诊断奇偶断言也掩盖了它)。 + "approx_apx_dex_callee", + "", + "d : dex, apx = 2.0; return 7;", + "parity", 7, 7, + ), +] + +# 响亮失败用例必须出现的诊断标记(interp 侧)——缺诊断说明是静默失败 +LOUD_MARKERS = { + "dyn_dispatch_callee_loud": "IR_DYN_DISPATCH", + "extern_call_callee_loud": "无法调用外部函数", + "depth_guard_runaway_loud": "IR_INTERP_MAX_DEPTH", +} + +# 覆盖自检:每例必须在 probe() 的 DFG 节点区内**真的**出现这些 opcode。 +# 内联路径只执行 probe() 的节点区(g_df_func_node_start..+count),故节点区 +# 里没有目标 opcode 的用例等于没测(空转)——写法错(如 `apx dex`)或前端 +# 不再发射该 opcode 时,用例会静默失去判据价值,此处用 `corec cir` 钉死。 +# TODO #11 清单里唯一无法覆盖的是 41 IR_DYN_TAG:ir_gen.cr 无发射点 +# (regalloc/instr/sizes/dataflow/opt/interp 均已有处理器)——该 opcode 当前 +# 不可达,内联路径的处理器是纯防御性对齐,不列入下表。 +EXPECT_OPS = { + "enum_payload_in_callee": {"make_enum", "load_enum_tag"}, + "enum_construct_callee_match_main": {"call", "load_enum_tag"}, + "enum_tag_variant_in_callee": {"make_enum", "load_enum_tag"}, + "enum_payload_tag_only_in_callee": {"make_enum", "load_enum_tag"}, + "bare_ptr_deref_callee": {"addr_index", "deref"}, + "ptr_store_callee": {"ref", "store_ptr"}, + "addr_index_deref_callee": {"addr_index", "deref"}, + "slice_runtime_bounds_callee": {"slice", "bounds_check"}, + "slice_literal_bounds_callee": {"slice"}, + "slice_oob_callee_loud": {"slice", "bounds_check"}, + "dyn_pack_val_callee": {"dyn_pack", "dyn_val"}, + "dyn_dispatch_callee_loud": {"dyn_dispatch", "dyn_pack"}, + "nested_call_callee": {"call"}, + "recursion_fib_callee": {"call"}, + "mutual_recursion_callee": {"call"}, + "depth_guard_runaway_loud": {"call"}, + "range_go_spawn_callee": {"spawn"}, + "loop_in_callee": {"jump", "branch"}, + "yield_in_callee": {"yield"}, + "await_in_callee": {"await"}, + "extern_call_callee_loud": {"call_extern"}, + "fnaddr_callee_approx": {"fnaddr"}, + "approx_apx_dex_callee": {"approx"}, +} + + +def clean_cache() -> None: + """cir 缓存键不含编译器身份(TODO #5)——判据前清缓存,失败即报错退出。""" + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "clean-cache"], + cwd=BASE, capture_output=True, text=True, + ) + if result.returncode != 0: + print(f"[FAIL] clean-cache failed (rc={result.returncode})") + for line in (result.stdout + result.stderr).strip().splitlines()[:10]: + print(f" | {line}") + raise SystemExit(1) + + +def has_diag(output: str) -> bool: + return "error[" in output + + +def no_core_dump(): + # TODO:125:core_pattern 为 systemd-coredump 管道时陷阱程序会挂起——测试 + # 禁用 core dump 让 SIGILL/SIGSEGV 立即终止。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def run_interp(source: str): + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "run", source], + cwd=BASE, capture_output=True, text=True, timeout=120, + ) + return result.returncode, result.stdout + result.stderr + + +def run_native(name: str, source: str): + tag = f"{name}_{os.getpid()}" + src = BUILD / f"interp_parity_{tag}.cr" + binary = BUILD / f"interp_parity_{tag}" + ccr = Path(str(binary) + ".ccr") + src.write_text(source.strip() + "\n", encoding="utf-8") + try: + built = subprocess.run( + ["nice", "-n", "19", str(COREC), "build", str(src), + "-o", str(binary), "--static"], + cwd=BASE, capture_output=True, text=True, timeout=120, + ) + out = built.stdout + built.stderr + if built.returncode != 0: + return built.returncode, out + if not binary.exists(): + return 127, out + "\n[no ELF produced]" + run = subprocess.run( + [str(binary)], cwd=BASE, capture_output=True, text=True, + timeout=30, preexec_fn=no_core_dump, + ) + return run.returncode, out + run.stdout + run.stderr + finally: + for artifact in (src, binary, ccr): + try: + artifact.unlink() + except FileNotFoundError: + pass + + +def callee_src(prelude: str, body: str) -> str: + """内联路径:函数体包进 probe(),main 调 probe()。""" + return f"{prelude}\nfn probe()->int {{ {body} }}\nfn main()->int {{ return probe(); }}" + + +def main_src(prelude: str, body: str) -> str: + """主循环路径:同一段函数体直接内联进 main。""" + return f"{prelude}\nfn main()->int {{ {body} }}" + + +def probe_ops(name: str, source: str): + """probe() 节点区里的 opcode 名集合(= 内联路径实际会执行的节点)。 + + 与 interp 同一数据源(g_df_nodes 的函数节点区),经 `corec cir` 打印; + cir 缓存需已 clean(调用方保证),否则旧 IR 会冒充。 + """ + tag = f"cover_{name}_{os.getpid()}" + src = BUILD / f"interp_parity_{tag}.cr" + src.write_text(source.strip() + "\n", encoding="utf-8") + try: + dumped = subprocess.run( + ["nice", "-n", "19", str(COREC), "cir", str(src)], + cwd=BASE, capture_output=True, text=True, timeout=120, + ) + ops, in_probe = set(), False + for line in (dumped.stdout + dumped.stderr).splitlines(): + if line.startswith("Function: "): + in_probe = line.strip() == "Function: probe" + continue + if in_probe and line.startswith(" ") and not line.startswith(" "): + tok = line.strip().split(" ")[0] + if tok: + ops.add(tok) + return ops, dumped.returncode + finally: + # 源文件 + cir 缓存一起删(缓存就落在源文件旁)——判据不留垃圾 + for artifact in (src, src.with_suffix(".cir")): + try: + artifact.unlink() + except FileNotFoundError: + pass + + +def main() -> int: + if not COREC.exists(): + print("build/corec is missing; run `python3 build_selfhost_native.py` first") + return 1 + BUILD.mkdir(exist_ok=True) + clean_cache() + + failures = [] + for name, prelude, body, mode, exp_interp, exp_elf in CASES: + ok = True + src_callee = callee_src(prelude, body) + src_main = main_src(prelude, body) + + # ⓪ 覆盖自检:内联路径必须真的执行到目标 opcode(否则用例空转) + want = EXPECT_OPS.get(name, set()) + if want: + ops, dump_rc = probe_ops(name, src_callee) + missing = want - ops + if dump_rc != 0 or missing: + ok = False + print(f"[FAIL] {name}/opcodes: missing {sorted(missing)} in probe() " + f"(dump rc={dump_rc}, 节点区 ops={sorted(ops)})") + + # ① 内联路径(解释器) + rc, out = run_interp(src_callee) + if mode == "loud": + if rc == 0 or has_diag(out): + ok = False + print(f"[FAIL] {name}/interp-callee: loud failure expected, rc={rc}, diag={has_diag(out)}") + marker = LOUD_MARKERS.get(name) + if rc != exp_interp or (marker and marker not in out): + ok = False + print(f"[FAIL] {name}/interp-callee: expected rc={exp_interp}" + f"{' + marker ' + marker if marker else ''}, got rc={rc}") + for line in out.strip().splitlines()[:6]: + print(f" | {line}") + else: + print(f"[PASS] {name}/interp-callee: rc={rc} (loud)") + else: + if rc != exp_interp: + ok = False + print(f"[FAIL] {name}/interp-callee: expected {exp_interp}, got rc={rc}" + f"{' + diagnostic' if has_diag(out) else ''}") + for line in out.strip().splitlines()[:6]: + print(f" | {line}") + else: + print(f"[PASS] {name}/interp-callee: {rc}") + + # ② 主循环路径(解释器)——同一段源码不写进 probe():两路径必须同值 + rc_m, out_m = run_interp(src_main) + expect_main = exp_interp + if rc_m != expect_main: + ok = False + print(f"[FAIL] {name}/interp-main: expected {expect_main}, got rc={rc_m}" + f"{' + diagnostic' if has_diag(out_m) else ''}") + else: + print(f"[PASS] {name}/interp-main: {rc_m}") + + # 诊断奇偶性(parity/approx):前端(lexer/parser/checker)为两形态共享, + # 同一段源码包进 probe() 与内联进 main 必须得到同一套诊断。只有诊断 + # 「只在一条路径上出现」才是分叉信号——要求的是存在性相等,不是必须为零: + # 两形态共有的既存诊断(如 B04 借用报错、apx dex 的 N01/TA01)是前端既有 + # 行为,不属本判据判域;rc 已单独断言,不存在被诊断掩盖的通道。 + if mode != "loud" and has_diag(out) != has_diag(out_m): + ok = False + print(f"[FAIL] {name}/diag-parity: callee diag={has_diag(out)}, " + f"main diag={has_diag(out_m)} — 同一源码两形态诊断不一致") + for line in (out if has_diag(out) else out_m).strip().splitlines()[:6]: + print(f" | {line}") + + # ③ ELF oracle(内联路径形态) + rc_e, out_e = run_native(name, src_callee) + if rc_e != exp_elf: + ok = False + print(f"[FAIL] {name}/elf-callee: expected {exp_elf}, got rc={rc_e}") + for line in out_e.strip().splitlines()[:6]: + print(f" | {line}") + else: + print(f"[PASS] {name}/elf-callee: {rc_e}") + + if not ok: + failures.append(name) + + print(f"{len(CASES) - len(failures)}/{len(CASES)} interp-parity cases passed " + f"(interp callee ≡ interp main ≡ ELF oracle)") + return 0 if not failures else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_let_check.py b/tests/selfhost/test_let_check.py new file mode 100644 index 00000000..72e8ee3f --- /dev/null +++ b/tests/selfhost/test_let_check.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +"""TODO #32 回归:`EXPR_LET` 站点**无任何兼容检查**(R2 P5 Task 6 修复)。 + +背景(RED,修复前实测——(a) 冻结基线 `/tmp/p5t0/base/corec`(pre-P5);(b) 本批起点 +`2342fd03…`;两代二进制同值 ⇒ 预存洞,非本批引入): +`checker.cr` 的 `EXPR_LET` 分支(`infer_expr`)**只登记符号**: + `ti := val_ti; if type_node >= 0 { ti = res_type_node(type_node); }` +——不做值/注解比对(该分支无 `type_equal`/`type_compat_strict` 调用;全仓 grep:`EC_TA_DECL` +(TA02)**定义零 raise**)。符号类型取**注解行** ⇒ 后端按注解行发射 = **静默错产物**: + * `x : int = "s"` → check **rc=0**(值 = 字符串指针按 int 读); + * `x : [int;3] = s`(s 为切片) → check **rc=0**; + * `x : [int;4] = [1,2,3]`(异长常量档)→ check **rc=0**(比切片面更宽); + * `x : int? = 5; y : int = x`(可选流进窄槽)→ check **rc=0**(`T? ⊄ T` 的站点级拒绝 + 在返回位/赋值位已落,本站点可被绕过); + * 全局同形:`g : int = "s";` → check **rc=0**(全局符号在注册趟即取注解行)。 + +修复(R2 P5 Task 6):声明位点加判定 = `type_compat_strict`(身份 + 长度档 + 可选目标注入) ++ `diag_type_incompatible`,码 = **TA02**(`EC_TA_DECL`,error-codes.md 既有槽位「变量声明 +类型与初始值不符」——复用而非新码,见报告 §裁决);参序 =(源 = 初始化值, 目标 = 注解行)。 +两个调用点共用 `check_let_annot_compat`:① 局部 `EXPR_LET` 分支;② `check_global_let` +(全局初始化器——同形缺口,本任务**显式划界覆盖**)。豁免四条(各附理由,见该函数头注): +无注解/auto/无初值 · 注解 dyn · 值 TI_NEVER(底部 + 错误标记=级联抑制)· 注解泛型形参。 +硬错门:`EC_TA_DECL` 入 `main.cr` 硬名单 ⇒ `build` 亦拒绝(同 TS01-04/TK02 的「判定继续 += 产出静默错产物」类)。 +report-only 开门依据:**全语料(72 档)零命中**(check rc 34×0/38×1 与 pre-P5 基线逐档同 + +诊断正文逐条同 + `check src/compiler` rc=0)——见 `.superpowers/sdd/p5-task6-report.md`。 + +判据口径(本套件):拒绝例 = rc≠0 + 定位诊断 needle + **无产物**(bin 与 .ccr)+ 拒绝发生在 +**前端**(不进入 `lower to ccr`/`save .ccr`/`generate ELF` 阶段);正控例 = check rc=0 且 +build+运行值正确(防「一律拒绝」的假牙)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 拒绝必须早于这三个阶段(硬名单闸位于 `[4/5] type check` 之后、lower 之前): +# 越过即 = 静默产物已生成(本检查的失败形态)。 +STAGES = ("lower to ccr", "save .ccr", "generate ELF") + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _write(source: str): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + return src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def _run(args, **kw): + return subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180, **kw) + + +def case_run(name, source, expect_rc): + """正控:build rc=0 + 运行值 = expect_rc(退出码 = main 返回值低 8 位)。""" + src = _write(source) + out = src[:-3] + try: + built = _run([str(COREC), "build", src, "-o", out, "--static"]) + if built.returncode != 0: + print(f"[FAIL] {name}: compile-failed(rc={built.returncode}): {built.stdout}{built.stderr}") + return False + r = subprocess.run([out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump) + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc}, got {r.returncode}") + return False + print(f"[PASS] {name}: rc={r.returncode}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_accept_check(name, source, expect_rc=0): + """正控(不运行):check rc = expect_rc(0 = 不误报)。""" + src = _write(source) + try: + r = _run([str(COREC), "check", src]) + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected check rc {expect_rc}, got {r.returncode}: " + f"{r.stdout}{r.stderr}") + return False + print(f"[PASS] {name}: check rc={r.returncode}") + return True + finally: + _cleanup(src) + + +def case_reject(name, source, needles, cmd="build", forbid=STAGES, want_no_artifact=True, + exact_diags=None): + """拒绝面:rc≠0 + 全部 needle 命中 + 无产物 + 全部 forbid 阶段缺席。 + + **必须**同时断言 needle(定位诊断文本)——仅断言 rc≠0 会被兜底式失败误当通过。 + forbid 钉「拒绝发生在**前端**」:护栏若落在读回侧/发射侧,静默面(check rc=0 与 + 「按注解行发射的错产物」)并不因此消失。 + exact_diags:给出时 = 断言 `error[` 条数**恰为此数**(级联抑制例用)。 + """ + src = _write(source) + out = src[:-3] + try: + args = ([str(COREC), "build", src, "-o", out, "--static"] if cmd == "build" + else [str(COREC), "check", src]) + r = _run(args) + blob = r.stdout + r.stderr + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默面复活): {blob}") + return False + for nd in needles: + if nd not in blob: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {nd!r}: {blob}") + return False + for fd in forbid: + if fd in blob: + print(f"[FAIL] {name}: 拒绝晚于前端——输出含流水线阶段 {fd!r}") + return False + if exact_diags is not None: + n = blob.count("error[") + if n != exact_diags: + print(f"[FAIL] {name}: expected exactly {exact_diags} diagnostics, got {n}: {blob}") + return False + if want_no_artifact and cmd == "build": + for p in (out, out + ".ccr"): + if os.path.exists(p): + print(f"[FAIL] {name}: 拒绝编译却仍产出 {p}") + return False + print(f"[PASS] {name}: rc={r.returncode} + needles + 无产物/前端拒绝") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +# ─── 语料 ─── +SLICE_SRC = ("fn h(s: [int]) -> int { x : [int;3] = s; return 0; }\n" + "fn main() -> int { return h([1,2,3]); }\n") + + +def main(): + ok = [ + # ── 拒绝面(RED 三形态 + 同族扩展)── + case_reject("let_int_string_build_rejected", 'fn main() -> int { x : int = "s"; return 0; }\n', + ["error[TA02]", "Variable declared as int, got string"]), + case_reject("let_int_string_check_rejected", 'fn main() -> int { x : int = "s"; return 0; }\n', + ["error[TA02]"], cmd="check"), + case_reject("let_slice_to_fixed_rejected", SLICE_SRC, + ["error[TA02]", "Array length constraint not satisfied"], cmd="check"), + case_reject("let_arr_len_literal_rejected", + "fn main() -> int { x : [int;4] = [1,2,3]; return 0; }\n", + ["error[TA02]", "Array length constraint not satisfied"]), + case_reject("let_optional_narrow_rejected", + "fn main() -> int { x : int? = 5; y : int = x; return y; }\n", + ["error[TA02]", "Variable declared as int, got int?"]), + case_reject("let_int_from_struct_rejected", + "struct P { a: int }\nfn main() -> int { p := P { a: 1 }; i : int = p; return i; }\n", + ["error[TA02]"], cmd="check"), + case_reject("let_struct_from_int_rejected", + "struct P { a: int }\nfn main() -> int { p : P = 5; return 0; }\n", + ["error[TA02]", "Variable declared as P, got int"], cmd="check"), + # 全局声明面(同形缺口:注册趟即取注解行)——本任务显式划界覆盖 + case_reject("let_global_rejected", 'g : int = "s";\nfn main() -> int { return 0; }\n', + ["error[TA02]", "Variable declared as int, got string"]), + # 批量声明(`a, b : int = ...`——每个名字一个 LET 节点,逐名判) + case_reject("let_batch_rejected", 'fn main() -> int { a, b : int = 1, "s"; return a; }\n', + ["error[TA02]", "Variable declared as int, got string"], cmd="check"), + # 丢弃名(`_ : int = ...`)同样受判 + case_reject("let_discard_rejected", 'fn main() -> int { _ : int = "s"; return 0; }\n', + ["error[TA02]"], cmd="check"), + # ── 级联抑制(值 = TI_NEVER 错误标记):只发原诊断,不叠 TA02 ── + case_reject("let_error_marker_no_cascade_ident", + "fn main() -> int { x : int = nosuchname; return 0; }\n", + ["error[N01]"], cmd="check", forbid=(), exact_diags=1), + case_reject("let_error_marker_no_cascade_call", + "fn main() -> int { x : int = nosuchfn(); return 0; }\n", + ["error[N06]"], cmd="check", forbid=(), exact_diags=1), + # ── R2 P6 Task 4a(E-11):`-> never` 调用的返回型透传(TA02 面)── + # 改前(RED 实测):`boom()` 推断 = unit(声明注册域守卫 checker.cr:1491 钳位)⇒ + # 声明位豁免③(:526 `val_ti == TI_NEVER`)不触发 ⇒ 误发 TA02。 + # 改后:调用推断 = never ⇒ 豁免③ 生效 ⇒ rc=0。 + case_accept_check("let_never_call_ok", + "fn boom() -> never { loop { } }\n" + "fn main() -> int { x : int = boom(); return 0; }\n"), + # near-miss 负控(突变 Mu2 的稳定牙齿):普通 int 调用的注解位仍判, + # 守卫不得退化成「一律透传 never」。 + case_accept_check("let_ordinary_call_unchanged", + "fn f2() -> int { return 1; }\n" + "fn main() -> int { x : int = f2(); return 0; }\n"), + # 强负控(Mu2 的**转红**牙齿):**普通调用**的返回型若真不符仍须拒—— + # 守卫退化为「无条件 return TI_NEVER」时本例转绿(= 抑制面被误开)。 + case_reject("call_result_mismatch_still_rejected", + "fn f2() -> int { return 1; }\n" + "fn main() -> int { x : char = f2(); return 0; }\n", + ["error[TA02]"], cmd="check", forbid=(), exact_diags=1), + # 宽面负控(C5):语句位调用本无 TA/TF 判定面,改动不得新开抑制/新开诊断。 + case_accept_check("never_stmt_position_unchanged", + "fn boom() -> never { loop { } }\n" + "fn main() -> int { if 1 > 2 { boom(); } return 0; }\n"), + # 登记钉(裁③):赋值位无 never 豁免(EXPR_ASSIGN,checker.cr:3314-3336)—— + # 改前(源 = unit)改后(源 = never)**都**报 TA01;既有缺口、非本改动引入, + # 入 TODO 登记(E-T4-5);同族豁免已在声明位/返回位/if 合并三处,赋值位是剩余缺口。 + case_reject("assign_never_still_ta01", + "fn boom() -> never { loop { } }\n" + "fn main() -> int { busy : int, mut = 0; busy = boom(); return 0; }\n", + ["error[TA01]"], cmd="check", forbid=(), exact_diags=1), + # ── 正控:合法注解 / 无注解 / auto / 泛型 / 可选注入 / 无初值 / 索引形态 ── + case_run("let_exact_ok", "fn main() -> int { x : int = 5; return x; }\n", 5), + case_run("let_no_annot_ok", + "fn main() -> int { x := 5; y : . = 6; z : auto = 7; return x + y + z; }\n", 18), + case_run("let_generic_param_ok", + "fn f[T](v: T) -> T { x : T = v; return x; }\n" + "fn main() -> int { return f(3); }\n", 3), + case_run("let_generic_array_ok", + "fn f[T](v: T) -> T { x : [T; 2] = [v, v]; return x[0]; }\n" + "fn main() -> int { return f(4); }\n", 4), + case_run("let_optional_target_injection_ok", + "fn main() -> int { x : int? = 5; y : int? = None; return 0; }\n", 0), + case_accept_check("let_dyn_annotation_ok", "fn main() -> int { x : dyn = 5; return 0; }\n"), + case_accept_check("let_no_init_ok", "fn main() -> int { x : int; return 0; }\n"), + case_run("let_slice_ann_from_literal_ok", + "fn f(s: [int]) -> int { x : [int] = [1,2,3]; return x[0]; }\n" + "fn main() -> int { return f([1]); }\n", 1), + case_run("let_fixed_ann_exact_literal_ok", + "fn f(s: [int]) -> int { x : [int;3] = [1,2,3]; return s[0]; }\n" + "fn main() -> int { return f([7]); }\n", 7), + case_run("let_named_and_string_ok", + 'struct P { a: int }\n' + 'fn main() -> int { p := P { a: 1 }; q : P = p; s := "hi"; t : string = s; return q.a; }\n', + 1), + case_accept_check("let_generic_element_param_ok", + "fn f[T](v: T) -> T { x : [T] = [v]; return x[0]; }\n" + "fn main() -> int { return f(2); }\n", expect_rc=0), + # 泛型形参注解**不判**(豁免④)——与返回位点既有策略同款同证:`return 5` 在 + # `-> T` 函数体内今天即 rc=0(返回位点的 TYP_GENERIC_PARAM 豁免)⇒ 声明位点同策略。 + case_accept_check("let_generic_param_annotation_not_judged", + "fn f[T](v: T) -> T { return 5; }\n" # 返回位点(既有策略) + "fn g[T](v: T) -> T { x : T = 5; return x; }\n" # 声明位点(本检查同策略) + "fn main() -> int { return 0; }\n", expect_rc=0), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_lexer_parity.py b/tests/selfhost/test_lexer_parity.py new file mode 100644 index 00000000..25bff813 --- /dev/null +++ b/tests/selfhost/test_lexer_parity.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""词法对照测试:`_` 分隔符与宽度后缀在两编译器均被拒绝(2026-09-10 语言面收窄 §2)。""" +import sys, os, subprocess +sys.path.insert(0, 'bootstrap') +from corec.frontend.lexer import Lexer + +BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +COREC = os.path.join(BASE, 'build', 'corec') +BAD = ('1_000', '1_000.5', '0x1_0', '10f32', '1.5f64', '10u8') + +def selfhost(src): + r = subprocess.run(['nice', '-n', '19', COREC, 'run', 'fn main()->int{return %s;}' % src], + cwd=BASE, capture_output=True, text=True) + return r.returncode, r.stdout + r.stderr + +def test_bootstrap_rejects(): + for bad in BAD: + try: + Lexer(bad).tokenize() + assert False, 'bootstrap accepted %s' % bad + except SyntaxError: + pass + +def test_selfhost_rejects(): + # 断言必须命中**我们自己的诊断文本**——否则 `return 1_000` 既有的 TF01 会假绿。 + # 注意:lexer 诊断走 src/compiler/lexer.cr add_error,输出前缀是 `error: `, + # **不是** `error[XX]`(实测 `fn main()->int{return 0xZZ;}` → `error: invalid digit + # in integer literal`,rc=1)——故只断言消息标记 + 出现 `error`,不断言 `error[`。 + for bad, marker in (('1_000', 'not supported'), ('1_000.5', 'not supported'), + ('0x1_0', 'not supported'), ('10f32', 'retired'), + ('1.5f64', 'retired'), ('10u8', 'retired')): + rc, out = selfhost(bad) + assert 'error' in out and marker in out, (bad, rc, out) + +def test_legal_forms_unchanged(): + for src, want in (('0x1f', 31), ('0o17', 15), ('0b1010', 10), ('1000', 232)): + rc, out = selfhost(src) + assert 'error[' not in out, (src, out) + assert rc == want, (src, rc, want) + +if __name__ == '__main__': + for fn in (test_bootstrap_rejects, test_selfhost_rejects, test_legal_forms_unchanged): + fn(); print('PASS', fn.__name__) diff --git a/tests/selfhost/test_live_ranges.py b/tests/selfhost/test_live_ranges.py new file mode 100644 index 00000000..44018105 --- /dev/null +++ b/tests/selfhost/test_live_ranges.py @@ -0,0 +1,728 @@ +#!/usr/bin/env python3 +"""存在区间推导 + 条目版本化(v6 数据基础)——O1/O2 编译冒烟:区间表不破坏寄存器分配。 + +背景:compute_live_ranges 把 alloc_registers 的内联区间构建提取为独立表 +(g_ir_live_ranges,每函数每 var 两条 i64:first_ref/last_ref,指令序), +alloc_registers 改读 live_first/live_last——行为必须与内联扫描一致。 + +Task 2 追加:compute_entries 按定值点切分版本条目(变量 × 版本,24B/条)。 +定值指令 = IR_ALLOC(局部槽初定值)与该变量为目标的每次 IR_STORE +(IR_STORE 形态 ρ(s1):=ρ(s2)——目标在 s1 不在 dest,见 ir-op-semantics.md)。 +调试通道(2026-09-07 regalloc 移后端随迁):`corearch FILE.ccr --dump-entries` +(corec cir 原通道——corearch load .ccr 后内存态自算 compute_live_ranges(尾部 +compute_entries)再出摘要;NOD 载入流与 corec cir 原 dump 流同构——ccr_v6 +conversion 对照测试曾逐条实证 dump ↔ 落盘 ENT 等价,现数据面归 corearch)。 + +覆盖路径: +- 默认 O1 build:pass_cse 路径(O2 分配在 corearch 侧不运行,仍须正确) +- --opt-level 2 build:corearch 自算 alloc(读新表)+ verify 路径 +- corearch --dump-entries:版本条目断言(多定值变量版本切割 / 参数无定值单条目) + +fib(10)==55 为行为锚点(递归 + 分支密集,区间表错误会破坏寄存器指派)。 + +条目版本化断言注记:brief/计划示例 `x:=1;x=x+1;x=x+2` 预期 x 3 条目 +(按「let 初始化只发射 IR_ALLOC」的 IR 模型估算)。实际 IR 中 `x := 1` 发射 +IR_ALLOC(x) + IR_STORE(x←1) 两条定值(ir_gen.cr EXPR_LET 路径),故 x 定值点 = +ALLOC + 3×STORE = 4 个版本条目。规则照计划原文逐字实现(每次 IR_STORE/IR_ALLOC +定值切分一个版本),测试按实际 IR 断言 4 条目 + 版本区间结构不变量。 +""" + +import os +import re +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" + + +def corearch_chan(source: str, *flags) -> tuple: + """数据面/判定调试通道(随迁 corearch):corec ccr 产 .ccr(-O0 无 opt + pass 干扰——NOD = pre-CSE 流)→ corearch load 后自算执行 flag。返回 + (rc, stdout+stderr)。""" + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(source) + path = f.name + ccr = path[:-3] + ".ccr" + try: + b = subprocess.run([str(COREC), "ccr", path, "--opt-level", "0", "-o", ccr], + capture_output=True, text=True, cwd=BASE, timeout=120) + if b.returncode != 0: + return 999, f"corec ccr failed:\n{b.stdout[-500:]}\n{b.stderr[-500:]}" + r = subprocess.run([str(COREARCH), ccr] + list(flags), + capture_output=True, text=True, cwd=BASE, timeout=120) + return r.returncode, r.stdout + r.stderr + finally: + for p in (path, ccr): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str, extra_flags) -> int: + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(source) + src = f.name + out = src[:-3] + try: + b = subprocess.run([str(COREC), "build", src, "-o", out, "--static"] + extra_flags, + capture_output=True, text=True, cwd=BASE, timeout=120) + if b.returncode != 0: + print(f" build stderr: {b.stderr.strip()[-500:]}") + return 999 + r = subprocess.run([out], capture_output=True, text=True, timeout=10) + return r.returncode + finally: + for p in (src, out, out + ".ccr"): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def dump_entries(source: str) -> tuple: + """corearch --dump-entries 通道:返回 (rc, stdout)。""" + return corearch_chan(source, "--dump-entries") + + +ENTRY_LINE = re.compile( + r"^e (\d+) var (\d+) name=(\S+) v (\d+) def (-?\d+) kind=(\S+) " + r"live (\d+)\.\.(\d+) home (-?\d+) flags (\d+)$") +HEADER_LINE = re.compile(r"^== entries func (\d+) \((.*)\): (\d+)$") + + +def parse_entry_blocks(out: str) -> dict: + blocks = {} + cur = None + for raw in out.splitlines(): + ln = raw.strip() + m = HEADER_LINE.match(ln) + if m: + blocks[m.group(2)] = [] + cur = blocks[m.group(2)] + continue + m = ENTRY_LINE.match(ln) + if m and cur is not None: + cur.append({ + "e": int(m.group(1)), "var": int(m.group(2)), "name": m.group(3), + "v": int(m.group(4)), "def": int(m.group(5)), "kind": m.group(6), + "ls": int(m.group(7)), "le": int(m.group(8)), + "home": int(m.group(9)), "flags": int(m.group(10)), + }) + return blocks + + +def check_versioned_entries() -> tuple: + """Task 2:多定值变量版本切割断言(GC 批 2 扩权后同步:定值 = dest≥0 全定值)。 + + x := 1 在 IR 中 = IR_ALLOC + IR_STORE(let 初始化发射两条定值), + 再加 x=x+1 / x=x+2 两条 STORE → x 应有 4 个版本条目(1×ALLOC + 3×STORE), + 版本区间按 [def_j, min(def_{j+1}-1, last_ref)] 切割(全局指令序闭区间): + e_j.live_end == def_{j+1} - 1(j < 末版),末版 live_end = var 的 last_ref ≥ 其 def。 + + GC 批 2(定值识别扩权,格式定稿 §4.1 dest≥0 = 定值点):producer 临时值 + (CONST/BINARY/ARENA_NEW 等)也按定值切版本——按 name 分组断言(跨组断言 + 会因临时值条目数变化而脆弱);每个 def'd 条目 ls == def 与同组版本链 + 不变量仍全表成立。 + """ + src = "fn identity(n:int)->int{return n;}\n" \ + "fn main()->int{x:=1;x=x+1;x=x+2;return x;}\n" + rc, out = dump_entries(src) + if rc != 0: + return False, f"cir --dump-entries rc={rc}\n{out[-500:]}" + blocks = parse_entry_blocks(out) + if "main" not in blocks: + return False, f"no 'main' entry block in dump:\n{out[-500:]}" + ent = blocks["main"] + + # x 的 4 版本:ALLOC + 3×STORE(GC 批 2 语义下 x 只被 ALLOC/STORE 定值) + multi = [e for e in ent if e["name"] == "x"] + if len(multi) != 4: + return False, f"main/x: expected 4 version entries (ALLOC+3 STORE), got {len(multi)}:\n{out}" + if [x["kind"] for x in multi] != ["ALLOC", "STORE", "STORE", "STORE"]: + return False, f"main/x: def kinds != ALLOC,STORE,STORE,STORE:\n{multi}" + if [x["v"] for x in multi] != [1, 2, 3, 4]: + return False, f"main/x: version ordinals != 1..4:\n{multi}" + defs = [x["def"] for x in multi] + if any(defs[i] >= defs[i + 1] for i in range(3)): + return False, f"main/x: def points not strictly increasing:\n{defs}" + for x in multi: + if x["ls"] != x["def"]: + return False, f"main/x: version live_start != def ({x}):\n{multi}" + for i in range(3): + if multi[i]["le"] != defs[i + 1] - 1: + return False, f"main/x: version {i+1} live_end != def[{i+1}]-1:\n{multi}" + if multi[3]["le"] < multi[3]["def"]: + return False, f"main/x: last version live_end < its def:\n{multi}" + + # GC 批 2 扩权实证:main 里 producer 临时值(CONST/BINARY/ARENA_NEW)也有 + # def'd 版本条目(旧实现止于 ALLOC/STORE——def'd 条目仅 x 一组 4 条)。 + kinds = [e["kind"] for e in ent if e["def"] >= 0] + for want in ("ALLOC", "CONST", "BINARY", "ARENA_NEW"): + if want not in kinds: + return False, f"main: expected a def'd entry kind={want} (dest>=0 producers " \ + f"versioned), got kinds {sorted(set(kinds))}:\n{out}" + # 排除集实证:STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 的 dest 非定值——不产生条目 + if any(e["kind"] in ("STORE_INDEX_VAR", "STORE_PTR", "DYN_DISPATCH") for e in ent): + return False, f"main: excluded non-def dest op created an entry:\n{out}" + # 全表不变量:def'd 条目 ls == def;同 name 组内版本序 1..k(dump v 序) + for e in ent: + if e["def"] >= 0 and e["ls"] != e["def"]: + return False, f"main: def'd entry live_start != def ({e}):\n{ent}" + if e["def"] < 0 and e["kind"] != "-": + return False, f"main: no-def entry has a def kind ({e}):\n{ent}" + if any(e["home"] != -1 or e["flags"] != 0 for e in ent): + return False, f"main: entries must have home=-1 flags=0 (unassigned):\n{ent}" + + # 无定值但有引用(函数参数):单条目 def=-1,区间 [first_ref,last_ref] + if "identity" not in blocks or not blocks["identity"]: + return False, f"identity: expected an entry block:\n{out}" + id_ent = blocks["identity"] + # 参数 n 单条目 def=-1(GC 批 2 后 identity 的 _arena 等 producer 有 def'd 条目) + n_ent = [e for e in id_ent if e["name"] == "n"] + if len(n_ent) != 1 or n_ent[0]["def"] != -1 or n_ent[0]["kind"] != "-": + return False, f"identity/n: param must be a single def=-1 entry:\n{id_ent}" + if n_ent[0]["ls"] < 0 or n_ent[0]["ls"] > n_ent[0]["le"]: + return False, f"identity/n: bad no-def live range:\n{n_ent[0]}" + return True, "entries versioning OK" + + +def check_array_birth_versions() -> tuple: + """GC 批 2(Task 2 评审 Important #2):内存对象诞生 = 定值点。 + + `a : [int; 3]` 无初值声明不发 IR_ALLOC——旧实现只认 ALLOC/STORE 定值 → + a 首个版本化定值 = 后续 STORE 重定值,诞生与重定值之间的读窗口无条目覆盖。 + dest≥0 全定值扩权后:a 的版本 = [ALLOC_ARRAY(诞生), STORE(重定值)] 两条, + 区间按 [def_j, def_{j+1}-1] 切割——诞生版覆盖元素写/拷贝读(a[0]=5、 + b := a),STORE 版覆盖重定值后读(a[0]+a[1]+a[i])。""" + src = "fn main()->int{\n" \ + " a : [int; 3];\n" \ + " a[0] = 5; a[1] = 7;\n" \ + " b := a;\n" \ + " a = [1, 2, 3];\n" \ + " i := 0;\n" \ + " a[i] = 9;\n" \ + " return a[0] + a[1] + b[0] + a[i];\n" \ + "}\n" + rc, out = dump_entries(src) + if rc != 0: + return False, f"cir --dump-entries rc={rc}\n{out[-500:]}" + blocks = parse_entry_blocks(out) + if "main" not in blocks: + return False, f"no 'main' entry block in dump:\n{out[-500:]}" + ent = blocks["main"] + + a_ent = [e for e in ent if e["name"] == "a"] + if len(a_ent) != 2: + return False, f"main/a: expected 2 versions (ALLOC_ARRAY birth + STORE), got {len(a_ent)}:\n{out}" + if [e["kind"] for e in a_ent] != ["ALLOC_ARRAY", "STORE"]: + return False, f"main/a: kinds != ALLOC_ARRAY,STORE:\n{a_ent}" + if [e["v"] for e in a_ent] != [1, 2]: + return False, f"main/a: version ordinals != 1,2:\n{a_ent}" + d0, d1 = a_ent[0]["def"], a_ent[1]["def"] + if d0 >= d1: + return False, f"main/a: def points not increasing:\n{a_ent}" + if a_ent[0]["ls"] != d0 or a_ent[1]["ls"] != d1: + return False, f"main/a: version live_start != def:\n{a_ent}" + # 诞生版区间止于重定值前(元素写/读在定值点之后——ALLOC_ARRAY 版覆盖它们) + if a_ent[0]["le"] != d1 - 1: + return False, f"main/a: birth version live_end != def2-1:\n{a_ent}" + if a_ent[1]["le"] < d1: + return False, f"main/a: STORE version live_end < its def:\n{a_ent}" + # b := a(读 a)的指令在 a 诞生版区间内——元素写读窗口被条目覆盖的实证: + # a_ent[0] 区间 [d0, d1-1] 必须非空(有读才叫「窗口无覆盖」场景成立) + if a_ent[0]["le"] <= a_ent[0]["ls"]: + return False, f"main/a: birth version window empty (no reads between birth " \ + f"and redef?):\n{a_ent}" + # b 的版本 = [ALLOC, STORE](拷贝声明);i 同构 + for nm, expect in (("b", ["ALLOC", "STORE"]), ("i", ["ALLOC", "STORE"])): + g = [e["kind"] for e in ent if e["name"] == nm] + if g != expect: + return False, f"main/{nm}: kinds != {expect}:\n{ent}" + # 排除集实证(动态索引写 a[i]=9:被存值 var 的 dest 直写不产生条目—— + # 该值 var 组内版本数 = producer 数 1,无 STORE_INDEX_VAR 伪造 def) + for e in ent: + if e["def"] >= 0 and e["kind"] in ("STORE_INDEX_VAR", "STORE_PTR", "DYN_DISPATCH"): + return False, f"main: excluded non-def dest op created an entry ({e}):\n{ent}" + return True, "memory-object birth versioning OK" + + +def run_smoke() -> tuple: + """Task 1 存量冒烟:fib(10)==55 at O1-default / O2。""" + src = "fn fib(n:int)->int{if n<2{return n;}return fib(n-1)+fib(n-2);}\n" \ + "fn main()->int{return fib(10);}\n" + passed = 0 + total = 2 + detail = [] + for name, flags in (("O1-default", []), ("O2", ["--opt-level", "2"])): + rc = build_and_run(src, flags) + ok = rc == 55 + print(f"[{'PASS' if ok else 'FAIL'}] {name} live-range smoke: rc={rc}") + if ok: + passed += 1 + else: + detail.append(f"{name} rc={rc}") + return passed == total, "; ".join(detail) if detail else "" + + +def dump_coexist(source: str) -> tuple: + """corearch --dump-coexist 通道:返回 (rc, stdout)。""" + return corearch_chan(source, "--dump-coexist") + + +COEXIST_LINE = re.compile( + r"^== coexist func (\d+) \((\S+)\): ver_conf (\d+) home_conf (\d+)$", + re.MULTILINE) + + +def check_coexistence() -> tuple: + """Task 3:共存推导断言。 + + (a) 版本冲突扫描 = 0(版本按定值点切割,同 var 跨版本必不交——数据自检); + (b) home 冲突 = 0(未分配态 home=-1 不参与,共存互斥判定输入雏形); + (c) 概念断言(Python 侧用 --dump-entries 区间数据):a/b 两变量条目 + 存在区间相交(共存),同一变量两版本区间不相交(不共存)。 + """ + src = "fn main()->int{a:=1;b:=2;return a+b;}\n" + rc, out = dump_coexist(src) + if rc != 0: + return False, f"cir --dump-coexist rc={rc}\n{out[-500:]}" + m = COEXIST_LINE.search(out) + if not m: + return False, f"no coexist line in dump:\n{out[-500:]}" + vc, hc = int(m.group(3)), int(m.group(4)) + if vc != 0 or hc != 0: + return False, f"coexist: ver_conf={vc} home_conf={hc}, expected 0/0:\n{out}" + + # (c) 区间相交概念断言(Python 侧) + rc2, out2 = dump_entries(src) + if rc2 != 0: + return False, f"dump-entries rc={rc2}" + blocks = parse_entry_blocks(out2) + if "main" not in blocks: + return False, "no main block" + ent = blocks["main"] + av = [x for x in ent if x["name"] == "a"] + bv = [x for x in ent if x["name"] == "b"] + if not av or not bv: + return False, f"a/b entries missing:\n{out2}" + # 取末版本(return 处活跃的是最后定值版——ALLOC 空版区间短暂不相交属正常; + # a/b 在此程序各有 ALLOC+STORE 两版本) + if len(av) < 2 or len(bv) < 2: + return False, f"a/b should each have >=2 versions (ALLOC+STORE):\n{out2}" + a0, b0 = av[-1], bv[-1] + coexist = a0["ls"] <= b0["le"] and b0["ls"] <= a0["le"] + if not coexist: + return False, f"a/b should coexist, ranges {a0['ls']}..{a0['le']} vs {b0['ls']}..{b0['le']}" + # a 两版本(ALLOC 空版 vs STORE 值版)区间不相交——同变量版本不共存 + if av[0]["ls"] <= av[1]["le"] and av[1]["ls"] <= av[0]["le"]: + return False, f"a versions should not coexist: {av[0]['ls']}..{av[0]['le']} vs {av[1]['ls']}..{av[1]['le']}" + return True, "coexistence OK" + + +def check_regalloc(source: str, extra_flags) -> tuple: + """corearch --check-regalloc 通道:O2 强制分配 + 判定自检,返回 (rc, stdout)。""" + return corearch_chan(source, "--check-regalloc", *extra_flags) + + +# 措辞注记(Task 4 输出面中性化——测试 = 行为锚, 措辞 = 输出面):内核违反 +# 诊断前缀 "regalloc-consistency" → "entry-consistency"(条目版本表一致性—— +# 中性语义名;内核措辞批,判定语义/规则号/数值结构不变);SUMMARY_LINE/ +# ASSIGN_LINE 前缀 = 实例侧(corearch.cr 通道名 --check-regalloc 命名空间) +# 输出——措辞批范围外,断言保持。 +SUMMARY_LINE = re.compile(r"^regalloc-consistency: funcs (\d+) violations (\d+)$", re.MULTILINE) +VIOLATION_LINE = re.compile(r"^entry-consistency: func \d+ \(.+\): rule (\d+) violation", re.MULTILINE) +ASSIGN_LINE = re.compile(r"^regalloc-assign: (\d+) pairs$", re.MULTILINE) + + +def check_regalloc_consistency() -> tuple: + """Task 5 绿路径 + CAG 真实化回归:O2 分配的**正确程序**自检静默通过 + (rc=0、无 violation 行)。 + + CAG 前(2026-09-06 注记,opt.cr alloc 区头):分配结果恒为空(rc=0 缺陷 + ——free 遍时机 + 循环末只收仍活跃 var → g_opt_meta 无 REG_ASSIGN 对),规则 + ①/② 在真实数据上平凡通过(无寄存器驻留条目)。CAG 挂账清项后分配真实化: + 本绿路径消费真实分配结果——寄存器组规则 ①/② 在真数据上必须仍绿(判定 + 消费端首次吃到真数据;误报即红)。规则 ①/② 的实际触发证据 = 注入红路径 + (check_regalloc_violations / check_regalloc_read_gap_nonfunc0,注入现以 + 改写真实分配输出为手段);fib/6 变量绿程序按行为锚点保留。""" + for name, src in ( + ("fib", "fn fib(n:int)->int{if n<2{return n;}return fib(n-1)+fib(n-2);}\n" + "fn main()->int{return fib(10);}\n"), + ("multi-var", "fn main()->int{a:=1;b:=2;c:=3;d:=4;e:=5;f:=6;" + "return a+b+c+d+e+f;}\n"), + ): + rc, out = check_regalloc(src, []) + if rc != 0: + return False, f"check-regalloc({name}) rc={rc}:\n{out[-500:]}" + m = SUMMARY_LINE.search(out) + if not m: + return False, f"check-regalloc({name}): no summary line:\n{out[-500:]}" + if int(m.group(2)) != 0: + return False, f"check-regalloc({name}): violations != 0:\n{out[-500:]}" + if VIOLATION_LINE.search(out): + return False, f"check-regalloc({name}): violation lines on correct program:\n{out[-500:]}" + return True, "regalloc consistency self-check OK" + + +def check_regalloc_real_use() -> tuple: + """CAG 挂账清项(opt.cr alloc_registers rc=0 缺陷):寄存器真实分配实证。 + + --check-regalloc 在 O2 强制分配后打印看门狗行 regalloc-assign(g_opt_meta + REG_ASSIGN 对总数)。修复前 rc 恒为 0(free 遍 `last_ref < ii` 复位 + reg_idx + 单调不复用 + 循环末只收集仍活跃 var → meta 恒空 → 后端全栈发射——正确但无 + 寄存器加速)→ 本断言红;真实分配后 pairs > 0 → 绿。 + + 计数载体注记(评审 I-2 措辞修正):看门狗数的是**整语料** REG_ASSIGN 对 + (被测 main + 15 个自动并入的标准库函数,funcs 16)——断言对象是总数 ≥ 5 + (证明分配真实发生、防 rc=0 回退),不断言被测 main 的 6 变量各获分配。 + 6 共存 int var 的作用是把共存压力拉到 5 callee-saved 上限,使分配必然发生 + 且必有栈驻留余数(不分配 = 保留栈 home;分配器无驱逐事件,不设驱逐路径)。 + """ + src = "fn main()->int{a:=1;b:=2;c:=3;d:=4;e:=5;f:=6;return a+b+c+d+e+f;}\n" + rc, out = check_regalloc(src, []) + if rc != 0: + return False, f"check-regalloc rc={rc}:\n{out[-500:]}" + m = ASSIGN_LINE.search(out) + if not m: + return False, (f"regalloc-assign watchdog line missing in check-regalloc " + f"output:\n{out[-500:]}") + n = int(m.group(1)) + if n <= 0: + return False, (f"regalloc-assign: {n} pairs — 寄存器从未真实分配(rc=0 " + f"缺陷回退):\n{out[-500:]}") + if n < 5: + return False, (f"regalloc-assign: {n} pairs < 5 — 全语料(main + stdlib)" + f"共存压力下应远超 5 对:\n{out[-500:]}") + return True, f"regalloc real assignment OK ({n} pairs)" + + +LOOP_CARRY_SRC = ( + "fn main()->int{\n" + " i:=0;s:=0;\n" + " loop {\n" + " if i>=4 { break; }\n" + " i = i + 1;\n" + " d := 5;\n" + " s = s + d;\n" + " }\n" + " return s;\n" + "}\n" +) + + +def check_regalloc_loop_carry() -> tuple: + """CAG 上下文贪心——循环携带值语义锚(region 生命周期上下文)。 + + i 的读点(cond)在文字序上先于其重定值(i=i+1)→ 每轮迭代末写入的值跨回边 + 存活(cond 读的是上一轮 i 的值)。朴素 [first_ref, last_ref] 文字窗口序 + First Fit 会把 i 的寄存器(窗口止于其末引用 = 重定值点)与循环尾文字区间不 + 交的 var(d := 5 的 ALLOC/STORE 在 i 末引用之后)共享 → d 的定值在回边前 + 污染 i 的寄存器 → 下轮 cond 读到 5 → 提前 break(s=5)。上下文贪心把携带 + 值(region 内首引用为读)窗口扩至整函数 → 不共享 → s = 4×5 = 20。 + + 断言 O2(真实分配)行为 == 20;O1(无分配路径基线)同断言。 + """ + for name, flags in (("O1-baseline", []), ("O2-regalloc", ["--opt-level", "2"])): + rc = build_and_run(LOOP_CARRY_SRC, flags) + ok = rc == 20 + print(f"[{'PASS' if ok else 'FAIL'}] loop-carry {name}: rc={rc}") + if not ok: + return False, f"loop-carry {name} rc={rc}, expected 20" + return True, "loop-carried register semantics OK" + + +def check_regalloc_cond_def_carry() -> tuple: + """CAG 复审 Critical 回归(条件定值跨回边存活):条件定值 = span 内首引用 + 是定值指令(def-form)但未必每轮执行——跳过路径上旧值仍跨回边存活。 + + 复现 A(v_noz):x 的定值 `if i==0 { x = 7; }` 是回边 span 内 x 的首引用 + (def-form),但 i==1 轮该 STORE 不执行 → s=s+x 读到的是上一轮的值—— + x 实际携带。旧判定(只认「span 内首引用为读」为携带)把 x 判不携带 → + 阶段 4 按文字窗口终点归还 x 的寄存器,循环尾 `i=i+1` 的文字区间不交的临时 + var 拿到同一寄存器 → 每轮回边前污染 → 下轮读 x 读到 i+1 临时值(8=7+1)。 + 复现 B(conddef_repro3):同形 + 尾 `z:=5; s=s+z;`(期望 24,错读 22)。 + + pre-CAG O2(rc=0 全栈发射)正确(14/24)→ 误编译由 CAG 寄存器真实分配引入。 + 断言 O2(真实分配)== 期望;O1(无分配路径基线)同断言。 + """ + for name, src, expect in ( + ("v_noz", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " loop {\n" + " if i>=2 { break; }\n" + " if i==0 { x = 7; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " }\n" + " return s;\n" + "}\n", 14), + ("conddef-z", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " loop {\n" + " if i>=2 { break; }\n" + " if i==0 { x = 7; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " z := 5;\n" + " s = s + z;\n" + " }\n" + " return s;\n" + "}\n", 24), + ): + for mode, flags in (("O1-baseline", []), ("O2-regalloc", ["--opt-level", "2"])): + rc = build_and_run(src, flags) + ok = rc == expect + print(f"[{'PASS' if ok else 'FAIL'}] cond-def-carry {name} {mode}: rc={rc}") + if not ok: + return False, f"cond-def-carry {name} {mode} rc={rc}, expected {expect}" + return True, "conditional-def loop-carried register semantics OK" + + +def check_regalloc_else_def_carry() -> tuple: + """CAG 复审第二轮回归(else 臂条件定值 = BRANCH-skip 差分残留)。 + + 同族机制残留于 else 臂:if-else 发射布局 + BRANCH c, L_then, L_else; L_then: A; JUMP L_merge; L_else: B; JUMP L_merge; + 中 else 体 B(`x = 7`)被 then 尾的无条件 JUMP 结构性跳过——阶段 2 差分标记 + 只扫 IR_BRANCH 的 forward 目标(B 正落在 BRANCH 自身 else 目标的标号处、 + 区间 [pp+1, t−1] 不含终点 t),漏 IR_JUMP → B 内定值 x 判「每轮必执行、 + 不携带」→ 阶段 4 文字窗口终点归还 x 寄存器 → 循环尾临时同寄存器 → 回边前 + 污染 → 下轮读 x 错值(p_else1/p_while 应 22 实得 20;pre-CAG O2 正确)。 + + 修复 = 对 span 内 forward IR_JUMP (u, t)(t ∈ (u, b0])同样生成 [u+1, t−1] + 差分。over-mark 只损利用率、方向安全(continue/else 臂/链式 JUMP 推演无害)。 + + 复现 A(p_else1):loop + break 头 + `if i<2 {s=s+1} else {x=7}`(应 22)。 + 复现 B(p_while):while 头条件同形 if-else(应 22)。 + 断言 O2(真实分配)== 期望;O1(无分配路径基线)同断言。 + """ + for name, src, expect in ( + ("else1", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " loop {\n" + " if i>=4 { break; }\n" + " if i<2 { s = s + 1; } else { x = 7; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " }\n" + " return s;\n" + "}\n", 22), + ("else-while", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " while i < 4 {\n" + " if i<2 { s = s + 1; } else { x = 7; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " }\n" + " return s;\n" + "}\n", 22), + ): + for mode, flags in (("O1-baseline", []), ("O2-regalloc", ["--opt-level", "2"])): + rc = build_and_run(src, flags) + ok = rc == expect + print(f"[{'PASS' if ok else 'FAIL'}] else-def-carry {name} {mode}: rc={rc}") + if not ok: + return False, f"else-def-carry {name} {mode} rc={rc}, expected {expect}" + return True, "else-arm conditional-def loop-carried register semantics OK" + + +def check_regalloc_else_controls() -> tuple: + """else 臂修复的控制组(无过标/无回归):同族形状的既有绿行为必须保持。 + + - then2(p_then2):`if i<2 {x=7} else {s=s+1}`——定值在 then 臂(BRANCH + else 目标区间已覆盖,第一轮修复路径)→ 30 保持(新增 JUMP 差分不得扰动)。 + - cont(p_cont):continue 臂 + then 臂定值 + else 读先行自刷新 s → 31 保持 + (continue 回跳目标 ≤ 跳源,JUMP 差分不计回跳——误标会扩窗损利用率, + 行为断言兜底正确性)。 + 断言 O2(真实分配)== 期望。 + """ + for name, src, expect in ( + ("then2", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " loop {\n" + " if i>=4 { break; }\n" + " if i<2 { x = 7; } else { s = s + 1; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " }\n" + " return s;\n" + "}\n", 30), + ("cont", "fn main()->int{\n" + " i:=0;s:=0;\n" + " x : int, mut = 3;\n" + " loop {\n" + " if i>=5 { break; }\n" + " if i==0 { i = i + 1; continue; }\n" + " if i<2 { x = 7; } else { s = s + 1; }\n" + " s = s + x;\n" + " i = i + 1;\n" + " }\n" + " return s;\n" + "}\n", 31), + ): + rc = build_and_run(src, ["--opt-level", "2"]) + ok = rc == expect + print(f"[{'PASS' if ok else 'FAIL'}] else-control {name} O2: rc={rc}") + if not ok: + return False, f"else-control {name} O2 rc={rc}, expected {expect}" + return True, "else-arm control shapes unchanged (then2=30, cont=31)" + + +def check_regalloc_violations() -> tuple: + """Task 5 红路径:注入冲突条目 → verify 返回违反(rc=1 + rule N 诊断行)。 + 注入经测试钩子(cir 隐藏 debug 标志——真实构建路径永不注入): + - --inject-home-conflict: 两条共存不同 var 条目 home 置同槽 → 规则 1(home 组) + - --inject-reg-conflict : 未分配 var f 伪造 meta 对 (f, e 的寄存器) → 规则 1(寄存器组) + - --inject-read-gap : 寄存器驻留 var 末版区间截断到定值点 → 规则 2(读点无版本) + O2 正确程序(无注入)rc=0 已在 check_regalloc_consistency 断言——同一二进制 + 注入后 rc=1 即证明 verify 真消费了分配/条目数据而非恒真。""" + src = "fn main()->int{a:=1;b:=2;c:=3;d:=4;e:=5;f:=6;return a+b+c+d+e+f;}\n" + for flag, rule in (("--inject-home-conflict", "1"), + ("--inject-reg-conflict", "1"), + ("--inject-read-gap", "2")): + rc, out = check_regalloc(src, [flag]) + if rc != 1: + return False, f"check-regalloc +{flag}: rc={rc}, expected 1:\n{out[-500:]}" + m = SUMMARY_LINE.search(out) + if not m or int(m.group(2)) == 0: + return False, f"check-regalloc +{flag}: summary missing or 0 violations:\n{out[-500:]}" + v = VIOLATION_LINE.search(out) + if not v or v.group(1) != rule: + return False, f"check-regalloc +{flag}: no rule {rule} violation line:\n{out[-500:]}" + return True, "regalloc violations detected (rules 1/1/2)" + + +def check_regalloc_read_gap_nonfunc0() -> tuple: + """F1 回归(评审发现):规则 ② 注入目标 = 非 func 0 函数。 + + rl_rule2_func 曾以局部扫描下标 ii 对照条目表全局坐标(ent_def/ent_live_end + 存 ist+局部),非 func 0 函数(ist > 0)上规则 ② 失明(只漏报不误报)—— + func 0 上 ii == inst 掩盖缺陷。本用例要求 --inject-read-gap 注入到非 func 0 + 函数:旧实现漏报 rc=0(红),修复(三处比较改用 inst := ist + ii)后 + rc=1 + 非 func 0 规则 ② 诊断行(绿)。 + + GC 批 2 注记:定值扩权后每函数的 _arena 等首条 def 使 func 0 恒可注入—— + inject_read_gap 的迭代序改为 1..n-1 再 0(调试钩子,真实路径永不注入), + 注入恒落非 func 0 函数;注入目标函数不再固定为 sum6(按函数序首个可注入 + 者定,此处为 ist>0 的 stdlib/用户函数)——断言 = func > 0 + rule 2。""" + src = "fn main()->int{return n0(1,2);}\n" \ + "fn n0(a:int,b:int)->int{return a+b;}\n" \ + "fn n1(a:int,b:int)->int{return a+b;}\n" \ + "fn n2(a:int,b:int)->int{return a+b;}\n" \ + "fn n3(a:int,b:int)->int{return a+b;}\n" \ + "fn n4(a:int,b:int)->int{return a+b;}\n" \ + "fn n5(a:int,b:int)->int{return a+b;}\n" \ + "fn n6(a:int,b:int)->int{return a+b;}\n" \ + "fn n7(a:int,b:int)->int{return a+b;}\n" \ + "fn sum6()->int{a:=1;b:=2;c:=3;d:=4;e:=5;f:=6;return a+b+c+d+e+f;}\n" + rc, out = check_regalloc(src, ["--inject-read-gap"]) + if rc != 1: + return False, f"check-regalloc +--inject-read-gap (non-func0): rc={rc}, expected 1:\n{out[-500:]}" + m = SUMMARY_LINE.search(out) + if not m or int(m.group(2)) == 0: + return False, f"check-regalloc +--inject-read-gap (non-func0): summary missing or 0 violations:\n{out[-500:]}" + v = re.search(r"^entry-consistency: func (\d+) \((\S+)\): rule (\d+) violation", + out, re.MULTILINE) + if not v: + return False, f"check-regalloc +--inject-read-gap (non-func0): no violation line:\n{out[-500:]}" + if v.group(3) != "2" or int(v.group(1)) == 0: + return False, (f"check-regalloc +--inject-read-gap (non-func0): violation not on " + f"non-func-0 rule 2 (got func {v.group(1)} ({v.group(2)}) rule " + f"{v.group(3)}):\n{out[-500:]}") + return True, f"read-gap detected on non-func-0 func (rule 2, func {v.group(1)} {v.group(2)})" + + +def check_coexist_oob_guard() -> tuple: + """GC-1(M-5)上界防御:entries_coexist 对越界条目索引(e1/e2 ≥ + g_entry_count)必须返回 0——不越界读。守卫缺失时槽后区域是分配器零 + 填充(rt.s alloc zero-init;条目表容量 ≥ 计数),全零条目区间 [0,0] + 与任何区间相交 → 误判共存返回 1(或读到缓冲外崩溃)。 + + --inject-coexist-oob 探针通道:输出 "coexist-oob-guard: ", + r != 0(探针自检失败)→ 退出码 1。与 dump-coexist 摘要同源同前置 + (compute_live_ranges),真实构建路径永不注入。""" + src = "fn main()->int{a:=1;b:=2;return a+b;}\n" + rc, out = corearch_chan(src, "--inject-coexist-oob") + if rc != 0: + return False, f"corearch --inject-coexist-oob rc={rc}\n{out[-500:]}" + m = re.search(r"^coexist-oob-guard: (\d+)$", out, re.MULTILINE) + if not m: + return False, f"no coexist-oob-guard line:\n{out[-500:]}" + if m.group(1) != "0": + return False, f"coexist-oob-guard: OOB probe returned {m.group(1)}, want 0:\n{out[-500:]}" + return True, "coexist OOB guard OK" + + +def main() -> int: + if not COREC.exists(): + print("[FAIL] missing build/corec") + return 1 + passed = 0 + total = 13 + ok, msg = run_smoke() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] live-range smoke: {msg}") + ok, msg = check_versioned_entries() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] entries versioning: {msg}") + ok, msg = check_array_birth_versions() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] array-birth versioning: {msg}") + ok, msg = check_coexistence() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] coexistence: {msg}") + ok, msg = check_coexist_oob_guard() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] coexist OOB guard: {msg}") + ok, msg = check_regalloc_consistency() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc consistency: {msg}") + ok, msg = check_regalloc_real_use() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc real use: {msg}") + ok, msg = check_regalloc_loop_carry() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc loop carry: {msg}") + ok, msg = check_regalloc_cond_def_carry() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc cond-def carry: {msg}") + ok, msg = check_regalloc_else_def_carry() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc else-def carry: {msg}") + ok, msg = check_regalloc_else_controls() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc else controls: {msg}") + ok, msg = check_regalloc_violations() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc violations: {msg}") + ok, msg = check_regalloc_read_gap_nonfunc0() + if ok: + passed += 1 + print(f"[{'PASS' if ok else 'FAIL'}] regalloc read-gap non-func0: {msg}") + print(f"{passed}/{total} passed") + return 0 if passed == total else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_lsp.py b/tests/selfhost/test_lsp.py index c591c5e9..a869cbe3 100644 --- a/tests/selfhost/test_lsp.py +++ b/tests/selfhost/test_lsp.py @@ -382,6 +382,41 @@ def test_large_frame_body(): assert diagnostics["params"]["diagnostics"] == [], diagnostics shutdown_and_wait(proc) +def test_double_open_bridge_cache_reset(): + """R2 P2a Task 3 评审 Critical:桥接缓存(g_term_map;P5 T5 前名 g_shadow_map)必须随类型表重置失效。 + + 机制:R2 P2a Task 3 起判定路径**无条件**调用 sh_term_of_ti(此前仅 --type-shadow 下译项; + 该旗标已随 R2 P5 Task 5 影子层下线删除), + 而桥接缓存 key = ti 本体;corelsp 每请求 `reset_frontend_state → check_all → init_types` + 会清空重建类型表(**行号空间复用**)→ 陈旧 ti→term 命中即返回 ⇒ 两个不同类型被判等 + (静默漏报)。本用例:同 URI 两次 didOpen,只把 `a` 的数组**元素型** int→bool,第二次 + 必须报出 1 条 `Assignment type mismatch`(新进程单跑 V2 = 1 条,见 fixture 注释)。 + 修复前实测 RED:V1=0 / V2=0(应为 V2=1)——证据见 r2p2-task-3-report §13。 + """ + proc = subprocess.Popen([BIN], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + send(proc, {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}) + uri = "file:///tmp/bridge_cache_reset.cr" + + def open_doc(text, version): + # didOpen 是通知:直接写帧再读一帧(不能用 send()——它会吞掉 publishDiagnostics) + body = json.dumps({"jsonrpc": "2.0", "method": "textDocument/didOpen", + "params": {"textDocument": {"uri": uri, "version": version, + "text": text}}}).encode() + proc.stdin.write(f"Content-Length: {len(body)}\r\n\r\n".encode() + body) + proc.stdin.flush() + return read_frame(proc) + + r1 = open_doc(BRIDGE_V1, 1) + assert r1 is not None and r1["method"] == "textDocument/publishDiagnostics", r1 + assert r1["params"]["diagnostics"] == [], r1 # 同为 [int;3] → 无诊断 + r2 = open_doc(BRIDGE_V2, 2) + assert r2 is not None and r2["method"] == "textDocument/publishDiagnostics", r2 + diags = r2["params"]["diagnostics"] + assert len(diags) == 1, r2 # [bool;3] ← [int;3]:必须报出 + assert "Assignment type mismatch" in diags[0]["message"], r2 + shutdown_and_wait(proc) + + # ── 统一驱动(Task 7)─────────────────────────────────────────────────────── # 按序执行全部测试组(每组独立 spawn,隔离全局状态);失败即非零退出。 @@ -395,6 +430,7 @@ def test_large_frame_body(): test_semantic_tokens_multiline, test_stdout_pollution_guard, test_large_frame_body, + test_double_open_bridge_cache_reset, ] def main() -> int: @@ -417,5 +453,23 @@ def main() -> int: SAMPLE = 'fn main() -> int { return 42; }' BAD = 'fn main() -> int { return ; }' +# 桥接缓存跨请求失效回归(R2 P2a Task 3 评审 Critical)——两版只差 `a` 的元素型: +# V1 `[int;3]` → 新进程 rc=0(ok);V2 `[bool;3]` → 新进程 1 条 error[TA01] Assignment +# type mismatch。两次 didOpen 之间类型行号被复用(同 URI 重开),故缓存不失效即漏报。 +BRIDGE_V1 = ("fn f() -> int {\n" + " a : [int;3] = [1,2,3];\n" + " b : [int;3], mut = [4,5,6];\n" + " b = a;\n" + " return 0;\n" + "}\n" + "fn main() -> int { return f(); }\n") +BRIDGE_V2 = ("fn f() -> int {\n" + " a : [bool;3] = [true,false,true];\n" + " b : [int;3], mut = [4,5,6];\n" + " b = a;\n" + " return 0;\n" + "}\n" + "fn main() -> int { return f(); }\n") + if __name__ == "__main__": sys.exit(main()) diff --git a/tests/selfhost/test_match_exhaust.py b/tests/selfhost/test_match_exhaust.py new file mode 100644 index 00000000..1c45682f --- /dev/null +++ b/tests/selfhost/test_match_exhaust.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""R2 P3 Task 3:match 穷尽性真判定(补集空性 + 具体变体反例)行为覆盖集。 + +判据(正/负/边界/登记;语义见 docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md Task 3): + + 正:全覆盖过(**解释器 + ELF 双路径同值**)/ 通配臂吸收 / 绑定臂吸收 / 限定名模式 + / payload 变体 / 泛型枚举实例 / 单变体枚举 / 枚举形参位 + 负:缺臂 ⇒ rc=1 + `error[TM03]` + 反例含具体缺失**变体名** + **无产物**(绝不静默 rc=0—— + 修复前实测:缺臂产物照出,未匹配值静默得 0);无臂 ⇒ 同上;`check` 子命令同拒 + 登记:非枚举域不判穷尽(int 匹配 rc=0);不可映射模式(枚举匹配里混字面量臂)不判穷尽 rc=0 + 软面:重复臂 ⇒ `error[TM04]` **非硬门**(rc=0 + 产物照出)——诊断面登记,非 rc 面收紧 + +背景(Task 3 前实测,本套件即其回归):`EC_TM_*`(9001-9009)此前仅定义零 raise,match +**零检查**;`match c { Red => 1, Green => 2 }`(缺 Blue)check/build rc=0、产物可跑, +`c := Blue()` 时静默落空得 0(探针 p1b 实测 rc=100 = 0 + 100)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="build"): + """返回 (result, out_path, src_path);cmd = build(ELF 产物)| check(仅前端)。""" + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + """返回 (compile_result, run_result|None)。""" + r, out, src = _compile(source) + try: + if r.returncode != 0: + return r, None + rr = subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + return r, rr + finally: + _cleanup(src, out, out + ".ccr") + + +def run_interp(source: str): + """解释器路径(共享同一 run_frontend ⇒ 同一判定面);rc = main 返回值。""" + return subprocess.run( + [str(COREC), "run", source], capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def case_run(name, source, expect_rc): + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc}, got {rr.returncode}") + return False + print(f"[PASS] {name}: rc={rr.returncode}") + return True + + +def case_reject(name, source, needles, cmd="build"): + r, out, src = _compile(source, cmd) + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + if cmd == "build" and os.path.exists(out): + print(f"[FAIL] {name}: 拒绝编译却仍产出二进制 {out}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_interp(name, source, expect_rc): + r = run_interp(source) + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected interp rc {expect_rc}, got {r.returncode}: {r.stdout}{r.stderr}") + return False + print(f"[PASS] {name}: interp rc={r.returncode}") + return True + + +def case_run_reject(name, source, needles): + """run(解释器)路径:判定在共享 run_frontend ⇒ 同拒(rc=1 + TM03)。""" + r = run_interp(source) + if r.returncode == 0: + print(f"[FAIL] {name}: run 路径 expected rejection, got rc=0") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + + +def case_soft(name, source, needle): + """软诊断面(非硬门):编译 rc=0 + 产物照出 + 诊断在文本中。""" + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: 软诊断面应为 rc=0(非硬门),compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if needle not in r.stdout + r.stderr: + print(f"[FAIL] {name}: 缺软诊断 {needle!r}: {r.stdout}{r.stderr}") + return False + print(f"[PASS] {name}: rc=0 + 产物照出 + {needle!r}") + return True + + +# ── 源形 ────────────────────────────────────────────────────────────── + +COLOR = "enum Color { Red, Green, Blue }\n" +CHOICE = "enum Choice { First(int), Second(int), Third(int) }\n" + + +def missing_arm_src(scrutinee="Blue"): + return COLOR + f"""fn main() -> int {{ + c := {scrutinee}(); + v := match c {{ + Red => 1, + Green => 2, + }}; + return v + 100; +}} +""" + + +def main(): + ok = [ + # 正:全覆盖 ELF 路径(Green ⇒ 2) + case_run("exhaustive_elf", COLOR + "fn main() -> int { c := Green(); " + "return match c { Red => 1, Green => 2, Blue => 3, }; }\n", 2), + # 正:同源解释器路径(Blue ⇒ 3;与 ELF 路径共享判定面) + case_interp("interp_exhaustive", + COLOR + "fn main() -> int { c := Blue(); return match c { Red => 1, Green => 2, Blue => 3, }; }\n", 3), + # 正:通配臂吸收(⊤) + case_run("wildcard_absorbs", COLOR + "fn main() -> int { c := Blue(); " + "return match c { Red => 1, _ => 7, }; }\n", 7), + # 正:绑定臂吸收 + case_run("binding_absorbs", COLOR + "fn main() -> int { c := Red(); " + "return match c { x => 9, }; }\n", 9), + # 正:限定名模式(Enum.Variant) + case_run("qualified_pattern", COLOR + "fn main() -> int { c := Blue(); " + "return match c { Color.Red => 1, Color.Green => 2, Color.Blue => 3, }; }\n", 3), + # 正:payload 变体(三臂全列) + case_run("payload_exhaustive", CHOICE + "fn main() -> int { c := Third(33); " + "return match c { First(v) => v, Second(v) => v, Third(v) => v, }; }\n", 33), + # 正:泛型枚举实例(Opt[int]) + case_run("generic_enum_exhaustive", + "enum Opt[T] { N, S(T) }\nfn main() -> int { c := S(5); " + "return match c { N => 1, S(x) => x, }; }\n", 5), + # 正:单变体枚举(域最小边界) + case_run("single_variant_exhaustive", + "enum Only { One }\nfn main() -> int { c := One(); " + "return match c { One => 4, }; }\n", 4), + # 正:枚举形参位(scrutinee 来自形参 ti 而非构造器) + case_run("enum_param_exhaustive", COLOR + "fn f(c: Color) -> int { " + "return match c { Red => 1, Green => 2, Blue => 3, }; }\n" + "fn main() -> int { return f(Green()); }\n", 2), + # 负:缺臂 ⇒ rc=1 + TM03(含具体缺失变体名)+ 无产物 + case_reject("missing_arm_rejected", missing_arm_src(), + ["error[TM03]", "Non-exhaustive match", "missing variant 'Blue'"]), + # 负:无臂(空 match 体)⇒ 同上(未覆盖面 = 全域,首个变体即反例) + case_reject("no_arms_rejected", + COLOR + "fn main() -> int { c := Red(); return match c { }; }\n", + ["error[TM03]", "missing variant 'Red'"]), + # 负:check 子命令同拒(判定在共享前端,非 ELF 路径专属) + case_reject("check_path_rejected", missing_arm_src(), ["error[TM03]"], cmd="check"), + # 负:run(解释器)路径同拒(同一 run_frontend ⇒ 同一判定面) + case_run_reject("run_path_rejected", + COLOR + "fn main() -> int { c := Blue(); return match c { Red => 1, Green => 2, }; }\n", + ["error[TM03]", "missing variant 'Blue'"]), + # 登记:非枚举域不判穷尽(int 字面量匹配 ⇒ 零诊断,rc=0) + case_run("non_enum_domain_not_judged", + "fn main() -> int { n := 3; return match n { 1 => 10, 2 => 20, }; }\n", 0), + # 登记:不可映射模式(枚举匹配混字面量臂)不判穷尽(-1 = 不判,不得当「不穷尽」) + case_run("unmappable_pattern_not_judged", + "enum Color2 { Red2, Green2, Blue2 }\nfn main() -> int { c := Blue2(); " + "return match c { Red2 => 1, 5 => 2, }; }\n", 0), + # 软面:重复臂 ⇒ TM04 非硬门(rc=0 + 产物照出) + case_soft("redundant_arm_soft", + COLOR + "fn main() -> int { c := Red(); " + "return match c { Red => 1, Red => 2, Green => 3, Blue => 4, }; }\n", + "error[TM04]"), + ] + passed = sum(1 for x in ok if x is True) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_mw_task1.py b/tests/selfhost/test_mw_task1.py new file mode 100644 index 00000000..26d77e43 --- /dev/null +++ b/tests/selfhost/test_mw_task1.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 1:tagged 槽框架(潜在多字变量识别 + tag 区 + 栈布局)。 + +验证对象(架构轴 src/arch/x86_64/ + 格式轴 src/format/elf/): + - mw_setup_tags(tag2l.cr——波 1 Task 4 自 instr.cr 整搬;tag 表 owner): + per 函数潜在多字识别 → tag 字节偏移表。 + 识别 = 不动点闭包(评审修复:单遍前向对循环携带值不成立——tag 态可沿 + 回边携带): + A. IR_BINARY(OP_ADD/OP_SUB) 的 dest(add/sub 溢出 = M1 唯一 >64 生产者); + B. IR_LOAD 值拷贝 d←s1(as 转换拷贝):s1 已标 → 标 d; + B'. IR_STORE 定值拷贝 ρ(s1):=ρ(s2)(ir_gen 赋值形态,真实槽拷贝载体): + s2 已标 → 标 s1——变量行(return/比较消费的槽)只经 STORE 得到值, + 只标临时行不够; + 重复全扫至无新标(单调 ⇒ ≤ vc+1 遍收敛)。 + - tag 区栈布局:帧 = vc*8 + tag_count,按 SysV 16 对齐规则取整; + 无 tagged 变量的函数帧公式与旧版逐字节一致(快路径零变化); + - 布局可观测面:ELF 中函数 prologue 的 sub rsp 立即数 = 帧总字节。 + +方法:Python 独立复刻识别规则(对 .ccr 二进制格式的旁路 oracle——不同实现 +互相校验),从 .ccr 读 per 函数 var 域/指令流/变量类型,按上述规则做不动点, +算出期望 tag 数与期望帧字节;再从 ELF 文本段定位 main(_start 尾 call main +的模式)并解析其 prologue 的 sub rsp 立即数,断言相等。行为层再跑一遍产物体 +核对 exit code。s6(循环携带拷贝)另断言不动点 ⊋ 单遍(防止 oracle/实现被 +回退成单遍而重新掩盖评审发现)。 + +.ccr 读取依赖 v7 段表布局(test_ccr_v7.py 的 V7File 为格式唯一真源——格式 +演进时只需改那边;Task 3 v7 收官后 test_ccr_v6.py 已退役合并,本文件别名 +V6File 仅为调用点命名惯性——语义 = v7 walker,见 import 行注释)。 +""" +import os +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +SCRATCH = BUILD / "mw_task1_scratch" + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from test_ccr_v7 import V7File as V6File # noqa: E402 (v7 段表解析器 = 格式唯一真源;别名保调用点不变) + +# IR opcodes (ast.cr) +IR_BINARY = 2 +IR_LOAD = 10 +IR_STORE = 9 +OP_ADD = 1 +OP_SUB = 2 +TI_INT = 0 + + +def load_ir(ccr_path): + """v6 .ccr → (funcs, var_types)。 + + funcs: list of {name, var_start, var_count, nodes}——nodes = 本函数节点切片 + (NOD 全表按 REG root region [enter, exit) 切); + var_start = globals 行 + 前缀函数块行序游标(SYM func 记录序累加)。 + var_types: {var_idx: tk}(SYM func 内嵌声明区:行序 = 函数块内索引)。 + """ + v = V6File(ccr_path.read_bytes()) + strs = v.str_table() + sym = v.sym_parse() + nods = v.nod() + regs = v.reg() + var_types = {} + funcs = [] + vs = len(sym["globals"]) + for f in sym["funcs"]: + n0, n1 = regs[f["root_region"]][2], regs[f["root_region"]][3] + if n0 < 0 or n1 > len(nods) or n0 >= n1: + raise AssertionError(f"func {f['name']}: bad node span [{n0},{n1}) " + f"of {len(nods)}") + for i, decl in enumerate(f["var_decls"]): + var_types[vs + i] = decl["type"] + funcs.append(dict(name=strs[f["name"]], var_start=vs, + var_count=f["var_count"], nodes=nods[n0:n1])) + vs += f["var_count"] + return funcs, var_types + + +def detect_tags(func, var_types, max_passes=None, store_edges=True): + """复刻 tag2l.cr mw_setup_tags:规则 A + B(IR_LOAD 拷贝) + B'(IR_STORE + 定值拷贝),重复全扫至无新标。 + + 退化形态(仅区分性断言用): + max_passes = 1 → 单遍前向(含 B'——评审缺陷形态:回边携带漏标); + store_edges = False → 无 B'(评审前实现规则:A+B,变量行永不入集)。 + """ + vs = func["var_start"] + ve = vs + func["var_count"] + tagged = set() + + def in_dom(x): + return vs <= x < ve and var_types.get(x, -1) == TI_INT + + npass = 0 + while True: + changed = False + for (op, d, s1, s2, s3, tk, fe, ec) in func["nodes"]: + if op == IR_BINARY: + if in_dom(d) and s3 in (OP_ADD, OP_SUB) and d not in tagged: + tagged.add(d) + changed = True + elif op == IR_LOAD: + if (in_dom(d) and s1 in tagged and in_dom(s1) + and d not in tagged): + tagged.add(d) + changed = True + elif op == IR_STORE and store_edges: + # 定值拷贝 ρ(s1) := ρ(s2)(dest 恒 -1) + if (in_dom(s1) and s2 in tagged and in_dom(s2) + and s1 not in tagged): + tagged.add(s1) + changed = True + npass += 1 + if not changed: + break + if max_passes is not None and npass >= max_passes: + break + return tagged + + +def frame_total(vc, n, opt): + """复刻 frame.cr pf_frame_size(含 tag 区 + SysV 16 对齐;x86 实例化波 1 + Task 3 自 instr.cr mw_frame_size 迁入并改名——公式 verbatim)。""" + sz = vc * 8 + n + if opt >= 1: + if sz % 16 != 8: + pad = (8 - sz % 16) % 16 + sz += pad + else: + if sz % 16 != 0: + sz += 16 - sz % 16 + return sz + + +# _start 尾模式:mov edi,eax(89 C7) + mov eax,60(B8 3C000000) + syscall(0F 05) +START_TAIL = b"\x89\xc7\xb8\x3c\x00\x00\x00\x0f\x05" + + +def find_main_frame(elf_path, opt): + """定位 main(_start 内 call main 的 E8 rel32 紧跟 START_TAIL)并解析帧字节。""" + b = elf_path.read_bytes() + text = b[176:] + idx = text.find(START_TAIL) + if idx < 5: + raise AssertionError("cannot locate _start tail in ELF text") + p = idx - 5 # E8 位置(text 相对) + rel = struct.unpack_from("= len(text): + raise AssertionError("main offset out of text") + m = main_off + # prologue + if opt >= 1: + # push rbx(53) push r12-r15(41 54..57) push rbp(55) mov rbp,rsp(48 89 e5) + assert text[m:m + 13] == b"\x53\x41\x54\x41\x55\x41\x56\x41\x57\x55\x48\x89\xe5", \ + text[m:m + 13].hex() + m += 13 + else: + assert text[m:m + 4] == b"\x55\x48\x89\xe5", text[m:m + 4].hex() + m += 4 + if text[m] == 0x48 and text[m + 1] == 0x83 and text[m + 2] == 0xEC: + return text[m + 3] + if text[m] == 0x48 and text[m + 1] == 0x81 and text[m + 2] == 0xEC: + return struct.unpack_from(" int {\n return 7;\n}\n", + "s2_intadd": "fn main() -> int {\n a := 2000000000;\n b := 2000000000;\n c := a + b;\n return c;\n}\n", + "s3_sub": "fn main() -> int {\n a := 10;\n b := 3;\n c := a - b;\n return c;\n}\n", + "s4_addcopy": "fn main() -> int {\n a := 1;\n b := 2;\n c := a + b;\n d := c;\n e := d + c;\n f := e;\n return f;\n}\n", + "s5_loop": "fn main() -> int {\n sum := 0;\n i := 0;\n while i < 10 {\n sum = sum + i;\n i = i + 1;\n }\n return sum;\n}\n", + # 评审场景:snap 的拷贝点线性先于 tag 源(x = x + k)的定值——运行时 + # x 的 tag 态沿回边携带,迭代 ≥2 的 snap = x as int 拷贝的是 2-limb x。 + "s6_loopcopy": "fn main() -> int {\n x : ., mut = 0;\n i : ., mut = 0;\n snap := 0;\n while i < 3 {\n snap = x as int;\n x = x + 1;\n i = i + 1;\n }\n return snap;\n}\n", + # 8 个 add 定值点(旧规则 tag 数 = 8)后跟一个拷贝变量行 s(新规则 +2): + # vc*8 = 160 时 O1/O2 帧 168(旧)vs 184(新)——二进制级可观测差。 + "s7_framediff": "fn main() -> int {\n r : ., mut = 0;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n r = r + 1;\n s := r;\n return s;\n}\n", +} + +EXPECTED_EXIT = {"s1_noarith": 7, "s2_intadd": 0, "s3_sub": 7, + "s4_addcopy": 6, "s5_loop": 45, "s6_loopcopy": 2, + "s7_framediff": 8} + + +def run_checked(args, label): + cmd = ["nice", "-n", "19", *map(str, args)] + r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=300) + if r.returncode != 0: + print(f"[FAIL] {label}: exit {r.returncode}") + print(r.stdout[-3000:]) + print(r.stderr[-3000:]) + return False + return True + + +def main() -> int: + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + ok = True + for name, desc in CASES: + src = SCRATCH / f"{name}.cr" + src.write_text(SRCS[name], encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + ccr = Path(str(out) + ".ccr") + if not run_checked([COREC, "build", src, "-o", out, "--static", "-O", str(opt)], + f"{name} @O{opt} build"): + ok = False + continue + if not ccr.exists(): + print(f"[FAIL] {name} @O{opt}: no .ccr at {ccr}") + ok = False + continue + funcs, var_types = load_ir(ccr) + mains = [f for f in funcs if f["name"] == "main"] + if len(mains) != 1: + print(f"[FAIL] {name} @O{opt}: expected 1 main, got {[f['name'] for f in funcs]}") + ok = False + continue + mainf = mains[0] + tags = detect_tags(mainf, var_types) + n = len(tags) + expect_f = frame_total(mainf["var_count"], n, opt) + try: + got_f = find_main_frame(out, opt) + except AssertionError as e: + print(f"[FAIL] {name} @O{opt}: prologue parse: {e}") + ok = False + continue + if got_f != expect_f: + print(f"[FAIL] {name} @O{opt}: frame {got_f} != expected {expect_f} " + f"(vc={mainf['var_count']}, tags={n}, desc={desc})") + ok = False + else: + print(f"[PASS] {name} @O{opt}: frame {got_f} == vc*8({mainf['var_count'] * 8}) " + f"+ {n} tags aligned (desc: {desc})") + # 行为 + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, timeout=30) + want = EXPECTED_EXIT[name] + if r.returncode != want: + print(f"[FAIL] {name} @O{opt}: run exit {r.returncode}, want {want}") + ok = False + else: + print(f"[PASS] {name} @O{opt}: run exit {r.returncode}") + # 区分性断言(oracle 级,防实现/测试共同退化掩盖评审缺陷) + if name == "s6_loopcopy" and opt == 0: + # 不动点必须 ⊋ 单遍前向——s6 的拷贝链只经回边跨遍收敛 + sp = detect_tags(mainf, var_types, max_passes=1) + if not sp < tags: + print(f"[FAIL] {name} @O{opt}: fixpoint {sorted(tags)} " + f"not a strict superset of single-pass {sorted(sp)} — " + f"case no longer discriminates the review defect") + ok = False + else: + print(f"[PASS] {name} @O{opt}: fixpoint({len(tags)}) > " + f"single-pass({len(sp)}) — 循环携带拷贝链被收敛捕获") + # s7:帧级可观测性——至少一个 opt 下旧规则(A+B 单遍,评审前实现) + # 的帧与新规则帧不同(否则帧断言无法区分新旧实现) + if name == "s7_framediff": + old_tags = detect_tags(mainf, var_types, max_passes=1, store_edges=False) + obs = [o for o in (0, 1, 2) + if frame_total(mainf["var_count"], len(old_tags), o) + != frame_total(mainf["var_count"], len(tags), o)] + if not obs: + print(f"[FAIL] {name}: 旧规则帧 == 新规则帧 at all opt " + f"(old={len(old_tags)}, new={len(tags)}) — 样例失去帧级区分力") + ok = False + else: + print(f"[PASS] {name}: old-rules({len(old_tags)} tags) vs " + f"fixpoint({len(tags)} tags) frame differs at O{obs}") + print() + print("=== mw task1: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_mw_task2.py b/tests/selfhost/test_mw_task2.py new file mode 100644 index 00000000..19cca7da --- /dev/null +++ b/tests/selfhost/test_mw_task2.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 2/3:快路径溢出检测发射(tagged int add/sub + jo → 慢路径块)。 + +验证对象(架构轴 src/arch/x86_64/ + 格式轴 src/format/elf/): + - emit_instr IR_BINARY int(TI_INT)ADD/SUB:dest ∈ tagged 集(g2_tag_off(d) + != -1——Task 1 规则 A 对域内 add/sub dest 无条件标记)→ e2_alu 后紧跟 + jo(0F 80 rel32——rel8 对函数尾附加的慢路径块在 >127B 函数体上不可达, + t3 是此必要性的行为级断言)。 + - Task 3 起:函数尾每 jo 站点独立真实慢路径块(2-limb 修正 + alloc(16) + + 写 2-limb + dest 回存 + tag 置位 + 跳回 e2_st 之后——e2_mw_slow_block); + 无 jo 的函数不发块、untagged 快路径零变化(z 用例逐字节验证)。 + - 块前缀 = push r10(41 52,确定性)——站点块定位锚。 + +行为层:无溢出 add(t1)jo 不触发、exit 正常;溢出 add(t2)jo 触发 → +慢路径块 → 2-limb 结果经测试钩子(syscall3 原始 16B 写 stdout——验全 128 +位通道)assert 数学正确(本文件验 2^63 一例,全套验算在 test_mw_task3.py); +t3 大函数 250 次 +1 无溢出 exit 250。 + +零 diff:z 用例(无 int 算术)需与改动前编译器输出逐字节一致。基线快照: + python3 tests/selfhost/test_mw_task2.py --snapshot # 改动前执行一次 +产物存 build/mw_task2_zdiff/(build 目录不入库——缺基线时零 diff 断言 SKIP, +其余断言照跑;CI/新克隆需在改动前编译器上自产基线)。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" +import argparse +import os +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +SCRATCH = BUILD / "mw_task2_scratch" +ZDIFF = BUILD / "mw_task2_zdiff" + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from test_mw_task1 import START_TAIL, detect_tags, find_main_frame, load_ir # noqa: E402 + +# IR opcodes / ops(ast.cr,oracle 用——与 tag2l.cr mw_setup_tags 同源常量) +IR_BINARY = 2 +OP_ADD = 1 +OP_SUB = 2 + +# jo near rel32 = 0F 80 cd(6B);慢路径块确定性前缀 = sbb r11, r11 +# (4D 19——高 limb 修正首操作,add/sub 共用;sub 后随 48 F7 not) +JO_PAT = b"\x0f\x80" +BLK_PREFIX = b"\x4d\x19" + +CASES = [ + # (name, exit, stdout_want(16B)/None, intent) + ("t1_add", 0, None, + "add 快路径无溢出(2e9+2e9=4e9 < 2^63):jo 在但恒不触发——jo 发射断言 + 行为不变"), + ("t2_add_overflow", 16, struct.pack(" i64max)触发 jo → 慢路径块 → 2-limb 数学正确:" + "测试钩子(syscall3 原始 16B 写 stdout)验 lo=2^63、hi=0(全 128 位通道)"), + ("t3_bigfunc", 250, None, + "250 次 +1(>127B 函数体):jo rel32 必需性——每个 jo 仍可达其函数尾块," + "首 jo 距块 >127B(rel8 编码在回退时不可达)"), + ("z1_noarith", 7, None, "untagged:无 int 算术——零 diff(与改动前快照逐字节一致)"), + ("z2_copy", 100, None, "untagged:const/STORE 拷贝链(B' 无 tag 源)——零 diff"), + ("z3_branch", 9, None, "untagged:BINARY cmp/分支/循环(无 ADD/SUB)——零 diff"), +] + +SRCS = { + "t1_add": "fn main() -> int {\n a := 2000000000;\n b := 2000000000;\n" + " c := a + b;\n return c;\n}\n", + "t2_add_overflow": "fn main() -> int {\n x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + "t3_bigfunc": None, # Python 生成(250 次 x = x + 1) + "z1_noarith": "fn main() -> int {\n return 7;\n}\n", + "z2_copy": "fn main() -> int {\n a := 123456;\n b := a;\n" + " c := b;\n return 100;\n}\n", + "z3_branch": "fn main() -> int {\n x : ., mut = 0;\n" + " while x > 0 {\n x = 0;\n }\n return 9;\n}\n", +} +SRCS["t3_bigfunc"] = ("fn main() -> int {\n x : ., mut = 0;\n" + + "\n".join(" x = x + 1;" for _ in range(250)) + + "\n return x;\n}\n") + + +def jo_sites_oracle(funcs, var_types): + """期望 jo 数 = 该函数 IR 中 dest ∈ tagged 的 int ADD/SUB(发射侧 + g2_tag_off(d) != -1 条件的 oracle 同构——规则 A 使域内 add/sub dest + 恒入集,dest ∈ tags 等价于发射侧判定)。""" + mains = [f for f in funcs if f["name"] == "main"] + if len(mains) != 1: + raise AssertionError(f"expected 1 main, got {[f['name'] for f in funcs]}") + mainf = mains[0] + tags = detect_tags(mainf, var_types) + n = 0 + for (op, d, s1, s2, s3, tk, fe, ec) in mainf["nodes"]: + if op == IR_BINARY and s3 in (OP_ADD, OP_SUB) and d in tags: + n = n + 1 + return mainf, n + + +def find_main_region(elf_path, opt, frame): + """main 代码区间 [start, ret_end)(text 相对偏移): + start = _start 尾 call main;main 区终点 = 其尾声 ret 之后(慢路径块即附 + 于此、下一函数之前)——尾声字节模板由 opt 与 oracle 帧字节构造 + (add rsp,frame + pop rbp [+ pop r15..rbx] + ret——用户函数 ret 仅尾声 + 一处,模板在函数体内出现的概率可忽略;main 之后是 stdlib 函数,不能用 + _init_globals 定位)。""" + b = elf_path.read_bytes() + text = b[176:] + idx = text.find(START_TAIL) + if idx < 5: + raise AssertionError("cannot locate _start tail in ELF text") + p = idx - 5 + rel = struct.unpack_from("= len(text): + raise AssertionError("main offset out of text") + if frame == 0: + # 无帧函数:尾声 = pop rbp; ret(无 add rsp) + tail = b"\x5d\xc3" if opt < 1 else b"\x5d\x41\x5f\x41\x5e\x41\x5d\x41\x5c\x5b\xc3" + elif frame > 127: + tail = b"\x48\x81\xc4" + struct.pack(" int: + ap = argparse.ArgumentParser() + ap.add_argument("--snapshot", action="store_true", + help="生成零 diff 基线(须在改动前编译器上运行)") + ap.add_argument("--skip-zdiff", action="store_true", + help="跳过零 diff 字节比较(即使基线存在)") + args = ap.parse_args() + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + if args.snapshot: + ZDIFF.mkdir(exist_ok=True) + ok = True + for name, want_exit, want_out, desc in CASES: + src = SCRATCH / f"{name}.cr" + src.write_text(SRCS[name], encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + ccr = Path(str(out) + ".ccr") + if not run_checked([COREC, "build", src, "-o", out, "--static", + "-O", str(opt)], f"{name} @O{opt} build"): + ok = False + continue + if not ccr.exists(): + print(f"[FAIL] {name} @O{opt}: no .ccr at {ccr}") + ok = False + continue + funcs, var_types = load_ir(ccr) + mainf, exp = jo_sites_oracle(funcs, var_types) + try: + frame = find_main_frame(out, opt) + except AssertionError as e: + print(f"[FAIL] {name} @O{opt}: prologue parse: {e}") + ok = False + continue + try: + text, m0, m1 = find_main_region(out, opt, frame) + except AssertionError as e: + print(f"[FAIL] {name} @O{opt}: region parse: {e}") + ok = False + continue + jos = scan_jos(text, m0, m1) + # ── jo 个数断言(untagged 用例:exp=0 → 不得有任何 0F 80)── + if len(jos) != exp: + print(f"[FAIL] {name} @O{opt}: jo sites {len(jos)} != oracle {exp} " + f"(tags={len(detect_tags(mainf, var_types))}) — {desc}") + ok = False + continue + if exp == 0: + print(f"[PASS] {name} @O{opt}: no jo (oracle {exp}); region " + f"[{m0},{m1}) len={m1 - m0} (desc: {desc})") + else: + # Task 3:每站点独立慢路径块——jo rel32 目标 = 各自块 + # (发射序 = 指令序 → 扫描序;首站点块 = 尾声 ret 后首字节 + # m1;块前缀 = sbb r11,r11 4D 19 确定性锚)。 + targets = [t for (_, t) in jos] + if jos[0][1] != m1: + print(f"[FAIL] {name} @O{opt}: first jo target {jos[0][1]} " + f"!= block region start {m1}") + ok = False + continue + bad_pre = [t for t in targets if text[t:t + 2] != BLK_PREFIX] + bad_ord = [t for t in targets if t < m1] + if bad_pre or bad_ord: + print(f"[FAIL] {name} @O{opt}: block targets invalid — " + f"prefix-miss {[t for t in bad_pre]} " + f"pre-epilogue {bad_ord}") + ok = False + continue + if any(targets[i] >= targets[i + 1] for i in range(len(targets) - 1)): + print(f"[FAIL] {name} @O{opt}: block targets not strictly " + f"increasing: {targets}") + ok = False + continue + dmax = targets[0] - jos[0][0] # 首 jo 距其块(= m1 − jo0) + print(f"[PASS] {name} @O{opt}: {len(jos)} jo -> per-site blocks " + f"@{targets[0]}..+{targets[-1] - targets[0]} " + f"(first-jo dist {dmax}B, desc: {desc})") + if name == "t3_bigfunc" and opt == 0: + # rel32 必需性:最远 jo 距块 >127B——rel8 编码不可达,回退必红 + if dmax <= 127: + print(f"[FAIL] {name} @O{opt}: farthest jo-block dist " + f"{dmax} <= 127 — t3 失去 rel32 判别力") + ok = False + # ── 行为(t2 溢出 → 2-limb 修正 + 测试钩子验 16B;其余 exit 码)── + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, capture_output=True, timeout=60) + if r.returncode != want_exit: + print(f"[FAIL] {name} @O{opt}: run exit {r.returncode}, " + f"want {want_exit}") + ok = False + else: + print(f"[PASS] {name} @O{opt}: run exit {r.returncode}") + if want_out is not None: + if r.stdout != want_out: + print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " + f"expected {want_out.hex()} (lo/hi 2-limb 全 128 位)") + ok = False + else: + print(f"[PASS] {name} @O{opt}: stdout 16B == {want_out.hex()} " + f"(2-limb lo/hi 正确)") + # ── 零 diff:untagged 用例 main 指令流 == 改动前快照(E8 call + # 位移例外——stdlib 函数因 tagged 代码合法增长而整体后移)── + if name.startswith("z") and not args.skip_zdiff: + bl = ZDIFF / f"{name}_o{opt}.bin" + if bl.exists(): + bt, b0, b1 = find_main_region(bl, opt, frame) + same, why = region_equal_mask_calls(text[m0:m1], + bt[b0:b1]) + if not same: + print(f"[FAIL] {name} @O{opt}: main stream differs vs " + f"pre-change baseline: {why}") + ok = False + else: + print(f"[PASS] {name} @O{opt}: main instruction stream " + f"identical vs pre-change baseline") + elif args.snapshot: + import shutil + shutil.copyfile(out, bl) + print(f"[SNAP] {name} @O{opt}: baseline saved {bl.name}") + else: + print(f"[SKIP] {name} @O{opt}: no zero-diff baseline " + f"(run --snapshot on pre-change compiler)") + print() + print("=== mw task2: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_mw_task3.py b/tests/selfhost/test_mw_task3.py new file mode 100644 index 00000000..5588e8fe --- /dev/null +++ b/tests/selfhost/test_mw_task3.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 3:慢路径 + 2-limb 表示运行时(jo 后数学修正 + 堆对象 + tag 写)。 + +验证对象(src/arch/x86_64/ tag2l.cr e2_mw_slow_block(波 1 Task 4 自 instr.cr +整搬)+ src/arch/x86_64/ frame.cr pf_epilogue 函数尾块发射——Task 3 自 elf.cr 抽出): + 溢出(jo/OF=1)后 128 位真值修正:低 limb = 快路径环绕结果 r10;高 limb = + add:CF ? -1 : 0(sbb r11,r11——正溢出 CF=0/负溢出 CF=1 推演); + sub:CF ? 0 : -1(sbb+not——正溢出借位 CF=1/负溢出混合符号无借位 CF=0)。 + 存储:alloc(16)(与 IR_ALLOC_* 同 bump/arena 语义)→ [+0] lo / [+8] hi → + dest 值槽存指针(含 O1/O2 reg 形态)+ tag 字节置位(g2_tag_off)→ jmp 回 + 快路径 store 之后(resume)。 + +验证通道(全 128 位可观测):溢出结果 2-limb 对象 16 原始字节经 + syscall3(1,1,x,16)(SYS_write,x 槽 = 对象指针——`x 值槽存指针`的直接可 + 观测面)写 stdout → Python 断言 lo/hi。判别矩阵(CF/op 四象限,任一规则 + 回退成另一 op 的规则即红): + m1 add 正溢出(CF=0)→ hi=0;m2 add 负溢出(CF=1)→ hi=-1; + m3 sub 正溢出(CF=1)→ hi=0(若用 add 规则 → hi=-1 红); + m4 sub 负溢出(CF=0)→ hi=-1(若用 add 规则 → hi=0 红); + m5 同函数双 add 站点(两块独立发射);m6 同函数 add+sub 混站(两块形状)。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" +import os +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +SCRATCH = BUILD / "mw_task3_scratch" + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from test_mw_task1 import detect_tags, load_ir # noqa: E402 +from test_mw_task2 import jo_sites_oracle # noqa: E402 + + +def limb_bytes(S): + """128 位数学值 S(Python int,∈ [-2^64, 2^64))→ 2-limb 原始 16B + (lo 小端 8B + hi 小端 8B——与 e2_mw_slow_block 的 [+0]/[+8] 布局一致)。""" + return struct.pack("> 64) + + +# (name, src, 期望 128 位值, 意图) +S64 = 1 << 63 +CASES = [ + ("m1_add_pos", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" # 2^62 + " x = x + 4611686018427387904;\n" # S = 2^63:正溢出 CF=0 → hi=0 + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + S64, "add 正溢出:CF=0 → 高 limb = 0(lo=2^63 环绕 0x8000..0)"), + ("m2_add_neg", + "fn main() -> int {\n" + " x : ., mut = -9223372036854775807;\n" # -(2^63-1) + " x = x + -2;\n" # S = -2^63-1:负溢出 CF=1 → hi=-1 + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + -S64 - 1, "add 负溢出:CF=1 → 高 limb = -1(lo=2^63-1、hi=0xFF..F)"), + ("m3_sub_pos", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" # 2^62 + " x = x - -4611686018427387904;\n" # S = 2^63:正溢出借位 CF=1 → + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", # sub 规则 hi=0(add 规则会给 -1 → 红) + S64, "sub 正溢出:CF=1 → 高 limb = 0(判别:add 规则在此给 -1)"), + ("m4_sub_neg", + "fn main() -> int {\n" + " x : ., mut = -4611686018427387905;\n" # -(2^62+1) + " x = x - 4611686018427387904;\n" # S = -2^63-1:负溢出(混合符号 + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", # 无借位 CF=0)→ sub 规则 hi=-1 + -S64 - 1, "sub 负溢出:CF=0 → 高 limb = -1(判别:add 规则在此给 0)"), + ("m5_two_add_sites", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 站点 1:2^63 + " r1 := syscall3(1, 1, x, 16);\n" + " y : ., mut = -9223372036854775807;\n" + " y = y + -2;\n" # 站点 2:-2^63-1 + " r2 := syscall3(1, 1, y, 16);\n" + " return r1 + r2;\n}\n", + None, "同函数双 add 站点:两独立块都执行、都正确(rc=32 + 32B 流)"), + ("m6_mix_sites", + "fn main() -> int {\n" + " a : ., mut = 4611686018427387904;\n" + " a = a - -4611686018427387904;\n" # sub 正溢出 → 2^63 + " r1 := syscall3(1, 1, a, 16);\n" + " b : ., mut = -4611686018427387905;\n" + " b = b - 4611686018427387904;\n" # sub 负溢出 → -2^63-1 + " r2 := syscall3(1, 1, b, 16);\n" + " c : ., mut = -9223372036854775807;\n" + " c = c + -2;\n" # add 负溢出 → -2^63-1 + " r3 := syscall3(1, 1, c, 16);\n" + " return r1 + r2 + r3;\n}\n", + None, "add+sub 混站:sub 块(含 not 修正)与 add 块同函数共存"), +] + +EXPECT_STREAM = { + "m1_add_pos": limb_bytes(S64), + "m2_add_neg": limb_bytes(-S64 - 1), + "m3_sub_pos": limb_bytes(S64), + "m4_sub_neg": limb_bytes(-S64 - 1), + "m5_two_add_sites": limb_bytes(S64) + limb_bytes(-S64 - 1), + "m6_mix_sites": limb_bytes(S64) + limb_bytes(-S64 - 1) + limb_bytes(-S64 - 1), +} + + +def run_checked(args, label): + cmd = ["nice", "-n", "19", *map(str, args)] + r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + print(f"[FAIL] {label}: exit {r.returncode}") + print(r.stdout[-3000:]) + print(r.stderr[-3000:]) + return False + return True + + +def main() -> int: + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + ok = True + for name, src, _val, desc in CASES: + srcf = SCRATCH / f"{name}.cr" + srcf.write_text(src, encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + ccr = Path(str(out) + ".ccr") + if not run_checked([COREC, "build", srcf, "-o", out, "--static", + "-O", str(opt)], f"{name} @O{opt} build"): + ok = False + continue + # 站点数 oracle(同 test_mw_task2——发射侧 jo 数 = dest∈tagged 的 + # int ADD/SUB 指令数):多站点用例要求站点数 == 溢出运算点数 + funcs, var_types = load_ir(ccr) + _mainf, nsites = jo_sites_oracle(funcs, var_types) + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, capture_output=True, + timeout=60) + want = EXPECT_STREAM[name] + ns = len(want) // 16 # 期望写的对象数(单站 1 / 双站 2 / 三站 3) + if r.stdout != want: + print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " + f"{want.hex()} — 2-limb 128 位值数学不正确 " + f"(sites={nsites}, 期望对象 {ns})\n {desc}") + ok = False + continue + if r.returncode != 16 * ns: + print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " + f"{16 * ns}(16B × {ns} 对象)") + ok = False + continue + print(f"[PASS] {name} @O{opt}: {ns} 对象 2-limb 流 == " + f"{want.hex()}(exit {r.returncode},sites={nsites}," + f"{desc})") + print() + print("=== mw task3: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_mw_task4.py b/tests/selfhost/test_mw_task4.py new file mode 100644 index 00000000..3083c479 --- /dev/null +++ b/tests/selfhost/test_mw_task4.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 4:消费者适配(tag 读路径 + tag 卫生)。 + +验证对象(src/arch/x86_64/ instr.cr(消费点/站点发射)+ tag2l.cr(tag 卫生 + +2L 块编码——波 1 Task 4 自 instr.cr 整搬)+ frame.cr(波 1 Task 3 起:帧序 + +函数尾 2L 操作数块发射)+ src/format/elf/ elf.cr): + A. tag 卫生(定义点写,无帧入口清零——每 tagged 行的每个定值点都写 tag): + ① 快路径定值清 tag:tagged 变量被快路径 add/sub 重定义(同站点回边再 + 执行——临时行 stale tag=1 会给拷贝传播错误值); + ② IR_STORE/IR_LOAD 定值拷贝 = 运行时 tag 拷贝(s2/s1 tagged → 拷贝 + tag 字节;untagged 源 → 清 0); + ③ 参数行 prologue 保存清 tag(tagged 参数行在函数内首次定值前的读取 + ——stale tag 会把 64 位参数误读为 2-limb 指针)。 + B. tag 读路径(消费者): + ① IR_RETURN:tag=1 → 解 2-limb 指针取低 limb → exit 低 8 位 = + 数学值低 8 位(无解引用 = 指针低字节 = 错值/随机); + ② 比较(int EQ/NE/LT/GT/LE/GE):2-limb 参与比较 = 128 位比较 + (先高 limb 带符号、同则低 limb 无符号——低 limb 无符号是判别点); + ③ 链式运算:2-limb 值再参与 add/sub = 128 位全精度混合算术 + (2L+快 / 快+2L / 2L+2L——低+低进位链 + 高+高),结果可装回 64 位 + 时降级为快值(down-normalize——保持「2L ⟹ |v|≥2^63 ⟹ 非零」不变量, + IR_BRANCH 指针真值性因此保持正确)、否则新 2-limb 对象(hi 可出 + {0,−1} 域,如 −2^64−8 → hi=−2——128 位码域内无误差); + ④ fits-64 判定判别(d1/d2——Critical 修复护栏):链达 −2^64−2 + (hi==lo==−2)/ 2^64+1(hi==lo==1)须保持 2L 并全 128 断言—— + fits 判据 hi==signmask(lo) 的 cmp 若错编成 hi vs lo(cmp r11,r10) + 即假拟合降级截断(静默错值,其余用例均不可见——唯一判别形态)。 + +验证通道(沿用 Task 3):2-limb 对象原始 16B 经 syscall3(1,1,x,16) 写 +stdout(x 槽 = 指针——call 参数走 M1 出逃边界,此处恰为直读面)。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +SCRATCH = BUILD / "mw_task4_scratch" + +B62 = 1 << 62 # 4611686018427387904 +NB63 = -(1 << 63) + 1 # -(2^63-1):可写字面量下限 + + +def limb_bytes(S): + """128 位数学值 S(Python int)→ 2-limb 原始 16B(lo 小端 + hi 小端)。""" + lo = S & ((1 << 64) - 1) + hi = (S >> 64) & ((1 << 64) - 1) + return struct.pack(" int {\n" + " x : ., mut = 4611686018427387910;\n" # 2^62 + 6 + " x = x + 4611686018427387904;\n" # = 2^63+6(正溢出 2-limb) + " return x;\n}\n", + None, 6, "return 2L:exit 低 8 位 = 数学值 2^63+6 的低字节 6(无 deref = 指针字节)"), + ("r2_ret_big_neg", + "fn main() -> int {\n" + " x : ., mut = -9223372036854775807;\n" # -(2^63-1) + " x = x + -2;\n" # = -2^63-1(负溢出 2-limb) + " return x;\n}\n", + None, 255, "return 2L 负值:低字节 0xFF = 255"), + # ── B② 比较(判别:指针比较给反 / 低 limb 有符号给反)── + ("c1_cmp_neg_lt_zero", + "fn main() -> int {\n" + " x : ., mut = -9223372036854775807;\n" + " x = x + -2;\n" # -2^63-1(2-limb,hi=-1) + " if x < 0 { return 1; }\n" + " return 2;\n}\n", + None, 1, "2L 负数 < 0 = 真(指针为正 → 指针比较给 2——判别)"), + ("c2_cmp_2l_vs_fast_neg", + "fn main() -> int {\n" + " x : ., mut = -9223372036854775807;\n" + " x = x + -2;\n" # -2^63-1(2-limb) + " if x > -5 { return 1; }\n" + " return 2;\n}\n", + None, 2, "2L(-2^63-1) > -5 = 假(指针为正 → 给 1——判别)"), + ("c3_cmp_lo_unsigned", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 1;\n" # 2^63+1(2L:hi=0 lo=2^63+1) + " if 5 < x { return 1; }\n" # 快操作数在 s1(快+2L 分派) + " return 2;\n}\n", + None, 1, "5 < 2^63+1 = 真——低 limb 须无符号比较(有符号 lo:5 > -2^63+1 → 给 2)"), + ("c4_cmp_eq_diff_obj", + "fn main() -> int {\n" + " a : ., mut = 4611686018427387904;\n" + " a = a + 4611686018427387904;\n" # 2^63 对象 1 + " b : ., mut = 4611686018427387904;\n" + " b = b + 4611686018427387904;\n" # 2^63 对象 2(不同指针) + " if a == b { return 1; }\n" + " return 2;\n}\n", + None, 1, "2L == 2L(同值异对象):指针比较给 2——判别 128 位 EQ"), + ("c5_cmp_ge_2l", + "fn main() -> int {\n" + " a : ., mut = 4611686018427387904;\n" + " a = a + 4611686018427387904;\n" # 2^63 + " b : ., mut = 4611686018427387904;\n" + " b = b + 4611686018427387904;\n" # 2^63 + " if a >= b && a <= b { return 1; }\n" # GE + LE(2L vs 2L 同值) + " return 2;\n}\n", + None, 1, "2L GE/LE 同值链(布尔合并 = 比较结果再参与运算)"), + # ── B③ 链式运算(2L 再参与 add/sub——128 位混合算术)── + ("a1_chain_mixed_add", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb) + " x = x + 5;\n" # 2L + 快 → 2^63+5 + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + limb_bytes((1 << 63) + 5), 16, "2L + 快正值:2^63+5(钩子验全 128)"), + ("a2_chain_fast_first", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb) + " y : ., mut = 6;\n" + " y = y + x;\n" # 快在 s1 + 2L 在 s2(分派 B 路) + " r := syscall3(1, 1, y, 16);\n return r;\n}\n", + limb_bytes((1 << 63) + 6), 16, "快 + 2L(操作数序反转):2^63+6"), + ("a3_chain_sub_2l_fast", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387911;\n" # 2^62 + 7 + " x = x + 4611686018427387904;\n" # 2^63+7(2-limb) + " x = x - 1;\n" # 2L − 快 → 2^63+6(仍 2L) + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + limb_bytes((1 << 63) + 6), 16, "2L − 快:2^63+6(保持 2L)"), + ("a4_chain_sub_downnorm", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb) + " x = x - 5;\n" # = 2^63-5:可装 64 位 → 降级快值 + " return x;\n}\n", + None, 251, "2L − 快降级回 64 位:2^63−5 低字节 0xFB = 251"), + ("a5_chain_2l_2l_neg_correc", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb) + " y : ., mut = -9223372036854775807;\n" + " y = y + -2;\n" # -2^63-1(2-limb,hi=-1) + " z : ., mut = x + y;\n" # 2L + 2L = -1 → 降级快值 + " w : ., mut = z + 7;\n" + " return w;\n}\n", + None, 6, "2L+2L 降级(2^63 + (-2^63-1) = -1 → 快值)+ 快链继续 → 6"), + ("a6_chain_2l_2l_borrow", + "fn main() -> int {\n" + " m : ., mut = -9223372036854775807;\n" + " m = m + -4;\n" # -2^63-3(2-limb hi=-1 lo=2^63-3) + " a : ., mut = 4611686018427387904;\n" + " a = a + 4611686018427387904;\n" # 2^63 + " b : ., mut = a + 5;\n" # 2^63+5(2-limb hi=0 lo=2^63+5) + " d : ., mut = m - b;\n" # (-2^63-3)-(2^63+5) = -2^64-8: + " r := syscall3(1, 1, d, 16);\n return r;\n}\n", # hi=-2(出 {0,-1} 域)lo=2^64-8 + limb_bytes(-(1 << 64) - 8), 16, "2L−2L 双 limb 借位链:−2^64−8 → hi=−2(128 码域内精确)"), + ("a7_chain_loop_hi1", + "fn main() -> int {\n" + " x : ., mut = 0;\n" + " i : ., mut = 0;\n" + " while i < 6 {\n" + " x = x + 4611686018427387904;\n" # 每轮 +2^62:2^62→2^63(溢出 2L) + " i = i + 1;\n" # →3·2^62→2^64(hi=1)→… + " }\n" # 6 轮 = 6·2^62 = 2^64+2^63 + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + limb_bytes((1 << 64) + (1 << 63)), 16, + "循环携带链:同站点 快→溢出→混合→hi=1(6·2^62 = 2^64+2^63)"), + # ── 判别回归(fits-64 假拟合——hi==lo 恰在 {0,−1} 之外,其余用例不可见)── + # d1/d2 链终值 hi==lo(−2^64−2 → hi=lo=−2;2^64+1 → hi=lo=1):真 fits + # 判据 hi == sar63(lo)(lo=−2 → −1 ≠ hi;lo=1 → 0 ≠ hi)→ 必须保持 2L。 + # fits 检查若错编成 cmp hi, lo(reg 字段误指 r10 而非 rdx)→ 假拟合 → + # 降级截断(−2 / 1 快值落槽 + tag 0)→ syscall3 把快值当指针 → EFAULT + # (红证据:stdout 空 + exit ≠ 16)。 + ("d1_chain_neg_fitdisc", + "fn main() -> int {\n" + " m : ., mut = -9223372036854775807;\n" + " m = m + -2;\n" # -2^63-1(2L:hi=-1 lo=2^63-1) + " b : ., mut = 4611686018427387904;\n" + " b = b + 4611686018427387904;\n" # 2^63(2L:hi=0 lo=2^63) + " b = b + 1;\n" # 2^63+1(2L:hi=0 lo=2^63+1) + " d : ., mut = m - b;\n" # (-2^63-1)-(2^63+1) = -2^64-2 + " r := syscall3(1, 1, d, 16);\n return r;\n}\n", + # (lo=2^64-2 hi=2^64-2——同值非同 fit:lo 负 → signmask = -1 ≠ hi) + limb_bytes(-(1 << 64) - 2), 16, "假拟合判别:−2^64−2 → hi==lo==−2 须保持 2L(错编 cmp hi,lo 截断 −2)"), + ("d2_chain_pos_fitdisc", + "fn main() -> int {\n" + " a : ., mut = 4611686018427387904;\n" + " a = a + 4611686018427387904;\n" # 2^63(2L:hi=0 lo=2^63) + " b : ., mut = 4611686018427387904;\n" + " b = b + 4611686018427387904;\n" # 2^63(2L:hi=0 lo=2^63) + " b = b + 1;\n" # 2^63+1(2L:hi=0 lo=2^63+1) + " c : ., mut = a + b;\n" # 2^63+(2^63+1) = 2^64+1 + " r := syscall3(1, 1, c, 16);\n return r;\n}\n", + # (lo=1 hi=1——lo 正 → signmask = 0 ≠ hi) + limb_bytes((1 << 64) + 1), 16, "假拟合判别:2^64+1 → hi==lo==1 须保持 2L(错编 cmp hi,lo 截断 1)"), + # ── A 卫生:溢出后重定义/拷贝/参数,快值不得被误读为 2L ── + ("h1_hygiene_store_redef", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb,tag=1) + " x = 7;\n" # 常量定值(IR_STORE 清 tag) + " return x;\n}\n", + None, 7, "卫生:2L 后常量重定义 → return 快值 7(stale tag 会解引用 7 → 崩)"), + ("h2_hygiene_site_reexec", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " i : ., mut = 0;\n" + " while i < 3 {\n" + " x = x + 4611686018427387904;\n" # 同站点回边:快(2^62)→溢出(2^63) + " x = x - 4611686018427387904;\n" # 混合减法:→2^62(降级快) + " i = i + 1;\n" + " }\n" # 每轮归位 → 终值 2^62 + " x = x - 4611686018427387904;\n" # → 0 + " return x;\n}\n", + None, 0, "卫生:同站点快/慢交替(stale tag=1 会把快值 2^62 解引用 → 崩)"), + ("h3_hygiene_copy_prop", + "fn main() -> int {\n" + " x : ., mut = 4611686018427387904;\n" + " x = x + 4611686018427387904;\n" # 2^63(2-limb,tag=1) + " y := x;\n" # IR_STORE 拷贝 → tag 传播 + " r := syscall3(1, 1, y, 16);\n" + " z := x as int;\n" # IR_LOAD 拷贝(as)→ tag 传播 + " r2 := syscall3(1, 1, z, 16);\n" + " return r + r2;\n}\n", + limb_bytes(1 << 63) * 2, 32, "拷贝链 tag 传播:y/z 都读到 2^63(无传播 = 指针低字节)"), + ("h4_hygiene_param", + # 参数行 prologue 清 tag 的回归护栏:call 1 令 p 以 2L 态(tag=1)离开 + # (函数内最后一次 p 定值 = 溢出的 p=p+2^62),call 2 若不清参数 tag, + # p=p+2^62 站点会把 64 位参数值当 2L 指针解引用(值 ~4.6e18 → 崩)。 + "fn fb(p: int) -> int {\n" + " p = p + 4611686018427387904;\n" # 2^62+6 → 2^63+6(2L tag=1) + " q : ., mut = p - p;\n" # 2L − 2L 同值 = 0 → 降级快值 + " return q;\n}\n" + "fn main() -> int {\n" + " r1 := fb(4611686018427387910);\n" # 参数 2^62+6(64 位可载) + " r2 := fb(4611686018427387910);\n" # 第 2 次调用(栈帧复用) + " return r1 + r2;\n}\n", + None, 0, "卫生:参数 prologue 清 tag(2 次调用都须把 64 位参数当快值)"), +] + + +def run_checked(args, label): + cmd = ["nice", "-n", "19", *map(str, args)] + r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + print(f"[FAIL] {label}: exit {r.returncode}") + print(r.stdout[-3000:]) + print(r.stderr[-3000:]) + return False + return True + + +def main() -> int: + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + ok = True + for name, src, want_stream, want_exit, desc in CASES: + srcf = SCRATCH / f"{name}.cr" + srcf.write_text(src, encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + if not run_checked([COREC, "build", srcf, "-o", out, "--static", + "-O", str(opt)], f"{name} @O{opt} build"): + ok = False + continue + os = __import__("os") + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, capture_output=True, + timeout=60) + if want_stream is not None and r.stdout != want_stream: + print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " + f"{want_stream.hex()}\n {desc}") + ok = False + continue + if r.returncode != want_exit: + print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " + f"{want_exit}\n {desc}") + ok = False + continue + print(f"[PASS] {name} @O{opt}: exit {r.returncode}" + + (f" stream={want_stream.hex()}" if want_stream else "") + + f"\n {desc}") + print() + print("=== mw task4: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_mw_task5.py b/tests/selfhost/test_mw_task5.py new file mode 100644 index 00000000..f0825b78 --- /dev/null +++ b/tests/selfhost/test_mw_task5.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 5:D6 分配器适配——现状确认 + 测试加固(裁决:不改)。 + +plan D6 原案:「tagged(潜在多字)变量排除寄存器分配(只栈)」。执行前核 +实现状(Task 4 concern #1 消费点确认)后裁决:**reg 形态自洽成立,D6 排除 +不实施**。依据: + + 1. 表示演进使 D6 原始动机失效:plan 写作时的「多字值不可驻单寄存器」前提 + 在最终 D1a 表示(值槽 = 64 位快值或 2-limb 堆对象**指针** + 帧旁路 tag + 字节)下不成立——寄存器驻留的是指针,128 位载荷在堆对象; + 2. 单 seam 结构保证:全部变量值读写经 g2_slot(含 Task 3 jo 慢路径块回存 + e2_mov(dst_reg,rax)、Task 4 2L 操作数块装载、return 解指针、tag 卫生 + 落值)——tag 读写经 g2_tag_off(帧字节,与值形态无关);强制栈例外仅 + prologue 参数保存/寄存器参数装载(消费任何 tag 之前);表(HIT)路径 + preflight 已拒 reg 形态变量(hit_ev_slot_addr_ok → 落旧路径); + 3. O2 实证:tagged 变量**确实**被 CAG 分配寄存器(含 2L 态驻留——本测试 + 元断言),且 task3/4 电池 60 组 O2 全绿(16B 通道全 128 断言)—— + reg 形态下 2L 语义可运行且精确; + 4. 排除零收益有代价:发射侧无独立 reg/栈路径可删(块对 g2_slot 参数化, + 单代码路径),排除只损 O2 性能(tagged = 最常见的 add 循环变量行)。 + +本测试 = 该裁决的可执行形态(若将来翻案改只栈,下列 O2 元断言会红——届时 +须同步更新本文件并给出新证据): + + A. 行为(O0/O1/O2):三类 2L 态用例全绿(exit + syscall3 16B 全 128 + 通道)——与 task3/4 语义面重叠,但此处与 B 的元断言同用例链接; + B. 元断言(2026-09-07 regalloc 移后端后载体 = corearch --dump-regassign: + .ccr 不再携带 REG_ASSIGN(R2/D-1=Y)——corearch load 后自算;dump 通道 + 按 --opt-level 门逐对输出 var→reg。oracle 识别规则 = test_mw_task1): + - O1:corearch -O1 dump = 空(分配门 = opt_level ≥ 2——O1 tagged 变量 + 恒全栈,只栈平凡成立); + - O2:corearch -O2 dump:指定函数(运行时确实发生 2L 溢出的函数) + tagged∩REG_ASSIGN ≠ ∅ ——「2L 态值驻寄存器 + 行为全绿」链接成立 = + reg 形态自洽的活体实证。 + +.ccr 结构读取 = test_mw_task1.load_ir/detect_tags(oracle 复刻识别规则); +meta 元数据改经 corearch --dump-regassign(分配真相源 = corearch 内存态)。 +""" +import os +import re +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +COREARCH = BUILD / "corearch" +SCRATCH = BUILD / "mw_task5_scratch" + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from test_mw_task1 import load_ir, detect_tags # noqa: E402 + +REGASSIGN_LINE = re.compile(r"^regassign: (\d+) (\d+)$", re.MULTILINE) + + +def limb_bytes(S): + """128 位数学值 S → 2-limb 原始 16B(lo 小端 + hi 小端)。""" + lo = S & ((1 << 64) - 1) + hi = (S >> 64) & ((1 << 64) - 1) + return struct.pack(" dict: + """corearch --dump-regassign 通道(regalloc 移后端):corearch load .ccr + 后按 --opt-level 门自算(O2 = 分配)并逐对输出 var→reg。O0/O1 → 空。 + + 返回 {var_idx: reg}(.ccr 不再携带 REG_ASSIGN——分配结果同进程消费, + 元断言改从此通道取分配真相源)。""" + r = subprocess.run(["nice", "-n", "19", str(COREARCH), str(ccr_path), + "--dump-regassign", "--opt-level", str(opt_level)], + cwd=BASE, capture_output=True, text=True, timeout=120) + if r.returncode != 0: + raise RuntimeError(f"corearch --dump-regassign rc={r.returncode}: " + f"{r.stdout[-500:]} {r.stderr[-500:]}") + pairs = {} + for ln in r.stdout.splitlines(): + m = REGASSIGN_LINE.match(ln.strip()) + if m: + pairs[int(m.group(1))] = int(m.group(2)) + return pairs + + +# (name, src, 2L 溢出所在函数名, 期望 stdout(None = 只看 exit), 期望 exit, 意图) +CASES = [ + # h4 同形:参数行 2L 态 + 跨调用帧复用(O2 下 fb 的 p/q 驻 callee-saved) + ("t5_param_2l_reg", + "fn fb(p: int) -> int {\n" + " p = p + 4611686018427387904;\n" # 2^62+6 → 2^63+6(2L,tag=1) + " q : ., mut = p - p;\n" # 2L − 2L 同值 = 0 → 降级快值 + " return q;\n}\n" + "fn main() -> int {\n" + " r1 := fb(4611686018427387910);\n" + " r2 := fb(4611686018427387910);\n" + " return r1 + r2;\n}\n", + ["fb"], None, 0, "O2 元断言目标:fb 的 p/q tagged 且驻寄存器;2 次调用绿"), + # a7 同形:循环携带 2L 态跨迭代驻 reg(每轮 jo 慢路径 + alloc 新对象) + ("t5_loop_carry_2l_reg", + "fn main() -> int {\n" + " x : ., mut = 0;\n" + " i : ., mut = 0;\n" + " while i < 6 {\n" + " x = x + 4611686018427387904;\n" # 每轮 +2^62:→2^63(2L)… + " i = i + 1;\n" + " }\n" # 6 轮 = 2^64+2^63(hi=1) + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + ["main"], limb_bytes((1 << 64) + (1 << 63)), 16, + "O2 元断言目标:循环携带 x 驻 reg 且运行时达 2L 态(hi=1)——16B 通道验证"), + # c5 同形:双 2L 对象比较(a/b 各自溢出生 2L 堆对象,比较读 tag 解指针) + ("t5_two2l_cmp_reg", + "fn main() -> int {\n" + " a : ., mut = 4611686018427387904;\n" + " a = a + 4611686018427387904;\n" # 2^63(2-limb 对象 1) + " b : ., mut = 4611686018427387904;\n" + " b = b + 4611686018427387904;\n" # 2^63(2-limb 对象 2) + " if a >= b && a <= b { return 1; }\n" + " return 2;\n}\n", + ["main"], None, 1, "O2 元断言目标:a/b 2L 态驻 reg;128 位比较绿"), +] + + +def run_checked(args, label): + cmd = ["nice", "-n", "19", *map(str, args)] + r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + print(f"[FAIL] {label}: exit {r.returncode}") + print(r.stdout[-3000:]) + print(r.stderr[-3000:]) + return False + return True + + +def main() -> int: + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + ok = True + for name, src, ovf_funcs, want_stream, want_exit, desc in CASES: + srcf = SCRATCH / f"{name}.cr" + srcf.write_text(src, encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + if not run_checked([COREC, "build", srcf, "-o", out, "--static", + "-O", str(opt)], f"{name} @O{opt} build"): + ok = False + continue + ccr = SCRATCH / f"{name}_o{opt}.ccr" + if not ccr.exists(): + print(f"[FAIL] {name} @O{opt}: .ccr missing at {ccr}") + ok = False + continue + funcs, var_types = load_ir(ccr) + if opt == 1: + regs = dump_regassign(ccr, 1) + if regs: + print(f"[FAIL] {name} @O1: corearch dump REG_ASSIGN = " + f"{len(regs)} != 0(O1 不应有寄存器分配——分配门 = O2)") + ok = False + else: + print(f"[PASS] {name} @O1: REG_ASSIGN = 0(全栈,tagged 只栈平凡)") + if opt == 2: + regs = dump_regassign(ccr, 2) + # 元断言:运行时 2L 溢出所在函数内 tagged∩REG_ASSIGN ≠ ∅ + hit = False + for f in funcs: + if f["name"] not in ovf_funcs: + continue + vs = f["var_start"] + ve = vs + f["var_count"] + tg = detect_tags(f, var_types) + ins = sorted(v for v in tg if vs <= v < ve and v in regs) + if ins: + hit = True + print(f"[PASS] {name} @O2 元断言: func {f['name']} " + f"tagged∩regs={[(v, regs[v]) for v in ins]}") + if not hit: + print(f"[FAIL] {name} @O2 元断言: {ovf_funcs} 内无 tagged 变量" + "被分配寄存器——reg 形态裁决失效(详见测试头注释)") + ok = False + # 行为断言(与 task3/4 同语义面;此处与上列元断言同用例链接) + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, capture_output=True, + timeout=60) + if want_stream is not None and r.stdout != want_stream: + print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " + f"{want_stream.hex()}\n {desc}") + ok = False + continue + if r.returncode != want_exit: + print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " + f"{want_exit}\n {desc}") + ok = False + continue + print(f"[PASS] {name} @O{opt}: exit {r.returncode}" + + (f" stream={want_stream.hex()}" if want_stream else "") + + f"\n {desc}") + print() + print("=== mw task5: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_mw_task6.py b/tests/selfhost/test_mw_task6.py new file mode 100644 index 00000000..5dfcccd3 --- /dev/null +++ b/tests/selfhost/test_mw_task6.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +"""int 多字 M1 Task 6:溢出程序边界套件(M1 收官回归语料补全)。 + +Task 3 已覆盖 CF/op 判别矩阵(m1-m6),Task 4 已覆盖消费者判别 +(return/比较/链式/卫生 r-a/h/d 系列)——本套件补 plan Task 6 要求的 +**边界值组合**:i64max(2^63−1,最大可写字面量)±1 链、i64min(−2^63, +不可写为字面量——恰在编码边界的快值形态)邻域、负数与快慢混合、2L 在 +上下边界处参与比较/链式/循环携带。全部用例 × {O0, O1, O2}。 + +边界事实(语料构造依据): +- i64max = 9223372036854775807 是最大合法字面量(lexer i64max 幅守卫); + −2^63 字面量被拒 → i64min 快值只能经 −(2^63−1) + −1 构造(该步**不** + 溢出——OF 只发生在和 < −2^63,硬件判定与 128 码域在此分界)。 +- 2L 值观测 = syscall3(1,1,x,16) 原始 16B 通道(spec §5:exit 只给低 8 位; + 全 128 断言经测试钩子);快值终点 = return 低字节数学断言。 +- 期望值全部为 Python 独立计算(limb_bytes),非从编译器输出回读。 + +验证面(每用例): +1. 16B 流(期望 2L 的站点)逐字节 == 数学值 2-limb 表示——越界点确实走了 + 慢路径且 128 位真值正确; +2. exit 码 == 16×对象数(有流)或 return 数学值低 8 位(快值终点); +3. 边界比较真值表(e5-e7 判别:128 语义下唯一正确结果,任一「指针比较/ + 低 limb 有符号/分派漏读」实现给反)。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" +import os +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +BUILD = BASE / "build" +COREC = BUILD / "corec" +SCRATCH = BUILD / "mw_task6_scratch" + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from test_mw_task4 import limb_bytes # noqa: E402 + +S64 = 1 << 63 +IMAX = S64 - 1 # 9223372036854775807(i64max——最大可写快值字面量) +IMIN1 = -IMAX # -(2^63-1)(最小可写负字面量 = i64min+1) + +# (name, src, 期望 stdout 流(None = 只看 exit), 期望 exit, 意图) +CASES = [ + # ── 上边界:i64max +1 = 恰好一步越界(jo 正溢出 hi=0)── + ("e1_max_plus1", + "fn main() -> int {\n" + " x : ., mut = 9223372036854775807;\n" # i64max(最大快值) + " x = x + 1;\n" # = 2^63 → 2L(hi=0 lo=0x8000..0) + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + limb_bytes(S64), 16, "上边界 +1:i64max → 2^63 恰好一步越界"), + # ── 上边界往返:三步连续越界 + 降级回快值 + 再越界(fits 判据 lo 符号位)── + ("e2_max_chain_roundtrip", + "fn main() -> int {\n" + " x : ., mut = 9223372036854775807;\n" + " x = x + 1;\n" # 2^63(2L) + " x = x + 1;\n" # 2^63+1(2L+快链) + " x = x + 1;\n" # 2^63+2(2L) + " r1 := syscall3(1, 1, x, 16);\n" # 流 1:2^63+2 + " x = x - 3;\n" # = 2^63-1 = i64max → 降级快值 + " x = x + 1;\n" # → 2^63(快→再越界 2L) + " r2 := syscall3(1, 1, x, 16);\n" # 流 2:2^63 + " return r1 + r2;\n}\n", + limb_bytes(S64 + 2) + limb_bytes(S64), 32, + "上边界往返:3 步越界链(2L+快)→ −3 降级回 i64max(fits:hi==signmask(2^63−1)==0)→ +1 再越界"), + # ── 下边界邻域:快值恰在 −2^63(不溢出)+ add/sub 双越界 + 回升降级 ── + ("e3_min_edge_addsub", + "fn main() -> int {\n" + " x : ., mut = -9223372036854775807;\n" # i64min+1(可写最小字面量) + " x = x + -1;\n" # = -2^63 **快值**(恰下边界——不 OF) + " x = x + -1;\n" # = -2^63-1 → 2L(hi=-1 lo=2^63-1) + " r1 := syscall3(1, 1, x, 16);\n" # 流 1:-2^63-1 + " x = x - 1;\n" # = -2^63-2 → 2L(sub 负溢出) + " r2 := syscall3(1, 1, x, 16);\n" # 流 2:-2^63-2(lo=2^63-2) + " x = x + 3;\n" # = -2^63+1 → 降级快值(hi=-1==signmask(lo)) + " return x;\n}\n", # exit = (-2^63+1) 低字节 0x01 + limb_bytes(-S64 - 1) + limb_bytes(-S64 - 2), 1, + "下边界:−(2^63−1)+−1 = −2^63 快值不溢出(负 OF 只发生在 < −2^63)→ add/sub 连续越界 → +3 降级回快值 return 1"), + # ── sub 形式的上边界(i64max − −n)+ 降级后再越界 ── + ("e4_sub_neg_edge", + "fn main() -> int {\n" + " x : ., mut = 9223372036854775807;\n" # i64max + " x = x - -1;\n" # = 2^63 → 2L(sub 正溢出 CF=1 → hi=0) + " r1 := syscall3(1, 1, x, 16);\n" # 流 1:2^63 + " x = x - 1;\n" # = 2^63-1 → 降级快值 i64max + " x = x - -2;\n" # = 2^63+1 → 快+快再越界(jo) + " r2 := syscall3(1, 1, x, 16);\n" # 流 2:2^63+1 + " return r1 + r2;\n}\n", + limb_bytes(S64) + limb_bytes(S64 + 1), 32, + "sub 边界:i64max − −1 = 2^63(CF 判别)→ −1 降级 → − −2 快→溢再越界"), + # ── 上下边界同函数双站越界 + 2L vs 2L 高 limb 符号判别 ── + ("e5_two_edge_cross_cmp", + "fn main() -> int {\n" + " a : ., mut = 9223372036854775807;\n" + " a = a + 1;\n" # 2^63(2L hi=0——上边界越界) + " b : ., mut = -9223372036854775807;\n" + " b = b + -1;\n" # -2^63 快值 + " b = b + -1;\n" # -2^63-1(2L hi=-1——下边界越界) + " r : ., mut = syscall3(1, 1, a, 16);\n" # 流 1:2^63 + " r = r + syscall3(1, 1, b, 16);\n" # 流 2:-2^63-1 + " if a > b { r = r + 1; }\n" # 2L(hi=0) > 2L(hi=-1):高 limb 符号判别 + " return r;\n}\n", # 16+16+1 = 33 + limb_bytes(S64) + limb_bytes(-S64 - 1), 33, + "上下边界混合:双站(上/下各一步越界)+ 2L vs 2L 高 limb 判别(同低 limb 无符号形给反)"), + # ── i64max 边界 fast↔2L 比较真值表(LT/GT 双分派 + EQ/GE 假性)── + ("e6_max_cmp_dispatch", + "fn main() -> int {\n" + " x : ., mut = 9223372036854775807;\n" + " x = x + 1;\n" # 2^63(2L) + " ok : ., mut = 0;\n" + " if 9223372036854775807 < x { ok = ok + 1; }\n" # fast(s1) < 2L(s2):B 路——紧贴边界的最大快值 + " if x > 9223372036854775807 { ok = ok + 1; }\n" # 2L(s1) > fast(s2):A 路镜像 + " if x == 9223372036854775807 { ok = ok - 10; }\n" # 128 EQ 必须假(值不等)——恒真实现给 248 + " if 9223372036854775807 >= x { ok = ok - 10; }\n" # GE 必须假——(i64max, 2^63) 最紧假例 + " return ok;\n}\n", + None, 2, "i64max 边界比较真值表:max<2^63 两分派方向真、EQ/GE 假(−10 哨兵)"), + # ── i64min 边界:快值 −2^63 vs 2L −2^63−1——同 hi 低 limb 无符号判别 ── + ("e7_min_cmp_lo_unsigned", + "fn main() -> int {\n" + " m : ., mut = -9223372036854775807;\n" + " m = m + -1;\n" # -2^63 快值(恰下边界,不 2L) + " n : ., mut = m;\n" + " n = n + -1;\n" # -2^63-1 → 2L(hi=-1 lo=2^63-1) + " o : ., mut = 0;\n" + " if n < m { o = o + 1; }\n" # hi 同为 -1(m 快值符号扩展)→ 低 limb 无符号: + " if m > n { o = o + 1; }\n" # n lo=2^63-1 < m lo=2^63 才真——有符号 lo 给反 + " if n > m { o = o + 100; }\n" # 反哨兵(错序 + 100) + " return o;\n}\n", + None, 2, "i64min 边界:快值 −2^63 vs 2L −2^63−1——同 hi(−1) 低 limb 无符号判别(有符号 lo 给 101)"), + # ── 循环携带越过上边界(同站点回边逐轮 2L 化)+ 独立快计数器 ── + ("e8_loop_max_chain", + "fn main() -> int {\n" + " x : ., mut = 9223372036854775807;\n" + " i : ., mut = 0;\n" + " while i < 3 {\n" + " x = x + 1;\n" # 每轮 +1:2^63 → 2^63+1 → 2^63+2 + " i = i + 1;\n" # 快计数器(永不越界) + " }\n" + " r := syscall3(1, 1, x, 16);\n return r;\n}\n", + limb_bytes(S64 + 2), 16, + "循环携带越过上边界:i64max 起每轮 +1(快→2L 同站点回边链)+ 独立快计数"), +] + + +def run_checked(args, label): + cmd = ["nice", "-n", "19", *map(str, args)] + r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + print(f"[FAIL] {label}: exit {r.returncode}") + print(r.stdout[-3000:]) + print(r.stderr[-3000:]) + return False + return True + + +def main() -> int: + if not COREC.exists(): + print("build/corec missing; run build_selfhost_native.py") + return 1 + SCRATCH.mkdir(exist_ok=True) + ok = True + for name, src, want_stream, want_exit, desc in CASES: + srcf = SCRATCH / f"{name}.cr" + srcf.write_text(src, encoding="utf-8") + for opt in (0, 1, 2): + out = SCRATCH / f"{name}_o{opt}" + if not run_checked([COREC, "build", srcf, "-o", out, "--static", + "-O", str(opt)], f"{name} @O{opt} build"): + ok = False + continue + os.chmod(out, 0o755) + r = subprocess.run([str(out)], cwd=BASE, capture_output=True, + timeout=60) + if want_stream is not None: + if r.stdout != want_stream: + print(f"[FAIL] {name} @O{opt}: stdout " + f"{r.stdout.hex()} != {want_stream.hex()} — " + f"2-limb 128 位流数学不正确\n {desc}") + ok = False + continue + ns = len(want_stream) // 16 + want_rc = want_exit if want_exit is not None else 16 * ns + if r.returncode != want_rc: + print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " + f"{want_rc}(16B×{ns} 对象{'+比较加分' if want_exit != 16 * ns else ''})") + ok = False + continue + else: + if r.returncode != want_exit: + print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " + f"{want_exit}(return 数学值低 8 位判别)\n {desc}") + ok = False + continue + print(f"[PASS] {name} @O{opt}: exit {r.returncode}" + + (f",流 {len(want_stream)}B == {want_stream.hex()}" + if want_stream is not None else "") + + f"\n {desc}") + print() + print("=== mw task6: " + ("ALL PASS" if ok else "FAILURES") + " ===") + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_named_dedup.py b/tests/selfhost/test_named_dedup.py new file mode 100644 index 00000000..6267ac1f --- /dev/null +++ b/tests/selfhost/test_named_dedup.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""R2 P2a Task 3(C-4):侧表 ↔ res_type_node **管线内断言**(corec check --verify-named-dedup)。 + +背景(Task 1 交接必测项):`f1.*` 自测用例是**人造夹具**(不经 check_all,手工 alloc + 单名查 +行数)——证明不了「8 个生产分配点全走侧表」:任一生产点若退回裸 `alloc_type(TYP_NAMED, n, 0)`, +自测照样全绿,而真实编译中该名字会**占两行**(读名字的点仍对,比**行号**的点错——现存比行号面 += 泛型应用基型行 / 桥接项 `AK_NAMED` 的 b 槽(规范形,`sh_apply_identity_term`))。本断言跑在真实流水线(check_all +之后)上,三组检查全部基于本编译期的真实类型表 / 符号表 / AST: + + ① 类型表 → 侧表:每个 TYP_NAMED 行按名查侧表须**命中且回指本行**; + ② 侧表 → 类型表:每条登记须回指合法行(kind=TYP_NAMED、名字相符)且该名字**恰 1 行**; + ③ 读取面:AST 中每个解析为已注册命名类型的 EXPR_IDENT,`res_type_node(node)` 须等于侧表 + 命中 ti(替弱断言 f1.row_count 的那条)。 + +判据 = rc=0 ∧ `mismatches=0` ∧ `ident_checks>0`(非空转:至少真的检查到类型名引用)。 +负控(注入故障)不在树内:需某生产点绕过侧表后**重建编译器**实测(输出 mismatches>0、rc=1), +见 .superpowers/sdd/r2p2-task-3-report.md 的 C-4 节。 + +需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py +""" + +import re +import subprocess +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +# 语料 = at_test_struct.cr(brief 指定:结构体密集,命名类型注册 + 多处类型名引用) +CORPUS = BASE / "tests" / "suite" / "at_test_struct.cr" + +SUMMARY = re.compile( + r"\[named-dedup\] named_rows=(\d+) ident_checks=(\d+) mismatches=(\d+)" +) + + +def run_verify(corpus=CORPUS): + return subprocess.run( + ["nice", "-n", "19", str(COREC), "check", str(corpus), "--verify-named-dedup"], + cwd=BASE, capture_output=True, text=True, + ) + + +def test_named_dedup_pipeline_assertion(): + r = run_verify() + out = r.stdout + r.stderr + m = SUMMARY.search(out) + assert m, out + named_rows, ident_checks, mismatches = (int(g) for g in m.groups()) + assert r.returncode == 0, (r.returncode, out) + assert mismatches == 0, out + # 非空转:至少检查到一个类型名引用节点(否则断言③恒真) + assert ident_checks > 0, out + assert named_rows > 0, out + + +if __name__ == "__main__": + test_named_dedup_pipeline_assertion() + print("PASS test_named_dedup_pipeline_assertion") diff --git a/tests/selfhost/test_named_face.py b/tests/selfhost/test_named_face.py new file mode 100644 index 00000000..d5a8387e --- /dev/null +++ b/tests/selfhost/test_named_face.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +"""R2 P5 Task 3 回归:命名面判定化(身份链 + 可判定面加强)行为覆盖集。 + +背景(RED,见 .superpowers/sdd/p5-task3-report.md §RED):命名型(struct/enum/接口行、 +泛型形参、泛型应用)在判定引擎里原本**一律未覆盖**(AK_NAMED 原子不展开 ⇒ `ty_equiv` 回 +-1)⇒ 判定回落 legacy(`g_replace_unknown`): + * 异名同形命名型(`NA` vs `NB`)= 引擎 -1 + 回落 1(legacy 判 0 = 拒); + * 同实参泛型应用两次实例化(`Box[int]` 两处 = **两行**)= 引擎 -1 + 回落 1(legacy 判 1 + = 受)。 +本任务把**可判定**的命名面接进引擎:身份 = 桥接层构造的**身份链**(`[名字令牌]` / +`[基名令牌, 实参项…]`),规则 = 同链 ⇒ 1 / 链异且链元素在域内 ⇒ 0 / 域外 ⇒ -1(三态纪律)。 +**不是**结构性展开(P3a 实测反证:展开项接进等价面 = 同形不同名 struct 判等)。 + +判据(本套件 = **行为断言形态**:只读 rc + 诊断 + 产物执行结果,**不依赖影子通道**—— +影子层下线(P5 Task 5)后本套件照常可跑): + ① **受**(rc=0 + ELF 执行 rc=0):同名命名互赋 / 泛型应用两次实例化(同实参)/ 嵌套应用 / + 容器与元组含命名(同名)/ 递归命名(*Node)/ 泛型形参 T vs T / 别名一层(透明) + ② **拒**(rc=1 + 具体诊断 + 无产物):异名同形命名 / 异实参泛型应用 / 命名 vs 原生 / + 命名 vs 泛型应用 / 可选含命名(异名)/ 泛型形参 T 赋 int + ③ **行为同值**:以上受/拒集合与 legacy 口径逐条相同(清零 ≠ 放宽,也 ≠ 收紧)—— + 判据面 = rc/诊断/产物;清零的**计数证据**(回落归零)在报告与自测段(`t3n.*`)。 + ④ **残留面**(计划停条件②):参数链**不变槽**元素非同形(`[NA;2]` vs `[NB;2]`、 + `*NA` vs `*NB`、元组含异名)——T3 时仍走 legacy 判定 + 回落(**行为已正确** = 拒), + **P5 Task 3b 已收口**(`te_elem_cmp` 三态:令牌按节点同一性 / 类型项按 `ty_equiv`) + ⇒ 本套件只钉行为(清零后不得变成接受);T4 已把 legacy 与回落面删除。 + +运行:`python3 tests/selfhost/test_named_face.py`(cwd = 仓库根;需先构建 build/corec)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _write(source): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + return src, src[:-3] + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def case_accept(name, source): + """受:ELF 编译 rc=0 ∧ 执行 rc=0(前端 + 发射 + 运行三面同证)。""" + src, out = _write(source) + try: + built = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + if built.returncode != 0: + print(f"[FAIL] {name}: compile rc={built.returncode}: {built.stdout}{built.stderr}") + return False + run = subprocess.run([out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump) + if run.returncode != 0: + print(f"[FAIL] {name}: run rc={run.returncode}(受却运行失败)") + return False + print(f"[PASS] {name}: compile+run rc=0") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_reject(name, source, needle): + """拒:`check` rc≠0 ∧ 诊断含 needle;且 `build` 面同诊断在册(发射面同为拒绝语义)。 + + 注(预存行为,非本任务引入,登记于 P5 Task 3 报告):TA01 这类**非硬名单**诊断不阻 + `build`(`main.cr` 的硬名单闸只收 R002/TK05/06/TS*/TK02/TM03;实测冻结基线同)——故本 + 套件以 `check` 的 rc 为判据(= 项目判据面口径),并断言 build 面诊断文本同现。 + """ + src, out = _write(source) + try: + chk = subprocess.run( + [str(COREC), "check", src], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + if chk.returncode == 0: + print(f"[FAIL] {name}: check 期望拒绝却 rc=0(静默误编译面复活)") + return False + if needle not in (chk.stdout + chk.stderr): + print(f"[FAIL] {name}: rc={chk.returncode} 但缺诊断 {needle!r}: {chk.stdout}{chk.stderr}") + return False + built = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + if needle not in (built.stdout + built.stderr): + print(f"[FAIL] {name}: build 面缺同诊断(判定未在发射面同现)") + return False + print(f"[PASS] {name}: check rc={chk.returncode} + {needle!r} + build 面同证") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +TA01 = "Assignment type mismatch" + +NA_NB = "struct NA { x: int }\nstruct NB { x: int }\n" +BOX = "struct Box[T] { v: T }\n" + + +def main(): + ok = [ + # ── ① 受:命名面同链(身份链逐元素同 ⇒ 1)── + case_accept("named.same_name_assign", NA_NB.replace("struct NB { x: int }\n", "") + + "fn main() -> int {\n" + " a : ., mut = NA { x: 1 };\n" + " b : ., mut = NA { x: 2 };\n" + " a = b;\n" + " return a.x - 2;\n" + "}\n"), + # 泛型应用两次实例化(**两行**同实参)——RED 形态之一,修复前 = 引擎 -1 + 回落。 + # 形参位声明(应用行随每处出现各分配一行)。注:`Box[int] { … }` 字面量形态在本 + # 语言 parser 下不可解析(P01,冻结基线同——预存面,见 T0 探针 d3 变体),故走形参位。 + case_accept("apply.two_instantiations_same_args", BOX + + "fn g(a: Box[int], b: Box[int]) -> int {\n" + " a = b;\n" + " return a.v - b.v;\n" + "}\n" + "fn main() -> int { return 0; }\n"), + # 嵌套泛型应用(外层实参 = 内层应用,两处**不同行**同实参)⇒ 规范形 ⇒ 同节点 + case_accept("apply.nested_same_args", BOX + + "fn g(a: Box[Box[int]], b: Box[Box[int]]) -> int { a = b; return 0; }\n" + "fn main() -> int { return 0; }\n"), + # 泛型应用实参含命名型(同链) + case_accept("apply.named_arg_same", NA_NB + BOX + + "fn g(a: Box[NA], b: Box[NA]) -> int { a = b; return 0; }\n" + "fn main() -> int { return 0; }\n"), + # 容器/元组/指针含命名(同元素)——b 槽标注不入身份 + 同链 + # (R2 P5 Task 6:指针位原为 `p : *NA = 0;`——`0` 是 int 字面量,声明位点此前无检查 + # 故静默通过;TA02 落地后与**赋值位点**既有语义对齐(`*T ← int/null` 早已 TA01 拒绝, + # 见 p5-task6-report §收紧台账)= 改为无初值声明。指针身份面语义不变。) + case_accept("named.in_container_same", NA_NB.replace("struct NB { x: int }\n", "") + + "fn main() -> int {\n" + " a : [NA; 2] = [NA { x: 1 }, NA { x: 2 }];\n" + " b : [NA; 2] = [NA { x: 3 }, NA { x: 4 }];\n" + " c : NA? = NA { x: 5 };\n" + " d : NA? = NA { x: 6 };\n" + " p : *NA, mut;\n" + " q : *NA, mut;\n" + " a = b; c = d; p = q;\n" + " return 0;\n" + "}\n"), + case_accept("named.in_tuple_same", NA_NB.replace("struct NB { x: int }\n", "") + + "fn main() -> int {\n" + " a : ., mut = (NA { x: 1 }, 2);\n" + " b : ., mut = (NA { x: 3 }, 4);\n" + " a = b;\n" + " return 0;\n" + "}\n"), + # 递归命名(*Node 元素位 = 命名行,同链) + case_accept("named.recursive_ptr_same", "struct Node { val: int, next: *Node }\n" + "fn f(p: *Node, q: *Node) -> int { p = q; return 0; }\n" + "fn main() -> int { return 0; }\n"), + # 泛型形参位 T vs T(同链:同一形参行) + case_accept("named.generic_param_same", "fn pick[T](a: T, b: T) -> int { return 0; }\n" + "fn main() -> int { return pick(1, 2); }\n"), + # 别名一层:透明(alias 解析到 RHS 行,不产生命名行身份) + case_accept("named.alias_transparent", "type X = int;\n" + "fn main() -> int { a : X = 1; b : int = 2; a = b; return a - 2; }\n"), + # 命名型经函数边界(形参/返回位同链) + case_accept("named.fn_boundary", NA_NB.replace("struct NB { x: int }\n", "") + + "fn take(v: NA) -> int { return v.x; }\n" + "fn main() -> int { a : ., mut = NA { x: 7 }; b : ., mut = NA { x: 7 }; a = b; return take(a) - 7; }\n"), + # ── ② 拒:链异 ⇒ 0(= legacy 拒;清零前的回落面)── + case_reject("named.diff_name_rejected", NA_NB + + "fn main() -> int { a : ., mut = NA { x: 1 }; b : ., mut = NB { x: 2 }; a = b; return a.x; }\n", TA01), + case_reject("apply.diff_args_rejected", BOX + + "fn g(a: Box[int], b: Box[str]) -> int { a = b; return a.v; }\n" + "fn main() -> int { return 0; }\n", TA01), + case_reject("named.vs_base_rejected", "struct NA { x: int }\n" + "fn main() -> int { a : NA = NA { x: 1 }; b : int = 2; a = b; return a.x; }\n", TA01), + case_reject("named.vs_apply_rejected", NA_NB + BOX + + "fn g(a: Box[NA], b: Box[int]) -> int { a = b; return 0; }\n" + "fn main() -> int { return 0; }\n", TA01), + case_reject("named.optional_diff_rejected", NA_NB + + "fn main() -> int { a : NA? = NA { x: 1 }; b : NB? = NB { x: 2 }; a = b; return 0; }\n", TA01), + case_reject("named.generic_param_vs_int_rejected", + "fn h[T](a: T, b: int) -> int { a = b; return 0; }\n" + "fn main() -> int { return h(1, 2); }\n", TA01), + # ── ③ 残留登记(停条件②面:不变槽元素非同形——行为已正确,本任务不动其清零)── + case_reject("residual.invariant_slot_arr_rejected", NA_NB + + "fn main() -> int {\n" + " a : [NA; 2] = [NA { x: 1 }, NA { x: 2 }];\n" + " b : [NB; 2] = [NB { x: 3 }, NB { x: 4 }];\n" + " a = b;\n" + " return 0;\n" + "}\n", TA01), + case_reject("residual.invariant_slot_ptr_rejected", NA_NB + + "fn g(a: *NA, b: *NB) -> int { a = b; return 0; }\n" + "fn main() -> int { return 0; }\n", TA01), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_nested_fn.py b/tests/selfhost/test_nested_fn.py new file mode 100644 index 00000000..445ae1ee --- /dev/null +++ b/tests/selfhost/test_nested_fn.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +"""TODO #16 回归:函数体内嵌套 `fn` 声明 → 编译段错误 rc=139(2026-09-11 修复)。 + +语言面判定(为何修成「定位报错」而非「支持嵌套 fn」): + * grammar/core.ebnf:`TopLevelDecl = FunctionDecl | ...` 且 + `Statement = Expr ';' | LetStmt | ReturnStmt | YieldStmt | BreakStmt | ContinueStmt` + —— 函数声明**只是顶层构造**,Statement 不含 FunctionDecl + ⇒ 函数体内的嵌套 `fn` 不在语言面内。 + * bootstrap 参考实现(bootstrap/corec/frontend/parser.py)对同一输入给出定位错误 + `SyntaxError: 2:6: Unexpected token: fn`(不崩、不静默通过)。 + * docs/spec-design.md:341:实现层函数值(TYP_FN/闭包)按 YAGNI 挂起。 + ⇒ 正确修复 = 响亮定位错误(rc=1 + error[P21]),不是「支持」,更不是崩溃。 + (原 TODO 记的「判据 = 最小复现 rc=0」隐含「应支持」,与本判定冲突; + 本文件与 src/ci/run.sh 的 SKIP 理由一并记录裁决。) + +RED(修复前实测 2026-09-11,核心版 0390f0f4): + * 最小件 `fn outer(){ fn inner(a:int)->int{return a+1;} return inner(1); }` rc=139, + 日志止于 `[3/5] parse...`(`[4/5]` 未打印)。 + * gdb -batch -ex run -ex "info registers" --args ./build/corec check ... + → 崩点 `grow_ast` 内 `rep movsb`:rdi(目的地)=0、rcx=0x12000000 + (g_ast_cap=4M 节点 × ESZ_ASTNODE=72 = 288MB)。即 bump allocator 触顶后 + rt.s 的 .Lalloc_oom 返回 NULL,grow_ast 仍向 NULL 拷贝 ⇒ SIGSEGV。 + * 临时插桩(已移除,仅取证)在 parse_primary 的 struct 字面量循环打点: + `SPIN_DIAG struct-literal-loop iter=500000 tokpos=1900 kind=0` + (kind=0 = T_EOF)⇒ 自旋点 = 该循环,非 checker。 + +根因链(两处缺陷叠加): + 1. 嵌套 `fn` 无诊断:parse_primary 无 T_FN 分支,落回通用兜底 + 「Unexpected token in expression」——只消费 `fn` 一个 token,解析失步。 + 2. 失步后 `int {`(内层 fn 的返回类型 + 函数体花括号)被当作 struct 字面量 + (IDENT + `{`)解析,把外层 `}` 当字段吃掉直至 EOF;而该循环只认 `}`, + advance_tok 在 EOF 是空操作 ⇒ 自旋,每轮分配 AST 节点直至 OOM。 + +修复: + 1. parser.parse_stmt:语句位置遇 `fn`/`flow`(含 `pub fn`)→ P021 定位诊断 + + 花括号配平整段跳过该声明,后续语句恢复正常解析(不再失步)。 + 2. parser 同族 6 处 `}`-键循环补 EOF 护栏(struct 字面量 / struct 模式 / + struct 体 / enum 体 / interface 体 / impl 体):失步至 EOF 一律退出。 + +判据:嵌套形态 rc=1 + error[P21] + 定位行号 + 无 139 + 毫秒级;失步型 EOF 探针 +(各构造截断在 EOF)rc=1 且不崩;扁平对照 rc=0 且运行结果正确;两个 suite fixture +由 rc=139 变为 rc=1 + P21。 +""" + +import os +import resource +import subprocess +import tempfile +import time +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 修复前该形态 ~12–16s 后 SIGSEGV;修复后毫秒级。阈值放宽以抗共享机负载。 +BOUND_SEC = 5.0 +PROC_TIMEOUT = 30 + +# 嵌套 fn 的最小件(TODO #16 原文形态) +NESTED_MIN = """fn outer() -> int { + fn inner(a: int) -> int { return a + 1; } + return inner(1); +} + +fn main() -> int { return outer(); } +""" + +# 同逻辑扁平对照(语言面内形态):inner(1) = 2 ⇒ 退出码 2 +FLAT_CONTROL = """fn inner(a: int) -> int { return a + 1; } + +fn outer() -> int { + return inner(1); +} + +fn main() -> int { return outer(); } +""" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _write(source: str): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + return src + + +def _check(source: str): + """check 编译:返回 (returncode, output, elapsed)。""" + src = _write(source) + try: + t0 = time.monotonic() + r = subprocess.run( + [str(COREC), "check", src], + capture_output=True, text=True, cwd=BASE, timeout=PROC_TIMEOUT, + ) + return r.returncode, r.stdout + r.stderr, time.monotonic() - t0 + finally: + os.unlink(src) + + +def _build_run(source: str): + """build + 运行:返回 (build_rc, run_rc, output)。""" + src = _write(source) + out = src[:-3] + try: + b = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=PROC_TIMEOUT, + ) + if b.returncode != 0: + return b.returncode, None, b.stdout + b.stderr + r = subprocess.run([out], capture_output=True, text=True, timeout=PROC_TIMEOUT) + return 0, r.returncode, r.stdout + r.stderr + finally: + for p in (src, out): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def case_reject_nested(name: str, source: str, fn_name: str = "inner"): + """嵌套 fn 形态:rc=1 + error[P21] + 定位 + 无 139 + 有界耗时。""" + rc, out, el = _check(source) + problems = [] + if rc != 1: + problems.append(f"rc={rc}(期望 1;139/负值 = 信号崩溃)") + if "error[P21]" not in out: + problems.append("缺 error[P21]") + if fn_name and f"Nested function declaration '{fn_name}'" not in out: + problems.append(f"缺诊断文本('{fn_name}')") + if " --> " not in out: + problems.append("缺定位(` --> line:col`)") + if el > BOUND_SEC: + problems.append(f"耗时 {el:.2f}s > {BOUND_SEC}s(自旋未除)") + if problems: + return f"FAIL {name}: " + "; ".join(problems) + "\n--- output ---\n" + out + return None + + +def case_reject_desync(name: str, source: str): + """失步型输入截断在 EOF:必须 rc=1(或非零)收场,绝不 139 / 挂死。""" + rc, out, el = _check(source) + problems = [] + if rc == 0: + problems.append("rc=0(期望拒绝)") + if rc < 0 or rc == 139: + problems.append(f"rc={rc}(信号崩溃 = 回归)") + if el > BOUND_SEC: + problems.append(f"耗时 {el:.2f}s > {BOUND_SEC}s(EOF 自旋未除)") + if problems: + return f"FAIL {name}: " + "; ".join(problems) + "\n--- output ---\n" + out + return None + + +def main() -> int: + _no_core_dump() + failures = [] + checks = 0 + + # ① 主判据:TODO #16 最小复现形态(修复前 rc=139) + checks += 1 + f = case_reject_nested("nested_min_todo16", NESTED_MIN, "inner") + if f: + failures.append(f) + + # ② suite fixture 两例(修复前 rc=139;现应指向 P21 诊断) + for fx in ("at_test_mini4", "at_test_mini6"): + checks += 1 + path = BASE / "tests" / "suite" / f"{fx}.cr" + src = path.read_text() + f = case_reject_nested(f"fixture_{fx}", src, "add") + if f: + failures.append(f) + + # ③ 变体:@inline 调用形 / 无 @inline 形 / pub 前缀 / 双层嵌套 / 嵌套后仍有语句 + variants = [ + ("nested_inline_hint", """import io + +fn test_inline_hint() -> int { + fn add(a: int, b: int) -> int { return a + b; } + result := @inline(add)(3, 4); + if result != 7 { return 1; } + return 0; +} + +fn main() -> int { return test_inline_hint(); } +""", "add"), + ("nested_plain_call", """fn test() -> int { + fn add(a: int, b: int) -> int { return a + b; } + return add(3, 4); +} + +fn main() -> int { return test(); } +""", "add"), + ("nested_pub_fn", """fn outer() -> int { + pub fn inner() -> int { return 1; } + return inner(); +} + +fn main() -> int { return outer(); } +""", "inner"), + ("nested_double", """fn outer() -> int { + fn mid() -> int { + fn deep() -> int { return 1; } + return deep(); + } + return mid(); +} + +fn main() -> int { return outer(); } +""", "mid"), + ("nested_then_stmt", """fn outer() -> int { + fn inner() -> int { return 1; } + x := 41; + return x + 1; +} + +fn main() -> int { return outer(); } +""", "inner"), + ] + for name, src, fn_name in variants: + checks += 1 + f = case_reject_nested(name, src, fn_name) + if f: + failures.append(f) + + # ④ 失步型 EOF 探针(修复前同族 139:自旋 + 分配 → OOM → NULL 拷贝) + desync = [ + ("eof_struct_literal_open", "fn main() -> int {\n x := Foo{\n"), + ("eof_struct_literal_tokens", "fn main() -> int {\n x := Foo{ a\n"), + ("eof_struct_decl_body", "struct S { a: int\n"), + ("eof_enum_decl_body", "enum E { A,\n"), + ("eof_impl_body", "struct S { a: int }\nimpl S {\n"), + ("eof_interface_body", "interface I {\n"), + ("eof_fn_param_list", "fn main() -> int {\n"), + ("eof_match_struct_pat", """fn main() -> int { + match 1 { + Foo { a +"""), + ] + for name, src in desync: + checks += 1 + f = case_reject_desync(name, src) + if f: + failures.append(f) + + # ⑤ 正控:同逻辑扁平形态必须 rc=0 且运行结果正确(防「把语言面内的东西一起禁掉」) + checks += 1 + brc, rrc, out = _build_run(FLAT_CONTROL) + if brc != 0 or rrc != 2: + failures.append( + f"FAIL flat_control: build_rc={brc} run_rc={rrc}(期望 0 / 2)\n--- output ---\n{out}" + ) + + total = checks - len(failures) + print(f"{total}/{checks} passed") + for f in failures: + print(f) + return 0 if not failures else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_optional.py b/tests/selfhost/test_optional.py new file mode 100644 index 00000000..a81d6079 --- /dev/null +++ b/tests/selfhost/test_optional.py @@ -0,0 +1,428 @@ +#!/usr/bin/env python3 +"""R2 P3 Task 4:联合/可选(`T?` = `T ∪ null`;退役内建 Option 注册)行为覆盖集。 + +判据(语义见 docs/superpowers/plans/2026-09-11-r2-p3-capabilities.md Task 4 + +docs/superpowers/specs/2026-09-10-type-interface-unification-design.md §5.4): + + 正(**解释器 + ELF 双路径同值**):`T ⊆ T?`(裸值 `return 5` 入 `int?` 返回位)/ + `null ⊆ T?`(`return None`)/ `Some(1)` 入 `int?` / `T?` 形参位 / `?` 解包回 T / + `Some(x)` 与 `None` 的 match 两臂(运行时 tag 分派) + 负:`T? ⊄ T`(`fn f(x: int?) -> int { return x; }` ⇒ TF01——**方向性**:可选不是 T 的子类型)/ + `Option` 名退役(`Option[int]` 作类型 = 未定义泛型应用 rc=1——退役前该名由内建注册) + 硬门:`T?` 的 match 缺臂 ⇒ TM03 rc=1 + 具体反例名('None' / 'Some')+ 无产物 + 登记:载荷**类型节点列**(T0 交接 ①):`--verify-evp-nodes` 断言每槽有节点(bad=0), + 并数出裸码列丢失真实类型的槽数(collapses>0 = 节点列携带的信息量;旧布局裸码把 + 非基型塌缩为 0 = TY_INT) + 表示面(R2 P4 Task 5,裁决 5「解包侧判表示」——本套件下半段):可选槽配**表示位** + (0 = 裸值 / 1 = 装箱,运行期值):写点置位、解包点(`match` 的 Some/None 臂、 + `?`)分派——裸值路径**值即载荷**(Some 臂直取、不读 tag;None 臂不命中),装箱 + 路径沿用既有 tag 分派 + 字段 fi+1。跨函数面(返回/形参)走隐藏全局信道(纯 IR)。 + **前置红态(B.7 一等条目)**:裸值 + `Some` 臂 = 双路径 SIGSEGV(check/build rc=0 + 零诊断)——修复前后 rc 逐形态见任务报告。 + 聚合面(**容量批 T2,裁-CAP-1 (a)「存储边界规范化」**):结构体字段 / 数组·切片元素 / + 元组元素 / 枚举载荷 / 全局槽的可选值在**写点装箱**(裸 T 值 ⇒ Some(v)),读取侧 + 零改动(既有装箱假定)。局部/形参/返回/调用结果仍走表示位机制(P4 T5,可持裸值)。 + **修复前**这四面的裸值形态是**响亮失败**(rc=139 双路径同,非静默);本批转为正确值 + (下方 `uncovered_*` 四例为**重钉**,死亡证据 = 本批能力变更)。**未覆盖面(登记)**: + 指针写 `IR_STORE_PTR`(同一站点服务局部与聚合 + 别名不可静态闭合)、元组/数组字面量 + 的非 ident/调用元素、泛型形参字段实例化为 `T?` 的形态。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="build", extra=None): + """返回 (result, out_path, src_path);cmd = build(ELF 产物)| check(仅前端)。""" + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + if extra: + args += list(extra) + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + """返回 (compile_result, run_result|None)。""" + r, out, src = _compile(source) + try: + if r.returncode != 0: + return r, None + rr = subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + return r, rr + finally: + _cleanup(src, out, out + ".ccr") + + +def run_interp(source: str): + """解释器路径(共享同一 run_frontend ⇒ 同一判定面);rc = main 返回值。""" + return subprocess.run( + [str(COREC), "run", source], capture_output=True, text=True, cwd=BASE, timeout=180, + ) + + +def case_dual(name, source, expect_rc): + """**双路径**(ELF 产物 + 解释器)同值断言——本任务主判据形。 + + ⚠️ 附加 `check` rc=0 断言(**必要**,实测教训):本仓库多数类型诊断(TF01/TA01…)是 + **软诊断**——`build` 路径 rc=0 + 产物照出,仅 `check` 因「有诊断即 rc=1」暴露。只断 + 「build+run rc」会让「判定面整体失效」的突变**照样全绿**(M1 实测:注入失效后 + plain_value_into_optional 仍 PASS)。故正例 = 三路同证:check rc=0 ∧ build/run rc=N ∧ interp rc=N。 + """ + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + ri = run_interp(source) + if ri.returncode != expect_rc: + print(f"[FAIL] {name}: interp expected rc {expect_rc}, got {ri.returncode}: {ri.stdout}{ri.stderr}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}") + return True + + +def case_dual_rc(name, source, expect_rc): + """双路径**同 rc**(不要求 check rc=0):用于**未覆盖面登记**——钉「未覆盖面要么正确、 + 要么响亮失败(elf rc == interp rc),**绝不静默分歧**」;rc=139 = 既有装箱假定下的 + 响亮失败(非本任务面,逐形态实测见任务报告 §未覆盖面)。""" + r, rr = build_and_run(source) + if rr is None: + print(f"[FAIL] {name}: compile rc={r.returncode}: {r.stdout}{r.stderr}") + return False + ri = run_interp(source) + if rr.returncode != expect_rc or ri.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc} both paths, got ELF={rr.returncode} interp={ri.returncode}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}(未覆盖面:双路径同,响亮)") + return True + + +def case_reject(name, source, needles, cmd="build"): + r, out, src = _compile(source, cmd) + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + if cmd == "build" and os.path.exists(out): + print(f"[FAIL] {name}: 拒绝编译却仍产出二进制 {out}") + return False + print(f"[PASS] {name}: rc={r.returncode} + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def case_evp(name, source, needles): + """载荷类型节点列**入库断言**(隐藏调试通道 `--verify-evp-nodes`;T0 交接 ① 的判据)。""" + r, out, src = _compile(source, cmd="check", extra=["--verify-evp-nodes"]) + try: + if r.returncode != 0: + print(f"[FAIL] {name}: verify rc={r.returncode}: {r.stdout}{r.stderr}") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: 缺 {n!r}: {text}") + return False + print(f"[PASS] {name}: rc=0 + {needles!r}") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +# ── 源形 ────────────────────────────────────────────────────────────── + +# 裸值入 T?(`int ⊆ int ∪ null`):判定点注入面(type_compat_strict 的可选目标分支) +PLAIN_INTO_OPT = """fn g() -> int? { return 5; } +fn main() -> int { return 42; } +""" +NONE_INTO_OPT = """fn g() -> int? { return None; } +fn main() -> int { return 42; } +""" +SOME_INTO_OPT = """fn g() -> int? { return Some(1); } +fn main() -> int { return 42; } +""" +# T? 形参位 + `?` 解包(EXPR_TRY 按类型项结构) +UNWRAP_SRC = """fn f(x: int?) -> int { return x?; } +fn main() -> int { return f(3); } +""" +# 运行时两形态:Some 值(枚举对象 + tag)与 None +SOME_FLOW = """fn g() -> int? { return Some(1); } +fn main() -> int { v := g(); return match v { Some(x) => x + 100, None => 0, }; } +""" +NONE_FLOW = """fn g() -> int? { return None; } +fn main() -> int { v := g(); return match v { Some(x) => x + 100, None => 7, }; } +""" + +# ── R2 P4 Task 5(裁决 5:解包侧判表示):B.7 四形态 + 跨函数面 ────────────── +# 语义口径:裸值入 `T?` = 该值本身(`T ⊆ T?`)⇒ Some 臂**命中**且载荷 = 裸值。 +BARE_SOME_ARM = """fn main() -> int { x : int? = 5; return match x { Some(v) => { return v; } None => { return 0; } }; } +""" +BARE_SOME_WILDCARD = """fn main() -> int { x : int? = 5; return match x { Some(v) => { return v; } _ => { return 6; } }; } +""" +BARE_MATCH_EXPR = """fn main() -> int { x : int? = 5; y := match x { Some(v) => v, None => 0, }; return y; } +""" +BARE_WILDCARD = """fn main() -> int { x : int? = 5; return match x { _ => { return 7; } }; } +""" +BARE_THEN_BOXED_WRITE = """fn main() -> int { x : int? = 5; x = Some(7); return match x { Some(v) => { return v; } None => { return 0; } }; } +""" +BOXED_THEN_BARE_WRITE = """fn main() -> int { x : int? = Some(7); x = 3; return match x { Some(v) => { return v; } None => { return 0; } }; } +""" +# 条件写:同一槽两条路径两种表示 ⇒ 表示位必须是**运行期值**(静态收窄无法覆盖) +COND_ON = """fn main() -> int { x : int? = 5; c := 1; if c > 0 { x = Some(4); } return match x { Some(v) => { return v; } None => { return 0; } }; } +""" +COND_OFF = """fn main() -> int { x : int? = 5; c := 0; if c > 0 { x = Some(4); } return match x { Some(v) => { return v; } None => { return 0; } }; } +""" +# 跨函数面(返回信道 / 形参信道) +BARE_RET_SOME_ARM = """fn g() -> int? { return 5; } +fn main() -> int { v := g(); return match v { Some(x) => { return x; } None => { return 0; } }; } +""" +BOXED_RET_SOME_ARM = """fn g() -> int? { return Some(9); } +fn main() -> int { v := g(); return match v { Some(x) => { return x; } None => { return 0; } }; } +""" +BARE_RET_WILDCARD = """fn g() -> int? { return 5; } +fn main() -> int { v := g(); return match v { _ => { return 11; } }; } +""" +BARE_PARAM_SOME_ARM = """fn f(x: int?) -> int { return match x { Some(v) => { return v; } None => { return 0; } }; } +fn main() -> int { return f(5); } +""" +BOXED_PARAM_SOME_ARM = """fn f(x: int?) -> int { return match x { Some(v) => { return v; } None => { return 0; } }; } +fn main() -> int { return f(Some(6)); } +""" +BARE_PARAM_TRY = """fn f(x: int?) -> int { return x?; } +fn main() -> int { return f(3); } +""" +# `?` 解包装箱值:修复前 ELF 返回指针、解释器返回内部地址(**双路径分歧的静默错值**) +BOXED_PARAM_TRY = """fn g() -> int? { return Some(7); } +fn f(x: int?) -> int { return x?; } +fn main() -> int { return f(g()); } +""" +# 混合返回(同函数两条 return 两种表示;表示位运行期分派 ⇒ 两条路径各自正确) +MIXED_RET_BARE = """fn g(c: int) -> int? { if c > 0 { return 5; } return Some(4); } +fn main() -> int { v := g(1); return match v { Some(x) => { return x; } None => { return 0; } }; } +""" +MIXED_RET_BOXED = """fn g(c: int) -> int? { if c > 0 { return 5; } return Some(4); } +fn main() -> int { v := g(0); return match v { Some(x) => { return x; } None => { return 0; } }; } +""" +# 装箱值 + 通配臂(既有语义保持) +BOXED_WILDCARD = """fn main() -> int { x : int? = Some(9); return match x { _ => { return 12; } }; } +""" +# 未覆盖面(登记,非本任务面):结构体字段裸值 + Some 臂 ⇒ 既有装箱假定 ⇒ 响亮失败 +# (rc=139 双路径**同**:钉「未覆盖面不静默分歧」,不钉错误值本身) +UNCOVERED_FIELD_BARE = """struct S { a: int? } +fn main() -> int { s : ., mut = S { a: 5 }; return match s.a { Some(v) => { return v; } None => { return 0; } }; } +""" +# 未覆盖面之装箱形态:照常正确(登记双面) +UNCOVERED_FIELD_BOXED = """struct S { a: int? } +fn main() -> int { s : ., mut = S { a: Some(5) }; return match s.a { Some(v) => { return v; } None => { return 0; } }; } +""" +# 全局槽(无表示位 = 未覆盖面,走既有装箱假定):装箱形态照常正确(**前提 = tag 读取 +# 源支持全局行**——修复前 ELF 读帧外伪偏移 ⇒ 静默走空臂返回 0,与解释器 5 分歧) +UNCOVERED_GLOBAL_BOXED = """g : int? = Some(5); +fn main() -> int { return match g { Some(v) => { return v; } None => { return 0; } }; } +""" +UNCOVERED_GLOBAL_NONE = """g : int? = None; +fn main() -> int { return match g { Some(v) => { return v; } None => { return 9; } }; } +""" + +# ── 容量批 T2:聚合面存储边界规范化(裁-CAP-1 (a))——A 类 8 写点逐点覆盖 ────────── +# 每例 = 裸值写入可选聚合槽后解包(修复前 139;现须给精确值);装箱/None 形为对照。 +CAP_FIELD_ASSIGN = """struct S { a: int? } +fn main() -> int { s : ., mut = S { a: Some(0) }; s.a = 5; return match s.a { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_FIELD_LITERAL = """struct S { a: int? } +fn main() -> int { s : ., mut = S { a: 6 }; return match s.a { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_FIELD_NONE = """struct S { a: int? } +fn main() -> int { s : ., mut = S { a: None }; return match s.a { Some(v) => { return v; } None => { return 3; } }; } +""" +CAP_ARR_ASSIGN_LIT = """fn main() -> int { a : [int?;2] = [Some(0), None]; a[0] = 7; return match a[0] { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_ARR_ASSIGN_DYN = """fn main() -> int { a : [int?;2] = [Some(1), None]; i := 1; a[i] = 8; return match a[i] { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_ARR_LITERAL_IDENT = """fn main() -> int { x : int? = 5; a : [int?;2] = [x, None]; return match a[0] { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_ARR_BOXED_LITERAL = """fn main() -> int { a : [int?;2] = [Some(4), None]; return match a[0] { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_GLOBAL_ASSIGN = """g : int? = None; +fn main() -> int { g = 9; return match g { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_GLOBAL_INIT_BARE = """g : int? = 10; +fn main() -> int { return match g { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_MATCHRES_COND = """fn f(c: int) -> int? { return match c { 0 => 11, _ => None, }; } +fn main() -> int { v := f(0); return match v { Some(x) => { return x; } None => { return 0; } }; } +""" +CAP_ENUM_PAYLOAD = """enum E { V(int?), W } +fn main() -> int { e := V(12); return match e { V(x) => { return match x { Some(v) => { return v; } None => { return 0; } }; } W => { return 1; } }; } +""" +CAP_TUPLE_ELEM = """fn main() -> int { x : int? = 13; t := (x, 3); return match t . 0 { Some(v) => { return v; } None => { return 0; } }; } +""" +CAP_FIELD_BARE_CROSSFN = """struct S { a: int? } +fn f(s: S) -> int { return match s.a { Some(v) => { return v; } None => { return 0; } }; } +fn main() -> int { s : ., mut = S { a: Some(0) }; s.a = 14; return f(s); } +""" +# 负控:非可选聚合槽不受本律影响(值形态不变) +CAP_NONOPT_FIELD = """struct S { a: int } +fn main() -> int { s : ., mut = S { a: 15 }; s.a = 16; return s.a; } +""" + + +def main(): + ok = [ + # 正:裸值入 T? 返回位(旧判定:TF01 拒绝——joint 语义落地后放行;双路径) + case_dual("plain_value_into_optional", PLAIN_INTO_OPT, 42), + # 正:None 入 T? 返回位(`null ⊆ T?`;双路径) + case_dual("none_into_optional", NONE_INTO_OPT, 42), + # 正:Some(1) 入 T?(类型项相等;双路径) + case_dual("some_into_optional", SOME_INTO_OPT, 42), + # 正:T? 形参位 + `?` 解包回 T(双路径) + case_dual("unwrap_optional_in_param", UNWRAP_SRC, 3), + # 正:Some 值流经 match 的 Some 臂(运行时 tag 分派;双路径) + case_dual("some_flow_some_arm", SOME_FLOW, 101), + # 正:None 值流经 match 的 None 臂(双路径) + case_dual("none_flow_none_arm", NONE_FLOW, 7), + # 负:T? ⊄ T(方向性——可选**不是**裸类型的子类型;钉死 soundness 面)。 + # 口径:TF01 是**软诊断**(本仓库硬门名单只含 R002/TK05-06/TS01-04/TK02/TM03, + # build 路径 rc=0 + 产物照出)⇒ 判据走 `check`(有诊断即 rc=1)+ 措辞断言。 + case_reject("optional_not_assignable_to_plain", + "fn f(x: int?) -> int { return x; }\nfn main() -> int { return f(1); }\n", + ["error[TF01]"], cmd="check"), + # 硬门:T? 的 match 缺 None 臂 ⇒ TM03 + 具体反例名('None')+ 无产物 + case_reject("optional_match_missing_none", + "fn main() -> int { x: int? = Some(3); return match x { Some(v) => v, }; }\n", + ["error[TM03]", "missing variant 'None'"]), + # 硬门:T? 的 match 缺 Some 臂 ⇒ TM03 + 'Some' + case_reject("optional_match_missing_some", + "fn main() -> int { x: int? = Some(3); return match x { None => 0, }; }\n", + ["error[TM03]", "missing variant 'Some'"]), + # 正:通配臂吸收 ⇒ 穷尽(零诊断) + case_dual("optional_match_wildcard", + "fn main() -> int { x: int? = None; return match x { _ => 9, }; }\n", 9), + # 负:退役内建 Option 注册——`Option` 不再是语言内建类型名。 + # 旧行为:collect_decls 自动注册命名行 ⇒ `Option[int]` 零诊断通过;新行为:该名未声明 + # ⇒ `Undefined type in generic application`(EC_N_GENERIC_TYPE,**软诊断**:rc 面 + # build 仍 0——故判据走 `check` 的 rc=1 + 措辞)。用户自声明 `enum Option` 时按普通 + # 枚举解析(Task 3 的 p20 语义保持,见 test_match_exhaust.py)。 + case_reject("builtin_option_name_retired", + "fn f(x: Option[int]) -> int { return 0; }\nfn main() -> int { return f(0); }\n", + ["Undefined type in generic application"], cmd="check"), + # 登记:载荷类型节点列入库(每槽有节点 = bad=0)+ 裸码列丢失真实类型的信息量 + # (`V(S)` 命名类型载荷与 `Some(T)` 泛型形参载荷在裸码列都塌缩为 0 = TY_INT) + case_evp("payload_type_nodes_recorded", + "struct S { a: int }\nenum E { V(S), W(int) }\nenum Opt[T] { N, Some(T) }\n" + "fn main() -> int { return 0; }\n", + ["[enum-payload-verify]", "slots=3", "collapses=2", "bad=0"]), + + # ── R2 P4 Task 5:解包侧判表示(B.7 四形态;修复前多个形态双路径 139)── + # 裸值 + Some 臂(B.7 最小复现):裸值即载荷 ⇒ 5 + case_dual("bare_value_some_arm", BARE_SOME_ARM, 5), + # 裸值 + Some/通配混合臂(B.7 边界形态) + case_dual("bare_value_some_wildcard", BARE_SOME_WILDCARD, 5), + # match 作表达式取值(B.7 边界形态) + case_dual("bare_value_match_expr", BARE_MATCH_EXPR, 5), + # 纯通配臂(B.7 边界形态;修复前即不崩——**不得回退**) + case_dual("bare_value_wildcard", BARE_WILDCARD, 7), + # 写点置位的运行期性:裸→装箱、装箱→裸,最后一次写点决定表示 + case_dual("bare_then_boxed_write", BARE_THEN_BOXED_WRITE, 7), + case_dual("boxed_then_bare_write", BOXED_THEN_BARE_WRITE, 3), + # 条件写:两路径两种表示 ⇒ 表示位必须是运行期值(两臂各自正确) + case_dual("conditional_write_boxed_taken", COND_ON, 4), + case_dual("conditional_write_bare_taken", COND_OFF, 5), + # ── 跨函数面(返回信道 / 形参信道)── + case_dual("bare_return_some_arm", BARE_RET_SOME_ARM, 5), + case_dual("boxed_return_some_arm", BOXED_RET_SOME_ARM, 9), + case_dual("bare_return_wildcard", BARE_RET_WILDCARD, 11), + case_dual("bare_param_some_arm", BARE_PARAM_SOME_ARM, 5), + case_dual("boxed_param_some_arm", BOXED_PARAM_SOME_ARM, 6), + case_dual("bare_param_try", BARE_PARAM_TRY, 3), + # `?` 解包装箱值:修复前 ELF/解释器**各错各的**(指针 vs 内部地址 = 双路径分歧) + case_dual("boxed_param_try", BOXED_PARAM_TRY, 7), + # 混合返回:同函数两条 return 两种表示 + case_dual("mixed_return_bare_taken", MIXED_RET_BARE, 5), + case_dual("mixed_return_boxed_taken", MIXED_RET_BOXED, 4), + # 装箱值 + 通配臂(既有语义保持) + case_dual("boxed_value_wildcard", BOXED_WILDCARD, 12), + # ── 原未覆盖面(**容量批 T2 重钉**:裸值形态已由写点装箱闭合)── + # 死亡证据 = 本批能力变更:修复前 `uncovered_field_bare_is_loud` 期望 -11(响亮 + # 失败),本批后为正确值 5 ⇒ 改判 `case_dual` 断言精确值(**禁令**:只断 rc 会 + # 让「静默错值」类突变假绿——突变 M4 即此形态)。 + case_dual("uncovered_field_bare_now_ok", UNCOVERED_FIELD_BARE, 5), + case_dual("uncovered_field_boxed_ok", UNCOVERED_FIELD_BOXED, 5), + # 全局槽:装箱/None 形态照常正确(钉 tag 读取源的全局行分派) + case_dual("uncovered_global_boxed_ok", UNCOVERED_GLOBAL_BOXED, 5), + case_dual("uncovered_global_none_ok", UNCOVERED_GLOBAL_NONE, 9), + + # ── 容量批 T2:聚合面存储边界规范化(裁-CAP-1 (a);A 类 8 写点逐点)── + # 字段:赋值点 / 字面量点(**两个独立写点**,突变 M1 专测其可判别性) + case_dual("cap_field_bare_assign", CAP_FIELD_ASSIGN, 5), + case_dual("cap_field_bare_literal", CAP_FIELD_LITERAL, 6), + case_dual("cap_field_none", CAP_FIELD_NONE, 3), + case_dual("cap_field_bare_crossfn", CAP_FIELD_BARE_CROSSFN, 14), + # 数组元素:字面量下标 / 动态下标 / 字面量元素(ident)/ 装箱对照 + case_dual("cap_array_bare_assign_lit_idx", CAP_ARR_ASSIGN_LIT, 7), + case_dual("cap_array_bare_assign_dyn_idx", CAP_ARR_ASSIGN_DYN, 8), + case_dual("cap_array_literal_ident_elem", CAP_ARR_LITERAL_IDENT, 5), + case_dual("cap_array_boxed_literal", CAP_ARR_BOXED_LITERAL, 4), + # 全局槽:赋值点 / **初值点**(两个独立代码点;初值点另涉常量折叠抑制——见实现注) + case_dual("cap_global_bare_assign", CAP_GLOBAL_ASSIGN, 9), + case_dual("cap_global_bare_init", CAP_GLOBAL_INIT_BARE, 10), + # match 结果(裁决 (i) 表示位案):条件写两表示共存 + case_dual("cap_matchres_cond_bare_arm", CAP_MATCHRES_COND, 11), + # 枚举载荷(B 类 #10 实证可达 ⇒ 升 A) + case_dual("cap_enum_payload_bare", CAP_ENUM_PAYLOAD, 12), + # 元组元素(B 类 #11 实证可达 ⇒ 升 A;读语法须带空格 `t . 0`) + case_dual("cap_tuple_elem_bare", CAP_TUPLE_ELEM, 13), + # 负控:非可选聚合槽不受本律影响 + case_dual("cap_nonopt_field_unaffected", CAP_NONOPT_FIELD, 16), + ] + passed = sum(1 for x in ok if x is True) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_params_limit.py b/tests/selfhost/test_params_limit.py new file mode 100644 index 00000000..6a9c9513 --- /dev/null +++ b/tests/selfhost/test_params_limit.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""TODO #8 回归:≥18 形参静默误编译 + FuncInfo 形参槽区上界(2026-09-11 修复)。 + +背景(RED,修复前实测):FuncInfo.param_types 是定长内嵌槽区(原 16 槽 = +OFF_FI_PARAM_TYPES 16 → 16+16×8 = 144 = OFF_FI_RETURN_TYPE),而 parser 的 +「Store param types」写入无界 ⇒ + * 第 17 个形参写 OFF_FI_RETURN_TYPE(144):形参类型值恰为 TY_INT=0 时「误 + 打正着」(这正是 N=17 求和探针当时通过的原因);返回类型非 int 的函数 + (如 -> bool,TY_BOOL=2)则被改写为 0 = TY_INT → 假 TF01 + 返回类型元数据错。 + * 第 18 个形参写 OFF_FI_AST_NODE(152):TY_INT=0 把 ast_node 写成节点表 + **首项**(实测 = rt.cr:4:13 `g_rt_argc : int, mut;` 的 int 类型节点)⇒ + checker 读 fi_ast_node=0 → body=0 节点 data ⇒ + - `error[TF01] Function return type mismatch` 误归到 rt.cr:4:13; + - .ccr 中该函数 name_idx=0(= 字符串表首项)/param_count=0、IR 体丢失 + (46 → 28 instrs); + - `corec build` 仍 rc=0,产物 SIGSEGV(实测 rc=139)。 +修复:槽区扩至 MAX_FN_PARAMS=64(与 ast.cr 的 FuncInfo 镜像 [int; 64] 对齐, +并覆盖 ≥22 = 6 寄存器参 + 16 栈参的 >127B 栈清理形)+ 读写访问器护栏 + +超限签名硬错 P020(rc=1,绝不 rc=0)。 + +判据:N ∈ {17,18,22,64} 逐形参值全对;N=65 编译拒绝(rc=1 + error[P20], +且不产出二进制)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 退出码 = main 返回值低 8 位(见 test_slice_bounds.py 头注);故求和形一律 < 256。 + + +def _no_core_dump(): + # core_pattern 为 systemd-coredump 管道时,崩溃的陷阱程序会挂起——禁用 core dump。 + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str): + """返回 (build_result, bin_path);build_result 含 returncode/stdout/stderr。""" + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + built = subprocess.run( + [str(COREC), "build", src, "-o", out, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + return built, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def build_and_run(source: str): + built, out, src = _compile(source) + try: + if built.returncode != 0: + return f"compile-failed(rc={built.returncode}): {built.stdout}{built.stderr}" + return subprocess.run( + [out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump, + ) + finally: + _cleanup(src, out, out + ".ccr") + + +def case_run(name, source, expect_rc): + r = build_and_run(source) + if isinstance(r, str): + print(f"[FAIL] {name}: {r}") + return False + if r.returncode != expect_rc: + print(f"[FAIL] {name}: expected rc {expect_rc}, got {r.returncode}") + return False + print(f"[PASS] {name}: rc={r.returncode}") + return True + + +def case_reject(name, source, needle): + built, out, src = _compile(source) + try: + if built.returncode == 0: + print(f"[FAIL] {name}: expected compile rejection, got rc=0(静默误编译面复活)") + return False + if needle not in (built.stdout + built.stderr): + print(f"[FAIL] {name}: rc={built.returncode} 但缺诊断 {needle!r}: {built.stdout}{built.stderr}") + return False + if os.path.exists(out): + print(f"[FAIL] {name}: 拒绝编译却仍产出二进制 {out}") + return False + print(f"[PASS] {name}: rc={built.returncode} + {needle!r} + 无产物") + return True + finally: + _cleanup(src, out, out + ".ccr") + + +def sum_src(n): + """求和形(值 = 1..n 之和,必须 < 256 才可与退出码比较)。""" + ps = ", ".join(f"a{i}: int" for i in range(n)) + args = ", ".join(str(i + 1) for i in range(n)) + body = " + ".join(f"a{i}" for i in range(n)) + return f"fn f({ps}) -> int {{\n return {body};\n}}\nfn main() -> int {{ return f({args}); }}\n" + + +def per_arg_src(n): + """逐形参值断言形(求和会掩盖错位/串位;任一错位返回该参数序号)。""" + ps = ", ".join(f"a{i}: int" for i in range(n)) + args = ", ".join(str(i + 1) for i in range(n)) + checks = "\n".join(f" if a{i} != {i + 1} {{ return {i + 1}; }}" for i in range(n)) + return f"fn f({ps}) -> int {{\n{checks}\n return 0;\n}}\nfn main() -> int {{ return f({args}); }}\n" + + +def main(): + ok = [ + # 修复前 N=17 求和「碰巧通过」(int→int 时第 17 槽写入 TY_INT=0 = 原值) + case_run("n17_args_pass", sum_src(17), 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17), + # 修复前 N=18 = 静默误编译(TF01 误归 + rc=0 + SIGSEGV 139) + case_run("n18_args_pass_issue8", sum_src(18), sum(range(1, 19))), + # 22 = 6 寄存器参 + 16 栈参(128B 栈清理,>127B 形;波 1 Task 5 Minor 4 收口条件) + case_run("n22_stack_arg_form", per_arg_src(22), 0), + # 上界内最大值(MAX_FN_PARAMS) + case_run("n64_upper_bound", per_arg_src(64), 0), + # 上界外:硬错 rc=1 + 诊断,绝不静默 rc=0 + case_reject("n65_rejected", per_arg_src(65), "too many parameters"), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_purity.py b/tests/selfhost/test_purity.py new file mode 100644 index 00000000..412a519e --- /dev/null +++ b/tests/selfhost/test_purity.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""效应/纯度修正 Task 1 判据:真纯度计算自测通道(corec selftest-purity)。 + +语义 = plans/2026-08-08-region-cfg.md:484(非纯调用才进 state 链;find_func +不可得保守进链)+ 本批 plan Task 1(docs/superpowers/plans/2026-09-11-effect-purity-fix.md): +纯 ⟺ 体无 store 族 / 无 IO·FFI·并发效应 opcode / 传递闭包内被调者全纯(不可解析 +不纯)/ 递归·SCC 保守不纯;泛型实例与源同值。 + +判据 = 用例表全 PASS + 零 FAIL + 计数行 + rc=0。用例表本身在 +src/compiler/purity_selftest.cr(正控 2 + 负控①-⑥ 含 SCC 互递归与有效应泛型)。 +""" + +import re +import subprocess +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 用例数下限:本批用例表 = 26 例=纯度 13(正控2 + ①store + ②传递 + ③extern + +# ④不可解析 + ⑤自递归 + ⑤b互递归x2 + ⑥泛型实例x2 + ⑥b有效应泛型x2)+ state 链 +# 4(效应调用被链穿过 / 纯调用不被触及 / 循环终止依赖 / 无循环无 label 边负控) +# + Task 2 分类表补全 9(验收例双调 x3 + extern x2 + spawn x2 + yield x2)。 +# 低于此数 = 用例被静默删减(判据空转),故设下限而非仅断言相等。 +# 注:Task 2 把用例从 17 加到 26 时未同步该下限(终审 Minor #3——守卫一度允许删 9 例); +# 此处随用例表钉死,后续再增删用例须同改此数。 +MIN_CASES = 26 + + +def run_selftest(): + return subprocess.run( + ["nice", "-n", "19", str(COREC), "selftest-purity"], + cwd=BASE, capture_output=True, text=True, + ) + + +def test_selftest_purity_all_pass(): + r = run_selftest() + out = r.stdout + r.stderr + assert "FAIL" not in out, out + m = re.search(r"(\d+)/(\d+) purity cases passed", out) + assert m, out + passed, total = int(m.group(1)), int(m.group(2)) + assert passed == total and total >= MIN_CASES, (passed, total, out) + assert r.returncode == 0, (r.returncode, out) + + +if __name__ == "__main__": + test_selftest_purity_all_pass() + print("PASS test_selftest_purity_all_pass") diff --git a/tests/selfhost/test_region_cfg.py b/tests/selfhost/test_region_cfg.py index 165b2959..642b02b2 100644 --- a/tests/selfhost/test_region_cfg.py +++ b/tests/selfhost/test_region_cfg.py @@ -227,11 +227,17 @@ def test_nested_loop_run(): capture_output=True, text=True, cwd=BASE, timeout=30) assert r.returncode == 9, f"nested loop expected 9 (3x3), got exit={r.returncode} stdout={r.stdout!r} stderr={r.stderr!r}" -# --- Serialization v2 (.ccr SG section + edge kind) --- +# --- v7 serialization (.ccr segment-table layout + REG segment; Task 1 机械更新) --- def ccr_walk(path: str): - """Walk the .ccr binary layout (mirrors load_ccr reading order) and return - (version, sg_count, file_size, end_pos). Raises if the layout is invalid.""" + """Walk the v7 .ccr binary layout (mirrors load_ccr reading order) and return + (version, sg_count, file_size, end_pos). Raises if the layout is invalid. + + v7 (Task 1 机械更新): Header 16B + 段表 6×12B {tag, offset, size}(规范序 + tag 1..6)+ 段体 STR/SYM/NOD/ENT/REG/EDG。NOD(tag 3)= 40B×nod_count(32B + 语义字段 + first_edge/edge_count 邻接)、EDG(tag 6)= 8B×edg_count 必落。 + REG(tag 5)= 24B×sg_count(坐标化字段序 kind/parent/enter/exit/ + first_ent/last_ent,记录宽不变)。见 ccr_io.cr 头注释(v7 布局权威)。""" with open(path, 'rb') as fh: d = fh.read() pos = 0 @@ -243,42 +249,56 @@ def u32(): assert struct.unpack_from('= 2: - gc = u32() - pos += gc * 16 - if ver >= 3: - mc = u32() - for _ in range(mc): - u32(); dl = u32() - pos += dl - if ver >= 5: - sg_count = u32() - pos += sg_count * 24 - return ver, sg_count, len(d), pos - -def test_ccr_v2_sg_section(): - """.ccr 序列化 v2:version==5,文件尾追加 SG 段(func+for 两个 region)""" + def body(tag): + off, size = segs[tag] + return d[off:off + size] + # STR (tag 1): str_count + strings + b = body(1) + (n,) = struct.unpack_from('= 2, \ - f"expected SG section with >=2 regions (func+for), got {sg_count}" + f"expected REG segment with >=2 regions (func+for), got {sg_count}" assert end == fsize, f"format walk ended at {end} of {fsize} bytes" @@ -311,7 +331,7 @@ def test_ccr_writer_rejects_i32_overflow_inputs(): assert "if ccr_validate_i32_fields() == 0 { return -1; }" in text def test_ccr_roundtrip_v2(): - """save→load 往返守卫:v2 文件经 corearch 加载后 ELF 输出行为不变。 + """save→load 往返守卫:v6 文件经 corearch 加载后 ELF 输出行为不变。 程序计算 0+1+2+3=6,ELF 运行时以 main 返回值为退出码。""" src = "fn main() -> int {\n s : ., mut = 0;\n for i in 0..4 { s = s + i; }\n return s;\n}\n" with tempfile.NamedTemporaryFile('w', suffix='.cr', delete=False) as f: @@ -432,7 +452,7 @@ def test_state_edges_cache_persist(): test_inline_callee_while_run, test_inline_callee_for_run, test_break_continue_run, test_nested_loop_run, - test_ccr_v2_sg_section, test_ccr_writer_rejects_i32_overflow_inputs, + test_ccr_v6_reg_section, test_ccr_writer_rejects_i32_overflow_inputs, test_ccr_roundtrip_v2, test_region_check_pointer_escape, test_region_check_cache_hit, test_nested_regions_cache_persist, test_state_edges_cache_persist] diff --git a/tests/selfhost/test_tc02_branch.py b/tests/selfhost/test_tc02_branch.py new file mode 100644 index 00000000..165116a6 --- /dev/null +++ b/tests/selfhost/test_tc02_branch.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +"""TC02 收口:`if` 分支相容判定的**发散豁免**(P3,不对称)。 + +背景(归因链 = `/tmp/fct1/tc02_attribution.md` / 设计 = `/tmp/fct2/tc02_minibatch.md`): +`EXPR_RETURN` 的推断 = **所返回值类型**(checker.cr 的 EXPR_RETURN 分支),故 `{ return 1; }` +的块类型 = int;而 `if` 的值类型定义为 **then_ti**(infer_expr 的 EXPR_IF 合并点 `return then_ti`)。 +⇒ 当 **else 支确定发散**(return 族收尾、不产出值)时,else 侧的「类型」是幻影 ⇒ 相容判定无对象。 + +判据(P3 = 不对称): + · else 支发散 ⇒ 不报(本批新增豁免;谓词 `stmt_diverges`,**只服务本判定点**) + · **then 支发散 ⇒ 仍报 TC02**(then_ti 本身即幻影 = 模型面,保留真信号;见 tc02_then_return_kept) + · 两侧皆不发散的真异型 ⇒ 仍报(tc02_true_mismatch_kept) + · 既有 NEVER 豁免(loop{} 收尾)与 TF01/TA02 面**零扰动**(各配钉子) + · TB01 真错(非级联)⇒ 仍报(负控;级联面见 TB01 归因报告 §2) + +用例只增不减(本文件 15 例 = 设计稿 §3 全表)。 +""" +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + import resource + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def run_corec(args, timeout=120): + return subprocess.run([str(COREC)] + args, cwd=BASE, capture_output=True, + text=True, timeout=timeout, preexec_fn=_no_core_dump) + + +def check_src(src: str): + """返回 (rc, output)。""" + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(src) + p = f.name + try: + r = run_corec(["check", p]) + return r.returncode, r.stdout + r.stderr + finally: + os.unlink(p) + + +# ── 正控:期望**无诊断**(rc=0) ────────────────────────────────────── +CASES_OK = [ + ("tc02_else_return_silenced", + "fn main() -> int {\n if 1 < 2 { } else { return 1; }\n return 0;\n}\n"), + ("tc02_else_return_after_stmt", + "fn main() -> int {\n if 1 < 2 { } else { x := 1; return x; }\n return 0;\n}\n"), + ("tc02_loop_control_unchanged", + "fn main() -> int {\n if 1 < 2 { } else { loop { } }\n return 0;\n}\n"), + ("tc02_bare_return_unchanged", + "fn main() -> int {\n if 1 < 2 { } else { return; }\n return 0;\n}\n"), + ("tf01_fallthrough_still_exempt", + "fn f() -> int { loop { } }\nfn main() -> int { return f(); }\n"), + ("tc02_stmt_position_multi", + "fn main() -> int {\n" + + "".join(f" if 1.5 < 2.5 {{ }} else {{ return {i}; }}\n" for i in range(1, 7)) + + " return 0;\n}\n"), +] + +# ── 负控/钉子:期望**仍报**(码 + rc=1) ───────────────────────────── +CASES_DIAG = [ + ("tc02_then_return_kept", + "fn main() -> int {\n if 1 < 2 { return 1; } else { }\n return 0;\n}\n", ["TC02"]), + ("tc02_true_mismatch_kept", + "fn main() -> int {\n if 1 < 2 { 1 } else { \"s\" }\n return 0;\n}\n", ["TC02"]), + ("tf01_still_reported", + "fn f() -> int { return \"s\"; }\nfn main() -> int { return f(); }\n", ["TF01"]), + ("ta02_still_reported", + "fn main() -> int {\n x : int = \"s\";\n return 0;\n}\n", ["TA02"]), + ("ta02_never_exempt_still", + "fn main() -> int {\n x : int = nosuchname;\n return 0;\n}\n", ["N01"]), + ("tb01_negative_ptr_add", + "fn main() -> int {\n x : ., mut = 1;\n p := &x;\n q := &x;\n return p + q;\n}\n", ["TB01"]), + ("tb01_negative_str_sub", + "fn main() -> int {\n return \"a\" - \"b\";\n}\n", ["TB01"]), +] + +# ── 端到端:期望 build 成功 + 运行 rc=0(= test_native_float 两源同形) ── +CASES_RUN = [ + ("tc02_end2end_cmp", + "fn main() -> int {\n" + " if 1.5 < 2.5 { } else { return 1; }\n" + " if 2.5 > 1.5 { } else { return 2; }\n" + " if 1.5 <= 1.5 { } else { return 3; }\n" + " return 0;\n}\n"), + ("tc02_end2end_arith", + "fn main() -> int {\n" + " x := 1.5;\n" + " y := 2.5;\n" + " if x + y == 4.0 { } else { return 1; }\n" + " if x * y == 3.75 { } else { return 2; }\n" + " return 0;\n}\n"), +] + + +def main() -> int: + if not COREC.exists(): + print("build/corec is missing; run `python3 build_selfhost_native.py` first") + return 1 + npass = nfail = 0 + + for name, src in CASES_OK: + rc, out = check_src(src) + if rc == 0: + print(f"[PASS] {name}: rc=0") + npass += 1 + else: + print(f"[FAIL] {name}: expected rc=0, got {rc}\n{out}") + nfail += 1 + + for name, src, codes in CASES_DIAG: + rc, out = check_src(src) + hit = [c for c in codes if f"error[{c}]" in out] + if rc == 1 and len(hit) == len(codes): + print(f"[PASS] {name}: rc=1 + {codes}") + npass += 1 + else: + print(f"[FAIL] {name}: expected rc=1 + {codes}, got rc={rc} hit={hit}\n{out}") + nfail += 1 + + for name, src in CASES_RUN: + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(src) + p = f.name + b = p[:-3] + try: + r = run_corec(["build", p, "-o", b, "--static"]) + if r.returncode != 0: + print(f"[FAIL] {name}: build rc={r.returncode}\n{r.stdout}\n{r.stderr}") + nfail += 1 + continue + os.chmod(b, 0o755) + rr = subprocess.run([b], capture_output=True, text=True, + preexec_fn=_no_core_dump, timeout=30) + if rr.returncode == 0: + print(f"[PASS] {name}: build+run rc=0") + npass += 1 + else: + print(f"[FAIL] {name}: run rc={rr.returncode}") + nfail += 1 + finally: + for art in (p, b, Path(str(b) + ".ccr")): + try: + os.unlink(art) + except FileNotFoundError: + pass + + print(f"{npass}/{npass + nfail} passed") + return 0 if nfail == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_tf01_fallthrough.py b/tests/selfhost/test_tf01_fallthrough.py new file mode 100644 index 00000000..1ab12d34 --- /dev/null +++ b/tests/selfhost/test_tf01_fallthrough.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +"""TF01 收口回归:函数体落空分析 + lits_copy 返回型(2026-09-13 修复)。 + +背景(RED,修复前实测;`check src/compiler` 长期恰 2 条 error[TF01],被当作 +「既有 red 划界」携带多批,同时卡住 check 作业/失败关闭工作流/P4 Task 2 的 +corearch 清单): + + ① 误报面(checker 落空分析缺失):`fn f() -> int { loop { return 5; } }` + ⇒ error[TF01]。根因 = 函数体返回检查取「块类型 = 末语句类型」,而 EXPR_LOOP + 推断恒 unit;但**无 break 的 loop 收尾**的体运行时永不走到函数尾(只从体内 + return 出),该 unit 不是缺返回值的证据。自源实测:`ty_memo_slot_no_grow`。 + ② 真错面(类型洗白,非误报):`fn f() -> int { b := alloc(8); return b; }` + ⇒ error[TF01]。类型模型里 `alloc` = `() -> string`(checker.cr + `bi_add("alloc", TI_STR)`),声明 int 是真·类型洗白。自源实测:`lits_copy`。 + 修法 = 声明改 string(连同消费面 `lits_contradictory_buf` 形参), + **不是**放宽模型——本文件负控钉住该形态仍须拒。 + +修复: + * checker.cr `stmt_cannot_fall_through`/`loop_body_has_break`/`seg_has_break`: + 落空分析(保守:只认确定**不产出值**的不可落空形态——无直系 break 的 loop / + 双分支皆不可落空且无 else 的 if / STMT·UNSAFE·嵌套 block 包裹;其余回「可落空」。 + `return` 明确**不判** divergence:它带返回值,其值类型正是站点 5 的被检对象, + 豁免它 = 洗白 lits_copy 型真错面。break 扫描在嵌套循环处截断(break 归内层), + **未知形态按「可能有 break」处理 = fail-closed**,绝不放过真落空体)。 + * type_engine.cr `lits_copy` 返回型 int → string;`lits_contradictory_buf` + 形参 buf int → string(两个调用点传的都是 string:g_ty_lits / lits_copy)。 + +判据:正控(确定不可走完的体接受 rc=0 无 TF01)+ 负控(可落空的体仍拒 +rc=1 恰 1 条 TF01)+ 端到端(接受面编译运行语义不变 rc=42)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def _no_core_dump(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _write(source): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + return src + + +def _check(source): + """返回 (rc, stdout+stderr)。""" + src = _write(source) + try: + r = subprocess.run( + [str(COREC), "check", src], + capture_output=True, text=True, cwd=BASE, timeout=120, + ) + return r.returncode, r.stdout + r.stderr + finally: + try: + os.unlink(src) + except FileNotFoundError: + pass + + +def case_accept(name, source): + rc, out = _check(source) + tf01 = out.count("error[TF01]") + if rc != 0 or tf01 != 0: + print(f"[FAIL] {name}: 期望接受(rc=0 无 TF01),实测 rc={rc} TF01×{tf01}\n{out}") + return False + print(f"[PASS] {name}: rc=0 无 TF01") + return True + + +def case_reject(name, source): + rc, out = _check(source) + tf01 = out.count("error[TF01]") + if rc == 0 or tf01 != 1: + print(f"[FAIL] {name}: 期望恰 1 条 TF01 拒绝,实测 rc={rc} TF01×{tf01}\n{out}") + return False + print(f"[PASS] {name}: rc={rc} TF01×1") + return True + + +def case_run(name, source, expect_rc): + """端到端:接受面编译 + 运行,退出码 = main 返回值低 8 位。""" + src = _write(source) + out_path = src[:-3] + try: + built = subprocess.run( + [str(COREC), "build", src, "-o", out_path, "--static"], + capture_output=True, text=True, cwd=BASE, timeout=180, + ) + if built.returncode != 0: + print(f"[FAIL] {name}: 编译失败 rc={built.returncode}\n{built.stdout}{built.stderr}") + return False + r = subprocess.run([out_path], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump) + if r.returncode != expect_rc: + print(f"[FAIL] {name}: 期望运行 rc={expect_rc},实测 rc={r.returncode}") + return False + print(f"[PASS] {name}: 运行 rc={r.returncode}") + return True + finally: + for p in (src, out_path, out_path + ".ccr"): + try: + os.unlink(p) + except FileNotFoundError: + pass + + +# ─── 正控:确定不能走完的体(修复前全报 TF01,修复后接受)─── + +POS_LOOP_IF_RETURN = """ +fn f() -> int { + loop { + if 1 > 0 { return 5; } + } +} +""" + +POS_LOOP_BARE_RETURN = "fn f() -> int { loop { return 5; } }\n" + +# 嵌套 loop:内层 break 归内层 ⇒ 外层无直系 break ⇒ 永不落空 +POS_NESTED_LOOP = "fn f() -> int { loop { loop { break; } } }\n" + +# if-else 双 diverges(两支皆无 break 的 loop ⇒ 两支类型均 unit ⇒ 修复前 TF01) +POS_IF_ELSE_DIVERGE = """ +fn f(c: int) -> int { + if c > 0 { loop { if c > 100 { return 1; } } } + else { loop { return 2; } } +} +""" + +# 语句包裹(`loop {...};` = EXPR_STMT(EXPR_LOOP)) +POS_STMT_WRAPPED = "fn f() -> int { loop { return 5; }; }\n" + +# alloc 的模型一致声明(string)——与本组同族:真错面的「正确写法」 +POS_ALLOC_STRING = "fn f() -> string { b := alloc(8); return b; }\n" + +# 遍历分支的牙(各自钉住 break 扫描的一个下钻面——删除对应分支即本档转红): +# · match 臂链(EXPR_ARM 链 ≠ 连续槽):臂内无 break ⇒ 外 loop 不落空 +POS_MATCH_NO_BREAK = """ +fn f(x: int) -> int { + loop { + match x { + 0 => { return 1; }, + _ => { return 2; } + } + } +} +""" + +# · 连续 wrapper 段(EXPR_ARRAY)+ LET + 索引 +POS_ARRAY_NO_BREAK = """ +fn f() -> int { + loop { + a := [1, 2, 3]; + if a[0] > 0 { return 1; } + } +} +""" + +# · 调用链(EXPR_CALL)与赋值/比较 +POS_LOOP_WITH_CALL = """ +fn g(x: int) -> int { return x; } +fn f() -> int { + loop { + if g(1) > 0 { return 5; } + } +} +""" + +# 登记边界(**本批显式登记,非静默**):非落空体内 return 的**值类型**不核对—— +# checker 现模型无「逐 return 核对」面(站点 5 只看块末类型),本条只把「无 break 的 +# loop 收尾」从 TF01 误报中解放。修复前该形因「末语句类型 = unit」被**误拒**;修复后 +# 按新口径接受(loop 永不落空)。若未来引入逐 return 核对,本条期望随之改为拒。 +POS_BOUNDARY_NO_RETURN_VALUE_CHECK = "fn f() -> int { loop { return \"x\"; } }\n" + +# ─── R2 P6 Task 4a(E-11):`-> never` 调用的返回型透传(TF01 面)─── +# 改前(RED 实测):`return boom()` 的块类型 = 调用推断 = unit(声明注册域守卫 +# checker.cr:1491 把 NEVER 钳成 unit)⇒ `body_ti != TI_NEVER` 成立(是 unit)⇒ +# 误发 TF01;改后:调用推断 = never ⇒ 返回位豁免(checker.cr:2234)⇒ 接受。 +NEVER_RETURN_SRC = ("fn boom() -> never { loop { } }\n" + "fn f() -> int { return boom(); }\n" + "fn main() -> int { return f(); }\n") + +# 变量传播面(与 test_iface_ops 的重钉项 t6_S1_never_unit 同源):`x` 绑定 = never +# ⇒ `return x` 的块类型 = never ⇒ 返回位豁免(改前 = unit ⇒ 误发 TF01)。 +NEVER_PROPAGATE_SRC = ("fn boom() -> never { loop { } }\n" + "fn main() -> int { x := boom(); return x; }\n") + +# 端到端(发射面回归):分支不执行 ⇒ 运行 rc=42;改前 build rc=1(TA02 在硬名单 ⇒ 无产物)。 +RUN_NEVER_DEAD_BRANCH = ("fn boom() -> never { loop { } }\n" + "fn main() -> int { if 1 > 2 { x : int = boom(); }\n" + " return 42; }\n") + +# ─── 负控:可落空的体(修复前报 TF01,修复后**必须仍拒**——静默接受洞的钉子)─── + +NEG_FALLTHRU = "fn f() -> int { 1 + 1; }\n" + +NEG_LOOP_BREAK = "fn f() -> int { loop { if 1 > 0 { break; } } }\n" + +NEG_IF_NO_ELSE = "fn f(c: int) -> int { if c > 0 { return 1; } }\n" + +# match 臂内 break:臂 = EXPR_ARM 链(非连续槽)——漏检即此处从「拒」变「接受」 +NEG_MATCH_BREAK = """ +fn f(x: int) -> int { + loop { + match x { + 0 => { break; }, + _ => { } + } + } +} +""" + +# 数组字面量(连续 wrapper 段)后 break——段扫描漏检即此处变「接受」 +NEG_ARRAY_BREAK = """ +fn f() -> int { + loop { + a := [1, 2, 3]; + if a[0] > 0 { break; } + } +} +""" + +# 嵌套 loop 之后仍有**直系** break ⇒ 可退出(截断只对「不下钻」,不得漏扫本层其余语句) +NEG_NESTED_THEN_BREAK = "fn f() -> int { loop { loop { break; } break; } }\n" + +# while 保守面:不判 diverging(常量真条件不做分析)⇒ 落空 = 照旧 TF01。 +# 条件用 `c == true` 而非裸标识符:`while c {` 会被 parser 当**结构体字面量** +# (parse_while_expr 缺 if 同款的 g_parse_no_struct_literal 守卫——既有面,本批不修, +# 见报告 §登记)。 +NEG_WHILE = "fn f(c: bool) -> int { while c == true { break; } }\n" + +# 真错面:alloc → int 声明(类型洗白)——修复**不得**把这条一起「修掉」 +NEG_ALLOC_INT_WASH = "fn f() -> int { b := alloc(8); return b; }\n" + +# ─── 端到端:接受面的语义不受影响 ─── + +RUN_LOOP_NO_BREAK = """ +fn f() -> int { + i : ., mut = 0; + loop { + i = i + 1; + if i >= 3 { return i * 14; } + } +} +fn main() -> int { return f(); } +""" + + +def main(): + ok = [ + case_accept("pos_loop_if_return", POS_LOOP_IF_RETURN), + case_accept("pos_loop_bare_return", POS_LOOP_BARE_RETURN), + case_accept("pos_nested_loop_inner_break", POS_NESTED_LOOP), + case_accept("pos_if_else_diverge", POS_IF_ELSE_DIVERGE), + case_accept("pos_stmt_wrapped", POS_STMT_WRAPPED), + case_accept("pos_alloc_string", POS_ALLOC_STRING), + case_accept("pos_match_no_break", POS_MATCH_NO_BREAK), + case_accept("pos_array_no_break", POS_ARRAY_NO_BREAK), + case_accept("pos_loop_with_call", POS_LOOP_WITH_CALL), + case_accept("pos_boundary_no_return_value_check", POS_BOUNDARY_NO_RETURN_VALUE_CHECK), + case_accept("pos_never_call_return", NEVER_RETURN_SRC), + case_accept("pos_never_call_propagates", NEVER_PROPAGATE_SRC), + case_run("run_never_call_dead_branch", RUN_NEVER_DEAD_BRANCH, 42), + case_reject("neg_fallthru", NEG_FALLTHRU), + case_reject("neg_loop_break", NEG_LOOP_BREAK), + case_reject("neg_if_no_else", NEG_IF_NO_ELSE), + case_reject("neg_match_break", NEG_MATCH_BREAK), + case_reject("neg_array_break", NEG_ARRAY_BREAK), + case_reject("neg_nested_then_break", NEG_NESTED_THEN_BREAK), + case_reject("neg_while_conservative", NEG_WHILE), + case_reject("neg_alloc_int_wash", NEG_ALLOC_INT_WASH), + case_run("run_loop_no_break", RUN_LOOP_NO_BREAK, 42), + ] + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_tuple_slots.py b/tests/selfhost/test_tuple_slots.py new file mode 100644 index 00000000..48c3a639 --- /dev/null +++ b/tests/selfhost/test_tuple_slots.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +"""F5(TODO #25):EXPR_TUPLE 元素槽位——复合元素元组的类型记录回归。 + +根因(两处,同属「元组元素类型错录」): + ① AST 槽位(parser.cr 元组分支):曾把「元素在 g_ast 连续」当契约,只记首元素节点 + + 元素个数;复合表达式元素自身子树占多槽 → 后续元素槽位全错位(元素子节点被当元素)。 + 修复 = 先解析全部元素值,再**统建连续 wrapper**(kind=EXPR_NONE,a=值节点); + EXPR_TUPLE 契约改为 a=首 wrapper(连续)/ b=元素个数,消费点(checker/ir_gen/ + monomorph/opt)经 ast_a(wrapper + i) 解引用。 + ② 类型侧表落盘(checker.cr EXPR_TUPLE 分支):data_start 曾在元素推断循环**前**取, + 而元素自身的推断会向 g_gen_apply_data 追加数据(嵌套元组 / 泛型应用)→ 后续元素 + 落点被顶开、extra 与实际落点错位。修复 = 两趟(先全推、再连续落盘)。 + +判据(每例双向断言:期望拒绝的必须出现对应码;期望接受的必须 rc=0 且无 error[): + 异型拒 / 异长拒 / 同型接受 + 反证边界(数组在首位、单节点元素)+ 嵌套元组三例。 +""" + +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# (名称, 源码, 期望) —— 期望:"ACCEPT" / "TA01"(赋值类型不合)/"LEN"(长度约束违反) +CASES = [ + ( + "compound elem same type accepted (F5 ①假拒回归)", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + x : ., mut = (1, a); + y : ., mut = (1, [1, 2, 3]); + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "compound elem diff type rejected (F5 ②soundness)", + """ +fn h() -> int { return 5; } +fn main() -> int { + x : ., mut = (1, [1, 2, 3]); + y : ., mut = (1, h()); + x = y; + return 0; +} +""", + "TA01", + ), + ( + "compound elem length mismatch rejected (F5 ③N 面)", + """ +fn main() -> int { + x : ., mut = (1, [1, 2, 3]); + y : ., mut = (1, [1, 2]); + x = y; + return 0; +} +""", + "LEN", + ), + ( + "both compound elems same type accepted", + """ +fn main() -> int { + x : ., mut = (1, [1, 2, 3]); + y : ., mut = (2, [7, 8, 9]); + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "both compound elems diff elem type rejected", + """ +fn main() -> int { + x : ., mut = (1, [1, 2, 3]); + y : ., mut = (2, ["a", "b", "c"]); + x = y; + return 0; +} +""", + "TA01", + ), + ( + "array in first slot accepted (反证边界)", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + x : ., mut = (a, 1); + y : ., mut = ([1, 2, 3], 1); + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "single-node elems accepted (反证边界)", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + x : ., mut = (1, a); + y : ., mut = (1, a); + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "single-node elems diff type rejected (反证边界)", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + x : ., mut = (1, a); + y : ., mut = (1, 5); + x = y; + return 0; +} +""", + "TA01", + ), + ( + "ident elems length mismatch rejected", + """ +fn main() -> int { + a : [int; 3] = [1, 2, 3]; + b : [int; 4] = [1, 2, 3, 4]; + x : ., mut = (1, a); + y : ., mut = (2, b); + x = y; + return 0; +} +""", + "LEN", + ), + ( + "nested tuple elems accepted", + """ +fn main() -> int { + x : ., mut = (1, (2, 3)); + y : ., mut = (1, (2, 3)); + x = y; + return 0; +} +""", + "ACCEPT", + ), + ( + "nested tuple arity mismatch rejected", + """ +fn main() -> int { + x : ., mut = (1, (2, 3)); + y : ., mut = (1, (2, 3, 4)); + x = y; + return 0; +} +""", + "TA01", + ), + ( + "nested tuple vs int rejected", + """ +fn main() -> int { + x : ., mut = (1, (2, 3)); + y : ., mut = (1, 5); + x = y; + return 0; +} +""", + "TA01", + ), + ( + "nested tuple in first slot vs int rejected", + """ +fn main() -> int { + x : ., mut = ((2, 3), 1); + y : ., mut = (5, 1); + x = y; + return 0; +} +""", + "TA01", + ), +] + +# IR 路径冒烟:复合元素元组必须能过 ir_gen(wrapper 解引用 + 数组元素构造) +IR_SMOKE = """ +fn g() -> int { return 33; } +fn main() -> int { + t := (g() + 1, [10, 20, 30]); + return 0; +} +""" + + +def run_case(name, source, expect): + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as src: + src.write(source) + src_path = src.name + try: + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "check", src_path], + cwd=BASE, + capture_output=True, + text=True, + timeout=60, + ) + finally: + os.unlink(src_path) + + output = result.stdout + result.stderr + if expect == "ACCEPT": + ok = result.returncode == 0 and "error[" not in output + elif expect == "TA01": + ok = result.returncode == 1 and "error[TA01]" in output and "Assignment type mismatch" in output + elif expect == "LEN": + ok = result.returncode == 1 and "error[TA01]" in output and "Array length constraint" in output + else: + ok = False + + if ok: + print(f"[PASS] {name}: {expect}") + return True + print(f"[FAIL] {name}: expect={expect} rc={result.returncode}") + print(output) + return False + + +def run_ir_smoke(): + """复合元素元组的 IR 生成 + ELF 冒烟(check 不过 ir_gen,此例专门覆盖 wrapper 解引用)。""" + src_dir = tempfile.mkdtemp(prefix="f5_tuple_") + src_path = os.path.join(src_dir, "t.cr") + out_path = os.path.join(src_dir, "t.bin") + with open(src_path, "w") as f: + f.write(IR_SMOKE) + try: + result = subprocess.run( + ["nice", "-n", "19", str(COREC), "build", src_path, "-o", out_path, "--static"], + cwd=BASE, + capture_output=True, + text=True, + timeout=300, + ) + output = result.stdout + result.stderr + if result.returncode != 0 or "error[" in output: + print("[FAIL] compound tuple IR smoke: build failed") + print(output) + return False + run = subprocess.run([out_path], capture_output=True, text=True, timeout=60) + if run.returncode != 0: + print(f"[FAIL] compound tuple IR smoke: exit={run.returncode}") + return False + print("[PASS] compound tuple IR smoke: build+run") + return True + finally: + shutil.rmtree(src_dir, ignore_errors=True) + + +def main(): + if not COREC.exists(): + print(f"[FAIL] missing native compiler: {COREC}") + return 1 + results = [run_case(*case) for case in CASES] + results.append(run_ir_smoke()) + passed = sum(results) + print(f"{passed}/{len(results)} passed") + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/selfhost/test_type_engine.py b/tests/selfhost/test_type_engine.py new file mode 100644 index 00000000..e4276b5f --- /dev/null +++ b/tests/selfhost/test_type_engine.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""R2 P0 判据:类型项引擎自测通道(corec selftest-types)。 + +引擎 = 集合语义的包含判定(spec docs/superpowers/specs/2026-09-10-type-interface-unification-design.md §3)。 +P0 只建层:checker 零改动、产物零变化;判据 = 用例表全 PASS + 零 FAIL + 计数行。 +""" + +import re +import subprocess +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + +# 用例数下限。P0 Task 1 = 建层,用例表 = 5 例(计划 Step 6 的 P0 最小面: +# bot_sub_int / int_sub_str / int_sub_int / dedup / notnot);计划 Step 1 原文的 +# 下限 32 是 **spec §8 全类**(子类型/等价/不相交/可空/反例/穷尽性/递归/参数化/ +# 预算)落地后的目标值;P0 终审修复轮后用例表实际 49 例(含 Critical/Important +# 各配回归例 + 预算窗口扫描 + 三态 -2 例)。 +# R2 P6 Task 5(E-7):下限由 **32 收紧为当批实测值 415**——32 是 spec §8 目标值的 +# 历史残留,远低于实测 ⇒ `total >= MIN_CASES` 形同虚设(用例被删到 33 也过)。 +# 纪律 **只增不减**:新增用例须同步下界;删除用例(须先有死亡证据)亦须同步下界。 +MIN_CASES = 415 + + +def run_selftest(): + return subprocess.run( + ["nice", "-n", "19", str(COREC), "selftest-types"], + cwd=BASE, capture_output=True, text=True, + ) + + +def test_selftest_types_all_pass(): + r = run_selftest() + out = r.stdout + r.stderr + assert "FAIL" not in out, out + m = re.search(r"(\d+)/(\d+) type-engine cases passed", out) + assert m, out + passed, total = int(m.group(1)), int(m.group(2)) + assert passed == total and total >= MIN_CASES, (passed, total, out) + assert r.returncode == 0, (r.returncode, out) + + +if __name__ == "__main__": + test_selftest_types_all_pass() + print("PASS test_selftest_types_all_pass") diff --git a/tests/selfhost/test_warm_cache_gate.py b/tests/selfhost/test_warm_cache_gate.py new file mode 100644 index 00000000..087be3a1 --- /dev/null +++ b/tests/selfhost/test_warm_cache_gate.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""#60 批 T3:暖缓存「定路径探针语料」两态回归(语料入仓 = `tests/probes/warm/`)。 + +**本设计的要点 = 定路径**:同一用例的两次编译必须用**同一路径**。套件既有用例把源写到唯一 +temp 路径 ⇒ 缓存键(源路径::函数名)唯一 ⇒ **结构性无暖态** ⇒ 腿恒绿而空洞(T1 方法学发现)。 +故本套件**直接编译入仓语料**(路径固定、跨运行跨机器恒定),冷 = `clean-cache` 后首跑, +暖 = 紧接二跑。**语料只增不减**。 + +缺陷(T1 实锤,`/tmp/fct4/task1-report.md`):缓存命中跳过 `ir_gen_func` ⇒ ir_gen 期 +`alloc_type` 出的类型行(`TYP_PTR extra=1` 等)不重建 ⇒ 读行内容的判定静默失效 +(首例 = `provenance_verify.cr:66` 的 TU03「外部指针解引用需 unsafe」)。 +修法(裁-W1 = (b) 先行,**共享面副作用见证**):生成期对「快照不载的共享面」有副作用 ⇒ +**本条目不可写** ⇒ 下次必 miss 重放全部副作用(见 `main.cr` miss 分支)。 + +判据(逐条对应语料文件,期望值表见 `tests/probes/warm/README.md`): + ① **缺陷面**:`tu03_load` / `tu03_store` —— 冷/暖**都** rc=1 + TU03(`ccr` 面); + ② **缺陷面(`build` 面同判)**:`tu03_load` 冷/暖都 rc=1 + TU03(`--static`); + ③ **机制钉**:`alloc_witness`(数组字面量 + 取址 ⇒ 生成期分配复合行)**无自己的条目** + (`::main.cir` 缺席 = 见证生效);同批 `plain`(普通程序)**有** ⇒ 抑制是**选择性**的; + ④ **正控(命中不退化)**:`plain` 二跑**真命中**(条目 size/mtime 不变)——防「腿恒绿」; + ⑤ **旁面不回归**:`tk01_oob_load` / `tk01_oob_store` / `tk01_width` 冷/暖都 rc=1 + TK01。 +""" +import os +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +BASE = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +COREC = BASE / "build" / "corec" +CACHE = BASE / ".core/cache/cir" +CORPUS = BASE / "tests" / "probes" / "warm" +TMP = Path(tempfile.mkdtemp(prefix="warm_gate_")) +npass = nfail = 0 + + +def _nc(): + import resource + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def cc(args, timeout=180): + return subprocess.run([str(COREC)] + args, cwd=BASE, capture_output=True, + text=True, timeout=timeout, preexec_fn=_nc) + + +def codes(txt): + return sorted(set(re.findall(r"error\[([A-Z0-9]+)\]", txt))) + + +def ok(name, cond, detail=""): + global npass, nfail + print(("[PASS] " if cond else "[FAIL] ") + name + (" " + detail if detail else "")) + if cond: + npass += 1 + else: + nfail += 1 + + +def src(name): + """入仓语料路径(**定路径**——不得复制到 temp)。""" + p = CORPUS / f"{name}.cr" + assert p.is_file(), f"语料缺失(只增不减): {p}" + return p + + +def cold_warm(name, mode="ccr"): + """同一入仓路径冷(clean-cache 后首跑)→ 暖(紧接二跑);返回两态 (rc, codes)。""" + p = src(name) + cc(["clean-cache"]) + if mode == "build": + a = cc(["build", str(p), "-o", str(TMP / "o1.bin"), "--static"]) + b = cc(["build", str(p), "-o", str(TMP / "o2.bin"), "--static"]) + else: + a = cc(["ccr", str(p), "-o", str(TMP / "o1.ccr")]) + b = cc(["ccr", str(p), "-o", str(TMP / "o2.ccr")]) + return (a.returncode, codes(a.stdout + a.stderr)), (b.returncode, codes(b.stdout + b.stderr)) + + +def entries(): + return sorted(p.name for p in CACHE.glob("*.cir")) if CACHE.exists() else [] + + +# ── 语料清点(非空洞:7 档必须在位;签名面 = 期望值表在 README) ── +WARM_FILES = ["tu03_load", "tu03_store", "tk01_oob_load", "tk01_oob_store", + "tk01_width", "plain", "alloc_witness"] +ok("corpus_present", all((CORPUS / f"{n}.cr").is_file() for n in WARM_FILES), + f"{CORPUS} 档数={len(list(CORPUS.glob('*.cr')))}(期望 ≥{len(WARM_FILES)})") + +# ① 缺陷面:外部指针解引用(load/store)——冷/暖必须一致(修复前:暖 rc=0 静默) +for nm in ("tu03_load", "tu03_store"): + c, w = cold_warm(nm) + ok(f"defect_{nm}_cold_warm_same", c == w and c[0] == 1 and "TU03" in c[1], f"冷={c} 暖={w}") + +# ② build 面同判(两面同病 ⇒ 两面同修) +c, w = cold_warm("tu03_load", mode="build") +ok("defect_tu03_build_face_cold_warm_same", c == w and c[0] == 1 and "TU03" in c[1], f"冷={c} 暖={w}") + +# ③ 机制钉:生成期分配共享行的函数无自己的条目;同批普通程序有(选择性) +cc(["clean-cache"]) +cc(["ccr", str(src("alloc_witness")), "-o", str(TMP / "w.ccr")]) +e_wit = entries() +cc(["clean-cache"]) +cc(["ccr", str(src("plain")), "-o", str(TMP / "p.ccr")]) +e_plain = entries() +ok("witness_no_own_entry", e_wit and not any(e.endswith("alloc_witness.cr::main.cir") for e in e_wit), + f"alloc_witness 条目={len(e_wit)} · 含自档 main={any(e.endswith('alloc_witness.cr::main.cir') for e in e_wit)}") +ok("witness_selective_control_has_entry", + any(e.endswith("plain.cr::main.cir") for e in e_plain), + f"plain 条目={len(e_plain)}(期望含 plain.cr::main.cir)") + +# ④ 正控:普通程序二跑真命中(条目 size/mtime 不变) +pe = [p for p in CACHE.glob("*plain.cr::main.cir")] +if pe: + st1 = pe[0].stat() + cc(["ccr", str(src("plain")), "-o", str(TMP / "p2.ccr")]) + st2 = pe[0].stat() + ok("positive_control_true_hit", + st1.st_size == st2.st_size and int(st1.st_mtime) == int(st2.st_mtime), + f"条目={pe[0].name} {st1.st_size}B mtime {int(st1.st_mtime)}→{int(st2.st_mtime)}") +else: + ok("positive_control_true_hit", False, "普通程序未产生 plain.cr::main.cir 条目") + +# ⑤ 旁面不回归:TK01(指针越界/宽度) +for nm in ("tk01_oob_load", "tk01_oob_store", "tk01_width"): + c, w = cold_warm(nm) + ok(f"tk01_{nm}_cold_warm_same", c == w and c[0] == 1 and "TK01" in c[1], f"冷={c} 暖={w}") + +print(f"{npass}/{npass + nfail} passed") +import shutil +shutil.rmtree(TMP, ignore_errors=True) +sys.exit(0 if nfail == 0 else 1) diff --git a/tests/selfhost/test_xcut_iface.py b/tests/selfhost/test_xcut_iface.py new file mode 100644 index 00000000..7ea8f8bf --- /dev/null +++ b/tests/selfhost/test_xcut_iface.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""R2 P3b Task 2:横切接口接线(序列/可索引形状判定接管 `TYP_*` 直比)的行为覆盖集。 + +交付面(计划 Task 2 Step 3):索引兜底门(`EXPR_INDEX` 落空)与切片(range)分支的 +**类别判定**从 `get_type_kind == TYP_ARRAY/TYP_SLICE/TI_STR` 直比换位到**横切形状满足判定** +(`iface_satisfies_term` + `sh_shape_*`;形状项见 `iface_registry.cr` 横切形状段): + · 索引兜底门 → **可索引**形状(`⊤ₖ(SEQUENCE) ∪ ⊤ₖ(STRING)`;与 IP_INDEX 许可集逐行同集); + · range 分支 → **序列**形状(`⊤ₖ(SEQUENCE)`)**∧ 固定性位 ≥ 0**(数组 = 固定长 ⇒ 产视图; + 切片 = 视图 ⇒ 现状 `TI_UNIT`,登记面 A.2 #15 未接线)。 +结果分支(arr→元素+F2 / slice→元素 / str→int)**原地保留**(同时决定结果类型;A.2 #16-18)。 + +**行为保持是本文件的判据**:以下每一例的 check rc / 诊断码 / 诊断文案 / 产物有无,改动前后 +**逐例相同**(旧二进制 = P3b Task 0 提交 `9aa1786c` 的构建,见报告 §5 的同源对拍)。本套件同时又 +是**新接线的咬合面**:range 分支若丢掉固定性维度(切片 range 改判为可索引),`slice_range_unit_pinned` +转红;索引兜底门若把形状拒绝集换错(如把 STRING 移出可索引形状),`str_index_*` 一族的负控转红。 + +判据口径:正例三路同证(check rc=0 ∧ ELF rc=N ∧ interp rc=N);负例 = check rc=1 + 措辞断言 ++ `build` **无产物**(收紧面同址)。`COREC_BIN` 环境变量可指向基线二进制做同源对拍(默认 +`build/corec`);临时源文件一律写在仓库根(cwd —— `/tmp` 下探针会触发 import 解析假失败)。 +""" + +import os +import resource +import subprocess +import tempfile +from pathlib import Path + + +BASE = Path(__file__).resolve().parents[2] +COREC = Path(os.environ.get("COREC_BIN", str(BASE / "build" / "corec"))) + + +def _no_core_dump(): + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + +def _compile(source: str, cmd="check"): + fd, src = tempfile.mkstemp(suffix=".cr") + with os.fdopen(fd, "w") as f: + f.write(source) + out = src[:-3] + if cmd == "build": + args = [str(COREC), "build", src, "-o", out, "--static"] + else: + args = [str(COREC), "check", src] + r = subprocess.run(args, capture_output=True, text=True, cwd=BASE, timeout=180) + return r, out, src + + +def _cleanup(*paths): + for p in paths: + try: + os.unlink(p) + except FileNotFoundError: + pass + + +def case_dual(name, source, expect_rc): + """正例三路同证:check rc=0 ∧ ELF rc=N ∧ interp rc=N(索引/切片语义真的跑到产物)。""" + rc_chk, outc, srcc = _compile(source, cmd="check") + try: + if rc_chk.returncode != 0: + print(f"[FAIL] {name}: check rc={rc_chk.returncode}(应有零诊断): {rc_chk.stdout}{rc_chk.stderr}") + return False + finally: + _cleanup(srcc, outc, outc + ".ccr") + r, out, src = _compile(source, cmd="build") + try: + if r.returncode != 0: + print(f"[FAIL] {name}: build rc={r.returncode}: {r.stdout}{r.stderr}") + return False + rr = subprocess.run([out], capture_output=True, text=True, timeout=10, + preexec_fn=_no_core_dump) + if rr.returncode != expect_rc: + print(f"[FAIL] {name}: ELF expected rc {expect_rc}, got {rr.returncode}") + return False + finally: + _cleanup(src, out, out + ".ccr") + ri = subprocess.run([str(COREC), "run", source], capture_output=True, text=True, + cwd=BASE, timeout=180) + if ri.returncode != expect_rc: + print(f"[FAIL] {name}: interp expected rc {expect_rc}, got {ri.returncode}: {ri.stdout}{ri.stderr}") + return False + print(f"[PASS] {name}: ELF+interp rc={rr.returncode}") + return True + + +def case_reject(name, source, needles, soft): + """负例:check rc≠0 + 诊断针 + `build` 面形态与既有诊断门同型(逐例钉住,防静默迁移)。 + + `soft=True`:软诊断(`check_error`)= check rc=1 而 **build rc=0 + 产物照出**(与既有 + 约束检查同门;TK01 索引门属此类——本套件把它钉住)。`soft=False`:硬错误 = build rc≠0 + + **无产物**(F2 的字面越界 / F11 的切片界属此类)。两态都必须与改动前逐例相同。""" + r, out, src = _compile(source, cmd="check") + try: + if r.returncode == 0: + print(f"[FAIL] {name}: expected rejection, got rc=0(静默通过面复活)") + return False + text = r.stdout + r.stderr + for n in needles: + if n not in text: + print(f"[FAIL] {name}: rc={r.returncode} 但缺诊断 {n!r}: {text}") + return False + finally: + _cleanup(src, out, out + ".ccr") + rb, outb, srcb = _compile(source, cmd="build") + try: + art = os.path.exists(outb) + if soft: + if rb.returncode != 0 or not art: + print(f"[FAIL] {name}: 软诊断面应为 build rc=0 + 产物,实得 rc={rb.returncode} artifact={art}") + return False + tag = "软诊断(build rc=0 + 产物)" + else: + if rb.returncode == 0 or art: + print(f"[FAIL] {name}: 硬错误面应为 build rc≠0 + 无产物,实得 rc={rb.returncode} artifact={art}") + return False + tag = "硬错误(无产物)" + print(f"[PASS] {name}: check rc={r.returncode} + {needles!r} + {tag}") + return True + finally: + _cleanup(srcb, outb, outb + ".ccr") + + +# ─── 正例(三路同证):形状接管的三个结果分支 + range 产视图 ─── +ARR_INDEX = """ +fn main() -> int { + a := [10, 20, 30]; + return a[1]; +} +""" + +SLICE_INDEX = """ +fn main() -> int { + a := [10, 20, 30]; + s := a[0..3]; + return s[2]; +} +""" + +STR_INDEX = """ +fn main() -> int { + s := "AB"; + return s[0]; +} +""" + +RANGE_ARRAY_SLICE = """ +fn main() -> int { + a := [1, 2, 3, 4]; + s := a[1..3]; + return s[0]; +} +""" + +RANGE_FULL_ARRAY = """ +fn main() -> int { + a := [7, 8]; + s := a[0..2]; + return s[1]; +} +""" + +# ─── 负例(收紧面同址;行为保持:改动前后同拒同码)── +INT_INDEX = """ +fn main() -> int { + x := 5; + return x[0]; +} +""" + +BOOL_INDEX = """ +fn main() -> int { + b := true; + return b[0]; +} +""" + +PTR_INDEX = """ +fn main() -> int { + x := 7; + p := &x; + return p[0]; +} +""" + +STRUCT_INDEX = """ +struct S { a: int } +fn main() -> int { + s := S{a: 1}; + return s[0]; +} +""" + +# ─── 结果分支/表示面钉子(行为保持:既有诊断与 range 结果形态逐字不变)── +OOB_LITERAL = """ +fn main() -> int { + a := [1, 2]; + return a[5]; +} +""" + +SLICE_BOUNDS = """ +fn main() -> int { + a := [1, 2, 3]; + s := a[0..5]; + return 0; +} +""" + +# range 的**切片**分支现状 = `TI_UNIT`(A.2 #15 登记:IP_INDEX_RANGE 未接线)⇒ 二次切片当下 +# 不可索引(TK01)。本钉即 range 分支固定性维度的咬合面:若丢掉固定性(切片 range 改产视图), +# 本例转 rc=0 = 静默放宽 ⇒ 红。 +SLICE_RANGE_UNIT = """ +fn main() -> int { + a := [1, 2, 3, 4]; + s := a[0..4]; + t := s[0..2]; + return t[0]; +} +""" + + +def main(): + print(f"[corec] {COREC}") + ok = [ + # ── 正例:结果分支三态(数组 / 切片 / 字符串)+ range 产视图 ── + case_dual("arr_index_elem", ARR_INDEX, 20), + case_dual("slice_index_elem", SLICE_INDEX, 30), + case_dual("str_index_byte", STR_INDEX, 65), + case_dual("range_array_view_indexable", RANGE_ARRAY_SLICE, 2), + case_dual("range_full_array_view", RANGE_FULL_ARRAY, 8), + # ── 负例:非容器行一律 TK01(形状拒绝集与 IP_INDEX 许可集同集;软诊断 = build 照出)── + case_reject("int_index_rejected", INT_INDEX, ["error[TK01]", "Cannot index non-array type"], True), + case_reject("bool_index_rejected", BOOL_INDEX, ["error[TK01]", "Cannot index non-array type"], True), + case_reject("ptr_index_rejected", PTR_INDEX, ["error[TK01]", "Cannot index non-array type"], True), + case_reject("struct_index_rejected", STRUCT_INDEX, ["error[TK01]", "Cannot index non-array type"], True), + # ── 表示面钉子:F2/F11 既有诊断(硬错误)、range 切片分支的 unit 结果(行为保持)── + case_reject("arr_oob_literal_pinned", OOB_LITERAL, ["index out of bounds: 5 (array length 2)"], False), + case_reject("slice_bounds_pinned", SLICE_BOUNDS, ["slice out of bounds:"], False), + case_reject("slice_range_unit_pinned", SLICE_RANGE_UNIT, ["error[TK01]"], True), + ] + + passed = sum(ok) + print(f"{passed}/{len(ok)} passed") + return 0 if passed == len(ok) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/suite/params_many.cr b/tests/suite/params_many.cr new file mode 100644 index 00000000..d2bb61ee --- /dev/null +++ b/tests/suite/params_many.cr @@ -0,0 +1,69 @@ +// TODO #8 收口(波 1 Task 5 Minor 4):≥18 形参静默误编译修复后的 runtime 用例。 +// +// 修复前 ≥18 形参的函数在 .ccr 中 name_idx=0/param_count=0(FuncInfo 形参槽区 +// 16 槽被无界写越界踩掉 return_type/ast_node),同源 checker 发 TF01 误归、IR +// 体丢失而 corec build 仍 rc=0 —— 即本文件在修复前不可能有 runtime 覆盖。 +// +// 覆盖形: +// * 18 形参 = 6 寄存器参 + 12 栈参(跨过修复前的 16 槽边界) +// * 22 形参 = 6 寄存器参 + 16 栈参 = 128B 栈清理(> 127B ⇒ add rsp, imm32 +// 7B 形;imm8 形编码不下,此形修复前结构不可达 = Minor 4 的收口条件) +// +// 判据:main rc=0(逐形参值全对;任一错位返回该参数序号+1 便于定位)。 +// 运行:./build/corec build tests/suite/params_many.cr -o /tmp/pm --static && /tmp/pm + +fn check18(a0: int, a1: int, a2: int, a3: int, a4: int, a5: int, a6: int, a7: int, a8: int, a9: int, a10: int, a11: int, a12: int, a13: int, a14: int, a15: int, a16: int, a17: int) -> int { + if a0 != 1 { return 1; } + if a1 != 2 { return 2; } + if a2 != 3 { return 3; } + if a3 != 4 { return 4; } + if a4 != 5 { return 5; } + if a5 != 6 { return 6; } + if a6 != 7 { return 7; } + if a7 != 8 { return 8; } + if a8 != 9 { return 9; } + if a9 != 10 { return 10; } + if a10 != 11 { return 11; } + if a11 != 12 { return 12; } + if a12 != 13 { return 13; } + if a13 != 14 { return 14; } + if a14 != 15 { return 15; } + if a15 != 16 { return 16; } + if a16 != 17 { return 17; } + if a17 != 18 { return 18; } + return 0; +} + +fn check22(a0: int, a1: int, a2: int, a3: int, a4: int, a5: int, a6: int, a7: int, a8: int, a9: int, a10: int, a11: int, a12: int, a13: int, a14: int, a15: int, a16: int, a17: int, a18: int, a19: int, a20: int, a21: int) -> int { + if a0 != 1 { return 1; } + if a1 != 2 { return 2; } + if a2 != 3 { return 3; } + if a3 != 4 { return 4; } + if a4 != 5 { return 5; } + if a5 != 6 { return 6; } + if a6 != 7 { return 7; } + if a7 != 8 { return 8; } + if a8 != 9 { return 9; } + if a9 != 10 { return 10; } + if a10 != 11 { return 11; } + if a11 != 12 { return 12; } + if a12 != 13 { return 13; } + if a13 != 14 { return 14; } + if a14 != 15 { return 15; } + if a15 != 16 { return 16; } + if a16 != 17 { return 17; } + if a17 != 18 { return 18; } + if a18 != 19 { return 19; } + if a19 != 20 { return 20; } + if a20 != 21 { return 21; } + if a21 != 22 { return 22; } + return 0; +} + +fn main() -> int { + r18 := check18(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18); + if r18 != 0 { return r18; } + r22 := check22(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22); + if r22 != 0 { return 40 + r22; } + return 0; +} diff --git a/tests/suite/syscall4_test.cr b/tests/suite/syscall4_test.cr new file mode 100644 index 00000000..7ca9aef9 --- /dev/null +++ b/tests/suite/syscall4_test.cr @@ -0,0 +1,33 @@ +// syscall4_test.cr — syscall4 第四参 r10 径持久覆盖(波 1 Task 7 carry 项 a) +// +// 消费面:src/stdlib/os.cr `system(cmd)` —— fork + execve("/bin/sh","-c",cmd) +// + wait4 收割退出码。 +// 为什么是 syscall4:wait4 有 4 个非编号参数(pid, &status, options, rusage), +// syscall3 只有 3 个通道,第 4 参落 rcx 被 `syscall` 指令破坏 → r10 残留垃圾 +// → 内核 EFAULT → 退出码传播不可靠。syscall4(src/os/linux/syscall.cr +// sys_syscall4_stub)把第 4 参经 r10 传递——本程序为该径的**持久套件覆盖** +// (波 1 Task 6 的覆盖仅是 /tmp 探针,未入树)。 +// +// 判据形式(suite 惯例 = run_suite「main 返回 0 为通过」,见 src/ci/run.sh): +// 子进程退出码经 wait 状态位 8..15 正确回传 → 本程序 rc=0;否则回传 rc=1/2 +// (等价 exit 断言:断言对象即 `system("exit 3") == 3` 本身)。 +import io +import os + +fn main() -> int { + rc := system("exit 3"); + if rc != 3 { + print("FAIL: system(\"exit 3\") rc="); + println_i(rc); + return 1; + } + // 第二探针换一个值:区分「常量/陈旧缓冲区回传」与真实状态位解码。 + rc2 := system("exit 42"); + if rc2 != 42 { + print("FAIL: system(\"exit 42\") rc="); + println_i(rc2); + return 2; + } + println("ALL PASS"); + return 0; +} diff --git a/tools/baseline/REBUILD.md b/tools/baseline/REBUILD.md new file mode 100644 index 00000000..42c13348 --- /dev/null +++ b/tools/baseline/REBUILD.md @@ -0,0 +1,107 @@ +# 冻结基线:重建配方 + sha 白名单(R2 P6 Task 1 / 交接包 E-13) + +## 这是什么 + +「**冻结基线**」= 判定面回归网**腿 ①(冻结基线同源对拍)** 里的那个「pre-批」编译器二进制: +拿它乘**当前源**,与**当前二进制**乘**当前源**逐档比(72 档 `check` 的 rc + 日志), +差异 = 该批对判定面的**行为变更**(预期内 = 收紧/放宽,须逐条入台账;预期外 = 回归)。 + +**二进制不入库**(维护者裁定,2026-09-13)——入库的是**配方 + sha 白名单**: +任何人可一条命令重建出**逐字节相同**的三二进制。 + +## 配方(唯一被许可的产生方式) + +```bash +bash tools/baseline/rebuild.sh /tmp/p6-baseline # 产物:/tmp/p6-baseline/{corec,corearch,corelsp} +``` + +脚本做的三件事(等价手工命令,见脚本头注): + +1. `jj workspace add --name p6-baseline-build --revision `(**仓库工作树之外**检出; + 收工 `jj workspace forget` + 删 tmp 目录) +2. `nice -n 19 python3 build_selfhost_native.py`(在检出根内) +3. `sha256sum` 对**白名单**(不符 ⇒ **失败退出**)+ 拷到 outdir + 冒烟(`corec run 'fn main()->int{return 42;}'` ⇒ rc=42) + +前置:`jj` 可用、工作树不处于多 agent 并发构建窗口(本仓纪律:一构建一编译串行)。 +`COREC_REPO_ROOT=` 可覆盖仓库根(脚本默认从自身位置 `../..` 推导)。 + +## sha 白名单(**现行代**) + +| 二进制 | sha256 | 源修订 | +|---|---|---| +| `corec` | `ae01de7534ea8428e3e062fccbc5fef5d45abfc5d3dafb90b3c85e08f354cce2` | `97f4394f`(R2 P6 终态 / P6 Task 6 收官) | +| `corearch` | `228f82e948cfaf8170ada982d80f1ebddfd3847e71cdf04ca4b42d9226373d2e` | 同上 | +| `corelsp` | `90eb19c6d3bd4e3234b4328d8b8f3284cca6d1644d3959bb83f07b7e71473032` | 同上 | + +> `PINNED_REV` = `97f4394f`(**P6 终态**)——容量批(CAP:E-2 表示面聚合 / E-3 `MAX_*` 解除 / +> E-4 收回可选程序 `.cir` 缓存)的「pre-批」侧。换代自证:重跑本配方 ⇒ 三 sha = 上表 + +> 冒烟 42 + 确定性 ×2(rebuilt 两次逐字节同);且 `jj diff --from 97f4394f --to <本批起点树>` +> 证明其后仅有计划文档新增(无构建输入变化)⇒ 与直接构建产物同 sha。 + +## 白名单历史(**旧值不删**——换代纪律 2;本表 = 历次换代台账) + +| 代 | 源修订 | `corec` | `corearch` | `corelsp` | 理由 / 出处 | +|---|---|---|---|---|---| +| 初代 | `9bcb7083`(R2 P4 收官 / P4 Task 7) | `5d2b15ad746018619b01143cce400e6f3489ae72a962e12283a7afd3dcdb41e7` | `493dc490dfaed7774b46b74ccf1cfeca9fe5f61620e34229afb8991b1628a2a7` | `18b94bd955fa204accf0753095a28f6499871566254d846333a7496c99109d49` | P5 批的 pre-侧(P6 T0 §1.4:与本配方产物逐字节 IDENTICAL ×3,白名单自洽) | +| **2 代(现行)** | `97f4394f`(R2 P6 终态) | `ae01de75…`(全值见上表) | `228f82e9…`(全值见上表) | `90eb19c6…`(全值见上表) | 容量批 CAP 的 pre-侧;换代依据 = 换代纪律 1(下一批以本批收官为 pre-侧);自证见「现行代」注与 `cap-task1-report.md` | + +## 用法 + +**批内(腿 ① 主判据)**: + +```bash +bash tools/baseline/parity_run.sh /tmp/p6-baseline/corec /tmp/parity_frozen # 冻结基线 × 当前源 +bash tools/baseline/parity_run.sh ./build/corec /tmp/parity_post # 当前二进制 × 当前源 +diff -rq /tmp/parity_frozen/logs /tmp/parity_post/logs # 逐档 rc + 日志 diff +``` + +**核对 runner 保真**(一次性 / 换代时):同一二进制分别跑入仓 runner 与迁移期 +`/tmp/p3t0_run.sh check`,两者 `logs/` 须 `diff -rq` 空。 + +**行为探针(腿 ②)**:`bash tools/baseline/probes_run.sh `(语料 = `tests/probes/`,29 档; +`rc=1` 多为**预期负例**,判据是两态零差异而非全 0 —— 见 `tests/probes/README.md`)。 + +**暖态腿(腿 ③,#60 T3)**:`tools/baseline/warm_leg.sh` —— 逐档「**同路径二跑**」× `ccr` 面 +(冷 = `clean-cache` 后首跑;暖 = 紧接二跑),断言 rc + 诊断码集一致,并报「**暖态生效 +(≥1 真命中)档数**」(0 = 空洞警报:腿绿但没走到暖态)。两层: + +```bash +bash tools/baseline/warm_run.sh ./build/corec /tmp/warm_now # 牙齿层(语料 tests/probes/warm/,7 档,期望值断言) +``` + +- **广度层**:`parity_run.sh` / `probes_run.sh` 默认附带(清单单一真源 = `/corpus.tsv`; + 结果落 `/warm/`,**`logs/` 格式不变** ⇒ 历史对拍可比性保持)。`WARM_LEG=0` 关闭。 + 对 **pre-fix 冻结基线预期绿**(既有 72/29 语料不含 `as *T` 形态;实测 M1 二进制 72 档 FAIL=0)。 +- **牙齿层**:`tests/probes/warm/`(**定路径**是该语料的设计要点;拷到唯一 temp 路径 ⇒ 腿恒绿而 + 空洞 —— 突变 M3 实证)。**pre-fix 二进制在本层必红**(缺陷本体)⇒ 对**冻结基线**调用 + `warm_run.sh` 时预期 rc=1,**不是**基线不合规(判据 = 突变 M1,见 `warm-task3-report.md`)。 +- 时长实测(2026-09-15,本机):牙齿层 **0.9s** · probes 广度层 **+3.5s** · parity 广度层 + **+125s**(72 档 ×2 次 `ccr`,大文件为主)⇒ **CI 只挂最小面**(`tests/selfhost/test_warm_cache_gate.py`,0.5s), + 广度层留手工判据。 +- **腿本体不进 CI**(维护者裁定 2026-09-15):与套件**同语料**、覆盖重叠,收益仅「校验 runner 自身」。 +- **何时该跑(手工判据纪律)**——下列任一情形**必跑** `warm_run.sh`(0.9s)**加**至少一次 + parity 广度层(`parity_run.sh` 默认已含):① 改动 `tools/baseline/*` runner 本体; + ② 改动缓存面(`src/compiler/cir_cache.cr` · `ccr_io.cr` 的保存/装载路径 · `main.cr` 的缓存门/ + 见证规则 · `CIR_CACHE_VER`);③ 关闭、降级或重定向 `.core/cache`(含 `clean-cache` 语义变更); + ④ 新增/改动 `tests/probes/warm/` 语料(须同步 `warm_leg.sh` 的 `WARM_TOTAL` 与语料 README)。 + 日常 CI 已由最小面覆盖语料本身,**不**替代上述腿。 + +## 换代纪律(**只在此处更新白名单**) + +1. 白名单换代 = 「基线世代」变更(例如尾批收官后,下一批以本批收官为 pre-侧)。 + **不得**因为重建 sha 不符就改白名单对齐——那正是本配方要抓的「构建不可复现」。 +2. 换代须**同批**记录:旧三 sha(含其源修订)+ 新源修订 + 理由 + 出处提交;旧值不删(台账留痕)。 +3. 换代后重跑一次**对拍自证**(新基线 × 当前源 vs 当前二进制 × 当前源)并把结果记入该批报告。 + +## 已知限制(不静默) + +- **CI 内不跑**:`.github/workflows/core-ci.yml` 用 `actions/checkout` + `fetch-depth: 2`(浅检出, + 无 `jj`)⇒ pinned revision 在 CI 环境**不可得**。本腿为**手工判据**(每任务/每批收尾跑), + 故入仓的是**可复现性**而非 CI 挂钩(R2 P6 Task 1 §Step 4 评估结论)。 +- **72 档语料的既有 rc=1 不是回归**:0 字节 fixture ×2 / P21 嵌套 fn 负例 ×2 / examples 解析错 ×2 / + 库单元单独 check(N01/N06/N11)×25 / TF01 并发族 ×5 / TF07 ×1 / B04 借用 ×1 —— 划界见 + `p6-task0-report.md` §3.1(跑之前先读,勿把既有失败当回归)。 +- **`examples/` 不在任何 CI job 的覆盖面内**(`suite` job 只跑 `tests/suite`)——但本对拍面含之 + (4 档),故对拍是 examples 的**唯一**行为证据来源。 +- **`.ccr`/`.cir` 缓存态**:比较任何产物前 `clean-cache`(runner 已逐档做);`.ccr` 记录值引用须带 + 命令口径 + 缓存态(TODO #26 / P4 附录 D-1)。 diff --git a/tools/baseline/parity_run.sh b/tools/baseline/parity_run.sh new file mode 100644 index 00000000..306c184e --- /dev/null +++ b/tools/baseline/parity_run.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# R2 P6 Task 1(E-13):语料同源对拍 runner —— 72 档 × `check`,逐档 clean-cache。 +# +# 用法: bash tools/baseline/parity_run.sh +# 产物: /logs/_.{log,rc}(与迁移期 runner 同名同格式)+ /parity.out +# +# 与迁移期 runner(`/tmp/p3t0_run.sh`,sha 12dc571f…)的关系: +# · 语料分层 / 命名 / 逐档 clean-cache 逐字继承(可 `diff -rq` 互证); +# · **影子模式整体剥除**——`--type-shadow` / `--type-shadow-dump` 两旗标已随 R2 P5 Task 5 +# 删除,原 `shadow` 分支为死码(不移植); +# · `shopt -s nullglob` + 计数断言 72:未匹配 glob 不再产生**字面 glob 伪条目** +# (R2 P6 T0 §3.2 的 `_tmp_p5t3b_probes_*.cr` 教训);语料数变化 ⇒ 硬失败(须显式改本文件)。 +# +# 判定用法(同源对拍):同一二进制跑本 runner 与在位的 `/tmp/p3t0_run.sh check`, +# `diff -rq /logs /logs` 与两者 stdout 的 rc 列须为空。**基线二进制**由 +# `tools/baseline/rebuild.sh` 产生(配方 = REBUILD.md)。 +set -u +shopt -s nullglob + +usage() { echo "usage: bash tools/baseline/parity_run.sh " >&2; exit 2; } +[ $# -eq 2 ] || usage +CCBIN="$1"; OUT="$2" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${COREC_REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" +cd "$REPO_ROOT" || exit 1 +mkdir -p "$OUT/logs" +CC="nice -n 19 $CCBIN" + +CORPUS_TOTAL=72 # = t1 32 + t2 2 + t3 15 + t4 19 + t5 4(R2 P6 T0 表 C 实核) + +run_one() { # $1 tier tag, $2 file + local tag="$1" f="$2" base + base=$(echo "$f" | tr '/' '_') + $CC clean-cache >/dev/null 2>&1 + $CC check "$f" >| "$OUT/logs/${tag}_${base}.log" 2>&1 + local rc=$? + echo "$rc" >| "$OUT/logs/${tag}_${base}.rc" + printf '%s\t%s\n' "$tag" "$f" >> "$OUT/corpus.tsv" # 暖态腿清单(单一真源) + printf '%s\t%s\trc=%s\n' "$tag" "$f" "$rc" +} + +: >| "$OUT/corpus.tsv" +{ + echo "=== Tier 1: tests/suite/*.cr ===" + for f in tests/suite/*.cr; do run_one t1 "$f"; done + + echo "=== Tier 2: compiler self ===" + run_one t2 src/compiler/main.cr + run_one t2 src/compiler/_import.cr + + echo "=== Tier 3: backend/kernel units ===" + run_one t3 src/compiler/ccr_io.cr + for f in src/format/elf/*.cr src/os/linux/*.cr src/arch/x86_64/*.cr src/lattice/*.cr src/targets/x86_64-linux/*.cr; do + run_one t3 "$f" + done + + echo "=== Tier 4: stdlib ===" + for f in src/stdlib/*.cr; do run_one t4 "$f"; done + + echo "=== Tier 5: examples/*.cr ===" + for f in examples/*.cr; do run_one t5 "$f"; done +} >| "$OUT/parity.out" + +n_rc=0; n0=0; n1=0 +for r in "$OUT"/logs/*.rc; do + n_rc=$((n_rc + 1)) + if [ "$(cat "$r")" = 0 ]; then n0=$((n0 + 1)); else n1=$((n1 + 1)); fi +done +if [ "$n_rc" -ne "$CORPUS_TOTAL" ]; then + echo "CORPUS COUNT $n_rc != $CORPUS_TOTAL —— 语料构成已变;须显式更新本 runner 的分层清单与计数(硬失败,不得静默)" >&2 + exit 1 +fi +echo "PARITY DONE($OUT):$n_rc 档 · rc 分布 $n0×0 / $n1×1" + +# #60 T3 暖态腿(广度层):同路径二跑 —— rc/诊断码集一致性 + 暖态生效档数。 +# 本层对 pre-fix 冻结基线**预期绿**(既有语料不含 TU03 形态,T1 E4);缺陷面的「牙齿」在 +# `tools/baseline/warm_run.sh`(入仓语料 tests/probes/warm/)。`WARM_LEG=0` 关闭。 +if [ "${WARM_LEG:-1}" != "0" ]; then + bash "$SCRIPT_DIR/warm_leg.sh" "$CCBIN" "$OUT/warm" < "$OUT/corpus.tsv" \ + || { echo "WARM LEG FAILED(暖态腿:冷/暖两态分歧)" >&2; exit 1; } +fi diff --git a/tools/baseline/probes_run.sh b/tools/baseline/probes_run.sh new file mode 100644 index 00000000..0cf2ff73 --- /dev/null +++ b/tools/baseline/probes_run.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# R2 P6 Task 1(E-13):行为探针 runner —— `tests/probes/*.cr` 全集(**29 档**),逐档 clean-cache。 +# +# 用法: bash tools/baseline/probes_run.sh +# 产物: /.log · /rc.txt · /probes.out +# +# 与迁移期 runner(`/tmp/p5t4/run_probes.sh`)的两处**纠正**(R2 P6 T0 §3.2): +# ① 语料由 `/tmp` 三目录改为**入仓**路径 `tests/probes/`(29 档;源 = `/tmp/p5t0/probes`(11) +# + `/tmp/p5t3/probes`(18),见 `tests/probes/README.md` 的逐档来源表); +# ② 显式清单 + `shopt -s nullglob` + 计数断言:原版 `for f in /tmp/p5t3b/probes/*.cr` +# (零匹配目录)产生**字面 glob 伪条目** `_tmp_p5t3b_probes_*.cr`(rc=1)——两态恒等 +# ⇒ 该条对拍是**空洞证据**,并会把「30 档」写进台账。本 runner 不再有此形态。 +# +# 注:`n13/n17/n18` 三档是 T3b 残留面探针(`[NA;2]`/`*NA`/元组含异名)——其**变异态** +# 证据需重造变异二进制,不随语料入仓(见 README「运行条件」节)。 +set -u +shopt -s nullglob + +usage() { echo "usage: bash tools/baseline/probes_run.sh " >&2; exit 2; } +[ $# -eq 2 ] || usage +CCBIN="$1"; OUT="$2" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${COREC_REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" +cd "$REPO_ROOT" || exit 1 +mkdir -p "$OUT" +CC="nice -n 19 $CCBIN" + +PROBE_TOTAL=29 # = p5t0 11 + p5t3 18(R2 P6 T0 表 C / U-4 正式清点值) + +: >| "$OUT/rc.txt" +: >| "$OUT/corpus.tsv" +{ + for f in tests/probes/*.cr; do + tag=$(echo "$f" | tr '/' '_') + $CC clean-cache >/dev/null 2>&1 + $CC check "$f" >| "$OUT/${tag}.log" 2>&1 + rc=$? + printf '%s\trc=%s\n' "$tag" "$rc" >> "$OUT/rc.txt" + printf 'probe\t%s\n' "$f" >> "$OUT/corpus.tsv" # 暖态腿清单(单一真源) + printf '%s\trc=%s\n' "$tag" "$rc" + done +} >| "$OUT/probes.out" + +n=$(wc -l < "$OUT/rc.txt") +if [ "$n" -ne "$PROBE_TOTAL" ]; then + echo "PROBE COUNT $n != $PROBE_TOTAL —— 探针语料构成已变;须显式更新本文件与 README(硬失败)" >&2 + exit 1 +fi +n0=$(grep -c 'rc=0$' "$OUT/rc.txt" || true) +n1=$(grep -c 'rc=1$' "$OUT/rc.txt" || true) +echo "PROBES DONE($OUT):$n 档 · rc 分布 $n0×0 / $n1×1" + +# #60 T3 暖态腿(广度层):同路径二跑 —— rc/诊断码集一致性 + 暖态生效档数(见 warm_leg.sh 头注)。 +if [ "${WARM_LEG:-1}" != "0" ]; then + bash "$SCRIPT_DIR/warm_leg.sh" "$CCBIN" "$OUT/warm" < "$OUT/corpus.tsv" \ + || { echo "WARM LEG FAILED(暖态腿:冷/暖两态分歧)" >&2; exit 1; } +fi diff --git a/tools/baseline/rebuild.sh b/tools/baseline/rebuild.sh new file mode 100644 index 00000000..64ba4c4c --- /dev/null +++ b/tools/baseline/rebuild.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# R2 P6 Task 1(E-13):冻结基线重建 —— 从 pinned revision 重建三二进制并核 sha 白名单。 +# +# 用法: bash tools/baseline/rebuild.sh +# 产物: /{corec,corearch,corelsp}(**不入库**——维护者裁定「重建,不落二进制」, +# 配方 + 白名单入仓 = tools/baseline/REBUILD.md) +# +# 语义:判定面回归网 ① 的「冻结基线」= 源出 pinned revision 的三二进制。本脚本 = 该腿 +# 唯一被许可的产生方式(手抄 /tmp 副本不算)。sha 不符白名单 ⇒ **失败退出**, +# 绝不改白名单对齐(停条件,见计划 Task 1)。 +set -euo pipefail + +# ── 白名单(来源修订 + 三 sha;换代纪律见 REBUILD.md)── +# 换代(2026-09-14,容量批 CAP Task 1):PINNED_REV 9bcb7083(P4 收官)→ 97f4394f(P6 终态 / +# 本批起点)。依据 = `REBUILD.md`「换代纪律」1/2(下一批以本批收官为 pre-侧;旧值留痕)。 +# 旧代三 sha(9bcb7083):corec 5d2b15ad… · corearch 493dc490… · corelsp 18b94bd9…(全值见 REBUILD.md 历史表)。 +PINNED_REV="97f4394f" # P6 终态(R2 P6 Task 6 收官)/ 容量批起点 +WS_NAME="p6-baseline-build" # 临时 workspace 名(收工 forget) + +WHITELIST_COREC="ae01de7534ea8428e3e062fccbc5fef5d45abfc5d3dafb90b3c85e08f354cce2" +WHITELIST_COREARCH="228f82e948cfaf8170ada982d80f1ebddfd3847e71cdf04ca4b42d9226373d2e" +WHITELIST_CORELSP="90eb19c6d3bd4e3234b4328d8b8f3284cca6d1644d3959bb83f07b7e71473032" + +usage() { echo "usage: bash tools/baseline/rebuild.sh " >&2; exit 2; } +[ $# -eq 1 ] || usage +OUTDIR="$1" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${COREC_REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" +TMPBASE="$(mktemp -d /tmp/p6-baseline.XXXXXX)" +WS_DIR="$TMPBASE/src" + +cleanup() { + ( cd "$REPO_ROOT" && jj workspace forget "$WS_NAME" >/dev/null 2>&1 ) || true + rm -rf "$TMPBASE" +} +trap cleanup EXIT + +echo "[1/4] 检出 pinned revision $PINNED_REV → $WS_DIR" +cd "$REPO_ROOT" +jj workspace add "$WS_DIR" --name "$WS_NAME" --revision "$PINNED_REV" + +echo "[2/4] 构建(nice -n 19;日志 $TMPBASE/build.log)" +cd "$WS_DIR" +nice -n 19 python3 build_selfhost_native.py 2>&1 | tee "$TMPBASE/build.log" +# 构建日志面(非硬闸——硬闸 = 下方 sha):GUARD clean 行计数 +echo "[info] build log 'error[ = 0' 行数 = $(grep -c 'error\[ = 0' "$TMPBASE/build.log" || true)" + +echo "[3/4] sha256 对白名单" +check_sha() { # $1 文件名, $2 期望 sha, $3 展示名 + local got + got="$(sha256sum "$WS_DIR/build/$1" | cut -d' ' -f1)" + if [ "$got" != "$2" ]; then + echo "MISMATCH $3" >&2 + echo " got = $got" >&2 + echo " expected = $2" >&2 + echo " ⇒ 白名单不符 = 停条件;不得改白名单对齐(见 REBUILD.md)。" >&2 + exit 1 + fi + echo " OK $3 $got" +} +check_sha corec "$WHITELIST_COREC" corec +check_sha corearch "$WHITELIST_COREARCH" corearch +check_sha corelsp "$WHITELIST_CORELSP" corelsp + +echo "[4/4] 拷贝到 $OUTDIR + 冒烟" +mkdir -p "$OUTDIR" +cp "$WS_DIR/build/corec" "$WS_DIR/build/corearch" "$WS_DIR/build/corelsp" "$OUTDIR/" + +set +e +nice -n 19 "$OUTDIR/corec" run 'fn main()->int{return 42;}' >"$TMPBASE/smoke.out" 2>&1 +smoke_rc=$? +set -e +if [ "$smoke_rc" -ne 42 ]; then + echo "SMOKE FAIL rc=$smoke_rc(期望 42;输出见下)" >&2 + cat "$TMPBASE/smoke.out" >&2 + exit 1 +fi +echo " OK smoke rc=42" + +echo "DONE 冻结基线 = $OUTDIR(三 sha 与白名单逐条同;源修订 $PINNED_REV)" diff --git a/tools/baseline/warm_leg.sh b/tools/baseline/warm_leg.sh new file mode 100644 index 00000000..aeebd1dc --- /dev/null +++ b/tools/baseline/warm_leg.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# #60 批 T3:**暖态腿** runner —— 「同一路径二跑」两态一致性 + 暖态生效档数(命中实证)。 +# +# 背景(T1 实锤,`/tmp/fct4/task1-report.md`):`.cir` 缓存命中会跳过 `ir_gen_func` 的全部 +# 生成期副作用;缺口(类型行不随快照)曾使安全检查诊断**暖态静默消失**(冷 rc=1 TU03 → +# 暖 rc=0 + 照常出 ELF)。既有 runner 逐档 `clean-cache` ⇒ 判据面对暖态**结构性零覆盖**; +# 且**既有语料不含 TU03 形态**(全仓 `as *T` 零命中)⇒ 只把二跑加进 72/29 档会**恒绿而空洞**。 +# 故本腿分两层(见 `--with-corpus`): +# · 广度层(默认):父 runner 传进来的清单(`\t` 行)逐档二跑 —— 抓**广谱** +# 暖态回归(任一类码在暖态变脸都红);对 pre-fix 冻结基线**预期绿**(T1 E4:码集零差异)。 +# · 牙齿层(`--with-corpus`):入仓语料 `tests/probes/warm/*.cr`(**定路径**是该语料的设计 +# 要点)—— 缺陷面(TU03)与旁面(TK01)**期望值逐档断言**;pre-fix 二进制在本层**必红** +# (这就是「腿有牙」的判据,见 warm-task3-report.md 突变 M1)。 +# +# 用法: +# printf '%s\t%s\n' tag file | bash tools/baseline/warm_leg.sh [--with-corpus] +# bash tools/baseline/warm_run.sh # 牙齿层专用入口(等价 --with-corpus 空清单) +# +# 判据(任一不满足 ⇒ 记 FAIL;全部跑完后 rc=1): +# ① 冷/暖 **rc 一致**;② 冷/暖 **诊断码集一致**(`error[XX]` 去重排序); +# ③ 牙齿层:期望值逐档命中(`tu03_*` ⇒ rc=1 且含 TU03;`tk01_*` ⇒ rc=1 且含 TK01;正控 ⇒ rc=0)。 +# 产物: /_.{cold.log,warm.log} · /stats.tsv · /warm.out +# 暖态生效档数 = 冷跑后有条目、且暖跑后**至少 1 条条目 size/mtime 未变**(= 真命中)的档数。 +# `%y` 取到纳秒:同秒重写不会被误判为命中。 +set -u +shopt -s nullglob + +usage() { echo "usage: bash tools/baseline/warm_leg.sh [--with-corpus]" >&2; exit 2; } +[ $# -ge 2 ] || usage +CCBIN="$1"; OUT="$2"; WITH_CORPUS=0 +[ "${3:-}" = "--with-corpus" ] && WITH_CORPUS=1 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${COREC_REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" +cd "$REPO_ROOT" || exit 1 +CACHE=".core/cache/cir" +CC="nice -n 19 $CCBIN" +WARM_TOTAL=7 # tests/probes/warm/*.cr 档数(**只增不减**;语料变化须显式更新本计数) + +mkdir -p "$OUT" +TMPD="$(mktemp -d /tmp/warm_leg.XXXXXX)" +trap 'rm -rf "$TMPD"' EXIT + +snap() { # $1 = 本档源路径的 munged 前缀(缓存键 = 源路径::函数名);条目指纹排序后可比 + local pref; pref=$(echo "$1" | tr '/' '_') + for e in "$CACHE"/"$pref"::*.cir; do [ -e "$e" ] || continue; stat -c '%n %s %y' "$e"; done | sort +} +codes_of() { grep -o 'error\[[A-Z0-9]*\]' "$1" 2>/dev/null | sort -u | tr '\n' ' '; } + +run_one() { # $1 tag, $2 file, $3 expect("" | ":") + local tag="$1" f="$2" expect="${3:-}" base + base=$(echo "$f" | tr '/' '_') + $CC clean-cache >/dev/null 2>&1 + $CC ccr "$f" -o "$TMPD/o1.ccr" > "$OUT/${tag}_${base}.cold.log" 2>&1; local rc1=$? + snap "$f" > "$OUT/.snap1" # 冷跑**后**取快照(冷跑写条目;**只认本档前缀**) + local n1 hits + n1=$(wc -l < "$OUT/.snap1") + $CC ccr "$f" -o "$TMPD/o2.ccr" > "$OUT/${tag}_${base}.warm.log" 2>&1; local rc2=$? + snap "$f" > "$OUT/.snap2" # 暖跑后:size/mtime 未变者 = 真命中(唯一 temp 路径会在此现形) + hits=$(comm -12 "$OUT/.snap1" "$OUT/.snap2" | wc -l) + local c1 c2 verdict="ok" note="" + c1=$(codes_of "$OUT/${tag}_${base}.cold.log"); c2=$(codes_of "$OUT/${tag}_${base}.warm.log") + if [ "$rc1" != "$rc2" ]; then verdict=FAIL; note="rc 冷=$rc1 暖=$rc2"; fi + if [ "$c1" != "$c2" ]; then verdict=FAIL; note="$note 码集 冷=[$c1] 暖=[$c2]"; fi + if [ -n "$expect" ]; then + local er="${expect%%:*}" ec="${expect##*:}" + if [ "$rc1" != "$er" ]; then verdict=FAIL; note="$note 期望冷 rc=$er 实=$rc1"; fi + if [ -n "$ec" ] && [ "${c1// /}" != "error[$ec]" ]; then + verdict=FAIL; note="$note 期望冷码=$ec 实=[$c1]" + fi + fi + [ "$verdict" != ok ] && nfail=$((nfail + 1)) + [ "$hits" -gt 0 ] && nhit=$((nhit + 1)) + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ + "$tag" "$f" "$rc1" "$rc2" "[${c1% }]" "[${c2% }]" "$n1" "$hits" "$verdict${note:+ ($note)}" >> "$OUT/stats.tsv" + printf '%-8s %-42s 冷 rc=%s 暖 rc=%s 条目=%s 命中=%s %s%s\n' \ + "$tag" "$f" "$rc1" "$rc2" "$n1" "$hits" "$verdict" "${note:+ ($note)}" +} + +: > "$OUT/stats.tsv" +nfail=0; nhit=0; ntot=0 +{ + echo "=== 广度层(父 runner 清单)===" + while IFS=$'\t' read -r tag f; do + [ -n "${f:-}" ] || continue + ntot=$((ntot + 1)); run_one "$tag" "$f" + done + if [ "$WITH_CORPUS" = 1 ]; then + echo "=== 牙齿层(入仓语料 tests/probes/warm/,**期望值逐档断言**)===" + nc=0 + for f in tests/probes/warm/*.cr; do + nc=$((nc + 1)); ntot=$((ntot + 1)) + base=$(basename "$f" .cr) + case "$base" in + tu03_load|tu03_store) run_one warm "$f" "1:TU03" ;; + tk01_*) run_one warm "$f" "1:TK01" ;; + plain|alloc_witness) run_one warm "$f" "0:" ;; + *) run_one warm "$f" "" ;; + esac + done + if [ "$nc" -ne "$WARM_TOTAL" ]; then + echo "WARM CORPUS COUNT $nc != $WARM_TOTAL —— 语料构成已变;须显式更新本文件与 tests/probes/warm/README.md(硬失败)" >&2 + nfail=$((nfail + 1)) + fi + fi +} >| "$OUT/warm.out" +rm -f "$OUT/.snap1" "$OUT/.snap2" + +cat "$OUT/warm.out" +echo "WARM LEG DONE($OUT):$ntot 档 · **暖态生效(≥1 真命中)档数 = $nhit** · FAIL=$nfail" +[ "$nfail" -eq 0 ] || exit 1 diff --git a/tools/baseline/warm_run.sh b/tools/baseline/warm_run.sh new file mode 100644 index 00000000..31a4afda --- /dev/null +++ b/tools/baseline/warm_run.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# #60 批 T3:入仓暖态语料专用 runner(**牙齿层**)—— 等价 `warm_leg.sh --with-corpus`(空清单)。 +# +# 用法: bash tools/baseline/warm_run.sh +# 语料: `tests/probes/warm/*.cr`(7 档;**定路径** = 本语料的设计要点,见其 README) +# 判据: 逐档「同路径二跑」冷/暖 rc + 诊断码集一致 ∧ 期望值命中(tu03 ⇒ rc=1 TU03 · tk01 ⇒ +# rc=1 TK01 · 正控 ⇒ rc=0);任一 FAIL ⇒ rc=1。 +# 注: pre-fix 二进制(本缺陷修复前)在本腿**必红** —— 这正是「腿有牙」的判据(突变 M1)。 +set -u +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec bash "$SCRIPT_DIR/warm_leg.sh" "$1" "$2" --with-corpus < /dev/null diff --git a/tools/module_to_ccr.py b/tools/module_to_ccr.py index 3e13784f..9a0d4c28 100644 --- a/tools/module_to_ccr.py +++ b/tools/module_to_ccr.py @@ -1,5 +1,15 @@ #!/usr/bin/env python3 """ +[DEPRECATED / 已废弃 —— R2 P6 Task 5(E-8)标注] + +本工具写出的是 **version = 1** 的远古 `.ccr`(`wu32(1)`,见本文件 `:219-220`),而现行 +loader 对 `version != 8` **整类拒收**(`src/compiler/ccr_io.cr:135`:`CCR_VERSION = 8`; +三态纪律 D10:不得静默当「空表」)⇒ **本工具的产物必被现行 corearch 拒绝**。 +零 in-repo 调用者(全仓 grep 仅命中本 docstring / CHANGELOG / 历史计划文档,无脚本或清单引用)。 +**保留不删**(删除属独立决定,已登记「删除候选」);历史价值 = P0 期「Python bootstrap → `.ccr`」过渡通道。 + +以上为现状标注;以下为原始说明(保留备查)。 +———————————————————————————————————————————— Convert a bootstrap IR Module to .ccr binary format, then compile with build/corearch for a fast native binary. diff --git a/tools/pseudocode_extract.py b/tools/pseudocode_extract.py index f1838414..74b56cdc 100644 --- a/tools/pseudocode_extract.py +++ b/tools/pseudocode_extract.py @@ -4,7 +4,9 @@ from pathlib import Path ROOTS = [Path("src/compiler"), Path("src/stdlib"), - Path("src/runtime"), Path("src/arch/linux/ld")] + Path("src/runtime"), Path("src/lattice"), + Path("src/arch/x86_64"), Path("src/format/elf"), + Path("src/os/linux"), Path("src/targets/x86_64-linux")] RE_FN = re.compile(r'(? Date: Tue, 15 Sep 2026 23:31:34 +0800 Subject: [PATCH 010/138] =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=87=8D=E7=BB=84(39=20=E6=8F=90=E4=BA=A4:=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E9=87=8D=E7=BB=84=20+=20=E4=BA=A4=E5=8F=89=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20+=20=E5=88=86=E7=AF=87=E7=B4=A2=E5=BC=95?= =?UTF-8?q?=20+=20=E8=A7=86=E8=A7=92=E6=B8=85=E7=90=86)=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: 核心维护文档重组落地——分类树(28 归位)+ README 索引 + onboarding/testing 手册 + editor-setup 拆分 + ADR-0001~0004 + 三范本重写(合树重建) * docs: proposals/ 存量校准——8 份状态头对照实现(active/partial/proposal)+ 实现与测试指向 * docs: language/design 退役同步校准——syntax/learning-path/spec-design/regalloc 的 .corespec 引用改指 .cr 内联 + ADR-0001 * docs: 残留校准收尾——z-vision/probabilistic 的 .corespec 引用改指退役后形态(全树仅剩合规叙述) * docs: active 文档树 emoji 状态标记清理(✅❌⚠ 换 [ok]/[fail]/[注意];ASCII 图线与 ✓✗ 排版符号保留) * docs: 存储语义拆四份——memory-model 总览/cache-semantics 七条权威/existence-structure v6 承载/region-model 经典映射(全引用更新指向) * docs: 目录按受众重建——developer/maintainer(design+adr+proposals)/academic + 顶层导航(链接全量修复+README 重写) * docs: pointer-model 重写——三 pass 与实现对齐(Andersen 约束/alloc 位图/逃逸三点/双路径边界)+ 受众定位维护者 * docs: regalloc-cache-mapping 重写——v6 对齐(ENT 区间/home/sweep)+ 两线实现状态(develop 朴素 LS vs v6 分支 CAG)+ 判定四条/CAG 定案保留 * docs: 规约语法定稿 #check/#ensure——syntax 第十章/语法示例统一 + tutorial 警示框修正(未实现实情)+ spec-design 管线一致化(.csp 退役)/链接修复 * docs: ir-op-semantics 目标修订——int 无上限定稿注记(编码层投影视角)/v6 NOD/规约语法定稿;契约表体保持(活对照表,重写风险>收益) * docs: dataflow-design 留档范围标注——§8 环语义标注仍权威(条款 4b 出处),§1-7 早期稿;链接路径修复 * docs: at-intrinsics 受众改开发者 + @fast 语义对齐(性能提示零变化,apx 才是精度授权) * docs: errors.md 真源声明——ast.cr EC_* 为码真源,L 族(词法)例外注记(消息文本真源,码为文档侧) * docs: 收尾批——glossary/z-vision/project-book 正文路径更新 + 全库链接真实相对化(修 docs/ 前缀系统性错误)+ editors/corelsp 互链核验 * docs: tutorial 去 .crasm 残留(改 HIT/unsafe + 决策链接)+ z-vision 条款指向修正(cache-semantics 为七条家) * docs: academic/lattice-theory.md 提炼(v4 定稿:三层映射/规则封闭对象开放/无格承诺/范式映射表)+ spec-design 双受众标注 + README 索引 * docs: tutorial 开发者调性重写——去内部机制术语(region/pass/provenance → 图模型直觉),保留深入链接 * docs: 受众声明与目录统一(developer 目录=开发者/academic=学术,verifier-kernel/cache-semantics 补兼维护者) * docs: z-vision 补定位头注(受众=所有读者) * docs: ADR 补全 0005-0015——git 历史决策事件系统化(内存分层/缓存语义/图锚定区域/region 化/规约 v2/dex-apx/平台桥/int 无上限/类型方向/惰性下沉/HIT) * docs: ADR 更早期补全 0016-0018——arena 模型/并发 GMP 定稿/仓库治理(历史最早段扫描,早期为编码修复无独立决策) * docs: ADR 补全 0019-0020(自举路线启动/IR 双形态)+ ADR-0004 日期修正(拆分实为 2026-05-28,历史全扫发现) * docs: developer/ 深化首批——quickstart/concepts/examples/faq 新建(新手完整体验入口),README 索引更新 * docs: CLAUDE.md 与受众树同步——设计文档列表按 developer/maintainer/academic 重写 + .corespec 退役措辞修正 * docs: 交叉引用错位修复——execution-model 重写后 §5.x 旧节号改新章节(§一/§三)+ 数处旧路径 * docs: syntax 关键字计数修正 37→36(与 lexer.cr lookup_keyword 逐词核对一致) * docs: syntax 头部开发者化——三层映射理论表移除(链 concepts.md),provenance/pass/hw-map 内部词清除,probabilistic 删 * docs: syntax 正文 checker/provenance 内部词改开发者表述 * docs: syntax hw-map 内部词清理(机器形状表述目标平台化) * docs: syntax 补复合赋值(+= -= *= /=)——记号表与优先级表(实现支持,文档遗漏;%= 不支持已注明) * docs: syntax 补 =>(match 臂)与变参(...name:Type)记号——lexer/parser 支持而文档遗漏的真实缺口 * docs: syntax 切片类型注记——2026-09-01 语法定稿为准,运行时 high 界完整化按 TODO 推进(计划优先表述) * docs: concepts 分篇四篇(graph/memory/deploy/spec)+ 入口改索引 + examples 扩 tests/suite 索引(内容创作深化) * docs: README 规则补判据层级(计划优先代码兜底)+ 演进义务声明——开发者文档迭代机制落位 * docs: onboarding 铁律段改人类视角——澄清铁律为 AI 会话规则,人类维护者遵守仓库协作约定(人机一致项保留) * docs: 铁律受众混淆清理(3 处)——ADR/testing/FAQ 改人类视角(CPU 限速=AI 会话规则,人类建议非必须) * docs: 维护文档重组 Task 9 总验收——陈旧交叉引用 95 处(29 文件)修复至最终布局(docs/design→docs/maintainer/design、docs/language→docs/developer、docs/adr→docs/maintainer/adr、glossary/at-intrinsics/verifier-kernel 实际落位)+ 根 README/TODO 同步 + README 索引回填(archive 4 份/ir-schema/verifier/coq/concepts 分篇);验收:失效链接 82→8(余 8 = 3 类有意保留:已删文件的历史叙述/crasm git 历史指针/前向链接)+ 移动文档定位声明 100% · crasm 34≤40 · project-book 64≤150 · 分支零 src 改动 * docs: 维护文档重组 Task 9 补遗——4 份未移动文档补定位头注(coq/README、ir-schema×2、verifier/kernel-spec;计划全局约束 6 的唯一未执行项),格式照 developer/errors.md 既有约定 --- CLAUDE.md | 6 +- README.md | 2 +- TODO.md | 24 +- docs/README.md | 52 +++ docs/academic/cache-semantics.md | 98 +++++ docs/academic/lattice-theory.md | 90 ++++ docs/{ => academic}/verifier-kernel.md | 4 +- docs/{ => archive}/compcert-reference.md | 8 +- .../{ => archive}/compcert-round4-findings.md | 8 +- .../memory-model-capability-lattice.md | 22 +- .../numeric-migration-inventory.md | 4 +- docs/coq/README.md | 2 + docs/crasm.md | 205 --------- docs/{ => developer}/at-intrinsics.md | 4 +- docs/developer/concepts.md | 24 ++ docs/developer/concepts/deploy.md | 41 ++ docs/developer/concepts/graph.md | 31 ++ docs/developer/concepts/memory.md | 39 ++ docs/developer/concepts/spec.md | 42 ++ .../{editor-setup.md => developer/editors.md} | 45 +- docs/{error-codes.md => developer/errors.md} | 6 +- docs/developer/examples.md | 48 +++ docs/developer/faq.md | 49 +++ docs/developer/quickstart.md | 67 +++ .../syntax.md} | 84 ++-- .../tutorial.md} | 52 +-- docs/execution-model.md | 282 ------------- docs/glossary.md | 266 +++--------- docs/ir-schema/coreir-schema.md | 4 +- docs/ir-schema/corespecir-schema.md | 4 +- docs/maintainer/adr/README.md | 48 +++ .../adr/adr-0001-corespec-crasm-retired.md | 30 ++ .../adr/adr-0002-ccr-v6-segment-table.md | 33 ++ .../adr/adr-0003-selfhost-elf-direct-emit.md | 33 ++ .../adr/adr-0004-corec-corearch-split.md | 31 ++ .../adr/adr-0005-memory-model-layers-v4.md | 23 + .../adr/adr-0006-cache-semantics.md | 21 + .../adr/adr-0007-graph-anchored-regions.md | 23 + .../adr/adr-0008-execution-model-regions.md | 22 + .../maintainer/adr/adr-0009-spec-system-v2.md | 22 + .../adr/adr-0010-numeric-dex-apx.md | 22 + .../adr/adr-0011-platform-abstract.md | 20 + docs/maintainer/adr/adr-0012-int-unbounded.md | 22 + .../adr/adr-0013-type-system-direction.md | 21 + .../adr/adr-0014-lazy-compile-time.md | 19 + .../adr-0015-hit-hardware-interface-table.md | 22 + docs/maintainer/adr/adr-0016-arena-model.md | 22 + .../adr/adr-0017-concurrency-gmp.md | 22 + .../adr/adr-0018-repo-governance.md | 20 + .../adr/adr-0019-bootstrap-route.md | 22 + docs/maintainer/adr/adr-0020-ir-dual-form.md | 22 + docs/maintainer/design/corelsp.md | 54 +++ docs/maintainer/design/crasm.md | 34 ++ .../design}/dataflow-design.md | 9 +- docs/maintainer/design/execution-model.md | 221 ++++++++++ docs/maintainer/design/existence-structure.md | 131 ++++++ .../design}/ir-op-semantics.md | 80 ++-- docs/maintainer/design/memory-model.md | 48 +++ docs/maintainer/design/pointer-model.md | 137 ++++++ .../design/regalloc-cache-mapping.md | 132 ++++++ docs/maintainer/design/region-model.md | 161 +++++++ docs/{ => maintainer/design}/spec-design.md | 44 +- docs/maintainer/onboarding.md | 119 ++++++ docs/{ => maintainer/proposals}/comptime.md | 2 + .../{ => maintainer/proposals}/concurrency.md | 2 + .../{ => maintainer/proposals}/distributed.md | 2 + .../proposals}/dynamic-typing.md | 2 + docs/{ => maintainer/proposals}/ffi.md | 2 + docs/{ => maintainer/proposals}/generics.md | 2 + docs/{ => maintainer/proposals}/lazy.md | 2 + .../proposals}/probabilistic.md | 8 +- docs/maintainer/testing.md | 132 ++++++ docs/memory-model.md | 393 ------------------ docs/pointer-model.md | 291 ------------- docs/project-book.md | 6 +- docs/pseudocode/compiler/ast.md | 2 +- docs/regalloc-cache-mapping.md | 130 ------ docs/verifier/kernel-spec.md | 2 + docs/z-vision.md | 18 +- src/compiler/ast.cr | 2 +- src/compiler/ptr_analysis.cr | 2 +- 81 files changed, 2582 insertions(+), 1721 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/academic/cache-semantics.md create mode 100644 docs/academic/lattice-theory.md rename docs/{ => academic}/verifier-kernel.md (95%) rename docs/{ => archive}/compcert-reference.md (97%) rename docs/{ => archive}/compcert-round4-findings.md (97%) rename docs/{ => archive}/memory-model-capability-lattice.md (94%) rename docs/{ => archive}/numeric-migration-inventory.md (99%) delete mode 100644 docs/crasm.md rename docs/{ => developer}/at-intrinsics.md (88%) create mode 100644 docs/developer/concepts.md create mode 100644 docs/developer/concepts/deploy.md create mode 100644 docs/developer/concepts/graph.md create mode 100644 docs/developer/concepts/memory.md create mode 100644 docs/developer/concepts/spec.md rename docs/{editor-setup.md => developer/editors.md} (60%) rename docs/{error-codes.md => developer/errors.md} (97%) create mode 100644 docs/developer/examples.md create mode 100644 docs/developer/faq.md create mode 100644 docs/developer/quickstart.md rename docs/{language-syntax.md => developer/syntax.md} (78%) rename docs/{learning-path.md => developer/tutorial.md} (70%) delete mode 100644 docs/execution-model.md create mode 100644 docs/maintainer/adr/README.md create mode 100644 docs/maintainer/adr/adr-0001-corespec-crasm-retired.md create mode 100644 docs/maintainer/adr/adr-0002-ccr-v6-segment-table.md create mode 100644 docs/maintainer/adr/adr-0003-selfhost-elf-direct-emit.md create mode 100644 docs/maintainer/adr/adr-0004-corec-corearch-split.md create mode 100644 docs/maintainer/adr/adr-0005-memory-model-layers-v4.md create mode 100644 docs/maintainer/adr/adr-0006-cache-semantics.md create mode 100644 docs/maintainer/adr/adr-0007-graph-anchored-regions.md create mode 100644 docs/maintainer/adr/adr-0008-execution-model-regions.md create mode 100644 docs/maintainer/adr/adr-0009-spec-system-v2.md create mode 100644 docs/maintainer/adr/adr-0010-numeric-dex-apx.md create mode 100644 docs/maintainer/adr/adr-0011-platform-abstract.md create mode 100644 docs/maintainer/adr/adr-0012-int-unbounded.md create mode 100644 docs/maintainer/adr/adr-0013-type-system-direction.md create mode 100644 docs/maintainer/adr/adr-0014-lazy-compile-time.md create mode 100644 docs/maintainer/adr/adr-0015-hit-hardware-interface-table.md create mode 100644 docs/maintainer/adr/adr-0016-arena-model.md create mode 100644 docs/maintainer/adr/adr-0017-concurrency-gmp.md create mode 100644 docs/maintainer/adr/adr-0018-repo-governance.md create mode 100644 docs/maintainer/adr/adr-0019-bootstrap-route.md create mode 100644 docs/maintainer/adr/adr-0020-ir-dual-form.md create mode 100644 docs/maintainer/design/corelsp.md create mode 100644 docs/maintainer/design/crasm.md rename docs/{ => maintainer/design}/dataflow-design.md (90%) create mode 100644 docs/maintainer/design/execution-model.md create mode 100644 docs/maintainer/design/existence-structure.md rename docs/{ => maintainer/design}/ir-op-semantics.md (78%) create mode 100644 docs/maintainer/design/memory-model.md create mode 100644 docs/maintainer/design/pointer-model.md create mode 100644 docs/maintainer/design/regalloc-cache-mapping.md create mode 100644 docs/maintainer/design/region-model.md rename docs/{ => maintainer/design}/spec-design.md (90%) create mode 100644 docs/maintainer/onboarding.md rename docs/{ => maintainer/proposals}/comptime.md (91%) rename docs/{ => maintainer/proposals}/concurrency.md (95%) rename docs/{ => maintainer/proposals}/distributed.md (96%) rename docs/{ => maintainer/proposals}/dynamic-typing.md (95%) rename docs/{ => maintainer/proposals}/ffi.md (91%) rename docs/{ => maintainer/proposals}/generics.md (94%) rename docs/{ => maintainer/proposals}/lazy.md (94%) rename docs/{ => maintainer/proposals}/probabilistic.md (85%) create mode 100644 docs/maintainer/testing.md delete mode 100644 docs/memory-model.md delete mode 100644 docs/pointer-model.md delete mode 100644 docs/regalloc-cache-mapping.md diff --git a/CLAUDE.md b/CLAUDE.md index 0adc87e4..1679c603 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -249,10 +249,10 @@ Formal EBNF definitions in `grammar/`: Design documents (Chinese): - `docs/project-book.md` — Philosophy, IR system, formal verification architecture - `docs/dataflow-design.md` — Dataflow execution model design -- `docs/language-syntax.md` — Language syntax reference +- `docs/developer/syntax.md` — Language syntax reference - `docs/execution-model.md` — Execution model -- `docs/memory-model.md` — Arena memory model design -- `docs/error-codes.md` — Compiler error code reference +- `docs/maintainer/design/memory-model.md` — Arena memory model design +- `docs/developer/errors.md` — Compiler error code reference ## Known Issues diff --git a/README.md b/README.md index 1af556b1..01113d64 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ | | `@hotpatch` 滚动更新(IR_HOTPATCH_ROUTE + SIGHUP 热加载) | 完成 | | | 惰性求值(IR_LAZY_THUNK/FORCE,调用级) | 部分(thunk/force 为值搬运,无实际延迟) | | | 控制流自动惰性(编译期指令下沉路线) | 设计定案,待实现 | -| **汇编层** | ~~`.crasm` 统一汇编抽象层~~——**2026-09-05 退役**,被硬件接口表(HIT)吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern | 已退役(docs/crasm.md 保留为历史) | +| **汇编层** | ~~`.crasm` 统一汇编抽象层~~——**2026-09-05 退役**,被硬件接口表(HIT)吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern | 已退役(docs/maintainer/design/crasm.md 保留为历史) | | **语义检查** | 名字解析 + 类型检查、结构化错误码 + 源码定位 | 完成 | | **模块系统** | `import`、`fileid`、`@project`、`_import.cr`、依赖裁剪 | 完成 | | **标准库** | `io.cr` / `cli.cr` / `toml.cr` | 完成 | diff --git a/TODO.md b/TODO.md index 9cae4c0c..ca41e8c8 100644 --- a/TODO.md +++ b/TODO.md @@ -657,19 +657,19 @@ ## 架构规划 ### 指针安全模型 -见 `docs/pointer-model.md`。裸指针 + HDFG provenance 推导,编译器自动验证,退路 `unsafe`。 +见 `docs/maintainer/design/pointer-model.md`。裸指针 + HDFG provenance 推导,编译器自动验证,退路 `unsafe`。 三 pass:PointerAnalysis、RegionCheck、ProvenanceVerify — 全部实现。 ### Arena 内存模型 -见 `docs/memory-model.md`。已完整实现。堆按 HDFG 子图划分独立 Arena,指针碰撞分配, +见 `docs/maintainer/design/memory-model.md`。已完整实现。堆按 HDFG 子图划分独立 Arena,指针碰撞分配, 游标重置回收。Arena 边界对应 HDFG 子图边界。 ### 文档更新 -- `docs/memory-model.md` — 设计文档(待同步实现细节) -- **docs 重组合并时的连带改点(2026-09-11 登记,`feature/docs-reorg` 已推送 `9eab0757` 待合)**:该分支把 `docs/error-codes.md` → `docs/developer/errors.md`、`docs/compcert-reference.md` → `docs/archive/compcert-reference.md`,故**合并该分支时必须同步两处 src 注释**:`src/compiler/ast.cr:315`、`src/compiler/ptr_analysis.cr:235`(在本线/当前 develop 上这两个路径**仍然正确**,故不在 docs 分支内改——避免把 src/ 拉进 docs 分支,也避免在本线留下悬空引用)。 -- `docs/pointer-model.md` — 指针安全完整设计 -- `docs/language-syntax.md` — 指针、@ 内建语法已更新 -- `docs/at-intrinsics.md` — @ 内建原语完整规格 +- `docs/maintainer/design/memory-model.md` — 设计文档(待同步实现细节) +- **docs 重组合并时的连带改点(2026-09-11 登记,`feature/docs-reorg` 已推送 `9eab0757` 待合)**:该分支把 `docs/developer/errors.md` → `docs/developer/errors.md`、`docs/archive/compcert-reference.md` → `docs/archive/compcert-reference.md`,故**合并该分支时必须同步两处 src 注释**:`src/compiler/ast.cr:315`、`src/compiler/ptr_analysis.cr:235`(在本线/当前 develop 上这两个路径**仍然正确**,故不在 docs 分支内改——避免把 src/ 拉进 docs 分支,也避免在本线留下悬空引用)。 +- `docs/maintainer/design/pointer-model.md` — 指针安全完整设计 +- `docs/developer/syntax.md` — 指针、@ 内建语法已更新 +- `docs/developer/at-intrinsics.md` — @ 内建原语完整规格 ### 6. 同步源码修改到伪代码文档(2026-08-09 记) - 背景:伪代码(docs/pseudocode/)基于源码快照翻译;以下源码变更后对应文档未同步 @@ -758,7 +758,7 @@ - VS Code 客户端(协议层已编辑器无关) ### 设计定稿待实现(补挂账,2026-08-28 审计) -- **概率性 pass**(probabilistic-pass,2026-07-30 已批准):src/ 零实现,仅文档(`docs/probabilistic.md`)——范式相关,优先级低,待排期 +- **概率性 pass**(probabilistic-pass,2026-07-30 已批准):src/ 零实现,仅文档(`docs/maintainer/proposals/probabilistic.md`)——范式相关,优先级低,待排期 - **硬件映射表**(hw-map,2026-08-23 已批准):MMIO 设备语义表 + 投影表——crasm 依赖已解除(2026-09-05 crasm 退役);与硬件接口表(HIT)同族——HIT = 指令/运行层接口(`specs/2026-09-05-hardware-interface-table.md`),hw-map = 设备层接口,同构(语义表范式无关 + 投影表实例)。实现仍待排期。关联 `docs/superpowers/specs/2026-08-23-hw-map-design.md` - **平台桥抽象**(platform-abstract,2026-08-16 定案):设计定案待实现——语义接口 + 后端实现原则;程序 IO = 流转导器。关联 `docs/superpowers/specs/2026-08-16-platform-abstract-design.md` - ~~**错误码体系**(error-codes,2026-08-08 已批准)~~(2026-09-05 复核:体系完整——ast.cr 定义 `EC_R_*`,checker.cr:2080/2095 发射 R002(编译期越界),main.cr:148 硬错误路径(R002/TK05/TK06 拦编译),diag.cr `error_cat_prefix`/`pad_diag_num` 完整打印 R001-R004;BC17「全仓库无引用」为历史旧况,已过时) @@ -872,9 +872,9 @@ corearch 恒空跑,零产物差异)。设计定稿 `docs/superpowers/specs/2 - **序列化/打印自动**(serialize-auto,2026-08-30):从接口声明自动生成序列化代码与打印格式(serde 式派生;`dex_str` 已是雏形)——类型驱动代码生成家族 ### ~~.crasm 统一汇编抽象层~~(2026-08-09 记|**2026-09-05 退役**) -- **退役(2026-09-05)**:独立 .crasm 格式取消——绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表),被**硬件接口表(HIT)**吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件;事件可读形态 = v6 NOD 文本 dump。正式声明 = `docs/crasm.md` 状态行(已废弃,保留为历史记录);依赖解除引用见本文件「硬件映射表」与「宽度类型移出语言」两条。**原里程碑方向全部作废(保留下文备查),无实施计划。** +- **退役(2026-09-05)**:独立 .crasm 格式取消——绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表),被**硬件接口表(HIT)**吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件;事件可读形态 = v6 NOD 文本 dump。正式声明 = `docs/maintainer/design/crasm.md` 状态行(已废弃,保留为历史记录);依赖解除引用见本文件「硬件映射表」与「宽度类型移出语言」两条。**原里程碑方向全部作废(保留下文备查),无实施计划。** - (历史)目标:内核路线(project-book 第五阶段)的汇编级能力——MMIO、特权指令、中断。跨平台统一指令集 + 无限虚拟寄存器 + 平台映射表,寄存器分配按 v4 方向(缓存语义映射实例,`docs/regalloc-cache-mapping.md`——无限虚拟寄存器 + 平台映射表正是映射实例形态;现 `alloc_registers` 线性扫描器为升级起点) -- (历史)现状:设计已批准(2026-08-08 brainstorming 逐节确认),2026-08-09 整理为正式文档 `docs/crasm.md`;实现从未落地(lexer/parser 无 asm 语法)——2026-09-05 整体退役 +- (历史)现状:设计已批准(2026-08-08 brainstorming 逐节确认),2026-08-09 整理为正式文档 `docs/maintainer/design/crasm.md`;实现从未落地(lexer/parser 无 asm 语法)——2026-09-05 整体退役 - ~~方向(按里程碑顺序):~~ - ~~1. `.crasm` 词法/解析(结构化指令 → AST 复用)~~ - ~~2. 寄存器生命周期 pass + 测试(未初始化读/重复写/生命周期逃逸/宽度一致/分支一致性)~~ @@ -883,9 +883,9 @@ corearch 恒空跑,零产物差异)。设计定稿 `docs/superpowers/specs/2 - ~~5. .cr extern 接口接线 + 端到端~~ - ~~6. (后补)ARM64/RISC-V 映射表~~ - 明确不做(YAGNI,退役前定案保留):模拟器/调试器、C 生态兼容、指令级时序验证、特权副作用验证(隔离,人工保证) -- 参考:`docs/crasm.md`(**已废弃 2026-09-05**)、`docs/superpowers/specs/2026-08-08-crasm-design.md`(批准记录,历史) +- 参考:`docs/maintainer/design/crasm.md`(**已废弃 2026-09-05**)、`docs/superpowers/specs/2026-08-08-crasm-design.md`(批准记录,历史) -### 对照 CompCert 审查发现的未修复 bug(2026-08-11 记,详见 docs/compcert-reference.md) +### 对照 CompCert 审查发现的未修复 bug(2026-08-11 记,详见 docs/archive/compcert-reference.md) - ~~**region_check 误报(B11)**:deref 读出的 int 值被当作指针做区域逃逸检查——`v := *p; return v;` 被拦(预先存在,pts 语义需按类型过滤)~~(2026-08-29 已修:deref/return/store 仅对指针类型执行 provenance/区域逃逸检查;`test_deref_loaded_int_is_not_pointer_escape` 覆盖原始误报) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..857f982a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,52 @@ +# Core 文档中心 + +> 定位:受众 = 所有读者(入口);状态 = active。 +> 分类规则:一级 = **受众**(developer/maintainer/academic),二级 = 主题;每份文档头部有定位声明(受众/状态/真源)。 +> 一份文档一个职责。 +> **判据层级(计划优先,代码兜底)**:设计定稿与计划(specs/plans)优先于代码——实现未跟上设计时文档保持定稿、标注"设计态/按计划推进";仅在计划未覆盖的细节(记号/关键字等实现真源类内容)以源码为准。 +> **演进义务**:语言或实现变化时,相关文档头部状态与正文随之更新(active 变 superseded、设计态转已实现等);开发者文档随语言演进迭代。 + +## 开发者(developer/)——用 Core 写程序 + +- [quickstart.md](developer/quickstart.md)— 快速开始(构建/第一个程序) +- [tutorial.md](developer/tutorial.md)— 渐进学习路径 +- [concepts.md](developer/concepts.md)— 核心概念导览(分篇:[graph](developer/concepts/graph.md)/[memory](developer/concepts/memory.md)/[deploy](developer/concepts/deploy.md)/[spec](developer/concepts/spec.md)) +- [syntax.md](developer/syntax.md)— 语言语法参考 +- [examples.md](developer/examples.md)— 示例集 +- [faq.md](developer/faq.md)— 常见问题 +- [errors.md](developer/errors.md)— 错误码参考 +- [editors.md](developer/editors.md)— 编辑器与 LSP 配置 +- [at-intrinsics.md](developer/at-intrinsics.md)— @ 内建原语 + +## 维护者(maintainer/)——改编译器 + +**上手**:[onboarding.md](maintainer/onboarding.md)(先读)→ [testing.md](maintainer/testing.md)(回归操作) + +**设计参考**(maintainer/design/): +- 执行模型 / 图与 region:[execution-model.md](maintainer/design/execution-model.md)、[dataflow-design.md](maintainer/design/dataflow-design.md)(早期稿,已被取代,留档) +- 存储语义:[memory-model.md](maintainer/design/memory-model.md)(总览)、[cache-semantics.md](academic/cache-semantics.md)(七条权威,跨 academic)、[existence-structure.md](maintainer/design/existence-structure.md)(v6 承载)、[region-model.md](maintainer/design/region-model.md)(经典映射) +- 指针与验证 pass:[pointer-model.md](maintainer/design/pointer-model.md)、[regalloc-cache-mapping.md](maintainer/design/regalloc-cache-mapping.md) +- 规约系统:[spec-design.md](maintainer/design/spec-design.md);IR 操作语义:[ir-op-semantics.md](maintainer/design/ir-op-semantics.md);corelsp:[corelsp.md](maintainer/design/corelsp.md);已废弃:[crasm.md](maintainer/design/crasm.md) + +**决策记录**:[adr/](maintainer/adr/README.md)— ADR-0001 起 + +**特性提案**(maintainer/proposals/):generics/concurrency/dynamic-typing/comptime/ffi/lazy/distributed/probabilistic(状态见各文件头注) + +## 学术(academic/)——验证/理论读者 + +- [cache-semantics.md](academic/cache-semantics.md)— 缓存语义七条(存储语义本体,权威) +- [lattice-theory.md](academic/lattice-theory.md)— 三层映射理论定稿(图 → 格 → 编码) +- [verifier-kernel.md](academic/verifier-kernel.md)— 验证内核选型(CIC 信任根) + +## 顶层 + +- [project-book.md](project-book.md)— 项目定位导航 +- [glossary.md](glossary.md)— 架构术语表(跨受众) +- [z-vision.md](z-vision.md)— 愿景 + +## 档案与工具链目录(不参与受众分类) + +- archive/(任务产物):[compcert-reference.md](archive/compcert-reference.md)、[compcert-round4-findings.md](archive/compcert-round4-findings.md)、[numeric-migration-inventory.md](archive/numeric-migration-inventory.md)、[memory-model-capability-lattice.md](archive/memory-model-capability-lattice.md) +- [pseudocode/](pseudocode/README.md)(TDD 交付物)、[superpowers/](superpowers/)(specs + plans 工作目录) +- [ir-schema/](ir-schema/):[coreir-schema.md](ir-schema/coreir-schema.md)、[corespecir-schema.md](ir-schema/corespecir-schema.md) +- [verifier/](verifier/):[kernel-spec.md](verifier/kernel-spec.md);[coq/](coq/README.md):Coq 验证 stdlib 纯函数 diff --git a/docs/academic/cache-semantics.md b/docs/academic/cache-semantics.md new file mode 100644 index 00000000..0a01df13 --- /dev/null +++ b/docs/academic/cache-semantics.md @@ -0,0 +1,98 @@ +# 缓存语义:存储语义本体(权威条款) + +> 定位:受众 = 学术(条款消费方:验证器实现者/维护者);状态 = active。 +> 本文件是 Core 存储语义本体的**唯一权威**——七条条款为定义性条款,将来供验证器(翻译桥/CIC 内核)消费;实现与文档冲突时以本文档为准(设计文档优先于代码)。 +> 设计依据:2026-08-15-cache-semantics-design;v4 晋升与归档讨论见 docs/archive/memory-model-capability-lattice.md。 +> 分层位置:三层映射链(图 → 格 → 编码)的「格」层语义本体;存在结构载体(IR 编码)见 docs/maintainer/design/existence-structure.md;经典映射(区域/字节)见 docs/maintainer/design/region-model.md;总览导航见 docs/maintainer/design/memory-model.md。 + +--- + +## 零、为什么是缓存 + +Core 的存储语义本体是**缓存**——范式无关的存储抽象,不是字节内存,也不是寄存器: + +- 传统编译器的内存模型从机器出发(字节寻址 + 权限),范式绑死 +- 缓存语义从"值如何产生与保留"出发:值 = 配方(可重算),存储 = 配方结果的保留(可驱逐) +- 机器内存只是缓存的一个实例(经典映射);量子存储、寄存器都是缓存的其他实例——范式映射表由此成立 + +实现从来就是缓存语义(HDFG):节点 = 配方(值的产生方式),边 = 值流,state edges = 顺序约束,驱逐/再生 = 丢令牌/重跑节点。此前的文档层错误是把 CompCert v2 字节模型当语义本体——已纠偏(2026-08-15,零代码改动)。 + +--- + +## 一、七条条款(权威) + +1. **值 = 配方**。条目的语义 = (产生它的图节点, 输入边)。不存在独立于配方的"存储事实"。 +2. **驱逐不变量**。驱逐图内任意条目(丢弃存储物、保留配方)不改变可观测语义——只影响性能,不影响正确性。形式化目标:⟦G ∖ storage(e)⟧ = ⟦G⟧。这是"缓存语义"的定义性质。 +3. **再生等价**。重跑配方节点产生的值与原条目可观测等价。这是条款 2 的机制保证,也是"任何范式都能换映射"的语义依据。 +4. **边界公理**。图边界输入(MMIO/FFI/输入/测量)是无配方条目(边界类),语义上是符号常量。语法层叫 `unsafe`(边界标注),语义层只叫"边界"。边界之外才需要 state,边界之内全是可重算的。 +4b. **图内不可重算(v4 并入,M4 输入)**。执行标注空间(神谕 / BSS 实数 / FIXPT 声明 / 模糊融合,见 docs/maintainer/design/dataflow-design.md §8)存在**图内**无配方条目——非边界、不可重算。驱逐不变量/再生等价对其不成立(重跑神谕 ≠ 原结果):其存储必须持久(home 保有材料),驱逐必须写回;身份 = 产生节点(图节点身份)。边界条目与图内不可重算条目统一为「无配方条目」规则,一视同仁、不按范式枚举。 +5. **赋值 = 版本化**。`x = x + 1` 在语义上 = "x₁ 创建、x₀ 失效、绑定移动",不是修改内存单元。顺序约束由 state edges 表达。"可变"不是内存单元的属性,是绑定可移动的许可。 +6. **地址 = 映射**。`&x` = (条目标识, 偏移),字节地址只是经典投影,不是语义对象。指针算术合法性由条目标识 + 偏移域验证(现有 provenance 三 pass 保留不动)。 +7. **映射实例正确性**。区域/arena/字节权限(CompCert v2)是经典映射实例,其正确性标准 = 保持条款 1–6 的可观测语义。 + +--- + +## 二、条款细读 + +### 条款 1:值 = 配方 + +"条目"是存储的一项,它的语义完全由产生方式决定。不存在"内存单元里存了什么"这种脱离产生方式的存储事实——问"这个值是什么"永远可以还原为"它是哪个图节点、用什么输入产生的"。这对验证的含义:验证器不需要为存储建模,只需要为配方(图)建模。 + +### 条款 2 + 3:驱逐与再生(缓存的两面) + +驱逐不变量是"缓存语义"的定义性质:任何存储都可以被丢弃,只要配方还在。再生成等价是机制保证:重跑配方得到可观测等价的值。 + +二者合起来给出编译器优化的正确性依据: +- 寄存器分配中把值溢出到 home(材料移走)= 驱逐 + 按需再生 +- rematerialization(重算代替装载)= 条款 3 的直接应用 +- 任何"缓存"层的动作都不改变语义——性能是唯一代价维度 + +### 条款 4 + 4b:无配方条目 + +两类条目没有配方,驱逐必须写回、不可再生: + +| 类别 | 例子 | 语法层标注 | +|---|---|---| +| 边界(图外输入) | MMIO、FFI 返回值、输入、测量 | unsafe(边界标注) | +| 图内不可重算 | 神谕、BSS 实数、FIXPT 声明、模糊融合 | 执行标注(见 dataflow-design §8) | + +统一规则:无配方条目必须有 home(持久位置),驱逐必写回;身份 = 产生节点。不按范式枚举——新增范式时无配方规则不变。 + +### 条款 5:赋值 = 版本化 + +非 SSA 语言中变量的多次赋值在语义上是版本序列:x₁ 创建、x₀ 失效、绑定移动。这解释了三件事: + +- "可变"不是内存单元的属性,是绑定可移动的许可(语义层不承诺任何内存单元) +- 顺序约束不属于存储,属于 state edges(副作用链) +- 存在区间(live range)是版本级别的——每条版本有自己的定值点与最后使用点(IR 载体见 existence-structure.md) + +### 条款 6:地址 = 映射 + +`&x` 在语义层是 (条目标识, 偏移)——字节地址是经典投影(编码层对象)。由此指针算术的合法性 = 条目标识 + 偏移域验证,与类型无关;provenance(归属)与 offset 是图上本有的信息(推论 C,见 region-model.md)。 + +### 条款 7:映射实例正确性 + +任何映射实例(区域/arena/字节权限/寄存器)的正确性标准是同一个:保持条款 1–6 的可观测语义。由此"范式映射表"= 映射正确性定理表——经典行 = 证明缓存语义 ⊇ 字节内存实现(CompCert 式语义保鲜);量子行 = 将来证明缓存语义 ⊇ 量子存储实现。语义保鲜从"编译正确性定理"升级为"映射正确性定理"。 + +--- + +## 三、推论:范式映射表 + +| 行 | 映射实例 | 正确性定理 | +|---|---|---| +| 经典 | 字节内存 + 权限(CompCert v2) | 缓存语义 ⊇ 字节内存实现 | +| 寄存器 | 寄存器缓存 + home(栈槽) | 判定四条(见 regalloc-cache-mapping.md) | +| 量子(将来) | 量子存储 | 缓存语义 ⊇ 量子存储实现 | + +每行定理共享条款 1–6——新范式不需要新的存储语义,只需要新映射与新证明。 + +--- + +## 四、关联 + +- 语义本体总览:docs/maintainer/design/memory-model.md +- IR 载体:v6 存在结构(ENT/NOD/REG)— docs/maintainer/design/existence-structure.md +- 经典映射:docs/maintainer/design/region-model.md(区域/arena/权限) +- 寄存器实例:docs/maintainer/design/regalloc-cache-mapping.md(判定四条) +- 设计依据:docs/superpowers/specs/2026-08-15-cache-semantics-design.md +- 术语索引:docs/glossary.md §四(缓存语义术语) diff --git a/docs/academic/lattice-theory.md b/docs/academic/lattice-theory.md new file mode 100644 index 00000000..91a8a281 --- /dev/null +++ b/docs/academic/lattice-theory.md @@ -0,0 +1,90 @@ +# 三层映射理论:图 → 格 → 编码(定稿) + +> 定位:受众 = 学术(验证/理论读者,兼维护者);状态 = active(v3 立、v4 晋升定稿,2026-08-27 第三层更名)。 +> 本文件 = 三层映射理论的**定稿提炼**;原始论证与演进(抽象升华系列/v1-v4 讨论)见 docs/archive/memory-model-capability-lattice.md;存储半边条款(cache-semantics 七条)见 docs/academic/cache-semantics.md;执行标注空间见 docs/maintainer/design/dataflow-design.md §8。 + +--- + +## 一、三层映射 + +``` +不同范式 / 计算语义 + ↓ + 图(HDFG) —— 关系空间:发生什么 + ↓ 图 → 格映射 + 格(内存模型) —— 状态/存储空间:如何存在 + ↓ 格 → 编码映射 + 编码 —— 物理编码空间:如何实现 +``` + +### 为什么需要格这一层 + +图回答"这个计算世界中有什么关系、什么可能发生";格回答"这些东西如何成为具有状态和位置的可实现存在"。若图 → 编码直连,大量"如何存在"的问题(节点是什么内存对象、关系怎么保存状态、两个可能分支的状态怎么共存、非因果怎么落地址、节点生命周期)都会被迫塞进一个万能的图 → 机器码映射器。 + +### 三层的数学定位 + +- **图 = 关系空间**:表达可超越经典图灵机执行模型的语义(非因果、模糊、超图灵)——**超图灵性属于图,不属于格** +- **格 = 状态/存储空间**:提供足够一般的状态模型使图能被映射下来;格不需要理解超图灵,只需让可被当前执行环境实现的部分拥有状态/位置/关系/可访问性 +- **编码 = 物理编码空间**:把格编码成具体机器的地址、字节、指令、位模式(2026-08-27 更名:原"二进制"硬编码经典惯例,违反零硬件惯例——量子/模拟/光学皆非二进制) +- **格不重新引入程序顺序**:传统 IR(SSA/LLVM)本质上仍是"另一种程序"——按执行路径组织;格 = 中间存在空间,不必重新引入程序顺序 + +### 跨范式自由度(格的价值) + +- 不同范式(命令式/函数式/数据流/并发/事件驱动/非确定/模糊/超图灵)的差异首先被图吸收(图层标注空间) +- 格不被某个有限、固定的传统计算范式预先封顶——可构造表达空间无界 +- 真正的问题是"C 的语义能否找到一个格映射",而不是"格能不能表示 C"——**格本身不需要知道范式是什么** + +## 二、术语澄清:格的两义 + +| 术语 | 含义 | +|---|---| +| 格(层) | 内存模型 = 中间存在空间(第二层映射)——本文主体 | +| 格(Lattice) | 层内组织代数成分(偏序 + join/meet)——**v4:映射参数,非层本体承诺** | + +## 三、定稿原则 + +### 规则封闭、对象开放(v4 定稿) + +- **规则(封闭,永不新建)**:缓存语义七条 + 最弱理论(关系层面)。新范式出现时层本体一字不改——**规则封闭 = 维护有界** +- **对象(开放,无界)**:条目、配方、标注。任何范式的值都装得下(量子振幅、FIXPT 不动点、模糊隶属度)——**对象开放 = 表达无界** +- 一切结构是图层导出或映射参数:共存关系是图层导出(图活性),不是格层承诺;格代数、区域、字节、寄存器都是映射实例 + +### 无格承诺(v4 定稿) + +- 任何具体格 = 签名 + 恒等式 → 恒等式封顶表达空间。解法不是选更宽的格,而是**把语义写在比格更弱的理论里**——最弱理论 = 最多模型 = 无界 +- 存储判定需要的最弱结构 = 共存关系(对称关系,无传递性——a 与 b 共存、b 与 c 共存,a 与 c 不必共存),连预序公理都不需要 +- 格(Lattice 代数)降级为**映射实例的组织参数**:经典行用乘积格、量子行用别的、未来范式用未知格——每范式引入自己的代数都是参数 +- 数学原则:**用函子式/普适构造定义层,不用情形分析**;对象由普适性质定义(直到同构唯一),不选具体结构 +- 验证视角:判定/定理写在最弱理论里,对一切格成立;CIC 内核里任何具体格都是该理论的一个模型——健全性对理论成立,就对所有模型成立 + +## 四、新范式只需三件事(不是"新建格") + +1. **图标注空间加标签**——统一空间,加标签不是新机制(FUZZY/FIXPT/Oracle/CORE 已是标签) +2. **CIC 内部定义新结构**——一个逻辑,健全性继承 +3. **硬件映射**——不可避免的工程,但是映射工作,不是层工作 + +判定规则是**条目泛型**的:操作"条目"这个类别(位置 + 配方 + 存在),不读值的内容——新条目种类不需要新判定规则。 + +## 五、范式映射表(推论) + +范式映射表 = 映射正确性定理表(cache-semantics 条款 7 的推论): + +| 行 | 映射实例 | 正确性定理 | +|---|---|---| +| 经典 | 字节内存 + 权限 | 缓存语义 ⊇ 字节内存实现 | +| 寄存器 | 寄存器缓存 + home | 判定四条(regalloc-cache-mapping) | +| 量子(将来) | 量子存储 | 缓存语义 ⊇ 量子存储实现 | + +每行定理共享七条——新范式不需要新的存储语义,只需要新映射与新证明。 + +## 六、验证主线 + +Graph → Lattice 映射通用性清单(每项通过 = 三层链成立的证据;原始清单见 archive 备忘 §十二):寄存器分配判定以纯格对象写出(无路径结构)= "格不重引入顺序"判据的实例化;存储半边(缓存七条)已定稿,执行半边(执行标注空间 = dataflow-design §8)为对应物。 + +## 七、关联 + +- 原始论证与演进:docs/archive/memory-model-capability-lattice.md(v1-v4 备忘,讨论过程档案) +- 存储半边条款:docs/academic/cache-semantics.md(七条权威) +- 执行标注空间:docs/maintainer/design/dataflow-design.md §8(仍权威) +- IR 载体:docs/maintainer/design/existence-structure.md(v6 ENT/NOD/REG) +- 映射实例:docs/maintainer/design/region-model.md(经典映射)、regalloc-cache-mapping.md(寄存器行) diff --git a/docs/verifier-kernel.md b/docs/academic/verifier-kernel.md similarity index 95% rename from docs/verifier-kernel.md rename to docs/academic/verifier-kernel.md index b0e3c0f3..cf6ecdef 100644 --- a/docs/verifier-kernel.md +++ b/docs/academic/verifier-kernel.md @@ -1,11 +1,13 @@ # 验证内核选型与融合架构(Verifier Kernel) +> 定位:受众 = 学术(兼维护者);状态 = active + > 信任根 = CIC 内核。表达力 = CIC + 公理 + HoTT 库。自动化 = 证书形态(计算在外、健全性在内)。 > 融合各家之长,不选边——每个维度取最优,其他体系全部变成方法贡献。 ## 一、问题 -规约系统(`docs/spec-design.md`)需要 Coq 级别的表达力(依赖类型/归纳/高阶量词)。表达力由**验证内核**承载——内核是信任根:**内核有 bug = 一切证明皆空**。本文档记录内核选型论证与融合架构决策(2026-08-10)。 +规约系统(`docs/maintainer/design/spec-design.md`)需要 Coq 级别的表达力(依赖类型/归纳/高阶量词)。表达力由**验证内核**承载——内核是信任根:**内核有 bug = 一切证明皆空**。本文档记录内核选型论证与融合架构决策(2026-08-10)。 ## 二、理论体系全谱系(选型时的候选) diff --git a/docs/compcert-reference.md b/docs/archive/compcert-reference.md similarity index 97% rename from docs/compcert-reference.md rename to docs/archive/compcert-reference.md index 0c68ad06..55f33347 100644 --- a/docs/compcert-reference.md +++ b/docs/archive/compcert-reference.md @@ -1,5 +1,7 @@ # CompCert 后端对照参考(形式化验证过的 C 编译器) +> 定位:受众 = 维护者;状态 = archive(compcert 对照审查任务产物) + > CompCert:世界上唯一被形式化验证过的 C 编译器(INRIA,Leroy 团队)。 > 整个后端用 Coq 编写并被 Coq 证明正确——学习"验证过的后端"如何写、如何证的唯一教材。 > 本文件是对照 Core 后端的阅读地图。 @@ -149,8 +151,8 @@ compcert/ ## 第四轮对照审查记录(2026-08-16,只读) -- 契约:`docs/ir-op-semantics.md`(IR 操作语义表——Task 1,对照 Op.v/Asm.v/Values.v/Integers.v/Floats.v/Memory.v 逐 opcode 定义) -- 发现清单:`docs/compcert-round4-findings.md`(Task 2 只读审查产物——F1-F20,含证据分级与修复优先级) +- 契约:`docs/maintainer/design/ir-op-semantics.md`(IR 操作语义表——Task 1,对照 Op.v/Asm.v/Values.v/Integers.v/Floats.v/Memory.v 逐 opcode 定义) +- 发现清单:`docs/archive/compcert-round4-findings.md`(Task 2 只读审查产物——F1-F20,含证据分级与修复优先级) - **流程**:四组并行只读审查(算术/转换、内存/安全、控制流/调用/并发/动态、未修复项复核)+ 清单任务审查(spec ✅)——**全程未修改任何 Core 代码**;修复范围待维护者反馈后另行授权(反馈门) - 核心结论: - **安全类**:直接数组索引越界守卫双重死亡(F1/F2,`arr[100]` 静默越界)——`&arr[i]` 路径第一轮已修、直接索引路径仍无检查;DEREF 检查对大分配(alloc_sz≥2³¹)失效(F17) @@ -176,4 +178,4 @@ compcert/ - **F18(环境项)**:build/ 产物重建 + 清缓存——修复验证均基于全新重建产物(build/corec + corearch,2026-08-17 00:27),消除 dex 实验残留污染。 - **test_directory_build 回归**:波 3 审查发现(波 1 附带修复「字符串长度头按存储字节数」对转义形式计数偏长,GAS .asciz 反转义后引号类比较恒失败→Core.toml 项目名读取失效)——**已修**(nsysysyukvyv c8c4d22f:长度头改按**原始字符串 UTF-8 字节数**,同时覆盖中文按字符数偏短与转义按转义形式偏长两种情况)。 -- 修复后遗留项(M-2/I-3/lexer 字面量等)见 `TODO.md`「第四轮 CompCert 对照遗留项(2026-08-17 记)」;语义表已同步(✅ 标记 + BC 表状态列,见 `docs/ir-op-semantics.md` §5/§7)。 +- 修复后遗留项(M-2/I-3/lexer 字面量等)见 `TODO.md`「第四轮 CompCert 对照遗留项(2026-08-17 记)」;语义表已同步(✅ 标记 + BC 表状态列,见 `docs/maintainer/design/ir-op-semantics.md` §5/§7)。 diff --git a/docs/compcert-round4-findings.md b/docs/archive/compcert-round4-findings.md similarity index 97% rename from docs/compcert-round4-findings.md rename to docs/archive/compcert-round4-findings.md index f2876f6b..9748f84e 100644 --- a/docs/compcert-round4-findings.md +++ b/docs/archive/compcert-round4-findings.md @@ -1,10 +1,12 @@ # CompCert 对照审查第四轮发现清单(只读审查——修复已完成) +> 定位:受众 = 维护者;状态 = archive(compcert 对照审查第四轮记录) + > **状态:修复已完成(2026-08-16~17,维护者授权全修)——F1-F20 全部修复合入,本清单转为修复记录参考**。 > 原始产出为只读审查(未修改任何 Core 代码),修复范围当时由维护者决定(反馈门)。 -> 修复记录见 `docs/compcert-reference.md`「第四轮修复记录(2026-08-17)」;语义表已同步修复后状态 -> (✅ 标记 + BC 表状态列,见 `docs/ir-op-semantics.md` §5/§7);遗留项见 `TODO.md`。 -> 契约:`docs/ir-op-semantics.md`(IR 操作语义表,Task 1 产物);方法论:CompCert Coq 语义 = 真源, +> 修复记录见 `docs/archive/compcert-reference.md`「第四轮修复记录(2026-08-17)」;语义表已同步修复后状态 +> (✅ 标记 + BC 表状态列,见 `docs/maintainer/design/ir-op-semantics.md` §5/§7);遗留项见 `TODO.md`。 +> 契约:`docs/maintainer/design/ir-op-semantics.md`(IR 操作语义表,Task 1 产物);方法论:CompCert Coq 语义 = 真源, > 三方对照(语义表 vs ELF 编码 instr.cr vs 解释器 interp.cr)。 > 日期:2026-08-16。审查分组:算术/转换(A)、内存/安全(B)、控制流/调用/并发/动态(C)、未修复项复核(T)。 diff --git a/docs/memory-model-capability-lattice.md b/docs/archive/memory-model-capability-lattice.md similarity index 94% rename from docs/memory-model-capability-lattice.md rename to docs/archive/memory-model-capability-lattice.md index 362fd1bb..3fe378fd 100644 --- a/docs/memory-model-capability-lattice.md +++ b/docs/archive/memory-model-capability-lattice.md @@ -1,5 +1,7 @@ # 内存模型方向讨论备忘:能力 + 格(v4) +> 定位:受众 = 维护者;状态 = archive(能力格讨论备忘,演进并入 maintainer/design/memory-model.md) + 日期:v1 2026-08-16 / v2 2026-08-20 / v3 2026-08-26 / v4 2026-08-27 状态:**v4 发布——方向定稿**:规则封闭对象开放 / 无格承诺 / 能力不提升一等公民(可逆)/ 寄存器分配 = 缓存语义映射实例。开放问题继续(§十三)。原一次性上下文包 `docs/capability-lattice-context.md` 已并入本文并删除(2026-08-27,自包含设计、无外部引用,验证过)。 @@ -87,7 +89,7 @@ | 术语 | 含义 | 位置 | |---|---|---| -| 格(层) | 内存模型 = 中间存在空间(第二层映射) | 本节;`docs/memory-model.md` | +| 格(层) | 内存模型 = 中间存在空间(第二层映射) | 本节;`docs/maintainer/design/memory-model.md` | | 格(Lattice) | 层内组织代数成分(偏序 + join/meet) | §九 代数(PCM × 布尔格);**v4:映射参数,非层本体承诺** | ### 4.5 规则封闭、对象开放(v4 定稿) @@ -120,7 +122,7 @@ ### 半边一:存储(缓存语义,已定稿) -memory-model.md §一 七条条款:值 = 配方、驱逐不变量、再生等价、边界公理、赋值 = 版本化、地址 = 映射、映射实例正确性。位置语义 = (条目标识, 偏移);区域 = 图锚定字节域(推论 B: split/merge/share);字节权限层(Freeable > Writable > Readable > Nonempty > Empty)= **经典映射实例的权限面**(条款 7),非语义本体。 +maintainer/design/memory-model.md §一 七条条款:值 = 配方、驱逐不变量、再生等价、边界公理、赋值 = 版本化、地址 = 映射、映射实例正确性。位置语义 = (条目标识, 偏移);区域 = 图锚定字节域(推论 B: split/merge/share);字节权限层(Freeable > Writable > Readable > Nonempty > Empty)= **经典映射实例的权限面**(条款 7),非语义本体。 ### 半边二:执行标注空间(dataflow-design §8) @@ -137,7 +139,7 @@ memory-model.md §一 七条条款:值 = 配方、驱逐不变量、再生等 - **现状缺口**:边界公理(条款 4)只覆盖**图边界**(MMIO/FFI/输入/测量);但执行标注空间的不可重算结构(神谕、BSS 实数、FIXPT 声明、模糊融合)在**图内**而非边界——条款 1–3 与条款 4 之间存在未覆盖地带 - **语义内容**:驱逐不变量/再生等价对图内不可重算条目**不成立**(重跑神谕 ≠ 原结果)——「必须有 home、驱逐必写回」不是便利,是唯一正确的语义 -- **建议条款(待并入 memory-model.md)**:图内不可重算条目(无配方且非边界)= 例外条目;其存储必须持久(home 保有材料),驱逐必须写回;身份 = 产生节点(图节点身份),无独立身份符号 +- **建议条款(待并入 maintainer/design/memory-model.md)**:图内不可重算条目(无配方且非边界)= 例外条目;其存储必须持久(home 保有材料),驱逐必须写回;身份 = 产生节点(图节点身份),无独立身份符号 - **一视同仁规则**:对所有无配方条目(边界 + 图内)统一处理——不按范式枚举(这正是 M1「能力不需要」的实证之一,§六) **关键判据(v2/v3 保留)**:缓存语义条款 1–3 只覆盖**可重算结构**;不可重算结构需要「无配方」标注 + 统一写回规则——这是语义层的最小必需,不需要独立「能力」概念(§六)。 @@ -240,7 +242,7 @@ v3 定位(保留):能力 = 图上「可能关系」的实例,与 FUZZY > 2026-08-27 讨论:寄存器分配的完全形式化验证问题,在缓存语义框架内得到定稿方向。这是「规则封闭对象开放 / 无格承诺 / 驱逐不变量」三原则的落地实测——分配判定以纯格对象写出(无路径结构),同时是 #10(格不重引入顺序)判据的实例化。 > -> 独立成文:`docs/regalloc-cache-mapping.md`(正式参考文档)+ `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`(设计决策记录)。 +> 独立成文:`docs/maintainer/design/regalloc-cache-mapping.md`(正式参考文档)+ `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`(设计决策记录)。 ### 定位 @@ -284,9 +286,9 @@ v3 定位(保留):能力 = 图上「可能关系」的实例,与 FUZZY |---|---|---| | 1 | 普通因果程序能否映射 | 现值流路径(DAG/顺序),已知成立——缓存语义条款 1–3 | | 2 | 非因果结构能否映射 | FIXPT 声明(μ/ν 不动点)——不可重算身份需要无配方标注 + 写回规则(§5.3) | -| 3 | 模糊分支能否映射 | FUZZY 加权融合——融合共享归属(区域 share,memory-model.md 推论 B) | +| 3 | 模糊分支能否映射 | FUZZY 加权融合——融合共享归属(区域 share,maintainer/design/memory-model.md 推论 B) | | 4 | 非确定计算能否映射 | 可能路径——格层不要求立即坍缩为单一执行路径 | -| 5 | 并发/异步能否映射 | go/flow/yield/recv——区域隔离 + 消息区域(memory-model.md) | +| 5 | 并发/异步能否映射 | go/flow/yield/recv——区域隔离 + 消息区域(maintainer/design/memory-model.md) | | 6 | 反馈结构能否映射 | ITER 环(现状 region 嵌套已有)→ 格层无新增 | | 7 | 超图灵语义能否表达 | 图层表达(神谕/BSS/ITTM 标注,dataflow-design §8);格层只需合理表示——「格层不实现超图灵」本身就是设计点 | | 8 | 格层不会重引入顺序执行 | **关键判据**:格 = 中间存在空间,不是「另一种程序」——v4 实证:寄存器分配判定以纯格对象写出(条目/共存/驱逐,无路径结构,§11.5),即判据实例化 | @@ -299,7 +301,7 @@ v3 定位(保留):能力 = 图上「可能关系」的实例,与 FUZZY - v1 问题 1(权限位集)→ **已答**:字节权限层(映射实例)+ 语义授权集(consult/assume/measure/pass,治理层) - v1 问题 7(用户面语法)→ **已答**:声明式句柄(alloc_at 先例),不透明 + 随子图生命周期 - **M1(能力到底应不应该存在)→ v4 已答**:语义层不需要独立概念(身份/无配方还原为图结构,授权归治理层);不提升一等公民(可逆、暂不执行)——#11-16 中预设「能力存在」的 5 项按治理层形态继续 -- **M4(缓存语义七条是否完备)→ v4 输入**:补「图内不可重算」显式条款(§5.3),待并入 memory-model.md +- **M4(缓存语义七条是否完备)→ v4 输入**:补「图内不可重算」显式条款(§5.3),待并入 maintainer/design/memory-model.md - **v3 追加(2026-08-26)**: 1. §十二 清单 8 项的逐项形式化论证——每项通过 = 三层链成立的证据 2. 「格不重引入顺序执行」的可检验判据(v4 实证:分配判定,§11.5) @@ -330,9 +332,9 @@ v3 定位(保留):能力 = 图上「可能关系」的实例,与 FUZZY ## 十五、仓库内参考 -- `docs/pointer-model.md` — 指针三 pass 定论 + 字节权限层 + 地址 = 映射 -- `docs/memory-model.md` — 缓存语义条款 + 图锚定区域(**待并入:图内不可重算条款,§5.3**) -- `docs/dataflow-design.md` §8 — 执行标注空间(FIXPT/FUZZY/超图灵/CORE) +- `docs/maintainer/design/pointer-model.md` — 指针三 pass 定论 + 字节权限层 + 地址 = 映射 +- `docs/maintainer/design/memory-model.md` — 缓存语义条款 + 图锚定区域(**待并入:图内不可重算条款,§5.3**) +- `docs/maintainer/design/dataflow-design.md` §8 — 执行标注空间(FIXPT/FUZZY/超图灵/CORE) - `docs/z-vision.md` — 愿景:范式映射表——存储半边已定稿 - `docs/superpowers/specs/2026-08-16-numeric-types-design.md` — dex/apx(抽象升华数值侧落地,apx = 授权注解先例) - ~~`docs/capability-lattice-context.md`~~ — **已删除(2026-08-27)**:一次性上下文包,内容并入本文 v4 diff --git a/docs/numeric-migration-inventory.md b/docs/archive/numeric-migration-inventory.md similarity index 99% rename from docs/numeric-migration-inventory.md rename to docs/archive/numeric-migration-inventory.md index 1c56b78d..8ad574fe 100644 --- a/docs/numeric-migration-inventory.md +++ b/docs/archive/numeric-migration-inventory.md @@ -1,7 +1,9 @@ # 数值类型迁移盘点——float 站点分类表(迁移契约) +> 定位:受众 = 维护者;状态 = archive(数值类型迁移盘点任务产物) + 日期:2026-08-16 -依据:[2026-08-16-numeric-types-design.md](superpowers/specs/2026-08-16-numeric-types-design.md)(已批准) +依据:[2026-08-16-numeric-types-design.md](../superpowers/specs/2026-08-16-numeric-types-design.md)(已批准) 用途:Task 5/6 的迁移契约——每个站点按本表逐点执行,**禁止机械替换(sed/全局替换)**。 > **追记(2026-09-10,语言面收窄 R1)**:本盘点为 2026-08-16 历史快照——下述分类结论按当时约束固化,**不在本文件改写历史**(原文保留)。R1(计划 `docs/superpowers/plans/2026-09-10-language-surface-narrowing-r1.md`)对其中**宽度三项**改判并落实:`T_FLOAT_F32/T_FLOAT_F64`(与 `T_INT_I8..T_INT_U64` 同族)及 `W_I8..W_F64` **删除**(ce3e541c——编号保留勿复用)、`_f32`/`_f64` 后缀**退役 + 响亮报错**(51d74be3——宽度 = 机器形状,归映射层)。故本表原标 `保留` 的三处(`ast.cr:93-94`、`ast.cr:117-118`、分类规则表「`_f32`/`_f64` 后缀 → 保留」行)**以本次改判为准**;下文相关行逐点标注 R1 落点与一处实测修正(`analysis.cr:895` 行)。 diff --git a/docs/coq/README.md b/docs/coq/README.md index ecc79a36..17ac8c09 100644 --- a/docs/coq/README.md +++ b/docs/coq/README.md @@ -1,5 +1,7 @@ # 用 Coq 验证 Core stdlib 纯函数 +> 定位:受众 = 维护者/验证者;状态 = active(方向与入口);真源 = 本文件(coq/ 证明待建)。 + > 目标:用 Coq(Rocq)做**程序验证**——验证 Core 系统里**几乎永远不会改**的部分(stdlib 纯函数)。 > 注意:这不是规约系统(`.corespec` / 翻译桥那套基础设施),是直接用 Coq 验证程序本身的性质。 diff --git a/docs/crasm.md b/docs/crasm.md deleted file mode 100644 index c5184721..00000000 --- a/docs/crasm.md +++ /dev/null @@ -1,205 +0,0 @@ -# .crasm 统一汇编抽象层 - -> **状态:已废弃(2026-09-05)**——独立 .crasm 格式取消,绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表)。 -> 被硬件接口表(`docs/superpowers/specs/2026-09-05-hardware-interface-table.md`)吸收:跨平台 = HIT 事件 + 投影表; -> MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件;事件可读形态 = v6 NOD 文本 dump。本文保留为历史记录。 - -## 概述 - -Core 的内核路线(project-book 第五阶段)需要汇编级能力(MMIO、特权指令、中断), -但当前无任何汇编层:lexer/parser 无 asm 语法,汇编只存在于 rt.s 手工汇编与 ELF -后端机器码发射。`.crasm` 是 Core 的汇编层——跨平台统一指令集 + 无限虚拟寄存器 + -平台映射表,为内核路线提供受控的汇编入口。 - -设计目标: - -- **不安全隔离**:特权操作隔离在受控区域 -- **验证绝大多数**:普通级指令全部 + 特权级用法可自动验证 -- **跨平台统一**:不同平台指令不同——统一指令集 + 平台映射表解决 - -## 设计原则 - -### 统一指令集 + 无限虚拟寄存器 - -- `.crasm` 是**新生态**(无旧代码兼容义务)——统一指令集可用 -- 虚拟寄存器 `%0, %1, ...` 无上限——写的人不管平台寄存器数,ISA 后端自动分配 - (复用现有 `opt.cr alloc_registers` 线性扫描分配器) -- 平台映射表(普通 + 特权指令)翻译到真实 ISA - -### 难写原则(刻意限制) - -`.crasm` 刻意保持"不好写"——这是特性不是缺陷: - -- 寄存器强制 `%0/%1` 编号(不命名)——代码难维护 → 自然阻止长汇编程序 -- 复杂逻辑写不出也写不进——复杂逻辑属于 Core 层 -- 汇编层保持薄:意图纯粹、滥用防护、验证聚焦 -- **代码越简单越容易验证**——限制可写性就是保障可验证性 - -### 生态边界 - -- `.crasm` 新代码按统一指令集写,无兼容义务 -- 旧 .c/汇编代码原样保留(不迁移)——互不干扰 - -## 架构 - -``` -.cr 侧(平台无关接口) .crasm 侧(统一抽象层) -extern fn port_read(addr: u64) fn port_read(addr: u64) -> u8 { - -> u8; %0 := load [addr]; - if %0 == 0xFF goto L1; - return %0; - } - ↓ ABI(参数→虚拟寄存器) ↓ 后端翻译 - ┌──────────────────┴─────────────────┐ - │ 平台映射表(src/arch//) │ - │ x86-64 / ARM64 / RISC-V │ - │ + 寄存器分配(alloc_registers 复用) │ - └──────────────────────────────────────┘ -``` - -## 指令集(统一、平台无关) - -### 标识约定 - -| 标识 | 约定 | 示例 | -|---|---|---| -| 寄存器 | `%` 前缀编号(LLVM IR 同款) | `%0, %1` | -| 函数 | 普通标识符(与 Core 一致,无 @) | `fn port_read` | -| 标签 | `L` 前缀无符号 | `L1` | -| 立即数 | 裸写 | `42`、`0xFF` | - -### 指令类 - -| 类 | 指令 | 平台无关语义 | -|---|---|---| -| 数据传输 | `%d := load [addr]`、`store [addr], %s` | 内存读写 | -| 算术/逻辑 | `%d := %a + %b`(+ - * / & \| ^ << >>)、`%d := ~%a` | 无限虚拟寄存器运算 | -| 比较/分支 | `if %c == 0 goto L`(== != < > <= >=)、`goto L`、`call fn`、`ret` | 控制流 | -| 内存操作 | `%d := %a + off`(地址算术)、`%d := zext/ext` | 地址计算与宽度转换 | -| 特权级 | `mmio_read(addr)`、`mmio_write(addr, val)`、`barrier`、`cache_op`、`irq_enable`/`irq_disable`、`swap_context`、`halt` | 硬件访问(固定集) | - -### 特权指令固定集 - -特权指令是**标准手册定义的有限固定集**(几十条)——固定 = 约束有限 = 可形式化 = 可验证。 -标准硬件布局同理:标准手册定义的寄存器/MMIO 区域也是固定集 → 可建表验证(见下文硬件描述表)。 - -## 示例 - -```crasm -unsafe fn port_write(addr: u64, val: u8) { - // 用法验证:addr 必须 64 位、val 必须 8 位(与 MMIO 寄存器宽度匹配) - // 硬件表验证:addr 落在标准/厂商表声明区域 → 自动(归属/宽度/对齐) - // 表外隔离:仅表外硬件行为与动态时序由人工保证 - mmio_write(addr, val); -} -``` - -## 安全模型 - -### 指令分级与验证策略 - -| 级 | 指令 | 验证策略 | -|---|---|---| -| 普通级 | 数据传输/算术/分支/内存操作/地址计算 | **自动验证**:寄存器生命周期 + 地址 provenance/边界(复用现有指针模型 pass)——"绝大多数可验证" | -| 特权级 | mmio/barrier/irq/swap_context/halt | **用法验证**(自动):参数类型/宽度、放置规则、指令间约束(固定集 → 可形式化);**硬件表验证**(自动):地址落在标准/厂商表声明区域、寄存器宽度匹配;**表外隔离**(unsafe 边界):表外硬件行为与动态时序人工保证 | - -### 寄存器生命周期规则(新 pass,.crasm 专用) - -| 规则 | 检查内容 | 违反示例 | -|---|---|---| -| 未初始化读 | 读操作数前必须被写过 | `%1 := %0 + 1` 而 %0 未定义 | -| 重复写 | 同一寄存器活跃期只写一次(SSA 式) | 两条路径写 %0 后汇合未合并 | -| 生命周期逃逸 | 寄存器值不能越过函数边界(除非经 ABI 声明) | 函数返回未声明的 %5 | -| 宽度一致 | 运算操作数宽度匹配(8/16/32/64) | `%0(8bit) := %1(64bit) + 1` | -| 分支一致性 | goto 目标存在;if 条件是条件寄存器 | `if %0 == 0 goto missing` | - -### 硬件描述表:标准部分自动验证 - -"硬件有的地方是标准的,也是可以验证的"——特权访问的验证对象分三层: - -| 层 | 覆盖 | 验证依据 | -|---|---|---| -| 标准表 | 公开标准硬件:UART 16550、x86 APIC/IOAPIC、PCIe ECAM、CMOS RTC | 标准手册 → 形式化表(地址范围、寄存器宽度、读写语义) | -| 厂商表 | SoC 外设:GPIO、SPI 控制器等 | 厂商手册 → 与标准表同构的形式化描述 | -| 表外 | 自定义 FPGA 逻辑、非标准设备 | unsafe 入口标注一次,人工保证 | - -mmio_read/write 的地址落在表内 → 编译器自动验证区域归属、寄存器宽度匹配、对齐合法。 -这与指针模型同构:unsafe 标注的是"图边界入口"(外部地址、FFI 返回),进入图内编译器 -重新获得追踪权——硬件表让标准部分拿回自动验证,unsafe 只承担表外入口。 - -无论硬件多标准,**动态行为**(时序、中断延迟、握手时序)都超出编译期验证范围, -始终位于表外,人工保证。 - -### 与现有验证的关系 - -- 寄存器规则:新 pass(`.crasm` 专用——虚拟寄存器是抽象层概念) -- 地址/内存验证:复用 PointerAnalysis / RegionCheck / ProvenanceVerify - (`.crasm` 的 `load [addr]` 转成与 IR 相同的 provenance 检查) -- 特权用法验证:固定指令集约束表(参数宽度/放置规则)+ 硬件描述表(区域/宽度/对齐),pass 检查 -- 错误报告:走新错误码体系(验证类,挂错误码规格类别表,实现时定归属) - -## 交互接口 - -### .cr ↔ .crasm - -```cr -extern fn port_read(addr: u64) -> u8; // .cr 侧:C ABI 先例 -data := port_read(0xFEED_0000); // 普通函数调用 -``` - -接口映射:参数 → 虚拟寄存器(ABI 参数寄存器),返回值 → 返回寄存器——平台 ABI -差异由后端处理。 - -### 平台映射表组织 - -``` -src/arch/ -├── x86_64/ → 指令映射表(普通 + 特权)、ABI 规则、寄存器分配 -├── arm64/ → 同接口,不同映射 -└── riscv/ → 同接口,不同映射 -``` - -映射表 = 每平台一份"统一指令 → 真实指令"翻译表(编译器后端的一部分,机械翻译—— -与现有 `src/arch/linux/ld/` 后端同构)。特权指令映射在表内固定(mmio_read → -x86 mov / arm ldr)——映射正确性由固定表保证。 - -### 编译流程 - -``` -.crasm → 解析(结构化指令 → AST 复用)→ 验证(寄存器/地址/特权用法) - → 翻译(统一指令 → 目标平台指令,映射表) - → 与 .cr 编译产物链接(现有 ELF 后端) -``` - -## 测试与里程碑 - -### 测试策略 - -| 层 | 载体 | 覆盖 | -|---|---|---| -| .crasm 解析/验证单测 | tests/selfhost(Python 驱动) | 寄存器规则、地址边界、特权用法 | -| 翻译正确性 | 每平台一个映射测试(x86 先行) | 统一指令 → 真实指令字节级断言 | -| 端到端 | .crasm + .cr 链接运行 | port_read/write 全链路 | -| 特权隔离 | unsafe 边界测试 | 普通函数用特权指令 → 编译错误 | - -### 里程碑 - -1. `.crasm` 词法/解析(结构化指令 → AST) -2. 寄存器生命周期 pass + 测试 -3. 特权约束表 + 用法验证 + unsafe 隔离 -4. x86-64 映射表(翻译器)+ 字节级测试 -5. .cr extern 接口接线 + 端到端 -6. (后补)ARM64/RISC-V 映射表 - -### 明确不做(YAGNI) - -- 模拟器/调试器支持 -- .crasm 的 C 生态兼容(新生态无义务) -- 指令级时序验证(动态行为无论硬件多标准都超出编译期验证范围) -- 表外硬件行为验证(unsafe 隔离,人工保证——表内标准部分仍自动验证) - -## 当前状态 - -设计已批准(2026-08-08 brainstorming 会话,逐节确认),2026-08-09 整理为正式文档(本文件),**尚未实现**。当前编译器 -无任何汇编层:lexer/parser 无 asm 语法;汇编仅存在于 rt.s 手工汇编与 ELF 后端 -机器码发射。实现起点见"里程碑"。 diff --git a/docs/at-intrinsics.md b/docs/developer/at-intrinsics.md similarity index 88% rename from docs/at-intrinsics.md rename to docs/developer/at-intrinsics.md index 8d7d954d..b424d874 100644 --- a/docs/at-intrinsics.md +++ b/docs/developer/at-intrinsics.md @@ -1,5 +1,7 @@ # Core @ 内建原语 +> 定位:受众 = 开发者;状态 = active(12 个内建已实现,2026-07-28 完整落地;语义契约见 docs/maintainer/design/ir-op-semantics.md §2.8/§4)。 + `@` 是编译器提供的能力入口,不通过标准库实现。分三类: | 类别 | 能力 | 使用场景 | @@ -51,7 +53,7 @@ fn serialize(buf: &[u8], obj: any) { ```core @no_bounds_check // 跳过边界检查 -@fast // 允许精度换速度 +@fast // 优化性能提示(行为零变化;精度换速度 = apx 语义级授权,见 syntax.md dex/apx 节) ``` ## 与其他语言的对比 diff --git a/docs/developer/concepts.md b/docs/developer/concepts.md new file mode 100644 index 00000000..9a318ce1 --- /dev/null +++ b/docs/developer/concepts.md @@ -0,0 +1,24 @@ +# 核心概念导览 + +> 定位:受众 = 开发者(理解 Core 的设计思维);状态 = active。 +> 四篇分篇各讲一个概念(每篇末有深入链接);本文是入口与一句话总览。 + +## 四个概念 + +| 概念 | 一句话 | 分篇 | +|---|---|---| +| 程序 = 一张图 | 代码编译为数据流图,图保留全部语义——不需要"模式"概念 | [concepts/graph.md](concepts/graph.md) | +| 内存自动管理 | 子图区域 + 编译器自动验证——无 borrow checker、无 GC、无标注 | [concepts/memory.md](concepts/memory.md) | +| 部署与代码分离 | 物理世界(内存策略/OS/时间源)进部署配置,不进代码 | [concepts/deploy.md](concepts/deploy.md) | +| 规约内联验证 | #check/#ensure 写在函数上,验证不用学第二门语言 | [concepts/spec.md](concepts/spec.md) | + +## 为什么这些概念成立:语义保鲜 + +编译中间表示(IR)保留全部类型与语义信息,不逐级降格为机器指令——验证器消费 IR 即获得完整语义模型。这是 Core 支持形式化验证、跨平台行为一致、范式可迁移(经典/量子/…)的共同基础。 + +## 阅读建议 + +- 第一次接触:先 [tutorial.md](tutorial.md) 上手,概念随阶段展开 +- 想理解设计:读四个分篇 +- 语法细节:[syntax.md](syntax.md) +- 术语表:`../glossary.md`(一般不需要查——本文只用开发者术语) diff --git a/docs/developer/concepts/deploy.md b/docs/developer/concepts/deploy.md new file mode 100644 index 00000000..505fc5e5 --- /dev/null +++ b/docs/developer/concepts/deploy.md @@ -0,0 +1,41 @@ +# 概念:部署配置与代码分离 + +> 定位:受众 = 开发者;状态 = active(部分落地,见注)。 + +## 一条原则 + +**代码描述计算;物理世界不进代码。** + +内存能否动态分配、有几个核、有没有 OS——这些不属于程序语义,属于部署目标。Core 用部署配置(TOML)声明它们: + +```toml +[deploy.mcu] +target = "cortex-m4" +allocation = "static" # 禁止动态分配:所有大小必须编译期可定 + +[deploy.server] +target = "x86-64-linux" +allocation = "dynamic" +``` + +同一份代码: + +- 教学环境 → 解释执行,可单步 +- 桌面 → 原生二进制(ELF) +- 裸机 → 静态分配,零运行时 +- (设计态)集群 → 图跨机部署 + +代码不改。语义不变。部署配置不同。 + +## 平台接口同理 + +I/O、时钟、随机、哈希在语言层是**语义接口**——程序的 I/O 语义是"流变换"(输入流 → 输出流),系统调用是后端按部署目标实现的物理细节。验证意义上 I/O 是一等公民,不是黑洞。 + +## 现状注记 + +部署配置文件的消费范围与平台桥(IO/时钟语义接口)落地按 TODO 推进——当前以源码行为为准,`deploy.toml` 的字段语义见各示例与维护者文档。 + +## 延伸 + +- `../../maintainer/design/execution-model.md` §五(设计定稿) +- examples/pi(deploy.toml 示例) diff --git a/docs/developer/concepts/graph.md b/docs/developer/concepts/graph.md new file mode 100644 index 00000000..b09dfe8f --- /dev/null +++ b/docs/developer/concepts/graph.md @@ -0,0 +1,31 @@ +# 概念:程序 = 一张图 + +> 定位:受众 = 开发者;状态 = active。 + +## 图是什么 + +Core 编译后的程序不是"指令序列",而是一张**数据流图**:节点是操作(取址、解引用、算术、调用),边是值的流动。控制流不是另加的机制,而是图的结构: + +| 代码 | 图上 | +|---|---| +| 顺序语句 | 值沿依赖链流动(不依赖的互不阻塞) | +| if/else | 两条候选路径,条件选择一条 | +| loop/for | 一条环:值绕环流动,每圈=一次迭代 | +| go | 运行时从当前节点长出并行子图 | + +## 对写代码意味着什么 + +**不需要"模式"概念。** 没有 debug/release/并发模式切换——代码写了什么,图就反映什么。学习、单步、并发是同一张图的不同看法: + +- 单步调试 = 沿图走值 +- 并发 = 图上有并行的子图 +- 惰性 = 没被需要的路径不求值 + +## 表达力上限 + +图的表达不限于经典机器能执行的顺序:非因果/概率/模糊语义在图上都有标注位置(设计态,超出本文范围;术语见 `../../glossary.md`)。 + +## 延伸 + +- 图的执行与并发细节:`../../maintainer/design/execution-model.md`(维护者视角,可选) +- 语法如何映射到图:tutorial 各阶段"图模型"栏 diff --git a/docs/developer/concepts/memory.md b/docs/developer/concepts/memory.md new file mode 100644 index 00000000..f9bf10f7 --- /dev/null +++ b/docs/developer/concepts/memory.md @@ -0,0 +1,39 @@ +# 概念:内存——自动、无 GC、无标注 + +> 定位:受众 = 开发者;状态 = active(arena 已实现;区域机制设计态见注)。 + +## 不用学 borrow checker + +Core 的内存管理与 Rust 不同路:Rust 用类型层的借用规则(需要你学与标注),Core 由编译器在图上自动验证指针安全。你写指针和 C 一样自由,编译器负责:越界、悬垂、生命周期。 + +## 内存怎么回收:子图区域 + +内存按**子图区域**管理: + +- 每个函数/循环有独立内存区域 +- 区域内分配 = 指针碰撞,O(1) +- 子图结束 → 区域整体重置回收,O(1),无 GC 停顿 + +```core +fn process_batch(items: [int]) -> int { + // 本函数内的临时数据都活在函数区域 + // 函数返回 → 整块回收,无需 free + total := 0; + for x in items { total = total + x; } + return total; +} +``` + +`go` 启动的并发子图各有独立区域,退出整块回收。 + +## 长期服务的内存上限 + +内存上限 = 并发子图数 × 区域容量——可预测,无 GC 停顿。区域复用/清零策略(敏感数据)为部署配置控制(非默认)。 + +## 现状注记 + +子图 arena 已实现;区域机制的扩展形态(split/merge/size class 等)为设计态,按 TODO 推进——当前以源码行为为准。 + +## 延伸 + +- 实现与理论:`../../maintainer/design/region-model.md`、`../../academic/cache-semantics.md`(学术,可选) diff --git a/docs/developer/concepts/spec.md b/docs/developer/concepts/spec.md new file mode 100644 index 00000000..ffdbb02a --- /dev/null +++ b/docs/developer/concepts/spec.md @@ -0,0 +1,42 @@ +# 概念:规约——验证不换语言 + +> 定位:受众 = 开发者;状态 = 设计定稿(未实现——见注)。 + +## 写性质,不用学逻辑语言 + +Core 的验证入口写在函数上,表达式就是 Core 表达式: + +```core +fn divide(a: int, b: int) -> int? + #check(b != 0) // 前置:调用方必须满足 + #ensure(result.is_some() implies (a / b) == result.unwrap()) +{ + if b == 0 { return None; } + return Some(a / b); +} +``` + +- `#check(...)`:调用方义务 +- `#ensure(...)`:实现方保证;`result` = 返回值,`old(x)` = 入口时的值 +- `spec fn`:表达复杂性质用纯 Core 写的检查函数(验证的标准) + +## 三层自动化的哲学 + +1. 编译器自动推导能推导的一切(`#pure`/`#terminating` 等标签——不用你写) +2. 推导不出的简单性质:一行 `#check`/`#ensure` +3. 复杂性质:`spec fn`——还是 Core,不是公式 + +验证的回报不只是安全:证明过的性质可流回优化器(证明驱动优化,设计态)——写的证明越多,优化越狠。 + +## 为什么可行:语义保鲜 + +编译中间表示保留全部语义——验证器消费 IR 即获得完整程序语义模型,不需要你为验证重写一遍程序,也不需要验证器自己猜语义。 + +## 现状注记 + +规约语法(#check/#ensure/.cr 内联)为 2026-09 设计定稿;**语法与验证管线均未实现**——当前标注不改变运行行为。推进见 TODO 与 `../../maintainer/design/spec-design.md`。 + +## 延伸 + +- 语法参考:`../syntax.md` 第十章 +- 系统设计:`../../maintainer/design/spec-design.md` diff --git a/docs/editor-setup.md b/docs/developer/editors.md similarity index 60% rename from docs/editor-setup.md rename to docs/developer/editors.md index 0154b918..9a221394 100644 --- a/docs/editor-setup.md +++ b/docs/developer/editors.md @@ -1,23 +1,24 @@ -# 编辑器 LSP 配置(corelsp) +# 编辑器接入配置(corelsp) -corelsp 是 Core 语言的语言服务器(LSP 协议,stdio 管道),提供: +> 定位:受众 = 开发者;状态 = active;真源 = corelsp 行为见 [maintainer/design/corelsp.md](../maintainer/design/corelsp.md)(实现与文档冲突时以源码为准);仓库内编辑器素材见 [editor/](../../editor/)(扩展源码与说明)。 +> 本文是纯用户向接入指南——各编辑器怎么接 corelsp;corelsp 的架构(检查管线/诊断通道/能力契约)见 [maintainer/design/corelsp.md](../maintainer/design/corelsp.md)。 -| 功能 | 触发方式 | 说明 | -|------|----------|------| -| 诊断 | 打开/编辑/保存时 | `textDocument/publishDiagnostics`,错误 + 行内定位 | -| 悬浮信息 | `hover` | 标识符类型/签名(基于快照) | -| 跳转定义 | `definition` | 函数/全局/类型声明位置 | -| 补全 | `completion`(`@` 触发) | 关键字 + 符号 + `@` 内建 | -| 文档符号 | `documentSymbol` | 函数/结构体/枚举大纲 | -| 语义令牌 | `semanticTokens/full` | keyword/type/function/variable/... 着色 | +corelsp 是 Core 语言的语言服务器(标准 LSP 协议,stdio 管道)。接入后编辑器可获得: -**限制**:全量同步(`textDocumentSync=1`);语义查询基于「最后一次成功检查的快照」——文档有语法错误时悬停/跳转可能返回空结果;服务器是单文档全局状态机,每次 `didOpen` 重新检查主文档。 +| 功能 | 触发 | 说明 | +|------|------|------| +| 诊断 | 打开/编辑/保存时 | 错误信息 + 行内定位,自动发布 | +| 悬浮信息 | 悬停 | 标识符类型/签名 | +| 跳转定义 | F12 / gd | 函数/全局/类型声明位置 | +| 补全 | `@` 触发 | 关键字 + 符号 + `@` 内建 | +| 文档大纲 | 大纲面板 | 函数/结构体/枚举符号 | +| 语义着色 | 自动 | keyword/type/function/variable 等 | -## 构建服务器 +行为要点: -```bash -python3 build_selfhost_native.py # 产出 build/corelsp -``` +- 每次打开/编辑文件都会触发一次重新检查(全量同步),诊断随之更新。 +- 文档有语法错误时,悬停/跳转可能返回空——语义查询基于最近一次成功检查的结果。 +- 前置条件:仓库根存在 `build/corelsp`。构建:`python3 build_selfhost_native.py`(与 build/corec、build/corearch 同批产出)。 ## Neovim(内置 LSP,nvim 0.10+) @@ -52,14 +53,14 @@ vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { buffer = 0 }) ### 说明 - **诊断**:nvim 自动接收 `publishDiagnostics` 并显示(`vim.diagnostic`),无需额外配置。 -- **补全**:`vim.lsp.buf.completion`(``),或 blink.cmp / nvim-cmp 等补全框架自动接入 LSP source。服务器补全为 `@` 触发(`triggerCharacters: ["@"]`)。 +- **补全**:`vim.lsp.buf.completion`(``),或 blink.cmp / nvim-cmp 等补全框架自动接入 LSP source。服务器补全为 `@` 触发。 - **语义着色**:nvim 0.10+ 语义令牌默认开启(不存在 `:SemanticTokensEnable` 命令,无需配置);如需高亮,需 colorscheme 将 `@lsp.type.*` 链接到 `@keyword` 等高亮组。 -- **与既有 compiler 插件互斥**:`editor/nvim/plugin/core.lua`(基于 `corec` 编译器的诊断/跳转)与 LSP 功能重叠——启用 LSP 时建议移除该插件,避免诊断与键位重复。 -- 安装语法高亮/文件类型检测等基础配置见 `editor/README.md`。 +- **与既有 compiler 插件互斥**:[editor/nvim/plugin/core.lua](../../editor/nvim/plugin/core.lua)(基于 `corec` 编译器的诊断/跳转)与 LSP 功能重叠——启用 LSP 时建议移除该插件,避免诊断与键位重复。 +- 安装语法高亮/文件类型检测等基础配置见 [editor/README.md](../../editor/README.md)。 ## VS Code -扩展位于 `editor/vscode-core/`(语法高亮 + corelsp 客户端): +扩展位于 [editor/vscode-core/](../../editor/vscode-core/)(语法高亮 + corelsp 客户端): ```bash code --install-extension editor/vscode-core/ @@ -67,7 +68,7 @@ code --install-extension editor/vscode-core/ - 服务器路径默认取工作区根目录下 `build/corelsp`,可用设置 `corelsp.path` 覆盖(绝对路径或相对工作区根)。 - 设置 `corelsp.enabled = false` 可关闭 LSP(仅保留语法高亮)。 -- 从源码调试:VS Code 打开 `editor/vscode-core/` 按 F5(见 `.vscode/tasks.json`)。 +- 从源码调试:VS Code 打开 [editor/vscode-core/](../../editor/vscode-core/) 按 F5(见 `.vscode/tasks.json`)。 ## Zed(2026-08-28 接入) @@ -82,7 +83,7 @@ code --install-extension editor/vscode-core/ ### 方式二:settings.json(无扩展时的 LSP 兜底) -本仓库已内置项目配置(`.zed/settings.json`,打开即用)——binary 相对路径以工作区根(仓库根)解析: +本仓库已内置项目配置([.zed/settings.json](../../.zed/settings.json),打开即用)——binary 相对路径以工作区根(仓库根)解析: ```json { @@ -102,7 +103,7 @@ code --install-extension editor/vscode-core/ **其他项目/全局接入**:全局 `~/.config/zed/settings.json` 同上,`binary.path` 建议用 **绝对路径**(全局配置无工作区根可解析相对路径)。此方式只有 LSP,无语法高亮(高亮需扩展的 grammar)。 -- 已通告能力(2026-08-28 补 `hoverProvider`/`definitionProvider` 通告):诊断(全量同步)、悬浮、跳转定义、补全(`@` 触发)、文档符号、语义令牌。 +- 此方式可用能力:诊断、悬浮、跳转定义、补全(`@` 触发)、文档符号、语义令牌。 - Zed 默认键位:`F12` 跳转定义;悬停即看;LSP 诊断自动显示。 - 构建 corelsp:`python3 build_selfhost_native.py`(产出 `build/corelsp`)。 diff --git a/docs/error-codes.md b/docs/developer/errors.md similarity index 97% rename from docs/error-codes.md rename to docs/developer/errors.md index 92a9b983..d10cad38 100644 --- a/docs/error-codes.md +++ b/docs/developer/errors.md @@ -1,7 +1,7 @@ # Core 错误码参考 -每一个错误码对应编译器中 **一个唯一可区分的检查点**。 -同一条性质但不同上下文(赋值 vs 传参 vs 初始化)的报错各占独立码。 +> 定位:受众 = 开发者;状态 = active。 +> 真源:错误码数值与注释名 = src/compiler/ast.cr 的 EC_* 常量(每码唯一检查点,注释带本文编号,如 `EC_P_EXPECTED = 1001 // P001`);**词法族(L 族)例外**——lexer.cr 的 add_error 只传消息文本不带码,L 码为本文档侧编号,以消息文本为真源。改码须同步 ast.cr 注释与本文。 > **fail-closed 语义(2026-09-15,FC 批 T2 起)**:**默认阻断**——任何**不在豁免登记表**内的 > 类型面诊断 ⇒ `rc=1` + **零产物**(不落目标 ELF、不落本次 `.ccr`)。豁免表 = @@ -14,6 +14,8 @@ ## L0xx — 词法 (Lexer) +> 注:本族码无数值常量(lexer add_error 不带码)——以消息模板为真源,码为文档侧编号。 + | 码 | 检查点 | 消息模板 | 触发条件 | |----|--------|---------|---------| | L001 | 字符串终止 | `Unterminated string literal` | 引号未闭合就到行尾 | diff --git a/docs/developer/examples.md b/docs/developer/examples.md new file mode 100644 index 00000000..5fa33e99 --- /dev/null +++ b/docs/developer/examples.md @@ -0,0 +1,48 @@ +# 示例集 + +> 定位:受众 = 开发者;状态 = active。仓库 examples/ 的可跑程序,按复杂度排列。 + +## 运行方式 + +```bash +./build/corec build examples//main.cr -o /tmp/ --static +/tmp/ +``` + +或先检查:`./build/corec check examples//main.cr` + +## 示例清单 + +| 示例 | 内容 | 看点 | +|---|---|---| +| [add](../../examples/add/main.cr) | 两数相加,main 返回退出码 | 最小完整程序——第一个程序从这里开始 | +| [complex](../../examples/complex/main.cr) | 复数运算 | 结构体 + 函数组合 | +| [pi](../../examples/pi/main.cr) | 圆周率计算 | 数值循环(带 deploy.toml 的部署配置示例) | +| [load_balancer](../../examples/load_balancer/main.cr) | 负载均衡器 | 并发/任务分发方向(含 spec/ 规约骨架) | + +> 注:examples/hello/ 目前是空骨架(未填 main)——忽略或用 add/ 起步。 + +## 更多示例:测试套件(tests/suite/) + +仓库测试套件的 .cr 文件是按主题组织的可跑示例(覆盖更广): + +| 主题 | 文件 | +|---|---| +| 基础/控制流 | test_control_flow.cr、generics_test.cr | +| 结构体/枚举 | at_test_struct.cr 等 at_test*.cr(@ 内建 + 结构) | +| 并发 | go_test.cr、chan_test.cr、conc_test.cr | +| 动态类型 | dyn_test.cr、dex_test.cr | +| 内存/指针 | ptr_arith.cr、ptr_ref_first.cr、arena_test.cr | +| FFI/热补丁/惰性 | ffi_test.cr、hotpatch_test.cr、lazy_test.cr | + +> 注:套件文件可能依赖测试驱动(检查点/期望输出)——作示例阅读时关注语言用法,运行请走测试入口(见 `../maintainer/testing.md`)。 + +## 配套 + +- 每个示例目录可能带 `.cir`/`.ccr`/`.s`/`prog`——构建副产物,手工分步调试时对照用 +- 完整编译产物链:`.cr` →(corec)→ `.cir`(图形态)+ `.ccr`(格形态)→(corearch)→ ELF + +## 建议下一步 + +写自己的程序时:类型检查循环(`check`)先行,`run` 试小段,`build` 出产物。 +语法速查见 [syntax.md](syntax.md);构建与命令见 [quickstart.md](quickstart.md)。 diff --git a/docs/developer/faq.md b/docs/developer/faq.md new file mode 100644 index 00000000..414812f1 --- /dev/null +++ b/docs/developer/faq.md @@ -0,0 +1,49 @@ +# 常见问题(FAQ) + +> 定位:受众 = 开发者;状态 = active。答案以当前实现为真源;标注"设计态"的条目尚未落地。 + +## 语言与设计 + +**Core 和 Rust 什么关系?为什么没有 borrow checker?** +Rust 用借用检查在类型层保证内存安全,代价是学习曲线与表达力约束。Core 的指针安全由编译器在数据流图上自动验证(来源追踪 + 边界核对)——你不需要标注生命周期。Rust 是启发,不是基础:语言从图模型独立设计。 + +**"语义保鲜"是什么?** +编译中间表示(IR)保留全部类型与语义信息,不逐级降格为机器指令。验证器消费 IR 即获得完整语义模型——这是 Core 支持形式化验证、跨平台行为一致、范式可迁移(经典/量子/…)的共同基础。通俗说:编译器不把"程序是什么"丢掉。 + +**没有 GC,内存谁回收?** +子图区域(arena):函数/循环结束,它的内存区域整块回收——O(1) 重置游标,无停顿。长期服务的内存上限 = 并发子图数 × 区域容量。 + +**部署配置里 allocation = "static"/"dynamic" 是什么?** +代码不决定内存策略;部署目标声明。static 目标(裸机)要求所有大小编译期可定;dynamic 目标允许运行时分配。同一份代码两种部署都成立时,它就两头都能跑。 + +## 使用 + +**怎么跑第一个程序?** +见 [quickstart.md](quickstart.md)——`python3 build_selfhost_native.py` 构建,`./build/corec build` 编译,`run` 内联执行。 + +**check / build / run / cir / ccr 什么区别?** +`check` 只做类型检查;`run` 解释器内联执行(不产文件);`build` 全链到 ELF;`cir`/`ccr` 输出中间 IR(研究/调试)。 + +**为什么我打印不出东西?** +先确认用了标准库的 print/println(实现在 src/stdlib/io.cr;模块导入与目录级批量导入规则见 syntax.md 模块章)。注意:裸 `fn main()->int{return 42;}` 没有输出是**正常的**——main 返回值 = 进程退出码,不是打印。 + +**编译很慢/风扇很响?** +自举编译是 CPU 密集任务;长时间构建(无人值守)建议 `nice -n 19` 或 `cpulimit -l 10` 限速——交互式短任务不必。 + +**corec2 是什么?为什么我 check 会卡住?** +corec2 = 自举的第二代前端产物(自编译验证用)。它曾有 tokenizer 死循环已知问题(全局变量注册缺陷,见 TODO)——日常开发请用 `./build/corec`,不要用 corec2。 + +## 状态相关 + +**哪些功能还没实现?** +语言与编译器在活跃自举期。已知设计态/挂账:验证管线(规约证明)、平台桥(IO/时钟语义接口落地)、惰性求值、分布式、部署配置完整消费、.ccr 内容随 v6 段表推进。最新状态看仓库 TODO.md 与各文档头部"状态"。 + +**float 呢?** +float 已退役(2026-08,dex/apx 定案)——精确小数用 `dex`,近似授权用 `apx`。历史遗留的 float 代码需迁移(见 ADR-0010)。 + +**.corespec 文件还有用吗?** +没有——独立规约格式 2026-09 退役,规约写成 .cr 内联 #check/#ensure(设计态,见 ADR-0001)。 + +## 相关 + +[tutorial.md](tutorial.md) / [syntax.md](syntax.md) / [quickstart.md](quickstart.md) / [concepts.md](concepts.md) diff --git a/docs/developer/quickstart.md b/docs/developer/quickstart.md new file mode 100644 index 00000000..d86135ae --- /dev/null +++ b/docs/developer/quickstart.md @@ -0,0 +1,67 @@ +# 快速开始 + +> 定位:受众 = 开发者(第一次接触 Core);状态 = active。 +> 前提:Linux(x86-64)、Python 3、一个终端。构建与运行的全部命令以仓库 CLAUDE.md 为真源。 + +## 一、构建编译器 + +Core 编译器由 Core 自身编写(自举),用 Python bootstrap 构建出原生二进制: + +```bash +# 在仓库根目录 +python3 build_selfhost_native.py +``` + +产物:`build/corec`(前端:源码 → IR)→ `build/corearch`(后端:IR → ELF)。 + +> 提示:构建是编译密集型任务,慢机器上可用 `nice -n 19` 降优先级。产物尚不随仓库分发——需要自己构建(发行打包见 PKGBUILD 方向)。 + +## 二、第一个程序 + +`examples/add/main.cr` 是最小完整程序: + +```core +fn add(a: int, b: int) -> int { + return a + b; +} +fn main() -> int { + return add(3, 4); +} +``` + +`main` 的返回值 = 进程退出码(所以这个程序"运行成功" = 退出码 7)。 + +## 三、三种运行方式 + +```bash +# 1. 类型检查(不产出) +./build/corec check examples/add/main.cr + +# 2. 内联执行(解释器,适合试一小段) +./build/corec run 'fn main()->int{return 42;}' + +# 3. 编译成原生二进制 +./build/corec build examples/add/main.cr -o /tmp/add --static +/tmp/add; echo $? # 打印退出码 7 +``` + +`build` 会自动调用后端产出 ELF;也可手工分步:`./build/corec ccr file.cr` 出中间 IR,`./build/corearch file.ccr --elf --static -o out` 出二进制(调试后端时有用)。 + +## 四、目录导览 + +| 路径 | 内容 | +|---|---| +| src/compiler/ | 编译器源码(用 Core 写)——前端 | +| src/arch/linux/ld/ | ELF 后端(指令编码/链接) | +| src/stdlib/ | 标准库(io/fmt/collections/arena/…) | +| examples/ | 示例程序(add/complex/pi/load_balancer 等,见 examples.md) | +| tests/ | 测试三套(bootstrap/selfhost/suite) | + +> 注:examples/hello/ 目前是空骨架——用 add/ 或自建文件开始。 + +## 五、下一步 + +- [tutorial.md](tutorial.md)— 渐进学习路径(从基础到系统编程) +- [syntax.md](syntax.md)— 语法参考 +- [examples.md](examples.md)— 可跑示例 +- [concepts.md](concepts.md)— 图模型等核心概念导览 diff --git a/docs/language-syntax.md b/docs/developer/syntax.md similarity index 78% rename from docs/language-syntax.md rename to docs/developer/syntax.md index 66075556..9149bf69 100644 --- a/docs/language-syntax.md +++ b/docs/developer/syntax.md @@ -1,36 +1,26 @@ # Core 语言语法 -本文档描述 Core 语言**本体**的语法:词法、类型标注、声明、表达式、控制流、接口与泛型、规约、并发。语法形式以 `grammar/core.ebnf` 与 `src/compiler/` 源码为准(冲突时以源码为准);语义以格形态为标准框架解释。 +> 定位:受众 = 开发者;状态 = active + +本文档是 Core 语言**本体**的语法参考:词法、类型标注、声明、表达式、控制流、接口与泛型、规约、并发。语法形式以 `grammar/core.ebnf` 与 `src/compiler/` 源码为准(冲突时以源码为准)。设计理念与术语见 [concepts.md](concepts.md);深入(IR/验证/理论)从 concepts 的链接走。 --- ## 一、设计要点 -Core 的语义保鲜(semantic preservation)由三层映射承载: - -| 层 | 形态 | 格式 | 职责 | -|----|------|------|------| -| 图 | 结构表达(关系空间) | `.cir` | 程序的结构:节点、边、region | -| 格 | 存在表达(状态空间) | `.ccr` | 存在:状态、条目、共存、驱逐 | -| 编码 | 机器形状 | ELF | 目标实现细节,由 hw-map 领域决定 | - -编译把图降为格的线性投影(`lower_to_ccr()`),格的线性投影承载全部类型与语义信息,形式验证直接消费——这就是语义保鲜:正确性陈述对全部可能执行成立,不按控制流路径陈述。详见 `docs/memory-model-capability-lattice.md`(三层映射 v4 §四)与 `docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md`(格形态 IR)。 +Core 是"数据流图 + 语义保鲜"的语言——代码编译为图,图保留全部语义(形式验证直接消费)。给语法带来的要点: -语言层面的设计要点: - -- **无独立类型系统**:类型 = 图上的标注,接口 = 图上的契约,类型检查 = 图的良构性验证(与指针三 pass 同级)。本文档只描写类型标注的**语法**。 - **无 `let` 关键字**:变量用 `:=` 或 `: Type =` 声明。 -- **无生命周期标注**:引用有效性由编译器全权推断。 -- **一切自动推断,无关键字**——编译器看图自动做,用户零标注负担: - - **类型推断**:`:=` / `auto` / `.` 推断;类型/接口是图的投影,标注可自动算出(语义保鲜,见类型方向定案) - - **所有权推断**:默认借用——传参、赋值自动借用,显式 `move` 转移所有权 - - **编译时执行**:图见全常量输入即自动执行,`@comptime` 兜底("算不了就报错") - - **惰性求值**:控制流分支自动惰性——未执行路径不求值,多路径需要时自动升级 eager,**无 `lazy()` 关键字**(见 `docs/lazy.md`) - - **指针安全**:裸地址与 C 同级自由,provenance 自动推导,编译器自动验证 - - **动态类型转换**:`dyn` 边界自动插转换,无需 cast - - **概率推导**:编译期自动进行(probabilistic pass) - - **机器形状**:整数位宽、浮点格式由编码层按目标自动决定(hw-map 领域) +- **无生命周期标注**:引用有效性由编译器自动验证。 +- **一切自动推断,零标注负担**: + - **类型推断**:`:=` / `auto` / `.` 推断,标注可选 + - **所有权**:默认借用,显式 `move` 转移 + - **编译时执行**:全常量输入自动求值,`@comptime` 兜底 + - **惰性分支**:控制流自动惰性(未走路径不求值),无 `lazy()` 关键字 + - **指针安全**:裸指针自由,越界/悬垂由编译器自动拦截 + - **动态类型**:`dyn` 边界自动转换 - **并发 = 流的构造**:`go` / `await` / `flow` / `yield` 构成数据流网络。 +- **规约内联**:`#check`/`#ensure` 写在函数上(设计态,见第 10 章)。 --- @@ -55,7 +45,7 @@ IDENT = LETTER { LETTER | DIGIT | '_' | '\'' } ### 2.3 关键字 -共 37 个(以 `src/compiler/lexer.cr lookup_keyword()` 为准): +共 36 个(以 `src/compiler/lexer.cr lookup_keyword()` 为准,2026-09 核对): ``` fn mut return if else loop while for break continue @@ -65,9 +55,9 @@ auto fileid move self in None Some unit ``` - `self` 是词法关键字,用于方法接收者(`self` / `&self` / `&mut self`)。 -- `Self` 不是词法关键字——它是接口/方法签名中的上下文类型名,按标识符解析,由 checker 按上下文处理。 +- `Self` 不是词法关键字——它是接口/方法签名中的上下文类型名,按标识符解析,由编译器按上下文处理。 - `comptime` 不是关键字(`@comptime` 是 @ 内建原语)。 -- `requires` / `ensures` / `old` / `result` 不是词法关键字——它们是规约层的语法(见第 10 章与 `grammar/corespec.ebnf`)。 +- `#check`/`#ensure`/`#tag`/`spec fn` 不是词法关键字——规约是标注层语法(第 10 章;设计定稿 = maintainer/design/spec-design.md;实现态:parser 未支持)。 ### 2.4 字面量 @@ -89,7 +79,10 @@ auto fileid move self in None Some unit |------|------| | `:=` | 变量声明(无 `let`) | | `=` | 赋值(含声明初始化 `: T =`) | +| `+=` `-=` `*=` `/=` | 复合赋值(`x += y` = `x = x + y`;无 `%=`) | | `->` | 返回类型(仅此用途) | +| `=>` | match 臂分隔(见 7.3) | +| `...` | 变参前缀(`...name: Type`,见 8.1) | | `*` | 乘 / 解引用 | | `&` `&mut` | 取地址(引用) | | `+ - / %` | 算术 | @@ -192,23 +185,25 @@ Type = BaseType | PathType | RefType | OptionalType | TupleType | ArrayType | Sl | `char` | Unicode 标量值 | | `unit` | 空值 `()` | | `never` | 发散类型 | -| `dyn` | 动态类型——图全量已知所有分支,边界自动插转换(详见 `docs/dynamic-typing.md`) | +| `dyn` | 动态类型——图全量已知所有分支,边界自动插转换(详见 `docs/maintainer/proposals/dynamic-typing.md`) | | `Self` | 上下文类型名,仅在接口/方法签名内有效(实现类型本身) | **复合类型**: ```core pair : (int, bool); // 元组 -zeros : [int; 16]; // 数组(长度是常量) -slice : [int]; // 切片 +zeros : [int; 16]; // 数组(长度是常量) +slice : [int]; // 切片(无界序列) maybe : int?; // 可选 ref : &Point; // 引用 mutr : &mut Point; // 可变引用 ``` +> 类型形态以 2026-09-01 语法重写定稿为准(计划优先);切片类型的运行时边界完整化(high 界进值)按 TODO 推进,当前编译器的切片操作语义以源码为准。 + **接口作类型**:任何接口名可作为类型标注(`PathType`),见第 9 章。 -**机器形状不在语言内**:整数位宽、浮点格式等机器形状由编码层按目标自动决定,不进入类型/标签体系;未来显式控制走 hw-map。 +**机器形状不在语言内**:整数位宽、浮点格式等机器形状由目标平台决定,不进入类型/标签体系。 ### 4.2 标注位置 @@ -277,7 +272,7 @@ Assignment → LogicalOr → LogicalAnd → Equality → Comparison | 优先级 | 运算符 | 结合性 | |--------|--------|--------| -| 最低 | `=`(赋值)`:=`(声明) | 右 | +| 最低 | `=`(赋值)`:=`(声明)`+= -= *= /=`(复合赋值,展开为 `x = x op y`) | 右 | | | `\|\|` | 左 | | | `&&` | 左 | | | `==` `!=` | 左 | @@ -435,7 +430,7 @@ fn add(a: int, b: int) -> int { fn pi() -> auto = 3.14159; // 单行形式;返回类型推断 ``` -`FunctionDecl = [ 'pub' ] 'fn' IDENT [ GenericParams ] '(' [ ParamList ] ')' '->' Type ( FunctionBody | '=' Expr ';' )`。参数 `Param = IDENT ':' Type`。返回类型可以是 `auto`(或 `.`),由函数体推导。 +`FunctionDecl = [ 'pub' ] 'fn' IDENT [ GenericParams ] '(' [ ParamList ] ')' '->' Type ( FunctionBody | '=' Expr ';' )`。参数 `Param = IDENT ':' Type`;末位参数可变参:`...name: Type`(收集剩余实参,见 stdlib variadic)。返回类型可以是 `auto`(或 `.`),由函数体推导。 ### 8.2 方法 @@ -503,14 +498,14 @@ type Point3 = (dex, dex, dex); ## 十、规约 -规约(requires / ensures)与函数体并列,可选编写,参与静态检查,不影响运行时性能。规约语法详见 `docs/spec-design.md` 与 `grammar/corespec.ebnf`。 +规约(#check/#ensure 标注,2026-09 语法定稿——与 #pure/#terminating 自动推导标签同族)与函数体并列,可选编写。设计定稿见 docs/maintainer/design/spec-design.md;语法归口 grammar/core.ebnf(并入为迁移事项;.corespec 独立格式已退役,见 docs/maintainer/adr/adr-0001-corespec-crasm-retired.md)。**实现状态:设计态——lexer/parser 尚未实现规约语法**,示例为定稿形态。 -### 10.1 requires / ensures +### 10.1 #check / #ensure ```core fn divide(a: int, b: int) -> int? - requires b != 0 - ensures result.is_some() implies (a / b) == result.unwrap() + #check(b != 0) + #ensure(result.is_some() implies (a / b) == result.unwrap()) { if b == 0 { return None; @@ -519,12 +514,13 @@ fn divide(a: int, b: int) -> int? } ``` -- `requires` —— 调用方必须满足的前提条件 -- `ensures` —— 保证的后置条件;`result` 指返回值,`old(expr)` 指函数入口时表达式的值 +- `#check(...)` —— 调用方必须满足的前提条件 +- `#ensure(...)` —— 保证的后置条件;`result` 指返回值,`old(expr)` 指函数入口时表达式的值 +- `spec fn` —— 用 Core 写的纯检查函数(验证标准,见 maintainer/design/spec-design.md §七) ### 10.2 where 值约束 -`where` 实现内联轻量前置条件,与 `requires` 同层: +`where` 实现内联轻量前置条件(行内形态,与 #check 同层;定稿形态以 spec-design 为准): ```core fn sqrt(x: dex) -> dex where x >= 0 { @@ -532,7 +528,7 @@ fn sqrt(x: dex) -> dex where x >= 0 { } ``` -值约束按表达式形态分三档语义(一条语法,两层消费:checker + 验证管线): +值约束按表达式形态分三档语义(编译期求值 / 生成验证义务 / 运行时检查): | 约束形态 | 语义 | |----------|------| @@ -540,7 +536,7 @@ fn sqrt(x: dex) -> dex where x >= 0 { | 符号约束 | 生成 VC(验证义务),由验证管线消费 | | 动态约束 | 运行时检查——「证明不了也不静默」 | -与 `.corespec` 前置条件互相映射。 +与规约前置条件互相映射(规约 = .cr 语法内约束,独立 .corespec 格式已退役,见 ADR-0001)。 --- @@ -588,10 +584,10 @@ for i in 0..5 { ### 12.1 指针 -指针是裸地址,与 C 同级自由,编译器通过格/图自动验证安全(详见 `docs/pointer-model.md`): +指针是裸地址,与 C 同级自由,编译器通过格/图自动验证安全(详见 `docs/maintainer/design/pointer-model.md`): ```core -p := &arr[0]; // 取地址,编译器记下 provenance +p := &arr[0]; // 取地址,编译器自动记录来源与偏移 p = p + n; // 偏移,随便算 x := *p; // 解引用,编译器验证 offset ∈ [0, len) ptr := addr as RawRef; // 显式转换 @@ -618,7 +614,7 @@ unsafe { ## 十三、@ 内建原语 -`@` 开头的标识符是编译器内建的能力入口,不通过标准库实现。分三类(详见 `docs/at-intrinsics.md`)。 +`@` 开头的标识符是编译器内建的能力入口,不通过标准库实现。分三类(详见 `docs/developer/at-intrinsics.md`)。 **元数据查询**——编译期查询类型信息,零运行时开销: diff --git a/docs/learning-path.md b/docs/developer/tutorial.md similarity index 70% rename from docs/learning-path.md rename to docs/developer/tutorial.md index 40cf3000..37e56229 100644 --- a/docs/learning-path.md +++ b/docs/developer/tutorial.md @@ -1,5 +1,7 @@ # Core 渐进式学习路径 +> 定位:受众 = 开发者;状态 = active + Core 是少数不能归入"第一门语言"或"第 N 门语言"二元分类的设计。它的结构使得同一个语言可以陪伴程序员从零基础走到编写操作系统内核。 学习路径自然展开——每个阶段的图结构复杂度递增,概念逐级累积,**没有换过语言,没有切换过"模式",没有需要遗忘的概念**。 @@ -18,7 +20,7 @@ Core 是少数不能归入"第一门语言"或"第 N 门语言"二元分类的 - 循环:`for x in arr`、`for i in 0..n` - 数组 `[T]`、结构体 `struct` -**图结构**:DAG——无环,按拓扑序串行执行。 +**图模型**:DAG——无环,值按依赖顺序流动;不依赖同一份数据的部分互不干扰。 **心智模型**:顺序执行——执行的每一步严格按文本顺序,所有变量值完全可观测、可单步。 @@ -37,7 +39,7 @@ fn sum(a: [int]) -> int { } ``` -**关联**:`docs/language-syntax.md`、`docs/execution-model.md` §5.1 +**关联**:`docs/developer/syntax.md`、`docs/maintainer/design/execution-model.md` §一(唯一执行模型:HDFG) --- @@ -50,9 +52,9 @@ fn sum(a: [int]) -> int { - `while` 条件循环 - 状态保持:可变变量跨迭代存活 -**图结构**:带反馈环的静态图(loop/for 嵌套 region,边界表达迭代与终止依赖)。 +**图模型**:带环的静态图——loop/for 让值流绕圈,循环的"再次执行"由图的环表达。 -**心智模型**:循环调度——每个周期内循环体按拓扑序执行,周期结束回到 region 头部触发下一轮迭代。程序开始出现时间维度。 +**心智模型**:循环 = 图中的环——每轮迭代值沿环流动一周;程序开始出现时间维度(状态跨迭代存活)。 **里程碑**:能写计数器、状态机雏形、轮询循环。 @@ -73,7 +75,7 @@ fn counter() -> int { } ``` -**关联**:`docs/execution-model.md` §5.2、`docs/superpowers/specs/2026-08-08-region-cfg-design.md`(region 结构细节) +**关联**:`docs/maintainer/design/execution-model.md` §三(深入:执行模型与图的实现) --- @@ -83,13 +85,13 @@ fn counter() -> int { **引入的概念**: - 裸指针:`*T` 类型、`&x` 取址、`*p` 解引用、`cast(p)` 转换 -- `unsafe` 块:图边界入口(外部地址、FFI、无 provenance 的场景) +- `unsafe` 块:图边界入口(外部地址、FFI 返回值、无法自动追踪来源的场景) - @ 内建:`@sizeOf(T)`、`@alignOf(T)`、`@fields(T)`、`@comptime`、`@inline` - 内存控制:`alloc_at(addr, size, align)` 声明式放置 -**图结构**:provenance/region——每个指针在 HDFG 上有来源(出生节点)和偏移,解引用点自动验证边界。 +**图模型**:每个指针自带来源(它来自哪次分配/取址)与偏移;解引用时编译器自动核对边界——不需要你标注任何生命周期。 -**心智模型**:指针和 C 一样自由,安全由 HDFG 自动验证(provenance/region/越界三 pass);unsafe 不是"关掉验证",是"标注图边界入口"——进入图内编译器重新获得追踪权。 +**心智模型**:指针和 C 一样自由;安全由编译器自动验证(来源追踪 + 边界核对)。unsafe 不是"关掉验证",是"声明此处是图边界入口"——一进图内,编译器重新获得追踪权。 **里程碑**:能安全地做指针算术、类型双关、外部地址访问——不需要学 borrow checker。 @@ -101,7 +103,7 @@ fn counter() -> int { ```core fn deref(arr: [int], i: int) -> int { - p := &arr[i]; // provenance = arr,偏移 = i + p := &arr[i]; // 来源 = arr,偏移 = i(编译器自动记录) return *p; // 越界 → 编译期拦截或运行时检查 } @@ -111,7 +113,7 @@ unsafe { } ``` -**关联**:`docs/pointer-model.md`、`docs/memory-model.md` +**关联**:`docs/maintainer/design/pointer-model.md`、`docs/maintainer/design/memory-model.md` --- @@ -120,13 +122,13 @@ unsafe { **目标**:理解"数据驱动"——程序是一张图,节点可以并发运行,数据在边上流动。 **引入的概念**: -- `go f(args)`:启动并发执行(独立 HDFG 子图 / fiber) +- `go f(args)`:启动一段并发计算(与主流程并行流动) - `h := go f(...)`:返回句柄 - `await h`:等待结果 - `flow`:可激活的命名子图模板;`yield`:暂停并输出值 - 通道(chan):跨执行流传递数据 -**图结构**:动态图——go 动态创建节点,拓扑在运行时扩展。 +**图模型**:动态图——go 在运行时长出新的子图,与启动它的代码并行流动。 **心智模型**:节点、边、令牌——从"一行一行执行"平滑过渡到"数据驱动":执行顺序由数据可用性驱动,而非文本顺序。 @@ -150,13 +152,13 @@ fn main() -> int { } ``` -**关联**:`docs/concurrency.md`、`docs/execution-model.md` §5.3 +**关联**:`docs/maintainer/proposals/concurrency.md`、`docs/maintainer/design/execution-model.md` §三(并发 GMP) --- ## 形式规约(横切能力) -> ⚠️ **未完成**:本节为设计预览。规约体系当前为**设计态**——语法与 EBNF 已定(`grammar/corespec.ebnf`),但 `.corespec` 编译、`.csr` 序列化、翻译桥、CIC 内核均**未实现**,当前编译器不支持本节内容。实现推进见 `docs/spec-design.md` 与 `TODO.md`。 +> [注意] **未实现**:本节为设计预览。规约形态 2026-09 定稿为 `#check`/`#ensure` 标注(与 #pure/#terminating 标签同族,见 `docs/developer/syntax.md` 第十章与 `docs/maintainer/design/spec-design.md`;独立 `.corespec` 格式已退役,见 adr/adr-0001),但**规约语法与验证管线均未实现**——当前编译器不消费规约。实现推进见 maintainer/design/spec-design.md 与 TODO.md。 **目标**:理解"行为契约"——程序 = 实现 + 保证。 @@ -166,9 +168,9 @@ fn main() -> int { - `#check(...)`:前置条件 - `#ensure(...)`:后置条件(可引用 `result`、`old(x)`) - `spec fn`:检查函数(用 Core 写的纯逻辑函数) -- `.corespec` 规约文件(`requires`/`ensures`/`invariant`/`variant`,见 `grammar/corespec.ebnf`) +- 函数内规约(`#check`/`#ensure` 标注,.cr 内表达——独立规约格式已退役) -**图结构**:图上的约束——规约编译为 .csr 的 TagNode,通过符号引用精确关联 HDFG 节点。 +**图模型**:规约约束挂载在图上(设计态)——性质与代码同源,验证器直接消费同一张图。 **心智模型**:函数不只"做什么",还"保证什么"——意图成为代码的一等公民,验证器可消费。 @@ -183,7 +185,7 @@ fn divide(a: int, b: int) -> int } ``` -**关联**:`docs/spec-design.md`、`docs/ir-schema/corespecir-schema.md`、`grammar/corespec.ebnf` +**关联**:`docs/maintainer/design/spec-design.md`、`docs/developer/syntax.md` 第十章、`docs/maintainer/adr/adr-0001-corespec-crasm-retired.md`(格式退役决策)、`docs/superpowers/specs/`(验证管线设计稿) --- @@ -193,9 +195,9 @@ fn divide(a: int, b: int) -> int | 领域 | 用到的能力层 | 图结构特征 | 部署配置 | 状态 | |------|------------|-----------|---------|------| -| **系统编程**(内核/驱动/协议栈) | ①②③④ + .crasm | 全部结构 | 裸机/无 OS | 能力已实现 | +| **系统编程**(内核/驱动/协议栈) | ①②③④ + unsafe/HIT | 全部结构 | 裸机/无 OS | 能力已实现 | | **嵌入式与实时**(无 MMU/确定性) | ①③ | DAG/静态图 | 静态分配/无 GC | 能力已实现 | -| **并发服务**(服务器/网络后端) | ③④ | 动态图 | 多核/OS 线程 | 已实现(单 M 端到端) | +| **并发服务**(服务器/网络后端) | ③④ | 动态图 | 多核/OS 线程 | 已实现(基础并发:go/chan) | | **数据处理与科学计算** | ①③ | DAG | 通用 | 已实现(dex/数组) | | **编译器与工具链** | ①②③④ | 全部 | 通用 | **自举实证**(语言写自己) | | **安全关键系统**(航空/医疗/金融) | 全部 + 规约 | 全部 + 图上约束 | 验证 | 规约设计态 | @@ -207,12 +209,12 @@ fn divide(a: int, b: int) -> int **新增概念**: - 裸指针:`*T` 类型、`&x` 取址、`*p` 解引用、`cast(p)` 转换 -- `unsafe` 块:图边界入口(外部地址、FFI、无 provenance 的场景) +- `unsafe` 块:图边界入口(外部地址、FFI 返回值、无法自动追踪来源的场景) - @ 内建:`@sizeOf(T)`、`@alignOf(T)`、`@fields(T)`、`@comptime`、`@inline` - 内存控制:`alloc_at(addr, size, align)` 声明式放置、布局控制 -- `.crasm` 汇编层:跨平台统一指令集 + 无限虚拟寄存器 + 平台映射表 +- 硬件接口(HIT):MMIO/特权/中断经 unsafe + HIT extern 事件(独立汇编格式 .crasm 已废弃,见 maintainer/adr/adr-0001) -**心智模型**:字节级控制 + 图边界——指针和 C 一样自由,安全由 HDFG 自动验证(provenance/region/越界三 pass);unsafe 不是"关掉验证",是"标注图边界入口"。 +**心智模型**:字节级控制 + 图边界——指针和 C 一样自由,安全由编译器自动验证;unsafe 是"声明图边界入口",不是"关掉验证"。 ```core unsafe { @@ -221,13 +223,13 @@ unsafe { } ``` -**关联**:`docs/pointer-model.md`、`docs/memory-model.md`、`docs/crasm.md` +**关联**:`docs/maintainer/design/pointer-model.md`、`docs/maintainer/design/memory-model.md`、`docs/maintainer/adr/adr-0001-corespec-crasm-retired.md`(crasm 废弃决策) ### 示例:嵌入式与实时 无 MMU 目标、确定性延迟——图锚定区域(静态路径纯 bump 零碎片、无 GC 停顿)+ 部署配置(`allocation = "static"`)是核心卖点。 -**关联**:`docs/execution-model.md` §3(部署配置)、`docs/memory-model.md` +**关联**:`docs/maintainer/design/execution-model.md` §五(部署配置与时间)、`docs/maintainer/design/memory-model.md` --- @@ -237,7 +239,7 @@ unsafe { |--------|---------|--------|---------| | ① 基础 | 变量、函数、分支、for | DAG | 顺序执行 | | ② 结构化控制流 | loop、while | 带反馈环的静态图 | 循环调度 | -| ③ 指针与内存 | *T、&、unsafe、@内建、alloc_at | provenance/region | 字节级控制 | +| ③ 指针与内存 | *T、&、unsafe、@内建、alloc_at | 来源追踪 + 自动边界验证 | 字节级控制 | | ④ 并发与通信 | go、await、flow、yield、chan | 动态图 | 数据驱动 | | 横切:形式规约 | #check/#ensure/spec fn | 图上约束 | 行为契约 | diff --git a/docs/execution-model.md b/docs/execution-model.md deleted file mode 100644 index 4d40b43c..00000000 --- a/docs/execution-model.md +++ /dev/null @@ -1,282 +0,0 @@ -# Core 执行模型设计书 - ---- - -## 一、核心原则 - -### 1.1 唯一执行模型 - -Core 只有一种执行语义:HDFG。 - -程序员编写的所有代码,经语义 IR 翻译后,构成一张有向图。图的拓扑结构承载了程序的执行语义(依赖、迭代、并发结构): - -| 图结构 | 承载的执行语义 | -|--------|----------| -| 有向无环图(DAG) | 依赖偏序——无反馈,确定性 | -| 带反馈环的静态图 | 迭代语义——循环结构,region 表达迭代与终止 | -| 动态图(运行时创建/销毁节点) | 并发语义——节点创建、生命周期动态管理 | - -程序员不需要声明"模式"——代码写了什么,图就反映什么。执行方式(串行/并行/调度策略)由部署配置决定,图语义不变。 - -### 1.2 部署配置原则 - -物理世界的约束——内存是否可动态分配、是否多核、是否有操作系统——不属于程序的执行语义,而属于部署目标的属性。 - -这些属性在部署配置文件中声明,由编译器据此检查代码兼容性并生成对应代码。代码不变,部署配置可变。 - -I/O 系统调用、时间源等物理实现同理:程序只见语义接口——I/O 语义是流变换(转导器:(输入流) → (输出流)),时钟语义是单调时间序;底层系统调用由后端实现按部署目标隔离,不进入程序语义。 - -### 1.3 渐进式能力解锁 - -Core 的语法特性按图结构复杂度分为三个层级: - -| 层级 | 语法特性 | 产生的图结构 | 心智模型 | -|------|----------|--------------|----------| -| 基础 | 变量、函数、分支、循环(for) | DAG | 顺序执行 | -| 中级 | loop、recv | 带反馈环的静态图 | 循环调度 | -| 高级 | flow、go、yield、await | 动态图 | 并发,有时间片或 OS 线程 | - -初学者从基础层级起步,图是 DAG,执行顺序与文本顺序一致。引入并发语法特性后,图的拓扑随之复杂化——无需声明"模式切换"。 - ---- - -## 二、图承载执行语义 - -### 2.1 DAG:天然的教学与调试执行 - -```core -fn add(x: int, y: int) -> int { - return x + y; -} - -fn main() { - a := 5; - b := 3; - c := add(a, b); - println(c); -} -``` - -生成的图是无环的。执行器按拓扑序串行走节点。 -单步调试即顺序走节点,变量值完全可预测。 - -DAG 结构本身适合教学场景——无需额外的模式声明。 - -### 2.2 静态循环图:有节奏的循环调度 - -```core -fn main() { - data := [1, 2, 3, 4, 5]; - loop { - if data.is_empty() { break; } - item := data.pop(); - println(item); - } -} -``` - -图包含循环 region(loop/for 结构节点,含子区域;region 边界表达迭代与终止依赖)。 -执行器按迭代节奏执行。仍然可串行模拟,可单步跟踪循环迭代。 - -### 2.3 动态图:并发与自适应 - -```core -flow worker(id: int) -> int { - loop { - yield compute(id); - } -} - -fn main() { - w1 := go worker(1); - w2 := go worker(2); - loop { - v1 := await w1; - v2 := await w2; - println(v1 + v2); - } -} -``` - -图在运行时动态扩展:go 创建新节点,yield 暂停当前节点并输出值。有 OS 时每个 go 对应一个线程,无 OS 时 Core 提供抢占式调度器(时间片轮转)。 - ---- - -## 三、资源分配:由部署配置声明 - -### 3.1 代码不规定分配策略 - -Core 代码只描述计算的逻辑和数据的结构,不规定内存从哪来、怎么分配。 -语义 IR 保留数据的类型和边界信息(包括编译期已知的上限),但不固定分配策略。 - -### 3.2 部署配置描述目标平台能力 - -```toml -[deploy.mcu] -target = "cortex-m4" -allocation = "static" # 禁止动态分配 -max_memory = "256KB" - -[deploy.server] -target = "x86-64-linux" -allocation = "dynamic" # 允许动态分配 -``` - -编译时: - -- 目标为 mcu:编译器检查所有可变大小数据结构是否已有编译期确定的上限。若无法确定,拒绝编译并给出明确错误。 -- 目标为 server:编译器生成使用目标平台分配器的代码,允许运行时决定大小。 - -程序员仍然只写一份代码。如果这份代码能在静态分配世界运行,它必须足够保守;如果只能在动态分配世界运行,它就对部署环境有要求。 - -### 3.3 资源配额:运行时可选策略 - -资源配额(CPU 时间片、内存上限、I/O 速率)不属于程序逻辑,属于运营策略——I/O 速率配额约束的是后端流传输的物理速率,不改变程序语义(流变换)。 -在部署配置中可选声明: - -```toml -[deploy.server.quotas.flow.worker] -max_memory = "10MB" -cpu_budget = "5ms" -``` - -不声明即无限制。代码不变。 - ---- - -## 四、时间语义:由部署上下文决定 - -时钟的语义是单调时间序(数学):程序代码中不区分"零时间""物理实时""仿真时间",只要求时间序单调。 - -时间是一个可选的外部输入源——时间源由部署上下文与后端实现决定(平台桥提供 now_monotonic/sleep_ms 语义接口,clock_gettime/nanosleep 等系统调用隔离在后端实现): - -- 裸机或实时 OS:硬件时钟提供物理时间,调度器可据此满足 deadline。 -- 普通 OS:系统时钟提供逻辑时间,保证事件偏序。 -- 仿真环境:仿真器提供可控时间,可加速、暂停、回放。 - -部署配置决定时间源,而非代码决定。程序语义始终是单调时间序,不绑定具体时间源。 - ---- - -## 五、调度器 - -### 5.1 原则 - -Core 不自己实现调度器——除非没有操作系统。 - -``` -有 OS: 用线程,OS 调度,Core 不需要 scheduler -无 OS: Core 自己管,时间片轮转,抢占式 -``` - -部署配置决定,代码不决定: - -```toml -[deploy.linux] -scheduler = "os" # 用线程,OS 调度 - -[deploy.bare_metal] -scheduler = "preemptive:10ms" # Core 自己调度,10ms 时间片 - -[deploy.wasm] -scheduler = "preemptive:5ms" # 同 -``` - -### 5.2 有 OS 环境 - -`go fn()` 创建一个新线程。yield、await 等操作在线程内进行,不涉及 Core 的调度器。OS 负责时间片分配和上下文切换。 - -### 5.3 无 OS 环境 - -Core 提供最小化的抢占式调度器。和 OS 线程调度相同——基于时间片中断,而不是协作式 yield。 - -```core -flow worker(id: int) { - loop { - data := recv(input); - result := compute(data); - yield result; - } -} - -w1 := go worker(1); // 新节点,加入就绪队列 -w2 := go worker(2); // 新节点,加入就绪队列 -``` - -调度器循环: - -``` -1. 时钟中断(或模拟 tick)→ 当前节点时间片到 -2. 保存节点上下文 -3. 选择下一个就绪节点(round-robin / work-stealing) -4. 恢复上下文,继续执行 -5. 节点 yield/await 时主动让出,不等待时间片 -``` - -### 5.4 默认策略 - -有 OS 时全部走 OS 线程,无 OS 时走抢占式。不提供协作式选项。 - ---- - -## 六、静态图与动态图 - -Core 区分静态图和动态图,但这不是模式——它们是图的两个形态: - -### 6.1 静态图 - -编译期已知所有节点和边。 -类型:DAG、带循环的静态图。 -执行:拓扑序、循环调度。 -用途:计算、控制逻辑、无动态创建的流。 - -### 6.2 动态图 - -运行时创建/销毁节点、重连边。 -类型:带有 go、动态 select 的图。 -执行:动态节点生命周期管理。 -用途:长期服务、自适应系统、热升级。 - -初学者从静态 DAG 起步,逐步解锁动态图特性。这不需要"切换模式",只是使用更多的语言能力。 - ---- - -## 七、代码与部署配置分离 - -```core -fn compute(x: int) -> int { x * 2 } - -fn main() { - result := compute(42); - println(result); -} -``` - -这段代码: - -- 在教学环境 → DAG,拓扑序解释执行,可单步、可回退 -- 在桌面 → AOT 编译,直接原生执行 -- 在嵌入式中 → 静态分配,零运行时 -- 在分布式集群 → 可远程部署 compute - -代码不改。图的语义相同。部署配置不同。 - ---- - -## 八、与传统做法的对比 - -| 传统做法 | Core | -|----------|------| -| 代码中声明模式(#[mode(release)]) | 代码无模式概念 | -| 不同模式影响语义或行为 | 图语义唯一 | -| 切换模式可能引入 bug | 部署配置不影响程序正确性 | -| 模式数量膨胀 | 无模式分类 | - -Core 只有一种执行语义:图。 -图结构由代码决定,物理约束由部署配置声明,执行策略由编译器自动选择。 - ---- - -## 九、结论 - -Core 的执行模型可以总结为一句话:代码生成图结构,图承载执行语义,执行方式由部署配置决定。拓扑简单则执行简单,拓扑复杂则执行复杂——不需要人为命名任何一种"模式"。 diff --git a/docs/glossary.md b/docs/glossary.md index 364ddf0d..4b85b45f 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,241 +1,85 @@ -# 术语表(Glossary)——Core 架构术语 +# 术语表(Glossary)——Core 架构术语 -> 定位:全仓库**架构术语**的唯一权威(文档侧)。语言关键字以 `src/compiler/lexer.cr` 为唯一真源(本表不重复)。实现与文档冲突时以源码为准(既有惯例)。 -> -> 状态:2026-08-27 v3——全量扩充(v1 = 57 行主条目;v2 = 全量术语;v3 = 补执行/并发/内存实现/编译器管线/讨论判据)。术语演进记录见 §十三。 +> 定位:受众 = maintainers(兼 contributors);状态 = active;真源 = 各术语「出处」列的文档与 +> `src/compiler/lexer.cr`(语言关键字唯一真源,本表不重复);实现与文档冲突时以源码为准(既有惯例)。 +> 规则:本表只收**架构级术语**,实现细节与设计论证在出处文档,不在本表展开。 +> 演进:2026-08-27 v3 全量扩充 → 2026-09-06 v4 校准(去 .corespec 退役残留、出处链接对齐、重复表压缩)。 -## 一、设计出发点与约束 +## 一、设计出发点 | 术语 | 含义 | 出处 | |---|---|---| -| 语义保鲜 | Core 的设计出发点:IR 保留全部语义与执行硬件解耦——范式迁移时语义不动、只换后端映射 | `z-vision.md` | -| 范式普适 | 语言跨越所有计算范式(含未来未知范式)——所有范式共享统一表达空间,范式差异经映射进入该空间 | 备忘 §2.1 | -| 零硬件惯例 | 核心语义纯数学/逻辑/结构;硬件惯例只允许以「后端实现选择」或「可忽略注解」存在 | 备忘 §2.1 | -| 用户面上手成本极低 | 任何要求用户学习的新概念都是成本(YAGNI 默认:不加) | 备忘 §2.1 | +| 语义保鲜 | 设计出发点:IR 保留全部语义,与执行硬件解耦——范式迁移语义不动、只换后端映射 | z-vision.md | +| 范式普适 / 零硬件惯例 | 语言跨越所有计算范式;核心语义纯数学/逻辑/结构,硬件惯例只作后端实现选择 | z-vision.md | ## 二、三层映射 | 术语 | 含义 | 出处 | |---|---|---| -| 图(HDFG) | 关系空间:发生什么——节点 = 操作、边 = 值流、region 嵌套 = 控制流、state edges = 顺序约束;超图灵性属于图 | `dataflow-design.md` | -| 格(层) | 状态/存储空间 = 中间存在空间:如何存在——条目、配方、驱逐、再生 | `memory-model.md`;备忘 §四 | -| 编码(层) | 物理编码空间:如何实现——把格编码到具体机器(2026-08-27 更名,原「二进制」) | 备忘 §四 | -| 格(Lattice) | 层内组织代数成分(偏序 + join/meet)——**映射参数,非层本体承诺**(无格承诺) | 备忘 §4.4/§4.6 | -| 范式映射表 | 映射正确性定理表——经典行/量子行 = 证明缓存语义 ⊇ 该实现;寄存器映射实例为其一行 | `z-vision.md` | -| 关系空间 / 存在空间 / 物理编码空间 | 图/格/编码三层的数学定位(发生什么 / 如何存在 / 如何实现) | 备忘 §4.2 | +| 图(HDFG) | 关系空间:发生什么——节点/边/region/state edges;超图灵性属于图 | maintainer/design/execution-model.md | +| 格(层) | 存在空间:如何存在——条目、配方、驱逐、再生 | maintainer/design/memory-model.md | +| 编码(层) | 物理编码空间:如何实现(2026-08-27 更名,原「二进制」) | maintainer/design/memory-model.md | +| 格(Lattice) | 层内组织代数成分——映射参数,非层本体承诺(无格承诺) | maintainer/design/memory-model.md | +| 范式映射表 | 映射正确性定理表——证明缓存语义 ⊇ 该实现;寄存器映射实例为其一行 | maintainer/design/memory-model.md | ## 三、IR 形态 -| 术语 | 含义 | -|---|---| -| `.cr` | 源码 | -| `.cir` | 图形态:HDFG + 规约约束(验证 IR) | -| `.ccr` | 格形态:**Core Region Representation**(v6 演进中;现 v5 = 格层线性投影) | -| `.corespec` / `.csr` | 规约层:规约源码 / 规约 IR | -| 图形态 / 格形态 / 编码形态 | 三层映射对应的 IR 形态(编码形态 = ELF) | -| HDFG | 全息数据流图——Core 唯一执行语义 | -| region(SG 段) | 图锚定存在结构(kind/enter/exit/parent/nstart/ncount);区域 = 子图节点的字节域 | -| 线性投影 | 格形态 v5 = 从图线性化的投影形态(后端消费);v6 升级为存在结构本体 | -| 数据可用性驱动 | HDFG 执行顺序由数据可用性驱动,非文本顺序;图只约束语义,不决定物理执行 | -| state edges | 顺序约束(副作用链 + 循环终止依赖)——「先发生」关系 | -| 部署配置 | 执行方式(串行/并行/调度策略)由部署配置声明决定,语义不变 | - -## 四、缓存语义(`memory-model.md` §一 七条 + 4b) - -| 术语 | 含义 | 条款 | +| 术语 | 含义 | 出处 | |---|---|---| -| 条目(entry) | 存储的一项:语义 = (产生它的图节点, 输入边) | 1 | -| 配方(recipe) | 值的产生方式 = 配方节点 + 输入边 | 1 | -| 驱逐(eviction) | 丢弃存储物、保留配方 | 2 | -| 驱逐不变量 | ⟦G ∖ storage(e)⟧ = ⟦G⟧——驱逐任意条目不改变可观测语义(语义保持定理;分配正确性的依据;order-free) | 2 | -| 再生(regeneration) | 重跑配方节点产生可观测等价的值 | 3 | -| 再生等价 | 再生值与原条目可观测等价——rematerialization 的依据 | 3 | -| 边界(boundary) | 图边界无配方条目(MMIO/FFI/输入/测量);语法层 = `unsafe` | 4 | -| 边界公理 | 边界条目 = 符号常量;边界之外才需要 state | 4 | -| 图内不可重算 | 图内无配方条目(神谕/BSS 实数/FIXPT 声明/模糊融合)——驱逐/再生对其不成立 | 4b | -| 无配方条目 | 边界 + 图内不可重算——必须有 home、驱逐必写回(一视同仁,不按范式枚举) | 4/4b | -| 版本化 | 赋值 = 版本化:x₁ 创建、x₀ 失效、绑定移动(「可变」= 绑定可移动的许可) | 5 | -| 地址 = 映射 | `&x` = (条目标识, 偏移);字节地址只是经典投影 | 6 | -| 映射实例 | 缓存语义在具体机器上的实现(区域/arena/字节权限/寄存器/格代数) | 7 | -| 映射实例正确性 | 映射实例保持条款 1–6 可观测语义 = 范式映射表的行定理 | 7 | -| 字节权限层 | Freeable > Writable > Readable > Nonempty > Empty——经典映射实例的权限面 | 7 | -| home | 条目的持久位置(经典映射 = 栈槽) | — | -| 存在区间 | 条目在图层上的存活区间 = 图活性(RegionCheck cur_seq/exit_seq 机制) | — | -| 图活性 | 条目的存活区间(生命周期 = 图活性,非 LIFO) | — | -| 共存 | 两个条目的存在区间相交——对称关系,无传递性 | — | -| 驱逐配对 | 驱逐时材料不丢:写回 home,或配方可再生(= remat);无配方条目必写回 | — | - -## 五、寄存器分配(缓存语义映射实例,`regalloc-cache-mapping.md`) - -| 术语 | 含义 | -|---|---| -| 一致性判定 | 单层验证:共存互斥 / 读点无陈旧 / 驱逐配对 / 调用失效——可判定、局部、条目泛型 | -| 共存互斥 | 每时刻每寄存器至多一个条目的材料 | -| 读点无陈旧 | 读到的材料与条目的当前版本对齐 | -| 调用点失效契约 | 调用点 = 缓存失效边界:caller-saved 失效(材料归 home)、callee-saved 保留(ABI = 缓存保留契约) | -| rematerialization(remat) | 配方重算恢复材料(条款 3 再生等价) | -| 共存偏序 width | 最小寄存器数(Dilworth 定理);区间序 First Fit 贪心 = 最优 | -| order-free 正确性 | 驱逐不变量对全部可能执行成立——强于 CFG 正确性(只对一个线性化成立) | -| 上下文贪心(CAG) | 分配器正式算法:存在结构上的 First Fit 贪心扫描——上下文 = 共存 / 再生成本 / region 生命周期 / 调用点 / 使用次数(`regalloc-cache-mapping.md` §五) | -| spill(溢出) | 寄存器压力下条目的材料移 home(驱逐的分配侧形态)——装载/存储成对 | -| 栈槽(stack slot) | home 的经典实现(函数相对偏移,disp8 友好) | -| 装载 / 存储(load/store) | 材料在 home 与寄存器缓存之间的移动(fill / write-back) | -| 写回(write-back) | 驱逐时材料写回 home(无配方条目必写回) | -| 缓存失效 | 调用点失效契约的动作(caller-saved 材料归 home) | - -## 六、能力(治理层,备忘 v4 §六) - -| 术语 | 含义 | -|---|---| -| 能力 | 治理层概念(v4 定论:**不提升一等公民、可逆、暂不执行**)——语义内容已还原图上(身份 = 图节点、无配方 = 标注) | -| 授权集 | consult / assume / measure / pass——「可能关系」标注,非语义本体 | -| 域约束 | 语义边界(类型签名),非字节长度 | -| 派生源 | 例外入口授权从哪个声明/能力派生——信任链可追踪 | -| 能力树 | 寻址/派生/撤销/隔离一体(seL4 CNode / KeyKOS-EROS 先例;编译期兑现,运行期零新增) | -| 例外入口 | 不可验证/不可追踪入口(与 unsafe 同层;验证侧对应 = ω-规则)——编译器无法追踪 provenance 的入口(外部地址/FFI/inline asm) | -| 可能关系 | A ──可能执行──> B——授权标注的图形态(与模糊/非确定统一) | -| 能力流分析 | PointerAnalysis 推广——能力如何派生/传递(编译期消费) | -| PCM | 偏交换幺半群:`·` 分离组合、序从组合导出——区域分量代数(Iris ghost state 底座) | -| 幂集格(布尔格) | 授权分量代数:⊆ 收窄 = 细化,∩/∪ = meet/join | -| 保序积 | (r₁,a₁) ⊑ (r₂,a₂) ⟺ r₁ ⊑ r₂ ∧ a₁ ⊆ a₂——PCM × 布尔格的组合方式 | -| 分数权限 | Boyland π ∈ (0,1] 分级授权——模糊隶属度 [0,1] 同构,扩展点(当前 YAGNI) | -| 可复制性 | 只读/consult = 可复制(share),写/assume = 独占(Iris 1.0→1.1 RA duplicable core 教训) | -| 声明式进图 | alloc_at 先例:声明即注册、生命周期随子图(句柄不透明) | - -## 七、设计原则(v4 定稿) - -| 术语 | 含义 | -|---|---| -| 规则封闭、对象开放 | 层规则零签名(缓存七条永不新建 = 维护有界);层对象(条目/配方/标注)无界(= 表达无界) | -| 无格承诺 | 判定/定理只在关系层面陈述;格(Lattice 代数)= 映射参数,将来挂接 | -| 最弱理论 | 共存关系连预序公理都不需要(对称关系)——最弱理论 = 最多模型 = 无界 | -| 新范式三件事 | 图标注 / CIC 内部定义 / 硬件映射——层本体零改动,不按范式枚举 | - -## 八、执行标注空间(`dataflow-design.md` §8) +| `.cr` / `.cir` / `.ccr` | 源码 / 图形态(HDFG + 规约约束)/ 格形态(v6 = 段表 + ENT 存在结构段,v6-only) | specs/2026-09-05-lattice-ir-v6-format.md | +| HDFG | 全息数据流图——Core 唯一执行语义 | maintainer/design/execution-model.md | +| region(SG 段) | 图锚定存在结构(kind/enter/exit/parent/nstart/ncount) | maintainer/design/execution-model.md | +| state edges | 顺序约束(副作用链 + 循环终止依赖) | maintainer/design/execution-model.md | +| 线性投影 | v5 格形态 = 从图线性化的投影;v6 升级为存在结构本体 | specs/lattice-ir-v6-format | +| 部署配置 | 执行方式由部署配置声明决定,图语义不变 | maintainer/design/execution-model.md | -| 术语 | 含义 | -|---|---| -| ITER | 迭代语义(因果循环,现状) | -| FIXPT | 一致解语义(非因果环)——声明性、执行后置 | -| 一致解 | 环上算子的不动点——值使环自洽,不迭代执行 | -| Knaster-Tarski 不动点 | μ/ν 最小/最大不动点——FIXPT 语义的理论(验证内核既有理论) | -| FUZZY | 模糊软分支(格值隶属度 [0,1],两路执行按隶属度加权融合) | -| t-范数 | 模糊逻辑合取算子——Lukasiewicz / Gödel / Product 三种基本选择 | -| 剩余格 | 模糊逻辑代数底座(t-范数 + 蕴涵,Hájek BL-代数系) | -| Oracle(神谕) | 超图灵·神谕:「此节点计算函数 F」,F 可不可计算;跳变层级 ∅ < ∅′ < ∅″(Turing 1939) | -| BSS | 超图灵·连续:无限精度实数 = 原子符号(Blum-Shub-Smale) | -| ITTM | 超图灵·超限:序数时间位置、limsup 极限阶段(Hamkins-Lewis) | -| CORE | 共归纳:流 = 终余代数(F X = D×X)——同步数字电路的语义;可验证,非例外入口 | -| CoNode | 流节点 = (S→T×S)×S(I/O 转导器形态) | -| 保护递归 | ▷ 算子 + Löb 不动点——生产率保证 | -| 双相似 | 共归纳观察等价(流等价) | -| ω-规则 | 无穷长推导规则——超图灵结构验证的形式化对应(内核理论已包含) | - -## 九、验证体系(`spec-design.md`) +> 注:.corespec/.csr 独立规约形态已退役(2026-09-06)——规约 = .cr 语法内约束,见 maintainer/adr/adr-0001。 -| 术语 | 含义 | -|---|---| -| 规约层 IR | 程序「应该做什么」——前置/后置条件、循环不变量、抽象规约 | -| requires / ensures | 函数规约的前置/后置条件子句(corespec.ebnf) | -| loop invariant / loop variant | 循环不变量 / 循环界(终止性) | -| `#check` / `#ensure` / `spec fn` | 用户面规约语法(不需要学数理逻辑) | -| 翻译桥 | 规约 → CIC 项的自动编译 | -| CIC 内核 | Coq 构造演算内核——健全性底座(求解器不可信,健全性在内核) | -| SMT 证书 | 求解器输出、可被内核独立核验;解不出时返回反例模型 | -| 生成器 + 验证器 | 生成器与验证器分离、验证管输出(分配算法 = 上下文贪心,零证明;VeriLocc 2025 同构先例) | -| 证书层 | 验证「最优」而不只是「一致」——最优性证书(DP 表重放或 ILP 对偶) | -| Graph → Lattice 验证主线 | 8 项清单(备忘 §十二)——每项通过 = 三层链成立的证据 | -| 类型不变量 / 抽象规约 | 规约层 IR 条目形态(type invariant / abstract contract) | -| 边界条件(loop variant) | 循环终止性证明(变体递减) | -| 反例模型 | SMT 解不出时返回「哪个输入违反性质」——用户可读反例 | -| 证明驱动优化 | 已证性质回流入优化器(证明与优化互哺) | - -## 十、指针安全与内存管理(`pointer-model.md`) +## 四、缓存语义(核心七条 + 边界) -| 术语 | 含义 | -|---|---| -| PointerAnalysis(pts) | 建立 points-to 关系——指针可能指向的分配块 | -| RegionCheck | 区域逃逸检查——跨区域引用 outlives 判定(引用者存活区间 ⊆ 被引用区域存活区间);存在区间的来源 | -| ProvenanceVerify | 解引用点边界验证(off + width ≤ alloc_size)——从 ALLOC 到 DEREF 的路径 = 安全证明 | -| 图锚定区域 | 区域 = 子图节点的字节域,非词法作用域影子;bump 分配 + 游标重置回收 | -| split / merge / share | 区域沿图边操作:划给子消费者 / 汇合点合并 / 分叉点只读共享 | -| alloc_at | 声明式进图:地址 + 大小 + 对齐声明式进图获得 provenance | -| provenance | 字节/指针的来源(ALLOC 节点)与归属 | -| 例外入口授权 | 例外入口的能力授权形态(可追踪,信任链)——`unsafe` / `alloc_at` 为创建点 | - -## 十一、数值与语言机制 +| 术语 | 含义 | 条款 | +|---|---|---| +| 条目(entry)/ 配方(recipe) | 存储的一项 = (产生它的图节点, 输入边);配方 = 值的产生方式 | 1 | +| 驱逐不变量 | ⟦G ∖ storage(e)⟧ = ⟦G⟧——驱逐任意条目不改变可观测语义(order-free) | 2 | +| 再生(regeneration) | 重跑配方节点产生可观测等价的值 | 3 | +| 边界(boundary) | 图边界无配方条目(MMIO/FFI/输入/测量);语法层 = unsafe | 4 | +| 版本化 | 赋值 = 版本化:x₁ 创建、x₀ 失效、绑定移动 | 5 | +| 地址 = 映射 | `&x` = (条目标识, 偏移);字节地址只是经典投影 | 6 | +| 映射实例正确性 | 映射实例保持条款 1-6 = 范式映射表的行定理 | 7 | -| 术语 | 含义 | -|---|---| -| dex | 精确十进制数值类型(抽象升华数值侧落地) | -| apx | 近似数值类型(授权注解先例) | -| 惰性求值 | 控制流分支自动惰性——未执行路径不产生求值开销(判定表见 `lazy.md`) | -| eager | 立即求值(副作用/IO/FFI/unsafe/volatile 永远 eager) | +> 完整七条 + 字节权限层/home/存在区间/驱逐配对等扩展术语:maintainer/design/memory-model.md。 -## 十二、执行模型(`dataflow-design.md`) +## 五、寄存器分配(缓存语义映射实例) -| 术语 | 含义 | -|---|---| -| 图只约束语义 | 图约束依赖/迭代/并发结构,不决定物理执行(执行方式由部署配置决定) | -| 三种图形态 | DAG(确定性顺序)/ 带环静态图(loop 迭代)/ 动态图(flow/go/yield/recv 并发) | +一致性判定/共存互斥/读点无陈旧/调用点失效契约/remat/共存偏序 width/order-free/ +上下文贪心 CAG/spill/栈槽/装载存储/写回——全部术语定义与论证见 +maintainer/design/regalloc-cache-mapping.md(正式参考),本表不重复。 -## 十三、执行模型与并发细节 +## 六、执行标注空间 | 术语 | 含义 | |---|---| -| flow / go / yield / recv / select | 动态图节点——运行时拓扑可变(节点创建、流传递、并发) | -| DFNode / DFEdge | 数据流图节点 / 边(值流 + state edges) | -| SG_IF / SG_LOOP / SG_FOR / SG_FLOW / SG_UNSAFE | 结构化 region 种类(region 嵌套 = 结构化控制流) | -| 子图 | 图锚定区域 / 能力树的锚定单位 | -| 值流 | 图边 = 值流(数据依赖) | -| 节点激活 | 执行时激活节点——激活时其区域设为当前区域(分配上下文) | -| 令牌(token) | 缓存材料的占位表示——驱逐 = 丢弃材料,再生 = 重跑配方节点 | -| 消息区域 | 并发/异步消息传递的区域隔离形态 | +| ITER | 迭代语义(因果循环,现状) | +| FIXPT | 一致解语义(非因果环)——声明性、执行后置 | +| 一致解 | 环上算子的不动点——值使环自洽,不迭代执行 | -## 十四、内存实现细节 +## 七、编译器管线 -| 术语 | 含义 | -|---|---| -| bump 分配 | 区域内线性指针碰撞分配(O(1)) | -| 游标(cursor) | 区域单调递增分配指针;回收 = 整体重置(格式化清空) | -| size class | 分档子区域——动态碎片控制(浪费上界由分档表决定) | -| 无碎片 | 静态已知大小分配路径保持纯 bump,零碎片 | -| 越界守卫 | off + width ≤ alloc_size(IR_BOUNDS_CHECK / ProvenanceVerify 落地) | -| alloc_size | 分配块的字节大小(边界验证的输入) | -| 字节域 | 区域的字节实现形态(子图节点的字节域) | +| 术语 | 含义 | 出处 | +|---|---|---| +| corec / corearch | 前端(lex→check→ir→ccr)/ 后端(.ccr→ELF),.ccr 为接口契约 | maintainer/adr/adr-0004 | +| 三级自举 | Stage 0(Python)→ Stage 1(corec)→ Stage 2(corec2);三阶段字节一致 | maintainer/onboarding.md | +| 自举阻塞项 | corec2 tokenizer 死循环(9 全局变量未注册 g_ir_globals) | TODO.md | -## 十五、编译器管线 +## 八、验证体系 -| 术语 | 含义 | -|---|---| -| 自举(self-hosting) | Python bootstrap 编译 Core 自身——编译器用 Core 写 Core | -| corec / corearch | 前端 / 后端可执行文件(.cr → .ccr / .ccr → ELF) | -| lower_to_ccr | 图节点拷贝为线性指令数组(图 → 线性投影) | -| monomorphization | 泛型函数在调用点特化实例化 | -| 两遍检查 | checker 先注册声明、后类型检查(两遍) | -| 版本区间 | IR_STORE 切分的条目版本存活范围(比 first/last 全函数区间精确) | +| 术语 | 含义 | 出处 | +|---|---|---| +| 规约层 | 程序"应该做什么"的约束——2026-09 起并入 .cr 语法(独立格式退役) | maintainer/adr/adr-0001、maintainer/design/spec-design.md | +| CIC 内核 / SMT 证书 | 信任根 = CIC;自动化 = 证书外包(计算在外、健全性在内) | academic/verifier-kernel.md | +| 证明驱动优化 | 已证性质回流入优化器 | maintainer/design/spec-design.md | -## 十六、讨论判据与算法参考 +## 九、讨论判据与术语演进 -| 术语 | 含义 | -|---|---| -| 还原测试 | 判断概念是否独立的测试:能否还原为「格 + 条件 + 操作」(M1 判据) | -| 无边界抽象风险 | 一个概念同时承担太多语义(权限/资源/技能/接口/潜能/权能)时失去解释力 | -| 提升判据 | 「图 + 格 + 映射规则」无法自然表达时才提升一等概念(备忘 #11)——当前未触发 | -| 跨范式发生在图层 | 范式差异首先被图吸收(标注空间),格/编码层不需要知道范式 | -| 图着色(Chaitin) | 寄存器分配经典算法(**已排除**:机制重、验证贵)——CFG 系退化实例 | -| 线性扫描 | 寄存器分配经典算法(已排除方向的最简形态)——CFG 系退化实例 | -| SSA / 弦图 / 完美消除序(PEO) | SSA 干扰图 = 弦图、PEO 贪心着色最优(**已排除**:需 IR 转 SSA)——CFG 系退化实例 | -| 生成器 + 验证器 | 生成器与验证器分离、验证管输出(§九 已列;判据视角:启发式不可证 → 验证落在输出上) | - -## 十七、术语演进记录(2026-08-27) - -| 旧 | 新 | 原因 | -|---|---|---| -| 二进制(层名) | 编码 | 零硬件惯例:二进制编码是经典惯例(量子/模拟/光学皆非二进制) | -| `.ccr` = Core Control-flow Representation | **Core Region Representation** | CFG 定位过时;v5 已是 SG region 段 | -| 线性 CFG IR | 格形态(v6 演进中) | CFG 淘汰 → 格(正确性 order-free) | -| 能力(语义本体候选) | 能力(治理层;不提升一等公民) | M1 定论:语义内容还原图上 | -| 律封闭物开放 | 规则封闭、对象开放 | 中二措辞清理(律/物 → 规则/对象) | -| 逃生舱 / 逃生门 / 逃生通道 | 例外入口 | 中二措辞清理(科幻味 → 严肃) | -| 内存模型 v3 讨论中 | v4 发布 | 律封闭物开放 / 无格承诺 / M1 定论 / 分配 = 映射实例 | +- 判据(何时一个新词值得进本表):跨文档引用 ≥2 处且语义唯一;否则留在出处文档 +- v3(2026-08-27)全量扩充 → v4(2026-09-06)校准;历史演进细节见 git 历史,不设演进记录节 diff --git a/docs/ir-schema/coreir-schema.md b/docs/ir-schema/coreir-schema.md index 5f909c51..596b8565 100644 --- a/docs/ir-schema/coreir-schema.md +++ b/docs/ir-schema/coreir-schema.md @@ -1,5 +1,7 @@ # Core IR Schema — HDFG (.cir / .csr) +> 定位:受众 = 维护者(后端/ccr_io 实现者);状态 = active(实现期规格);真源 = src/compiler/ccr_io.cr 与 ast.cr。 + ## 概述 Core 编译器使用两种中间表示: @@ -153,7 +155,7 @@ DFNode 覆盖两种节点:普通指令节点(opcode ≤ IR_AWAIT)和规约 ### ALLOC_AT 节点(声明式放置,2026-08-13) -固定地址放置的声明式进图节点(图锚定区域内存模型,见 `docs/memory-model.md` 机制 #3): +固定地址放置的声明式进图节点(图锚定区域内存模型,见 `docs/maintainer/design/memory-model.md` 机制 #3): | 字段 | 含义 | |------|------| diff --git a/docs/ir-schema/corespecir-schema.md b/docs/ir-schema/corespecir-schema.md index 72b294cf..c7620d85 100644 --- a/docs/ir-schema/corespecir-schema.md +++ b/docs/ir-schema/corespecir-schema.md @@ -1,5 +1,7 @@ # Core Spec IR Schema — 规约约束层 (.csr) +> 定位:受众 = 维护者(规约约束层实现者);状态 = active;真源 = src/compiler/ccr_io.cr(.csr 读写)+ maintainer/design/spec-design.md。 + ## 概述 `.csr` 是 `.cir`(HDFG)的规约约束扩展的二进制序列化格式。它不是独立的 IR,而是 `.cir` 的补充——携带规约约束元数据(check/ensure/invariant/标签),与 `.cir` 的 DFNode 数组通过节点索引精确关联。 @@ -112,4 +114,4 @@ - **验证执行**(spec-design v2):规约表达式(`#check`/`#ensure`/`spec fn` 体)经**翻译桥**编译为 CIC 项,走 CIC 内核 + SMT 证书双通道;`.csr` 的 `status` 字段(0=unproven, 1=auto_proven, 2=user_proven)承接验证结果 - **自动推导**:编译器从 `.cir` 图结构推导的标签(`#pure`/`#terminating`/`#safe_index` 等)写入 `.csr`(status=auto_proven) -即:**本 schema 定义"约束存在哪里",spec-design v2 定义"约束如何被验证"**——两者分层,v2 不改变本格式。完整验证工作流见 `docs/spec-design.md`。 +即:**本 schema 定义"约束存在哪里",spec-design v2 定义"约束如何被验证"**——两者分层,v2 不改变本格式。完整验证工作流见 `docs/maintainer/design/spec-design.md`。 diff --git a/docs/maintainer/adr/README.md b/docs/maintainer/adr/README.md new file mode 100644 index 00000000..4ac30d04 --- /dev/null +++ b/docs/maintainer/adr/README.md @@ -0,0 +1,48 @@ +# 决策记录(ADR) + +> 定位:受众 = maintainers;状态 = active;真源 = 各 ADR 正文(与 git 历史一致时以历史为准)。 +> 规则:每份 ADR 记录一个已做决策的"为什么";设计细节不重复,见正文"关联"指向的文档。 + +## 编号规则 + +- 从 0001 起递增;新决策落地时追加,不修改已 accepted 的历史记录 +- 状态:accepted(已采纳)/ superseded by ADR-000N(被取代)——被取代的保留原文,新决策另起新号 +- 模板: + +```markdown +# ADR-000N: 标题 + +- 日期:YYYY-MM-DD +- 状态:accepted +- 决策者: + +## 背景 +## 决策 +## 后果(正面/负面) +## 关联 +``` + +## 索引 + +| 编号 | 标题 | 状态 | 日期 | +|---|---|---|---| +| ADR-0001 | .corespec/.crasm 独立格式退役——规约并入 .cr 语法 | accepted | 2026-09-05/06 | +| ADR-0002 | .ccr v6 段表架构 + ENT 存在结构段 + REG 坐标化 | accepted | 2026-09-05 | +| ADR-0003 | 自举后端 x86-64 ELF 直出 | accepted | 2026-08 | +| ADR-0004 | corec/corearch 前后端拆分(.ccr 为接口契约) | accepted | 2026-05-28(8 月自举定格) | +| ADR-0005 | 内存模型分层定稿(图 → 格 → 编码)+ 能力定位 v4 | accepted | 2026-08-26/27 | +| ADR-0006 | 缓存语义 = 存储语义本体(2026-08-15 纠偏) | accepted | 2026-08-15 | +| ADR-0007 | 图锚定区域内存模型 + 逃逸 outlives 修订 | accepted | 2026-08-13 | +| ADR-0008 | 执行模型 region 化(RVSDG 嵌套 + state edges) | accepted | 2026-08-08 | +| ADR-0009 | 规约系统 v2(CIC 内核 + SMT 证书架构) | accepted | 2026-08-11 | +| ADR-0010 | 数值类型 dex/apx——精确/授权二分 | accepted | 2026-08-16 | +| ADR-0011 | 平台桥抽象(I/O 流/随机/哈希/时钟) | accepted | 2026-08-16 | +| ADR-0012 | int 无上限语义(数学整数 + 编码层投影) | accepted | 2026-08-23~09-06 | +| ADR-0013 | 类型系统方向 14 条(自动推导/借用收缩/声明式边界) | accepted | 2026-08-30 | +| ADR-0014 | 惰性求值——编译期下沉路线 | accepted | 2026-08-09 | +| ADR-0015 | HIT 硬件接口表(取代 .crasm 汇编层) | accepted | 2026-09-05 | +| ADR-0016 | 多 Arena 内存模型(取代全局 bump) | accepted | 2026-07-28 | +| ADR-0017 | 并发模型 = Go 风格 GMP 简化 | accepted | 2026-07-30/31 | +| ADR-0018 | 仓库治理——GitFlow + 双 ruleset + CI | accepted | 2026-08-09 | +| ADR-0019 | 项目启动与自举路线(Python bootstrap + 自托管) | accepted | 2026-04~07 | +| ADR-0020 | IR 双形态确立(.cir 图 / .ccr 格) | accepted | 2026-05-28 | diff --git a/docs/maintainer/adr/adr-0001-corespec-crasm-retired.md b/docs/maintainer/adr/adr-0001-corespec-crasm-retired.md new file mode 100644 index 00000000..3f178ee2 --- /dev/null +++ b/docs/maintainer/adr/adr-0001-corespec-crasm-retired.md @@ -0,0 +1,30 @@ +# ADR-0001: .corespec/.crasm 独立格式退役——规约并入 .cr 语法 + +- 日期:2026-09-05/06 +- 状态:accepted +- 决策者:DslsDZC +- 关联提交:`582a1e1`(refactor: .corespec 退役);crasm 废弃标注(2026-09-05) + +## 背景 + +仓库一度存在两套独立格式:`.corespec`(规约/契约文件)与 `.crasm`(统一汇编抽象层)。二者 +同构于同一思路——为特定职责发明独立文件格式与语法。代价:格式越多,真源越散(lexer/parser/ +ebnf/后端各要维护一套),且独立格式绑定经典硬件过深(寄存器/寻址/ISA 助记符/私有平台映射表)。 + +## 决策 + +- **.corespec 退役**(2026-09-06,提交 582a1e1):独立规约格式取消——规约 = .cr 语法唯一表达 +- **.crasm 废弃**(2026-09-05):独立汇编格式取消,能力由硬件接口表(HIT)吸收——跨平台 = HIT + 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件 +- 语法责任并入 `grammar/core.ebnf`;`.corespec` 文件标注退役,CLAUDE.md 同步 + +## 后果 + +- 正面:真源收敛——语言语法只有一个表达(.cr);维护面缩小(无私有格式链) +- 负面:core.ebnf 与 CLAUDE.md 需挂账清理残余 .corespec 引用;spec/ 目录残留 7 行占位文件待清; + 既有 .corespec 资产(regalloc-consistency 已迁 .cr 注释契约)需按新形态改写 + +## 关联 + +- 文档:docs/maintainer/design/crasm.md(废弃收尾);grammar/core.ebnf +- 后续:spec/ 目录 .corespec 占位清理(挂账) diff --git a/docs/maintainer/adr/adr-0002-ccr-v6-segment-table.md b/docs/maintainer/adr/adr-0002-ccr-v6-segment-table.md new file mode 100644 index 00000000..976e7d8b --- /dev/null +++ b/docs/maintainer/adr/adr-0002-ccr-v6-segment-table.md @@ -0,0 +1,33 @@ +# ADR-0002: .ccr v6 段表架构 + ENT 存在结构段 + REG 坐标化 + +- 日期:2026-09-05 +- 状态:accepted +- 决策者:DslsDZC +- 关联提交:`3a287cd`(feat: v6 SYM 归并/REG 坐标化)、v6 spec(2026-09-05-lattice-ir-v6-format.md) + +## 背景 + +.ccr(格形态 IR)早期为线性指令投影(v5 前),全局变量/函数记录/寄存器信息分散,缺乏 +结构化承载。v6 的目标形状(spec §3.2/§3.5):全局数据段带类型、函数记录声明区并入 vars、 +寄存器以"坐标"描述生命周期。 + +## 决策 + +- **v6 = 段表架构**:.ccr 按段组织(存在结构段 ENT、数据段、函数记录段等),段表为格式骨架 +- **ENT 存在结构段**:声明"什么东西存在"于后续各段,loader 侧有 entries_coexist 上界校验等 + 一致性守卫(GC 批) +- **REG 坐标化**:REG 记录带 kind/parent/enter/exit/first/last;root_region span 派生函数边界 +- **SYM 归并**:vars 并入函数记录声明区,globals 带 type +- 写读双端(ccr_io.cr)与格式 spec 同步演进 + +## 后果 + +- 正面:v5 前 .ccr 的线性形态被结构化段表取代——dump/验证/loader 都有明确格式锚点; + REG 坐标让寄存器分配元数据可序列化、可对照验证(regalloc-consistency 三层定位) +- 负面:v5 前 .ccr 文件不兼容(格式边界硬切);ELF 发射层需保持零变化前提下适配 + (v6 落地时已验证发射零变化) + +## 关联 + +- 文档:spec 2026-09-05-lattice-ir-v6-format.md;docs/maintainer/design/ir-op-semantics.md +- 提交:88272207(GC 批 3 定向测试)等 v6 系列 diff --git a/docs/maintainer/adr/adr-0003-selfhost-elf-direct-emit.md b/docs/maintainer/adr/adr-0003-selfhost-elf-direct-emit.md new file mode 100644 index 00000000..6399234b --- /dev/null +++ b/docs/maintainer/adr/adr-0003-selfhost-elf-direct-emit.md @@ -0,0 +1,33 @@ +# ADR-0003: 自举后端 x86-64 ELF 直出(放弃 .s 汇编中介) + +- 日期:2026-08(自举贯通 2026-07-27) +- 状态:accepted +- 决策者:DslsDZC + +## 背景 + +自举需要编译器自身编译自身。若后端产出 `.s` 汇编再经外部 `as`/`ld` 汇编链接,则: +(1) 依赖外部工具链,自举链脆弱;(2) 汇编器行为差异导致构建不可复现;(3) 无法做 +字节级一致性验证。早期 Python 后端(StackAsmGen/arm64_asm)走 .s 路线,自举阶段发现 +需要 ELF 直出能力。 + +## 决策 + +- 后端直接发射 **x86-64 ELF 二进制**(`src/arch/linux/ld/`:elf.cr/instr.cr/sizes.cr/ + resolve.cr/ld.cr),跳过 `.s` 中介 +- 动态链接能力保留(PLT/GOT、.so 加载,ld.cr),静态链接为默认路径 +- corec/corearch 拆分(见 ADR-0004)后由 corearch 承担发射;rt.s 手工汇编保留为运行时种子 +- legacy_asm_backend/ 移出主链,Python 后端仅用于 bootstrap 构建 + +## 后果 + +- 正面:三阶段自举(corec→corec2→corec3)可连续发射且**二进制逐字节一致**(2026-07-27 贯通); + 无外部 as/ld 依赖;O0/O2 寄存器分配元数据可序列化并由自举后端读取 +- 负面:指令编码(REX/ModRM/SIB)全部自维护——每修一个编码 bug 都是全链回归; + 新平台支持成本高(需重写发射器,ARM64 仍走 .s) + +## 关联 + +- 提交:2026-07-27 自举贯通批;后端修复批(SIGFPE/SIGSEGV 解除、64 位编码、CCR 有符号字段) +- 文档:docs/maintainer/design/execution-model.md(后端管线);docs/pseudocode/arch/linux/ld/(TDD 交付物) +- 相关 ADR:ADR-0004(corec/corearch 拆分) diff --git a/docs/maintainer/adr/adr-0004-corec-corearch-split.md b/docs/maintainer/adr/adr-0004-corec-corearch-split.md new file mode 100644 index 00000000..c77c46f5 --- /dev/null +++ b/docs/maintainer/adr/adr-0004-corec-corearch-split.md @@ -0,0 +1,31 @@ +# ADR-0004: corec/corearch 前后端二进制拆分(.ccr 为接口契约) + +- 日期:2026-05-28(拆分,随 .core → .cr 更名);2026-08(自举贯通后职责定格,corearch 独立发射) +- 状态:accepted +- 决策者:DslsDZC +- 关联提交:main.cr/corearch.cr 拆分批(CLAUDE.md 已记录) + +## 背景 + +2026-05-28 将单编译器拆为前后端(.core → .cr 更名同期);自举管线进一步要求"前端编译 .cr → 中间产物 → 后端发射 ELF"两步可独立驱动:corec2 自我编译 +时,前端需反复调用后端;若前后端同体,则无法单独验证后端对同一 .ccr 的发射一致性, +也无法分离调试(前端死循环与后端 SIGFPE 会互相掩盖)。 + +## 决策 + +- 拆为两个二进制:**corec**(前端:lex→parse→check→ir→ccr 写出)与 **corearch**(后端: + .ccr 读入 → ELF 发射),corearch 亦可单独跑 `--elf --static` +- **.ccr 文件 = 前后端接口契约**:v5 线性指令/变量段(后演进为 v6 段表,见 ADR-0002) +- corec `build` 子命令内部自动调用 corearch;用户也可手工分步(如逐字节对比发射) + +## 后果 + +- 正面:三阶段自举可逐字节验证(corec→corec2→corec3 发射一致);后端可独立回归 + (O0/O1/O2 CCR 与原生 ELF 运行验证);.ccr 成为稳定的格式边界与 dump 载体 +- 负面:两二进制分发的复杂度(CLI 入口两份);.ccr 格式稳定性成为硬约束—— + 格式一变,前后端必须同步(版本耦合) + +## 关联 + +- 文档:docs/project-book.md(系统骨架);CLAUDE.md(Build & Test 命令) +- 相关 ADR:ADR-0002(.ccr v6 段表)、ADR-0003(ELF 直出) diff --git a/docs/maintainer/adr/adr-0005-memory-model-layers-v4.md b/docs/maintainer/adr/adr-0005-memory-model-layers-v4.md new file mode 100644 index 00000000..ee5b7ab5 --- /dev/null +++ b/docs/maintainer/adr/adr-0005-memory-model-layers-v4.md @@ -0,0 +1,23 @@ +# ADR-0005: 内存模型分层定稿——图 → 格 → 编码 + 能力定位(v4) + +- 日期:2026-08-26/27(v2 08-20 起演进,v4 定稿) +- 状态:accepted +- 决策者:DslsDZC + +## 背景 +内存模型讨论备忘演进多版(v1 位置抽象 → v3 三层映射总纲 → v4 定稿)。需要回答:语义保鲜的存储本体是什么、能力(能力安全概念)是否是一等公民。 + +## 决策 +- **三层映射**:图 = 关系空间(发生什么)、格 = 状态/存储空间(如何存在)、编码 = 物理编码空间(如何实现;2026-08-27 由「二进制」更名,违反零硬件惯例) +- **超图灵性属于图不属于格**;格不重新引入程序顺序;跨范式发生在图层 +- **规则封闭、对象开放**:层规则 = 缓存语义七条,永不新建;对象(条目/配方/标注)无界 +- **无格承诺**:判定/定理写在比格更弱的理论(最弱理论 = 最多模型);格(Lattice)代数 = 映射实例的组织参数 +- **能力不提升一等公民**(可逆):身份 = 图节点、无配方 = 标注、授权归治理层(consult/assume/measure/pass = 可能关系标注) + +## 后果 +- 正面:新范式只需三件事(图标注加标签/CIC 定义新结构/硬件映射),不新建格;判定对一切格成立 +- 负面:能力作为独立语义概念被否决——治理层授权机制待实现 + +## 关联 +- 文档:docs/academic/lattice-theory.md(定稿提炼)、docs/archive/memory-model-capability-lattice.md(v1-v4 备忘) +- 相关 ADR:ADR-0006(缓存语义) diff --git a/docs/maintainer/adr/adr-0006-cache-semantics.md b/docs/maintainer/adr/adr-0006-cache-semantics.md new file mode 100644 index 00000000..81aa04ef --- /dev/null +++ b/docs/maintainer/adr/adr-0006-cache-semantics.md @@ -0,0 +1,21 @@ +# ADR-0006: 缓存语义 = 存储语义本体(2026-08-15 纠偏) + +- 日期:2026-08-15 +- 状态:accepted +- 决策者:DslsDZC + +## 背景 +memory-model 文档此前把 CompCert v2 字节模型(块 + 字节偏移 + 每字节权限)描述为语义本体。文档层错误——实现(HDFG)从来就是缓存语义:节点 = 配方、边 = 值流、驱逐/再生 = 丢令牌/重跑节点。 + +## 决策 +- 语义本体 = **缓存语义七条**(值 = 配方/驱逐不变量/再生等价/边界公理 + 4b 图内不可重算/赋值 = 版本化/地址 = 映射/映射实例正确性) +- CompCert 字节模型降级为**经典映射实例**(权限面,条款 7) +- 零代码改动——本次只纠偏文档 + +## 后果 +- 正面:范式映射表成立(经典行/寄存器行/未来量子行共享七条);验证器条款单一权威 +- 负面:无(文档纠偏) + +## 关联 +- 文档:docs/academic/cache-semantics.md(七条权威)、docs/superpowers/specs/2026-08-15-cache-semantics-design.md +- 相关 ADR:ADR-0005(v4 分层)、ADR-0007(区域映射) diff --git a/docs/maintainer/adr/adr-0007-graph-anchored-regions.md b/docs/maintainer/adr/adr-0007-graph-anchored-regions.md new file mode 100644 index 00000000..25f900a1 --- /dev/null +++ b/docs/maintainer/adr/adr-0007-graph-anchored-regions.md @@ -0,0 +1,23 @@ +# ADR-0007: 图锚定区域内存模型 + 逃逸修订(统一方向 v2) + +- 日期:2026-08-13(含 08-09 统一方向 v2 演进) +- 状态:accepted(部分实现——arena 层已落地,区域机制 M1-M3 设计态) +- 决策者:DslsDZC + +## 背景 +多 Arena 模型(2026-07-28)把 arena 绑定子图;进一步问题是内存区域与图的关系、跨区域引用策略、栈的角色。早期方案逃逸规则四条一刀切禁止跨区域。 + +## 决策 +- **区域锚定图**:区域 = 子图节点的字节域,非词法作用域影子;生命周期 = 图活性(非 LIFO) +- **Arena 降级为分配器面实现**(区域 = arena slot 语义升级) +- **逃逸四条坍缩为一条 outlives 顺序判定**:引用者使用区间 ⊆ 被引用区域存活区间(RegionCheck cur_seq < exit_seq 即该判定);全局 = 程序级虚拟区域 +- **栈 = 区域仅语义层**(栈上对象有 provenance;硬件栈保留,防双栈) +- 外部世界接口(FFI/IO)与硬件描述表同构(后演化为 HIT,见 ADR-0015) + +## 后果 +- 正面:go/flow 非 LIFO 生命周期可行;跨区域引用合法化(顺序判定);验证判据 = 图活性 +- 负面:split/merge/share、IR 层回收点、size class 策略待实现(M1-M3) + +## 关联 +- 文档:docs/maintainer/design/region-model.md、pointer-model.md;specs/2026-08-13-graph-anchored-regions-design.md +- 相关 ADR:ADR-0006(缓存语义条款 7 = 正确性标准) diff --git a/docs/maintainer/adr/adr-0008-execution-model-regions.md b/docs/maintainer/adr/adr-0008-execution-model-regions.md new file mode 100644 index 00000000..dc2cb394 --- /dev/null +++ b/docs/maintainer/adr/adr-0008-execution-model-regions.md @@ -0,0 +1,22 @@ +# ADR-0008: 执行模型 region 化——RVSDG 风格嵌套 + state edges + +- 日期:2026-08-08(region-cfg 定稿;HDFG v2 文档体系 2026-08-15 同步) +- 状态:accepted(已实现:dataflow.cr sg 段表 + 边 kind) +- 决策者:DslsDZC + +## 背景 +早期 HDFG 文档的图是平面概念(节点/边/子图),控制流与副作用顺序的表达依赖标签跳转与全局位置表;验证与 region 迭代需要结构化边界。 + +## 决策 +- 控制流 = **region 嵌套**(SG_FUNC/LOOP/FOR/FLOW/UNSAFE + 新增 SG_IF),region = 存在结构段(kind/enter/exit/parent/nstart/ncount) +- **state edges(边 kind=1)** 两条来源:副作用链(STORE 族/非纯 CALL 按指令序)+ 循环终止依赖(loop/for exit 连边——循环不终止则图不终止) +- 解释器循环按 region enter/exit 迭代(break/continue = region 语义操作),摆脱全局标签位置表 +- g_df_node_region 显式映射(O(1) 归属查询) + +## 后果 +- 正面:终止性依赖显式化可证;验证 pass/解释器/DOT 共享 region 结构;v6 REG 段承载(坐标升级) +- 负面:早期平面图文档(dataflow-design)整体被取代(留档,§8 环标注仍权威) + +## 关联 +- 文档:docs/maintainer/design/execution-model.md §二;specs/2026-08-08-region-cfg-design.md +- 相关 ADR:ADR-0002(v6 REG 段) diff --git a/docs/maintainer/adr/adr-0009-spec-system-v2.md b/docs/maintainer/adr/adr-0009-spec-system-v2.md new file mode 100644 index 00000000..388f251c --- /dev/null +++ b/docs/maintainer/adr/adr-0009-spec-system-v2.md @@ -0,0 +1,22 @@ +# ADR-0009: 规约系统 v2——CIC 内核 + SMT 证书架构 + +- 日期:2026-08-11(v2 定稿) +- 状态:accepted(设计定稿,未实现) +- 决策者:DslsDZC + +## 背景 +规约系统早期形态未定验证后端与表达力上限。需要:规约表达力、验证健全性、平民化三者的平衡。 + +## 决策 +- 规约语言编译为 **CIC 项**(归纳构造演算):量词/归纳/高阶全在内核,表达力无上限 +- **SMT 证书外包**:目标一阶化 → SMT 求解 → 证书 → 翻译成 CIC 证明项 → 内核重验——健全性唯一来源是 CIC 内核(SMTCoq 模式);SMT 只当搜索器 +- 平民化分层:编译器自动推导 → #check/#ensure → spec fn(Core 写的纯检查函数)→ 用户入口分层(#induct/#lemma/证明项逃逸) +- 语法形态 2026-09 定稿:#check/#ensure 标注,.cr 内联(见 ADR-0001 退役同步) + +## 后果 +- 正面:表达力与健全性解耦(内核最小信任根);证明驱动优化(已证性质门控喂优化器) +- 负面:全链未实现(解析/翻译桥/SMT 通道/内核绑定);CIC 内核选择挂起(Rocq vs Lean 4,等社区) + +## 关联 +- 文档:docs/maintainer/design/spec-design.md、docs/academic/verifier-kernel.md(选型论证) +- 相关 ADR:ADR-0001(规约格式退役) diff --git a/docs/maintainer/adr/adr-0010-numeric-dex-apx.md b/docs/maintainer/adr/adr-0010-numeric-dex-apx.md new file mode 100644 index 00000000..d5b7338e --- /dev/null +++ b/docs/maintainer/adr/adr-0010-numeric-dex-apx.md @@ -0,0 +1,22 @@ +# ADR-0010: 数值类型 dex/apx——精确/授权二分 + +- 日期:2026-08-16 +- 状态:accepted(设计定案;dex 迁移推进中,float 退役) +- 决策者:DslsDZC + +## 背景 +float 的 IEEE 754 语义与"语义保鲜 + 范式普适"冲突(NaN/±0/舍入是机器惯例);需要精确数值的范式无关表达 + 性能快路径。 + +## 决策 +- **dex** = 精确小数(定点缩放整数,S = 10⁶ 契约决策):全序、无 NaN/±0/无穷,数学实数语义 +- **apx** = 近似授权(变量级标签):CPU 兑现 = binary64 快路径;仅授权后近似 +- IR_I2F/IR_F2I 迁移为 int↔dex 转换(浮点形态退役);TI_FLOAT 路径 = apx 前身 +- 宽度类型(f32/f64 后缀)= 编码层标注,移出语言定案(2026-08-30 随类型系统方向) + +## 后果 +- 正面:精确默认、近似显式授权;验证无 NaN 陷阱;契约表(ir-op-semantics §4)有显式缩放决策 +- 负面:溢出/除零/有损转换需要错误码检查点(R001/R003/R004);128 位中间积处理 + +## 关联 +- 文档:specs/2026-08-16-numeric-types-design.md、docs/maintainer/design/ir-op-semantics.md §4 +- 相关 ADR:ADR-0012(int 无上限) diff --git a/docs/maintainer/adr/adr-0011-platform-abstract.md b/docs/maintainer/adr/adr-0011-platform-abstract.md new file mode 100644 index 00000000..37a6bd21 --- /dev/null +++ b/docs/maintainer/adr/adr-0011-platform-abstract.md @@ -0,0 +1,20 @@ +# ADR-0011: 平台桥抽象——I/O 流/随机/哈希/时钟语义接口 + +- 日期:2026-08-16 +- 状态:accepted(设计定案,待实现) +- 决策者:DslsDZC + +## 背景 +I/O 与时间等系统接口若直接暴露系统调用,程序语义被平台绑定,验证无法覆盖;部署配置分离原则要求物理实现在后端。 + +## 决策 +- 程序 I/O 语义 = **流变换**(转导器:(输入流)→(输出流));read/write 系统调用 = 物理实现,后端按部署目标隔离 +- 四个语义抽象:I/O 流 / 随机(概率分布)/ 哈希(散列函数)/ 时钟(单调时间序)——语言层语义接口,熵源/算法/系统调用在后端 +- 验证意义上 I/O 是一等公民(流性质可规约),非验证黑洞 + +## 后果 +- 正面:程序语义平台无关;规约可描述输入流性质 → 输出流性质 +- 负面:平台桥实现未落地(语义接口待接线) + +## 关联 +- 文档:specs/2026-08-16-platform-abstract-design.md;docs/maintainer/design/execution-model.md §五 diff --git a/docs/maintainer/adr/adr-0012-int-unbounded.md b/docs/maintainer/adr/adr-0012-int-unbounded.md new file mode 100644 index 00000000..0add8a4c --- /dev/null +++ b/docs/maintainer/adr/adr-0012-int-unbounded.md @@ -0,0 +1,22 @@ +# ADR-0012: int 无上限语义——数学整数 + 编码层快路径投影 + +- 日期:2026-08-23(语义修订)~ 2026-09-06(语言层定稿 + 错误码归位) +- 状态:accepted +- 决策者:DslsDZC + +## 背景 +int 若为 64 位机器整数,规约验证在无界域证明的结论与实际运行(溢出回绕)可能不一致(int-unbounded 问题);同时性能需要 64 位快路径。 + +## 决策 +- **语义层:int = 无上限数学整数**(公理化接口,无溢出概念——溢出非语义事件) +- **编码层:64 位机器字 = 快路径投影**;超界 = 编码层事务(静态区间证明免检;推导不出才检查提升;表示策略归 hw-map 远期,后端策略 B:64 快路径 + 溢出扩展 2-limb) +- 溢出类错误(lexer 字面量守卫/CCR i32 字段校验)= 编码层限制错误,重新归位 +- int 公理可被 .cr 规约对照验证(CompCert 精神推广到原语层);与 dex 精确两线不混(int = 语义无上限、dex = 表示层工程) + +## 后果 +- 正面:验证结论与运行语义一致(#ensure(x + y > x) 无条件成立);快路径性能保留 +- 负面:多字表示/溢出升级实现推进中(M1/M2,见 plans/2026-09-06-int-multiword-m1);IR 契约表以编码层为基准需持续注记(ir-op-semantics) + +## 关联 +- 文档:docs/maintainer/design/ir-op-semantics.md §1/D1;specs(int-unbounded-semantics、plans/int-multiword-m1) +- 相关 ADR:ADR-0010(dex/apx) diff --git a/docs/maintainer/adr/adr-0013-type-system-direction.md b/docs/maintainer/adr/adr-0013-type-system-direction.md new file mode 100644 index 00000000..087a6226 --- /dev/null +++ b/docs/maintainer/adr/adr-0013-type-system-direction.md @@ -0,0 +1,21 @@ +# ADR-0013: 类型系统方向 14 条定案 + +- 日期:2026-08-30(v2 备忘定案) +- 状态:accepted(方向定稿,实施分步) +- 决策者:DslsDZC + +## 背景 +类型系统需要总体方向:类型推断强度、借用/可变性语义、显式边界语法的取舍长期有分叉讨论(图本体 vs 传统类型层)。 + +## 决策 +- 方向总纲:**图本体 + 接口统一**——类型推导建立在图结构上 +- 14 条定案要点:自动推导全量(用户零标注默认)+ 借用收缩(borrow 语义收窄)+ 声明式边界(显式边界语法按需) +- **宽度类型移出语言定案**:位宽/后缀为编码层概念,不进语言本体(与 dex/apx 归位一致) + +## 后果 +- 正面:类型标注成本趋向零;语言本体与编码层职责清晰 +- 负面:自动推导全量在复杂场景的检查器实现工作量;借用收缩细节待落地 + +## 关联 +- 文档:specs/2026-08-30-type-system-direction-design.md、docs/archive/(类型方向备忘) +- 相关 ADR:ADR-0010(宽度类型移出) diff --git a/docs/maintainer/adr/adr-0014-lazy-compile-time.md b/docs/maintainer/adr/adr-0014-lazy-compile-time.md new file mode 100644 index 00000000..3e50812a --- /dev/null +++ b/docs/maintainer/adr/adr-0014-lazy-compile-time.md @@ -0,0 +1,19 @@ +# ADR-0014: 惰性求值——编译期下沉路线 + +- 日期:2026-08-09 +- 状态:accepted(方向定案;实现设计态) +- 决策者:DslsDZC + +## 背景 +图模型天然可表达惰性(节点不在边上游等待时不执行),但 IR 生成策略是 eager。惰性求值若做成运行时机制将引入新执行语义。 + +## 决策 +- **惰性控制流 = 编译期下沉**:if 分支/循环体内的条件性使用经图分析延迟执行(编译期变换),非运行时 thunk 语义 +- IR 保留显式 thunk 形态(IR_LAZY_THUNK/FORCE)作为迁移载体,当前 eager 近似 + +## 后果 +- 正面:执行模型不新增惰性运行时语义;图分析统一处理 +- 负面:控制流级惰性未实现(自动升级 eager 在实现中体现为使用点 force) + +## 关联 +- 文档:docs/maintainer/proposals/lazy.md;docs/maintainer/design/ir-op-semantics.md §2.7 diff --git a/docs/maintainer/adr/adr-0015-hit-hardware-interface-table.md b/docs/maintainer/adr/adr-0015-hit-hardware-interface-table.md new file mode 100644 index 00000000..9ba236cd --- /dev/null +++ b/docs/maintainer/adr/adr-0015-hit-hardware-interface-table.md @@ -0,0 +1,22 @@ +# ADR-0015: HIT 硬件接口表——跨平台硬件访问(取代 .crasm 汇编层) + +- 日期:2026-09-05 +- 状态:accepted(设计定稿;M1 最小核推进中) +- 决策者:DslsDZC + +## 背景 +.crasm 独立汇编层废弃(ADR-0001)后,内核路线的 MMIO/特权/中断访问需要受控入口:跨平台统一 + 可验证 + 与 .cr 语义衔接。 + +## 决策 +- **HIT = 表驱动硬件接口**:最小核 4 事件契约 {sub, nand, load, store} + 投影表——换表即换平台 +- 跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = .cr unsafe + HIT extern 接口事件 +- 事件可读形态 = v6 NOD 文本 dump;NOD op ↔ HIT event_id 对齐为实施期事项 +- 硬件访问进图获得 provenance(声明式进图路径,与 alloc_at 同构) + +## 后果 +- 正面:硬件层统一契约,表驱动换平台;事件级可验证;取代 .crasm 的私有映射表 +- 负面:标准硬件表(寄存器/MMIO 区域)工程量;表外硬件行为 unsafe 人工保证 + +## 关联 +- 文档:specs/2026-09-05-hardware-interface-table.md;docs/maintainer/design/crasm.md(废弃收尾) +- 相关 ADR:ADR-0001(.crasm 退役)、ADR-0002(v6 NOD) diff --git a/docs/maintainer/adr/adr-0016-arena-model.md b/docs/maintainer/adr/adr-0016-arena-model.md new file mode 100644 index 00000000..faea5c26 --- /dev/null +++ b/docs/maintainer/adr/adr-0016-arena-model.md @@ -0,0 +1,22 @@ +# ADR-0016: 多 Arena 内存模型(取代全局 bump 分配器) + +- 日期:2026-07-28 +- 状态:accepted(已实现) +- 决策者:DslsDZC + +## 背景 +早期运行时用单一全局 bump 分配器(BSS + mmap 扩展)。问题:内存生命周期与子图脱节——函数/循环产生的数据只能等到程序结束统一回收,长期服务内存无界;回收时机无法与执行结构对齐。 + +## 决策 +- **每个子图(function/loop/for/unsafe)自己的 arena**:子图入口 arena_new、出口 arena_reset(游标重置,格式化清空) +- 新 IR opcode:IR_ARENA_NEW = 32 / IR_ARENA_RESET = 33(编译期大小预计算) +- ELF 后端双路径 alloc:arena 感知(g_current_arena)+ 全局 bump 回退;mmap 堆扩展 +- stdlib arena.cr 完整生命周期(init/new/reset)、动态元数据、free list、嵌套 + +## 后果 +- 正面:子图退出即整块回收;分配 O(1) bump;无 GC +- 负面:arena 绑定子图 = 数据不能逃逸子图(跨子图引用受约束,后续由 outlives 修订放宽——ADR-0007);07-28 后遇 bump 死循环等实现 bug(已修) + +## 关联 +- 文档:docs/maintainer/design/region-model.md §四(arena = 区域机制的分配器面);specs/2026-07-28-arena-model-design.md +- 相关 ADR:ADR-0007(图锚定区域,arena 的语义升级) diff --git a/docs/maintainer/adr/adr-0017-concurrency-gmp.md b/docs/maintainer/adr/adr-0017-concurrency-gmp.md new file mode 100644 index 00000000..cb21cffc --- /dev/null +++ b/docs/maintainer/adr/adr-0017-concurrency-gmp.md @@ -0,0 +1,22 @@ +# ADR-0017: 并发模型 = Go 风格 GMP 简化(G 并入 M) + +- 日期:2026-07-30(设计定稿)/ 2026-07-31(go 端到端) +- 状态:accepted(已实现——单 M 端到端;多 M 推进中) +- 决策者:DslsDZC + +## 背景 +早期执行模型文档设想"有 OS 用 OS 线程 / 无 OS 用时间片抢占"。实现 go 并发时发现:OS 线程模型把并发绑定平台线程与内核调度,无 OS 抢占在用户态成本高且与数据流图协作模型不匹配。 + +## 决策 +- 采用 **Go runtime 同构的 GMP 简化模型**:G = goroutine(fiber,16KB 栈,独立 arena)、M = machine、**P 不分离合并到 M**;无 GOMAXPROCS,M 数 = CPU 核 +- G 状态机(_Gidle/_Grunnable/_Grunning/_Gwaiting/_Gdead);local run queue + global 偷取;切换 = 用户态 fiber_switch(协作) +- channel 通信(环形缓冲 + send_wait/recv_wait 链表,值拷贝语义);go 返回 result_ch +- 早期"OS 线程/抢占式"设计废弃(文档层,2026-09 全量重写时清除) + +## 后果 +- 正面:协作调度无内核介入;每 G arena 生命周期对齐(goroutine 栈 + G 结构同生共死);与图模型一致(yield/recv = 图节点操作) +- 负面:多 M 并行、global queue、阻塞链完整化推进中;并发执行器语义(IR_SPAWN eager 近似)与调度模型的差距持续注记(ir-op-semantics §2.7) + +## 关联 +- 文档:docs/maintainer/design/execution-model.md §三(GMP);specs/2026-07-30-concurrency-design.md;stdlib sched.cr/goroutine.cr/chan.cr +- 相关 ADR:ADR-0007(每 G 独立区域) diff --git a/docs/maintainer/adr/adr-0018-repo-governance.md b/docs/maintainer/adr/adr-0018-repo-governance.md new file mode 100644 index 00000000..75b717dc --- /dev/null +++ b/docs/maintainer/adr/adr-0018-repo-governance.md @@ -0,0 +1,20 @@ +# ADR-0018: 仓库治理——GitFlow 分支模型 + 双 ruleset + CI 门槛 + +- 日期:2026-08-09 +- 状态:accepted(已落地) +- 决策者:DslsDZC(维护者) + +## 背景 +自举进入协作期(第二维护者 RhineIris 参与 PR #9/#14),需要分支保护、审批流与 CI;早期直接提交 main 的流程不可持续。 + +## 决策 +- **分支模型 feature → develop → main**:main = 正式版线(仅维护者可合入,update 规则 + 无管理员绕过);develop = 集成分支(PR + 审批 + merge queue + CI 门槛);feature 分支每改动独立 +- 仓库治理四件套:CODE_OF_CONDUCT/CONTRIBUTING/SECURITY/PR 模板 + CI(core-ci.yml) +- 版本控制工具 = jj(禁 git 写操作,仓库约定);合并 = 手动 squash 合入 develop(merge queue 因免费计划降级) + +## 后果 +- 正面:合入门槛(审批 + CI 绿)防回归;main 稳定线;协作并行(PR 通道) +- 负面:多会话共享工作副本互踩风险(后续用独立 jj workspace 根治);squash 合入丢失分支粒度 + +## 关联 +- 文档:CLAUDE.md(版本控制流程);specs/2026-08-09-repo-governance-design.md diff --git a/docs/maintainer/adr/adr-0019-bootstrap-route.md b/docs/maintainer/adr/adr-0019-bootstrap-route.md new file mode 100644 index 00000000..3ba8f5e0 --- /dev/null +++ b/docs/maintainer/adr/adr-0019-bootstrap-route.md @@ -0,0 +1,22 @@ +# ADR-0019: 项目启动与自举路线——Python bootstrap 先行 + 自托管目标 + +- 日期:2026-04-24(init)~ 2026-05-19(自编译测试)/ 2026-07-26(corec2→corec10 贯通) +- 状态:accepted(已实现:三级自举管线) +- 决策者:DslsDZC + +## 背景 +全新语言无编译器可用。早期提交(2026-04-24 init)同时出现:语言核心(lexer/parser/checker)、编辑器支持与后端起步。关键路线问题:用什么写第一个编译器、目标产物怎么验证。 + +## 决策 +- **Python bootstrap 先行**(bootstrap/corec/):纯 Python 单遍管线,无外部依赖——作为 stage 0 构建自托管编译器 +- **自托管为目标**:编译器用 Core 自身编写(src/compiler/),Python bootstrap 编译它出原生二进制 +- 三级自举:stage 0(Python)→ stage 1(corec)→ stage 2(corec2);2026-07-26 corec2→corec10 全线贯通 +- 扩展名 .core → .cr(2026-05-28,与前后端拆分同期) + +## 后果 +- 正面:自举闭环(语言写自己 = 最强验证);Python bootstrap 简单可审计 +- 负面:Python/自托管双实现需保持一致(bootstrap 解释器截断方向等 bug 曾致发散——ir-op-semantics BC2);bootstrap 后期只作构建工具保留 + +## 关联 +- 文档:CLAUDE.md(构建与自举);docs/maintainer/onboarding.md +- 相关 ADR:ADR-0003(ELF 直出)、ADR-0004(前后端拆分) diff --git a/docs/maintainer/adr/adr-0020-ir-dual-form.md b/docs/maintainer/adr/adr-0020-ir-dual-form.md new file mode 100644 index 00000000..04a1c0ca --- /dev/null +++ b/docs/maintainer/adr/adr-0020-ir-dual-form.md @@ -0,0 +1,22 @@ +# ADR-0020: IR 双形态确立——.cir(图形态)+ .ccr(格形态) + +- 日期:2026-05-28(.cir/.ccr 格式与 spec 文档) +- 状态:accepted(演进中:.cir 图形态保持,.ccr 历 v2-v6 段表升级——ADR-0002) +- 决策者:DslsDZC + +## 背景 +自托管编译器需要中间产物格式承载"语义保鲜":单一 IR 要么偏图(验证友好)要么偏线性(后端友好)。早期(2026-05-28)确立双产物:数据流图描述(.cir)与二进制格式(.ccr)。 + +## 决策 +- **.cir** = 数据流图格式描述(图形态——验证/分析消费) +- **.ccr** = 二进制格式(格形态线性投影——后端消费;早期版本含变量/指令段) +- corec 产出两者,corearch 消费 .ccr(前后端以 .ccr 为契约——ADR-0004) +- v6(2026-09)将 .ccr 升级为存在结构主体(段表 + ENT/NOD/REG),.cir 语义为图坐标真源 + +## 后果 +- 正面:验证面(.cir)与执行面(.ccr)分离;格式演进有 spec 文档与 schema(ir-schema/) +- 负面:双格式同步成本(.cir/.ccr 表述需一致);v5→v6 不兼容(中间产物零持久生态,可接受) + +## 关联 +- 文档:docs/ir-schema/(schema);docs/maintainer/design/existence-structure.md(v6);docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md(方向) +- 相关 ADR:ADR-0002、ADR-0004 diff --git a/docs/maintainer/design/corelsp.md b/docs/maintainer/design/corelsp.md new file mode 100644 index 00000000..1c4740fb --- /dev/null +++ b/docs/maintainer/design/corelsp.md @@ -0,0 +1,54 @@ +# corelsp 架构(语言服务器设计) + +> 定位:受众 = maintainers;状态 = active;真源 = [src/lsp/](../../../src/lsp/) + [build_selfhost_native.py](../../../build_selfhost_native.py)(corelsp 段);实现与文档冲突时以源码为准。 +> 用户接入(Neovim/VS Code/Zed 配置步骤)见 [developer/editors.md](../../developer/editors.md)。 +> 早期设计稿(历史,细节以源码为准):2026-08-08 [lsp-design](../../superpowers/specs/2026-08-08-lsp-design.md)、2026-08-28 [lsp-production-design](../../superpowers/specs/2026-08-28-lsp-production-design.md)。 + +## 一、是什么 + +corelsp = 用 Core 自举编译器**前端子集**拼接出的语言服务器:LSP over stdio,rpc 循环收请求、按请求跑一次静默检查、回协议帧。与 corec 共享 lexer/parser/checker/diag/module/globals 等前端模块;**不含** corec 的 main.cr/entry.cr(corec_main 不参与 corelsp 构建)。 + +`src/lsp/` 文件职责: + +| 文件 | 职责 | +|---|---| +| main.cr | 入口 `lsp_main`:`g_silent_stdout = 1`(import 进度等输出在协议通道静默)→ `rpc_loop()` | +| rpc.cr | LSP 帧读取/回写循环(`rpc_loop`) | +| json.cr | JSON 解析/序列化,字符串池复用(`g_json_strs` 每帧解析重置,跨帧引用须显式拷贝——见 lsp.cr `lsp_str_pool_copy`) | +| lsp.cr | 文档状态(`g_open_docs`)+ 静默检查管线 + `publishDiagnostics` + 语义查询快照比对(`g_lsp_snapshot_path` / `lsp_request_matches_snapshot`) | +| analysis.cr | 语义分析相关(具体职责见文件头注释) | +| _import.cr | 目录共享 import | + +## 二、构建接线 + +`build_selfhost_native.py` 的「corelsp」段:前端依赖子集(lexer/parser/checker/diag/module/globals 及其依赖链)+ `src/lsp/` 六文件,按依赖序 concat 后包装为 `lsp_main`,产出 `build/corelsp`。拼接顺序约束(json → rpc → lsp 同翻译单元内可见)见 lsp.cr 头部注释;给 src/lsp/ 加新文件时需同步该 concat 列表。 + +## 三、检查管线与诊断通道 + +管线(lsp.cr 注释实录,改动前先读源码): + +- **禁止 `run_frontend()`**——它含 `println("[1/5]...")` 进度输出,会污染 stdout 协议通道。检查固定走 `lsp_check_file`。 +- `lsp_check_file(path, src)`:`g_source` 置源文本 → `reset_frontend_state()` → tokenize → `res_imports` → parse_all →(parse 阶段有诊断则跳过 check_all,AST 可能不完整)→ check_all → publishDiagnostics。 +- **publishDiagnostics**:主文件(`g_files[0]`,当前检查文档)无条件发布——无诊断也发空数组以清旧;其余有诊断的文件(file_id 去重)逐个发布。`file_id → 路径` 经 g_files 路径表,`uri = "file://" + path`。诊断字段:range 1-based → 0-based 减 1、severity=1、message 做 JSON 转义(含引号/反斜杠/控制符时保证合法 JSON)。 + +## 四、同步模型与能力契约 + +- **同步模式**:`textDocumentSync=1`(全量同步,无增量),单文档全局状态机——每次 `didOpen` 重新检查主文档;检查覆盖 import 链(res_imports/parse_all),链上文件有诊断也会发布。 +- **语义查询 = 快照查询**:hover/definition 等基于「最后一次成功检查」的结果——请求 uri 与 `g_lsp_snapshot_path` 比对(`lsp_request_matches_snapshot`),文档有语法错误(本次检查失败)时返回空。取舍:不为查询阻塞重建,结果可能滞后一次检查。 +- **能力通告**(initialize 响应):诊断(publishDiagnostics)、hover、definition、completion(`triggerCharacters = ["@"]`——关键字 + 符号 + `@` 内建)、documentSymbol、semanticTokens/full(差分编码令牌流;跨行差分重建位置,deltaStartChar 非负)。 + +## 五、stdout 通道纪律 + +协议通道是 stdout——任何非协议字节都会使严格客户端(VS Code)断连(main.cr 注释)。corelsp 相关输出一律静默或改道,不得新增 println 类进度。 + +## 六、客户端接线现状 + +用户配置步骤见 [developer/editors.md](../../developer/editors.md),仓库内接线: + +- **VS Code**:[editor/vscode-core/](../../../editor/vscode-core/)(语法高亮 + 客户端;`corelsp.path`/`corelsp.enabled` 设置) +- **Zed**:扩展仓库 `dslsdzc/core-plugin-zed`(Rust wasm:`language_server_command` 返回命令路径——纯 TOML 无法指定命令,Zed 机制约束);[.zed/settings.json](../../../.zed/settings.json) 内置兜底配置(仅 LSP,无高亮) +- **Neovim**:内置 LSP 直连 stdio;旧 compiler 插件 [editor/nvim/plugin/core.lua](../../../editor/nvim/plugin/core.lua)(基于 corec)功能重叠,启用 LSP 时移除 + +## 七、集成测试 + +[tests/selfhost/test_lsp.py](../../../tests/selfhost/test_lsp.py):八组场景按序执行——lifecycle、jsonrpc 错误码、diagnostics+reset、hover+definition、completion+documentSymbol、semanticTokens(含跨行差分)、stdout 污染守卫。**每组独立 spawn corelsp**(隔离全局状态——单文档全局状态机跨组复用会串状态);结尾打印 `lsp suite: ALL PASS`,任一失败打印 FAIL 并非零退出。 diff --git a/docs/maintainer/design/crasm.md b/docs/maintainer/design/crasm.md new file mode 100644 index 00000000..504cbe5b --- /dev/null +++ b/docs/maintainer/design/crasm.md @@ -0,0 +1,34 @@ +# .crasm 统一汇编抽象层(已废弃) + +> 定位:受众 = maintainers;状态 = **deprecated**(2026-09-05);真源 = 本页 + 下方链接。 +> 本文为废弃收尾页——是什么/为什么废弃/去向;设计细节(统一指令集、特权固定集、 +> 寄存器生命周期规则、硬件描述表、里程碑)不再保留,历史正文见 git 历史。 + +## 是什么 + +`.crasm` 曾是 Core 的汇编层设计:跨平台统一指令集 + 无限虚拟寄存器(`%0/%1`)+ 平台映射表, +为内核路线(MMIO、特权指令、中断)提供受控汇编入口,并刻意"难写"以限制汇编程序规模、 +保障可验证性。设计于 2026-08-08,2026-08-09 定稿,从未实现。 + +## 为什么废弃(2026-09-05) + +- **绑定经典硬件过深**:寄存器/寻址/ISA 助记符/私有平台映射表——把"汇编"概念做成了 + 新格式,但汇编本质是机器码的文本投影,不值得一套独立语法 +- **与 .corespec 同构问题**:独立格式 = 真源扩散(lexer/parser/ebnf/后端各维护一套), + 与同期 .corespec 退役(见 ADR-0001)是同一个决策模式 + +## 去向(能力被谁吸收) + +- 跨平台 = 硬件接口表(HIT)事件 + 投影表 +- MMIO/特权/中断 = `.cr` unsafe + HIT extern 接口事件 +- 事件可读形态 = v6 NOD 文本 dump +- 详细设计:`docs/superpowers/specs/2026-09-05-hardware-interface-table.md` + +## 决策记录 + +- ADR-0001(.corespec/.crasm 退役——规约并入 .cr 语法) + +## 历史正文 + +原 205 行设计(统一指令集表/特权固定集/安全模型/里程碑)已从本文件删除; +需要细节时从 git 历史取回(2026-09-05 前的 docs/crasm.md)。 diff --git a/docs/dataflow-design.md b/docs/maintainer/design/dataflow-design.md similarity index 90% rename from docs/dataflow-design.md rename to docs/maintainer/design/dataflow-design.md index 4dd4aec0..609c2292 100644 --- a/docs/dataflow-design.md +++ b/docs/maintainer/design/dataflow-design.md @@ -1,6 +1,9 @@ # Core HDFG 设计 -> **注(2026-08-15)**:本文档为 HDFG 的早期设计(07-22 前);v2 执行模型已演进为 **region 嵌套 + state edges**(RVSDG 风格的结构化控制流,见 docs/execution-model.md 与 docs/superpowers/specs/2026-08-08-region-cfg-design.md)。本文档的 HDFG 基本结构(节点/边/子图)仍然成立,region 化细节以 v2 文档为准。 +> 定位:受众 = 维护者;状态 = superseded(由 execution-model.md 取代,留档) + +> **注(2026-08-15)**:本文档为 HDFG 的早期设计(07-22 前);v2 执行模型已演进为 region 嵌套 + state edges(RVSDG 风格结构化控制流,见 [execution-model.md](execution-model.md) 与 docs/superpowers/specs/2026-08-08-region-cfg-design.md)。本文档的 HDFG 基本结构(节点/边/子图)仍成立,region 化细节以 v2 为准。 +> **留档范围(2026-09)**:§1-7 与 §结论 = 早期设计稿(§7 指针安全已由 [pointer-model.md](pointer-model.md) 取代);**§8 环语义标注(ITER/FIXPT/超图灵/模糊/共归纳)= 仍权威**——执行标注空间是 cache-semantics 条款 4b「图内不可重算」与 glossary 的出处,修改需先改条款。 --- @@ -179,7 +182,7 @@ ALLOC(arr[16]) ──→ ADDR(&arr[0]) ──→ ADD(p + n) ──→ DEREF(*p) provenance=arr offset=n 验证 offset < 16 ``` -编译器在 DEREF 节点处自动验证当前偏移是否在分配范围内。从 ALLOC 到 DEREF 的路径就是安全证明。不需要 borrow checker、Arena tag、或 RawRef 等额外概念。详见 `docs/pointer-model.md`。 +编译器在 DEREF 节点处自动验证当前偏移是否在分配范围内。从 ALLOC 到 DEREF 的路径就是安全证明。不需要 borrow checker、Arena tag、或 RawRef 等额外概念。详见 `docs/maintainer/design/pointer-model.md`。 ## 8. 环语义标注:迭代与一致解(非因果范式准备) @@ -229,7 +232,7 @@ FUZZY 标注 = { - **与概率的边界**:概率是可加测度(测度论),模糊是格值隶属度(格论,min/max 结构)——两个独立语义 - **无专属硬件**:历史模糊芯片(Fujitsu/Togai,80-90 年代)已消亡;模糊无物理动机(对比量子/模拟),执行 = 通用设备软件实现——同概率先例(语义标注的价值不依赖专属硬件,模糊控制/推理程序在通用 CPU 上运行) -- **存储侧对应(2026-08-27 v4)**:本节不可重算标注(FIXPT / 神谕 / BSS / 模糊融合)= **图内无配方条目**——统一规则见 `docs/memory-model.md` 条款 4b(必须有 home、驱逐必写回、身份 = 图节点;不按范式枚举);映射实例侧(寄存器分配 = 缓存语义映射实例)见 `docs/regalloc-cache-mapping.md` §三 +- **存储侧对应(2026-08-27 v4)**:本节不可重算标注(FIXPT / 神谕 / BSS / 模糊融合)= **图内无配方条目**——统一规则见 `docs/academic/cache-semantics.md` 条款 4b(必须有 home、驱逐必写回、身份 = 图节点;不按范式枚举);映射实例侧(寄存器分配 = 缓存语义映射实例)见 `docs/maintainer/design/regalloc-cache-mapping.md` §三 ### 共归纳/非良基语义(CORE 标注——已存在的范式,非预留) diff --git a/docs/maintainer/design/execution-model.md b/docs/maintainer/design/execution-model.md new file mode 100644 index 00000000..0aab9640 --- /dev/null +++ b/docs/maintainer/design/execution-model.md @@ -0,0 +1,221 @@ +# Core 执行模型 + +> 定位:受众 = 维护者/贡献者/用户;状态 = active。 +> 真源:语义设计以本文件为准;实现以源码为准——图/region:src/compiler/dataflow.cr;并发:src/stdlib/sched.cr、goroutine.cr、chan.cr;执行器:src/compiler/interp.cr;语法:grammar/core.ebnf。 +> 本文 2026-09 全量重写:并发部分废弃旧"OS 线程/抢占式"设计,改为与实现一致的 Go 风格 GMP 简化模型;实现与设计的差距以"设计态/已实现"标注,不删设计意图。 + +--- + +## 一、唯一执行模型(HDFG) + +Core 只有一种执行语义:HDFG(全息数据流图)。代码经语义 IR 翻译后构成一张有向图—— +节点是操作,边是数据依赖。图的拓扑承载执行语义: + +| 图结构 | 承载语义 | +|---|---| +| 有向无环图(DAG) | 依赖偏序——无反馈,确定性 | +| 带环静态图 | 迭代——region 表达迭代与终止 | +| 动态图 | 并发——节点运行时创建、生命周期动态管理 | + +程序员不需要声明"模式":代码写了什么,图就反映什么。执行方式(串行/并行/调度)属于 +部署层,图语义不变。 + +三层映射(术语见 docs/glossary.md):**图**表达计算(关系空间)、**格**承载计算 +(存在空间,条目/配方)、**编码**实现计算(物理空间,ELF)。本文只讲图这一层。 + +--- + +## 二、图的落地形状:region 嵌套与 state edges + +图不是一张摊平的网络——控制流以 **region(存在结构段 SG)嵌套**表达,副作用与终止以 +**state edges** 表达。实现于 dataflow.cr(DFNode/DFEdge + sg 段表)。 + +### 2.1 节点与边 + +- DFNode:操作节点,图结构携带类型与语义信息(不降格为指令) +- DFEdge:值流边;其中 **state edge**(边 kind=1)表达顺序约束——副作用链与循环终止依赖 + +### 2.2 region(SG 段) + +region 是子图的字节域锚定:每个 region 记录 kind/enter/exit/parent/nstart/ncount +(sg_push/sg_pop,见 dataflow.cr;设计定稿 = region-cfg-design)。kind 取值: + +| 值 | kind | 对应语法 | 语义 | +|---|---|---|---| +| 0 | SG_FUNC | 函数 | 函数体域(根 region) | +| 1 | SG_LOOP | loop | 迭代域(break/continue 为 region 语义操作) | +| 2 | SG_FOR | for 循环 | 有界迭代域 | +| 3 | SG_FLOW | flow | 可激活命名子图域 | +| 4 | SG_UNSAFE | unsafe | 图边界入口域(provenance 边界) | +| 5 | SG_IF | if/else | 条件分支域(条件求值前 push,区间覆盖 [条件, 汇合);match 展开为 if 链,不单独建 region) | + +显式映射:`g_df_node_region[]` 并行数组——df_create_node 时写入当前 open region id, +O(1) 归属查询(解释器 region 迭代/验证 pass/DOT 分组免线性扫描)。 + +### 2.3 终止依赖与副作用链 + +state edge = DFEdge 的 kind=1 边(0=data,1=state;边结构设计定稿 = region-cfg-design)。 +两条来源: + +1. **副作用链**:每个函数维护一条 state 链——STORE/STORE_FIELD/STORE_INDEX/非纯 CALL + 按指令序连 state 边(prev → cur),构建于 df_connect_srcs(emit 同步建图),df_end_func 闭合 +2. **循环终止依赖**:SG_LOOP/SG_FOR 的 exit 节点,从循环体最后一条副作用指令连一条 + state 边到 exit——"循环不终止则图不终止",终止性依赖显式化后可证循环终止与顺序性质 + +### 2.4 执行投影:顺序即拓扑 + +- **解释器**(interp.cr):按 IR 顺序执行节点——对直线代码,文本顺序即合法拓扑序 + (源码注释明言);region 表按 SG_LOOP/SG_FOR 的 enter/exit 驱动循环(globals.cr) +- **原生**:ELF 直出 x86-64 机器码(编码层投影,见 adr/adr-0003);arena 生命周期见 §四 + +图是执行语义的载体,不是执行方式的决定者——物理执行由编码层决定,图只约束语义。 + +--- + +## 三、并发:Go 风格 GMP 模型(G 并入 M 的简化) + +> 2026-09 定稿(设计 = concurrency-design,2026-07-30):并发执行采用 **Go runtime 同构的 +> GMP 简化模型**——G = goroutine(fiber),M = machine(OS 线程),**P 不分离,合并到 M**; +> 无全局 GOMAXPROCS,M 数量 = CPU 核数(运行时配置)。 +> 废弃早期设计的"有 OS 用线程 / 无 OS 时间片抢占"。实现:src/stdlib/sched.cr(调度)、 +> goroutine.cr(G 生命周期)、chan.cr(通信);ir_gen.cr 发射 go 调用。 + +### 3.1 并发原语 + +```core +ch := make(chan int, 10); // 缓冲 channel,容量 10 +go f(args); // 异步启动,不等待 +ch2 := go g(args); // 返回 channel 句柄,用于接收返回值 +send(ch, 42); // 发送 +val := recv(ch); // 接收 +close(ch); // 关闭 +``` + +- 语法层与 stdlib chan_* 的对应见 docs/developer/syntax.md 第十一章与 chan.cr +- go 也支持范围批量形态(go var start..end expr,parser.cr desugar) + +### 3.2 G:goroutine(fiber) + +每个 G = 一个用户态协程,固定 16KB 栈,绑定独立 arena: + +```core +struct Goroutine { + id: int; status: int; stack_ptr: int; stack_lo: int; + arena_id: int; chan_wait: int; next: int; // 链表(run/wait queue) +} +``` + +G 状态机:_Gidle(未启动)/ _Grunnable(队列中)/ _Grunning(执行中)/ +_Gwaiting(阻塞在 chan send/recv)/ _Gdead(退出待回收)。 + +`go f(args)` 语义(设计定稿): +1. 分配 G 结构 + 16KB 栈 +2. 复制参数到新 arena +3. 创建新 arena +4. 状态置 _Grunnable,投递到 M 的 local run queue +5. 返回(不等待;若用作句柄则返回 result channel) + +实现(goroutine.cr):g_new 先建 G 的 arena,再 fiber_init 初始化协程栈;entry 恒为 +goroutine_entry_wrapper,saved_fn/result_ch 存 G 结构体;sched_go = g_new + sched_enqueue; +wrapper 经 saved_fn 调用户函数,结果经 result_ch 回传;g_free = reset arena 整块回收。 + +### 3.3 M:machine(OS 线程) + +```core +struct Machine { + id: int; cur_g: int; runq_head: int; runq_tail: int; g0: int; // g0 = M 自身的系统 G +} +``` + +- sched_init 注册主线程(G0 = 主流程的 G);sched_spawn_workers(n) 派生多 M(m_idx 标识) +- 切换 = 用户态 fiber_switch(保存旧 G 寄存器到栈、装入新 G 栈指针)——无内核介入 +- 调度循环(local run queue):取一个 G 运行;local 空 → 从 global run queue 偷; + global 也空 → idle(OS 线程休眠) +- 切换点:send 遇满 chan / recv 遇空 chan → G 阻塞入等待链表,schedule();yield() 主动让出 + +### 3.4 Channel 与通信 + +channel = 环形缓冲 + 等待链表: + +```core +struct Channel { + buf: string; cap: int; len: int; head: int; elemsize: int; + send_wait: int; recv_wait: int; closed: int; +} +``` + +- send:已关闭 → panic;有等待接收的 G → 直拷给接收者并唤醒;buf 未满 → 入缓冲; + buf 满 → 当前 G 阻塞入 send_wait +- recv:有等待发送的 G → 直收并唤醒发送者;buf 非空 → 取缓冲;已关闭且空 → 零值; + 空且未关 → G 阻塞入 recv_wait +- **值拷贝语义**:channel 边界永远拷贝值,不传引用;发送方的值发送后不变 + +实现:chan.cr 的 chan_make/chan_send/chan_recv/chan_close;go 句柄本身即 result channel。 + +### 3.5 实现现状与设计态 + +已实现(2026-07-31 go 端到端):go 单发、chan 同步、result 回传、G0 = 主线程、 +协作切换(单 M 起)。设计态/推进中:多 M worker 并行、global run queue 偷取、 +G 状态机完整化(_Gwaiting 阻塞链)、range go 批量、select 聚合、语言层 await 收尾语法。 +推进记录见 docs/superpowers/plans/2026-07-30-concurrency.md。 + +--- + +## 四、内存生命周期:arena + +分配以 **arena** 为单元——arena 是"图锚定区域"内存模型的当前实现形态 +(区域 = 子图节点,生命周期 = 图活性;概念设计见 2026-08-13-graph-anchored-regions-design, +映射实例层见 docs/maintainer/design/region-model.md): + +- 函数/loop/for/unsafe 等子图自动获得 arena 生命周期(ir_gen.cr 在子图边界发射 + IR_ARENA_NEW=32/IR_ARENA_RESET=33,含编译期大小预计算;ELF 后端双路径 alloc: + arena 感知 + 全局 bump 回退,见 arch/linux/ld/elf.cr;实现计划 = arena-model) +- 每个 go 的 G 在创建时独立建 arena,fiber 栈与 G 结构体同生共死,g_free = reset 整块回收 +- 静态已知大小路径 = 纯 bump 零碎片;动态路径分档;物理约束(配额等)由部署配置声明(§五) + +--- + +## 五、部署配置与时间语义(设计态) + +> 以下为设计意图,部分未落地——落地状态以平台桥设计与 plans/ 为准。 + +### 5.1 代码不规定分配策略 + +Core 代码只描述计算逻辑与数据结构,不规定内存从哪来。语义 IR 保留类型/边界/编译期上限 +信息,但不固定分配策略。物理约束在部署配置中声明(TOML,如 allocation = "static"/"dynamic"), +编译器据此检查兼容性并生成适配代码。 + +### 5.2 时间语义 + +时钟语义 = 单调时间序(数学):代码不区分零时间/物理实时/仿真时间,只要求单调。 +时间源由部署上下文与后端决定(平台桥语义接口 now_monotonic/sleep_ms;系统调用隔离在后端) +——裸机用硬件时钟、普通 OS 用系统时钟、仿真器可控。部署配置决定时间源,程序语义不变。 + +I/O 同理:程序语义 = 流变换(转导器:(输入流)→(输出流));系统调用是物理实现,由后端按 +部署目标隔离,不进入程序语义。 + +### 5.3 资源配额 + +配额(CPU/内存/速率)属运营策略,在部署配置中可选声明,不声明即无限制;代码不变。 + +--- + +## 六、代码与部署分离 + +同一份代码: + +- 教学环境 → 解释器逐步执行,可单步 +- 桌面 → AOT 编译原生执行(ELF 直出) +- 嵌入/裸机 → 静态分配 + arena,零运行时依赖 +- 集群 → 图可远程部署(分布式为设计态,见 docs/maintainer/proposals/distributed.md) + +代码不改。图语义唯一。部署配置不同。 + +--- + +## 七、结论 + +代码生成图结构,图承载执行语义;region 嵌套与 state edges 表达控制流与副作用; +并发 = Go 风格 GMP 模型(go/fiber/chan,协作调度);内存 = arena 生命周期; +执行方式与物理约束归部署层。拓扑简单则执行简单,拓扑复杂则执行复杂—— +不需要人为命名任何"模式"。 diff --git a/docs/maintainer/design/existence-structure.md b/docs/maintainer/design/existence-structure.md new file mode 100644 index 00000000..1c6f86d3 --- /dev/null +++ b/docs/maintainer/design/existence-structure.md @@ -0,0 +1,131 @@ +# 存在结构:v6 格形态 IR 的语义承载(ENT/NOD/REG) + +> 定位:受众 = 维护者(ccr_io/分配器/验证器实现者);状态 = active(设计定稿,实现推进中)。 +> 本文件从**语义视角**讲 v6 存在结构——七条缓存语义条款(权威 = docs/academic/cache-semantics.md)如何在 .ccr 中落成 IR 一等结构;字节级布局(段表/字段宽/对齐)的最终规格在 docs/ir-schema/coreir-schema.md(实现期并入),格式设计草案与实施状态以 docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md 与实施计划为准。 +> 决策:adr/adr-0002(.ccr v6 段表架构)。 + +--- + +## 一、设计原则(v6 与 v5 的分野) + +1. **存在为主体,执行为投影**:v5 文件描述"按什么顺序执行什么"(线性指令数组);v6 描述"什么存在(条目 × 版本)、存在多久(区间)、由什么产生(配方)、嵌套在哪(region)"——执行序是可重建的投影,不再是文件主干。 +2. **图节点坐标(D1)**:存在区间端点、region 边界、条目定值点全部以 NOD id(图节点序)为坐标——与 .cir(图形态)同坐标系,兑现"图 = 唯一真相层"。 +3. **执行序重建(D2)**:corearch 从 NOD + REG 重建线性投影——图节点文件序即合法拓扑序,重建 = 文件序直出 + region 边界标注,无排序成本。 +4. **共存不落盘(D3)**:判定消费时从存在区间推导,sweep O(n log n),不超线性、不冗余存储。 +5. **v6-only**:不兼容 v5、无转换工具(.ccr 为管线中间产物,零持久生态)。 +6. **格层 vs 编码层**:ENT/NOD/REG 的语义(条目数学结构/版本/区间/共存)属格层,范式无关;字节宽/段表布局是编码层投影,可按编码空间调整。 + +--- + +## 二、段总览 + +| 段 | 内容 | 语义层角色 | +|---|---|---| +| STR | 字符串表 | 符号名的字节投影 | +| SYM | 符号表(函数/全局/结构/枚举,v5 func_meta 等归并) | 函数记录带 root_region 与 first_ent/last_ent | +| NOD | 节点表 = 图事件(v5 instrs 图坐标化)+ 显式边表 | **配方** | +| ENT | 条目表(v6 新增,存在结构核心) | **条目**:位置 + 配方 + 存在 | +| REG | region 表(SG 嵌套,坐标升级) | 存在区间锚定 | + +变量(局部)不入 SYM——由 ENT 全量携带(存在即声明)。 + +--- + +## 三、ENT:条目表 + +ENT 记录字段与缓存条款的对应: + +| 字段 | 语义 | 条款 | +|---|---|---| +| var_id | 变量 id(NOD dest/srcs 同命名空间;-1 = 匿名常量条目) | 条款 6(条目坐标) | +| version | 该变量第几版(1-based;每次定值 +1) | 条款 5(赋值 = 版本化) | +| def_nod | 定值节点 id(-1 = 函数参数/全局) | 条款 1/4(无配方) | +| live_start / live_end | 存在区间(NOD id);live_end 开区间(最后使用点 +1) | 条款 2 的量化基础 | +| home | 槽位(分配器回填;-1 = 未分配/寄存器候选) | 条款 4b + 条款 7 的接口 | +| flags | bit0 无配方 / bit1 参数 / bit2 全局 / bit3 驱逐候选(v6.1) | 条款 4b / 条款 2 | + +### 版本化切分(条款 5 的落盘规则) + +Core IR 非 SSA——变量每次定值切分一个新版本条目,相邻版本存在区间按定值点切割(版本 k 区间终点 = 版本 k+1 定值点)。 + +定值点收集规则:扫 NOD,dest ≥ 0 的节点 = 该变量的定值点,两处 carve-out(GC 批 2 勘定): +- IR_STORE 单列:定值目标在 s1(ρ(s1):=ρ(s2)),dest 恒 -1——不走 dest 列 +- 三 op 排除:STORE_INDEX_VAR/STORE_PTR/DYN_DISPATCH 的 dest ≥ 0 **非**定值(值 = 被存值源/基址标注/占位) + +参数/全局条目:def_nod = -1,区间 = [函数首节点, last_ref+1)。 + +### 无配方条目(条款 4/4b) + +ENT flags bit0 = 无配方(图内不可重算)——必须有 home。匿名常量条目(var_id = -1)仅当被判定消费时物化,否则常量内联于 NOD src1——避免条目爆炸。 + +--- + +## 四、NOD:配方(图事件) + +- NOD 的 dest/srcs 语义与 v5 instrs 相同(引用 vars 表变量 id)——corearch 指令发射逻辑最小改动 +- v6 新增**显式边表**(from_nod/to_nod/kind):kind = 数据/state 边(.cir 语义落盘;v5 边不落盘) + - state 边(kind=1)两条来源:副作用链(STORE 族/非纯 CALL 按序连边)+ 循环终止依赖(见 execution-model.md §二) +- 坐标约定:NOD id = 文件序索引——图节点坐标,存在区间端点直接引用 + +--- + +## 五、REG:存在区间锚定 + +REG 记录 = v5 sgs 坐标升级: + +| 字段 | 语义 | +|---|---| +| kind | SG_IF/LOOP/FOR/FLOW/UNSAFE/FUNC | +| parent | 父 region id(-1 = 根) | +| enter_nod / exit_nod | 入口/出口节点(NOD id) | +| first_ent / last_ent | 区内条目范围(-1 = 无) | + +REG 的 first_ent/last_ent 使"子图边界 = 存在域边界"成为直接查询——图锚定区域内存模型(region-model.md)的语义锚点:区域生命周期由 region 的存在结构推导,而非词法作用域。 + +--- + +## 六、共存判定:不落盘,sweep 推导 + +`entries_coexist` 不逐对枚举(消费侧现算): + +1. 按 live_start 排序条目(O(n log n)) +2. 单遍扫描:维护活跃条目集(start < 当前点 ≤ end 的条目);新条目进入时,与活跃集中 end > start 的条目即共存——按 end 排序的活跃集弹出(end ≤ start 移出) +3. 判定四条消费此关系(共存互斥等:同 home 组内做 2 的扫描,O(k log k) per 组) + +共存 = 对称、无传递性(最弱理论——最多模型)。无需输出全量共存表——判定四条都是存在性/∀ 检查,sweep 给出相交证据即止。 + +--- + +## 七、与 v5 差异与消费方影响 + +| 面 | v5 | v6 | +|---|---|---| +| 组织 | 定序段 | Header + 段表(tag/offset/size) | +| 指令 | instrs 线性数组(主干) | NOD 图事件 + 显式边表(与 ENT 并列) | +| 坐标 | 指令序 | NOD id(图坐标) | +| 变量 | vars 表 | ENT(变量 × 版本,含区间/home/flags) | +| region | sgs(enter/exit 指令号) | REG(enter/exit NOD id + 区内条目范围) | +| 共存 | 无 | 不落盘,sweep 推导 | +| 兼容 | — | v6-only,无转换工具 | + +消费方:corec 前端产 NOD+REG + 条目重建;corearch 段表寻址 → 投影重建 → 发射;判定(分配器/一致性自检)消费 ENT;证书等判定产物独立于 .ccr,不落本文件。 + +--- + +## 八、开放点(实现期定夺,以实施计划为准) + +1. NOD 显式边表是否必须(v6.0 可省略,v6.1 按需)——编码层问题 +2. 变长记录/对齐/校验规则——编码层问题 +3. ENT home 回填方式(原地改写 vs 内存态) +4. 预留段 tag 6+(驱逐标注段 v6.1、证书段) + +--- + +## 九、关联 + +- 条款权威:docs/academic/cache-semantics.md(七条) +- 格式字节级:docs/superpowers/specs/2026-09-05-lattice-ir-v6-format.md(设计定稿) +- 方向:docs/superpowers/specs/2026-08-27-lattice-form-ir-design.md +- 决策:docs/maintainer/adr/adr-0002-ccr-v6-segment-table.md +- 实施:docs/superpowers/plans/2026-09-05-lattice-ir-v6.md(状态以此为准) +- 分配器判定:docs/maintainer/design/regalloc-cache-mapping.md(判定四条消费共存) diff --git a/docs/ir-op-semantics.md b/docs/maintainer/design/ir-op-semantics.md similarity index 78% rename from docs/ir-op-semantics.md rename to docs/maintainer/design/ir-op-semantics.md index 9f089eb3..6cb3956b 100644 --- a/docs/ir-op-semantics.md +++ b/docs/maintainer/design/ir-op-semantics.md @@ -1,12 +1,15 @@ # Core IR 操作语义表(对照 CompCert x86/Op.v + Asm.v) +> 定位:受众 = 维护者;状态 = active + > 用途:CompCert 对照审查第四轮(2026-08-16)的**契约文档**——Task 2 后端审查逐 opcode 三方对照 > (本表定义语义 vs `src/arch/linux/ld/instr.cr` ELF 编码 vs `src/compiler/interp.cr` 解释执行)以本表为准。 > 本任务纯只读:**不修改任何 Core 代码**;`~/compcert/` 为只读真源,绝不修改。 > 差异标注约定:**D** = Core 有意不同于 CompCert(附设计理由);**BC** = 可疑/bug 候选(Task 2 核实对象)。 -> **状态(2026-08-17)**:第四轮修复已完成并合入(F1-F20,维护者授权全修)——本表已同步修复后状态 -> (「✅ 已修复」标记),BC 表转为修复记录参考;D 表为设计差异,不受修复影响。 -> 修复记录见 `docs/compcert-reference.md`「第四轮修复记录(2026-08-17)」;本表末尾「修复记录」小节汇总。 +> **状态(2026-08-17 → 2026-09)**:第四轮修复已完成并合入(F1-F20)——本表已同步修复后状态 +> (「[ok] 已修复」标记),BC 表转为修复记录参考;D 表为设计差异,不受修复影响。 +> **2026-09 更新注记**:① int 语义定稿为无上限数学整数(2026-08-23 起)——§1 的 64 位契约是**编码层快路径投影**,超界 = 编码层事务(见 adr/adr-0002 语境与 plans/2026-09-06-int-multiword-m1);② .ccr v6 NOD 坐标化(本表 instr 引用 = v5 线性形态,语义不变);③ 规约语法定稿 #check/#ensure。 +> 修复记录见 `docs/archive/compcert-reference.md`「第四轮修复记录(2026-08-17)」;本表末尾「修复记录」小节汇总。 ## 0. 真源与引用 @@ -28,8 +31,7 @@ Core 侧只读来源:`src/compiler/ast.cr`(opcode 常量,L527-580)、`sr - **状态** `σ = (ρ, M, ctrl)`:`ρ` = 变量环境(IR 变量 → 64 位值槽);`M` = 字节寻址内存(堆/栈/rodata); `ctrl` = 控制流位置(解释器:节点游标 + 标签表;ELF:PC)。 -- **int** = 64 位二补整数(实现层机器语义;验证层默认数学语义,见 spec-design §9.4 与 dex/apx 设计)。 - 溢出/截断 = **模 2^64 回绕**(与 CompCert `Int64` 位向量一致)。 +- **int**(2026-08-23 定稿后)= 无上限数学整数:本表的 64 位二补语义是**编码层快路径投影**(i64 快路径,超界 = 编码层事务:溢出自动升级,见 plans/int-multiword-m1);验证层数学语义与编码层投影的分离见 spec-design §9.4。**本契约表以编码层(ELF 实际发射)为基准**,与 CompCert `Int64` 位向量对照仍成立——编码层投影正确性 = 数学语义的映射实例(见 academic/cache-semantics.md 条款 7)。 - **dex**(迁移目标)= 精确小数,定点表示:`v ∈ ℚ` ↔ 缩放整数 `⌊v·10⁶⌋`(S = 10⁶)。见 §4。 > 注:设计文档(numeric-types-design.md)只定「缩放整数/定点实现、精度内置」,**未定缩放系数**——S = 10⁶ 是本契约的显式决策(十进制直觉、i64 内余量充足),迁移实现须以本表为准。 - **apx** = 近似授权(变量级标签);CPU 兑现 = binary64(IEEE 754)快路径。当前 `TI_FLOAT` 路径即其前身实现。 @@ -95,7 +97,7 @@ Core 侧只读来源:`src/compiler/ast.cr`(opcode 常量,L527-580)、`sr | 46 | IR_LAZY_THUNK | 惰性 | ir_gen | ✓(eager) | ✓(eager) | 2.7 | | 47 | IR_LAZY_FORCE | 惰性 | ir_gen | ✓(eager) | ✓(eager) | 2.7 | | 48 | IR_FNADDR | 地址 | ir_gen | ✓ | ✓(d=0) | 2.5 | -| 49 | IR_I2F | 转换 | ir_gen | ✓(⚠️ 见 BC-I2F) | ✗ | 2.3 | +| 49 | IR_I2F | 转换 | ir_gen | ✓([注意] 见 BC-I2F) | ✗ | 2.3 | | 50 | IR_F2I | 转换 | ir_gen | ✓ | ✗ | 2.3 | | 51 | IR_APPROX | 注解(规划) | —(迁移时加入) | — | — | 2.9 | @@ -154,10 +156,10 @@ Core 侧只读来源:`src/compiler/ast.cr`(opcode 常量,L527-580)、`sr - OP_ADD..OP_DIV:`d := a ⊕ b`(IEEE 754 `addsd/subsd/mulsd/divsd`)。对照 `Oaddf..Odivf`(Op.v:L419-422)→ `Val.addf`(Values.v:L538,556)→ IEEE binary64(`Bplus/Bminus/Bmult/Bdiv`,mode_NE 就近舍入);Asm 层 `Paddd_ff` 等(Asm.v:L865-872)。 - OP_EQ..OP_GE:`comisd` + 无符号 setcc(`sete/setne/setb/seta/setbe/setae`)。对照 Asm.v `compare_floats`(L453-463):ZF = (x=y ∨ 无序),CF = ¬(x≥y)(= x= 0` 守卫(修复前发射恒 dest=-1 → `g_ir_vals[-8]` 堆下溢写,静默 UB)。三方一致为 eager 近似(D:单线程模式的既定近似);flow fn 语法已修(parser 不再把 T_FN 当函数名)。 +- **IR_YIELD**(28):语义 = **向 flow 消费者通道发射 `ρ(s1)`**(ast.cr L557)。**BC8 [ok] 已修复(2026-08-17,第四轮 F5)**:ELF 改 **eager 值传递近似**(`d := ρ(s1)`,不再 `call sched_yield()`——修复前实现与定义语义不符且未导入符号 rel32=0 崩溃);interp 补 `d >= 0` 守卫(修复前发射恒 dest=-1 → `g_ir_vals[-8]` 堆下溢写,静默 UB)。三方一致为 eager 近似(D:单线程模式既定近似——完整 go/G-M 调度见 docs/maintainer/design/execution-model.md §三);flow fn 语法已修(parser 不再把 T_FN 当函数名)。 - **IR_AWAIT**(29):语义 = 阻塞直到 future 就绪,`d` = 结果。ELF/interp 均为值复制(eager 近似,已文档化)——D:单线程模式的既定近似。 - **IR_LAZY_THUNK**(46):`d` = 惰性包装(s1 = 表达式求值)。**IR_LAZY_FORCE**(47):`d` = 强求结果。两者当前均 eager 近似(值传递,ELF/interp 一致)——D:惰性求值尚未落地(lazy 设计文档),IR 保留显式 thunk 形态以便迁移。 @@ -259,13 +261,13 @@ load/store(对照 `Mem.load`/`Mem.store` Memory.v:L428/L531 + `valid_access` L | # | 差异 | 设计理由 | |---|---|---| -| D1 | int = 64 位机器整数,模 2⁶⁴ 回绕 | 与 CompCert `Int64` 位向量一致;验证层默认数学语义(spec 9.4)——双层语义显式分离 | +| D1 | 编码层 int = 64 位机器整数,模 2⁶⁴ 回绕(2026-08-23 后 = 无上限数学整数的快路径投影) | 与 CompCert `Int64` 位向量一致(编码层对照);语义层 = 数学整数(spec 9.4/int-unbounded 定稿)——三层分离:语义(数学)→ 格(编码层事务)→ 64 位投影 | | D2 | 无标志寄存器状态;比较直接产 0/1 值 | 数据流 IR(语义保鲜)形态——标志是机器惯例,不进 IR 语义 | | D3 | 错误 = 硬陷阱(SIGILL/SIGFPE)而非 `None`/`Stuck` | 安全优先:越界/除零中止程序,不留未定义值路径;错误不在 IR 层显式表达 | | D4 | 无无符号比较 opcode(无符号仅用于边界检查技巧) | int 全有符号(CompCert 的 Ccompu/Ccomplu 显式二元来自 C 语义,Core 无此需求) | | D5 | 枚举 tag = 变体名驻留索引(运行时布局 [tag, payload]) | 语义保鲜(tag 可读名)而非编号;C 枚举无运行时形态 | | D6 | 内存模型 = 分配块 + 边界检查,无权限/对齐维度 | 语言无 MMIO/无别名权限需求;对齐由 x86 容忍(未对齐访问合法) | -| D7 | arena 内存模型(ARENA_NEW/RESET) | 作用域内存回收(docs/memory-model.md);CompCert 每函数一帧不可比 | +| D7 | arena 内存模型(ARENA_NEW/RESET) | 作用域内存回收(docs/maintainer/design/region-model.md);CompCert 每函数一帧不可比 | | D8 | IR_YIELD/AWAIT/SPAWN/DYN_*/HOTPATCH/LAZY/FAST 等 | CompCert 无并发/动态类型/热补丁/惰性——无对照部分不强行映射 | | D9 | 移位量 ≥ 64:硬件掩码(mod 64)vs CompCert `Vundef` | Core 跟随硬件(机器语义),CompCert 保守未定义;OP_SHL/SHR 当前无发射方 | | D10 | dex/apx 精确-授权二分 | 范式普适哲学:默认精确(数学语义),近似需显式授权(apx 标签) | @@ -275,23 +277,23 @@ load/store(对照 `Mem.load`/`Mem.store` Memory.v:L428/L531 + `valid_access` L | # | 位置 | 现象 | 依据 | 状态(2026-08-17) | |---|---|---|---|---| -| **BC-I2F** | instr.cr L352-363 | **IR_I2F 的 `cvtsi2sd` 缺 REX.W**:`F2 0F 2A`(32 位操作数)→ `\|a\|≥2³¹` 的 int64→float 截断为低 32 位符号扩展(如 2⁴⁰ → 0.0)。IR_F2I 有 `F2 48 0F 2C` 对照可见遗漏 | 本表 2.3;CompCert `Ofloatoflong`(Op.v:L438)语义对照 | ✅ 已修复(2026-08-17,第四轮 F7:`e2_sd_cvt` 补 REX.W) | -| BC1 | instr.cr L825-836(IR_SPAWN `name_ni := s1`) | SPAWN 操作数约定错位:发射方/interp 用 `s3`=函数名、`s1`=首参;ELF 拿 `s1`(参数变量索引)当函数名查表;ast.cr L556 注释与二者皆不同 | 本表 2.5;ir_gen.cr L871 vs instr.cr L828 | ✅ 已修复(2026-08-17,第四轮 F4:统一 s3=函数名 + SysV 参数装载) | -| BC2 | bootstrap/corec/backend/interpreter.py L108 | 整数除/模截断方向:Python `//` 向下取整 vs 契约向零截断(ELF `idiv` ✓,CompCert `Z.quot` ✓)——负除数程序在 bootstrap 与 ELF 下结果不同 | 本表 2.2;Values.v L727 / Integers.v L193 | ✅ 已修复(2026-08-17,第四轮 F10:abs+符号向零截断,补四组负操作数回归) | +| **BC-I2F** | instr.cr L352-363 | **IR_I2F 的 `cvtsi2sd` 缺 REX.W**:`F2 0F 2A`(32 位操作数)→ `\|a\|≥2³¹` 的 int64→float 截断为低 32 位符号扩展(如 2⁴⁰ → 0.0)。IR_F2I 有 `F2 48 0F 2C` 对照可见遗漏 | 本表 2.3;CompCert `Ofloatoflong`(Op.v:L438)语义对照 | [ok] 已修复(2026-08-17,第四轮 F7:`e2_sd_cvt` 补 REX.W) | +| BC1 | instr.cr L825-836(IR_SPAWN `name_ni := s1`) | SPAWN 操作数约定错位:发射方/interp 用 `s3`=函数名、`s1`=首参;ELF 拿 `s1`(参数变量索引)当函数名查表;ast.cr L556 注释与二者皆不同 | 本表 2.5;ir_gen.cr L871 vs instr.cr L828 | [ok] 已修复(2026-08-17,第四轮 F4:统一 s3=函数名 + SysV 参数装载) | +| BC2 | bootstrap/corec/backend/interpreter.py L108 | 整数除/模截断方向:Python `//` 向下取整 vs 契约向零截断(ELF `idiv` ✓,CompCert `Z.quot` ✓)——负除数程序在 bootstrap 与 ELF 下结果不同 | 本表 2.2;Values.v L727 / Integers.v L193 | [ok] 已修复(2026-08-17,第四轮 F10:abs+符号向零截断,补四组负操作数回归) | | BC3 | ast.cr L282-283 + instr.cr L495-512 | OP_SHL/OP_SHR 无发射方(死路径);若启用,OP_SHR 用 `shr`(逻辑)而契约方向未定(CompCert `Oshrl` 为算术) | 本表 2.2 | 死路径已确认(第四轮 §2)——启用时按契约补语义 | -| BC4 | interp.cr L96-123 | 解释器对 TI_FLOAT 无分支:float 算术按整数位模式运算(结果错误);op 24/30/31/32/33/41-45/49/50 未实现(静默跳过);`IR_STORE_PTR` 槽拷贝错位(BC12 细化) | 本表 2.2/2.3/2.4 | ✅ 已修复(2026-08-17,第四轮 F9:TI_FLOAT 走 f64.cr 软件实现;其余 opcode 随 BC11 补齐) | -| BC5 | instr.cr L460-481 | apx 路径 `==`/`!=` 对 NaN:`sete`/`setne` 直接读 ZF——NaN==NaN 得 1(IEEE/CompCert 为 false) | 本表 2.2 浮点路径;Asm.v compare_floats L453-463 | ✅ 已修复(2026-08-17,第四轮 F8:`setnp+sete`/`setp+setne`,对照 Asmgen.v L260) | -| **BC6** | ext_safety.cr L8-31 + ir_gen.cr L1551/L1557 + instr.cr L1219-1230 | **直接数组索引的运行时越界守卫双重死亡**:①插件注册被 `CORE_SAFE=1` 门控;②ir_gen 恒传 `arr_len_lit=-1` → `IR_BOUNDS_CHECK` 永不发射。`arr[i]`(非指针路径)越界读写无任何检查(与 `&arr[i]` 的 provenance 检查链不对称)。另外 ELF 编码按变量槽加载 s2,与「字面量长度」发射约定冲突(若修复发射方需同步修编码)。**安全检查类,高优先级** | 本表 2.4 | ✅ 已修复(2026-08-17,第四轮 F1:CORE_SAFE 默认开 + 注册表布局修复 + 真实长度 + 写路径钩子 + s2 字面量加载) | -| BC7 | ir_gen.cr L1546 + instr.cr L1202-1212 | IR_SLICE 只算指针;high 界(s3)不进入运行时值——slice 解引用无长度守卫 | 本表 2.4 | ✅ 部分修复(2026-08-17,第四轮 F11:切片字面量界侧表 + 创建期检查 + provenance 传播);运行时 high 界需 slice 类型(IR 形态演进,设计项,见 TODO) | -| BC8 | instr.cr L1265-1274 | IR_YIELD 实现为 `call sched_yield()` 且忽略 s1——与定义语义「向消费者通道发射值」不符;interp 复制、ELF 让出 CPU,三方不一致 | 本表 2.7 | ✅ 已修复(2026-08-17,第四轮 F5:ELF 改 eager 值传递近似,interp 补 d≥0 守卫,三方一致) | -| BC9 | instr.cr L1304-1364 | IR_DYN_DISPATCH 未知 tag → `xor eax,eax; ret`(函数体中间 ret,语义 = 整个函数返回 0);`s2` 未用 | 本表 2.6 | ✅ 已修复(2026-08-17,第四轮 F3:rel8 补丁加 pos 基——修复前已知 tag 也崩溃(SIGSEGV),现已知 tag 正确分发) | -| BC10 | ir_gen.cr L758 + instr.cr L1048-1059 | IR_STORE_PTR 发射时 dest=-1/s3=0 → ELF 边界检查恒跳过;仅 provenance_verify 后置改写才启用——非 build 流程/多目标时无检查 | 本表 2.4 | ✅ 已修复(2026-08-17,第四轮 F6:s3=0 保留 null 陷阱(拆出 `e2_ptr_null_check`),修复前连 null 陷阱都没有) | -| BC11 | interp.cr | 解释器未实现 12 个 opcode(24/30/31/32/33/41/42/43/44/45/49/50)——`run` 模式静默错值 | 本表 2.4-2.7 | ✅ 已修复(2026-08-17,第四轮波 2:12 个 opcode 全部补齐——SLICE/BOUNDS_CHECK/ADDR_INDEX/ARENA no-op/DYN 双槽/DISPATCH 与 CALL_EXTERN 显式报错/I2F/F2I f64 路径) | -| BC12 | interp.cr L211 | IR_STORE_PTR 解释为 `w64(slot[s1], slot[d])`(槽拷贝、源在 d),与 ELF `M[ρ(s1)] := ρ(s2)` 不一致;d=-1 时恒 no-op | 本表 2.4 | ✅ 已修复(2026-08-17,第四轮 F12:改 `ir_interp_deref_write`,与 ELF 操作数一致) | -| BC13 | interp.cr L192 | 枚举 payload:MAKE_ENUM/LOAD_ENUM_TAG 值复制 + STORE_FIELD 把名字索引当地址写 → 带 payload 枚举损坏 | 本表 2.6 | ✅ 已修复(2026-08-17,第四轮 F13:interp 按堆布局实现 MAKE_ENUM/STORE_FIELD) | -| BC14 | instr.cr L905-915(IR_ALLOC_ARRAY)、L885-903(ALLOC_STRUCT) | 分配尺寸经 `mov edi, imm32`(0xBF 零扩展写 32 位寄存器):≥2³² 按 mod 2³² 回绕([2³¹, 2³²) 零扩展编码正确——原「高 32 位丢失」表述证伪) | 本表 2.4 | ✅ 已修复(2026-08-17,第四轮 F14:改 `movabs rdi, imm64`,溢出按 OOM;同族 F17 一并修复) | +| BC4 | interp.cr L96-123 | 解释器对 TI_FLOAT 无分支:float 算术按整数位模式运算(结果错误);op 24/30/31/32/33/41-45/49/50 未实现(静默跳过);`IR_STORE_PTR` 槽拷贝错位(BC12 细化) | 本表 2.2/2.3/2.4 | [ok] 已修复(2026-08-17,第四轮 F9:TI_FLOAT 走 f64.cr 软件实现;其余 opcode 随 BC11 补齐) | +| BC5 | instr.cr L460-481 | apx 路径 `==`/`!=` 对 NaN:`sete`/`setne` 直接读 ZF——NaN==NaN 得 1(IEEE/CompCert 为 false) | 本表 2.2 浮点路径;Asm.v compare_floats L453-463 | [ok] 已修复(2026-08-17,第四轮 F8:`setnp+sete`/`setp+setne`,对照 Asmgen.v L260) | +| **BC6** | ext_safety.cr L8-31 + ir_gen.cr L1551/L1557 + instr.cr L1219-1230 | **直接数组索引的运行时越界守卫双重死亡**:①插件注册被 `CORE_SAFE=1` 门控;②ir_gen 恒传 `arr_len_lit=-1` → `IR_BOUNDS_CHECK` 永不发射。`arr[i]`(非指针路径)越界读写无任何检查(与 `&arr[i]` 的 provenance 检查链不对称)。另外 ELF 编码按变量槽加载 s2,与「字面量长度」发射约定冲突(若修复发射方需同步修编码)。**安全检查类,高优先级** | 本表 2.4 | [ok] 已修复(2026-08-17,第四轮 F1:CORE_SAFE 默认开 + 注册表布局修复 + 真实长度 + 写路径钩子 + s2 字面量加载) | +| BC7 | ir_gen.cr L1546 + instr.cr L1202-1212 | IR_SLICE 只算指针;high 界(s3)不进入运行时值——slice 解引用无长度守卫 | 本表 2.4 | [ok] 部分修复(2026-08-17,第四轮 F11:切片字面量界侧表 + 创建期检查 + provenance 传播);运行时 high 界需 slice 类型(IR 形态演进,设计项,见 TODO) | +| BC8 | instr.cr L1265-1274 | IR_YIELD 实现为 `call sched_yield()` 且忽略 s1——与定义语义「向消费者通道发射值」不符;interp 复制、ELF 让出 CPU,三方不一致 | 本表 2.7 | [ok] 已修复(2026-08-17,第四轮 F5:ELF 改 eager 值传递近似,interp 补 d≥0 守卫,三方一致) | +| BC9 | instr.cr L1304-1364 | IR_DYN_DISPATCH 未知 tag → `xor eax,eax; ret`(函数体中间 ret,语义 = 整个函数返回 0);`s2` 未用 | 本表 2.6 | [ok] 已修复(2026-08-17,第四轮 F3:rel8 补丁加 pos 基——修复前已知 tag 也崩溃(SIGSEGV),现已知 tag 正确分发) | +| BC10 | ir_gen.cr L758 + instr.cr L1048-1059 | IR_STORE_PTR 发射时 dest=-1/s3=0 → ELF 边界检查恒跳过;仅 provenance_verify 后置改写才启用——非 build 流程/多目标时无检查 | 本表 2.4 | [ok] 已修复(2026-08-17,第四轮 F6:s3=0 保留 null 陷阱(拆出 `e2_ptr_null_check`),修复前连 null 陷阱都没有) | +| BC11 | interp.cr | 解释器未实现 12 个 opcode(24/30/31/32/33/41/42/43/44/45/49/50)——`run` 模式静默错值 | 本表 2.4-2.7 | [ok] 已修复(2026-08-17,第四轮波 2:12 个 opcode 全部补齐——SLICE/BOUNDS_CHECK/ADDR_INDEX/ARENA no-op/DYN 双槽/DISPATCH 与 CALL_EXTERN 显式报错/I2F/F2I f64 路径) | +| BC12 | interp.cr L211 | IR_STORE_PTR 解释为 `w64(slot[s1], slot[d])`(槽拷贝、源在 d),与 ELF `M[ρ(s1)] := ρ(s2)` 不一致;d=-1 时恒 no-op | 本表 2.4 | [ok] 已修复(2026-08-17,第四轮 F12:改 `ir_interp_deref_write`,与 ELF 操作数一致) | +| BC13 | interp.cr L192 | 枚举 payload:MAKE_ENUM/LOAD_ENUM_TAG 值复制 + STORE_FIELD 把名字索引当地址写 → 带 payload 枚举损坏 | 本表 2.6 | [ok] 已修复(2026-08-17,第四轮 F13:interp 按堆布局实现 MAKE_ENUM/STORE_FIELD) | +| BC14 | instr.cr L905-915(IR_ALLOC_ARRAY)、L885-903(ALLOC_STRUCT) | 分配尺寸经 `mov edi, imm32`(0xBF 零扩展写 32 位寄存器):≥2³² 按 mod 2³² 回绕([2³¹, 2³²) 零扩展编码正确——原「高 32 位丢失」表述证伪) | 本表 2.4 | [ok] 已修复(2026-08-17,第四轮 F14:改 `movabs rdi, imm64`,溢出按 OOM;同族 F17 一并修复) | | BC15 | instr.cr L556-557 | IR_BINARY `OP_AND`/`OP_OR` 实现为按位 and/or:非 0/1 输入时与逻辑语义发散(当前无发射方,低优先级) | 本表 2.2 | 死路径已确认(第四轮 §2)——ir_gen 分支化短路(D11),启用时按契约补语义 | -| BC16 | checker.cr L2065 + ext_safety.cr L20-25 | 常量索引(`arr[100]`)的编译期越界检查缺失:checker 只查类型不查界;ext_safety 的编译期分支同样死于 BC6 的门控(`arr_len_lit` 恒 -1) | 本表 2.4 | ✅ 已修复(2026-08-17,第四轮 F2:checker 编译期常量界检查,R002/TK05/TK06 硬错误门) | +| BC16 | checker.cr L2065 + ext_safety.cr L20-25 | 常量索引(`arr[100]`)的编译期越界检查缺失:checker 只查类型不查界;ext_safety 的编译期分支同样死于 BC6 的门控(`arr_len_lit` 恒 -1) | 本表 2.4 | [ok] 已修复(2026-08-17,第四轮 F2:checker 编译期常量界检查,R002/TK05/TK06 硬错误门) | | BC17 | — | `EC_R_DIV_ZERO`/`EC_R_OVERFLOW`/`EC_R_LOSSY_CONVERT`/`EC_R_OOB`(ast.cr L483-486)全仓库无引用——运行时错误不产生诊断(依赖硬件陷阱) | 本表 0/2.4 | 死路径已确认(第四轮 §2,预留常量)——dex 迁移时按 R001/R003/R004 启用 | ## 6. 自检 @@ -304,12 +306,12 @@ load/store(对照 `Mem.load`/`Mem.store` Memory.v:L428/L531 + `valid_access` L - [x] dex(S=10⁶ 定点)与 apx(binary64)语义已覆盖(§4) - [x] 只读性:本任务未修改任何 Core 代码与 ~/compcert/ - [x] 质量审查修正(2026-08-16 复核后):Mem.alloc 行号更正(Memory.v:L348,非 L531);BC14 零扩展措辞更正;S=10⁶ 标注为契约显式决策;D11 短路措辞更正;BC-F2I Intel SDM 措辞更正;Ccompf NaN 补 Op.v 层 None 与 Asmgen.v:L260 `Cond_and Cond_np Cond_e` 引用;Onotl/Values.v shll/shrl/ext_mgr 行号与引用归属校正 -- [x] 第四轮修复同步(2026-08-17):F1-F20 全部修复合入后,本表已同步修复后状态(✅ 标记 + BC 表状态列 + 正文过时表述更新);契约修正 3 处见文件末尾「修复记录」 +- [x] 第四轮修复同步(2026-08-17):F1-F20 全部修复合入后,本表已同步修复后状态([ok] 标记 + BC 表状态列 + 正文过时表述更新);契约修正 3 处见文件末尾「修复记录」 ## 7. 修复记录(2026-08-17,第四轮) - **F1-F20 全部修复并合入**(维护者授权全修,2026-08-16~17):安全类 F1/F2/F6/F17、崩溃类 F3/F4/F5/F15/F16、 - 正确性 F7-F14、工具链 F18/F19/F20。分三波合入,详见 `docs/compcert-reference.md`「第四轮修复记录(2026-08-17)」。 + 正确性 F7-F14、工具链 F18/F19/F20。分三波合入,详见 `docs/archive/compcert-reference.md`「第四轮修复记录(2026-08-17)」。 - **契约修正 3 处**(本表已同步): 1. L154(BC-FCMP):`Val.cmpf_bool` 对 NaN 返回 **`Some false`**(IEEE 语义,Values.v L928-931)而非 `None` ——`None` 仅出现在非 float 值输入;机器层分歧在 `compare_floats` 无序时 ZF=1,Asmgen 用 diff --git a/docs/maintainer/design/memory-model.md b/docs/maintainer/design/memory-model.md new file mode 100644 index 00000000..3179d7b6 --- /dev/null +++ b/docs/maintainer/design/memory-model.md @@ -0,0 +1,48 @@ +# 存储语义总览:缓存 → 存在结构 → 经典映射 + +> 定位:受众 = 维护者/贡献者;状态 = active。 +> 本文件是存储语义三层结构的**导航总览**——细节各归其位:条款权威 = cache-semantics.md;IR 载体 = existence-structure.md;经典映射 = region-model.md。 +> 分层:三层映射链(图 → 格 → 编码)中「格」层的内容;术语权威见 docs/glossary.md。 + +--- + +## 一、三份文档的分工 + +| 文档 | 内容 | 谁读 | +|---|---|---| +| [cache-semantics.md](../../academic/cache-semantics.md) | 缓存语义七条(权威条款)+ 细读 + 范式映射表推论 | 验证器实现者、所有内存讨论的起点 | +| [existence-structure.md](existence-structure.md) | v6 存在结构:ENT/NOD/REG 如何承载七条(语义视角) | ccr_io/分配器/验证器实现者 | +| [region-model.md](region-model.md) | 经典映射:图锚定区域/Arena/字节权限/逃逸规则 | 内存管理实现者 | +| 本文件 | 三层关系、阅读路径、关联 | 所有读者(入口) | + +## 二、一张图看懂三层 + +``` +语义本体(cache-semantics.md) 存在结构(existence-structure.md) 经典映射(region-model.md) + 条款 1-7:缓存语义 ENT:条目(变量 × 版本 × 区间) 区域/Arena:bump 分配 + 值 = 配方(条款 1) NOD:配方(图事件) 字节权限:Freeable > ... + 驱逐/再生(条款 2/3) REG:存在区间锚定 逃逸规则:outlives 判定 + 版本化(条款 5) 共存判定:sweep 不落盘 布局/放置:layout()/alloc_at + │ │ │ + └────────── 条款 7:映射实例正确性 = 保持 1-6 ─────────────────────────────┘ +``` + +## 三、术语定位 + +"内存"在仓库里有三层含义,阅读时先判定语境(详见 glossary): + +| 术语 | 含义 | 文档 | +|---|---|---| +| 物理内存 | 硬件(DRAM/寄存器/量子存储) | 无(硬件层) | +| 语义存储 | 缓存(范式无关本体)——条目、配方、驱逐、再生 | cache-semantics.md | +| 存在结构 | 本体在 IR 中的编码——ENT/NOD/REG | existence-structure.md | +| 映射 | 字节寻址、区域、Arena | region-model.md | +| 格 | 三层链第二层 = 中间存在空间(本组文档整体);「格(Lattice)代数」= 映射参数,见 archive/memory-model-capability-lattice.md §4.4 | 本组 | + +## 四、相关文档 + +- 寄存器分配 = 缓存语义的映射实例:docs/maintainer/design/regalloc-cache-mapping.md(判定四条) +- 指针模型(provenance 三 pass):docs/maintainer/design/pointer-model.md +- 控制流 region(与内存"区域"区分):execution-model.md §二、existence-structure.md REG +- 术语表:docs/glossary.md §二(三层映射)/§四(缓存语义术语) +- 归档讨论:docs/archive/memory-model-capability-lattice.md(v1-v4 备忘) diff --git a/docs/maintainer/design/pointer-model.md b/docs/maintainer/design/pointer-model.md new file mode 100644 index 00000000..c315d076 --- /dev/null +++ b/docs/maintainer/design/pointer-model.md @@ -0,0 +1,137 @@ +# Core 指针模型:图上的三点验证 pass + +> 定位:受众 = 维护者(改/验证三点 pass 的人);状态 = active;真源 = 源码(src/compiler/ptr_analysis.cr、region_check.cr、provenance_verify.cr)——本文描述的是已实现行为,文档与源码冲突时以源码为准,并请更新本文。 +> 语义定位:指针安全建立在 HDFG 上,不引入 borrow checker/生命周期标注。存储语义本体(条目标识 + 偏移)见 docs/academic/cache-semantics.md 条款 6;经典映射见 docs/maintainer/design/region-model.md。 +> 本文 2026-09 重写:三 pass 描述与实现对齐(Andersen 约束求解、逃逸三点检查、双路径边界验证)。 + +--- + +## 一、问题与方案 + +C 风格裸指针表达力强但无保障;Rust 用 borrow checker 换安全,代价是学习曲线与表达力。Core 的目标:**指针和 C 一样自由,安全保障不依赖用户标注**——编译器维护完整 HDFG,图中每个值的出生节点记录来源(provenance),每个解引用是 DEREF/STORE_PTR 节点;三点 pass 在图上验证,与类型系统解耦: + +| Pass | 文件 | 功能 | +|---|---|---| +| PointerAnalysis | ptr_analysis.cr | 建 points-to(约束求解)→ alloc content 跟踪 | +| RegionCheck | region_check.cr | 逃逸检查(传参/返回/存储三点),按 region(SG)区间 | +| ProvenanceVerify | provenance_verify.cr | DEREF/STORE_PTR 越界检查(静态证明或运行时检查) | + +## 二、术语 + +| 术语 | 含义 | +|---|---| +| provenance | 指针的来源——出自哪个分配(ALLOC/ALLOC_STRUCT/ALLOC_ARRAY/…),经 provenance 边追踪 | +| points-to(pts) | 指针可能指向哪些分配块(位图表示) | +| alloc content | 存进某分配块的值集合(g_alloc_pts)——Store/Load 规则的中介 | +| direct flow | 值沿 def-use 链直接传递 | +| pointer-induced flow | 经 STORE_PTR/DEREF 间接传递——需要 pts 才能追踪 | +| 子图/SG region | 图锚定的执行域(函数/loop/for/flow/unsafe,见 execution-model.md §二) | +| 图边界 | 编译器无 provenance 的入口(外部地址、FFI 返回、inline asm)→ unsafe | + +## 三、用户可见语法(开发者的形状) + +开发者视角的语法与保证见 docs/developer/syntax.md 指针节;此处只列 pass 输入形状: + +```core +p := &arr[0]; // REF/ADDR_INDEX → self-pointer +p = p + n; // 偏移(不影响 pts;偏移入 g_offsets) +x := *p; // DEREF → 读 alloc content +*p = v; // STORE_PTR → v 的 pts 写入 alloc content +q := cast(p); // 类型转换(pts 不变,视图变化) + +unsafe { + mmio := 0x7fff0000 as *int; // 图边界入口:整数转指针,asp=1 + *mmio = 42; +} +``` + +类型双关(`*(dex*)&i`)不需要 unsafe——存储语义是条目标识 + 偏移(cache-semantics 条款 6),经典映射是"字节序列 + 宽度 + 边界",provenance/offset/alloc_size 与类型无关;cast 保留值流,provenance 边不断。判据 = 边界 + 宽度。 + +## 四、Pass 1:PointerAnalysis(ptr_analysis.cr) + +**算法**:interprocedural Andersen-style 约束求解(参考 SVF, Sui & Xue CC 2016——inclusion constraints + interprocedural function summaries),在数据流图上跑。规则(源码注释): + +| 规则 | 触发 | 动作 | +|---|---|---| +| Addr | ALLOC/REF/ADDR_INDEX | 产生 self-pointer(alloc 自身入 pts) | +| Copy | LOAD/STORE 沿 def-use | 沿 def-use 链传播 pts | +| Store | STORE_PTR | ∀alloc ∈ pts(ptr):val 的 pts 并入 g_alloc_pts[alloc](alloc content) | +| Load | DEREF | dest 的 pts ∪= g_alloc_pts[alloc],∀alloc ∈ pts(ptr) | +| Call | 调用 | 函数摘要传播 + 参数保守处理(interprocedural) | + +实现要点: +- pts 为**位图**(pa_set_bit 等,grow_pa_alloc_nodes 动态扩容)——非逐对象列表 +- **alloc content 跟踪**:g_alloc_pts[alloc_id] 记录"存进该分配块的值集合",DEREF 读它——pointer-induced flow 的实现形态(不是图上物理加边,是位图数据) +- unsafe 感知:pa_in_unsafe 使 unsafe 域内指针不参与正常约束 + +按子图独立分析 + 函数摘要跨子图——不需要全程序统一求解。 + +## 五、Pass 2:RegionCheck(region_check.cr) + +**功能**:逃逸检查——子图 A 分配的内存不能逃逸出 A 的存活域。判定基准 = SG region 区间(enter/nstart/ncount/exit,dataflow.cr 的 sg 表)。 + +三点逃逸检查(rc_pts_has_escaped / rc_return_escape / rc_store_escape): + +1. **传参逃逸**:把 alloc 的指针作参数传给外部 → 检查 alloc 所在 SG 的 exit 是否覆盖使用点(pts 中任一 alloc 逃逸即报) +2. **返回逃逸**:返回 alloc 指针——函数级(SG_FUNC)alloc 允许返回(与调用者共享区间语义);函数内子 region 的 alloc 返回 → 逃逸(除非该 region 存活域覆盖调用者) +3. **存储逃逸**:把 alloc 指针存入外部可达位置(store)→ 同样按 SG 区间判定 + +判定的图活性语义:引用者使用区间 ⊆ 被引用区域存活区间(outlives)——2026-08-13 修订从"一刀切禁止"放宽为顺序判定,RegionCheck 的 SG exit 比较就是这个判定的实现。 + +## 六、Pass 3:ProvenanceVerify(provenance_verify.cr) + +**功能**:DEREF/STORE_PTR 越界检查——偏移 + 访问宽度 vs 分配大小。 + +处理(per DEREF/STORE_PTR,per 函数 provenance_verify_func): +1. 从 s1(指针变量)取 pts 目标集合;经 g_offsets 取累计偏移 +2. 对每个目标 alloc:get_alloc_size(编译期已知大小)且偏移可静态定 → `0 <= off && off + width <= alloc_size`?通过则**编译期证明**;越界 → 编译错误(EC_TU_DEREF 错误码) +3. 未知大小或动态偏移 → **运行时检查**:编码 allocation base/size/width,`sub+cmp+jae+ud2` 序列(panic);多 pts 目标时按目标定位实际 base(runtime_targets),不再用页内偏移近似(2026-08-16 落地) + +宽度取 tk(类型码→宽度,宽 ≤ 0 时默认 8)。 + +## 七、unsafe 边界与 ALLOC_AT + +`unsafe` = 编译器无法追踪 provenance 的唯一退路,发生在**图边界**: + +| 场景 | 原因 | +|---|---| +| 外部硬件地址 | 裸 `0x7fff0000 as *int` 无 ALLOC;整数转指针带 asp=1——解引需在 unsafe 域内(pa/pv 的 unsafe 感知) | +| FFI 返回值 | 外部指针无 Core provenance | +| inline asm | 输出指针无来源 | + +**ALLOC_AT(声明式放置,2026-08-13 定稿)**:`alloc_at(addr, size, align)` 是声明式进图节点——固定地址区域以(地址+大小+对齐)声明,与 ALLOC 同路径获得 provenance,之后全图追踪、照常验证: + +```core +mmio := alloc_at(0x7fff0000, 4096, align(4096)); // MMIO 页 +*mmio = 42; // safe 代码即可,边界内照常验证 +p := mmio + 4096; // 越界 → 编译错误或运行时 check +``` + +声明是唯一信任点(等价受控图边界入口);与 2026-08-10 定论不冲突(0x 字面量仍只作 unsafe 外部入口)。 + +## 八、字节权限层(设计态) + +经典映射在"字节序列 + 宽度 + 边界"之上补充每字节权限(CompCert v2 投影,cache-semantics 条款 7): + +Freeable > Writable > Readable > Nonempty > Empty + +权限与 provenance/offset/size 一样是图数据(验证器消费图即获得每字节控制)。实现状态:设计态(region-model.md §5)。 + +## 九、与其它语言的对比 + +| | C | Rust | Zig | Core | +|--|---|------|-----|------| +| 指针算术 | 随便 | *T 不行 | [*]T 可以 | 裸指针随便 | +| 越界检查 | 无 | bounds check | bounds check | 编译期证明或运行时 check | +| 生命周期验证 | 无 | borrow checker | 编译时 | RegionCheck(逃逸三点) | +| 别名分析 | 无 | 独占/共享引用 | 编译时 | PointerAnalysis(Andersen) | +| 用户需标注 | 无 | lifetimes | 有时 | 无 | +| unsafe | 整个语言 | 关键字 | 关键字 | 关键字(图边界) | + +## 十、参考与谱系 + +- SVF(Sui & Xue, CC 2016):Andersen inclusion + interprocedural summaries——PointerAnalysis 直接参考 +- Fridtjof Siebert(2006):全程序 context/flow-sensitive 指针分析检区域悬垂——RegionCheck 概念来源 +- Prov-GC(Banerjee 2020):provenance 追踪——ProvenanceVerify 概念来源 +- 存储语义:docs/academic/cache-semantics.md(条款 6 地址 = 映射) +- 经典映射:docs/maintainer/design/region-model.md diff --git a/docs/maintainer/design/regalloc-cache-mapping.md b/docs/maintainer/design/regalloc-cache-mapping.md new file mode 100644 index 00000000..2f295c6a --- /dev/null +++ b/docs/maintainer/design/regalloc-cache-mapping.md @@ -0,0 +1,132 @@ +# 寄存器分配:缓存语义映射实例 + +> 定位:受众 = 维护者(分配器/opt.cr/一致性自检);状态 = active。 +> 本文是寄存器分配在 Core 架构中的正式参考文档——缓存语义(权威 = docs/academic/cache-semantics.md)的寄存器映射实例;存在区间载体 = v6 ENT(docs/maintainer/design/existence-structure.md)。 +> 设计决策记录(日期/排除方向/讨论):docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md;分配器逻辑契约文档 = src/compiler/regalloc-consistency.cr(v6 分支)。 +> 本文 2026-09 重写:v6 对齐(ENT 区间/home/sweep 共存)+ 实现状态分线(develop vs lattice-ir-v6)。 + +--- + +## 零、术语 + +| 术语 | 含义 | +|---|---| +| 条目(entry) | IR 变量版本(缓存语义条款 5:每次赋值 = 新版本 = 新条目;v6 载体 = ENT 记录) | +| 配方(recipe) | 条目值的产生方式 = (产生它的图节点, 输入边)(条款 1;v6 载体 = NOD) | +| 驱逐(eviction) | 丢弃存储物、保留配方(条款 2) | +| 再生(regeneration) | 重跑配方节点产生可观测等价的值(条款 3) | +| home | 条目的持久位置(经典映射 = 栈槽;v6 载体 = ENT home 字段) | +| 存在区间 | 条目在图层上的存活区间 = 图活性(v6 载体 = ENT live_start/live_end,NOD id 坐标) | +| 共存 | 两条目存在区间相交(对称、无传递性;v6 sweep 推导,不落盘) | +| 寄存器映射实例 | 寄存器文件 = 缓存层、home = 栈槽(条款 7) | + +## 一、定位:分配是映射实例,不是编译器 pass + +寄存器分配与区域/arena/字节权限同族,是格 → 编码的映射实例(条款 7): + +``` +图(HDFG) 关系空间:值流、配方、图活性 +格(存在) 条目存在:版本、共存、驱逐(cache-semantics 本体;v6 ENT 编码) +编码(物理) 映射实例:寄存器(缓存层)/ 栈槽(home)/ 字节 +``` + +推论: + +- CFG 系算法(线性扫描/图着色/SSA 弦图)是**线性化退化实例**——不进入语义本体 +- 正确性 **order-free**:对全部可能执行陈述,不对某条线性化路径 +- 范式映射表 = 映射正确性定理表——寄存器实例是其中一行(经典行),与字节内存行同构:证明缓存语义 ⊇ 该实现 + +## 二、语义基座:驱逐不变量 = 语义保持定理 + +公理 = 缓存七条(docs/academic/cache-semantics.md)。其中: + +- **驱逐不变量(条款 2)**:⟦G ∖ storage(e)⟧ = ⟦G⟧——驱逐任意条目不改变可观测语义 +- 分配器放寄存器还是栈、放多久 = 放置/替换策略——**算法零证明**,语义保持不依赖具体贪心(生成器 + 验证器分离;VeriLocc 同构先例) +- **无格承诺**:判定/定理只在关系层面陈述——共存无传递性(最弱理论);对一切格成立 + +## 三、对象与 v6 载体 + +| 对象 | 定义 | v6 载体 | +|---|---|---| +| 条目 | IR 变量版本(每次定值切分新版本) | ENT 记录(var_id × version) | +| 配方 | 产生节点 + 输入边 | NOD(def_nod) | +| 存在区间 | 图活性存活区间 | ENT live_start/live_end(NOD id 坐标) | +| 共存 | 存在区间相交 | 不落盘——sweep 推导(existence-structure.md §六) | +| home | 栈槽 | ENT home(分配器回填;-1 = 寄存器候选) | + +**无配方条目**(边界 + 图内不可重算,cache-semantics 条款 4/4b):必须有 home、驱逐必写回——不按范式枚举。v6 载体 = ENT flags bit0(无配方)。 + +## 四、判定(验证):一致性四条 + +「寄存器映射实例合法」= 一致性判定,全部**可判定、局部、条目泛型**(只操作位置 + 配方 + 存在,不读值内容): + +1. **共存互斥**:每时刻每寄存器至多一个条目的材料(共存条目不同寄存器) +2. **读点无陈旧**:读到的材料与条目当前版本对齐(条款 5) +3. **驱逐配对**:寄存器回收(驱逐)时材料不丢——写回 home,或配方可再生(条款 3 = remat);无配方条目必写回 +4. **调用点失效契约**:调用点 = 缓存失效边界——caller-saved 失效(材料归 home)、callee-saved 保留(ABI = 缓存保留契约) + +**算法零证明**:正确性由判定保证,与分配算法无关。分配算法 = 上下文贪心(§五,定案)。 + +**落点**(双层消费同一规约):①规约契约(.cr 内联,spec-design 体系:翻译桥 → CIC 内核,健全性由内核保证;格式退役见 adr/adr-0001);②编译器内 checker 实现期自检——分配器逻辑契约文档 src/compiler/regalloc-consistency.cr(v6 分支)+ 运行自检(opt.cr 侧)。 + +## 五、分配器(定案):上下文贪心(CAG) + +**正式名:上下文贪心**——存在结构上的 First Fit 贪心扫描。上下文 = 共存关系 + 再生成本(配方)+ region 生命周期 + 调用点 + 使用次数。定案(2026-08-27):CAG 为唯一分配算法;判定(§四)不依赖算法选择。 + +``` +对每个函数: +1. 条目表构建:变量 × 版本(定值点切分,IR_STORE carve-out 见 existence-structure.md §三) + → 存在区间 [live_start, live_end](图活性;region 嵌套序结构化计算,非 CFG 数据流不动点) +2. 遍历序:region 嵌套序(存在结构拓扑序)——CFG 退化时 = 指令序 = 经典线性扫描 +3. 活跃集合:当前点存在区间覆盖的条目 = 共存集合(sweep 维护) +4. 分配:对每个新活跃条目 e: + free = 寄存器全集 \ 活跃集合已占 + free 非空 → e 取最小空闲寄存器(First Fit) + 无 free → 驱逐决策:对候选驱逐者 e′ 比较 + cost = region 深度 × 使用次数 ×(写回 + 装载)vs 配方重算代价(remat) + 选便宜者:材料写回 home(spill)或直接重算(remat);e 占释放的寄存器 +5. 调用点:活跃 caller-saved → 驱逐写回(缓存失效契约);callee-saved 保留 +6. 输出:条目 → 位置(寄存器/home)映射 + 驱逐/装载点表 → g_opt_meta / 格形态 +``` + +谱系:主干 = Poletto-Sarkar 线性扫描的**存在结构版本**;spill = 成本感知驱逐(second-chance 变体 + remat);调用点失效 = 解锁 caller-saved。复杂度 ~O(V+E)。数学闭合:CFG 退化情形 = 区间序 First Fit = 最优(Dilworth:最小寄存器数 = 共存偏序 width;区间图 = 完美图);非区间序 = 贪心近似(最优性本质 NP-hard),质量靠上下文拉高,验证不依赖它。 + +## 六、数学支撑 + +| 结果 | 内容 | +|---|---| +| Dilworth / 区间序 | 链分解 = 分配;width = 最小寄存器数;区间序 First Fit = 最优(退化情形精确性) | +| PCM / 资源代数(Verus/Iris) | 寄存器 = 可分离资源——验证的可选代数层(非必需,§四 判定已足够) | +| SPL 语法分解(Cai & Goharshady 2024-2026) | series-parallel 带 spill 最小代价分配多项式精确——证实 CFG 系 = 格分配线性化特例;DP 表可作最优性证书形态参考 | + +## 七、实现状态(两线) + +| 线 | alloc_registers 现状 | 契约/自检 | +|---|---|---| +| develop(基线) | 朴素线性扫描(first/last 区间、5 callee-saved、无 spill;负编码寄存器,g2_slot 机械翻译) | — | +| lattice-ir-v6(推进中) | CAG 重写——rc=0 根因修复 + 寄存器真实分配 + 看门狗实证(v6 挂账清项) | regalloc-consistency.cr(分配器逻辑契约:机制 + opt.cr 映射 + 三层正确性定位)+ verify 运行自检 | + +与实现的对应(设计概念 → 代码): + +| 概念 | 代码 | +|---|---| +| 寄存器映射 | g_opt_meta(var_idx, reg)对——判定的检查对象 | +| home 槽共享 | pass_stack_share(共存互斥条目共享 home,条件:不同时存在) | +| 存在区间 | v6:ENT live_start/live_end;develop:vars first/last + RegionCheck 机制 | +| 物理位置懒查询 | arch/linux/ld/instr.cr g2_slot(判定的挂接处) | + +## 八、范围边界(YAGNI) + +- 不做 SSA 转换(版本化语义已在:条目 = 版本) +- 不做图着色机制、不做 SPL 分解实现(CFG 系,方向已弃) +- 不做格代数承诺(关系层面陈述;格 = 映射参数,将来挂接) +- 做:共存关系落定 → 判定规约 → CAG 实现 → checker 自检 + +## 九、关联 + +- 设计决策:docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md +- 条款权威:docs/academic/cache-semantics.md;v6 载体:docs/maintainer/design/existence-structure.md +- 备忘:docs/archive/memory-model-capability-lattice.md(v4 分层/无格承诺/图内不可重算) +- RegionCheck/存在区间实现:docs/maintainer/design/pointer-model.md +- 规约体系:docs/maintainer/design/spec-design.md;执行标注空间:docs/maintainer/design/dataflow-design.md §8 +- 契约文档:src/compiler/regalloc-consistency.cr(v6 分支) diff --git a/docs/maintainer/design/region-model.md b/docs/maintainer/design/region-model.md new file mode 100644 index 00000000..75fdf371 --- /dev/null +++ b/docs/maintainer/design/region-model.md @@ -0,0 +1,161 @@ +# 图锚定区域:缓存语义的经典映射(区域/Arena/权限) + +> 定位:受众 = 维护者/贡献者(内存管理实现与验证);状态 = active(arena 层已实现;区域机制部分设计态,状态见 §六)。 +> 本文件 = 缓存语义(权威 = docs/academic/cache-semantics.md)在经典字节硬件上的**映射实例**详细设计——正确性标准 = 保持七条条款的可观测语义(条款 7)。 +> 设计谱系:2026-07-28 多 Arena(已实现,分配器面)→ 2026-08-13 图锚定区域(语义面升级,本文档);设计依据 specs 见 §七。 + +--- + +## 一、方案概述 + +Core 采用**图锚定区域**的统一内存管理:区域(Region)锚定在 HDFG 上——**区域是子图节点的字节域**,不是词法作用域的影子。堆按 HDFG 子图划分独立区域,区域内线性指针碰撞分配(bump allocation),回收 = 游标重置(格式化清空)。 + +传统区域内存管理(Tofte-Talpin 区域栈、Cyclone、Verona)锚定词法作用域(LIFO 嵌套);Core 的执行模型是 HDFG,区域与图同构——这是与经典区域模型的分野,也是非 LIFO 生命周期(go/flow)可行的原因。 + +不可放弃的两个核心价值: + +1. **无碎片**——静态已知大小的分配路径保持纯 bump,零碎片 +2. **动态模式下工程可控的极低碎片**——动态大小分配路径走分档子区域,浪费上界由 size class 表决定(档位越细上界越低:pow2 档约 50%,细分档约 12.5%) + +长期运行服务的内存占用上限由并发区域数量决定,天然无 GC 停顿。 + +--- + +## 二、设计动机(为什么区域) + +| 方案 | 问题 | +|---|---| +| 手动 malloc/free | 悬挂指针、双重释放、内存泄漏 | +| 引用计数(RC) | 循环引用、原子操作开销、cache miss 链式更新 | +| 跟踪式 GC | 停顿不可预测、内存开销不确定、不适合硬实时 | +| RAII + 所有权(Rust) | 静态正确,但复杂数据结构需精细设计借用关系,运行期开销 | + +区域方案优势:分配 O(1)(指针碰撞)、回收 O(1)(整体重置)、区域内无碎片、可预测(无 GC 停顿)、cache 友好(同子图数据同区域,空间局部性好)。 + +论文依据:Tofte & Talpin 区域推断(POPL'94,泄漏弱点需显式提前释放)、Gay & Aiken 显式区域(PLDI'98,提前释放工程可用)、Verona/snmalloc(Oopsla'24,size class 分档)、CompCert v2(每字节权限层 = 经典映射的权限面)。细节见 graph-anchored-regions-design。 + +--- + +## 三、区域模型 + +### 3.1 区域与子图策略 + +| 子图类型 | 区域策略 | 说明 | +|---|---|---| +| DAG(函数/分支/for) | 栈式区域 | 函数入口创建、出口回收,与调用深度同步 | +| 静态循环(loop) | 固定区域 | 循环前分配、结束后回收,容量按循环不变量预计算 | +| Flow | 独立区域 | 每次激活独立区域;并发 flow 各自独立 | +| Go | 独立区域 | 每 goroutine 独立区域(G 的 arena),退出整体回收 | +| Yield/Recv | 消息区域 | 跨 flow 数据在接收端区域重新分配 | + +区域可嵌套:子图在父区域内建子区域,子区域回收不影响父区域。 + +### 3.2 三个推论(与词法锚定模型的分野) + +- **推论 A:生命周期 = 图活性,不是 LIFO**。RegionCheck 的 cur_seq < exit_seq 判定就是图版本的生命周期定义。天然支持 flow/go 的独立区域(区域栈做不到非 LIFO),也天然给出跨区域引用合法性的判据(outlives 的图形式)。 +- **推论 B:区域操作沿图边**。区域不仅嵌套(树),还能沿 HDFG 边操作: + - split:一块字节沿边划给子消费者(字节块独立回收;子区域有自己的游标与重置点) + - merge:汇合点两个子区域的数据汇入(拷贝或区域合并) + - share:分叉点一个区域被多个消费者只读共享(共享者必须 outlive 引用) +- **推论 C:每字节归属 = 图的 provenance 边**。"字节序列 + 宽度 + 边界"与类型无关——provenance(归属)、offset(偏移)、alloc_size 是图上本有的信息(缓存条款 6 的经典视图)。 + +> 术语注意:dataflow 的「region」(控制流嵌套,见 execution-model.md §二与 existence-structure.md REG 段)与本文的「区域」(内存字节域)不同。v6 REG 段 first_ent/last_ent 提供"子图边界 = 存在域边界"的直接查询——区域机制在 IR 层的锚点。 + +### 3.3 逃逸规则(2026-08-13 修订) + +区域方案的核心前提:子图内分配的引用不会逃逸到子图外——编译器静态分析保证: + +1. **向下逃逸禁止**:区域 A 内分配的对象不能作为参数传给生命周期比 A 长的子图 +2. **向上逃逸禁止**:区域 A 内分配的对象不能作为返回值给生命周期比 A 长的调用者 +3. **全局逃逸禁止**:区域 A 内分配的对象不能赋值给全局变量 +4. **跨区域引用:outlives 顺序判定**(修订)——区域 A 的指针可以指向区域 B 的对象,当且仅当 B 的存活区间 ⊇ 引用的使用区间(RegionCheck 判定)。不再一刀切禁止 + +例外(图边界):编译器无法追踪 provenance 的入口(外部地址、FFI 返回值、inline asm)走 `unsafe`——语义是"标注图边界入口"(进入图内编译器重新获得追踪权),不是"关掉检查"。 + +--- + +## 四、分配器面:Arena(已实现) + +2026-07-28 的多 Arena 设计原样继承,作为分配器面实现。概念关系: + +| 语义面(区域) | 分配器面(Arena) | +|---|---| +| 区域(Region) | Arena slot | +| 子区域 split | 子图嵌套 arena / 分档子分配器 | +| 图活性生命周期 | arena_new / arena_reset 插桩(IR_ARENA_NEW=32 / IR_ARENA_RESET=33) | +| 策略推导 | 大小预计算(IR 层) | + +实现要点(已落地): +- stdlib arena.cr 完整生命周期(init/new/reset)、动态元数据、free list、嵌套 +- IR 子图绑定:函数/loop/for/unsafe 自动 arena lifecycle + 编译期大小预计算(ir_gen.cr) +- ELF 后端双路径 alloc:arena 感知(g_current_arena 检查)+ 全局 bump 回退;mmap 堆扩展(BSS 打满自动 mmap) +- emit_alloc_body 零初始化 + 链式扩容 + +运行时:每区域单线程访问,bump pointer 无需原子操作(无锁分配);go 创建独立区域(G 的 arena),返回值拷贝到父区域;区域池制——活跃区域数 = 当前并发子图数,上限 = MAX_CONCURRENCY,耗尽时新 go/flow 阻塞等待;无 GC。 + +--- + +## 五、字节权限层与用户面 + +### 5.1 字节权限层(经典投影的权限面) + +经典映射 = 图 + 字节权限层(CompCert v2):权限层不是语义本体,是缓存语义在字节寻址机器上的权限投影(条款 7)。每字节权限序:**Freeable > Writable > Readable > Nonempty > Empty**。 + +- 区域树 = 图的子图结构;布局元数据、ALLOC_AT 边界、outlives 结论全部从图导出——验证器消费图即消费全部内存语义 + +### 5.2 布局与放置(用户面,零管理负担) + +| 事项 | 谁做 | +|---|---| +| 区域划分/生命周期 | 图活性推导(子图边界即区域) | +| 回收时机(指定回收) | 图分析生成回收点(IR 层)——推断在编译器内部,用户看不到 | +| 碎片策略 / size class | 图分析推导分配策略 | +| 跨区域合法性 | RegionCheck 自动判定 | +| 所有验证 | 三点 pass 自动 | + +用户只表达两件只有用户自己知道的事实: + +```core +// 1. 布局声明(例外入口)——默认布局全自动(字段顺序 + 自然对齐),用户不写任何东西; +// layout(...) 仅当默认布局不合用时:packed(FFI)、强制对齐、硬件结构 +struct PackedHeader layout(packed, align(4)) { + a: u8, // 偏移 0 + b: u32, // 偏移 4 + c: u16, // 偏移 8 +} + +// 2. 放置声明——地址是物理事实,只有用户知道;声明式进图,之后全图追踪 +mmio := alloc_at(0x7fff0000, 4096, align(4096)); +``` + +- 不提供显式区域/回收语法(YAGNI) +- 0x 字面量仍是 unsafe 外部入口;alloc_at 是声明式进图(获得 provenance 的节点)——声明是唯一信任点,之后全图追踪 + +--- + +## 六、设计状态与差距 + +**已实现**: +- 多 Arena 模型(2026-07-28):stdlib arena + IR 子图绑定 + ELF 双路径 + mmap 扩展(§四) +- 三点 pass(provenance/region/bounds,与 pointer-model 共享) + +**设计态(M1-M3 推进,以计划为准)**: +- split/merge/share 沿图边操作、IR 层回收点生成(自动 early deallocation) +- size class 分档策略推导(动态路径碎片上界) +- 字节权限层消费(验证器侧) + +**待解决问题**: +- 区域级空间浪费(小分配占大区域;动态区域大小调整——设计已定分档,待实现) +- 逃逸分析精度:函数指针/接口间接逃逸、条件性逃逸、RawRef 与 unsafe 交互——需验证 +- 区域复用清零(敏感数据/跨安全边界,部署配置控制,非默认) + +--- + +## 七、关联 + +- 语义本体:docs/academic/cache-semantics.md(七条,条款 7 = 本方案正确性标准) +- IR 锚点:docs/maintainer/design/existence-structure.md(REG first_ent/last_ent) +- 指针模型:docs/maintainer/design/pointer-model.md(provenance 三 pass、RegionCheck) +- 并发衔接:execution-model.md §四(每 G arena 生命周期) +- 设计依据:docs/superpowers/specs/2026-08-13-graph-anchored-regions-design.md、2026-07-28-arena-model-design.md +- 术语索引:docs/glossary.md diff --git a/docs/spec-design.md b/docs/maintainer/design/spec-design.md similarity index 90% rename from docs/spec-design.md rename to docs/maintainer/design/spec-design.md index 1549f693..77942c5b 100644 --- a/docs/spec-design.md +++ b/docs/maintainer/design/spec-design.md @@ -1,5 +1,8 @@ # Core 规约系统设计(v2 — CIC 内核 + SMT 证书架构) +> 定位:受众 = 维护者(主,实现规约系统)+ 学术(验证架构);状态 = active(设计定稿,未实现——实现状态以 TODO.md 与 plans/ 为准)。理论侧重(验证器架构/信任根/自举)见 docs/academic/verifier-kernel.md,本文件不重复其论证。 +> 语法形态 2026-09 定稿:#check/#ensure 标注(与 #pure 标签同族),.cr 内联书写——本文 .csp 段落均为自动生成物方向的设计稿。 + > 规约 = 图上的约束。表达力 = CIC(归纳构造演算)。自动化 = SMT 证书外包。 ## 一、哲学 @@ -21,7 +24,7 @@ corec build file.cr -s → .cir + .ccr + 自动生成 file.csp → .csr 规约语言(一套 Core 语法)编译为 **CIC 项**(归纳构造演算),验证走双通道: ``` -Core 规约语言(.corespec / .csp / .cr 内联) +Core 规约语言(.cr 内联——2026-09 起独立 .corespec/.csp 源格式退役,见 ADR-0001) ↓ 编译(翻译桥:命令式 → 函数式) CIC 项(归纳构造演算——一切表达力:量词/归纳/依赖类型/递归性质) ├─ 目标一阶可表达 → SMT 通道(自动求解 + 用户可选 #smt) @@ -63,6 +66,8 @@ CIC 提供 Coq 级别的全部表达力,逐项对应: ## 四、文件格式 +> **退役同步注记(2026-09-06,ADR-0001)**:独立规约源码格式 `.corespec` 已退役——规约源 = `.cr` 内联(#check/#ensure 标注/标签,2026-09 语法定稿),下方 `.csp` 段落为编译器生成物方向的设计稿,落地时以"规约 = .cr 语法唯一表达"为准则重新定案;`.csr`(约束二进制序列化)与图标注机制不受影响。 + ### `.cr` — 实现源码(也可内联规约) ```core @@ -83,9 +88,11 @@ fn sort(a: [int]) -> [int] } ``` -### `.csp` — 编译器自动生成的规约文件 +### `.csp` — 规约源(设计稿,见上方退役同步注记) -`corec build file.cr -s` 自动生成 `file.csp`: +> 注:本节原为"编译器自动生成的独立规约文件"。ADR-0001 退役后方向改为 **.cr 内联书写**——本节保留为设计历史与"自动推导骨架"机制参考,文件级形态以退役决策为准。 + +`corec build file.cr -s` 自动生成 `file.csp`(设计态): ```core // file: sort.csp @@ -134,7 +141,7 @@ spec fn vec_invariant[T](v: Vec[T]) -> bool { ## 五、编译器自动推导(零门槛的核心) -编译器从 `.cir` 图结构中自动推导性质,写入 `.csr`,不需要用户写任何东西。 +编译器从 `.cir` 图结构中自动推导性质(标签),输出进约束 IR(.csr),不需要用户写任何东西。 ### 自动推导的标签 @@ -151,7 +158,7 @@ spec fn vec_invariant[T](v: Vec[T]) -> bool { ### 自动生成的检查函数(骨架) -编译器识别常见图模式,自动生成检查函数代码。用户可以在 `.csp` 中 `#use` 或忽略。 +编译器识别常见图模式,自动生成检查函数代码。用户可以在规约区(.cr 内联或编译器生成的规约骨架)中 `#use` 或忽略(生成物形态见 §四 注记)。 ``` fn sort(a: [int]) -> [int] @@ -187,7 +194,7 @@ fn transfer(from: &mut Account, to: &mut Account, amt: int) 使用 `#` 前缀,与 `@`(外部项目引用)区分。 ```core -// 在 .cr 或 .csp 中使用 +// 在 .cr 中使用(设计定稿:规约内联于 .cr) fn foo() -> int #pure #check(x > 0) @@ -250,7 +257,7 @@ spec fn all_nonneg(arr: [int]) -> bool `forall x: int => P(x)` 必须是规约语言的一等构造(**不是** for 循环的翻译)——int 域无限,遍历不了;for 循环只是有限域的便利糖(§7 纯公式支持的 `forall x in arr` 是有限域情形)。 ```core -// EBNF 已定义(grammar/corespec.ebnf) +// EBNF 已定义(2026-09 起归口 grammar/core.ebnf;corespec.ebnf 为迁移期残留) forall (x: int) => x >= 0 exists (i: int) => a[i] == target ``` @@ -336,17 +343,17 @@ Core 的 `int` 是无上限数学整数(2026-08-23 修订:i64 快路径 + | 机制 | 箭头类型 `int -> int` → CIC 原生 | TYP_FN + 闭包/捕获/调用约定 | | 需求 | 高阶量词 `forall f: int -> int => P(f)` | 函数值编程(map/filter 传函数、回调表) | -**决策:规约专属函数类型。** `int -> int` 只存在于规约语言(`.corespec` 类型宇宙的一部分),直接映射 CIC 箭头;量化的是数学函数,不需要实现层有函数值。零污染 Core 语言(checker/ir_gen/后端/内存模型不动),符合"规约是独立源文件"的哲学。 +**决策:规约专属函数类型。** `int -> int` 只存在于规约语言(规约类型宇宙的一部分——独立源格式已退役,类型机制不受影响),直接映射 CIC 箭头;量化的是数学函数,不需要实现层有函数值。零污染 Core 语言(checker/ir_gen/后端/内存模型不动)。 **实现层函数值(TYP_FN/闭包)按 YAGNI 挂起**:现状 `@addr(f)` + int 能表达函数地址(内核函数表、中断向量表);闭包与 arena 内存模型(捕获变量归属)交互复杂,无真实用例不做。 ## 十一、验证器:CIC 内核 + SMT 证书(v2 新增) -> 内核选型与融合架构的完整论证见 `docs/verifier-kernel.md`(理论谱系、2025–2026 论文扫描、融合决策、自举路线)。 +> 内核选型与融合架构的完整论证见 `docs/academic/verifier-kernel.md`(理论谱系、2025–2026 论文扫描、融合决策、自举路线)。 ### 内核 -CIC 类型检查器(Coq 内核级别:约数千行的信任根)。初期绑定成熟实现,后期自举为 Core 版(见 §14 与 `docs/verifier-kernel.md`)。 +CIC 类型检查器(Coq 内核级别:约数千行的信任根)。初期绑定成熟实现,后期自举为 Core 版(见 §14 与 `docs/academic/verifier-kernel.md`)。 ### SMT 通道(证书架构,SMTCoq 模式) @@ -446,18 +453,15 @@ corec build file.cr → tokenize/parse/check/ir_gen/lower → file.cir + file.ccr -# 编译 + 规约 +# 编译 + 规约(设计定稿形态;.csp 独立文件概念已退役,见 §四 注记) corec build file.cr -s → tokenize/parse/check/ir_gen/lower → file.cir + file.ccr - → 自动生成/更新 file.csp - ├── 所有函数的声明骨架 - ├── 编译器自动推导的标签(从 .cir 图结构分析) - └── 用户上次手写的规约(保留) - → 解析 file.csp → spec ir_gen → 输出 file.csr - ├── DFNode[](指令节点 + 规约约束节点) - ├── TagNode[](约束元数据:check/ensure/invariant/标签) - └── 符号引用表(指向 .cir 中的函数/变量) + → 解析 .cr 内联规约(#check/#ensure/spec fn)+ 编译器自动推导标签 + → 输出 file.csr(规约约束 IR) + ├── DFNode[](指令节点 + 规约约束节点) + ├── TagNode[](约束元数据:check/ensure/invariant/标签) + └── 符号引用表(指向 .cir 中的函数/变量) # 验证(外部工具) verify file.csr @@ -516,7 +520,7 @@ fn map_page(pt: &mut PageTable, virt: Addr, phys: Addr, flags: u64) ## 十七、实现里程碑(建议) -1. `.corespec` 解析(量词/函数类型/变体)+ 规约类型检查 +1. `.cr` 内联规约解析(量词/函数类型/变体)+ 规约类型检查 2. 翻译桥:spec fn → CIC 项(循环→递归、数组→归纳列表) 3. SMT 通道:目标翻译 + 证书校验 + 内核验证(绑定内核起步) 4. 用户入口:`#induct` → `#lemma` → 逃逸 diff --git a/docs/maintainer/onboarding.md b/docs/maintainer/onboarding.md new file mode 100644 index 00000000..7d926c0a --- /dev/null +++ b/docs/maintainer/onboarding.md @@ -0,0 +1,119 @@ +# 新维护者入门手册(onboarding) + +> 定位:受众 = maintainers;状态 = active;真源 = 源码 + [CLAUDE.md](../../CLAUDE.md)(实现与文档冲突时以源码为准,既有惯例)。 +> 文档导航见 [docs/README.md](../README.md)(分类规则:主题 × 读者 × 状态)。本文档聚焦仓库结构、构建、工作流与雷区。 + +## 一、仓库地图 + +**仓库协作约定**(人类维护者视角;CLAUDE.md 的"铁律"是面向 AI 助手的会话规则,不直接约束人——但下述约定人机一致): + +- **版本控制只用 jj,不用 git 写操作**——仓库工具链统一(jj 分支模型见 §四) +- **不随意还原/回滚文件**——改坏了向前修,不回头抹历史(有争议先讨论) +- **修问题找 root cause**,不绕过、不掩盖、不加临时开关了事 +- **长构建/测试注意机器负载**:无人值守或共享机器上建议 `cpulimit -l 10` / `nice -n 19`(减风扇噪音与卡顿;交互式短任务不必) + +``` +src/compiler/ → 自举编译器主体(corec 前端 + corearch 后端共用) +├── ast.cr → AST 结点、IR opcode、类型常量(扁平 g_ast/g_ir_instrs,无指针树) +├── lexer.cr → tokenizer(int 字符访问,热路径零字符串分配) +├── parser.cr → 递归下降解析 → 扁平 AST +├── checker.cr → 类型检查 + 借用检查 + 声明收集(两遍:先注册声明再查体) +├── ir_gen.cr → AST → IR 指令生成 +├── dataflow.cr → HDFG 构建:DFNode/DFEdge(含 state edges)+ 嵌套 region(SG_IF/LOOP/FOR/FLOW/UNSAFE) +├── ccr_io.cr → .ccr 二进制读写(v6 = 段表 + ENT 存在结构段) +├── opt.cr → 优化 pass:CSE、寄存器分配、栈共享 +├── pass.cr → AST 级优化(常量折叠) +├── diag.cr → 编译诊断 +├── module.cr → import 解析、文件 ID 管理 +├── project.cr → Core.toml 工程配置加载 +├── interp.cr → IR 解释器(`run` 命令) +├── dump.cr → 调试 dump 工具 +├── dyn_arr.cr → 动态字节缓冲扩容 + 字符串驻留 +├── globals.cr → 全部全局变量声明 +├── entry.cr → 入口包装 +├── main.cr → corec CLI + 管线编排 +├── corearch.cr → corearch(后端二进制)入口 +└── _import.cr → 目录共享 import +注:自举推进期另有后增模块(monomorph/region_check/ptr_analysis/linker 等),未列于此表,职责以源码为准。 + +src/arch/linux/ld/ → x86-64 ELF 直出后端(corearch 用) +├── elf.cr → ELF header/program header 生成、_start 发射 +├── instr.cr → 指令编码:REX/ModRM/SIB、全部 IR opcode 发射器 +├── sizes.cr → 指令字节数预估(sz_*,label 前向引用依赖) +├── resolve.cr → label 解析 pass(res_labels) +└── ld.cr → 动态链接(PLT/GOT、.so 加载) + +src/stdlib/ → 标准库:cli/fmt/io/os/toml/panic/math/collections/_import + 后增 + (arena/assert/dex/goroutine/chan/sched 等,见目录实况) +src/runtime/ → 运行时:rt.s(_start、bump allocator、__builtin_*)+ rt.cr(g_rt_argc/g_rt_argv_ptr) +bootstrap/corec/ → Python bootstrap:纯 Python 单遍管线(syntax/frontend/ir/backend/utils/verifier), + 无外部依赖,只用于产出第一代原生编译器 +tests/ → 三套: + bootstrap/*.py → bootstrap 管线测试(Python 驱动内联 Core 源码字符串): + test_pipeline / test_borrow / test_generics 等 + selfhost/*.py → 自举/后端/原生回归:test_compile / test_backend_bootstrap / test_borrow / + test_impl / test_pointer_safety 等 + suite/*.cr → 集成用例源码,经 ./build/corec 运行 +grammar/ → 语言语法 EBNF 唯一真源:core.ebnf(全语言)/ corespec.ebnf(规约)/ tokens.ebnf +docs/ → 文档分类树(language 用户向 / design 定稿 / proposals 提案 / archive 归档 / + maintainer 手册 / adr 决策记录),导航索引 = [docs/README.md](../README.md) +``` + +## 二、构建管线 + +三条命令: + +```bash +python3 build_selfhost_native.py # Stage 0:Python bootstrap → build/corec + build/corearch +./build/corec build FILE.cr -o OUT --static # 前端:.cr → .ccr(自动调后端出 ELF);另有 check/run/cir/ccr 子命令 +./build/corearch FILE.ccr --elf --static -o OUT # 后端独立驱动:.ccr → ELF +``` + +三级自举(回归必验): + +- **Stage 0** — Python bootstrap 编译 `src/compiler/*.cr` → corec(第一代原生编译器) +- **Stage 1** — corec 编译 `src/compiler/*.cr` → corec2 +- **Stage 2** — corec2 编译 `src/compiler/*.cr` → corec3;闭环判据 = corec2/corec3 输出逐字节一致(可加 O1,历史 O0/O1 均已贯通) + +任何长时间构建/测试任务建议 `cpulimit -l 10` 或 `nice -n 19`(机器负载与噪音考虑;交互式短任务不必)。 + +## 三、分支模型与 jj 速查 + +模型:feature → develop → main。 + +- **main** = 正式版线:仅维护者 DslsDZC 可合入(ruleset:update 规则 + 无管理员绕过) +- **develop** = 集成分支:日常 PR 目标(ruleset:PR 通道 + 审批 + merge queue + CI 门槛) +- 日常开发 feature 分支 base = develop;PR 一律指向 develop + +jj 速查(仓库 hook 机械拦截 git;提交经 SSH 自动签名,无需人工操作): + +```bash +jj bookmark create feature/xxx # 每个改动独立分支 +# ……开发提交…… +jj git push -b feature/xxx # 推送 GitHub +gh pr create --base develop --fill # 不能指向 main +# → 审查(审批 + CI 绿)→ 手动 squash 合入 develop(merge queue 因免费计划降级,见 gitflow spec §3.3) +jj git fetch && jj bookmark move develop -r develop@origin # 同步远端 develop +``` + +发布(develop → main,仅维护者):`jj git push -b develop` → `gh pr create --base main --fill` → 合入后 `jj git fetch && jj bookmark move main -r main@origin`。 + +## 四、已知雷区 + +- **corec2 tokenizer 死循环**:约 9 个全局变量(`g_tok_cap`/`g_tokens`/`g_str_count`/`g_line`/`g_source_len` 等)未被 parser 注册到 `g_ir_globals` 时,赋值语句被静默丢弃 → tokenizer 卡死。曾以 tokenizer 参数化(PR #9)规避 `g_source` 依赖,残留项待根治。机理全文见 [CLAUDE.md Known Issues](../../CLAUDE.md);遇 tokenizer 卡住先查 `g_pos`/`g_tok_cap` 是否递增。 +- **O1 不稳定**:`--opt-level 1`(pass_cse 与 alloc_registers 元数据交互)在自举编译大函数时可能崩溃;遇崩降 O0 定位,详见 [CLAUDE.md Known Issues](../../CLAUDE.md)。 +- **core dump 挂起(桌面环境)**:trap 类测试进程 SIGILL 后,内核写 core 经 systemd-coredump 管道唤醒服务,拥堵时进程挂起。2026-09-05 起 `src/ci/run.sh` 全局 `ulimit -c 0`、trap 测试内置 `RLIMIT_CORE=0`;根治方向 = 图推导边界判定 pass。详见 [TODO.md](../../TODO.md)。 +- **测试总览与回归流程**:命令与跑法见 [CLAUDE.md Build & Test](../../CLAUDE.md),历史修复记录在 [TODO.md](../../TODO.md)。 + +## 五、贡献清单(检查列表) + +分工约定:第二维护者负责 TODO 横向扩展与 bug 修复;验证闭环主线归 DslsDZC。 + +- [ ] 读 [docs/README.md](../README.md) 定位文档;背景从 [project-book.md](../project-book.md)(项目定位)与 [design/execution-model.md](design/execution-model.md)(执行模型)入手 +- [ ] 从 [TODO.md](../../TODO.md) 挑一个明确任务;不启动归属验证闭环主线的改动 +- [ ] `jj bookmark create feature/xxx`(base = develop);全程只用 jj +- [ ] 开发;改动只限目标文件;提交前 `jj st` 复查无意外文件 +- [ ] 构建 + 测试:`python3 build_selfhost_native.py` 后跑相关套件(长任务建议 `cpulimit -l 10` / `nice -n 19`) +- [ ] 若动文档:头部定位声明、链接指向真实新路径、不复制职责重叠内容 +- [ ] `jj git push -b feature/xxx` → `gh pr create --base develop --fill` +- [ ] 审查意见闭环(审批 + CI 绿)后手动 squash 合入 develop;不自行合入 main diff --git a/docs/comptime.md b/docs/maintainer/proposals/comptime.md similarity index 91% rename from docs/comptime.md rename to docs/maintainer/proposals/comptime.md index aa5969a4..49939502 100644 --- a/docs/comptime.md +++ b/docs/maintainer/proposals/comptime.md @@ -1,5 +1,7 @@ # Core 编译时能力 +> 定位:受众 = 维护者/贡献者;状态 = **active(部分实现)**——@ 内建原语与编译期机制见 docs/developer/at-intrinsics.md;本设计文档为推导策略蓝图,多数单元格未落地 + ## 设计原则 - **90% 的编译时执行由HDFG自动推导,用户不需要任何关键字。** diff --git a/docs/concurrency.md b/docs/maintainer/proposals/concurrency.md similarity index 95% rename from docs/concurrency.md rename to docs/maintainer/proposals/concurrency.md index b1497c1b..ed4ede6a 100644 --- a/docs/concurrency.md +++ b/docs/maintainer/proposals/concurrency.md @@ -1,5 +1,7 @@ # Core 并发原语 +> 定位:受众 = 维护者/贡献者;状态 = **active(已实现)**——go/chan 端到端可用(2026-07-31);细节见 src/stdlib/goroutine.cr、chan.cr、src/scheduler/;本设计文档的 flow/yield/await 扩展为演进方向 + ## 设计原则 - **只有一张图。** go/flow/yield/await/walk 都是图上的节点和边,不是额外的运行时概念。 diff --git a/docs/distributed.md b/docs/maintainer/proposals/distributed.md similarity index 96% rename from docs/distributed.md rename to docs/maintainer/proposals/distributed.md index d0c83f7c..6f036c9a 100644 --- a/docs/distributed.md +++ b/docs/maintainer/proposals/distributed.md @@ -1,5 +1,7 @@ # Core 分布式 +> 定位:受众 = 维护者/贡献者;状态 = **proposal(核心已备,运行时未实现)**——go/await/子图边界就位;跨机器传输/QUIC/认证/重试未实现 + ## 设计原则 - **图跨机器。** `go` 本来就在本地创建节点,加上 `@("node")` 目标就是远程节点。 diff --git a/docs/dynamic-typing.md b/docs/maintainer/proposals/dynamic-typing.md similarity index 95% rename from docs/dynamic-typing.md rename to docs/maintainer/proposals/dynamic-typing.md index aea4830a..920eb7d0 100644 --- a/docs/dynamic-typing.md +++ b/docs/maintainer/proposals/dynamic-typing.md @@ -1,5 +1,7 @@ # Core 动态类型 +> 定位:受众 = 维护者/贡献者;状态 = **active(部分实现)**——dyn 类型(2026-07-30)+ @hotpatch 基础(2026-07-29)已实现;滚动更新/参数化分流为设计态(未实现);细节见 src/stdlib/dex.cr + ## 设计原则 - **弱动态风格,编译器在图知道的范围内做到强动态的保证。** diff --git a/docs/ffi.md b/docs/maintainer/proposals/ffi.md similarity index 91% rename from docs/ffi.md rename to docs/maintainer/proposals/ffi.md index 5d0a73df..0d9c977f 100644 --- a/docs/ffi.md +++ b/docs/maintainer/proposals/ffi.md @@ -1,5 +1,7 @@ # Core FFI +> 定位:受众 = 维护者/贡献者;状态 = **partial(仅 extern fn)**——@ffi 统一入口为设计态未实现;当前 C ABI 走 extern fn(IR_CALL/syscall);细节见 src/stdlib/io.cr extern 声明 + ## 设计原则 - **所有语言统一走 `@ffi`,参数描述运行时特征。** diff --git a/docs/generics.md b/docs/maintainer/proposals/generics.md similarity index 94% rename from docs/generics.md rename to docs/maintainer/proposals/generics.md index 11f2b90a..61d5adf7 100644 --- a/docs/generics.md +++ b/docs/maintainer/proposals/generics.md @@ -1,5 +1,7 @@ # Core 泛型 +> 定位:受众 = 维护者/贡献者;状态 = **active(已实现)**——泛型函数/struct + 跨模块实例化(monomorph.cr)已纳入自举;本设计文档留作机制参考,细节见 checker.cr/monomorph.cr/tests/suite/generics_test.cr + ## 设计原则 - **尽量靠图推导,不需要用户标注约束。** diff --git a/docs/lazy.md b/docs/maintainer/proposals/lazy.md similarity index 94% rename from docs/lazy.md rename to docs/maintainer/proposals/lazy.md index 80fcddba..f866619d 100644 --- a/docs/lazy.md +++ b/docs/maintainer/proposals/lazy.md @@ -1,5 +1,7 @@ # Core 惰性求值 +> 定位:受众 = 维护者/贡献者;状态 = **proposal(未实现)**——图天然惰性论点成立,eager 为当前策略;使用点 force 机制与设计语义一致(部分),控制流级惰性未做 + ## 设计原则 - **图本来就是惰性的。** 节点不在边上游等待时就不执行。当前的 IR 生成策略是 eager——节点一出现就执行。这不是图的限制,是策略的选择。 diff --git a/docs/probabilistic.md b/docs/maintainer/proposals/probabilistic.md similarity index 85% rename from docs/probabilistic.md rename to docs/maintainer/proposals/probabilistic.md index 4a315121..7648caa2 100644 --- a/docs/probabilistic.md +++ b/docs/maintainer/proposals/probabilistic.md @@ -1,5 +1,7 @@ # Core 概率编程 +> 定位:受众 = 维护者/贡献者;状态 = **proposal(讨论中)**——概率 = pass 的论点未落地,无实现痕迹 + ## 设计原则 - **概率不是新特性——是一个 pass。** @@ -45,7 +47,7 @@ z := random(binomial(10, 0.5)); // 二项分布 ## 观测——图上的约束 -观测是贝叶斯更新。在 Core 上不发生在运行时——发生在编译期的图分析。约束可以写在 `.corespec` 规约文件中,也可以直接在条件分支中表达: +观测是贝叶斯更新。在 Core 上不发生在运行时——发生在编译期的图分析。约束可以写在规约中(.cr 内联,独立 `.corespec` 格式已退役,见 ADR-0001),也可以直接在条件分支中表达: ```core sensor := random(normal(true_temp, 0.1)); @@ -66,7 +68,7 @@ if sensor > 30 { 处理: 1. 扫描 random() < p 模式 → 分支边标权重 2. 沿值流传播权重 → 变量的分布信息 - 3. 如果遇到 .corespec 中的观测约束 → 更新后验 + 3. 如果遇到规约中的观测约束 → 更新后验 4. 结果的统计量(期望值、方差)附加到图节点上 输出:带概率标注的HDFG @@ -88,7 +90,7 @@ if sensor > 30 { |------|------|-----------| | 分支概率 | `random() < p` 自动推导 | pass,不是语法 | | 抽样 | `random(dist)` | 库函数 | -| 观测 | `if cond` + `.corespec` | 已有机制复用 | +| 观测 | `if cond` + 规约约束(.cr 内联) | 已有机制复用 | | 后验推导 | pass 传播 | pass | `@` 不参与概率。 diff --git a/docs/maintainer/testing.md b/docs/maintainer/testing.md new file mode 100644 index 00000000..b40277b4 --- /dev/null +++ b/docs/maintainer/testing.md @@ -0,0 +1,132 @@ +# 测试与回归操作手册(testing) + +> 定位:受众 = maintainers;状态 = active;真源 = [tests/](../../tests/) 与 [src/ci/run.sh](../../src/ci/run.sh)(命令以仓库现状为准,有出入先改源码注释/本文)。 +> 仓库地图/构建/分支见 [onboarding.md](onboarding.md);命令速览另见 [CLAUDE.md](../../CLAUDE.md) Build & Test 段;未竟项见 [TODO.md](../../TODO.md)。 + +## 一、三套测试的定位与跑法 + +### tests/bootstrap/ —— Python bootstrap 管线回归 + +定位:纯 Python 驱动,import `bootstrap/corec` 模块走全管线(lex → parse → resolve → desugar → typecheck → ir_gen)后解释执行,比较结果;诊断类比较错误数。**不依赖 build/corec**。 + +核心文件:test_pipeline.py(核心管线:词法/字面量/表达式/控制流)、test_borrow.py(借用检查错误检测)、test_generics.py(泛型函数 + 泛型结构体);同目录 test_apx_tag/test_dex_arith/test_dex_type/test_modules 等(以目录实况为准)。 + +跑法(仓库根,无需先构建): + +```bash +python3 tests/bootstrap/test_pipeline.py +python3 tests/bootstrap/test_borrow.py +python3 tests/bootstrap/test_generics.py +``` + +### tests/selfhost/ —— 自举/原生编译器回归 + +定位:驱动原生 `build/corec` 子进程为主;test_compile.py 另演示「拼接 src/compiler + src/stdlib 源 → Python bootstrap 全管线编译 → 解释器执行」。**前置**:先跑 `python3 build_selfhost_native.py`——build/corec 缺失时脚本直接报 `[FAIL] missing native compiler`。 + +核心三件: + +- test_compile.py — 自举管线冒烟(编译器源拼接编译 + 示例程序执行比对) +- test_impl.py — impl/方法接收者/self 字段:CHECK_CASES(check 必须通过)+ NATIVE_CASES(编译成 ELF 运行,断言退出码) +- test_borrow.py — 借用检查 7 规则(分类见 §二);用例 = (名称, 内联源码, 期望是否报借用错),写临时 .cr 跑 `corec check`。契约:期望诊断 → 退出码 1、无诊断 → 0(check 命令「诊断即失败」;F19 修复后断言随契约更新,见文件内注释) + +跑法: + +```bash +python3 tests/selfhost/test_compile.py +python3 tests/selfhost/test_impl.py +python3 tests/selfhost/test_borrow.py +``` + +同目录其余(test_backend_bootstrap / test_lsp / test_pointer_safety / test_native_* / test_region_cfg 等)同一模式、以目录实况为准;test_backend_bootstrap.py 已内置 `nice -n 19` 与 180s 超时。 + +### tests/suite/ —— 集成用例(.cr 源码) + +定位:端到端集成——tests/suite/*.cr 逐个经 `./build/corec build` 编译成 ELF 并运行,**main 返回 0 为通过**;`*_mini*.cr` 视为开发草稿,CI 跳过。 + +跑法(单文件): + +```bash +./build/corec build tests/suite/at_test.cr -o /tmp/core_suite_bin --static +chmod +x /tmp/core_suite_bin && /tmp/core_suite_bin +``` + +全量走 CI job:`CI_JOB_NAME=suite src/ci/run.sh`(job 一览见 §三)。 + +## 二、怎么加用例 + +通用形态:文件头部 helper + 用例列表或内联调用 + 结尾汇总(passed/failed → sys.exit)。加完先跑单文件,再跑该套件全量;动了编译器本体还要走 §三 full-bootstrap。 + +- **bootstrap 管线类**:模块体直接调 `run_test(name, src, expected)`(int 结果)或 `run_test_raw(name, src, expected)`(string/float): + ```python + run_test("add", "fn add(a: int, b: int) -> int { return a + b; } fn main() -> int { return add(2, 3); }", 5) + ``` + 借用错误类用 `check_errors(src, desc, expect_errors)`(断言错误数有无)。 +- **selfhost 借用检查**(test_borrow.py):向 `CASES` 加元组 `(名称, 内联源码, 期望是否报借用错)`。7 规则现状(以 CASES 用例名为准,每条含放行/报错两个方向的样本;规则语义真源 = [src/compiler/checker.cr](../../src/compiler/checker.cr) 的 check_borrow 等): + 1. immutable borrow then use original + 2. mutable borrow then use original + 3. multiple immutable borrows allowed + 4. immutable then mutable borrow + 5. borrow released after block scope exit + 6. normal copy use + 7. mutable then immutable borrow +- **selfhost impl/编译**(test_impl.py):CHECK_CASES 加 `(名称, 源码)`(check 须过);NATIVE_CASES 加 `(名称, 源码, 期望退出码)`(原生 ELF 运行)。 +- **suite 集成**:新建 `tests/suite/xxx_test.cr`,`fn main() -> int` 正常路径 `return 0`;崩溃/非零退出 = 失败。注意别用 `*_mini*.cr` 命名(CI 跳过)。 + +## 三、回归流程 + +**快检**(改动小/未动编译器核心):`./build/corec check src/compiler`(编译器全源整目录类型检查,不触发 ELF 后端)。 + +**自举三阶段 + 字节一致验证**(src/ci/run.sh full-bootstrap job 实录): + +```bash +python3 build_selfhost_native.py # Stage 0:Python bootstrap → build/corec(+corearch/corelsp) +./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 # Stage 1:corec 编译编译器 → corec2 +cp ./build/corearch /tmp/corearch && chmod +x /tmp/corec2 /tmp/corearch +/tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 # Stage 2:corec2 编译编译器 → corec3 +cmp /tmp/corec2 /tmp/corec3 # 判据:两代编译器输出逐字节一致 +``` + +说明:固定 `-O 0`(CI 实录;O1 现状见 §四);corec3 生成后做一次 `--help` 冒烟。后端独立回归(ELF 后端/自举 corearch):`python3 tests/selfhost/test_backend_bootstrap.py`(三阶段自举 corearch 并比对,内置 nice + 超时)。 + +**CI job 一览**(本地复现 = `CI_JOB_NAME= src/ci/run.sh`;run.sh 已内置 `ulimit -c 0`): + +| CI_JOB_NAME | 内容 | +|---|---| +| check | build_selfhost + `./build/corec check src/compiler` | +| bootstrap-tests | 三个 bootstrap 套件(§一) | +| selfhost-tests | 构建 + test_compile / test_impl / test_borrow / test_pointer_safety | +| suite | 构建 + run_suite(逐 .cr 编译运行) | +| full-bootstrap | 三阶段自举 + cmp 字节一致 | + +**CPU 限制**:长时间构建/测试任务(无人值守/共享机器)建议 `cpulimit -l 10` 或 `nice -n 19`,防风扇噪音与卡顿(注:CLAUDE.md 铁律 #6 约束的是 AI 会话;人类交互式短任务不必限): + +```bash +nice -n 19 python3 build_selfhost_native.py +nice -n 19 python3 tests/selfhost/test_compile.py +# 或安装 cpulimit 后整条命令包 cpulimit -l 10(等效) +``` + +CI 环境无此约束;本地桌面必守。CI job 的本地复现同样要先包一层:`nice -n 19 src/ci/run.sh` 场景下用 `nice -n 19 env CI_JOB_NAME=full-bootstrap src/ci/run.sh`。 + +## 四、常见失败定位 + +### tokenizer 死循环(自举第二代起) + +- 症状:`build/corec build` 挂起无输出(或 CPU 空转)。 +- 机理:约 9 个全局变量(`g_tok_cap`/`g_tokens`/`g_str_count`/`g_line`/`g_source_len`/`g_x86_is_global`/`g_x86_global_cap`/`g_str_hash`/`g_error_count`)未被注册到 `g_ir_globals` 时,对其赋值被静默丢弃 → tokenize 循环推进变量不增长。全文见 [CLAUDE.md Known Issues](../../CLAUDE.md)。 +- 排查:先确认卡点确在 tokenize(tokenizer 循环内临时打印 `g_pos`/`g_tok_cap` 是否递增);新增全局变量时核对注册链路(注册点以源码为准)。历史规避:tokenizer 参数化 `tokenize(_src)` 消除 `g_source` 全局依赖(PR #9),残留项待根治。 + +### O1 崩溃(--opt-level 1) + +- 症状:自举编译大函数时崩溃(pass_cse 与 alloc_registers 元数据交互异常)。 +- 处理:回归/CI 固定 O0(§三实录);定位优化问题时单独 `-O 1` 复现并二分到函数;相关文件:[src/compiler/opt.cr](../../src/compiler/opt.cr)(pass_cse/alloc_registers)、pass.cr。 + +### ELF 字节不一致或产物错乱 + +- 先查指令编码层再怀疑逻辑:[src/arch/linux/ld/](../../src/arch/linux/ld/) 下 instr.cr(指令发射编码)、sizes.cr(`sz_*` 字节数预估——label 前向引用依赖它)、resolve.cr(label 解析后置填充)。改指令发射必须同步 instr 与 sizes 两处。 +- 比对法:O0 下 corec2 ≠ corec3 = 编译不自洽;排除输出差异(路径/环境)后缩到最小复现,修完先 suite 后 full-bootstrap。 + +### trap 类测试挂起(桌面环境) + +- 机理:core_pattern = systemd-coredump 管道,trap 程序(SIGILL)core dump 写入拥堵时进程挂起。 +- 处理:先 `ulimit -c 0` 或测试内设 `RLIMIT_CORE=0`(src/ci/run.sh 已内置);根治方向见 [TODO.md](../../TODO.md)(图推导边界判定 pass——检查前移编译期,缩小运行时 trap 面)。 diff --git a/docs/memory-model.md b/docs/memory-model.md deleted file mode 100644 index 8e14ccf0..00000000 --- a/docs/memory-model.md +++ /dev/null @@ -1,393 +0,0 @@ -# 存储语义:缓存(Cache Semantics)与经典映射(图锚定区域) - -## 零、术语对照表 - -"内存"一词在本文件(及全仓库)有三层含义,阅读时先判定语境;全仓库架构术语唯一权威见 `docs/glossary.md`: - -| 术语 | 含义 | 例子 | -|------|------|------| -| 物理内存 | 硬件 | DRAM、寄存器、量子存储 | -| 语义存储 | 缓存(范式无关本体) | 条目、配方、驱逐、再生 | -| 映射 | 实现 | 字节寻址、区域、Arena | -| 格 | 内存模型层 = 中间存在空间 | 图 → 格 → 编码 三层链的第二层(本文件 = 格层内容;与「格(Lattice)层内代数」区分,见 memory-model-capability-lattice.md §四 4.4) | - -## 一、语义本体:缓存语义 - -> **2026-08-15 升级说明**:本文件此前将 CompCert v2 字节模型(块 + 字节偏移 + 每字节权限)描述为语义本体。这是文档层的错误——**实现(HDFG)从来就是缓存语义**:节点 = 配方(值的产生方式),边 = 值流,state edges = 顺序约束,驱逐/再生 = 丢令牌/重跑节点。本次改动只纠偏文档,零代码改动。设计依据:`docs/superpowers/specs/2026-08-15-cache-semantics-design.md`。 - -> **2026-08-27 定位(v4 晋升)**:本文件是三层映射链(图 → 格 → 编码,见 `docs/memory-model-capability-lattice.md` §四)中「格」层的内容——缓存语义 = 格层的语义本体(如何存在),经典映射 = 格在字节机器上的实例(如何实现)。图的职责(关系空间:非因果/模糊/超图灵)见 `docs/dataflow-design.md` §8——超图灵性属于图,格层只需提供足够一般的状态模型;编码职责见各后端。v4 定稿(`docs/memory-model-capability-lattice.md`):**规则封闭对象开放**(层规则零签名)、**无格承诺**(格代数 = 映射参数)、**能力不提升一等公民**(语义还原图上,授权归治理层);**寄存器分配 = 缓存语义映射实例**(`docs/regalloc-cache-mapping.md`)。 - -Core 的存储语义本体是**缓存**——范式无关的存储抽象。以下七条为定义性条款,将来供验证器(翻译桥/CIC 内核)消费: - -1. **值 = 配方**。条目的语义 = (产生它的图节点, 输入边)。不存在独立于配方的"存储事实"。 -2. **驱逐不变量**。驱逐图内任意条目(丢弃存储物、保留配方)不改变可观测语义——只影响性能,不影响正确性。形式化目标:⟦G ∖ storage(e)⟧ = ⟦G⟧。这是"缓存语义"的定义性质。 -3. **再生等价**。重跑配方节点产生的值与原条目可观测等价。这是条款 2 的机制保证,也是"任何范式都能换映射"的语义依据。 -4. **边界公理**。图边界输入(MMIO/FFI/输入/测量)是无配方条目(边界类),语义上是符号常量。语法层叫 `unsafe`(边界标注),语义层只叫"边界"。边界之外才需要 state,边界之内全是可重算的。 -4b. **图内不可重算(2026-08-27 v4 并入,M4 输入)**。执行标注空间(神谕 / BSS 实数 / FIXPT 声明 / 模糊融合,见 `docs/dataflow-design.md` §8)存在**图内**无配方条目——非边界、不可重算。驱逐不变量/再生等价对其不成立(重跑神谕 ≠ 原结果):其存储必须持久(home 保有材料),驱逐必须写回;身份 = 产生节点(图节点身份)。边界条目与图内不可重算条目统一为「无配方条目」规则,一视同仁、不按范式枚举(映射实例侧规则见 `docs/regalloc-cache-mapping.md` §三)。 -5. **赋值 = 版本化**。`x = x + 1` 在语义上 = "x₁ 创建、x₀ 失效、绑定移动",不是修改内存单元。顺序约束由 state edges 表达。"可变"不是内存单元的属性,是绑定可移动的许可。 -6. **地址 = 映射**。`&x` = (条目标识, 偏移),字节地址只是经典投影,不是语义对象。指针算术合法性由条目标识 + 偏移域验证(现有 provenance 三 pass 保留不动)。 -7. **映射实例正确性**。区域/arena/字节权限(CompCert v2)是经典映射实例,其正确性标准 = 保持条款 1–6 的可观测语义。 - -**推论(范式映射表 = 映射正确性定理表)**:经典行 = 证明缓存语义 ⊇ 字节内存实现(CompCert 式语义保鲜);量子行 = 将来证明缓存语义 ⊇ 量子存储实现。语义保鲜从"编译正确性定理"升级为"映射正确性定理"。 - -## 二、经典映射:图锚定区域 - -本部分及以下全部内容**不再是语义本体**——它们是缓存语义(§一)在经典硬件(字节寻址)上的**映射实例**。 - -Core 采用**图锚定区域**的统一内存管理方案:区域(Region)锚定在HDFG上——**区域是子图节点的字节域**,不是词法作用域的影子。堆内存划分为与 HDFG 子图绑定的独立区域,每个区域内部使用线性指针碰撞分配(bump allocation),回收直接将整个区域游标重置回起始地址(格式化清空)。 - -本设计是 2026-07-28 多 Arena 模型(`docs/superpowers/specs/2026-07-28-arena-model-design.md`)的**概念升级**:Arena 保留为分配器面实现,语义面升级为图锚定区域。传统区域内存管理(Tofte-Talpin 区域栈、Cyclone、Verona)全部锚定词法作用域(LIFO 嵌套);Core 的执行模型是HDFG,区域与图同构。 - -**不可放弃的两个核心价值**(放弃它们的代价): - -1. **无碎片**——静态已知大小的分配路径保持纯 bump,零碎片 -2. **动态模式下工程可控的极低碎片**——动态大小分配路径走分档子区域,浪费上界由 size class 表决定 - -长期运行服务的内存占用上限由并发区域数量决定,天然无 GC 停顿。 - ---- - -## 设计动机 - -### 传统内存管理的痛点 - -| 方案 | 问题 | -|------|------| -| 手动 malloc/free | 悬挂指针、双重释放、内存泄漏 | -| 引用计数 (RC) | 循环引用、原子操作开销、Cache Miss 链式更新 | -| 跟踪式 GC (Mark-Sweep/Compact) | 停顿不可预测、内存开销不确定、不适合硬实时 | -| RAII + 所有权 (Rust) | 静态正确,但复杂数据结构需精细设计借用关系,且 Drop 顺序在运行时产生开销 | - -### 区域方案的优势 - -- **分配 O(1)**:仅指针碰撞,无空闲链表遍历 -- **回收 O(1)**:整体重置游标,不逐元素析构 -- **无碎片**:区域内线性分配,无释放操作,不会产生内部碎片 -- **可预测**:分配只检查区域剩余容量,无 GC 停顿 -- **Cache 友好**:同一子图的数据集中在同一区域,空间局部性好 - -### 论文依据 - -| 论文 | 结论 | 对 Core 的意义 | -|------|------|---------------| -| Tofte & Talpin, *Region-Based Memory Management* (POPL'94 / I&C 1997) | 存储 = 区域的栈;区域分配/回收点由类型-效果分析自动推断;形式化可靠性证明。已知弱点:区域活得太久导致内存泄漏 | 自动推断可行且可证;泄漏弱点 → 需要显式提前释放(见 Cyclone) | -| Gay & Aiken, *Memory Management with Explicit Regions* (PLDI'98);*Language Support for Regions* (PLDI'01) | 显式区域与 malloc/free 性能相当;区域子类型(outlives);多级区域层次;**提前释放是工程可用的关键** | "指定回收"作为编译器内部机制(IR 层生成回收点),不暴露语法 | -| *Reference Capabilities for Flexible Memory Management* (OOPSLA'24, Verona 线) | 区域 = dominator scope,内部对象同生共死;内存按区域局部管理;底层 snmalloc = 分档 bump 分配(size class) | size class 是动态碎片控制的工程标准答案 | -| Leroy et al., *The CompCert Memory Model, Version 2* (2012) | 块 + (块 ID, 字节偏移) 指针;**每字节权限**(Freeable > Writable > Readable > Nonempty > Empty);块按构造分离;Coq 机器验证 | 每字节权限层 = **经典映射实例的权限面**(验证器在经典机器上消费的权限视图,见 §一 条款 7) | - ---- - -## 核心概念 - -### 区域(Region) - -区域是一段连续虚拟内存区域,维护一个单调递增的分配游标(bump pointer): - -``` -┌─────────────────────────────────────────────┐ -│ 区域 │ -│ ┌──────┬──────┬──────┬──────┬─────────────┐│ -│ │ Alloc │ Alloc │ Alloc │ │ 剩余空间 ││ -│ │ #1 │ #2 │ #3 │ │ ││ -│ └──────┴──────┴──────┴──────┴─────────────┘│ -│ ↑ ↑ │ -│ 游标 (ptr) 上限 (end) │ -└─────────────────────────────────────────────┘ -``` - -分配伪代码: - -``` -fn alloc(size: usize) -> *mut u8 { - let remaining = self.end - self.ptr; - let aligned = align_up(size, 8); - if aligned > remaining { return OOM; } - let addr = self.ptr; - self.ptr += aligned; - addr -} -``` - -### 图锚定:区域 = 子图节点的字节域 - -每个HDFG节点(DFNode)可关联一个区域。执行器在激活节点时将其区域设为当前区域,节点内所有分配均来自该区域。 - -``` -DFGraph Arena Pool(区域的实现) -┌──────────┐ ┌──────────────┐ -│ node A │────────────▶│ 区域 A │ -│ node B │────────┐ │ │ -│ node C │──┐ └───▶│ 区域 B │ -│ node D │──│─────────▶│ 区域 C │ -└──────────┘ │ │ 区域 D │ - │ └──────────────┘ - │ ↑ - │ 区域池 —— 所有区域 - │ 由运行时统一管理 -``` - -三个推论(与传统词法锚定模型的分野): - -**推论 A:生命周期 = 图活性,不是 LIFO。** RegionCheck 的 `cur_seq < exit_seq` 判定(见 `docs/pointer-model.md`)就是图版本的生命周期定义。这天然支持 flow/go 的独立区域(区域栈做不到非 LIFO 生命周期),也天然给出跨区域引用的合法性判据(outlives 的图形式)。 - -**推论 B:区域操作沿图边。** 区域不仅嵌套(树),还能沿 HDFG 边操作: - -- **split**:一块字节沿边划给子消费者(= 字节块独立回收;子区域 = 图边的子块,自己的游标 + 自己的重置点) -- **merge**:汇合点两个子区域的数据汇入(拷贝或区域合并) -- **share**:分叉点一个区域被多个消费者只读共享(共享者必须 outlive 引用) - -**推论 C:每字节归属 = 图的 provenance 边。** 记忆模型是"字节序列 + 宽度 + 边界"——provenance(归属)、offset(字节偏移)、alloc_size(字节大小)全部与类型无关。"控制每一个字节"不是新语法,是图上本来就有的信息。 - -> (2026-08-15)在缓存语义下,"字节序列 + 宽度 + 边界"是**经典映射的视图**——语义本体是条目标识 + 偏移(见 §一 条款 6)。 - -### 与传统区域模型的关系 - -区域树是图区域的特例(无汇合/分叉的图 = 树 = 词法嵌套);Tofte-Talpin 的区域栈是更窄的特例(树 + LIFO)。本设计是推广,不是发明。 - -> 术语注意:dataflow 模块已有「region」一词指控制流嵌套(SG_IF/LOOP/FOR/FLOW/UNSAFE)。本文的「区域」指**内存字节域**,按上下文区分。 - -### Arena = 区域的实现 - -2026-07-28 的 Arena 设计(arena pool、bump 双路径、链式扩容、子图绑定、大小预计算,见 `docs/superpowers/specs/2026-07-28-arena-model-design.md`)原样继承,作为分配器面实现。概念关系: - -| 语义面(本设计) | 分配器面(07-28 设计) | -|------------------|----------------------| -| 区域(Region) | Arena slot | -| 子区域 split | 子图嵌套 arena / 分档子分配器 | -| 图活性生命周期 | arena_new / arena_reset 插桩 | -| 策略推导 | 大小预计算(IR 层) | - ---- - -## 机制总图 - -| # | 需求 | 图上的机制 | 验证/推导者 | -|---|------|-----------|-----------| -| 1 | 字节块独立回收 | 区域 = 子图节点;子区域 = 沿图边 split;显式提前释放(回收点)由 IR 层生成(Cyclone early deallocation 的自动版);merge/share 处理汇合/分叉 | 图活性(RegionCheck) | -| 2 | 逐字节布局 | **布局元数据进图**:字段字节偏移/packed/对齐,后端与验证器同一来源(语义保鲜)。默认布局全自动(自然对齐),`layout(...)` 仅例外入口 | 编译期计算,图节点存储 | -| 3 | 固定地址放置 | **ALLOC_AT 节点**(地址+大小+对齐)进图,与 ALLOC 同路径获得 provenance | ProvenanceVerify(边界+宽度) | -| 4 | 碎片有界 | **策略推导**:子图全静态大小 → 纯 bump(零碎片);混动态 → 分档子区域(size class 表由图上分配大小分布推导) | 图分析(大小预计算扩展) | -| 5 | 跨区域 | **outlives 顺序判定**:引用者存活区间 ⊆ 被引用区域存活区间(RegionCheck 现有机制,从"禁止"放宽为"顺序判定") | RegionCheck | - -不变的三条铁律: - -1. 静态已知大小路径 = 纯 bump,**零碎片** -2. 动态路径 = 分档子区域,**浪费上界由 size class 表决定**(工程可控:档位越细上界越低,如 pow2 档 ≈50%、细分档 ≈12.5%) -3. 区域池 / 嵌套 / 子图绑定 = 原样继承,概念升级为"区域" - ---- - -## 用户面:零管理负担 - -用户零参与、全部由图推导的事项: - -| 事项 | 谁做 | -|------|------| -| 区域划分/生命周期 | 图活性推导(子图边界即区域) | -| 回收时机(含"指定回收"需求) | 图分析生成回收点——推断在编译器内部完成,用户看不到 | -| 碎片策略 / size class | 图分析推导分配策略 | -| 跨区域合法性 | RegionCheck 自动判定 | -| 所有验证 | 三点 pass 自动 | - -用户只表达两件只有用户自己知道的事实: - -```core -// 1. 布局声明(例外入口)——默认布局全自动:编译器按类型推导自然布局 -// (字段顺序 + 自然对齐),用户不写任何东西。 -// layout(...) 仅当默认布局不合用时才需要:packed(FFI)、强制对齐、硬件结构。 -struct PackedHeader layout(packed, align(4)) { - a: u8, // 偏移 0 - b: u32, // 偏移 4 - c: u16, // 偏移 8 -} - -// 2. 放置声明——地址是物理事实,只有用户知道。 -// 声明式进图(地址+大小+对齐),之后全图追踪,ProvenanceVerify 照常验证。 -mmio := alloc_at(0x7fff0000, 4096, align(4096)); -``` - -- **不提供**显式区域/回收语法(YAGNI)。「指定回收」是编译器内部机制(IR 层生成回收点)。 -- 与 `docs/pointer-model.md` 2026-08-10 定论的关系:0x 字面量仍是 unsafe 外部入口;`alloc_at` 是**声明式进图**(获得 provenance 的节点),不冲突——声明是唯一信任点,之后全图追踪。 - ---- - -## 经典映射的权限面:字节权限层 - -经典映射 = 图 + 字节权限层(CompCert v2):权限层**不是语义本体**,是缓存语义(§一)在字节寻址机器上的权限投影,正确性标准 = 保持 §一 条款 1–6 的可观测语义(条款 7)。 - -- 每字节内容 = 字节序列(已有,见 `docs/pointer-model.md`) -- 每字节权限:**Freeable > Writable > Readable > Nonempty > Empty**(新加) - - Freeable:可比较、可读、可写、可释放 - - Writable:可比较、可读、可写、不可释放 - - Readable:可比较、可读、不可写 - - Nonempty:仅可比较(指针有效性) - - Empty:无任何权限(未分配/已释放) -- 区域树 = 图的子图结构;布局元数据、ALLOC_AT 边界、outlives 结论全部从图导出——**验证器消费图即消费全部内存语义** - ---- - -## 所有权与逃逸 - -区域方案的核心前提:**子图内分配的引用不会逃逸到子图之外**。编译器通过静态分析保证。逃逸分析在 IR 层面以数据流分析实现:每个 IR 变量标注区域来源(`ArenaTag`),赋值/传参时检查兼容性,违反规则产生编译错误。 - -### 逃逸规则(2026-08-13 修订) - -1. **向下逃逸禁止**:区域 A 内分配的对象不能作为参数传递给生命周期比 A 长的子图 -2. **向上逃逸禁止**:区域 A 内分配的对象不能作为返回值给生命周期比 A 长的调用者 -3. **全局逃逸禁止**:区域 A 内分配的对象不能赋值给全局变量 -4. **跨区域引用:outlives 顺序判定**(修订)——区域 A 的指针可以指向区域 B 的对象,**当且仅当** B 的存活区间 ⊇ 引用的使用区间(RegionCheck 的 `cur_seq < exit_seq` 判定)。不再一刀切禁止。 - -### 例外:图边界 - -编译器无法追踪 provenance 的入口点(外部地址、FFI 返回值、inline asm)仍需要 `unsafe`,和所有HDFG无法追踪 provenance 的情况一样。详见 `docs/pointer-model.md`。`unsafe` 块在此处的作用不是"关掉检查",而是"标注图边界入口"。 - ---- - -## 与HDFG的集成 - -### 子图类型与区域策略 - -| 子图类型 | 区域策略 | 说明 | -|----------|-----------|------| -| DAG (函数/分支/for) | 栈式区域 | 函数入口创建,出口回收。与调用栈深度同步 | -| 静态循环 (loop) | 固定区域 | 循环开始前分配,结束后回收。容量根据循环不变量预计算 | -| Flow | 独立区域 | 每次 flow 激活创建独立区域。并发 flow 各自独立 | -| Go | 独立区域 | 每个 goroutine 拥有独立区域。退出时整体回收 | -| Yield/Recv | 消息区域 | 跨 flow 传递的数据在接收端区域中重新分配 | - -### 区域嵌套 - -区域可嵌套:子图在父图的区域内创建子区域。子区域回收后,父区域不受影响。 - -``` -区域 A (函数 main) -├── 区域 B (loop 主体) -├── 区域 C (flow 1) -└── 区域 D (flow 2) - └── 区域 E (flow 2 内的子 loop) -``` - ---- - -## 运行时布局 - -### 区域内存池 - -运行时维护一个区域池(Arena Pool),包含固定数量的预分配区域: - -``` -区域池 -┌────┬────┬────┬────┬────┬────┬────┬────┐ -│ P0 │ P1 │ P2 │ P3 │ P4 │ P5 │ P6 │ P7 │ -└────┴────┴────┴────┴────┴────┴────┴────┘ - │ ↑ - │ 分配中的区域 - │ - └─── 空闲区域 -``` - -- 活跃区域数量 = 当前并发子图数量 -- 任一时刻,最多 `MAX_CONCURRENCY` 个区域同时活跃 -- 未使用的区域留在池中,不需归还操作系统 -- 必要时可向操作系统扩展区域容量 - -### 大小预计算 - -编译器在 IR 生成阶段计算每个子图的最大内存需求: - -1. 遍历子图内所有 `alloc` 调用 -2. 静态推断大多数分配大小(结构体、数组已知长度) -3. 对动态分配(如运行时决定的缓冲区大小)标注最大容量上限 -4. 若无法推断上限,使用部署配置中的默认容量 - -**策略推导**(2026-08-13 扩展):预计算结果同时决定子图的分配策略——全静态已知 → 纯 bump(零碎片);混有动态大小 → 分档子区域(size class 表由图上分配大小分布推导)。 - ---- - -## 与并发模型的协作 - -### 无锁分配 - -每个区域只被一个执行线程访问。区域内 bump pointer 可以是线程局部变量,无需原子操作。 - -``` -// 线程安全:每线程区域 -thread_local! { - static CURRENT_REGION: RefCell; -} -``` - -### Go/Flow 的区域隔离 - -``` -go f() → 新区域 G → 函数 f 内所有分配在 G - ↓ - f 结束 → 区域 G 整体回收 - ↓ - go 表达式的结果 → 拷贝到父区域 -``` - -- `go f()` 创建独立区域,f 的返回值在 f 结束后拷贝到父区域 -- `flow` 的每个分支拥有独立区域,分支退出时回收 -- `yield` 的数据通过消息区域传递,接收方在自己的区域中重新分配 - -### 停止机制 - -长期运行的服务,内存占用上限为 `MAX_CONCURRENCY × MAX_REGION_SIZE`。当区域池耗尽空闲区域时,新的 go/flow 被阻塞直到有区域可用。不需要 GC。 - ---- - -## Rust 风格的对比 - -| | Rust (RAII + 所有权) | Core (图锚定区域) | -|--|---------------------|--------------| -| 分配开销 | 栈分配 O(1),堆分配需寻找空闲块 | Bump O(1) | -| 释放开销 | Drop 链递归 O(深度) | 游标重置 O(1) | -| 借用检查 | 生命周期标注复杂,NLL 推断有局限 | 区域标签传播 + 图活性,按子图边界检查 | -| 并发 | Arc/RwLock 运行时开销 | 区域隔离,无共享 | -| 循环引用 | 需 Weak 打破 | 同区域内允许循环(回收时整体释放) | -| GC | 无 GC | 无 GC(区域回收等价于批量释放) | -| 适用场景 | 通用系统编程 | 数据流驱动、并发密集、实时系统 | - ---- - -## 设计状态 - -**2026-07-28**:多 Arena 模型已完整实现(`src/stdlib/arena.cr`)——完整生命周期 -(init/new/reset)、动态元数据、free list、嵌套;IR 子图绑定(函数/loop/for/unsafe 自动 -arena lifecycle + 大小预计算);ELF 后端双路径 alloc(arena 感知 + 全局 bump 回退); -mmap 堆扩展(BSS 打满自动 mmap 1GB);emit_alloc_body 零初始化 + 链式扩容。实现细节见 -`docs/superpowers/specs/2026-07-28-arena-model-design.md`。 - -**2026-08-09**:发现 arena bump 分配运行时死循环(`arena_init` + `arena_new` 后调用 `alloc` 运行时挂起、无输出、CPU 占用)。ELF 后端 arena 相关编码已全部 objdump 验证正确(`mov [r8],r10d` 的 44 89 00 错误已修复回 45 89 10),定位方向为 `emit_alloc_body` 生成的运行时逻辑(g_current_arena 检查 / bump 推进 / .Lretry 循环 / OOM 链式扩展)。详见 TODO.md 预存 bug 5。 - -**2026-08-13**:设计升级为图锚定区域(本文档)。本阶段**只改文档,不实现**;实现按 -M1(生命周期+碎片)→ M2(布局+放置)→ M3(跨区域)推进,见 -`docs/superpowers/specs/2026-08-13-graph-anchored-regions-design.md`。 - -**2026-08-15**:语义本体定稿为缓存语义(§一,七条条款)——实现早已是缓存语义(HDFG 节点=配方、边=值流、state edges),本文档此前将 CompCert v2 字节模型误作语义本体;本次仅文档纠偏,零代码改动。设计依据:`docs/superpowers/specs/2026-08-15-cache-semantics-design.md`。 - -## 待解决问题 - -### 区域级空间浪费 - -虽然区域内无碎片,但区域整体大小预分配可能导致: -- 小分配占用大区域 → 空间浪费 -- 动态区域大小调整策略 - -**设计已定(2026-08-13)**:动态路径走分档子区域(size class 表由图分析推导), -静态路径保持纯 bump。实现待 M1。 - -### 逃逸分析精度 - -当前设计依赖编译器的跨子图逃逸分析。以下情况的精度需要验证: -- 通过函数指针/接口调用的间接逃逸 -- 条件性逃逸(某些分支逃逸某些分支不逃逸) -- RawRef 与 unsafe 的交互边界 - -### 区域复用策略 - -区域返回池后,重置游标但不清除内存。敏感场景可能需要清零: -- 跨安全边界的进程隔离 -- 包含密钥或隐私数据的区域 -- 由部署配置控制,非默认行为 diff --git a/docs/pointer-model.md b/docs/pointer-model.md deleted file mode 100644 index 0480402f..00000000 --- a/docs/pointer-model.md +++ /dev/null @@ -1,291 +0,0 @@ -# Core 指针模型 - -## 问题 - -C 风格的裸指针表达能力极强,但完全没有安全保障。Rust 用 borrow checker 和生命周期标注来保障安全,代价是陡峭的学习曲线和表达力的损失。 - -Core 的目标:**指针和 C 一样自由,安全保障不依赖用户标注。** - -## 方案 - -Core 的编译器内部维护一张完整的HDFG(dataflow graph)。图中每个值的出生节点记录了它的来源(provenance),每个解引用操作记录在 DEREF 节点中。编译器通过分析图上的路径自动验证指针安全,不需要类型系统层面的 borrow 规则。 - -验证逻辑由三个编译期 pass 完成,全部是图上的稀疏分析: - -| Pass | 功能 | 输入 | 输出 | -|------|------|------|------| -| PointerAnalysis | 建 points-to 关系 | HDFG | pointer-induced flow 的边 | -| RegionCheck | 子图存活检查 | HDFG + 子图边界 | 每个 DEREF 的存活判定 | -| ProvenanceVerify | 越界检查 | HDFG + points-to | 每个 DEREF 的偏移判定 | - -## 术语 - -| 术语 | 含义 | -|------|------| -| provenance | 指针的来源——它来自哪个分配操作(ALLOC、ALLOC_STRUCT 等) | -| points-to | 指针可能指向哪些内存位置 | -| direct flow | 值通过赋值直接传递:`a = b` | -| pointer-induced flow | 值通过指针间接传递:`*p = x`,需要 points-to 才能追踪 | -| 子图 | HDFG上与一个执行上下文(函数、loop、flow)对应的连通子图 | -| DEREF 节点 | 表示一次指针解引用操作的图节点 | -| 图边界 | 编译器无法追踪 provenance 的入口点(外部地址、FFI 返回值等) | - -## 地址 = 映射(2026-08-15 语义定稿) - -**语义本体**:`&x` = (条目标识, 偏移)——条目由产生它的图节点定义(值 = 配方),偏移是条目内的位移(数组元素、结构体字段)。 - -**经典映射**:字节地址。编译器和后端把条目标识投影为经典机器上的基地址;在非经典范式(如量子存储)上投影为别的物理表示。 - -字节地址从来不是语义对象——它只是"缓存"(`docs/memory-model.md` §一)在经典硬件上的映射实例。本文件三个 pass(PointerAnalysis / RegionCheck / ProvenanceVerify)的判定全部在条目标识 + 偏移域上进行,与物理地址表示无关。 - -## 用户可见的语法 - -```core -p := &arr[0]; // 取地址 -p = p + n; // 偏移 -x := *p; // 解引用 -p = p - 1; // 往回偏移 -casted := cast(p); // 类型转换 - -unsafe { - mmio := 0x7fff0000 as *int; // 外部地址,编译器没有 provenance - *mmio = 42; -} -``` - -用户不需要学习 borrow lifetime、RawRef、Arena tag 等概念,不需要 `@ptrFromInt` 之类的内置函数。指针的操作和 C 一样自由。动态类型(`dyn`)作为类型标注时,指针的行为也由图自动管理。 - -## Pass 1: PointerAnalysis - -### 解决的问题 - -HDFG天然记录了 direct flow。`a = b`、`a = &x`、`a = a + n` 这些操作在图上有直接的边。但 pointer-induced flow(`*p = x`)需要通过 points-to 关系才能追踪:编译器必须先知道 `*p` 可能指向哪些分配块,才能建立 p→target 的边。 - -### 算法 - -PointerAnalysis 遍历HDFG,收集每个指针变量的 points-to 信息。分析是流敏感的,按图上的拓扑序进行。 - -``` -输入: HDFG -处理: - 1. 对每个 ADDR 节点 (p = &x): - record points-to(p) ∪= {x} - - 2. 对每个 COPY 节点 (q = p): - record points-to(q) ∪= points-to(p) - - 3. 对每个 ADD/SUB 节点 (p = p + n): - points-to(p) 不变(偏移不影响 points-to 集) - record offset(p) += n - - 4. 对每个 STORE 节点 (*p = x): - 对每个 t ∈ points-to(p): - record store(t, x) - 在图上添加一条从 x 到 t 的边(pointer-induced flow) - - 5. 对每个 LOAD 节点 (x = *p): - 对每个 t ∈ points-to(p): - record x ∈ points-to(t 的成员) - 在图上添加一条从 t 到 x 的边 - - 6. 对每个 CALL 节点 (f(args)): - 保守处理:假设函数可能修改任何通过参数可达的内存 - (内联后可精确化) - -输出: 补充了 pointer-induced flow 边的HDFG -``` - -### 复杂度 - -O(N × P),其中 N 为指针变量数,P 为 points-to 集平均大小。Core 的图是 flat array,可以按子图独立分析,不需要全程序统一求解。 - -## Pass 2: RegionCheck - -### 解决的问题 - -跨子图引用:子图 A 分配的内存被子图 B 引用。当子图 A 退出后,B 中的指针变成悬垂指针。 - -**2026-08-13 修订**:跨区域引用不再一刀切禁止——安全当且仅当**被引用区域的存活区间 ⊇ 引用的使用区间**(outlives 顺序判定,Cyclone 区域子类型的图形式)。RegionCheck 的 `cur_seq < exit_seq` 判定就是这个顺序判定。见 `docs/memory-model.md` 机制 #5。 - -### 算法 - -RegionCheck 为每个子图分配递增的序号(由控制流决定,不是运行时值)。每个 ALLOC 节点标记它所属的子图 ID。每个 DEREF 节点检查目标子图是否存活。 - -``` -输入: 带有子图边界标记的HDFG - -处理: - 为每个子图分配范围 [enter_seq, exit_seq] - - 对每个 DEREF 节点: - 1. 从 DEREF 沿指针来源倒推 ALLOC 节点 - 2. 获取 ALLOC 所在的子图 ID - 3. 获取该子图的 exit_seq - 4. 获取当前指令的序号 cur_seq - - if cur_seq < exit_seq: - → 目标子图存活,安全 - else: - → 目标子图已退出,编译错误 -``` - -### 子图确定 - -| 结构 | 子图范围 | -|------|---------| -| 函数 | [函数入口, 函数返回] | -| loop | [loop 开始, loop 退出] | -| for | [for 开始, for 结束] | -| flow/go | [创建, 回收] | - -子图的入口和出口节点在 IR 生成时已存在(df_begin_func / df_end_func 等)。RegionCheck 只是消费这些信息。 - -## Pass 3: ProvenanceVerify - -### 解决的问题 - -指针算术后的解引用是否越界。`p = &arr[0]; p = p + 100; *p`——编译器需要知道 `arr` 的长度是否大于 100。 - -### 算法 - -ProvenanceVerify 从每个 DEREF/STORE_PTR 节点出发,沿指针来源倒推,找到最初的 ALLOC 节点,比较偏移量与访问宽度。 - -``` -输入: 带有 points-to 信息的完整HDFG - -处理: - 对每个 DEREF/STORE_PTR 节点: - 1. 沿 pointer 的 def 链倒推到 ALLOC 节点 - (经过 COPY、ADD、SUB、LOAD 等中间节点) - - 2. 累计偏移量: - - ADD n: offset += n - - SUB n: offset -= n - - LOAD: 偏移重置(解引用一个指针,取其指向的成员的偏移) - - COPY: 偏移不变,沿来源继续 - - 3. 获取 ALLOC 节点的大小 alloc_size 和本次访问宽度 width - - if alloc_size 是编译期已知的: - if 0 <= offset && offset + width <= alloc_size: - → 安全(编译期证明,零运行时开销) - else: - → 编译错误 - else: - → 插入运行时边界检查(由后端 emit) -``` - -### 运行时边界检查 - -当 allocation 大小已知、但指针偏移由运行时索引决定时,编译器为 DEREF/STORE_PTR 编码 allocation base、size 和 access width: - -``` -offset = pointer - allocation_base -check offset >= 0 && offset + width <= alloc_size -fail → panic -``` - -后端将其编译为 `sub` + `cmp` + `jae` + `ud2` 序列。points-to 只有一个 allocation 目标时可生成精确基址检查;多目标且偏移无法静态证明时保守拒绝编译。 - -### ALLOC_AT:声明式放置(2026-08-13) - -`alloc_at(addr, size, align)` 是**声明式进图节点**:固定地址区域以(地址+大小+对齐)声明,与 ALLOC 同路径获得 provenance。声明之后全图追踪,ProvenanceVerify 照常验证边界+宽度: - -```core -mmio := alloc_at(0x7fff0000, 4096, align(4096)); // MMIO 页,声明式进图 -*mmio = 42; // 安全代码即可,边界内照常验证 -p := mmio + 4096; // 越界 → 编译错误或运行时 check -``` - -- **声明是唯一信任点**(等价于一次受控的图边界入口),之后编译器重新获得追踪权——与 `unsafe` 的"标注图边界入口"语义一致,但进图后是普通 ALLOC 的验证路径 -- 与 2026-08-10 定论(不扩展 0x 字面量直接指内部对象)**不冲突**:0x 字面量仍是 unsafe 外部入口;`alloc_at` 是声明式进图,用户必须给出地址+大小+对齐三个事实 - -## unsafe 边界 - -`unsafe` 是编译器无法追踪 provenance 时的唯一退路。发生在图边界: - -| 场景 | 原因 | -|------|------| -| 外部硬件地址 | 裸 `0x7fff0000 as *int` 没有 ALLOC 节点;声明式放置用 `alloc_at`(见上节,进图后不需要 unsafe) | -| FFI 返回值 | 外部函数返回的指针没有 Core 的 provenance | -| inline assembly | 汇编的输出指针没有来源 | - -### 类型双关 - -`*(dex*)&i` **不需要 unsafe**。图的存储语义是条目标识 + 偏移(`docs/memory-model.md` §一 条款 6);经典映射下表现为"字节序列 + 宽度 + 边界"——provenance -(alloc 归属)、offset(字节偏移)、alloc_size(字节大小)全部与类型无关,类型只是 -DEREF 处的"视图"。cast 以有类型的 `IR_LOAD` 保留值流,provenance 边不断: - -- 双关合法判据 = 边界 + 宽度:`offset >= 0` 且 `offset + width <= alloc_size` -- 越界双关由 DEREF/STORE_PTR 的统一边界检查拦截 -- 整数转指针产生 `asp=1`;该外部地址空间的解引必须在 `unsafe` 边界内 - -### 字节权限层(2026-08-13) - -经典映射在"字节序列 + 宽度 + 边界"之上补充**每字节权限**(CompCert v2 范式——经典机器的权限投影,见 `docs/memory-model.md` §一 条款 7): - -``` -Freeable > Writable > Readable > Nonempty > Empty -``` - -- Freeable:可比较、可读、可写、可释放;Writable:可比较、可读、可写、不可释放; - Readable:可比较、可读、不可写;Nonempty:仅可比较(指针有效性);Empty:无权限 -- 分配后默认 Freeable;`drop_perm` 可收窄(如 const 数据降为 Readable) -- 权限与 provenance/offset/size 一样是**图数据**——验证器消费图即获得每字节控制 - -`unsafe` 是外部地址空间的显式边界。整数转指针会保留 `asp=1`,因此该指针即使流出块外,后续解引仍需要另一个 `unsafe` 边界;需要在 safe 代码中持续验证的固定地址应通过 `alloc_at` 声明式进图。 - -```core -unsafe { - p := 0x7fff0000 as *int; // 入口,编译器接受用户保证 - *p = 42; -} -// 块外再次解引 p 仍需要 unsafe -``` - -## 与其它语言的对比 - -| | C | Rust | Zig | Core | -|--|---|------|-----|------| -| 指针算术 | 随便 | `*T` 不行 | `[*]T` 可以 | 裸指针随便 | -| 越界检查 | 无 | bounds check | bounds check | 编译期证明或运行时 check | -| 生命周期验证 | 无 | borrow checker | 编译时 | RegionCheck | -| 别名分析 | 无 | 独占&共享引用 | 编译时 | PointerAnalysis | -| 验证时机 | 无 | 类型检查 | 编译时 | 图 pass | -| 用户需标注 | 无 | lifetimes | 有时 | 无 | -| unsafe | 整个语言 | 关键字 | 关键字 | 关键字 | - -## 当前状态 - -Core 编译器已有HDFG(`src/compiler/dataflow.cr`)和线性扫描寄存器分配器(`src/compiler/opt.cr`)。 - -**更新(2026-07-28)**:三个 pass 已全部实现—— -`src/compiler/ptr_analysis.cr`(PointerAnalysis)、`src/compiler/region_check.cr`(RegionCheck)、 -`src/compiler/provenance_verify.cr`(ProvenanceVerify)。DEREF/STORE_PTR 后端发射 -allocation-base-relative 的 `sub+cmp+jae+ud2` 边界检查,配合 2026-07-28 的 Arena 内存模型 -(`src/stdlib/arena.cr`)。细节见 `TODO.md`。 - -**更新(2026-08-10)**:设计定论—— -1. 类型双关由图自动验证(见上"类型双关"节),不需要 unsafe -2. **不扩展"程序内部地址直接指"(0x 字面量指内部对象)**——YAGNI:内部对象用 `&` 取址 - 更优(无漂移、类型全、验证无条件);外部契约地址 unsafe 已够用。0x 字面量仅保留 - unsafe 外部入口角色(上表前三行),详见 TODO 预存 bug 7 - -**更新(2026-08-13)**:设计修订(图锚定区域内存模型,只改文档不实现)—— -1. **ALLOC_AT 声明式放置节点**进图(见上节):固定地址区域声明式获得 provenance, - 与 2026-08-10 定论不冲突(0x 字面量仍是 unsafe 入口) -2. **跨区域引用放宽**:从"禁止"改为 outlives 顺序判定(RegionCheck 图活性判定即该检查) -3. **字节权限层**:每字节 Freeable/Writable/Readable/Nonempty/Empty(见上节) - -**更新(2026-08-16)**:访问宽度、store 边界检查和 `asp` 外部地址约束已落地。 -动态偏移检查使用 points-to 定位的实际 allocation base,不再使用页内偏移近似。 - -设计依据:`docs/superpowers/specs/2026-08-13-graph-anchored-regions-design.md` + `docs/memory-model.md`。 - -**更新(2026-08-15)**:`&` 所指定稿——条目标识 + 偏移是语义,字节地址是经典投影(见上"地址 = 映射"节)。与三个 pass 无行为冲突(判定域本就是条目标识 + 偏移)。 - -## 参考 - -- **SVF** (SVF-tools): LLVM 上的值流图框架,自动检测 use-after-free、double-free、buffer overflow。Core 的HDFG是更统一的形式——同一张图同时做 regalloc、调度、验证。 -- **Fridtjof Siebert** (2006): 全程序 context-sensitive flow-sensitive 指针分析,静态检测区域内存中的悬垂指针。RegionCheck 的直接参考。 -- **Prov-GC** (Banerjee 2020): 动态 pointer provenance 追踪,实现 C 的声浪 GC。ProvenanceVerify 的 provenance 追踪概念来源。 diff --git a/docs/project-book.md b/docs/project-book.md index 1d0f3f99..931f19d0 100644 --- a/docs/project-book.md +++ b/docs/project-book.md @@ -98,7 +98,7 @@ Core 只有一种执行语义:HDFG(Holographic Dataflow Graph,全息数据 - loop、recv → 带反馈环的图——迭代语义,region 边界表达迭代与终止。 - flow、go、yield、select → 运行时动态图——节点创建与并发语义。 -图是执行语义的载体,**不是执行方式的决定者**:执行方式(串行、并行、调度策略)由部署配置决定——同一张 DAG,单核串行、多核并行、无 OS 抢占式时间片。图只约束语义(依赖、迭代、并发结构),不决定物理执行。同样,图也是存储语义的载体——值即条目(配方可重算),存储即缓存(范式无关),内存只是经典映射(见 `docs/memory-model.md`)。 +图是执行语义的载体,**不是执行方式的决定者**:执行方式(串行、并行、调度策略)由部署配置决定——同一张 DAG,单核串行、多核并行、无 OS 抢占式时间片。图只约束语义(依赖、迭代、并发结构),不决定物理执行。同样,图也是存储语义的载体——值即条目(配方可重算),存储即缓存(范式无关),内存只是经典映射(见 `docs/maintainer/design/memory-model.md`)。 **三层映射(2026-08-27 正式晋升)**:语义 → 图 → 格 → 编码——**图负责表达计算**(关系空间),**格负责承载计算**(内存模型 = 中间存在空间),**编码负责实现计算**(物理编码空间;2026-08-27 更名:原「二进制」硬编码经典惯例)。跨范式发生在图层,超图灵性属于图不属于格;格的价值 = 表达空间未被有限范式封顶——不同范式各找自己的格映射(见 `docs/memory-model-capability-lattice.md` §四;v4 定稿:规则封闭对象开放 / 无格承诺 / 能力不提升一等公民 / 寄存器分配 = 缓存语义映射实例,`docs/regalloc-cache-mapping.md`)。 @@ -203,9 +203,9 @@ I/O 等平台接口与部署目标属性同属物理层,遵循同一原则线 ### 4.6 指针安全 -Core 的指针安全完全建立在HDFG上,不引入 borrow checker、Arena tag、RawRef 等独立概念层。编译器从图中推导每个指针的来源(provenance)和偏移,在解引用点自动验证。详见 `docs/pointer-model.md`。 +Core 的指针安全完全建立在HDFG上,不引入 borrow checker、Arena tag、RawRef 等独立概念层。编译器从图中推导每个指针的来源(provenance)和偏移,在解引用点自动验证。详见 `docs/maintainer/design/pointer-model.md`。 -存储语义本体为**缓存语义**(值 = 配方、条目可驱逐可再生、图边界为唯一不可再生来源);字节内存是其在经典硬件上的映射实例——见 `docs/memory-model.md`。 +存储语义本体为**缓存语义**(值 = 配方、条目可驱逐可再生、图边界为唯一不可再生来源);字节内存是其在经典硬件上的映射实例——见 `docs/maintainer/design/memory-model.md`。 ### 4.7 形式化验证工具(外部系统) diff --git a/docs/pseudocode/compiler/ast.md b/docs/pseudocode/compiler/ast.md index 01bd2067..c8b03373 100644 --- a/docs/pseudocode/compiler/ast.md +++ b/docs/pseudocode/compiler/ast.md @@ -735,7 +735,7 @@ ## 错误码定义(Error codes) -> 格式:类别 * 1000 + 编号,与 `docs/error-codes.md` 对应。 +> 格式:类别 * 1000 + 编号,与 `docs/developer/errors.md` 对应。 > 0=未分类,1=语法解析(P),2=名称解析(N),3=类型推断(I),4=类型赋值(TA),5=类型函数(TF),6=类型二元运算(TB),7=类型一元运算(TU),8=类型控制流(TC),9=类型匹配(TM),10=类型数组切片(TK),11=类型结构体(TS),12=类型泛型(TG),13=借用(B),14=运行时(R),15=输入输出(E),16=内部编译器错误(ICE)。 ### 语法解析错误(P0xx) diff --git a/docs/regalloc-cache-mapping.md b/docs/regalloc-cache-mapping.md deleted file mode 100644 index 0f7f4f54..00000000 --- a/docs/regalloc-cache-mapping.md +++ /dev/null @@ -1,130 +0,0 @@ -# 寄存器分配:缓存语义映射实例 - -> 本文是寄存器分配在 Core 架构中的**正式参考文档**(与 `memory-model.md` / `pointer-model.md` 同级的架构文档)。 -> 设计决策记录(日期、排除方向、讨论过程):`docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`。 -> 关联备忘:`docs/memory-model-capability-lattice.md` v4 §11.5。 - -## 零、术语对照表 - -| 术语 | 含义 | -|---|---| -| 条目(entry) | IR 变量**版本**(缓存语义条款 5:每次赋值 = 新版本 = 新条目) | -| 配方(recipe) | 条目值的产生方式 = (产生它的图节点, 输入边)(条款 1) | -| 驱逐(eviction) | 丢弃条目的存储物、保留配方(条款 2) | -| 再生(regeneration) | 重跑配方节点产生可观测等价的值(条款 3) | -| home | 条目的持久位置(经典映射 = 栈槽) | -| 存在区间 | 条目在图层上的存活区间 = 图活性(RegionCheck 机制) | -| 共存 | 两个条目的存在区间相交(对称关系,无传递性) | -| 寄存器映射实例 | 寄存器文件 = 缓存层、home = 经典映射的映射实例(条款 7) | - -## 一、定位:分配是映射实例,不是编译器 pass - -寄存器分配与区域 / arena / 字节权限同族,是**格 → 编码的映射实例**(缓存语义条款 7): - -``` -图(HDFG) —— 关系空间:值流、配方、图活性 -格(存在空间) —— 条目存在:版本、共存、驱逐(缓存语义本体) -编码(物理) —— 映射实例:寄存器(缓存层)/ 栈槽(home)/ 字节 -``` - -推论: - -- **CFG 是格的特例**。线性扫描、图着色、SSA 弦图、SPL 语法分解都是**线性化退化实例**——不进入语义本体 -- **正确性 order-free**:不对某条线性化路径陈述,而对**全部可能执行**成立(§二) -- 范式映射表(`z-vision.md`)= 映射正确性定理表——寄存器映射实例是其中一行(经典行),与字节内存行同构:证明缓存语义 ⊇ 该实现 - -## 二、语义基座(律):驱逐不变量 = 语义保持定理 - -公理 = 缓存语义七条(`memory-model.md` §一)。其中: - -- **驱逐不变量(条款 2)**:⟦G ∖ storage(e)⟧ = ⟦G⟧——驱逐图内任意条目(丢弃存储物、保留配方)不改变可观测语义 -- 分配器把条目放寄存器还是栈、放多久,只是**放置/替换策略**——与缓存替换策略(LRU 等)同理,**算法零证明**。语义保持不依赖具体贪心 -- **无格承诺**(`memory-model-capability-lattice.md` v4 §4.6):判定/定理只在**关系层面**陈述——共存关系连预序公理都不需要(共存无传递性,是对称关系);对一切格成立 - -## 三、对象(物) - -| 对象 | 定义 | 来源 | -|---|---|---| -| 条目 | IR 变量版本(每次赋值 = 新版本) | 条款 5 版本化 | -| 配方 | 产生节点 + 输入边 | 条款 1 | -| 存在区间 | 图活性存活区间 | 图层导出(RegionCheck 机制,`pointer-model.md`) | -| 共存 | 存在区间相交 | 对称关系(无传递性、无路径结构) | -| home | 栈槽(经典映射) | 映射实例 | - -**无配方条目**(边界 + 图内不可重算,见 `memory-model-capability-lattice.md` v4 §5.3)一条通用规则:**必须有 home,驱逐必写回**——驱逐不变量/再生等价对其不成立(重跑神谕 ≠ 原结果)。对所有无配方条目一视同仁,不按范式枚举。 - -## 四、判定(验证):一致性四条 - -「寄存器映射实例合法」= 一致性判定,全部**可判定、局部、条目泛型**(只操作「位置 + 配方 + 存在」,不读值的内容): - -1. **共存互斥**:每时刻每寄存器至多一个条目的材料(共存 = 存在区间相交 → 同寄存器条目不共存) -2. **读点无陈旧**:读到的材料与条目的当前版本对齐(条款 5) -3. **驱逐配对**:寄存器回收(驱逐)时材料不丢——写回 home,或配方可再生(条款 3 = rematerialization);无配方条目必写回(§三) -4. **调用点失效契约**:调用点 = 缓存失效边界——caller-saved 失效(材料归 home)、callee-saved 保留(ABI = 缓存保留契约) - -**算法零证明**:正确性由判定保证,与分配算法选择无关(生成器 + 验证器分离;2025 年 VeriLocc 同构先例)。分配算法 = 上下文贪心(§五,定案)。 - -**落点**:判定写成 `.corespec` 契约(`spec-design.md` §2.7 体系:翻译桥 → CIC 内核,健全性由内核保证)+ 编译器内 checker 实现期自检,双层消费同一规约。 - -## 五、分配器(定案):上下文贪心(Context-Aware Greedy, CAG) - -**正式名:上下文贪心**——存在结构上的 First Fit 贪心扫描。上下文 = 共存关系 + 再生成本(配方)+ region 生命周期 + 调用点 + 使用次数。**定案(2026-08-27):上下文贪心为唯一分配算法**;判定(§四)不依赖算法选择。 - -算法规格: - -``` -对每个函数: -1. 条目表构建:变量 × 版本(IR_STORE 切分,条款 5)→ 存在区间 [start, end] - (图活性;region 嵌套序上结构化计算,非 CFG 数据流不动点) -2. 遍历序:region 嵌套序(存在结构拓扑序)——CFG 退化时 = 指令序 = 经典线性扫描 -3. 活跃集合:当前点存在区间覆盖的条目 = 共存集合 -4. 分配:对每个新活跃条目 e: - free = 寄存器全集 \ 活跃集合已占 - free 非空 → e 取最小空闲寄存器(First Fit) - 无 free → 驱逐决策:对候选驱逐者 e′ 比较 - cost = region 深度 × 使用次数 ×(写回 + 装载)vs 配方重算代价(remat) - 选便宜的:材料写回 home(spill)或直接重算(remat);e 占释放的寄存器 -5. 调用点:活跃的 caller-saved 条目 → 驱逐写回 home(缓存失效契约);callee-saved 保留 -6. 输出:条目 → 位置(寄存器/home)映射 + 驱逐/装载点表 → g_opt_meta / 格形态 -``` - -谱系:主干 = Poletto-Sarkar 线性扫描的**存在结构版本**;spill = 成本感知驱逐(second-chance bin-packing 变体 + remat 选项);调用点失效 = 解锁 caller-saved(x86-64 通用寄存器 16 个 + XMM;现实现仅用 5 个 callee-saved)。复杂度 ~O(V+E)(活跃集合维护 O(条目 × 寄存器数))——分配器本身不拖编译速度。 - -- 版本区间(IR_STORE 切分)——对应条款 5 版本化,比 first/last 全函数区间精确 -- spill = 驱逐写回 home;remat = 配方重算(成本 = 产生节点重算代价) -- **CFG 退化情形 = 区间序 First Fit = 最优**(Dilworth:最小寄存器数 = 共存偏序的 width;区间图 = 完美图,χ = ω)——数学闭合 -- 非区间序(有分支)时 = 贪心近似(最优性本质 NP-hard),质量靠上下文信息拉高,验证不依赖它 - -## 六、数学支撑 - -| 结果 | 内容 | 位置 | -|---|---|---| -| Dilworth 定理 / 区间序 | 链分解 = 分配;width = 最小寄存器数;区间序 First Fit 贪心 = 最优 | §五(CFG 退化情形的精确性) | -| PCM / 资源代数(Verus/Iris) | 寄存器 = 可分离资源(每寄存器一位置的 `Resource

`,alloc/join/split)——验证语言与能力+格备忘 §九 代数同构 | 验证的可选代数层(非必需,§四 判定已足够) | -| SPL 语法分解(Cai & Goharshady 2024–2026) | series-parallel 控制流图上带 spill 的最小代价分配多项式精确(O(\|G\|·\|D\|⁶)) | 退化实例自身的最优解参考——证实「CFG 系算法是格分配的线性化特例」;其 DP 表可作最优性证书的形态参考 | - -## 七、与现有实现的映射 - -| 现有 | 对应 | -|---|---| -| `src/compiler/opt.cr` alloc_registers | 朴素线性扫描(first/last 区间、5 callee-saved、无 spill)——升级起点 | -| `g_opt_meta` | 寄存器映射(var_idx, reg) 对——判定的检查对象 | -| `pass_stack_share` | home 槽共享(共存互斥的不同条目可共享 home,条件:不同时存在) | -| RegionCheck(`pointer-model.md`) | 存在区间来源(cur_seq/exit_seq)——共存关系落点 | -| `src/arch/linux/ld/instr.cr` g2_slot | 物理位置懒查询点——判定挂接处 | - -## 八、范围边界(YAGNI) - -- ❌ 不做 SSA 转换(版本化语义已在,§三 条目 = 版本) -- ❌ 不做图着色机制、不做 SPL 分解实现(CFG 系,方向已弃) -- ❌ 不做格代数承诺(§二:关系层面陈述;格 = 映射参数,将来挂接) -- ✅ 做:共存关系落定 → 判定规约 → 贪心放置实现 → checker 自检 - -## 九、关联 - -- `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md` — 设计决策记录(日期、排除方向、落地顺序) -- `docs/memory-model-capability-lattice.md` v4 §11.5 — 备忘摘要与定位;v4 §4.5/§4.6(规则封闭对象开放、无格承诺);v4 §5.3(图内不可重算条款) -- `docs/memory-model.md` §一 — 缓存语义七条(待并入「图内不可重算」条款) -- `docs/dataflow-design.md` §8 — 执行标注空间(无配方条目的来源) -- `docs/pointer-model.md` — RegionCheck:存在区间的现有实现 -- `docs/spec-design.md` — .corespec 规约体系 diff --git a/docs/verifier/kernel-spec.md b/docs/verifier/kernel-spec.md index ddb3de1a..3b9586c5 100644 --- a/docs/verifier/kernel-spec.md +++ b/docs/verifier/kernel-spec.md @@ -1,5 +1,7 @@ # kernel-spec.md — McTT → Core 内核移植契约 +> 定位:受众 = 维护者(内核移植 Task 3/6 实现者);状态 = active(移植契约,对应 McTT 快照);真源 = ~/mctt 源文件(本文件为行号对照转换层)。 + > 本文件由 Task 2 生成,行号对应 McTT icfp25 分支。 > 源仓库:`~/mctt`(只读参考,非 git 拷贝)。所有行号均为对应源文件的实际行号, > 格式为「文件基名:行号」;文件全路径见下表。 diff --git a/docs/z-vision.md b/docs/z-vision.md index ddf4e79d..3bcbd567 100644 --- a/docs/z-vision.md +++ b/docs/z-vision.md @@ -1,4 +1,6 @@ -# Core 愿景(Vision) +# Core 愿景(Vision) + +> 定位:受众 = 所有读者(开发者/维护者/学术);状态 = active;真源 = 本文件为愿景陈述,技术细节以各 design 文档为准。 > 本文件是 Core 的长期目标陈述——"为什么存在"。技术细节见各设计文档;进度见 TODO.md。 @@ -10,9 +12,9 @@ **Core 的路径**:语义保鲜——IR(HDFG)保留全部语义信息,与执行硬件解耦。范式迁移时**语义不动,只换后端映射**;验证器、优化器、未来后端都从同一张图读取完整语义。 -**状态**:语义保鲜已实现(HDFG + region/state edge/provenance);范式映射表——**存储半边已定稿**(语义本体 = 缓存语义,字节内存 = 经典映射实例,见 `docs/memory-model.md` §一),执行半边设计态(每范式一张表)。 +**状态**:语义保鲜已实现(HDFG + region/state edge/provenance);范式映射表——**存储半边已定稿**(语义本体 = 缓存语义,字节内存 = 经典映射实例,见 `docs/maintainer/design/memory-model.md` §一),执行半边设计态(每范式一张表)。 -**三层映射链(2026-08-27 正式晋升)**:范式 → 图 → 格 → 编码——**图 = 关系空间**(跨范式统一中轴:非因果、模糊分支、超图灵标注,见 `docs/dataflow-design.md` §8),**格 = 状态/存储空间**(内存模型 = 中间存在空间,见 `docs/memory-model.md` 与 `docs/memory-model-capability-lattice.md` §四),**编码 = 物理编码空间**(把格编码到具体实现;2026-08-27 更名:原「二进制」硬编码经典惯例,违反零硬件惯例——量子/模拟/光学皆非二进制)。跨范式发生在图层;**超图灵性属于图,不属于格**;格的价值 = 表达空间未被有限范式封顶——不同范式各找自己的格映射,格本身不需要知道范式是什么。v4 定稿原则(`docs/memory-model-capability-lattice.md`):**规则封闭对象开放**(层规则零签名)、**无格承诺**(格代数 = 映射参数)、**能力不提升一等公民**(语义还原图上,授权归治理层)、**寄存器分配 = 缓存语义映射实例**(`docs/regalloc-cache-mapping.md`)。 +**三层映射链(2026-08-27 正式晋升)**:范式 → 图 → 格 → 编码——**图 = 关系空间**(跨范式统一中轴:非因果、模糊分支、超图灵标注,见 `docs/maintainer/design/dataflow-design.md` §8),**格 = 状态/存储空间**(内存模型 = 中间存在空间,见 `docs/maintainer/design/memory-model.md` 与 `docs/archive/memory-model-capability-lattice.md` §四),**编码 = 物理编码空间**(把格编码到具体实现;2026-08-27 更名:原「二进制」硬编码经典惯例,违反零硬件惯例——量子/模拟/光学皆非二进制)。跨范式发生在图层;**超图灵性属于图,不属于格**;格的价值 = 表达空间未被有限范式封顶——不同范式各找自己的格映射,格本身不需要知道范式是什么。v4 定稿原则(`docs/archive/memory-model-capability-lattice.md`):**规则封闭对象开放**(层规则零签名)、**无格承诺**(格代数 = 映射参数)、**能力不提升一等公民**(语义还原图上,授权归治理层)、**寄存器分配 = 缓存语义映射实例**(`docs/maintainer/design/regalloc-cache-mapping.md`)。 ## 目标二:形式化验证门槛降到普通程序员可用 @@ -20,13 +22,13 @@ **Core 的路径**:规约用 Core 语言本身书写(`#check`/`#ensure`/`spec fn`,不需要学数理逻辑),编译器经翻译桥自动编译进 CIC 项,健全性由 CIC 内核 + SMT 证书保证。用户写意图,编译器管证明。 -**状态**:设计完成(spec-design v2),**0 实现**——语法/EBNF 已定,.corespec 编译、.csr、翻译桥、内核绑定待推进。 +**状态**:设计完成(spec-design v2),**0 实现**——requires/ensures 语法已入 .cr(独立 .corespec 格式 2026-09 退役,见 maintainer/adr/adr-0001),.csr、翻译桥、内核绑定待推进。 ## 目标三:一门语言覆盖从零基础到系统编程的完整路径 **现状问题**:教学语言与生产语言是两套(学了要重学);系统编程语言(C/Rust)学习曲线陡峭,与教学语言脱节。 -**Core 的路径**:渐进式学习路径——同一门语言从基础(DAG)到系统编程(字节级 + 并发 + 规约),概念只增不减,无模式切换(见 `docs/learning-path.md`)。 +**Core 的路径**:渐进式学习路径——同一门语言从基础(DAG)到系统编程(字节级 + 并发 + 规约),概念只增不减,无模式切换(见 `docs/developer/tutorial.md`)。 **状态**:语言本体已实现(自举成功、ELF 后端、apx 后端快路径(binary64)、并发、指针三 pass)。 @@ -50,7 +52,7 @@ ## 关联 - 哲学与技术总纲:`docs/project-book.md` -- 执行模型:`docs/execution-model.md` -- 验证体系:`docs/spec-design.md` -- 学习路径:`docs/learning-path.md` +- 执行模型:`docs/maintainer/design/execution-model.md` +- 验证体系:`docs/maintainer/design/spec-design.md` +- 学习路径:`docs/developer/tutorial.md` - 进度:`TODO.md` diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index 4acad224..1146b812 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -294,7 +294,7 @@ TYP_OPTIONAL : int = 12; // data = inner type idx(T? = T ∪ null) // AK_NULL 原子(原生第九员);行不带参数(None 无载荷)。与 AK_UNIT/AK_NEVER 均不相交。 TYP_NULL : int = 13; // 无字段(data/extra 恒 0) -// Error codes: category * 1000 + number, matching docs/error-codes.md +// Error codes: category * 1000 + number, matching docs/developer/errors.md // Category 0 = unclassified (000-) // Category 1 = P (Parser) // Category 2 = N (Name resolution) diff --git a/src/compiler/ptr_analysis.cr b/src/compiler/ptr_analysis.cr index 21529b51..6af6fd73 100644 --- a/src/compiler/ptr_analysis.cr +++ b/src/compiler/ptr_analysis.cr @@ -232,7 +232,7 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int) { // offset 由索引 s2 决定:常量索引可精确计算(idx*8), // 运行时索引 → 未知(-1),迫使 provenance_verify 生成运行时检查。 // 修复前:无条件传播 s1 的 offset(数组=0),运行时越界被误判为 - // 编译期安全 → 越界裸读(见 docs/compcert-reference.md 审查记录)。 + // 编译期安全 → 越界裸读(见 docs/archive/compcert-reference.md 审查记录)。 base_off := r64(g_offsets, s1 * 8); idx_val : int = -1; if s2 >= 0 { From 24a2f4160a3aed84e6244b2adf716f253af35a22 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Tue, 15 Sep 2026 23:38:22 +0800 Subject: [PATCH 011/138] =?UTF-8?q?docs:=20=E5=90=88=E5=85=A5=20feature/co?= =?UTF-8?q?mpile-time-linearity=20=E5=87=80=E5=86=85=E5=AE=B9=E2=80=94?= =?UTF-8?q?=E2=80=94=E7=BC=96=E8=AF=91=E6=97=B6=E9=97=B4=E7=BA=BF=E6=80=A7?= =?UTF-8?q?=E5=8C=96=E8=AE=A1=E5=88=92=20+=20=E6=B6=88=E6=81=AF=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=E6=8B=B7=E8=B4=9D=E6=B6=88=E9=99=A4=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=20(#65)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 取净内容而非按提交链合入,理由(实测): - 该分支的**语法文档重写**(9 个提交)已被 develop 侧超越 —— develop 的 docs/developer/syntax.md 是同一份重写的后续修订(含 += / => / 变参记号、切片类型注记、#check/#ensure 规约退役 .corespec 等, 由文档重组批补齐),且已按「语言本体 / 不标实现状态」把设计理念移到 concepts.md ⇒ rebase 后 8 个提交在 docs/language-syntax.md 上带同一处「移动 vs 修改」冲突,逐个解无意义 - 其净贡献 = 本提交的 2 个文档(其余 8 个提交的内容均已在 develop 中) 内容: - plans/2026-09-04-compile-time-linearity.md —— 编译时间线性化计划:每 pass 一条上界定理(数学保证而非测量) + 计数器证书(--emit-ops-cert)+ scaling 回归守门;含只读审计出的真嫌疑(checker find_sym/find_gsym 全符号表回扫、 调用点 find_func 全表扫 = 确定超线性) - specs/2026-09-04-message-copy-elision-design.md —— 消息边界拷贝消除设计 --- .../2026-09-04-compile-time-linearity.md | 163 ++++++++++++++++++ .../2026-09-04-message-copy-elision-design.md | 146 ++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-04-compile-time-linearity.md create mode 100644 docs/superpowers/specs/2026-09-04-message-copy-elision-design.md diff --git a/docs/superpowers/plans/2026-09-04-compile-time-linearity.md b/docs/superpowers/plans/2026-09-04-compile-time-linearity.md new file mode 100644 index 00000000..286004c4 --- /dev/null +++ b/docs/superpowers/plans/2026-09-04-compile-time-linearity.md @@ -0,0 +1,163 @@ +# 编译时间线性化计划 + +日期:2026-09-04 +状态:计划(待执行) +范围:corec + corearch 自举主链(`src/compiler/` + `src/arch/linux/ld/`);时间、内存、IO 一并计入 +目标(用户定稿):**编译时间不出现超线性;且这个保证是数学上界,不是测量结果** + +## 1. 目标与判据 + +### 1.1 排除性保证(主判据) + +每 pass 一条**上界定理**:输入规模 N(按 pass 定义:token 数 / AST 节点数 / IR 指令数 / .ccr 字节数)下,耗时 ≤ C·N(C 为 pass 相关常数,hash 表按期望 O(1) 时定理中注明「期望」,翻倍 buffer 写总量界)。**定理成立 ⟹ 该 pass 结构上不可能显现超线性**——这是数学原因,不是测量结果。 + +定理清单 = 本文 §4;每改一个 pass 必须保持其定理成立(code review 对照项)。 + +### 1.2 机器证书(次级判据) + +计数器插桩(`--emit-ops-cert` 模式):每 pass 统计原语操作数(迭代次数、str_eq 次数、map 操作数、buffer 搬移字节),输出 `(pass, N, ops ≤ C_pass·N)` 证书。**每次真实编译携带线性证书**——定理人证,常数机器逐次验。普通构建零开销。 + +### 1.3 scaling 回归(守门判据) + +代表性合成样本 ×1/×2/×4,断言时间比 ≤ 阈值(×2 ≤ 2.4、×4 ≤ 6.0,余量含噪声与缓存效应),同机中位数三次。不证明什么,只把「定理与代码脱节」的发现时间压到分钟级。进 CI。 + +### 1.4 非目标 + +- 不做常数级优化(上界存在即可,C 大小不承诺); +- 不做并行编译、不做增量缓存(既有独立计划:`docs/superpowers/specs/2026-07-29-incremental-cache-design.md`、`docs/superpowers/plans/2026-08-15-cache-semantics.md`); +- 不做 Python bootstrap 审计(用户范围裁定排除); +- 不做 Coq 全机械化证明(远期选项,不阻塞本计划——上界定理人证 + 结构论证先行,计数器证书是机器检验的中间形态)。 + +## 2. 审计结论(2026-09-04,只读审计) + +已确认线性、无需动的底子:`dyn_arr.cr` grow 全部倍增(append 平摊 O(1));`g_str_hash` 倍增 + 全量 rehash(平摊线性);`ccr_io.cr` 写侧先算总长、单缓冲、一次 syscall(线性);`resolve.cr res_labels` 是死代码不构成热点。 + +### 2.1 前端核心(src/compiler/) + +| 位置 | 嫌疑 | 触发 | 判定 | +|---|---|---|---| +| checker.cr:215 `find_sym`、:225 `find_gsym` | 每标识符引用全符号表回扫 O(U×S) | 大文件/大函数 | 确定 | +| checker.cr:1502 mod 调用、:1563 方法调用、:845 `find_func` | 每调用点全表扫 O(C×M) | import/impl 方法多 | 确定 | +| checker.cr:265-346 borrow 表 | 每次 use 全表扫 + pop 数组左移 O(U×B) | `&` 借用多的函数 | 确定 | +| ir_gen.cr:672-684 EXPR_IDENT | find_local→global_let→ir_global 逐级全表回扫 | 大函数、全局引用多 | 确定 | +| ir_gen.cr:283 `track_str` | 每字符串字面量全表查重 O(L×S) | 字符串多 | 确定 | +| ir_gen.cr:29-50 `slice_len_*` | 每个赋值两次全表扫 O(A×S_slice),表全局不清 | 有切片字面量的函数 | 确定 | +| ir_gen.cr:1147-1516 调用发射 | 每调用点 ~6 次 `find_func` 全扫 O(C×F×6) | 调用点多的程序 | 确定 | +| dataflow.cr:160 `df_connect_state` | 每 IR_CALL 一次 `find_func` | 同上 | 确定 | +| ptr_analysis.cr:15 / region_check.cr:13 / provenance_verify.cr:36 `*_is_in_unsafe` | **每 DF 节点扫全 g_sgs** O(N×SG)(ptr 版乘固定点轮数,最多 10 轮) | 任何程序,无条件(总是运行的安全 pass) | 确定 | +| provenance_verify.cr:18 `get_alloc_size` | ALLOC_STRUCT 名称→struct 线性扫 O(D×M_struct) | 结构体分配多 | 确定 | +| opt.cr:258 `alloc_registers` | 每指令扫全函数变量 O(I×V) | -O≥2 大函数 | 确定 | +| opt.cr:367 `pass_stack_share` | 双重循环 O(V²) | -O≥2 | 确定 | +| opt.cr:417 `pass_cse` | seen 线性查 + replace_map 每指令全扫 O(P²) | -O≥1 | 确定 | +| module.cr:442-605 `res_imports` | 每轮全量重扫 token + 重 tokenize 累积 g_source O(pass×T + K²);:563 逐文件字符串拼接 O(K²) 复制 | 深 import 链 K 层 | 确定(残留) | +| module.cr:177 `reg_fileid` | 每文件注册全表查重 O(K²) | K 个 import | 疑似 | +| monomorph.cr:354 + 172 | 泛型实例缓存线性扫 + clone 逐节点查 dedup 表 | 泛型实例化多 | 确定 | +| lexer.cr:403 | 字符串字面量逐字节 concat O(L²) | 超长字面量 | 确定 | +| diag.cr:7 `read_source_line` | 每条诊断从文件头数行 O(诊断数×源长) | 大量错误(失败路径) | 确定 | +| dump.cr:279 | 每函数扫全 g_sgs O(F×SG) | dump 命令 | 确定 | +| interp.cr:343/410 | IR_JUMP 扫 loop 区域、IR_CALL 全扫函数表 | run/eval 模式 | 确定(次要) | + +### 2.2 ELF 后端(src/arch/linux/ld/,无条件路径) + +| 位置 | 嫌疑 | 触发 | 判定 | +|---|---|---|---| +| elf.cr:1475 前向 call 补丁 | 每 call 全扫 name_idx(str_eq)+ func_offsets O(C×F) | 任何构建 | 确定 | +| instr.cr:802 IR_CALL 发射 | 每 call 全表 str_eq 找偏移 O(C×F) | 任何构建 | 确定 | +| elf.cr:1200 Phase 3 | 每发射一函数回扫全部更新偏移 O(F²) str_eq | 函数多 | 确定 | +| instr.cr:67 `g2_str_off` + elf.cr:1024 | 从 0 累积重算偏移 O(S²×len) | 字符串常量多 | 确定 | +| instr.cr:26 `get_reg_for_var` | 每操作数扫全 g_opt_meta O(I×R_total) | --opt-level ≥ 1 | 确定 | +| instr.cr:1260 IR_LABEL | 每 label 扫全部 pending 前向跳转 | 大函数多前向分支 | 疑似 | +| ld.cr:328 `patch_relocs`、:448 `so_find` | 每重定位扫 PLT 表、每符号重扫 symtab | 动态/静态链接 | 确定/疑似(表小) | + +### 2.3 最可能先踩雷(修复顺序依据) + +1. `find_sym`/`find_gsym` 名称解析全表回扫——贯穿所有程序,大文件最先放大; +2. 三个总是运行的安全 pass 的 per-DF-node 全 g_sgs unsafe 扫描——输入越大越接近平方; +3. ELF 后端 name-based 补丁链(call 补丁 ×函数表、Phase 3 每函数回扫)——自举编译无条件触发。 + +## 3. 修复顺序(五批,每批自带上界定理更新) + +护栏先行(§5 harness + 基线测量)→ 逐批修复 → 每批过证书断言 + scaling 比。 + +### Phase A:名称解析 id 化 + 哈希(雷区 1) + +- checker:`find_sym`/`find_gsym`/`find_func`/方法表/mod 函数表 → 编译期一次建 hash(名→序号)或 AST 期符号编号;borrow 表改栈式按作用域分区 + pop O(1)(借用按作用域成批弹出); +- ir_gen:`find_local` 按函数作用域链缓存;`track_str`/`slice_len_*` → hash;调用发射 6 次 find_func → 每函数一次解析结果缓存; +- monomorph:实例缓存 hash;clone dedup 表 hash; +- ELF:函数名 → id 直寻(call 补丁/发射/Phase 3 全链改 id 表 + 一次 name→id 建表);`g2_str_off` 改一次 pass 建偏移表。 +- 定理:以上全部 ≤ C·N。 + +### Phase B:unsafe 判定 O(1)/节点(雷区 2) + +- ptr_analysis / region_check / provenance_verify 共享:unsafe 嵌套信息已存于 DFNode→region 显式映射,编译期一次线性预计算「节点是否在 unsafe 内」标志,三个 pass 改为 O(1) 查标志; +- provenance `get_alloc_size`:struct 名→序号 hash。 +- 定理:每安全 pass ≤ C·N(指针分析含固定点轮数常量)。 + +### Phase C:opt 家族 + +- `alloc_registers`:按活性区间事件排序扫描;`pass_stack_share`:排序/按槽分组;`pass_cse`:seen 哈希化(opcode+操作数键)+ replace_map 版本化索引。 +- 定理:O1/O2 各 pass ≤ C·N(pass_cse 的 seen 桶总量 ≤ C·N 需在定理中论证哈希键分布或退化为每桶常数级比对)。 + +### Phase D:module / import + +- `res_imports`:单轮收集 + 每文件独立 tokenize + 已加载 hash set;累积 g_source 用预分配缓冲(总量线性);链式 import 显式队列一次通过;`reg_fileid` hash。 +- 定理:import 解析 ≤ C·T_total(T = 全部文件 token 总量)。 + +### Phase E:杂项收尾 + +- lexer 长字符串字面量:char buffer 倍增或预分配目标长度; +- diag `read_source_line`:行起始偏移索引表(错误路径一次建表); +- dump:一次全表分区替代每函数全扫;interp 次要热点顺手修。 +- 定理:各 ≤ C·N。 + +## 4. 上界定理清单(承诺升级版——code review 对照) + +形式:每行一条定理。N 按 pass 定义;「期望」= hash 表随机种子假设;总量界 = 平摊严格线性。 + +| Pass | N | 定理 | 关键论证点 | +|---|---|---|---| +| lexer | 源字符数 | ≤ C·N | 每字符常数工作(含长字面量修复后) | +| parser | token 数 | ≤ C·N | 无回溯全扫 | +| checker(全部解析函数) | token/符号数 | ≤ C·N | Phase A 哈希 + borrow 分区 | +| ir_gen | AST 节点数 | ≤ C·N | Phase A 缓存 | +| dataflow(HDFG 构建) | IR 指令数 | ≤ C·N | 每指令常数边 | +| ptr/region/provenance | DF 节点数 | ≤ C·N | Phase B 标志(指针分析 ≤ C·轮数·N,轮数常数) | +| monomorph | 泛型调用 × 体节点 | ≤ C·N_total | Phase A 哈希 | +| opt(CSE/栈共享/分配) | IR 指令数 | ≤ C·N | Phase C | +| module/import | 全部文件 token 总量 | ≤ C·T | Phase D | +| ccr_io(写/读) | .ccr 字节数 | ≤ C·B | 已有单缓冲线性(维持定理) | +| ELF 发射 | IR 指令数 + 函数数 | ≤ C·N | Phase A id 直寻 | +| 内存 | 输入规模 | ≤ C·N | 翻倍总量界,禁止字符串逐字节 concat 类回归 | + +## 5. 护栏落地 + +### 5.1 scaling 测试(tests/complexity/) + +- 样本生成器(脚本合成 .cr):大函数(N 语句 × 1/2/4)、多 import(K 文件 ×1/2/4)、大结构体、深泛型实例化、长字符串字面量、链式 import、ELF 发射(大 .ccr ×1/2/4); +- 命令:`./build/corec build` 全链 + `./build/corearch` 各跑一遍;同机三次取中位数; +- 断言:×2 ≤ 2.4、×4 ≤ 6.0(首轮基线校准后可收紧); +- 长任务遵守铁律 6:`cpulimit -l 10` 或 `nice -n 19`。 + +### 5.2 证书模式(--emit-ops-cert) + +- 插桩点:pass 级迭代计数、str_eq 次数、map 操作数、buffer 搬移字节; +- 输出 JSON:`{pass, N, ops, C_pass}`;检查脚本 `check_cert.py` 断言 ops ≤ C_pass·N(C 常量表随修复更新); +- 普通构建零开销(flag 关闭)。 + +### 5.3 承诺清单的强制 + +- 本文 §4 表 = code review checklist:改 pass 必须保持定理成立,破坏定理的改动需先改定理并说明; +- scaling 测试进 CI(develop 门槛已有 CI,直接并入)。 + +## 6. 验收 + +1. §4 全部定理成立(人证 + code review); +2. `--emit-ops-cert` 在代表性样本上全绿; +3. scaling 回归全绿(修复前基线先测量留档,修复后 ×4 时间比 ≤ 6.0); +4. 内存无超线性(样本峰值 RSS ×2/×4 比 ≤ 2.4/6.0)。 + +## 7. 关联 + +- 上游上下文:`docs/project-book.md` §二.1(编译时间膨胀);历史已修:res_imports O(n²)(2026-06-30 会话); +- 不并入:incremental-cache(07-29 spec / 08-15 plan)、lazy/概率 pass 的既有计划; +- 审计由只读子代理完成(2026-09-04),行号以当时 HEAD 为准,修复前逐条复核。 diff --git a/docs/superpowers/specs/2026-09-04-message-copy-elision-design.md b/docs/superpowers/specs/2026-09-04-message-copy-elision-design.md new file mode 100644 index 00000000..76969217 --- /dev/null +++ b/docs/superpowers/specs/2026-09-04-message-copy-elision-design.md @@ -0,0 +1,146 @@ +# 消息边界拷贝消除 + +日期:2026-09-04 +状态:设计定稿(待实现) +关联:`docs/concurrency.md`、`docs/superpowers/specs/2026-07-30-concurrency-design.md`(本文修订其「值拷贝语义」条款的表述,可观察语义不变)、`docs/memory-model.md`(推论 A/B、消息区域、字节权限层)、`docs/pointer-model.md`(地址 = 映射)、`docs/memory-model-capability-lattice.md`(v4:规则封闭对象开放、无格承诺、M1 身份还原) + +## 背景与问题 + +并发模型:goroutine 各自独立 arena,无共享可变内存,channel 边界传值。`2026-07-30` spec 将 channel 边界写为「永远拷贝值」——跨边界数据在接收端区域重建。这带来两个难点: + +1. **成本**:消息是图结构(含指针),拷贝需要逐对象复制 + 指针重定位,且无法覆盖环与共享子结构的高效复制; +2. **表达力**:无配方条目(§5.3:句柄、外部资源、神谕结果等不可重算结构)无法通过重建材料跨边界。 + +本文目标:**在并发模型不变的前提下消除这两个难点**——不引入共享可变内存、不引入用户标注、语义保鲜线不动。 + +## 设计原则 + +1. **两层纪律**:语义本体只陈述于图的关系层面(条目/配方/共存/可能关系),经典惯例(arena、地址、memcpy、锁)一律作为映射实例参数存在——范式无关,与 capability-lattice v4 的「规则封闭、对象开放 / 无格承诺」一致。 +2. **语义本体不动**:边界公理(下)是验证器唯一依据;本文不修订它,只修订「物理拷贝是唯一实现」这一条款的表述。 +3. **标签永不失语义**:三个边界标签的义务不成立时静默落回 COPY——COPY 永可行。分析不精确只亏性能,永不产生假编译错误。 + +## 边界公理(语义本体,验证器唯一依据) + +1. **接收侧**:接收条目与发送侧条目配方同源,是可独立读写的等价条目; +2. **发送侧**:发送者的条目在发送之后继续有效,任何后续读观察到发送时刻的状态。 + +> 修订说明:`2026-07-30` spec 的「channel 边界永远拷贝值」是实现条款;边界公理才是语义条款。本文以标签化实现替代「一律拷贝」,可观察语义不变。 + +## 语义层:边界标签与关系句义务 + +### 边界 + +执行边界 = 需要跨执行体传递条目的位置:channel send/recv、go 参数、yield 输出、flow 返回值。 + +### 标签(子区域粒度) + +判定粒度 = **消息子区域**,不是整消息:闭包内每个材料子区域独立判定,一次边界携带一张**子区域标签表**——每子区域一个标签 ∈ {COPY, MOVE, SHARE},另附身份重指表(见「无配方条目」)。整消息单标签只是全部子区域同标签的简并情形;子区域间义务互不牵连——大载荷子区域可 MOVE 而头结构子区域可 COPY,物理上只重建必须重建的部分。 + +| 标签 | 义务(关系句,对该子区域的条目闭包) | 发送侧仍可 | 接收侧可 | +|---|---|---|---| +| **MOVE** | 发送点之后发送侧与消息条目闭包无「可能读」 | —(无读) | 读 + 写(唯一持有) | +| **SHARE** | 发送侧此后无「可能写」∧ 接收侧无「可能写」 | 读 | 只读 | +| **COPY** | 无(义务即公理本身) | 一切照旧 | 一切照旧 | + +判定顺序:先 MOVE(读集为空),再 SHARE(双侧写集为空;若连读都没有,MOVE 严格更优),否则 COPY。 + +「此后」陈述于图的序关系(发送节点在图上的后继可达域),不依赖全局时间——非经典范式无全局序时,标注随图结构走,由该范式自行映射。 + +### 归属与寿命 + +- **COPY** = 双 home:双方各执材料,配方同源(再生等价); +- **MOVE** = home 迁移:材料随条目身份走; +- **SHARE** = home 常驻出生侧:接收侧只读可能关系指向同一材料;材料寿命 = 两侧共存区间上限(图活性,非 LIFO——memory-model 推论 A)。 + +### 无配方条目(身份重指) + +无配方条目(不可重算,必须有 home、驱逐必写回,§5.3)**永不重建材料**——重建材料 = 重跑产生源 = 语义漂移。跨边界只传指认: + +- 身份 = 图节点身份(M1 定论,零新概念); +- 跨边界 = 可能关系重指:访问可能关系从发送侧条目转移到接收侧,或复制只读指认; +- 授权(consult/assume/measure 等)归治理层,是可能关系标注,非语义本体; +- 复合消息 = 材料部分按 COPY/MOVE/SHARE 判定 × 无配方成员一律进身份重指表; +- 重指表是编译期静态内容,不落运行期; +- 三个标签对身份条目无差别(COPY 也不复制它)——身份成员不参与 COPY/MOVE/SHARE 判定。 + +channel 本身是执行体间唯一特许共享对象(运行体通过它通信),其内部同步归属自身,不因本文新增。 + +### 语义保持定理 + +义务成立 ⟹ 标签实现与 COPY 观察等价。错误形态唯一:标签声称 MOVE/SHARE 而义务关系句不成立 = 分析不健全 = 编译器 bug,不是用户错误。 + +## 经典映射实例(经典行) + +本章全部内容都是映射参数;其他范式行消费同样的标签与义务,映射到各自机制。 + +### 1. 判定实现 + +**结构约束(confinement by construction)**:逃逸到执行边界的分配,在 IR 区域指派阶段放入**消息子区域**(独立子区域、分档 bump、闭包自含、唯一外部入口——哨兵/句柄结构)。约束是结构性的,不需要后验证明;消息混入普通区域分配时自动落 COPY。 + +**义务推导**:可能读/写关系 = HDFG 上别名分析(PointerAnalysis)+ 发送点前向切片 + RegionCheck 序关系的保守近似。过度近似 → 更保守标签 → 只亏性能。 + +**代价**:全部编译期(区域指派一遍数据流分析 + 每边界一次前向切片,稀疏图线性量级,可增量缓存);零运行期——标签是边界标注,编译期消费。 + +**精度分级(工程路线)**:义务推导先保守后精确——第一版用现有别名分析 + 前向切片;后续按域敏感/路径敏感的稀疏指针分析逐步收窄过度近似,提高 M/S 命中率、压缩 COPY 闭包。每一级精度只改变「少亏多少性能」,不改变正确性边界。 + +### 2. 物理机制 + +**基址相对表示(MOVE/SHARE 的 O(1) 前提)**:arena 内部指针 = (基址, 偏移)。这是映射实例选择——语义层 (条目标识, 偏移)(2026-08-15 地址 = 映射定稿)一字不动。跨 arena 引用保持绝对句柄 + outlives 判定。块搬迁 = 换基址生效,零遍历。 + +- **MOVE 兑现**:提交点 = channel 入队成功(此前仍归发送者;channel 满阻塞期间不碰块)→ 摘除消息子区域 → 接收侧挂入自身 arena 池(换基址)。队列中的块无归属 = 落在 channel 自身临界区内,无新增锁。 +- **SHARE 兑现**:发送侧 `drop_perm` 收窄到 Readable(字节权限层已有)+ 区域引用计数;任一侧区域重置前递减,归零归还池。冻结是**永久单向门**(Pony/Verona/Gordon 文献一致结论;无解冻)。多核并发读零同步。 +- **COPY 兑现**:接收端重建材料 = 连续块搬移(消息子区域保证连续)+ 新 home 注册;环与共享子结构天然保持。无配方成员不复制——分析期自动升级进身份重指表。 + - **物理分档**:重建材料不必全量即时。小载荷(子区域 ≤ 阈值,如 4KB)走 eager memcpy——fault 开销摊不薄时不进页机制;大载荷且双侧「可能写但预期少写」走 **COW 页共享**:同一物理页映射进两个 arena 的虚拟地址(基址相对表示使双映射成立),初始双侧只读,任一侧首次写触发页分裂、复制该页后放行。 + - COW 的语义仍是 COPY,不是 SHARE 的偷换:SHARE 静态禁止接收侧写,COW 允许写、只是推迟付账——两侧各自写、各自分裂,最终是两个独立 home。 + - 模型不破:任意时刻每个物理页要么单侧可写、要么双侧只读——「无共享可变内存」在字节层面逐瞬间成立;多 M 安全(页权限由内核强制)。 + - 分档阈值与 COW 启用策略是映射实例调优参数,不进语义层。 +- **区域池复用**:MOVE 出去的块经接收者用完归还池;1:1 稳态管线自动呈现双缓冲形态——角色随所有权转,不随编号固定(双缓冲/乒乓缓冲是本文机制在 1:1 稳态下的特例)。 +- **调度交互**:不新增切换点类别(沿用 channel 满/空阻塞);wait 队列等的是 G 不是块;channel 关闭时队列中的 MOVE/SHARE 块走同一条归还路径。 + +**跨区域引用边角**:消息闭包含指向发送者其他区域的可达引用时——被引区域无其他存活引用 → 级联 MOVE(闭包整体迁);否则落 COPY。 + +### 3. 身份传递实例 + +channel 套 channel、传文件句柄:传标识符(指向特许共享对象的引用)+ 授权标注副本。身份可多份指认(只读共享)→ fan-out 广播引用合法。 + +## 验证与错误处理 + +### 语义层 + +`.corespec` 写边界公理 + 三个标签的关系句义务。验证器消费图标注,检查「标注 vs 义务」一致性——不证明物理物化本身。义务可成为规格断言。 + +### 经典实例层 + +- 编译期自检:义务自动推导 + 标注一致性自检(与 regalloc 判定同构:checker 实现期自检 + 规约双层消费同一规则); +- 消毒模式(可选):MOVE 后发送侧块所在页降权为只读,误写触发 fault——把分析 bug 变成运行期可抓的崩溃而非静默损坏(字节权限层已有降权机制); +- 测试面:环、别名逃逸、跨区域引用、关闭 channel、混合身份消息、fan-out 广播、级联 MOVE——每项 = 标签 × 义务配对用例。 + +## 先例 + +| 先例 | 借鉴 | 差异 | +|---|---|---| +| Singularity exchange heap(EuroSys'07)/ CoreSing#(arXiv:1202.2086) | 所有权随 send 转移、零拷贝通道 | 只能传无环树形数据;本文块搬移天然带环 | +| Pony deny capabilities(AGERE'15) | iso/val/tag 三分 = MOVE/SHARE/身份 | 本文靠推断而非用户标注 | +| SOTER(PPoPP'11) | 静态推断引用传递、推断不出就拷贝 | 缺少区域级物理机制 | +| Verona(OOPSLA'24) | 区域级转让、SCC 冻结 + 原子引用计数 | cown 模型不同;物理机制同构 | +| Gordon et al.(OOPSLA'12) | 永久冻结、不可变环合法 | — | +| Cap'n Proto / FlatBuffers | 偏移指针 = 位置无关表示 | 构造后难改;本文 ProvenanceVerify 覆盖其安全检查面 | + +## 不做的事(YAGNI) + +- 无解冻(冻结永久单向门); +- 无用户标注(全部推断); +- 无运行期标签分派(标签编译期消费); +- 不新增共享可变内存类别(channel 仍为唯一特许共享对象); +- 不改并发模型(go/flow/yield/await、arena 隔离、channel 传值)。 + +## 开放问题(实现前收敛) + +1. 闭包 = 可达关系传递闭包的精确化:按 region 边界截断还是按 points-to 全图?倾向:消息子区域结构边界截断,跨区域引用显式清单; +2. 级联 MOVE 的判据细节(被引区域「无其他存活引用」如何保守判定); +3. 基址相对表示的迁移成本评估:现有 ELF 后端绝对指针的改动面; +4. 区域池复用策略(池容量、归还时机); +5. 消毒模式的默认开关策略; +6. COPY 物理分档阈值:eager vs COW 的分界、子区域大小分布调优; +7. COW 的 arena 支撑:页对齐子区域、双映射与页分裂路径(fault 处理)、与消毒模式(MOVE 后降权页)的机制共享与冲突检查; +8. 子区域标签表的编码与边界描述符布局(材料标签 × 身份重指 × 子区域句柄)。 From 3fe8cbbc3c1ccacbd4ea90451bf358ef099940f8 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Tue, 15 Sep 2026 23:42:20 +0800 Subject: [PATCH 012/138] =?UTF-8?q?chore:=20=E5=81=9C=E6=AD=A2=E8=B7=9F?= =?UTF-8?q?=E8=B8=AA=20examples/=20=E6=9E=84=E5=BB=BA=E4=BA=A7=E7=89=A9?= =?UTF-8?q?=EF=BC=8812=20=E4=B8=AA=20*.ccr=20/=20*.cir=20/=20*.o=EF=BC=89?= =?UTF-8?q?=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `.gitignore` 早已覆盖这三类(`*.ccr` / `*.cir` / `*.o`,共 4 处规则),但这 12 个文件在规则生效前就已入库 ⇒ 用 `jj file untrack` 从仓库摘除,**磁盘文件保留**(构建产物照常可用) - 判据:`jj file list | grep -cE "\.(ccr|cir|o)$"` = 0 · 磁盘文件仍在 · 零源码/测试改动 - 清单:examples/{add,complex}/{main.ccr,main.cir,prog.o} · examples/{test_max,test_scale,test_sum,test_sumto}.{ccr,o} --- examples/add/main.ccr | Bin 391 -> 0 bytes examples/add/main.cir | 15 -------- examples/complex/main.ccr | 73 -------------------------------------- examples/complex/prog.o | Bin 1928 -> 0 bytes examples/test_max.ccr | 16 --------- examples/test_max.o | Bin 976 -> 0 bytes examples/test_scale.ccr | 31 ---------------- examples/test_scale.o | Bin 1096 -> 0 bytes examples/test_sum.ccr | 17 --------- examples/test_sum.o | Bin 920 -> 0 bytes examples/test_sumto.ccr | 30 ---------------- examples/test_sumto.o | Bin 1136 -> 0 bytes 12 files changed, 182 deletions(-) delete mode 100644 examples/add/main.ccr delete mode 100644 examples/add/main.cir delete mode 100644 examples/complex/main.ccr delete mode 100644 examples/complex/prog.o delete mode 100644 examples/test_max.ccr delete mode 100644 examples/test_max.o delete mode 100644 examples/test_scale.ccr delete mode 100644 examples/test_scale.o delete mode 100644 examples/test_sum.ccr delete mode 100644 examples/test_sum.o delete mode 100644 examples/test_sumto.ccr delete mode 100644 examples/test_sumto.o diff --git a/examples/add/main.ccr b/examples/add/main.ccr deleted file mode 100644 index 055b5f4b56c47296cf50852488f7d3b39fbc34d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmZ9IOA3H63`FbS>v#qaAb5jHH(gj=cz?|k21*9Td?rpp*L7c-5JGF5jiYfe*66J` zV=kS=m=fcW&%y9^Klv)MU=%P46v@%3u_r>-LVyPZ%)AC^f3xbNsYm2LdHOzkb(b`1 f-N6pO*FS;o@q6q0*kAid n1; - n3 -> n5; - n4 -> n5; - n5 -> n6; -} diff --git a/examples/complex/main.ccr b/examples/complex/main.ccr deleted file mode 100644 index 9fb35366..00000000 --- a/examples/complex/main.ccr +++ /dev/null @@ -1,73 +0,0 @@ -Function: Point.sum - Block: entry_1 - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='x', dest=IRVar(name='t1', kind=, type=None, id=0), field_index=0) - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='y', dest=IRVar(name='t2', kind=, type=None, id=0), field_index=1) - BinaryInstr(op='+', left=IRVar(name='t1', kind=, type=None, id=0), right=IRVar(name='t2', kind=, type=None, id=0), dest=IRVar(name='t3', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t3', kind=, type=None, id=0)) -Function: Point.scale - Block: entry_2 - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='x', dest=IRVar(name='t4', kind=, type=None, id=0), field_index=0) - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='y', dest=IRVar(name='t5', kind=, type=None, id=0), field_index=1) - BinaryInstr(op='+', left=IRVar(name='t4', kind=, type=None, id=0), right=IRVar(name='t5', kind=, type=None, id=0), dest=IRVar(name='t6', kind=, type=None, id=0)) - BinaryInstr(op='*', left=IRVar(name='t6', kind=, type=None, id=0), right=IRVar(name='factor', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t7', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t7', kind=, type=None, id=0)) -Function: sum_to - Block: entry_3 - ConstInstr(value=1, type='int', dest=IRVar(name='t8', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='int'), dest=IRVar(name='i', kind=, type=BaseType(name='int'), id=0)) - StoreInstr(addr=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t8', kind=, type=None, id=0)) - ConstInstr(value=0, type='int', dest=IRVar(name='t9', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='int'), dest=IRVar(name='s', kind=, type=BaseType(name='int'), id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t9', kind=, type=None, id=0)) - JumpInstr(label='loop_header_4') - Block: loop_header_4 - JumpInstr(label='loop_body_5') - Block: loop_body_5 - BinaryInstr(op='>', left=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='n', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t10', kind=, type=None, id=0)) - BranchInstr(cond=IRVar(name='t10', kind=, type=None, id=0), true_label='then_7', false_label='if_merge_8') - Block: then_7 - JumpInstr(label='loop_exit_6') - Block: if_merge_8 - BinaryInstr(op='+', left=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t11', kind=, type=None, id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t11', kind=, type=None, id=0)) - ConstInstr(value=1, type='int', dest=IRVar(name='t12', kind=, type=None, id=0)) - BinaryInstr(op='+', left=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='t12', kind=, type=None, id=0), dest=IRVar(name='t13', kind=, type=None, id=0)) - StoreInstr(addr=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t13', kind=, type=None, id=0)) - JumpInstr(label='loop_header_4') - Block: loop_exit_6 - ReturnInstr(value=IRVar(name='s', kind=, type=BaseType(name='int'), id=0)) -Function: max - Block: entry_9 - BinaryInstr(op='>', left=IRVar(name='a', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='b', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t14', kind=, type=None, id=0)) - BranchInstr(cond=IRVar(name='t14', kind=, type=None, id=0), true_label='then_10', false_label='else_11') - Block: then_10 - ReturnInstr(value=IRVar(name='a', kind=, type=BaseType(name='int'), id=0)) - Block: else_11 - ReturnInstr(value=IRVar(name='b', kind=, type=BaseType(name='int'), id=0)) - Block: if_merge_12 - ReturnInstr(value=None) -Function: main - Block: entry_13 - AllocStructInstr(struct_name='Point', dest=IRVar(name='t15', kind=, type=None, id=0), field_count=2) - ConstInstr(value=10, type='int', dest=IRVar(name='t16', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t15', kind=, type=None, id=0), field='x', value=IRVar(name='t16', kind=, type=None, id=0), field_index=0) - ConstInstr(value=20, type='int', dest=IRVar(name='t17', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t15', kind=, type=None, id=0), field='y', value=IRVar(name='t17', kind=, type=None, id=0), field_index=1) - AllocInstr(type=PathType(path=['Point']), dest=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)) - StoreInstr(addr=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0), value=IRVar(name='t15', kind=, type=None, id=0)) - CallInstr(func='Point.sum', args=[IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)], dest=IRVar(name='t18', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='s', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t18', kind=, type=None, id=0)) - ConstInstr(value=3, type='int', dest=IRVar(name='t19', kind=, type=None, id=0)) - CallInstr(func='Point.scale', args=[IRVar(name='p', kind=, type=PathType(path=['Point']), id=0), IRVar(name='t19', kind=, type=None, id=0)], dest=IRVar(name='t20', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t20', kind=, type=None, id=0)) - ConstInstr(value=5, type='int', dest=IRVar(name='t21', kind=, type=None, id=0)) - CallInstr(func='sum_to', args=[IRVar(name='t21', kind=, type=None, id=0)], dest=IRVar(name='t22', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='loop_val', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='loop_val', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t22', kind=, type=None, id=0)) - CallInstr(func='max', args=[IRVar(name='s', kind=, type=BaseType(name='unit'), id=0), IRVar(name='loop_val', kind=, type=BaseType(name='unit'), id=0)], dest=IRVar(name='t23', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='biggest', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='biggest', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t23', kind=, type=None, id=0)) - BinaryInstr(op='+', left=IRVar(name='biggest', kind=, type=BaseType(name='unit'), id=0), right=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0), dest=IRVar(name='t24', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t24', kind=, type=None, id=0)) diff --git a/examples/complex/prog.o b/examples/complex/prog.o deleted file mode 100644 index 79108c74ed59de7c73817dfb5c9adae6f527e4e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1928 zcmb`IPiPZS5XNV-rhizmm5LF2C{m%?N@|+c3O%?|6om>s^e8;Gb~VAKkz@-gMT~e+ z^b$}!Xwid63wjegTIt1Wi|92$LAS}J$32wzP4;aPU(=I5*xmPj^JeCodArHIOH-Fa znnuCU=p!{&0VVnpZp_Wv)TDN5quu3CYr8rXst;(h*hQY(WqN~}>22$B9>%<#ur|GN zyXj47#^Z`XUePc;E?aNS+9|`Ll4KP<>^HaI8LwZK_BdWup{rUYBf^VJE`sM2lp2HO3+q| z0ey8XQh%UDhmIoh9N;JRv9^8tJQ><_e`p2n!Bq{lBM0|_a|~+aJqpQwFhi`tuy5y@ zSnvI6^*Nst_rgWJN$wx&WLpV!K4E)MC;Gj{_5s)jOmBs4L+~Af?=Tp8RT?(E4d!w1 zIQTf~9rt@*dv7MNZ^A#aKbCj{2#ms_;1@wkB zZwGLMut_*+nopkFuV&M8OJa, type=BaseType(name='int'), id=0), right=IRVar(name='b', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t1', kind=, type=None, id=0)) - BranchInstr(cond=IRVar(name='t1', kind=, type=None, id=0), true_label='then_2', false_label='else_3') - Block: then_2 - ReturnInstr(value=IRVar(name='a', kind=, type=BaseType(name='int'), id=0)) - Block: else_3 - ReturnInstr(value=IRVar(name='b', kind=, type=BaseType(name='int'), id=0)) - Block: if_merge_4 - ReturnInstr(value=None) -Function: main - Block: entry_5 - ConstInstr(value=3, type='int', dest=IRVar(name='t2', kind=, type=None, id=0)) - ConstInstr(value=7, type='int', dest=IRVar(name='t3', kind=, type=None, id=0)) - CallInstr(func='max', args=[IRVar(name='t2', kind=, type=None, id=0), IRVar(name='t3', kind=, type=None, id=0)], dest=IRVar(name='t4', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t4', kind=, type=None, id=0)) diff --git a/examples/test_max.o b/examples/test_max.o deleted file mode 100644 index 7259da6ac8b9d0e63063327f36c5863a33add369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 976 zcmbVKu}T9$5Pg?5iE@PzL_{k=&_YtEg@xcYTBWpKE_j-NvB+%^Nnt>LAeE(U`~vM% zu&_0VwimF8NulKx;hatO$g#O91INs~eQ#!V_O1>KM=8rfWELJU&LR}ZrN%KIh8$UB zFsMJ>4;Y%^HQxO!f{u;gC(DDL%>!ZCz5dtTN}nOzRqqc&#!T?XJ=(Pj!9LcS!RchPOWTP}-K=Xw$48t0P~EBI(v7@+XdTkS}_k*My~Fw^yUk^ z8oVe~+_-H}qIN&6Ue!zbE8cXIU+vYq&(Sw}XJA?53Nw4FqengHrshS?oPU81lJveN KZbd@vP5nPdp=RL# diff --git a/examples/test_scale.ccr b/examples/test_scale.ccr deleted file mode 100644 index 60cb454e..00000000 --- a/examples/test_scale.ccr +++ /dev/null @@ -1,31 +0,0 @@ -Function: Point.sum - Block: entry_1 - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='x', dest=IRVar(name='t1', kind=, type=None, id=0), field_index=0) - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='y', dest=IRVar(name='t2', kind=, type=None, id=0), field_index=1) - BinaryInstr(op='+', left=IRVar(name='t1', kind=, type=None, id=0), right=IRVar(name='t2', kind=, type=None, id=0), dest=IRVar(name='t3', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t3', kind=, type=None, id=0)) -Function: Point.scale - Block: entry_2 - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='x', dest=IRVar(name='t4', kind=, type=None, id=0), field_index=0) - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='y', dest=IRVar(name='t5', kind=, type=None, id=0), field_index=1) - BinaryInstr(op='+', left=IRVar(name='t4', kind=, type=None, id=0), right=IRVar(name='t5', kind=, type=None, id=0), dest=IRVar(name='t6', kind=, type=None, id=0)) - BinaryInstr(op='*', left=IRVar(name='t6', kind=, type=None, id=0), right=IRVar(name='factor', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t7', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t7', kind=, type=None, id=0)) -Function: main - Block: entry_3 - AllocStructInstr(struct_name='Point', dest=IRVar(name='t8', kind=, type=None, id=0), field_count=2) - ConstInstr(value=10, type='int', dest=IRVar(name='t9', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t8', kind=, type=None, id=0), field='x', value=IRVar(name='t9', kind=, type=None, id=0), field_index=0) - ConstInstr(value=20, type='int', dest=IRVar(name='t10', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t8', kind=, type=None, id=0), field='y', value=IRVar(name='t10', kind=, type=None, id=0), field_index=1) - AllocInstr(type=PathType(path=['Point']), dest=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)) - StoreInstr(addr=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0), value=IRVar(name='t8', kind=, type=None, id=0)) - CallInstr(func='Point.sum', args=[IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)], dest=IRVar(name='t11', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='s', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t11', kind=, type=None, id=0)) - ConstInstr(value=3, type='int', dest=IRVar(name='t12', kind=, type=None, id=0)) - CallInstr(func='Point.scale', args=[IRVar(name='p', kind=, type=PathType(path=['Point']), id=0), IRVar(name='t12', kind=, type=None, id=0)], dest=IRVar(name='t13', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='unit'), dest=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0)) - StoreInstr(addr=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0), value=IRVar(name='t13', kind=, type=None, id=0)) - BinaryInstr(op='+', left=IRVar(name='s', kind=, type=BaseType(name='unit'), id=0), right=IRVar(name='scaled', kind=, type=BaseType(name='unit'), id=0), dest=IRVar(name='t14', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t14', kind=, type=None, id=0)) diff --git a/examples/test_scale.o b/examples/test_scale.o deleted file mode 100644 index fcaa3fd68b4d20a78eca97f9fdd8faefd84f6b2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1096 zcmbtRJxfAS7=Dj8wYZRlgOP)Tfgs`Rcp!=bht?*!eLzAMcyC}xB)Ak!P7SRM_6r&_ zqB)Xiw(+We;0$8V@t&J?^m81O(XyL!}9{3 zD2cBa@X;|Y8UMo70arH$?hEs-2Muj#7`#m3uCaG_oz-1v+h*043tcXBb)YNFNL&X{ zgSKG}oavw!yNdI5Fu0Z4q%Ifw3WWK%ci&f>i#OR%pr1fLi8n)2H>uv7!d!}rhFNXl ze?LaQIT;yKH$Y_2*b>HI20FTVJDl94IUJm<&#U^DMzoB40ebGWI_;bmPMnN=R;1Ok zpX^)5hm^M+tK<-6?DL{y?ZG=G&mZ}bb69pLb6`0ZW%g`ar~XSG2S~muq=C80v)Yqq zw7C`Jazy{hvWD|vdO3y0>_705Z`yYK`, type=PathType(path=['Point']), id=0), field='x', dest=IRVar(name='t1', kind=, type=None, id=0), field_index=0) - LoadFieldInstr(struct=IRVar(name='self', kind=, type=PathType(path=['Point']), id=0), field='y', dest=IRVar(name='t2', kind=, type=None, id=0), field_index=1) - BinaryInstr(op='+', left=IRVar(name='t1', kind=, type=None, id=0), right=IRVar(name='t2', kind=, type=None, id=0), dest=IRVar(name='t3', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t3', kind=, type=None, id=0)) -Function: main - Block: entry_2 - AllocStructInstr(struct_name='Point', dest=IRVar(name='t4', kind=, type=None, id=0), field_count=2) - ConstInstr(value=10, type='int', dest=IRVar(name='t5', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t4', kind=, type=None, id=0), field='x', value=IRVar(name='t5', kind=, type=None, id=0), field_index=0) - ConstInstr(value=20, type='int', dest=IRVar(name='t6', kind=, type=None, id=0)) - StoreFieldInstr(struct=IRVar(name='t4', kind=, type=None, id=0), field='y', value=IRVar(name='t6', kind=, type=None, id=0), field_index=1) - AllocInstr(type=PathType(path=['Point']), dest=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)) - StoreInstr(addr=IRVar(name='p', kind=, type=PathType(path=['Point']), id=0), value=IRVar(name='t4', kind=, type=None, id=0)) - CallInstr(func='Point.sum', args=[IRVar(name='p', kind=, type=PathType(path=['Point']), id=0)], dest=IRVar(name='t7', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t7', kind=, type=None, id=0)) diff --git a/examples/test_sum.o b/examples/test_sum.o deleted file mode 100644 index 85abd79716423595856922fd53064226d1caf749..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 920 zcmbtRJxT*X6#gb-5@o?CK@hA2K?t&Em_|`Z;SItD1udeG9U+p!fR)&o$^)nnPhc56 zLV}2e#TFKB^a4|a_08_A3FB5e&G+8--uM3IY_qzRV+`qIc)-LSbw6frGOL;>S= z|9;HTjm|L$N>E`*s0tHm#EqRNy9n}y`YZ}nWzOZ70~I(zk)}ItKlL7Y?d7KqXM^wt z-H`MxJD}cgQFI-Y$u0}kb2>vB`+2fOw#73y z1~_t=0!|V8CFhwsl1Xr4{LRl3zd(Jr?Lxc9hey)&Pa0Tjq2^1kDFN3%ZAh<9D4Pi$ zq?X)oNw|BS^x)Qg-!Au8e}#m4>Vj2382{GdXDok}e66=H^={>*u6?0(3e5gPuj^&~ u-iK+}GngmTJL+EgEhm6ELVI={x-x?!Hst^Wqf^l*Rx diff --git a/examples/test_sumto.ccr b/examples/test_sumto.ccr deleted file mode 100644 index e8d3a7fe..00000000 --- a/examples/test_sumto.ccr +++ /dev/null @@ -1,30 +0,0 @@ -Function: sum_to - Block: entry_1 - ConstInstr(value=1, type='int', dest=IRVar(name='t1', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='int'), dest=IRVar(name='i', kind=, type=BaseType(name='int'), id=0)) - StoreInstr(addr=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t1', kind=, type=None, id=0)) - ConstInstr(value=0, type='int', dest=IRVar(name='t2', kind=, type=None, id=0)) - AllocInstr(type=BaseType(name='int'), dest=IRVar(name='s', kind=, type=BaseType(name='int'), id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t2', kind=, type=None, id=0)) - JumpInstr(label='loop_header_2') - Block: loop_header_2 - JumpInstr(label='loop_body_3') - Block: loop_body_3 - BinaryInstr(op='>', left=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='n', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t3', kind=, type=None, id=0)) - BranchInstr(cond=IRVar(name='t3', kind=, type=None, id=0), true_label='then_5', false_label='if_merge_6') - Block: then_5 - JumpInstr(label='loop_exit_4') - Block: if_merge_6 - BinaryInstr(op='+', left=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), dest=IRVar(name='t4', kind=, type=None, id=0)) - StoreInstr(addr=IRVar(name='s', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t4', kind=, type=None, id=0)) - ConstInstr(value=1, type='int', dest=IRVar(name='t5', kind=, type=None, id=0)) - BinaryInstr(op='+', left=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), right=IRVar(name='t5', kind=, type=None, id=0), dest=IRVar(name='t6', kind=, type=None, id=0)) - StoreInstr(addr=IRVar(name='i', kind=, type=BaseType(name='int'), id=0), value=IRVar(name='t6', kind=, type=None, id=0)) - JumpInstr(label='loop_header_2') - Block: loop_exit_4 - ReturnInstr(value=IRVar(name='s', kind=, type=BaseType(name='int'), id=0)) -Function: main - Block: entry_7 - ConstInstr(value=5, type='int', dest=IRVar(name='t7', kind=, type=None, id=0)) - CallInstr(func='sum_to', args=[IRVar(name='t7', kind=, type=None, id=0)], dest=IRVar(name='t8', kind=, type=None, id=0)) - ReturnInstr(value=IRVar(name='t8', kind=, type=None, id=0)) diff --git a/examples/test_sumto.o b/examples/test_sumto.o deleted file mode 100644 index cad0268a031a07688a2502ed2fa62662c70c692a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1136 zcmbVKze~eF6n<%Jt7K@!Le&l`RYZ^uT7PT?LrU@@?HU zb%;U)I`zlfjtUpfy7FqLfz3hVt*HWQsyb^%femr2A=VJbfMMj> zUXQm&FlIz&Yhn1s{M%SA5$AC+jKkQRqMf&s7?bq!xOGy%+&n4apFsZv`mye}<0Ng= zVqX`X`t!|`NP4~TH_7-d-wmg*HZgV2tb9vkODFlJ@WEq z?0tj2K^OG$n&7j+V6?{szbf%Fg5Qz&dBN{W{EFbOBz_%n9RK%D;=wv85n6`nDjb%} z$E0W)1+zjHJd`gNYGe^;(L5qcaIi;8W@X|{qrF$=DrI^WS$@a=)$@G) xURk&Pq$n81IR0k5_XIGCHeW-y<=UKI6-l=^2rRk(Vf6LWp9#J9zWfBT{ttlkl{EkW From 82bc7972e67aa4e4bf0fd8f971ae6ba80ce17383 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Wed, 16 Sep 2026 00:00:33 +0800 Subject: [PATCH 013/138] =?UTF-8?q?docs:=20=E9=A6=96=E9=A1=B5=E9=87=8D?= =?UTF-8?q?=E5=86=99=EF=BC=88=E5=8F=8C=E8=AF=AD=EF=BC=9AREADME.md=20?= =?UTF-8?q?=E8=8B=B1=E6=96=87=20+=20README.zh-CN.md=20=E4=B8=AD=E6=96=87?= =?UTF-8?q?=EF=BC=89=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原则(与维护者逐条定稿):陈述技术事实,不自我推销 ——「越是不普通的项目,README 越该写得普通」 结构:普通开头(2 段)→ 可运行代码样例 → Features → Design → Status → Building → Usage → Documentation → Contributing → License;顶部互链 `English | 中文`。 相对旧首页(219 行)的实质变化: - 删:slogan 式开场("全新的、完全自主设计的")· 自举里程碑大表 · 已实现特性大表 · 未完成限制大表 - 定位不缩窄:`experimental programming language`(非 systems programming language) - 口径修正:「retained at every layer」→「remains available in the semantic IR through lowering, allowing verification and optimization to operate before final target encoding」 (.cir = 验证消费面;.ccr = 格层线性投影;不主张 ELF 仍承载 type/specification) - Features 按辨识度排序:reproducible three-stage bootstrap · direct ELF · semantic graph IR · graph→lattice→encoding · raw pointers + provenance · arena tied to regions · fibers/channels · function-level incremental compilation;泛型/枚举/元组/数组沉到最后 - 代码样例走真实接口 io.println_i(旧首页的 io.println_int 是不存在的函数) 实测:样例用当前源构建运行 = 输出 55 / rc=0;全部文档链接存在 (execution-model 已随文档重组移至 docs/maintainer/design/) --- README.md | 263 ++++++++++++++---------------------------------- README.zh-CN.md | 100 ++++++++++++++++++ 2 files changed, 175 insertions(+), 188 deletions(-) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 01113d64..c25842dc 100644 --- a/README.md +++ b/README.md @@ -1,219 +1,106 @@ # Core Programming Language -**Core** 是一门全新的、完全自主设计的编程语言,以及一套围绕"语义保鲜"理念构建的编译工具链。 -其核心是一个保留全部类型与语义信息的中间表示(IR),并原生支持完全形式化验证。 - ---- - -## 项目状态 - -当前处于 **自举编译器阶段**:编译器自身使用 Core 语言编写,可通过引导编译器(Python)编译为原生 x86-64 二进制。 -`build/corec` 是自举编译器前端,`build/corearch` 是后端。 - -### 自举里程碑:corec2 自我编译 - -**已实现三级自举管线:** - -| 阶段 | 编译方式 | 产物 | 状态 | -|------|---------|------|------| -| Stage 0 | Python 引导编译器 | `build/corec`(前端)+ `build/corearch`(后端) | 正常 | -| Stage 1 | `build/corec` + `build/corearch` 编译自举编译器源码 | `build/corec2`(自编译前端) | 可运行 | -| Stage 2 | `build/corec2` + `build/corearch` 再次编译 | `build/corec3`(二次自编译) | 可运行 | - -> 注意(2026-08 现状):`corec2 check` 卡在 tokenizer(约 9 个全局变量未注册进 `g_ir_globals`,赋值静默丢弃)——自举阻塞项,见 TODO.md。 - -### 已实现的核心特性 - -| 类别 | 特性 | 状态 | -|------|------|------| -| **类型系统** | `int`、`dex`、`bool`、`string`、`char`、`unit`、`never` | 完成 | -| | 泛型函数与泛型结构体 | 完成 | -| | `auto` / `.` 类型推导、位宽后缀 `_i32` `_u64`(`_f32`/`_f64` 保留为 apx 的 CPU 位宽标注) | 完成 | -| **变量** | `:=` / `: type` 声明,`mut` / `pub` / `apx` 标签,批量声明 | 完成 | -| **函数** | 函数定义、调用、单行函数体、`pub fn` 可见性 | 完成 | -| **控制流** | `if` / `else` / `elif`、`while`、`loop` + `break`/`continue`、`for` 区间和数组迭代 | 完成 | -| | region 嵌套式(SG_IF/LOOP/FOR/FLOW/UNSAFE + state edges + .ccr v5,即 HDFG 结构) | 完成 | -| **并发** | `go f(args)` / `go var start..end expr` 协程生成 | 完成(单 M 端到端) | -| | 协作式 Fiber 调度器、缓冲通道(阻塞) | 完成(单 M 验证) | -| | 多 M worker 线程 | 未完成(TODO bug 2) | -| | `await` 异步等待 | 未完成 | -| **复合类型** | 结构体、枚举 + `match`、元组、定长数组、切片 | 完成 | -| **方法** | `impl` 块、`self` / `&self` / `&mut self`、`impl Trait for Type` | 完成 | -| **引用与借用** | `&T` / `&mut T`、借用检查(Borrow Checker) | 完成 | -| **内存模型** | Arena 内存模型(init/new/reset + 子图绑定 + mmap 堆扩展) | 完成 | -| | 指针安全三 pass:PointerAnalysis / RegionCheck / ProvenanceVerify | 完成 | -| **@ 内建** | `@sizeOf` `@alignOf` `@fields` `@hasField` `@field` `@typeInfo` `@comptime` `@inline` `@no_bounds_check` `@fast` `@unroll` `@section`(12 个) | 完成 | -| **编译器** | 增量缓存(函数级 .cir,默认开启 + clean-cache) | 完成 | -| | `@hotpatch` 滚动更新(IR_HOTPATCH_ROUTE + SIGHUP 热加载) | 完成 | -| | 惰性求值(IR_LAZY_THUNK/FORCE,调用级) | 部分(thunk/force 为值搬运,无实际延迟) | -| | 控制流自动惰性(编译期指令下沉路线) | 设计定案,待实现 | -| **汇编层** | ~~`.crasm` 统一汇编抽象层~~——**2026-09-05 退役**,被硬件接口表(HIT)吸收:跨平台 = HIT 事件 + 投影表;MMIO/特权/中断 = `.cr` unsafe + HIT extern | 已退役(docs/maintainer/design/crasm.md 保留为历史) | -| **语义检查** | 名字解析 + 类型检查、结构化错误码 + 源码定位 | 完成 | -| **模块系统** | `import`、`fileid`、`@project`、`_import.cr`、依赖裁剪 | 完成 | -| **标准库** | `io.cr` / `cli.cr` / `toml.cr` | 完成 | -| | `math.cr` / `collections.cr` | 部分(stub,TODO bug 4) | -| | 平台桥抽象:I/O 流(程序 = 流转导器)/ 随机 / 哈希 / 时钟(语义接口 + 后端实现) | 设计定案([spec](docs/superpowers/specs/2026-08-16-platform-abstract-design.md)),待实现 | -| **编译器基础设施** | 自举编译器(Core 写编译器)、x86-64 ELF 直接输出 | 完成 | -| | `build`/`check`/`ccr`/`cir`/`run`/`clean-cache` 子命令 | 完成 | -| | CIR HDFG(带完整类型/语义信息)、`.ccr` 线性 CFG | 完成 | -| **形式化验证** | 规约层 IR / 验证条件生成 / SMT 求解器接口 | 占位 | -| **发布与治理** | Arch Linux PKGBUILD | 完成 | -| | GitFlow 治理:双 ruleset(main 仅维护者合入 / develop 集成分支)+ 签名提交 + merge queue(免费计划降级为手动合入) | 完成 | -| | CI 骨架(Rust 模板重写:PR 快速层 + 完整层) | 部分(GitHub 侧注册冻结,待自愈) | - -### 未完成与已知限制 - -| 类别 | 项 | 参考 | -|------|-----|------| -| 自举 | corec2 tokenizer 死循环(约 9 个全局变量未注册进 g_ir_globals) | TODO 自举阻塞项 | -| | O1 自举稳定性(pass_cse 大函数崩溃) | TODO | -| 解释器 | for 循环 / 递归 / 泛型函数不支持 | TODO 预存 bug 3 | -| 运行时 | arena bump 分配死循环(编码已 objdump 排除,emit_alloc_body 运行时逻辑待查) | TODO 预存 bug 5 | -| 内存 | 完整自举 1 GiB bump heap 峰值(需按函数回收而非扩堆) | TODO 预存 bug 1 | -| 并发 | 多 M worker 未连调度器、channel 队列未并发验证 | TODO 预存 bug 2 | -| CI | 工作流注册冻结(注册表陈旧,GitHub 侧)——develop 的 required_status_checks 暂移除 | spec §3.3 | -| 语言 | 控制流自动惰性、分布式(跨机器/QUIC);~~`.crasm` 汇编层~~(2026-09-05 退役,被 HIT 吸收) | 设计完成/定案,待实现 | - ---- - -## 快速开始 +**English** | [中文](README.zh-CN.md) -```bash -# 构建自举编译器 -python3 build_selfhost_native.py - -# -c 模式:直接执行代码(解释器) -./build/corec run '__builtin_println("hello"); 42' - -# 编译文件 -./build/corec ccr hello.cr # → hello.ccr -./build/corec cir hello.cr # → hello.cir(HDFG) -./build/corec build hello.cr --static # → a.out(ELF,无需 as/ld) -as -o hello.o hello.s && ld ... # 或用传统路径 +![self-hosted](https://img.shields.io/badge/self--hosted-yes-blue) +![target](https://img.shields.io/badge/target-x86--64-lightgrey) +![license](https://img.shields.io/badge/license-GPLv3-blue) +![status](https://img.shields.io/badge/status-experimental-orange) -# 通过引导编译器(Python)编译 -python3 tools/corec ir hello.cr # → .cir HDFG -python3 tools/corec cir hello.cr # → .ccr 线性 IR -``` +Core is an experimental programming language built around a semantic graph IR. +The compiler is self-hosted, emits native x86-64 ELF directly, and keeps regions, +state relations, provenance, and other program semantics through its IR instead of +reducing them early to a conventional machine model. -### 并发示例 +Core's compilation model is organized around graph → lattice → encoding: computation +is represented as relations first, state/storage is mapped separately, and machine +representation comes last. ```core import io; -fn worker(id: int, base: int) { - io.println_int(id + base); +fn fib(n: int) -> int { + if n < 2 { + return n; + } + return fib(n - 1) + fib(n - 2); } -fn main() { - // 批量生成 8 个 worker 协程,每个传入不同 ID - go f 1..8 worker(f * 10000, 10000); - - // 单个协程 - go worker(1, 0); +fn main() -> int { + io.println_i(fib(10)); // 55 + return 0; } ``` ---- +Core is under active development. -## 项目结构 +## Features -``` -core/ -├── bootstrap/corec/ # 引导编译器(Python) -│ ├── syntax/ # AST 定义、Token、关键字 -│ ├── frontend/ # 词法/语法/语义分析/IR 生成 -│ ├── ir/ # IR 数据结构(CIR HDFG / CCR 线性 CFG) -│ ├── backend/ # 解释器、ARM64/x86-64 代码生成 -│ └── verifier/ # 形式化验证(占位) -├── src/ -│ ├── compiler/ # 自举编译器(Core 源码) -│ │ ├── ast.cr # Token 定义、AST 节点、IR 指令、类型常量 -│ │ ├── lexer.cr # 词法分析器 -│ │ ├── parser.cr # 语法分析器 -│ │ ├── checker.cr # 类型检查 + 借用检查 -│ │ ├── ir_gen.cr, dataflow.cr # IR 生成 + HDFG -│ │ ├── main.cr # 前端入口(corec) -│ │ ├── corearch.cr # 后端入口(corearch) -│ │ ├── ccr_io.cr # .ccr 二进制序列化 -│ │ ├── elf.cr # ELF 直接输出(无需 as/ld) -│ │ ├── interp.cr # IR 解释器(-c 模式) -│ │ ├── project.cr # 项目文件读取(Core.toml) -│ │ └── backend/x86_64.cr # x86-64 汇编生成 -│ ├── stdlib/ # 标准库 -│ │ ├── io.cr, cli.cr # I/O、命令行 -│ │ ├── math.cr, toml.cr # 数学、TOML 解析 -│ │ ├── arena.cr # Arena 分配器 -│ │ ├── scheduler.cr # Fiber 调度器 -│ │ ├── chan.cr # 缓冲通道 -│ │ └── collections.cr # 集合操作 -│ └── runtime/ # 运行时(rt.s bump allocator + 系统调用) -├── tests/ # 测试 -│ ├── bootstrap/ # 引导编译器流水线测试 -│ └── selfhost/ # 自举编译器测试 -├── tools/corec # Python CLI 工具 -├── build/corec # 自举编译器原生 x86-64 二进制 -├── grammar/ # EBNF 语法定义 -├── docs/ # 设计文档 -└── PKGBUILD # Arch Linux 打包 -├── spec/ # 形式规约文件(.corespec 示例) -├── examples/ # 示例程序 -└── README.md -``` +- Self-hosting compiler with a reproducible three-stage bootstrap +- Native x86-64 ELF generation without an external assembler or linker +- Semantic graph IR with explicit regions, state edges, and provenance +- Graph → lattice → target encoding compilation model +- Raw pointers with compile-time pointer, region, and provenance analysis +- Arena-based memory management tied to program regions +- Cooperative fibers and channels +- Function-level incremental compilation through `.cir` snapshots +- Hardware Interface Tables for target-specific operations +- Generics, interfaces, enums, pattern matching, tuples, arrays, and slices +- Specifications use Core syntax; the verification backend is still under development ---- +## Design -## 运行测试 +The **graph** represents computation and program relationships. The **lattice** +layer represents state and storage mappings. **Target encodings** map those +semantics onto a concrete execution model. Type, provenance, and specification +information remains available in the semantic IR through lowering, allowing +verification and optimization to operate before final target encoding. -```bash -# 引导编译器流水线测试(19 项) -python3 tests/bootstrap/test_pipeline.py +See the [Project Book](docs/project-book.md), +[Execution Model](docs/maintainer/design/execution-model.md), and +[Memory Model](docs/maintainer/design/memory-model.md) for the full design. + +## Status -# 泛型测试 -python3 tests/bootstrap/test_generics.py +Core is under active development and not ready for production use. -# 借用检查测试 -python3 tests/bootstrap/test_borrow.py +- The bootstrap chain is stable: the compiler builds itself in three stages, and + stages 2 and 3 are byte-identical. +- The specification layer is designed but not implemented; the verification + backend is a placeholder. +- Language, standard library, and tooling gaps are tracked in [TODO.md](TODO.md). -# 自举编译器测试 -python3 tests/selfhost/test_compile.py +## Building + +```bash +python3 build_selfhost_native.py ``` -### 通过 Python 调用流水线 - -```python -import sys -sys.path.insert(0, 'bootstrap') -from corec.frontend.lexer import Lexer -from corec.frontend.parser import Parser -from corec.frontend.name_resolver import NameResolver -from corec.frontend.desugar import MatchDesugarer -from corec.frontend.type_checker import TypeChecker -from corec.frontend.ir_gen import IRGen -from corec.backend.interpreter import Interpreter - -src = ''' -fn add(a: int, b: int) -> int { return a + b; } -fn main() -> int { return add(3, 4); } -''' -lex = Lexer(src) -ast = Parser(lex.tokenize()).parse_compilation_unit() -resolver = NameResolver(); resolver.resolve(ast) -desugarer = MatchDesugarer(resolver.symtab) -ast = desugarer.desugar(ast) -checker = TypeChecker(resolver.symtab); checker.check(ast) -ir_gen = IRGen(resolver.symtab); mod = ir_gen.gen_module(ast) -interp = Interpreter(mod); print(interp.run('main', [])) # 输出 7 +This produces `build/corec` (frontend) and `build/corearch` (backend). + +## Usage + +```bash +./build/corec run 'fn main() -> int { return 42; }' # execute code (interpreter) +./build/corec check FILE.cr # type-check only +./build/corec build FILE.cr -o OUT --static # compile to a native ELF binary +./build/corec cir FILE.cr # dump the dataflow graph +./build/corec ccr FILE.cr # dump the lattice-form IR +./build/corec clean-cache # clear the incremental cache ``` +## Documentation -## 贡献 +- [Project Book](docs/project-book.md) — design and rationale +- [Developer documentation](docs/developer/) — syntax reference and internals +- [Execution Model](docs/maintainer/design/execution-model.md) · [Memory Model](docs/maintainer/design/memory-model.md) +- [Error codes](docs/developer/errors.md) -当前项目处于早期阶段,欢迎参与设计讨论和实验性实现。 -请参阅 docs/ 下的设计文档了解整体构想。 +## Contributing ---- +The project is at an early stage; design discussion and experimental +implementation are welcome. See `docs/` for the current design documents. -## 许可 +## License -GNU General Public License v3.0(含 GPLv3 第 7 节附加许可) +GNU General Public License v3.0 (with the GPLv3 §7 additional permission). diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..5c7e64d9 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,100 @@ +# Core 编程语言 + +[English](README.md) | **中文** + +![self-hosted](https://img.shields.io/badge/self--hosted-yes-blue) +![target](https://img.shields.io/badge/target-x86--64-lightgrey) +![license](https://img.shields.io/badge/license-GPLv3-blue) +![status](https://img.shields.io/badge/status-experimental-orange) + +Core 是一门围绕**语义图 IR** 构建的实验性编程语言。编译器自举,直接发射原生 +x86-64 ELF,并在整个 IR 中保留 region、状态关系、provenance 等程序语义,而不是 +过早把它们归约成传统的机器模型。 + +Core 的编译模型围绕 **graph → lattice → encoding** 组织:计算先表示为关系, +状态/存储单独映射,机器表示最后落地。 + +```core +import io; + +fn fib(n: int) -> int { + if n < 2 { + return n; + } + return fib(n - 1) + fib(n - 2); +} + +fn main() -> int { + io.println_i(fib(10)); // 55 + return 0; +} +``` + +Core 仍在积极开发中。 + +## 特性 + +- 自举编译器,三段**可复现**自举 +- 原生 x86-64 ELF 直接发射(不依赖外部汇编器或链接器) +- 语义图 IR:显式 region、状态边、provenance +- graph → lattice → target encoding 编译模型 +- 裸指针 + 编译期指针 / region / provenance 分析 +- 与程序 region 绑定的 arena 内存管理 +- 协作式 fiber 与 channel +- 基于 `.cir` 快照的**函数级**增量编译 +- 面向目标特定操作的硬件接口表(HIT) +- 泛型、接口、枚举、模式匹配、元组、数组、切片 +- 规约采用 Core 语法;验证后端仍在开发中 + +## 设计 + +**图**承载计算与程序关系;**格**层承载状态与存储映射;**目标编码**把这些语义映射 +到具体执行模型。类型、provenance 与规约信息在**下降过程中始终保留在语义 IR 里** +(`.cir` 是验证的消费面,`.ccr` 是格层存在结构的线性投影),因此验证与优化可以在 +最终目标编码之前进行。 + +详见 [Project Book](docs/project-book.md)、 +[Execution Model](docs/maintainer/design/execution-model.md) 与 +[Memory Model](docs/maintainer/design/memory-model.md)。 + +## 状态 + +Core 仍在积极开发中,尚不可用于生产。 + +- 自举链稳定:编译器三段自举,且**第 2 段与第 3 段逐字节相同**。 +- 规约层已设计但未实现;验证后端目前是占位。 +- 语言、标准库与工具链的缺口记录在 [TODO.md](TODO.md)。 + +## 构建 + +```bash +python3 build_selfhost_native.py +``` + +产物:`build/corec`(前端)与 `build/corearch`(后端)。 + +## 用法 + +```bash +./build/corec run 'fn main() -> int { return 42; }' # 直接执行代码(解释器) +./build/corec check FILE.cr # 仅类型检查 +./build/corec build FILE.cr -o OUT --static # 编译为原生 ELF 二进制 +./build/corec cir FILE.cr # 导出数据流图 +./build/corec ccr FILE.cr # 导出格形态 IR +./build/corec clean-cache # 清除增量缓存 +``` + +## 文档 + +- [Project Book](docs/project-book.md) —— 设计与理由 +- [开发者文档](docs/developer/) —— 语法参考与内部实现 +- [Execution Model](docs/maintainer/design/execution-model.md) · [Memory Model](docs/maintainer/design/memory-model.md) +- [错误码](docs/developer/errors.md) + +## 贡献 + +项目处于早期阶段,欢迎参与设计讨论与实验性实现。现有设计文档见 `docs/`。 + +## 许可 + +GNU General Public License v3.0(含 GPLv3 第 7 节附加许可)。 From 9b13e43712857eaf2fe66c22d47dd9ebbd660121 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Wed, 16 Sep 2026 00:11:10 +0800 Subject: [PATCH 014/138] =?UTF-8?q?fix:=20TODO=20#23=E2=80=94=E2=80=94?= =?UTF-8?q?=E7=A6=81=20git=20=E9=92=A9=E5=AD=90=E5=8A=A0=E5=9B=BA=EF=BC=88?= =?UTF-8?q?=E8=AF=8D=E8=BE=B9=E7=95=8C=E6=89=AB=E6=8F=8F=EF=BC=9B=E5=A0=B5?= =?UTF-8?q?=E5=A4=8D=E5=90=88/=E7=AE=A1=E9=81=93/=E5=AD=90=E5=A3=B3/?= =?UTF-8?q?=E5=8C=85=E8=A3=85=E7=BB=95=E8=BF=87=EF=BC=9B=E5=90=AB=E8=AF=AF?= =?UTF-8?q?=E4=BC=A4=E8=B4=9F=E6=8E=A7=EF=BC=89+=2036=20=E4=BE=8B=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=20+=20=E6=8C=82=20CI=20(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原实现只拦「整条命令以 git 开头」⇒ `cd x && git …` / `echo hi; git log` / `(git …)` / `bash -c "…"` / `$(…)` / `sudo …` 全部放行(铁律 #2 的机械拦截有洞)。 修法:先剥离 heredoc 体(脚本体是数据),再用 shlex(punctuation_chars,**尊重引号**) 分词,按分隔符 token 切段,每段跳过前导(环境赋值 / 前缀命令 / shell 名 / 旗标 / 纯数字) 后看**首个命令词**是否为 git(含 /usr/bin/git);另递归扫 bash -c / sh -c 内联脚本。 实现期自查抓到两处真缺陷(首版不尊重引号 ⇒ 提交信息/数据里的 git 文本被误拦): - 引号/heredoc 内的 git 文本必须放行 ⇒ 改为 shlex 分词 + heredoc 剥离 - `$(` 是单 token ⇒ 把 `$` 计入分隔符集 - 换行被 shlex 当空白吃掉 ⇒ 移出 whitespace 并计入标点(多行命令的行首调用不再漏拦) 判据 tests/harness/test_block_git.py:**36 例(BLOCK 20 / ALLOW 16)⇒ 实测 36/36** - BLOCK:裸/带路径/&&/;/|/||/&/子壳/$()/反引号/bash -c/sh -c/sudo/nice/env/xargs/timeout/ 嵌套/heredoc 之后有真调用 - ALLOW:jj git …(git 非命令词)·echo git·grep git·.gitignore 族·引号内非命令位· **提交信息含 git 文本**·**heredoc 体内的 git 文本**(两条为误伤负控) - 已挂 CI bootstrap-tests job(src/ci/run.sh 一行;本地 CI_JOB_NAME=bootstrap-tests 实跑 rc=0) 失败模式有意为「放行」:hook 自身异常 exit 0 —— 宁可漏拦,不可把会话卡死。 同提交更新 TODO #23(登记 → ✅ 修复 + 判据 + 失败模式)。 --- .claude/hooks/block-git.py | 150 +++++++++++++++++++++++++++++--- TODO.md | 10 ++- src/ci/run.sh | 2 + tests/harness/test_block_git.py | 101 +++++++++++++++++++++ 4 files changed, 247 insertions(+), 16 deletions(-) create mode 100644 tests/harness/test_block_git.py diff --git a/.claude/hooks/block-git.py b/.claude/hooks/block-git.py index 097969f1..b57e4fd6 100644 --- a/.claude/hooks/block-git.py +++ b/.claude/hooks/block-git.py @@ -1,16 +1,142 @@ #!/usr/bin/env python3 -"""铁律 #2 机械执行:Bash 命令以 git 开头 → 拒绝执行(exit 2)。""" +"""铁律 #2 机械执行:命令串中出现 git **调用** → 拒绝执行(exit 2)。 + +加固(2026-09-16,TODO #23):原实现只拦「整条命令以 git 开头」, +`cd x && git …` / `bash -c 'git …'` / `… | git …` / `$(git …)` 全能绕过。 + +现覆盖: + · 以 git 开头(原行为;含 `/usr/bin/git` 等带路径形态) + · 分隔符之后:`;` `&&` `||` `|` `&` `(` `)` 换行 + · 命令替换 / 子壳:`$(git …)`、反引号、`(git …)` + · shell 内联脚本:`bash -c '…'` / `sh -c "…"`(递归扫描,深度 ≤ 3) + · 前缀命令之后:sudo / env / nice / nohup / time / exec / command / xargs / + timeout / stdbuf / setsid / ionice / cpulimit / watch(含其旗标与数字参数) + +**不得误伤(同为零容忍:误伤会让人绕开 hook)**: + · `jj git push`(git 不是命令词) + · **引号内**的 git 文本:`jj commit -m '… git log …'` · `echo "use git"` + · **heredoc 体内**的 git 文本(`<<'PY' … PY`;脚本体是数据不是命令) + · `grep -n git` · `find . -name .gitignore` · `cat .gitignore` + +实现要点:先剥离 heredoc 体(按行扫,遇 `<<[-]?WORD` 丢弃到终止行), +再用 `shlex`(`punctuation_chars`,**尊重引号**)分词,按分隔符 token 切段, +每段跳过前导「环境赋值 / 前缀命令 / shell 名 / 旗标 / 纯数字」后看**首个命令词**。 + +失败模式:hook 自身异常 **一律放行**(exit 0)—— 宁可漏拦,不可把会话卡死。 +""" import json +import re +import shlex import sys -data = json.load(sys.stdin) -cmd = data.get("tool_input", {}).get("command", "") -stripped = cmd.strip() - -if stripped == "git" or stripped.startswith("git "): - sys.stderr.write( - "铁律 #2:全面使用 jj,禁止 git。\n" - "改用 jj 命令:jj status / jj log / jj describe / jj git push / jj bookmark ...\n" - ) - sys.exit(2) # PreToolUse exit 2 = block the tool call -sys.exit(0) +PREFIX_CMDS = { + "sudo", "doas", "env", "nice", "nohup", "time", "exec", "command", "xargs", + "timeout", "stdbuf", "setsid", "ionice", "cpulimit", "parallel", "watch", +} +SHELLS = {"bash", "sh", "zsh", "dash", "ksh", "fish"} +MAX_DEPTH = 3 +PUNCT = "();|&<>`$\n" +SEP_CHARS = set("();|&`$\n") # 切段用;< > 是重定向、不是命令分隔符 +ENV_ASSIGN_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=") +HEREDOC_RE = re.compile(r"<<-?\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\1") +INNER_RE = re.compile( + r"""(?:^|[;&|(\s])(?:bash|sh|zsh|dash|ksh|fish)\s+(?:-\S+\s+)*-\S*c\S*\s+""" + r"""(?P['"])(?P