Skip to content

Commit 6dd15aa

Browse files
olwangclaude
andcommitted
feat(selfhost): AST producer — body-less structs/sums + malformed lets → 543/587
- Body-less type declarations (`struct Image`, `struct ImageError`) no longer swallow following items: a body exists iff the token right after the name (past any generics/derives) is `{`. Previously find_block_open borrowed a LATER decl's brace, dropping every item up to it. Same fix for emit_sum. - Let statements now render `malformed=true` when `=` is present with no value expression, or `:` with no type, and suppress the corresponding child — matching parse_let_stmt (equals.is_some && value.is_none, and the colon analog). Corpus reach: 521 -> 543 / 587 byte-exact (+22, ~92.5%), still 0 run-failures. Floor -> 543. New samples nobody_struct, malformed_let; 32 samples green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8f8c721 commit 6dd15aa

4 files changed

Lines changed: 44 additions & 11 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 = 521;
1525+
const AST_CORPUS_PARITY_FLOOR: usize = 543;
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: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,20 +1223,29 @@ fn emit_let(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int)
12231223
let a = String.concat(left: read "let kind=", right: read kind)
12241224
let b = String.concat(left: read a, right: read " name=")
12251225
let c = String.concat(left: read b, right: read name)
1226-
let f = String.concat(left: read c, right: read " mut=")
1227-
let g = String.concat(left: read f, right: read boolstr(b: read isMut))
1228-
let h = String.concat(left: read g, right: read " async=false")
1229-
emit(depth: read d, s: read h)
12301226
// Value split is the FIRST `=` after the name (matching parse_let_stmt): a
12311227
// let's name/annotation never contains `=`, so no `>=`/`==` guard is needed —
12321228
// and top_assign's guard would wrongly skip the `=` after a generic `>`
12331229
// (`let x: Option<Int> = …`).
12341230
let asg = first_eq(toks: read toks, start: read j, end: read e)
1235-
if at_symbol(toks: read toks, i: read j, code: read SYM_COLON) {
1231+
let hasColon = at_symbol(toks: read toks, i: read j, code: read SYM_COLON)
1232+
let mut typeEnd = e
1233+
if asg >= 0 { typeEnd = asg }
1234+
// malformed=true when `=` is present with no value expression, or `:` with no
1235+
// type (parse_let_stmt: equals.is_some && value.is_none, or the colon analog).
1236+
let emptyVal = asg >= 0 && (asg + 1) >= e
1237+
let emptyType = hasColon && (j + 1) >= typeEnd
1238+
let malformed = emptyVal || emptyType
1239+
let f = String.concat(left: read c, right: read " mut=")
1240+
let g = String.concat(left: read f, right: read boolstr(b: read isMut))
1241+
let mut h = String.concat(left: read g, right: read " async=false")
1242+
if malformed { h = String.concat(left: read h, right: read " malformed=true") }
1243+
emit(depth: read d, s: read h)
1244+
if hasColon && emptyType == false {
12361245
emit_type_ref(toks: read toks, i: read (j + 1), tag: read "type", eff: read "", d: read (d + 1))
12371246
}
12381247
// Value.
1239-
if asg >= 0 {
1248+
if asg >= 0 && emptyVal == false {
12401249
emit(depth: read (d + 1), s: read "value")
12411250
emit_expr(toks: read toks, start: read (asg + 1), end: read e, d: read (d + 2))
12421251
}
@@ -1778,8 +1787,13 @@ fn emit_type_decl(toks: read List<Tok>, start: read Int, d: read Int) -> Int {
17781787
emit(depth: read d, s: read h)
17791788
j = emit_generics(toks: read toks, i: read j, d: read (d + 1))
17801789
j = emit_derives(toks: read toks, i: read j, d: read (d + 1))
1781-
let openb = find_block_open(toks: read toks, start: read j, limit: read List.len(list: read toks))
1782-
if openb < 0 { return declaration_line_end(toks: read toks, start: read start) }
1790+
// After the name (and any generics/derives) the very next token is either the
1791+
// body `{` or the start of the NEXT declaration — so a body exists iff that
1792+
// token is `{`. (A raw find_block_open would borrow a LATER decl's brace for a
1793+
// body-less declaration like `struct Image`, swallowing everything up to it.)
1794+
let mut openb = -1
1795+
if at_symbol(toks: read toks, i: read j, code: read SYM_LBRACE) { openb = j }
1796+
if openb < 0 { return j }
17831797
let close = find_matching(toks: read toks, open: read openb, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
17841798
let dropPos = find_drop(toks: read toks, open: read openb, close: read close)
17851799
let mut fieldsEnd = close
@@ -1810,8 +1824,13 @@ fn emit_sum(toks: read List<Tok>, start: read Int, d: read Int) -> Int {
18101824
emit(depth: read d, s: read c)
18111825
j = emit_generics(toks: read toks, i: read j, d: read (d + 1))
18121826
j = emit_derives(toks: read toks, i: read j, d: read (d + 1))
1813-
let openb = find_block_open(toks: read toks, start: read j, limit: read List.len(list: read toks))
1814-
if openb < 0 { return declaration_line_end(toks: read toks, start: read start) }
1827+
// After the name (and any generics/derives) the very next token is either the
1828+
// body `{` or the start of the NEXT declaration — so a body exists iff that
1829+
// token is `{`. (A raw find_block_open would borrow a LATER decl's brace for a
1830+
// body-less declaration like `struct Image`, swallowing everything up to it.)
1831+
let mut openb = -1
1832+
if at_symbol(toks: read toks, i: read j, code: read SYM_LBRACE) { openb = j }
1833+
if openb < 0 { return j }
18151834
let close = find_matching(toks: read toks, open: read openb, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
18161835
// Variants: newline-separated `Name` or `Name(fields)`.
18171836
let mut i = openb + 1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// expect: RS0015
2+
fn main() -> Unit {
3+
let missing =
4+
return Unit
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// expect: RS0004
2+
struct Image
3+
struct ImageError
4+
5+
fn load(path: read Path) -> Result<Image, ImageError>
6+
effects(fresh)
7+
{
8+
return Image()
9+
}

0 commit comments

Comments
 (0)