Skip to content

Commit cff4251

Browse files
olwangclaude
andcommitted
docs(selfhost-ledger): honest AOT caveat on SH-022 + record SH-024
- SH-022: mark the VM-vs-AOT figure as an EXTRAPOLATION from SH-006, not a measurement (only VM-vs-native and the StringBuilder control are measured); align the title to the measured ~5100x. - SH-024: record the multi-field variant destructuring limitation (RS0026 on positional Variant(a,b); struct-style Variant{a,b} + match-effect required) — found in the initial spike and used throughout the self-hosted code but not previously written to the ledger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9ef32e0 commit cff4251

1 file changed

Lines changed: 46 additions & 6 deletions

File tree

docs/ledgers/rss-selfhost-ledger.md

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ gap is VM value-representation / intrinsic-dispatch cost (the next big lever).
537537
- **Tests:** `crate::selfhost_parity::parser_parity_corpus`.
538538
- **Status:** decided.
539539

540-
### SH-022 — self-hosted lexer is ~5000× slower on the VM; cost is per-char intrinsic/collection dispatch, NOT string building
540+
### SH-022 — self-hosted lexer is ~5100× slower on the VM; cost is per-char intrinsic/collection dispatch, NOT string building
541541

542542
- **Tool:** self-hosted lexer (`selfhost/lexer.rss`) run on the reg-VM vs native
543543
`crate::lexer::lex`, over the whole 545-file corpus (712 KB).
@@ -565,15 +565,22 @@ gap is VM value-representation / intrinsic-dispatch cost (the next big lever).
565565
forces per-char boxed access).
566566
- **Decision:** the real lever is cheaper per-char access, NOT string building:
567567
e.g. a native string byte/char cursor intrinsic (iterate without materializing a
568-
boxed `List<Char>`), and lower per-intrinsic dispatch overhead. Cross-ref SH-006:
569-
AOT is ~144× faster than the VM on such code, so an AOT-compiled self-hosted lexer
570-
would land ~0.5 s (~30× native Rust) — the residual being the intrinsic *count*
571-
per char, which the char-cursor intrinsic would also cut. Feeds
568+
boxed `List<Char>`), and lower per-intrinsic dispatch overhead. Feeds
572569
[[perf-refactor-roadmap]] / [[jit-collection-perf-measurement]] with real-workload
573570
evidence (the trigger that work was waiting for).
571+
- **Measured vs. extrapolated (be honest):** only two things here are *measured*
572+
(a) the VM-vs-native table above, and (b) the `String.concat``StringBuilder`
573+
control. The VM-vs-**AOT** split is **NOT measured**: SH-006 measured AOT ~144×
574+
faster than the VM on comparable tool code, which *would* put an AOT-compiled
575+
self-hosted lexer near ~0.5 s (~30× native Rust), but this lexer has not actually
576+
been run under AOT. That AOT number is the piece that would separate *fixable VM
577+
per-op overhead* from the *inherent per-char intrinsic count* (which AOT also
578+
pays) — worth measuring as a follow-up (needs a file-reading lexer variant so a
579+
700 KB input isn't passed via `argv`).
574580
- **Tests / bench:** `crate::selfhost_parity::lexer_perf_corpus`
575581
(`--release -- --ignored`).
576-
- **Status:** open (VM/stdlib lever identified; feeds perf roadmap).
582+
- **Status:** open (VM/stdlib lever identified; feeds perf roadmap; AOT split
583+
still to be measured).
577584

578585
### SH-023 — self-hosted checker reaches RS0005 parity at declaration level; the merged callable namespace is the load-bearing rule
579586

@@ -619,3 +626,36 @@ gap is VM value-representation / intrinsic-dispatch cost (the next big lever).
619626
- **Tests:** `crate::selfhost_parity::checker_parity_tiny_sample` and
620627
`crate::selfhost_parity::checker_parity_corpus` (`--ignored`).
621628
- **Status:** done.
629+
630+
### SH-024 — multi-field variant destructuring is not positional; only struct-style field patterns bind
631+
632+
- **Tool:** pre-code feasibility spike for the self-hosting effort (`rss run --vm`).
633+
- **Symptom:** matching a sum variant with ≥2 payload fields positionally —
634+
`Add(l, r) => …` — fails: each binding is reported `RS0026 "unknown value
635+
binding"`. The struct-style form `Add { left, right } => …` works, but it also
636+
requires an explicit scrutinee effect (`match read e { … }`, else `RS0202`).
637+
Single-field positional binding (`Circle(r) => …`) *does* work, so the
638+
two-field failure is an inconsistency, not a blanket "no positional patterns".
639+
- **Minimal RSS:**
640+
```
641+
sum Pair { Both(a: Int, b: Int) Nothing }
642+
// fails: match p { Both(a, b) => ... } // RS0026 on a, b
643+
// fails: match p { Both { a, b } => ... } // RS0202 missing effect
644+
// works: match read p { Both { a, b } => ... }
645+
```
646+
- **Backend:** all (frontend / parser + binding resolution).
647+
- **Root cause:** positional binding is only wired for single-field variants;
648+
multi-field variants must be destructured with named `{ field, … }` patterns,
649+
which additionally project fields and so require a `read`/`mut`/`take` scrutinee
650+
effect. The two rules compound into confusing errors for the natural
651+
`Variant(a, b)` shape.
652+
- **Classification:** language (parser / pattern binding) + docs.
653+
- **Decision:** worked around throughout the self-hosted code by using
654+
`match read scrutinee { Variant { field, … } => … }`. Recorded here for
655+
completeness — this was found in the initial spike and used to choose the AST
656+
representation, but had not been written to the ledger. Language-side: allow
657+
positional binding for multi-field variants (or emit a targeted diagnostic
658+
pointing at the missing feature rather than `RS0026`).
659+
- **Tests:** covered indirectly by every `match read … { V { … } }` in
660+
`selfhost/parser.rss` / `selfhost/check.rss`.
661+
- **Status:** open (worked around; language decision pending).

0 commit comments

Comments
 (0)