Skip to content

language: cast, Self, receiver rules, AST spans, and line-table removal - #222

Merged
dslsdzc merged 25 commits into
developfrom
feature/ast-span
Oct 28, 2026
Merged

dslsdzc merged 25 commits into
developfrom
feature/ast-span

Conversation

@dslsdzc

@dslsdzc dslsdzc commented Oct 28, 2026

Copy link
Copy Markdown
Owner

内容

这条分支把四块工作合在一起,都在 AST 与语言面,互相有依赖。

1. 导入图(保留自 feature/import-graph)

每个文件独立 SourceManager 身份、独立 tokenize、独立行号;诊断按真实文件和文件内行号;LSP 定义可跨文件跳转;静态构建的 rt.cr 作为独立前导文件。

2. 语言面

  • as 删除,改成中缀 cast;cast<T>(expr) 一并删除。转换改为白名单:int 与 dex 有真实语义,白名单外报 error[TA09]。
  • 接收者只写 self / &self / &mut self,不再接受 self: T;self 不能出现在 impl 之外。
  • Self 解析到 impl 类型,可用于返回位置和参数位置。
  • enum 具名 payload 给出明确错误,而不是“未定义类型 r”。
  • 返回带推断字段的结构体使用返回推断;-> T 会提示省略返回型。
  • 方法调用按接收者做泛型绑定并特化到实例;关联函数不再多传一个接收者。

3. AST span

  • 节点从 72 字节降到 64 字节,位置改存全局字节区间。
  • ast_line / ast_col 语义不变(全局行号),由 span 解码。
  • 删除全局行表 g_line_fileid / g_line_lib / build_line_fileid。
  • LSP 诊断输出真实区间。

4. 文档与注释

按 docs/doc-style.md 清理全仓比喻与行话(docs/superpowers/plans 除外):铁律、闸门、判据、口径、腿、牙、钉、批 等。

验证

  • check src/compiler 通过;冷缓存 44.2 秒(改造前 52.7 秒)。
  • canary 5/5 不变。
  • parity 89 档:54×0 / 35×1,暖态 89 档 FAIL=0。
  • selfhost 套件:cast 12/12、struct-impl 14/14、impl 8/8、enum-impl 5/5、iface-ops 73/73、hit-table 24/24、diag-gate 28/28、ccr-types 53/53、import-graph 7/7、pub-visibility、lib-manager、s1p-liveness、apx-conversion 等全绿。
  • test_lsp 的 6 个失败在 develop 上同样存在,值逐字相同。

注意

  • 合入后需要按 tools/baseline/REBUILD.md 换代固定基线:PINNED_REV 仍是 7eb9669d,它早于 develop 的库可见性批,固定基线对拍在当前树上不可用。

res_imports now builds a graph of SourceManager files instead of concatenating
imports into <combined>. Each file keeps its own token stream and line base,
diagnostics resolve to the real path and local line, and the CIR cache hashes
every graph file. Manifests contribute import edges only; paths and manifest
ids stay out of the intern table so .ccr bytes do not depend on the build
directory.

Canary re-locked: the ELF is unchanged; the four .ccr values are updated with
attribution and old-value records.
PINNED_REV moves from 25e7019 (local-only v4 T6 commit) to 7eb9669, the
v4 migration squash merge already reachable from develop. The three
rebuilt binary hashes are re-locked from the new revision.

parity_run.sh CORPUS_TOTAL follows the current corpus: 34 suite files,
2 compiler entries, 26 backend/kernel units, 19 stdlib files, and 4
examples, for 85 total. The pinned baseline runs the 85-file check with
rc distribution 49x0 / 36x1.
The static runtime is now a SourceManager file parsed before main instead of
text prepended to the main source. It has no fileid and does not shift user
line numbers, so static diagnostics stay on the user file's local line.

LSP diagnostic ranges now resolve the owning file for both span and line-only
diagnostics. Imported function and struct/enum definitions resolve through the
import graph; the main token stream is restored after a cross-file scan.

