Skip to content

Commit 2211cbb

Browse files
olwangclaude
andcommitted
feat(selfhost): AST producer — async-let + nested-fresh return type → 459/582
- `async let name = value` (managed, no annotation, async=true) — dispatched in emit_stmt before the plain let; previously mis-parsed as an assignment. - returns-fresh now also fires when `fresh` appears NESTED in the return-type region (e.g. `-> Result<fresh String, JsonError>`), not only immediately after `->`, matching parse_function_decl's region scan (the nested `fresh` still renders on the inner arg TypeRef; the immediate `-> fresh T` form is still consumed). Corpus reach: 405 -> 459 / 582 byte-exact (+54, ~79%), still 0 run-failures. Floor -> 459. New samples async_let.rss, nested_fresh.rss; 27 samples green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e30b99f commit 2211cbb

4 files changed

Lines changed: 44 additions & 1 deletion

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 = 405;
1525+
const AST_CORPUS_PARITY_FLOOR: usize = 459;
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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,18 @@ fn emit_stmt(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int
11701170
emit(depth: read d, s: read "continue")
11711171
return stmt_end(toks: read toks, start: read start, limit: read limit)
11721172
}
1173+
// async let name = value (managed, no annotation, async=true)
1174+
if is_kw_text(toks: read toks, i: read start, word: read "async") && is_kw_text(toks: read toks, i: read (start + 1), word: read "let") {
1175+
let e = stmt_end(toks: read toks, start: read start, limit: read limit)
1176+
let name = tk_text(toks: read toks, i: read (start + 2))
1177+
emit(depth: read d, s: read String.concat(left: read String.concat(left: read "let kind=managed name=", right: read name), right: read " mut=false async=true"))
1178+
let asg = first_eq(toks: read toks, start: read (start + 3), end: read e)
1179+
if asg >= 0 {
1180+
emit(depth: read (d + 1), s: read "value")
1181+
emit_expr(toks: read toks, start: read (asg + 1), end: read e, d: read (d + 2))
1182+
}
1183+
return e
1184+
}
11731185
// let / local [mut] name [: type] = value
11741186
if is_kw_text(toks: read toks, i: read start, word: read "let") || is_kw_text(toks: read toks, i: read start, word: read "local") {
11751187
return emit_let(toks: read toks, start: read start, limit: read limit, d: read d)
@@ -1476,6 +1488,11 @@ fn emit_function(toks: read List<Tok>, start: read Int, d: read Int) -> Int {
14761488
let sigEnd = function_signature_end(toks: read toks, start: read (pclose + 1))
14771489
let effPos = find_effects_kw(toks: read toks, start: read (pclose + 1), end: read sigEnd)
14781490
let defaultImpl = has_default_impl(toks: read toks, start: read (pclose + 1), end: read sigEnd)
1491+
// A `fresh` anywhere in the return-type region (e.g. `-> Result<fresh String, E>`)
1492+
// also sets returns-fresh, matching parse_function_decl's region scan.
1493+
if hasRet && fresh_in_return(toks: read toks, start: read retStart, sigEnd: read sigEnd) {
1494+
retFresh = true
1495+
}
14791496
// Emit fn line.
14801497
local sb = StringBuilder.new()
14811498
StringBuilder.push(builder: mut sb, value: read "fn name=")
@@ -1532,6 +1549,20 @@ fn same_stmt_line(toks: read List<Tok>, a: read Int, b: read Int) -> Bool {
15321549
return tk_line(toks: read toks, i: read a) == tk_line(toks: read toks, i: read b)
15331550
}
15341551

1552+
// True if `fresh` appears in the return-type region [start, sigEnd) before the
1553+
// effects clause / body `{` / default-impl `=` (mirrors parse_function_decl).
1554+
fn fresh_in_return(toks: read List<Tok>, start: read Int, sigEnd: read Int) -> Bool {
1555+
let mut k = start
1556+
while k < sigEnd {
1557+
if is_kw_text(toks: read toks, i: read k, word: read "effects") || at_symbol(toks: read toks, i: read k, code: read SYM_LBRACE) || at_symbol(toks: read toks, i: read k, code: read SYM_EQ) {
1558+
return false
1559+
}
1560+
if at_ident(toks: read toks, i: read k, wid: read WORD_FRESH) { return true }
1561+
k = k + 1
1562+
}
1563+
return false
1564+
}
1565+
15351566
// Index of a top-level `effects(` keyword in [start, end), or -1.
15361567
fn find_effects_kw(toks: read List<Tok>, start: read Int, end: read Int) -> Int {
15371568
let mut depth = 0

selfhost/samples/ast/async_let.rss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
async fn run(body: read String) -> Int {
2+
async let a = Upload.put(path: read "/x", body: read body)
3+
async let b = Upload.put(path: read "/y", body: read body)
4+
return 0
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn edition_of(package: read JsonValue) -> Result<fresh String, JsonError> {
2+
return Ok(value: read "x")
3+
}
4+
5+
fn path_array(m: read JsonValue) -> Result<fresh List<String>, JsonError> {
6+
return Ok(value: read m)
7+
}

0 commit comments

Comments
 (0)