Repository navigation
feat(v4): 目标语法迁移(批内 T0–T7) - #178
Merged
Merged
Conversation
基底问题(本轮探明的根因):本地那堆 v4 改动原在 dbadb8d 上(落后 develop 24 提交、 缺 104 档文件——其中恰含 develop 已合入的施工计划与 T0 布局规格),所以「改了一大半 v4 语法」却看不到计划。本笔把堆 rebase 到 develop(920b1cbb)。 - 前端(T1/T2):bootstrap/corec 取 feature/v4-migration-t1t2 的**计划版**实现—— 39 关键字 + NEWLINE/INDENT/DEDENT 发射 + parser 吃 INDENT…DEDENT(取代 {}), 随附判据载体 tools/v4_frontend_probe.py(94 条,含正反两态);唯一 FAIL 是 J-T1-2「两套前端关键字一致」= 批末判据(自源侧属 T5)。 本地那套「双形并存」实现退居 bookmark wip/v4-local-stale-base(bb8cb353)备查。 - 自源面:src/compiler 恢复 develop 版(本地双形自源改动按计划留待 T5 重做)。 - T3:build_selfhost_native.py 的 wrapper 与 tests/selfhost/test_compile.py 的内联源 改 v4 形(`:` + layout)。(tools/build_stdlib.py 无 wrapper 行,计划该格不适用。) - 文档:48 档冲突逐档合(事实/结构/引用以 develop 为准,本地较新的事实与风格保留) ⇒ 0 冲突;另恢复被堆清空的 docs/superpowers/plans/2026-07-30-lazy-eval.md(develop 186 行) 并删两枚 .tmp 原子写残骸。 - 判据现状:T1/T2 探针 94 条(93 PASS);tests/bootstrap 全红 = (乙) 预期 (料语料仍 v3);src/** 语料未动(花括号 14,962 / `;` 18,663)⇒ T4 起点。
工具(一次性迁移工具,非常设判据):tools/v4_migrate.py + tools/v4_migrate_v3lex.py(vendored v3 扫描器)
## 结果(92 档 = src/**/*.cr 全量)
- **85 档已迁移**(内容改变)· **6 档 no-op**(本就无 {} / ;,v4 形与 v3 形逐字节同:
compiler/_import.cr · linker.cr(0 字节) · regalloc-consistency.cr · lsp/_import.cr ·
stdlib/_import.cr · targets/x86_64-linux/_import.cr)· **1 档未迁移**(compiler/elf.cr)。
## 判据读数
- **V1(v4 前端解析,最强)**:91/92 过;唯一挂的恰是未迁移的 elf.cr。我**独立复跑**确认同一读数。
三条反向突变(region 头缩进错 / 删 `:` / sibling 错位)全红 ⇒ J-T4-3 由 V1 直接兜住。
- **V2(只改形状的证明)**:76 档**逐 token(类型名, 词素)完全相同**(我另抽检 fmt.cr 1507 token /
checker.cr 21361 token 独立复核 = IDENTICAL);9 档的差异**恰为**本笔裁定的显式改写,
逐条 difflib 可读:dyn_arr 1 · parser 7 · ld 14 · cli 4(unsafe 上提 + return 补写)·
interp 4 · main 1 · module 1 · opt 1 · provenance_verify 1(空 region 裁决,全是纯 delete)。
口径补正(采纳):不能一把抓全丢 `;`/`{}` —— `[T; N]` 的 `;` 与 struct 字面量的 `{}` 必须保留;
v4 侧的块开是 `COLON_BLOCK`,也须列入丢弃集。首轮不做此区分时假红 701/38 项。
- **V3(计数)**:`{`/`}` token 级 11,378 → **1**(只剩 src/stdlib/toml.cr:268 的 struct 字面量);
`;` 29,556 → **16**(全是 src/stdlib/collections.cr 的 `[T; N]`)。
- **V4a 注释行:10,289 → 10,636(↑,0 档下降)· V4b 空行:2,583 → 2,596(↑,0 档下降)**。
⇒ V4b 是本笔**新增的判据面**:我抽检 fmt.cr 时抓到空行被吞(17→6,`}` 行消失时把相邻空行一起吃了),
要求修(根因两条:`_plain_gap` 连续换行吞行 + EOF 收尾少一个 `\n`)并扩成 A/B 双读数。
## 本笔裁定(附理由,逐条留痕)
- **空 region 12 处 / 7 档**(计划只记 3 处——「只含注释的块」与 `{ }` 在 token 面不可区分):
5 处按规则自处理(无 else 且条件纯局部量的 `if … { }` 整条删;只含注释的函数体补匹配返回类型的显式 `return`);
7 处逐条裁定后全批(if/else-if 链整条删 · `if str_len(…) > 0 { }` 整条删(str_len = fmt.cr:4-8 纯读取,
我实核)· 空 `else` 从句单独摘除)。
**已知偏差**:main.cr:21 / module.cr:172 删掉的条件各含一次内存读 ⇒ 无效指针下原式会陷阱而删后不会;
列为 B1/B2 建起来后的重点核对项。
- **`unsafe { … }` 作值 10 处 / 3 档**(ld 6 · cli 2 · parser 2):v4 里 unsafe 是 region(语句位),
实参位是表达式位 ⇒ 原式非法。前端加「**region 作声明右值**」后机械上提临时量(10 处全为单个
`@str_of`/`@ptr_of`),求值次数与副作用次序逐条论证保持。
- **elf.cr 不迁移**:死文件(三面清单皆无)+ v3 下也解析不过(86/375/380/388 行各缺一个 `)`)+ J4 载体。
## 前端与文档的配套改动(本笔)
- `bootstrap/corec/frontend/parser.py`:`_at_line_end()` 增**第三态**「prev == DEDENT 即行边界」
⇒ `x := unsafe: …` 这类以 region 收尾的声明可解析(实测 OK)。
- `docs/core-language-syntax_v4.md` §4.1:新增「region 作声明右值」(含反向不成立:`f(unsafe: …)` 非法)。
- `docs/maintainer/design/v4-layout-spec.md` §10:T0 补遗三节(括号外续行非法 · 空 region 无法表达 +
裁定与留痕 · region 作右值),逐条带施工期实测。
- `tools/v4_frontend_probe.py`:修 `selfhost_keywords` 抽取(原按 `\n}\n` 切块,v4 形下当场 ValueError)。
- `docs/superpowers/specs/2026-09-11-policy-injection-design.md:439`:`缺口` → `口子`(避免与该文
其余 5 处「缺口 = 缺失项」撞义)。
## 批内位置
T0 规格 ✓ · T1/T2 ✓(探针 94 条)· **T3 ✓** · **T4 ✓(本笔)** · T5 自源 parser · T6 其余语料 · T7 收尾重锁。
⇒ 此刻 tests/bootstrap 仍全红、自举链仍断 —— 这是 (乙) 的**预期中间态**(前端已硬切,自源尚未跟上)。
## 问题(上一笔 T4 暴露的语义漂移)
T4 把语料从 v3(`{}` + `;`)改写成 v4(`:` + layout)后,全量构建虽过(BUILD SUCCESS),
但 checker 刷出 **450 条 `Function …: expected return type …` + 308 条 `If branches have
different types`**。根因:v4 没有 `;` ⇒ 语句末的裸表达式被当成 **region 尾值**(`Block.expr`),
而 v3 里它是 `ExprStmt` ⇒ 块类型改变 ⇒ 函数推断返回类型改变。
**token 级判据(T4 的 V2)天然看不见它**(token 流完全相同,差的是 AST 形状)。
## 修法:位置语义
依据 = v4 目标语法 §7.4(函数无隐式尾返回)+ §4.1(region 作声明右值取尾值)+ **v3 逐形等价**:
- **表达式位**(声明/赋值右值、`return` 的值、调用实参、索引、括号内、match 臂体等,
统一走 `_want_value_expr()` 入口)⇒ **保留尾值**。
- **语句位**(`parse_stmt`、函数体、`loop`/`while`/`for` 体)⇒ 末项裸表达式包 `ExprStmt`;
**例外(v3 等价)**:**块状末项**(`If`/`Match`/`Loop`/`For`/`Unsafe`/`Flow`)**保尾值**——
v3 的 `;` 只终止**普通**表达式语句,块状语句从不带 `;`,它本来就是 v3 的暂定尾表达式。
(对块状语句说「等价 v3 带 `;`」是凭空补一个 v3 没有的 `;`。此条是实施者发现「我给的规则①
与我列的验收③互相矛盾」后**停下上报**、由我裁定采纳的。)
## 读数(均实测;我对主判据做了独立复跑)
- **两类类型告警 758 → 20**(`expected return type` 450→7 · `If branches` 308→13);
对 (单元, 消息) 多重集做差:**新增 0 条**。残余 20 条根因与尾值无关(`loop:` 尾项被判 unit、
`ReturnStmt` 被判其值类型),v3 同报。
- **三产物 sha256 与改前逐字节相同** ⇒ **纯判定面修复、发射面零变化**(T4 语料语义未动)。
- 全量构建 `=== BUILD SUCCESS ===` + 三条 `[GUARD] error[ = 0`。
- **T1/T2 探针 94 条 / 1 FAIL**(唯一 FAIL 仍是批末判据 J-T1-2)——**我独立复跑确认**。
- 四格对照(我独立复跑):v3 if尾随 0 / v3 赋值尾随 1 = v4 if尾随 0 / v4 赋值尾随 1 ⇒ 逐格等价。
- 反向对照:改前 parser 在同串上报 `If branches have different types` ⇒ 判据非空转。
## 本笔另含(T4 收口)
- **空行收拢(按删除缝)**:裁定删掉的整条 `if … { }` 在同一 region 内留下的连续 ≥2 空行收成 1。
**只收 3 处**(main.cr · module.cr · interp.cr)——先测出语料本就有 **11 档**存在「连续 2 空行」的
**原有写法**,全局收拢会平白吃掉原文空行、让 V4b 假性下降。反向对照:≥2 空行多重集的变化档数 = 0。
- `tools/v4_migrate.py`:按缝收拢逻辑 + `V4B_BLANK_DROP_EXEMPT` 常量(注释写明**应为空集**)。
- `docs/core-language-syntax_v4.md` §4.1:`if` 作右值的例子**降为设计态/未实现**——实测
`total := if ready: … else: …` 能解析能过检,但发射面丢值(`ir_gen.py` 的 `gen_if` 无条件
`return None`;**既有缺陷**,改前 parser 同样复现)。可运行的是 `t := unsafe: <E>` 形。
## 另案(未动,已登记)
- `tests/bootstrap/{test_pipeline,test_borrow,test_generics}.py` 三档红 = 内联源码仍是 v3 形
(改前/改后 parser 对 7 档归一化输出逐条相同 ⇒ 与本次改动无关)⇒ 属 **T6** 迁移面。
- 块值 `if` 的发射面缺口(上条 §4.1 注)⇒ 另开 TODO。
## 批内位置
T0 ✓ · T1/T2 ✓ · T3 ✓ · **T4 ✓**(+ 本笔位置语义闭合)· **T5 待起** · T6 · T7。
纯注释改动(行为与 4ad482d 逐字节一致:探针 94/1 复跑确认)。 - 规则①②③在 __init__ 里按最终口径重述:语句位控制流尾项保留 Block.expr,其余包 ExprStmt; 表达式位一律保留尾值;收尾由「位置 ∧ 种类」共同决定。 - _is_block_like 的 docstring 补齐依据(v3 里控制流语句本来不带 ; ⇒ 本来就是块尾值; 真正分叉的只是「带 ; 的表达式语句」),并留下「一律包 ExprStmt ⇒ 探针 94/5 转红」的反向实证, 防止后人改回字面形。 - Flow 保留在集合内(同为开 region、以 DEDENT 收尾的块状形;实测 src/ 里 `flow:` 语句命中 0 ⇒ 零影响)。
两条均为 v4 迁移施工期产出,**只登记未修**;迁移对照表两行旧号列写 `—`。
- **#2026-10-11-1**:维护者裁定 `#` 族**只有使用面** ⇒ `#tag`(全族里唯一「用 # 声明新东西」的构造)属**范畴错误、该删而非改名**;
连带两条结构性毛病(体是竖排序列而语义是**集合**=形状说谎;可复用名被关进标签族、不能 import/文档化/单测)。
两个候选方向(未拍):**不留** = §10.8 整节删、复用只走库 + `spec fn`;**留** = 必须长成一个**非 `#`** 的声明形。
卡住的面:`docs/core-language-syntax_v4.md` §10.8 + `docs/maintainer/design/spec-design.md:203`(旧形 `#tag sorted = { … }`,同面两形不一致)。
- **#2026-10-11-2**:`x := if c: 1 else: 2` 能解析能过检但**运行期得 None**;根因 = `bootstrap/corec/frontend/ir_gen.py` 的 `gen_if` **无条件 return None**。
**既有缺陷、与迁移无关**(同片段走改前 parser 同样得 None);旁证 = `t := unsafe:` + `7` ⇒ 运行期 7(region 作右值这条通道是通的)。
⇒ v4 文档 §4.1 的 `total := if ready: …` 例子已降为「设计态/未实现」并注明根因。
判据:`python3 tests/harness/test_todo_id_migration.py` → **6/6 PASS**(J1 残留 0 · J2 悬空 0 · J3 映射完备 153/153 · J6 同日段唯一)。
## 交付(12 档,+1163 / −232) - **`src/compiler/ast.cr`**:token 常量 `T_NEWLINE=103 / T_INDENT=104 / T_DEDENT=105 / T_COLON_BLOCK=106 / T_COLON_DECL=107`(**既有 0..102 一个未动**,从空洞外起)+ `T_NEVER=108 / T_CATCH=109 / T_FAIL=110 / T_SPEC=111 / T_PIPE=112`;`dyn` 复用既有 `T_DYN=99`(该号此前零发射点);解析器码 `EC_P_STRUCT_LIT_SEP = 1030`。 - **`src/compiler/lexer.cr`**:布局机件(缩进栈 / `g_bracket_depth` / `layout_skip_blank` / `colon_at_line_end` / `layout_break` / `finish_layout`;更浅且不匹配任何外层列 ⇒ **响亮报错**,不静默对齐); 关键字表 **36 → 39**(去 `None`/`Some`/`move`,加 `catch`/`dyn`/`fail`/`never`/`none`/`spec`); `tokenize(_src, layout_bypass)` 旁路改**显式形参**(使 call site 原文即判据载体)。 - **`src/compiler/parser.cr`**:region 产生式 + 位置语义(`g_want_value`)+ 行边界三态 + struct 字面量分隔符 **盲跳 → 校验 `=`**(未选定形响亮 `P030`)+ `match` 臂 `|` 前缀两形都收 + 删 `T_MOVE` 分支。 - **`never` 语义(两前端同改)**:以**发散语句**收尾的路径(`return`/`break`/`continue`)其类型 = `never` (不再是「所返回值的类型」),且 `never` 与任何类型相容;落点在**组合函数层** (`bootstrap/corec/frontend/type_checker.py` 的 `_compat` / `src/compiler/checker.cr` 的 `type_compat_strict`)。 - **`tokenize()` 调用点逐处复核(J-T5-1)**:实算 **10 处**(裸 grep 13 − 定义 1 − 注释 3 = 9,+ `lsp/lsp.cr:179`; 计划 §7.2 的「12」是旧口径,T0 §5.4 已更正为 9/10)。**唯一需要旁路的是 `parser.cr` 的 `interp_parse_holes`** (洞是从源切出的表达式、无 layout 上下文;不旁路会伪造缩进状态并改写外层缩进栈)。其余 9 处**不旁路**, 逐处给出理由(其后都有 `res_imports`/`parse_all` 等 layout 消费者)。 ## 判据(我独立复跑主门) | 门 | 读数 | |---|---| | **J-T5-3** `./build/corec clean-cache` + `check src/compiler` | **rc=0 · 零诊断 · `ok`**(改前 12638 条诊断)——**我自跑复核** | | 同一门 CI 形态 `CI_JOB_NAME=check src/ci/run.sh` | rc=0 | | **J-T5-2** `corec2`/`corec3` 两次构建 + `cmp` | **IDENTICAL**——**我自跑复核**(corec2 编 arith 运行 = 110) | | **T1/T2 探针**(含新增 `positions` 52 条) | **146 条 ALL PASS**——**我自跑复核**(J-T1-2 真绿:两表皆 39) | | J-T0-5 零构建半 | `tools/v4_layout_probe.py tokenize-sites --require-bypass` ⇒ rc=0 · YES | | 两套前端诊断集一致 | 35 条差分样例逐条同判 | | v3↔v4 逐字节等价 | 10 组同程序对(v3 文本走代 0 vs v4 文本走 T5)**10/10 输出逐字节同** | | 反向对照 | ① 代 0 读 v4 ⇒ 4/4 rc=1 零产物;② 破坏「行尾 `:`」判定 ⇒ 3/3 BUILDFAIL;③ 摘洞内旁路 ⇒ `P27`;④ 撤 `never` 相容 ⇒ 两前端各自把诊断**全部打回**(自源 `ICE04` / bootstrap 两条齐回) | **语料一字未动** —— 裁定的 `never` 语义把 2 条 `TC02` 全部消解,故「改 `main.cr` 语料」的候选作废。 (`main.cr` 本笔的两处改动是 `tokenize(g_source, 0)` 的**签名扩展**,非语义改动。) ## 施工期挖出的两条(前者已登记另案,后者见 TODO) 1. **改前 12638 条诊断的头号根因 = 「把注释的列当成 anchor」**:`ast.cr` 里 `TY_DEX_S … // 注释续行` 处 伪造出 `INDENT(22)`。⇒ 布局扫描器必须**先吃空白+注释再判行首**(纯空行/纯注释行成 no-op;T0 §1.1 判定①同面)。 2. **`never ⊆ X` 在类型项引擎里是未覆盖面**:`ty_sub(never, named)` 返回 **-1** ⇒ `error[ICE04] (uncovered face)`。 本批只在**组合层**兑现语义 ⇒ **两层对「`never` 作子类型」的认定当前不一致**(组合层收、引擎层 -1)。 当前 `src/**` 零命中(三绿即证),但属**潜在缺口**,另案登记。 3. **两套前端的 block-like kind 清单本就该不同**:自源侧 `EXPR_WHILE` 是独立 kind(Python 侧 desugar 成 `Loop`), `Flow` 在自源侧不是表达式节点 ⇒ 判据锚在**语义**(v3 逐形等价),**不锚 kind 名**。已写进 `is_block_like` 头注并注明 「禁止为『两边清单长得一样』而增删」。 ## 另案(未动,逐条留痕) - `fail`/`catch` **两套前端均无产生式** ⇒ 本批「发散出口」只覆盖 `return`/`break`/`continue`(计划 §十.5 定其语义属另一批)。 - **bootstrap 的 `none` 拼写面**:`src/**` 真实使用 0 处;但 `tests/bootstrap/test_pipeline.py` 的 `'V4 lowercase none'` 用例与 `tests/selfhost/test_compile.py:179` 真用 ⇒ 自源 rc=0/运行 42 ✓、bootstrap `Undefined name: none` ✗ ⇒ 已固化进判据档的真实分歧,另行处置。 - `tests/selfhost/`(78 档)2 绿 / 76 红,**逐档归因 = 内联语料仍是 v3 形**(T6 面);`tests/bootstrap/*.py` 同因。
…;组合层与引擎层认定不一致(v4 T5 施工期实名,本批只在组合层兑现,未碰 ty_sub) 判据:`python3 tests/harness/test_todo_id_migration.py` → 6/6 PASS。
## 1. `none` 作值(T1/T2 面欠账)
`parse_primary` 的 `T_NONE`/`T_SELF` 合成一格 ⇒ **拆开**:`T_NONE` 归一到 canonical 零载荷变体
`EnumConstructor(['None'], [])`(与自源同语义)。
`NameResolver` 内建表**不需要**补 `none` —— 归一路径根本不经过名字解析,且补一个 `Ident('none')`
反而会让节点缺 `__variant`、断掉 desugar 臂比较与 `ir_gen` 的 `MakeEnum`
(**这是实施者对我的方案的一处正确更正**,我采纳)。
- 正:判据档原样源(`value := none` + `| none => 42`)⇒ resolver=[] · checker=[] · **运行 42**(与自源同值)
- 反:把该格改回「当标识符」⇒ `Undefined name: none` + `Comparison not allowed` 齐回
## 2. 基本类型 `T?` 的**语法面**
依据 = v4 文档 §3.2 原文(`value : int? = 5`)+ 两前端同语义。
`parse_type` 收尾结构重排:`never`/`dyn`(关键字令牌)· `base_types` · `unit` · `PathType`+泛型实参
**四族并入同一条 if/elif/else 链**,链后**唯一一个** `?` 处理点(四族共用)。
选「并入同一条尾部」而非「早退前再写一份 `?`」= 避免同一判定两份实现(T0 §2.3 明令)。
- **接受矩阵 10/10 与自源逐格一致**(`int?`/`dex?`/`bool?`/`string?`/`char?`/`unit?`/`never?`/`dyn?`/
`Self?`/`Foo?`)——**我独立复跑**。中途只改 `base_types` 一族时,矩阵暴露 `never?`/`dyn?` 两格残余
(它们是关键字令牌、走另一条早退)⇒ 一并并入同链,残余清零。**这条是被矩阵逼出来的,不是看出来的。**
- 反:撤掉「`?` 唯一处理点」⇒ 四格全部复现原错
## 3. ⚠ 本笔**未达成**的部分(另案,非本笔范围)
四格现在**解析全过**但**check 全不过**:bootstrap 的 `type_checker.py` 里 `OptionalType` **0 处命中**
(`grep` 一命令可复现)⇒ bootstrap **完全没有 `T?` 的语义面**(无 `T ⊆ T?` 注入、无 `none : T?`、
无可选值 match 面)。那是把自源 R2 P3 Task 4 的可选表示机制(`TYP_NULL`/`TYP_OPTIONAL`/`g_optrep_on`/
`IR_MAKE_ENUM`/match 两分支面)搬到 Python —— **一笔独立的批**,不是「补几行」。
⇒ 本笔只兑现**语法面**;语义面登记另案(见 TODO)。
## 4. 实施者对我一处前提的更正(采纳)
我派活时写的「不修就卡 T6」**不成立**:T6 语料走**自源**编译器(`tests/suite/*.cr` 由 `./build/corec`
编译、`tests/selfhost/*.py` 驱动 `./build/corec`),只有 `tests/bootstrap/*.py` 驱动 Python 前端,
而该目录 `T?` 用量 **0**(实测 grep 空)。
⇒ 本修的**有效依据是另两条**(都成立):① 两前端同语义(本批硬判据);② v4 文档 §3.2 的目标形。
## 5. 复跑读数(我独立复核前三条)
`tools/v4_frontend_probe.py` **146 条 ALL PASS** ✓ · `clean-cache` + `check src/compiler` **rc=0 零诊断** ✓ ·
7 档 harness 机械门 **7/7 rc=0** ✓ · `T?` 形参位 10 格解析 **10/10** ✓。
## 6. 顺带实名一条**既有缺陷**(代 0 同病,但**会卡 T6**)
`enum E: V(int?)` **能编过但运行 rc=139(SIGSEGV)**;`/tmp/v3ws` 的代 0 编译器对 v3 形
`enum E { V(int?) }` **同样 139** ⇒ **非 T5 引入**。
⇒ 但 `tests/suite/opt_dex_test.cr:20` 逐字 `enum EOpt { V(dex?) }` ⇒ **T6 会撞上**。另案登记(见 TODO)。
…的枚举变体运行 SIGSEGV,代 0 同病,会卡 T6) 两条均 v4 迁移 T5 施工期实名,**只登记未修**;迁移对照表两行旧号列写 `—`。 - **#2026-10-11-4**:`grep -rn OptionalType bootstrap/` ⇒ type_checker.py 零命中 ⇒ bootstrap 的 `T?` **语法面已通、语义面全缺** (无 `T ⊆ T?` 注入、无 `none : T?`、无可选值 match 面)⇒ 四格解析全过、check 全不过。修法 = 搬自源 R2 P3 Task 4 的可选表示机制。**当前不阻塞**(tests/bootstrap 与 src 的 T? 用量皆 0)。 - **#2026-10-11-5**:`enum E: V(int?)` build rc=0 但 **run rc=139**;代 0(`/tmp/v3ws` @ 920b1cb)v3 形同样 139 ⇒ 既有缺陷。 **但会卡 T6**:`tests/suite/opt_dex_test.cr:20` 逐字 `enum EOpt { V(dex?) }`。 判据:`python3 tests/harness/test_todo_id_migration.py` → 6/6 PASS。
## 一、T5:六项「文档 vs 实现」冲突,全部按「以文档为准 ⇒ 改实现」落地(两套前端同改)
| # | 文档依据 | 内容 |
|---|---|---|
| ① | §10.1 | `#check`/`#ensure` 写在 **region 开头**(inline 保留为**兼容**,文档只写 region 头);含 `need_eol` 补漏(同行 `return` 被当第二条 region 项) |
| ② | §13.5/13.6/13.7 | **顶层前导 `@`** 注解(白名单 `hotpatch`/`ffi`/`preferredDomain`/`executionDomain`);笔误响亮报错 |
| ③ | §11.3/§13.7 | **`flow name(...)`** 文档形(`flow fn` 保留兼容;2×2 矩阵两格全绿) |
| ④ | §13.7 第三例 | **语句前导位 `@`**(`@preferredDomain(gpu)` 在 `loop:` 前);白名单 = **文档释文 + 语料实证**(含 `NoBoundsCheck`/`fast`/`unroll`/`section`),口径已写进**两处代码注释**并注明「禁止后人按章号再收窄」 |
| ⑤ | §1.6(新裁) | **region 可为空**(7 个 region 头位全覆盖);**运行时硬证**:空 `loop:` ⇒ 运行 rc=124(真死循环,`never` 语义不破);空函数体 ⇒ `TF01`(无尾值为真) |
| ⑥ | §7.3 | **`=>` 后开 region**(多行臂体);`=>:` 收为兼容(文档无此形) |
**反向对照累计 7 条**(flow 文档形 · `need_eol` · 语句位 `@` · `never` 相容 · 空 region · match 臂体 region · `=>:`),全部必红。
## 二、判据面:`never` 裁定 × 空 region 规格化的交汇(**四方互斥,见下**)
T5 用**六行实测表**证明:**硬门(`check src/compiler` rc=0 零诊断)与四方钉子在本设计空间内互斥**——没有一行同时满足。⇒ 裁决(维护者 2026-10-11「按 T5 的建议」):
- **(a) 位置规则保持现状** ⇒ **`nail2` 让**(`if 1<2: 1 else: "s"` 的 v3 `;`-less 语义在 v4 不可表达;三条救援路分别要 86/102/270 条诊断且破 `-> never` 惯用形);
- **(b) `return ⇒ never` 保留** ⇒ **`nail3` 让**,但**新增判定面**把它要回:`EXPR_RETURN` 处取**当前函数声明返回型**做 `type_compat_strict`(承载 = `g_cur_fn_ret_ti` 显式上下文)⇒ `test_tc02_branch` **13/15 → 14/15**(唯一剩红 = 已裁定让的 `nail2`);
- **(c) `TI_ERROR` 独立哨兵**(把「真 `never`」与「错误恢复」分家;收窄到 `infer_expr` 侧,`res_type_node` 全族不动)⇒ `nail4` **部分**:`q1/q2/q3` 如预期,`ta1` **仍未回归**(见三);
- **`nail1` 空-region 判别式**(`then_ti == TI_NEVER ∧ else 是零项 EXPR_BLOCK`)⇒ F1–F7 逐格如预期(含 **F2 反向钉**与 **F7 回归钉**),构造性不碰 `src/compiler`(谓词在那两处恒假)。
## 三、悬空账(**如实留档,不许人动**)
- **`nail4`「同一节点第二推断来源」**:判别探针(`d1`/`d2`)证实 `undef_x` 的 ti **有第二个来源**(`EXPR_IDENT` 的 `UNDEFINED→TI_ERROR` 未参与赋值位那条链);4 项已排除、3 条候选(`collect_decls` 预登记 / `ast_set_type_val` 回填 / `res_type_node` 族)**未验完 ⇒ 按裁定挂起**。
⚠ **代价已被量出**:3 档 `tests/probes/` **负例探针被静默接受**(`n05_recursive` rc=1`{TS03}`→rc=0 · `n10_named_vs_int`/`n21_t_assign_int` rc=1`{TA01}`→rc=0),同文本跨三次二进制读数差证「必在二进制侧」;且 **`probes_run.sh` 不在任何 CI job 里**(只在 `src/ci/run.sh:129` 的注释中被提及)⇒ **无自动门看着**。
- **`-> int` 返回 `bool`(新判定面暴露的既有偏差)**:检查闭包内 **3 处**(`type_engine.cr::lit_implies`)+ 闭包外 **8 处**(`src/kernel/**`,`build_kernel.py` 只编译不 lint)= **11 处 / 3 档**。语义上 = 本编译器「`bool ⊆ int` 可隐式」的既有约定 ⇒ **待维护者裁**(认可 ⇒ 豁免转长期项且应写进语言面;不认可 ⇒ 11 处源该改)。**当前代码保持豁免,只限这一对**。
- **不带码 `add_error` 被带码诊断遮蔽**(`main.cr:133-137` 的次序:`g_diag_count > 0` 先命中 ⇒ `print_parse_errors()` 永不到达);正控已证机制(唯一诊断不带码时才打印)。
- **`corec run` 包裹壳的 `;`-切分启发式**(`main.cr:356-391`,7 段内嵌源)——设计缺口,非字符串迁移。
## 四、T6 语料迁移(`.cr` 135 档 + 内联 Core 85 档 `.py` + 63 档 `.md`)
- **`.cr`**:已迁 **125** / no-op **5** / 未迁 **5**(`legacy_asm_backend`×4 按计划 §十 #10「不迁」+ `t2_neg_unclosed` 工具中止)。
**B1/B2**(pre = `920b1cbb`×v3 文本 · post = 钉版 sha `0e23a78a…`×v4 文本):① 严格等价 **113** · ③ 真空集 9(**不当绿灯**)· ④ 其余 13(未迁 5 · 改善方向 1 · **负例失效 3** · 二进制侧新增级联 3 · 代际面变化 1)。
**`E4/E6` 撤回**(用改语料绕前端缺口 = 把缺陷藏进语料)⇒ 待 T5 实现到位后 `opt_dex_test`/`apx_conversion_test` **双双回到严格等价**——**裁定的正确性由结果证明**。
- **内联 Core**:`.py` 绿档 **2/85 → 61/85**、**由绿转红 0**;`.md` 63 档**注释记号 1607→1607、空行 5829→5829 逐数不变**,384 块 0 违规;`rebuild.sh:71` 冒烟改 v4 形(实测 rc=42,**`[4/4]` 在 `PINNED_REV` 换代前必红 = 已知窗口态**)。
**构建窗口闭环**:`purity_selftest.cr` 23 段迁后 `tests/selfhost/test_purity.py` **rc=1 → rc=0**(改前实证 `expected ':' to open a region, got ''`)。
- **过渡形登记**:`E2`(`#check` inline)9 档 + `E3`(`@hotpatch` 并入声明行)1 档 ⇒ **目标形 = 文档形**,**翻转时点 = T7 窗口**(一次重建后两形都收)。维护者已同意。
## 五、其它
- `src/` 空 region 处置:**2 处回流**(`main.cr::read_source_file` · `module.cr::load_imports` —— T4 按旧政策整条删掉时**连带删掉一次内存读**,回流后消掉该「已知偏差」);**其余 10 处保留删除**(无副作用的空 `if`,删除等价且更干净)。**口径留档:判据 = 改动是否变了可观察行为。**
- `src/compiler/checker.cr` 本笔含 `TI_ERROR` 27 处 + `nail1` 判别式(逐行带理由注释)。
## 六、判据(我独立复核的那些)
`check src/compiler` **rc=0 零诊断** · `corec2 == corec3` **IDENTICAL** · `tools/v4_frontend_probe.py` **146 条 ALL PASS** · 7 档 harness **7/7**。
重锁四类对象(逐条实测,见 tools/baseline/ 头注留痕):
1/2 canary 5 值 + .ccr 四条 —— **实测零变化 ⇒ 不重锁**(D1 确定性 5/5 绿 + F3 5/5 同)。
归因:本批是前端形状迁移,不动发射器/后端;STR 段由被编译程序驱动而非编译器源排版。
反向对照:把 pa_ccr 植回 2026-09-20 旧值 ⇒ canary_check.sh --verify-only 必红(2 项 FAIL)。
3/4 二进制 sha 白名单 3 条 + PINNED_REV —— 97f4394(v3 末代) → 25e7019(v4 迁移 T6 收官)。
依据 = v4 可读性硬约束(旧 rev 只认 v3 形,rebuild.sh:71 冒烟已改 v4 形 ⇒ 旧 rev 下必红)。
自证:rebuild.sh 重建三 sha 与白名单逐条同 + 冒烟 42 + 与工作树自建二进制逐字节同。
旧 rev/旧三 sha 留痕(rebuild.sh 注 + REBUILD.md 历史表)。
过渡形翻转(与本次重锁同批):
E2 —— 9 档 tests/spec/*.cr 正例的 inline `#check` 改文档 §10.1 的 region 头形;
E3 —— tests/suite/hotpatch_test.cr 的 `@hotpatch` 改文档 §13.6 的前导 `@` 形。
翻转前后逐档 rc + 码集**逐格相同**;tests/selfhost/test_spec_grammar.py 48/48 绿。
收尾复跑(实跑读数):v4 探针 146 ALL PASS · check src/compiler rc=0 零诊断 ·
corec2 == corec3 IDENTICAL · canary 5/5 + carrier 7/7 · probes 29 档 20×0/9×1
(已知 3 档 rc 1→0:n05/n10/n21,checker 侧悬空账,非本面)· parity 76 档 43×0/33×1 两腿零差异。
判据面清理:parity_run.sh CORPUS_TOTAL=76 与实算一致(36+2+15+19+4);
REBUILD.md:77/82/130 的「75 档」加当时值标注 + 补 T7 自建基线分布;tests/spec/README.md 74→75 改 76。
已知遗留(预存,非本笔引入,T6 修订处同样红):tests/harness/{test_lsp_builtin_table,
test_criteria_mutations}.py 的锚点正则未随 v4 形状更新 ⇒ 各自 L1-L4 / M7-M10 红。
## 1. `tests/selfhost/test_string_interp.py`(**我们这批引入的真缺陷**,会让 CI 整 job 断)
- **根因**:`build_run()` 的 Core 载荷模板在 T6 内联迁移时被**机械折叠成单行**,**`%s` 前多了 4 空格**
⇒ `extra_body=""` 时生成 **8 空格缩进的 `return 0`**(比上一行更深)⇒ v4 parser 报
`error[P02]: unexpected dedent at top level`;另 `:130` 的 `extra_body` 仍是 v3 形带 `;`。**两处缺陷**。
- **归属**:`jj diff -r` 逐提交实核 ⇒ 仅落在第五笔 `25e70197`;干净 rev `920b1cbb` 模板**良构 v3**。
- **⚠ 严重性**:该档是 **`src/ci/run.sh:334` 的 `selfhost-tests` 挂点**,且 `run.sh:15` = **`set -euo pipefail`**
⇒ 该档 RC≠0 会**直接中止整个 `selfhost-tests` job**(不是单档红)。
- **修**:模板 `%s` 前**去掉那 4 空格**(恢复 v3 契约「`extra_body` 自带缩进」)+ `:130` 去 `;`。**期望值一字未动**。
- **读数**:`python3 tests/selfhost/test_string_interp.py` ⇒ **RC=0 · 失败项 [] · B 组 14/14 PASS**。
- **同款扫描**:全仓 `tests/*/*.py` 搜「`\n %s` 折叠模板」= **仅此一处** ⇒ 单点机械错误,非普遍现象。
## 2. `tests/harness/test_lsp_builtin_table.py` + 3. `test_criteria_mutations.py`(锚点抽取去 v3 形)
两档因 v4 形状而**抽取为空**(预存红,非新引入):
- `annotation_names()` 原以「首个顶格 `}` 行」为体边界 ⇒ **v4 函数无闭括号** ⇒ 抽到 `None` ⇒ L3 红;
- `lsp_at_names()` 原以 `return out;` 为界 ⇒ v4 无 `;` ⇒ **抽到空集**;
- M7–M10 的 11 个锚 + `mut_self_item2` 真跑腿锚 = v3 形(`{`/`;`)⇒ `str.count` 全 0。
**修法:只改「抽取」层,断言一字未动**;边界判据统一改走新增的 `top_level_body()`
(「`fn` 行之后首个**顶格行**」= v3 的 `}` 与 v4 的下一顶层构造同为**排他**边界,取出同一段体)。
**M7 真跑腿探针载荷**同步改 v4 形 + `None`→`none`(**先实测再改**:在未突变树先编译,确认 rc=0 且回 30 才落笔)。
## 判据(实跑)
- `tests/harness/*.py` **7 档全 RC=0**(`criteria_mutations` 27/27 含真跑腿开 · `lsp_builtin_table` 4/4)。
- **含牙证明**:L4-①~④ 逐条实跑;M1–M6 的 20 个突变体;**M7–M10 的「咬合证明」**(内存内撤掉该判据所描述的守卫 ⇒ 锚归 0)
⇒ 证明**锚与突变同处**(判据咬的正是它自称咬的那处)。
- `nice -n 19 ./build/corec check src/compiler` ⇒ **RC=0 / `error[`=0**(证明未动 `src/**`)。
## 同族登记(**不改**,附后人防误读标注)
- `tests/harness/test_lsp_builtin_table.py:195` · `tests/harness/test_ir_ref_sites.py:90` —— 追加载荷为 v3 形,
**形状陈旧但当前仍工作**(锚正则不涉 `;`/`}`)⇒ 登记不动,免得后人见 v3 形以为漏迁。
…· 设计缺口 1 · 发布步骤 1 · 登记不动 1 · 语料另案 1) 逐条(按标题行号 = 序号): - **#2026-10-12-1**【缺陷·checker】`nail4` 同一节点**第二推断来源**(`undef_x = 0` 的 LHS ti 非错误哨兵);★ 代价 = **3 档 `tests/probes/` 负例探针静默接受**(`n05_recursive`/`n10_named_vs_int`/`n21_t_assign_int`)+ **`probes_run.sh` 不在任何 CI job 里** ⇒ 无自动门。已排除 4 项、剩 3 条候选未验 ⇒ **维护者裁定挂起**。 - **#2026-10-12-2**【既有约定·待裁】`-> int` 却 `return` `bool`:**11 处 / 3 档**(闭包内 3 `type_engine.cr::lit_implies` + 闭包外 8 `src/kernel/**`);「`bool ⊆ int` 可隐式」是否为**语言面规则**。**新判定面(nail3)的第一批战果**。 - **#2026-10-12-3**【诊断面·系统性静默】不带码 `add_error` 只要同批有带码诊断就**永远不打印**(`main.cr:133-137` 次序);正控已证机制。 - **#2026-10-12-4**【设计缺口】`corec run` 包裹壳按**行尾 `;`** 切分(`main.cr:356-391`);`has_main != 0` 时不执行 ⇒ 不影响 `rebuild.sh` 冒烟。 - **#2026-10-12-5**【**发布步骤·非缺陷**】`PINNED_REV` 与 squash 合入耦合:`25e70197` 不进 develop 历史 ⇒ **合入那一步须同批重指 + 重锁三 sha**。 - **#2026-10-12-6**【判据面·登记不动】两处 v3 形载荷(`test_lsp_builtin_table.py:195` · `test_ir_ref_sites.py:90`)——形状陈旧但**当前仍工作**,防后人误读为漏迁。 - **#2026-10-12-7**【语料面·另案】inline 过渡形**余 15 档**未翻文档形(负例本体 + `t3_pos_ensure_*`);两形都收 ⇒ 不阻塞。 判据:`python3 tests/harness/test_todo_id_migration.py` → **6/6 PASS**(J1 残留 0 · J2 悬空 0 · J3 映射完备 · J6 同日段唯一)。
…#apply`/`#rewrite`)+ **删除 `#tag`**(明写「Core 不引入用户可见的策略定义机制」) 维护者 2026-10-12 给出更新后的 §十,逐字落盘(仅补齐标题层级)。相对旧版的三处实质变化: 1. **§10.3 由「`spec fn`」改为「可复用命题与纯函数」** —— **Core 不引入 `spec fn`,所有函数都是 `fn`**;规约表达式可调用**任何被编译器判定为纯、终止、可判定**的函数(纯性由 HDFG 自动推导,不需用户标记);有副作用/不终止/不可判定者出现在规约中 ⇒ **编译器直接报错**。 ⇒ **函数契约本身就是可复用的命题**:已证明的函数经 `#apply`/`#rewrite` 引用,`#apply(append_assoc)` 引用的是那条**全称命题**。 ⇒ 规约表达式中的**自由变量自动泛化为全称闭包**(`#ensure(append(xs, Nil) == xs)` ≡ `forall(xs: List) => …`)。 2. **`#tag` 整节删除**(原 §10.8)—— 改为明写「**Core 不引入用户可见的策略定义机制;战术库的扩展通过普通模块和导入完成**」。⇒ **这正是此前设计讨论里判为「范畴错误」的那一处**(`#` 族应只有使用面)**由维护者裁定落定**(TODO #2026-10-11-1 据此闭档,见该条更新)。 3. **战术标签重组为四类**:提示标签(`#induct`/`#cases`/`#unfold`/`#rewrite`/`#apply`/`#exists`)· 语义范围标签(`#graph.*`)· 判定与求解标签(`#ring`…`#smt`,**只能在 VERIFY 阶段执行**)· 预算标签(`#budget`)。 另:§1.3 关键字的注里删掉 `#tag` 并加注「战术标签里**没有**『策略/标签定义』这一形」。 §10 体量:旧 388 行 → 新 322 行。
…整节删除 + `spec fn` 取消) 该条所记三条毛病(`#` 既使用又定义 / 形状说谎 / 名字关在标签族里)由「**不引入该构造**」一并消除; 原记录保留供追溯。判据:`tests/harness/test_todo_id_migration.py` → 6/6 PASS。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v4 目标语法迁移(批内 T0–T7 完成)
依据 =
docs/superpowers/plans/2026-09-24-v4-syntax-migration.md(develop 上的施工计划)与docs/core-language-syntax_v4.md(目标语法)。策略 = (乙) 大爆炸:一次迁完,不开影子语法。起点问题与修法
本批开工时发现:本地那堆 v4 改动停在旧基底
dbadb8d6(落后 develop 24 提交 / 缺 104 档),其恰含 develop 已合入的施工计划与 T0 布局规格 ⇒ "改了一大半却没看到计划"。本批先把堆 rebase 到 develop,再按计划推。做了什么
NEWLINE/INDENT/DEDENT发射 + 块产生式改吃INDENT…DEDENT),判据载体tools/v4_frontend_probe.py(146 条)build_selfhost_native.py的 wrapper 等)src/**语料 92 档改写(85 迁移 / 6 no-op / 1 死文件)never语义 + 六项「文档 vs 实现」缺口src/外.cr135 档 + 内联 Core(85 档.py/ 63 档.md)迁移.ccr四条/二进制 sha 白名单/PINNED_REV)+ 过渡形翻转判据(实跑)
nice -n 19 ./build/corec clean-cache && ./build/corec check src/compiler⇒ rc=0 · 零诊断corec2 == corec3⇒ IDENTICALpython3 tools/v4_frontend_probe.py⇒ 146 条 ALL PASStests/harness/*.py⇒ 7/7 rc=0(含criteria_mutations27/27 真跑腿开)tests/selfhost/绿档 2/85 → 61/85,由绿转红 0.md内联 Core:注释记号 1607→1607 · 空行 5829→5829(逐数不变).ccr四条:实测零变化(D1 确定性门 5/5 + 反向对照必红)PINNED_REV换代 + 三 sha 重锁:rebuild.sh实跑重建出与白名单逐条相同的三 sha⚠ 合入那一步必须同批做(否则他人重跑
rebuild.sh必失败)本仓 develop 走 squash 合入 ⇒ 新
PINNED_REV = 25e70197不进 develop 历史。合入时须:① 重指
PINNED_REV到合入后 develop 的提交;② 重锁三 sha 白名单。(条件已逐字写进
rebuild.sh:22-25/REBUILD.md:44-46。见 TODO#2026-10-12-5。)悬空账(已登记 TODO
#2026-10-12-1..7+#2026-10-11-1..5,均未修)#2026-10-12-1【缺陷·checker】nail4同一节点第二推断来源 —— ★ 代价已量出:3 档tests/probes/负例探针被静默接受,且probes_run.sh不在任何 CI job 里(无自动门)#2026-10-12-2【待裁】-> int却returnbool的 11 处(闭包内 3 +src/kernel/**8):bool ⊆ int是否为语言面规则#2026-10-12-3【诊断面】不带码add_error被带码诊断遮蔽(main.cr:133-137)#2026-10-12-4【设计缺口】corec run包裹壳的;-切分启发式(main.cr:356-391)#2026-10-12-6【登记不动】两处 v3 形载荷(形状陈旧但当前仍工作)#2026-10-12-7【语料另案】inline 过渡形余 15 档#2026-10-11-1【设计·未决】#tag战术策略定义语法(维护者暂缓)#2026-10-11-4/-5bootstrap 缺T?语义面 ·enum V(dex?)运行 SIGSEGV(代 0 同病)三条「工具看不见的层面」教训(本批已留档)
selfhost-testsjob 整 job 断,且同族里没红的那批可能正在空转)。