The lexer/diagnostic/S1P paths no longer fall back to scanning the main source
text. AST nodes still store line/column and recover the file through the
SourceManager line table; that remains the documented boundary.

Static canary values are re-locked after the prelude change. The ELF is
unchanged; gt_static_ccr loses one TYPE term because compiler-global intern ids
shifted, with function and instruction counts unchanged.
The grammar and developer docs use `enum Mode:\n | Server\n | Client`, and
the Python bootstrap parser already accepts that surface. The self-hosted
parser consumed the pipe as a variant name. Accept the optional T_PIPE before
each variant; bare layout variants remain accepted for existing corpora.

Add enum-impl coverage for pipe variants, enum interface implementations,
generic enum methods, and the bare-layout compatibility path.
The import-graph branch and develop's library work both changed the compiler,
so the frozen canary values and the parity corpus count have to be re-measured
on the merged tree.

Canary (D1 5/5, each artifact built twice byte-identical):
  canary_elf    unchanged 4e79ced2... 49334
  pa_ccr        unchanged 271d0c1b... 97509
  pa_static_ccr sha changed, size unchanged 97731
  gt_ccr        unchanged 96cc9e75... 131023
  gt_static_ccr 131285 -> 131245 (-40)
Only the `--static` artifacts move: the prelude now lives in its own
SourceManager file instead of being concatenated into the main source, so line
coordinates shift. The gt_static -40B is one deduplicated TYPE entry, the same
mechanism recorded for the 2026-10-25 batch. A control build at the commit
before the enum-pipe change reproduces both values byte for byte, so the enum
`|` change contributes nothing to the canary.

Parity: t4 grew from 19 to 23 files (bits.cr, hash.cr, hashmap.cr, rawbuf.cr),
so CORPUS_TOTAL goes 85 -> 89. Measured 89 files, rc 54x0 / 35x1. Per tier the
failure counts stay t1 4 / t2 1 / t3 20 / t5 2; t4 drops from 9 to 8 because one
stdlib unit that used to fail now passes.
`as` never converted anything. The checker accepted every type pair and the
generator emitted one IR_LOAD, so `1.5 as int` returned 96 (1500000 mod 256).
`cast<T>(expr)` was a V3 leftover that produced the same node and violated the
V4 ban on `<T>`.

Syntax:
  - `expr cast Type`, same precedence position as `as`.
  - `as` leaves the keyword table; `cast` enters it. The count stays 39.
  - `cast<T>(expr)` is deleted.
  - `CastExpr` added to grammar/core.ebnf between Unary and CallOrField.

