Skip to content

Commit dd57a0b

Browse files
olwangclaude
andcommitted
fix(selfhost): AST producer — type-annotated let value split → 339/579
emit_let used top_assign to find the `=`, whose `>=`/`<=`/`==` guard wrongly skipped the assignment `=` immediately after a generic close `>` — so `let x: Option<Int> = Some(...)` dropped its entire value subtree. Switched the let to the reference's rule (parse_let_stmt): the value split is simply the FIRST `=` after the name (a let's name/annotation never contains `=`, so no guard is needed). top_assign is kept for assignment statements, which do need the comparison guard. Corpus reach: 331 -> 339 / 579 byte-exact, still 0 run-failures. Floor -> 339. New sample let_typed.rss (incl. `let flag: Bool = seed >= 0`). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a101d4e commit dd57a0b

3 files changed

Lines changed: 23 additions & 3 deletions

File tree

crates/rsscript/src/selfhost_parity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ const AST_SAMPLE_MIN: usize = 6;
15221522
/// Floor for `ast_parity_corpus` — the number of corpus files whose rss AST dump
15231523
/// already matches the oracle byte-for-byte. Ratchets up as the producer's
15241524
/// coverage grows; a drop signals a regression. (Full parity = files.len().)
1525-
const AST_CORPUS_PARITY_FLOOR: usize = 331;
1525+
const AST_CORPUS_PARITY_FLOOR: usize = 339;
15261526

15271527
/// Step-2 measurement gate (ignored by default): how many corpus files the rss
15281528
/// producer reproduces byte-for-byte. Not full parity yet — this ratchets a floor

selfhost/astdump.rss

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,17 @@ fn is_cont_op(toks: read List<Tok>, i: read Int) -> Bool {
10491049
}
10501050

10511051
// First top-level standalone `=` (assignment, not == != <= >=) in [start, end), or -1.
1052+
// First `=` symbol token in [start, end), or -1 (used for let value split;
1053+
// `=>` is a distinct FatArrow token, not matched).
1054+
fn first_eq(toks: read List<Tok>, start: read Int, end: read Int) -> Int {
1055+
let mut i = start
1056+
while i < end {
1057+
if at_symbol(toks: read toks, i: read i, code: read SYM_EQ) { return i }
1058+
i = i + 1
1059+
}
1060+
return -1
1061+
}
1062+
10521063
fn top_assign(toks: read List<Tok>, start: read Int, end: read Int) -> Int {
10531064
let mut depth = 0
10541065
let mut i = start
@@ -1172,8 +1183,11 @@ fn emit_let(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int)
11721183
let g = String.concat(left: read f, right: read boolstr(b: read isMut))
11731184
let h = String.concat(left: read g, right: read " async=false")
11741185
emit(depth: read d, s: read h)
1175-
// Optional type annotation.
1176-
let asg = top_assign(toks: read toks, start: read j, end: read e)
1186+
// Value split is the FIRST `=` after the name (matching parse_let_stmt): a
1187+
// let's name/annotation never contains `=`, so no `>=`/`==` guard is needed —
1188+
// and top_assign's guard would wrongly skip the `=` after a generic `>`
1189+
// (`let x: Option<Int> = …`).
1190+
let asg = first_eq(toks: read toks, start: read j, end: read e)
11771191
if at_symbol(toks: read toks, i: read j, code: read SYM_COLON) {
11781192
emit_type_ref(toks: read toks, i: read (j + 1), tag: read "type", eff: read "", d: read (d + 1))
11791193
}

selfhost/samples/ast/let_typed.rss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn hot(seed: Int) -> Int {
2+
let value: Option<Int> = Some(seed)
3+
let names: List<String> = List<String>.new()
4+
let flag: Bool = seed >= 0
5+
return 0
6+
}

0 commit comments

Comments
 (0)