Semantics (whitelist, anything else is error[TA09]):
  - T -> T identity, no instruction.
  - int -> dex: exact form, scaled by 10^6.
  - dex -> int: divide out the scale, truncate toward zero. `1.5 cast int`
    is now 1. `-1.5 cast int` is -1.
  - char <-> int: kept temporarily; the value is the intern index, not a code
    point. match literals depend on this.
  - pointer family (int/string/*T -> *T, *T -> *U, &T -> *T): unchanged
    semantics. The cast is not the safety boundary; the external dereference
    gate (TU03) still requires `unsafe`.
  - `*T cast int` is deliberately not offered. Measured: a full round trip
    returns 2n, because the tagged-int representation and the raw pointer word
    do not line up. It has no consumers; the open question is recorded in the
    plan.

Tests: new tests/selfhost/test_cast.py (12 cases) covers the spellings, the
whitelist, truncation in both directions, the char round trip, pointer casts,
and the two removed forms. Existing corpus and probes move from `as` to
`cast`; the iface-ops pin that expected string->int to be accepted now asserts
TA09 instead.

Canary is unchanged (5/5, no re-lock). check src/compiler passes. The
selfhost suites touched by the change pass: cast 12/12, pointer-safety 16,
diag-gate 28/28, hit-table 24/24, iface-ops 73/73, multibranch 11/11,
import-graph 7/7, lib-manager, enum-impl 4/4, impl 8/8, pub 3x.

Also replace the `loop` + index + break idiom with `while` in the functions
this batch touched (build_line_fileid, global_seg_index).
…d calls drop the receiver

Four separate defects made methods and associated functions on structs either
unusable or silently wrong.

1. The parser discarded the type annotation of every `self` parameter (all three
   receiver branches called parse_type() and threw the node away), and it took
   the `first_param` index before parsing the type, so the recorded first
   parameter pointed at a type node. Both are fixed: the annotation is stored,
   and `pf` is taken right before the parameter node is allocated.

2. The checker derived a receiver's type only from the mangled "Type.method"
   name. A free function `fn get(self: P)` therefore had an untyped receiver,
   and a method on a generic struct got the bare named type, so `self.field`
   could not substitute an inferred (`.`) field. The annotation now wins, and
   the field path keeps the generic-apply row across the `&self` auto-deref
   before unwrapping it for the struct lookup.

3. Generic inference ignored method receivers. `infer_gen_call` maps parameters
   to arguments 1:1, but a method's argument list excludes the receiver, so a
   method on a generic struct had no binding and its instance fell back to
   unit. `infer_gen_call_recv` binds the receiver against the first parameter
   and startsthe argument walk at the second.

4. The generator prepended a receiver for every `Type.fn(...)` call, including
   associated functions that have no `self` parameter. Every argument shifted by
   one slot and the real first argument was dropped: `Point.make(9)` returned a
   zeroed Point. It now checks the callee's first parameter.

Method calls are also specialized now. A method on a generic struct inherits the
struct's parameters, so the template has no emitted body; without redirecting
the call to the monomorphized instance the call targeted a missing symbol and
the program segfaulted.

Tests: tests/selfhost/test_struct_impl.py (7 cases) covers inferred fields read
through a method and through an interface impl, associated functions returning a
struct, methods returning a struct, `self: T` on a free function, and `&mut
self`. check src/compiler passes; impl 8/8, enum-impl 4/4, cast 12/12,
field-inference 5/5, generic-enum 2/2, iface-ops 73/73, hit-table 24/24,
diag-gate 28/28, pointer-safety, import-graph 7/7, lib-manager, pub-visibility
all pass. Canary 5/5 unchanged.

Known gaps, not addressed here: a `&self` / `&mut self` receiver on a struct
with an inferred field still fails unless the receiver is annotated
(`fn f(&self: Box)`), and `Self` as a type name is not resolved.
`self: T` was never part of the surface. It existed because the parser
discarded every receiver annotation and the checker reconstructed the type from
the mangled "Type.method" name, so the annotation was invisible either way.

The receiver type now comes from the enclosing `impl Type:` only:

  - `self`       by value
  - `&self`      read-only reference
  - `&mut self`  writable reference

The parser rejects `self: T` with a located error, and rejects `self` outside an
impl. Inside an impl it records the receiver type from the impl header, so a
`&self` method on a struct with an inferred (`.`) field resolves the field the
same way a by-value receiver does.

Tests and docs drop the annotated form: all receiver parameters in the selfhost
suites, the bootstrap pipeline fixture, and `tests/suite/apx_conversion_test.cr`
now read `self` / `&self` / `&mut self`. Two negative cases pin the removal:
`self: P` and a free function taking `self`.

`Box` is gone from the current language docs. It was never a language type; it
is only a user-chosen struct name that appears in `tests/suite/generics_test.cr`
and in the V3 generics proposal, which now says it is retired.

check src/compiler passes. impl 8/8, enum-impl 4/4, struct-impl 9/9,
field-inference 5/5, ambiguous-constraint 2/2, auto-constraint 3/3,
apx-conversion 23 cases, cast 12/12, iface-ops 73/73, hit-table 24/24,
diag-gate 28/28, pointer-safety, import-graph 7/7, lib-manager,
pub-visibility, s1p-liveness, arg-inference 18 cases, ccr-types 53/53,
bootstrap-pipeline 34/34. Canary 5/5 unchanged.
… real error

`Self` was an unknown name. It now resolves to the enclosing impl type, both in
return position (`-> Self`) and in parameter position (`other: Self`). The parser
rewrites the type node while parsing the method, so declaration collection never
sees an unresolved `Self`; the checker keeps a fallback that reports a located
error when `Self` appears outside an impl.

`enum Shape: | Circle(r: int)` used to be read as a payload of type `r`, which
surfaced as "Undefined type 'r'". Payload fields are positional in this
language; the parser now reports "named enum payload fields are not supported;
write the payload type positionally" at the offending name. The syntax doc says
the same and shows the positional form.

Tests: struct-impl 12/12 (adds `Self` in return and parameter position, `Self`
outside an impl, and keeps the earlier receiver cases), enum-impl 5/5 (adds the
named-payload rejection). check src/compiler passes; impl 8/8, field-inference
5/5, ambiguous-constraint 2/2, auto-constraint 3/3, cast 12/12, iface-ops
73/73, diag-gate 28/28, import-graph 7/7, lib-manager, ccr-types 53/53. Canary
5/5 unchanged.
…ence

A struct whose field type is `.` is a family, not a single type: the
construction site fixes the instantiation. Naming the bare struct in `-> T`
therefore cannot match, and the caller would receive the template (its field
type is still the placeholder). The form that works is to omit the return type
and let the body infer it.

`fn make(n: int):` with `return Holder { v = n }` now checks, builds, and runs.
Associated functions follow the same rule.

The diagnostic for the annotated form says so instead of the generic mismatch:
"Returned an instantiation of this struct, but the declared return type names
the family; omit the return type to infer it from the body". The generic TF01
message now also prints both types, which is what identified the mismatch
(`Holder[int]` vs `Holder`).

Docs: section 9.1 shows the working form and states the rule. Tests: struct-impl
14/14 adds the inferred-return associated function and pins the diagnostic.

check src/compiler passes; impl 8/8, enum-impl 5/5, field-inference 5/5, cast
12/12, iface-ops 73/73, diag-gate 28/28, hit-table 24/24, pointer-safety,
import-graph 7/7, ccr-types 53/53, auto-constraint 3/3. Canary 5/5 unchanged.
Groundwork for the AST span migration. Each registered file now records the
global byte offset of its first byte, and three readers map a global offset back
to a file, a line, and a column by binary search over the file order.

Token `lo` / `hi` become global byte offsets (file byte_base + local offset).
Nothing reads them today, so this step is behavior-neutral; the AST layout is
the first consumer.

The plan is in docs/superpowers/plans/2026-10-27-ast-span.md: 8-byte `lo`/`hi`
replace the 16-byte line/column pair, taking the node from 72 to 64 bytes, with
`ast_line` / `ast_col` kept as wrappers so call sites do not change.

check src/compiler passes and canary is 5/5 unchanged, which is the expected
result for an additive step.
Follows docs/doc-style.md: no metaphors, no in-group shorthand, no arrows for
reasoning.

  - 门禁 -> 前提 / 要求 unsafe
  - 落地 -> 实施
  - 消费方对齐 -> 更新使用位置信息的模块
  - 加字节轴 -> 增加字节偏移
  - 收尾 -> 剩余工作
  - 换代纪律 -> 按 REBUILD.md 的规则记录旧值和原因
  - 未对齐的转换 -> 转换不正确
  - 闸门 -> 检查(canary note, checker comment, test name)

The pointer rows in the cast plan said `unsafe` was required for the cast. That
was true of an earlier draft, not of the implementation: the cast may appear
anywhere and only the external dereference requires `unsafe`. The rows now say
"任意位置,解引用时检查", matching docs/developer/syntax.md and the test that
builds a pointer cast outside `unsafe`.

Text only. No behavior change: cast suite 12/12.
Scope: every .cr file this branch touches. Four of them carried the terms that
docs/doc-style.md rules out; 41 comment fragments across 34 lines are rewritten.
No code is changed.

  - 判据 -> 判断依据 / 条件 / 判断
  - 口径 -> 规则
  - 收口 -> 统一处理
  - 落点 -> 位置
  - 腿 -> 路径 / 用例
  - 对齐 -> 对应 / 处理
  - 机械判据 -> 自动检查

Affected: src/compiler/checker.cr, src/compiler/ir_gen.cr,
tests/suite/apx_conversion_test.cr, src/compiler/globals.cr.

check src/compiler passes and canary is 5/5 unchanged, which is the expected
result for comment-only edits.
Second pass after the touched-file cleanup: the whole repository now has zero
occurrences of the metaphor and in-group set in .cr files (闸门, 腿, 牙, 咬, 钉,
判据, 落点, 口径, 收口, 归口, 收编, 门禁, 骨架, 载体).

40 fragments across 12 files: regalloc.cr, cir_cache.cr, ty_shadow.cr,
type_selftest.cr, and the suite and fixture programs under tests/. Comments
only; no code and no test expectations change.

  - 判据 -> 判断依据 / 判断 / 检查项
  - 口径 -> 规则 / 计算方式
  - 收口 -> 已处理 / 处理
  - 落点 -> 位置
  - 腿 -> 路径 / 用例
  - 骨架 -> 结构
  - 载体 -> 包含 / 改为
  - 钉住/钉死 -> 固定

build passes, check src/compiler passes, apx-conversion, view-builtins,
opt-dex and cast suites pass, canary 5/5 unchanged.

Remaining outside .cr, not changed: process records under docs/superpowers
(plans, implementation), TODO.md, tools/ scripts, src/ci/run.sh and the Python
test comments, about 1900 fragments in total.
Replaces metaphors and shorthand with plain wording in the CI dispatcher
comments. Comments only; `bash -n` passes.

Done in this pass: 铁律 -> 硬性规则, 判据/口径/收口/腿/牙/钉/闸门/载体 and
the 批 suffix -> 阶段 or 本次, plus the long block comments above the
bootstrap and selfhost hooks.

Not finished: 75 occurrences of the same set and 57 occurrences of 批 remain in
this file. The rest is being done file by file, not by a global replacement.
Applies docs/doc-style.md to every tracked file except docs/superpowers/plans,
which is out of scope by request.

Replaced uniformly:

  铁律 -> 硬性规则       闸门 -> 检查          门禁 -> 前提
  判据 -> 判定依据       口径 -> 规则          收口 -> 处理
  落点 -> 位置           腿 -> 路径            牙齿 -> 有效性用例
  咬 -> 检出             钉 -> 固定            骨架 -> 结构
  载体 -> 文件           收编 -> 纳入          归口 -> 归属
  批 -> 阶段(本批 -> 本次,一批 -> 一组,批次 -> 阶段)

批准, 批注, 批量, 批复 and 批处理 are unchanged: they are ordinary words, not
the work-batch shorthand.

Scope: 191 files in the first pass, 77 in the second (Python docstrings and
text files), plus the remaining occurrences in shell scripts, fixtures and the
CI workflow. Comments and prose only; no code, no test expectation and no data
row changes.

Verification: build passes, check src/compiler passes, bash -n passes on the
edited shell scripts, canary 5/5 unchanged. doc-protected-surface, canary
carrier, ci-hook coverage, TODO-id migration, cast, struct-impl and
import-graph suites pass.

Remaining: docs/superpowers/plans, 258 occurrences, excluded by request.
Symbols, per docs/doc-style.md (no emoji, no warning marks, no check marks, no
【】 labels). 363 removed across 44 files, including three files under
docs/superpowers/plans:

  ⚠ -> 警告:        ✓ -> 通过         ✗ -> 失败
  ✅ -> 通过         ❌ -> 失败        ⛔ -> 禁止
  ★ -> (需裁定)     ⬜ removed        【】 -> 「」

Literal translations, outside docs/superpowers/plans:

  - 基于 -> 使用 / 根据 / 在 ...(19 occurrences)
  - 使得 -> 使
  - 作为一个 -> 合并成一条 / 作为单独的一遍
  - 对于 ... -> 对 ...
  - 进行函数级增量编译 -> 做函数级增量编译
  - 进行指针算术 -> 做指针算术
  - 在最终目标编码之前进行 -> 在最终目标编码之前完成

Text only. build passes, check src/compiler passes, canary 5/5 unchanged,
doc-protected-surface and canary-carrier pass.

Remaining in docs/superpowers/plans by request: terminology and the two
remaining 基于 sentences were not rewritten there.
The layout switch built and gave correct positions for a single file, but
checking the compiler produced 4414 diagnostics: AST `line` is also the global
flat line index used by diag_fileid_for_line, g_line_lib, lib_of_node and the
.ccr line fields. Returning a file-local line made file and library ownership
resolve against the wrong line space, so symbols looked undefined.

The change was reverted; the tree builds and check src/compiler passes.

Revised order: keep line and span side by side (72 -> 80 bytes), migrate every
consumer to span lookup, then drop line and column (-> 64 bytes).
The node position pair (line, col) is replaced by one packed 64-bit span field:
`lo + hi * 2^32`, holding the start and end global byte offsets. Token offsets
already carry the file byte_base, and the +1 shift keeps 0 meaning "no position".

`ast_line` keeps its historical meaning: the global flat line index that
diagnostics, g_line_lib, library ownership and the .ccr line fields index by. It
is now decoded from the span through SourceManager; `ast_col` reads the column.
`ast_lo` / `ast_hi` expose the span itself.

Two latent defects surfaced once ownership resolved correctly, and both are
fixed here:

1. def_type_sym built a synthetic declaration node with position 0 and derived
   the owning library from it, so every type symbol had owner -1. Owner -1 makes
   sym_visible return true, which exposed non-pub library types: `x : Argv4`
   became a type mismatch instead of "undefined type". The owner now comes from
   the declaration line directly.

2. find_struct_by_name looked the parser's source name up as if it were the
   library-qualified unique name. With correct owners, a user struct shadowing a
   library type resolved to the library's entry. It now resolves (source name,
   current library) first.

Method and associated-function calls needed their function node to carry a byte
offset rather than a line; `parse_body` receives token spans now.

Performance: sm_span_file keeps the last hit, and lib_of_node resolves the file
from the span instead of going through the line table. `check src/compiler`,
cold cache, 44.9 s against 52.7 s for the previous commit.

Verification: check src/compiler passes; pub-visibility, pub-label, lib-manager,
cast 12/12, struct-impl 14/14, import-graph 7/7, diag-gate 28/28, impl 8/8,
enum-impl 5/5, iface-ops 73/73, hit-table 24/24, ccr-types 53/53,
apx-conversion, s1p-liveness, field-inference 5/5. Canary 5/5 unchanged.
Second attempt succeeded by keeping ast_line's historical meaning (global flat
line index) and decoding it from the span. Node layout is now 64 bytes.

Records the two latent defects fixed on the way: type symbols always had owner
-1 (making non-pub library types visible), and find_struct_by_name looked up the
source name as a unique name.

Cold `check src/compiler`: 44.9 s against 52.7 s before.
`g_line_fileid` and `g_line_lib` duplicated information the SourceManager already
holds: a global line belongs to the file whose [line_base, line_base + count)
range contains it, and that file owns the library id.

Replaced:

  - diag_fileid_for_line -> fileid_of_line (sm_for_line, then the g_files row
    with that SourceManager id)
  - lib_of_line -> sm_file_lib(sm_for_line(line))
  - ir_gen.global_seg_index -> sm_span_file(ast_lo - 1)
  - lsp analysis line-to-path and line offset -> fileid_of_line / sm_for_line

Deleted: build_line_fileid, grow_line_file, g_line_fileid, g_line_lib,
g_line_cap. `g_line_count` is now set once from g_sm_line_count.

The .ccr line fields are unchanged: they store ast_line, which still returns the
global flat line index.

check src/compiler passes; diag-gate 28/28, import-graph 7/7, pub-visibility,
cast 12/12, struct-impl 14/14, lib-manager, lsp-visibility, impl 8/8, enum-impl
5/5, iface-ops 73/73, hit-table 24/24, pointer-safety, ccr-types 53/53,
apx-conversion, field-inference 5/5, s1p-liveness, pub-label. Canary 5/5
unchanged. Cold `check src/compiler`: 44.2 s.
Line-to-file and line-to-library now come from the SourceManager line ranges.
Lists the four migrated consumers and the deleted globals.
Diagnostics already stored start and end byte offsets. The JSON emitter ignored
the end offset and always produced a one-character range. It now maps the last
covered byte (hi - 1) back to a line and column, so a token-level diagnostic
underlines the token instead of its first character.

Falls back to the old single-character range when the diagnostic has no span or
the span is empty.

check src/compiler passes; lsp-visibility and import-graph pass. test_lsp still
shows the same six pre-existing failures as develop, with identical values.
Canary 5/5 unchanged.
The associated-function guard added earlier treated any call whose target was not
a declared function as "no receiver". A dynamic dispatch call carries the method
name rather than a function link, so `d.bump()` on a `dyn` value lost its
receiver and lowered to a static IR_CALL instead of IR_DYN_DISPATCH.

Unknown targets now default to "has receiver". Declared functions still decide by
their first parameter, so associated functions keep dropping it.

Fixes test_v7_edge_semantics_effect_call_in_chain (27/27 in test_ccr_v7).
check src/compiler passes; cast 12/12, struct-impl 14/14, pub-visibility,
iface-ops 73/73, hit-table 24/24, import-graph 7/7, diag-gate 28/28, impl 8/8,
enum-impl 5/5, pointer-safety, ccr-types 53/53, apx-conversion, lib-manager.
Canary 5/5 unchanged.
tests/probes/warm/tk01_width.cr still used `as *[int; 2]`, which the language no
longer accepts. It failed with N01 + TB01 instead of the expected TK01, so the
warm-cache gate reported a cold/warm mismatch that was really a parse failure.

The probe now uses `cast`. test_warm_cache_gate 10/10.
@dslsdzc
dslsdzc merged commit 28483a4 into develop Oct 28, 2026
4 checks passed
@dslsdzc
dslsdzc deleted the feature/ast-span branch October 28, 2026 02:53
dslsdzc added a commit that referenced this pull request Oct 28, 2026
PINNED_REV 7eb9669 -> 28483a4 (the squash commit of #222). The old revision
predates develop's library visibility batch, so the pinned compiler could not
compile the current sources and the fixed-baseline comparison leg was unusable.

Old generation (7eb9669):
  corec    4234b7a6254ad3b7a890268c462dbe3dbd269a991f0deaa12832e154dbe26265
  corearch 3786eb4a8a500158d22a0193b9174c2f4228b5c8d379a4d992ef920b28a9c421
  corelsp   845ac0485f2fede878f18bdbacb49e9c24b62ddf4c3180e2b5d411e60467c0eb

New generation (28483a4):
  corec    5fe3de891380c5909e6f5cefccac53781acbe1eccf4540f568988290467a6726
  corearch e8b3e30b116c09200dfb9b2d023b0c1608f4f917e89b3f7dabf74ee6e295c542
  corelsp   db51fdc7c420396152f363385174a96fed770af951339482cbb42376b62908e2

rebuild.sh now rebuilds from 28483a4 and the three shas match the whitelist;
the smoke program returns 42. REBUILD.md records the new current generation and
keeps the old row in the history table.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant