diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-25 11:06:34 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-25 11:06:34 -0700 |
| commit | f258c28fb4fc98e472be2e57a2504dc1a955f2eb (patch) | |
| tree | 68733de8c475792a7df4d86af6c28dfa3e255f1b | |
| parent | 69e4a21f71aca4954469063387063434af35e679 (diff) | |
| download | Simple-f258c28fb4fc98e472be2e57a2504dc1a955f2eb.tar.gz Simple-f258c28fb4fc98e472be2e57a2504dc1a955f2eb.zip | |
Cleanup docs
| -rw-r--r-- | README.ja.md | 6 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | build-support/spill-matrix.py | 4 | ||||
| -rw-r--r-- | chapter24a/README.md | 3 | ||||
| -rw-r--r-- | chapter24b/README.ja.md | 1 | ||||
| -rw-r--r-- | chapter28a/README.md | 4 | ||||
| -rw-r--r-- | chapter28b/README.ja.md | 3 | ||||
| -rw-r--r-- | chapter28b/README.md | 1 | ||||
| -rw-r--r-- | chapter28b/ainotes.txt | 4 | ||||
| -rw-r--r-- | docs/appendices/README.md | 4 | ||||
| -rw-r--r-- | docs/chapter-backports.md | 253 | ||||
| -rw-r--r-- | docs/regalloc-spills.md | 2 | ||||
| -rw-r--r-- | docs/roadmap.ja.md | 468 | ||||
| -rw-r--r-- | docs/roadmap.md | 750 | ||||
| -rw-r--r-- | skills/chapter-split.md (renamed from docs/chapter-split.md) | 6 | ||||
| -rw-r--r-- | skills/chapter25-codex-notes.md | 35 | ||||
| -rw-r--r-- | skills/current-spill-matrix.md (renamed from docs/appendices/current-spill-matrix.md) | 2 | ||||
| -rw-r--r-- | skills/graph-viewer-codex-notes.md | 4 | ||||
| -rw-r--r-- | skills/incremental-builds.md (renamed from docs/incremental-builds.md) | 0 | ||||
| -rw-r--r-- | skills/japanese-translation.md (renamed from docs/japanese-translation.md) | 0 | ||||
| -rw-r--r-- | skills/stage-audit.md (renamed from docs/stage-audit.md) | 6 |
21 files changed, 316 insertions, 1248 deletions
diff --git a/README.ja.md b/README.ja.md index 96a8160e..41823d56 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,6 +1,6 @@ # Simple -[English](README.md) | 日本語 | [翻訳について](docs/japanese-translation.md) +[English](README.md) | 日本語 | [翻訳について](skills/japanese-translation.md) この文書は [Simple の README](README.md) の日本語訳です。 @@ -71,7 +71,7 @@ Simple 言語のスタイルは、C または Java のサブセットに倣っã� 付録:[ネイティブライブラリ](docs/appendices/native-libraries.md)、 [最適化とバックエンドの調整](docs/appendices/optimizer-tuning.ja.md)。 -旧章と現在の章の対応は[分割記録](docs/chapter-split.md)にあります。 +今後のコンパイラと言語の計画は[ロードマップ](docs/roadmap.ja.md)を参照してください。 ## 章をまたぐビルド @@ -95,8 +95,6 @@ CI は Linux と JDK 21 で `make lib tests CTAGS=` を実行します。 Make のターゲットは、それらを使うテストの実行前に、第28b章のネイティブランタイムと `sys` ライブラリもビルドします。Maven の Java ビルドだけでは、これらの前提となる成果物は生成されません。 -[章へのバックポート一覧(英語)](docs/chapter-backports.md)には、提案された小さな修正と -各章のテスト・レビューワークフローを記録しています。大規模なアーキテクチャ変更は、そこで別に管理します。 第10〜14章は現在、メモリ全体、必要時のエイリアス分割、参照、GCM、数値型の順に構成されています。 このメモリ表現は第28b章まで引き継がれています。 @@ -80,7 +80,7 @@ The Simple language is styled after a subset of C or Java. Appendices: [Native libraries](docs/appendices/native-libraries.md), [optimizer and backend tuning](docs/appendices/optimizer-tuning.md). -The [split record](docs/chapter-split.md) maps the old chapters to this sequence. +Future compiler and language work is collected in the [roadmap](docs/roadmap.md). ## Building across chapters @@ -105,11 +105,9 @@ CI runs `make lib tests CTAGS=` on Linux with JDK 21. The Make targets also buil Chapter 28b's native runtime and `sys` library before running the tests that use them; Maven's Java build alone does not produce those prerequisites. -The [chapter backport queue](docs/chapter-backports.md) records proposed small -corrections and the per-chapter test/review workflow. Larger architectural moves -are tracked separately there. Chapters 10-14 now group whole memory, +Chapters 10-14 cover whole memory, lazy alias splitting, references, GCM, and numeric types in that order. -The memory representation is forwarded through Chapter 28b. +Later chapters build on this memory representation. To build and test just the memory chapters: diff --git a/build-support/spill-matrix.py b/build-support/spill-matrix.py index 422be3a9..bd11c447 100644 --- a/build-support/spill-matrix.py +++ b/build-support/spill-matrix.py @@ -144,7 +144,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--log-dir", type=Path, default=ROOT / "build/spill-stats") parser.add_argument("--reuse", action="store_true", help="read completed logs without rerunning tests") - parser.add_argument("--update-docs", action="store_true", help="refresh the marked tables in docs and READMEs") + parser.add_argument("--update-docs", action="store_true", help="refresh the allocator report in skills/current-spill-matrix.md") args = parser.parse_args() args.log_dir.mkdir(parents=True, exist_ok=True) if not args.reuse: @@ -166,7 +166,7 @@ def main(): (args.log_dir / "matrix.md").write_text(matrix + "\n", encoding="utf-8", newline="\n") if args.update_docs: # Preserve the dated pre-split measurements in the original READMEs. - paths = [(ROOT / "docs/appendices/current-spill-matrix.md", matrix)] + paths = [(ROOT / "skills/current-spill-matrix.md", matrix)] # Validate all destinations before changing any document. for path, _ in paths: text = path.read_text(encoding="utf-8") diff --git a/chapter24a/README.md b/chapter24a/README.md index 338eebe8..0209b3ef 100644 --- a/chapter24a/README.md +++ b/chapter24a/README.md @@ -13,9 +13,6 @@ functions whose return types need inference across calls can still fail with `No defined return type`; Chapter 24b solves those recursive return cycles. -This is an independent compiler snapshot. The [split record](../docs/chapter-split.md) -explains how the former combined SCCP chapter became Chapters 24a and 24b. - ## Sparse Conditional Constant Propagation [SCCP](https://en.wikipedia.org/wiki/Sparse_conditional_constant_propagation) diff --git a/chapter24b/README.ja.md b/chapter24b/README.ja.md index 108732e7..32be519f 100644 --- a/chapter24b/README.ja.md +++ b/chapter24b/README.ja.md @@ -4,7 +4,6 @@ [Previous: Chapter 24a](../chapter24a/README.ja.md) | [Next: Chapter 25](../chapter25/README.ja.md) -[章の分割案](../docs/chapter-split.md) 前の章では関数ごとに保守的な呼び出し境界で解析しました。この章では 呼び出し辺を発見しながら、同じワークリストで関数間の値を伝播します。 diff --git a/chapter28a/README.md b/chapter28a/README.md index 051738b7..d46a24d7 100644 --- a/chapter28a/README.md +++ b/chapter28a/README.md @@ -270,5 +270,5 @@ and x86/ARM/RISC-V code generation. `make view` starts the shared graph viewer. The next chapter will keep this language behavior while allowing a client to compile without the library's sources. That requires saving types and ideal IR, remapping identities on import, and extending the same initialization checks -across imported bodies. Library construction and optimizer tuning remain -appendix topics in the [approved split](../docs/chapter-split.md). +across imported bodies. The [appendices](../docs/appendices/README.md) cover +library construction and optimizer tuning. diff --git a/chapter28b/README.ja.md b/chapter28b/README.ja.md index d04e9ccb..c4f60712 100644 --- a/chapter28b/README.ja.md +++ b/chapter28b/README.ja.md @@ -85,7 +85,7 @@ java -ea -cp "build/classes/main:build/classes/test:lib/*" \ コンストラクタのフィールド更新はローカル値と Phi のままで、ヘルパーの出口に Store を 置きます。未完成のオブジェクトでも初期化済みフィールドは読めます。 アンダースコアは公開インターフェースの選択に使いますが、ソースのアクセス制限は -[レビュー待ち](../docs/chapter-backports.md)です。 +まだ実装されていません。 ## 検証と今後 @@ -104,5 +104,4 @@ make tests CHAPTERS=chapter28b オブジェクトを再構築してください。高コストの往復一致検査は無効のままで、古い増分 ビルドの実験 `testModule0` も実行対象外です。 -[分割計画](../docs/chapter-split.md)の主要な七つのスナップショットはこれで揃います。 ネイティブライブラリの構築と最適化の調整は[付録](../docs/appendices/README.md)にまとめています。 diff --git a/chapter28b/README.md b/chapter28b/README.md index 189f2b7b..f464fed4 100644 --- a/chapter28b/README.md +++ b/chapter28b/README.md @@ -172,6 +172,5 @@ serialization bijection check remains disabled, and `testModule0`, the older incremental-rebuild experiment, remains ignored. They are not validation guarantees for this snapshot. -This completes the seven main snapshots in the [approved split](../docs/chapter-split.md). See the appendices on [native libraries](../docs/appendices/native-libraries.md) and [optimizer/backend tuning](../docs/appendices/optimizer-tuning.md). diff --git a/chapter28b/ainotes.txt b/chapter28b/ainotes.txt index c0b03a4d..5678e686 100644 --- a/chapter28b/ainotes.txt +++ b/chapter28b/ainotes.txt @@ -1,7 +1,7 @@ Historical design notes from the former combined Chapter 25, now Chapter 28b. -Current status and remaining work are in ../docs/stage-audit.md and -../docs/chapter-backports.md. The original intent below is preserved. +The cleanup record is in ../skills/stage-audit.md; remaining work is in +../docs/roadmap.md. The original intent below is preserved. Meta-info: I intend to keep extending and re-editing this "ainotes.txt" file so I can reprompt the you (the AI) between AI restarts, As such this file will diff --git a/docs/appendices/README.md b/docs/appendices/README.md index d7803c97..9e39770c 100644 --- a/docs/appendices/README.md +++ b/docs/appendices/README.md @@ -9,7 +9,3 @@ another compiler directory. rematerialization, inlining, and historical measurements from the split's original snapshots. [Measurement methods](../regalloc-spills.md) describe what those numbers include. - -The [roadmap](../roadmap.md), [incremental build design](../incremental-builds.md), -and [review queue](../chapter-backports.md) distinguish future work from current -behavior. The [stage audit](../stage-audit.md) records known limits and cleanup. diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md deleted file mode 100644 index e7293d8c..00000000 --- a/docs/chapter-backports.md +++ /dev/null @@ -1,253 +0,0 @@ -# Chapter backport queue - -This file tracks pending backports and deferred work. Completed work and old -validation results belong in Git history. Reusable implementation constraints -live in the [AI notes](../skills/chapter25-codex-notes.md). - -The [completed split](chapter-split.md) places local/interprocedural SCCP in -24a/24b, incomplete types in 25, construction in 26, escape in 27, and -modules/separate compilation in 28a/28b. The combined old 24/25 snapshots are -retired. Do not backport fixes into those retired copies; their replacements -already retain the constructor, alias, native-exposure and import fixes. - -Native library construction and tuning material live in [appendices](appendices/README.md). -Future incremental builds are specified in [incremental-builds.md](incremental-builds.md). - -## Four README intentions to review - -Keep these four issues visible while reviewing the named chapters. Decisions -may change the construction/escape boundary or later chapter splits. - -1. **Construction (active):** final fields should be local SSA values during - construction, including repeated assignments and loops, with field Stores - at helper exits. Silent loss of writes is a bug; reject unsupported cases. - Also decide whether to allow helpers after inlining and defer completion - checks until SCCP. Those are separate from local field updates. - The local-value implementation now supports final data fields in construction, - escape, and modules, including constant defaults and loop Phis. Shared - function declarations remain fixed. Validate this frontier before adjusting - the broader constructor rules. -2. **Privacy:** underscore names select the public interface, but sibling access - to private fields is not yet rejected. Confirm and enforce the intended - source access rules. The proposed chapter boundary and library changes are - recorded below; Cliff reviews that proposal before implementation. -3. **Class initialization:** review the corrected rule: A may start, initialize - B, then resume. Passive references may cycle; active dependencies may not. - Parents finish before children start. The README now matches this behavior; - further semantic changes need an explicit decision. -4. **Escape summaries and ELF visibility:** analysis tracks unknown callers, - while the current ELF writer exports retained named functions. Decide which - symbols should be public and how that boundary relates to separate compilation. - -### Privacy proposal for review - -Introduce enforced underscore privacy in **Modules and Lazy Class -Initialization** (`chapter28a`). This is a visible language change: -earlier snapshots allow underscore field access; from Modules onward, access -must obey the declaring class's boundary. The teaching outline already places -privacy here, alongside class names, ownership, and the public interface. -The earlier escape snapshot does not filter fields by underscore spelling. -Its private construction memory is a different concept. - -Review follow-up: preserve the distinction between export filtering and access -checks. Before the 2026-10-24 cleanup, the first underscore helper in the -numbered snapshots' tests was Chapter 19's `Chapter19Test.testString`, -`_hashCodeString`. Renaming it to -`hashCodeString` produces the identical LocalSched graph there: single-use -inlining removes it regardless of spelling. In Chapter 25's copy of that test, -the same rename retains an extra function Return. Underscore export filtering -currently starts in Chapter 25 (and its Modules extraction), not Chapter 19. -Old test class names inside newer snapshots do not identify when their current -underscore behavior was introduced. Any access-check change must preserve the -existing ability to hide helpers and classes from the public interface. -Chapter 19's example now uses `hashCodeString` and field `hashCode`; all 42 -Chapter19Test tests pass with the existing expected graph unchanged. - -Proposed rules: - -- A field or method beginning with `_` is accessible only from code in its - declaring class, including its constructors and method bodies. Enclosing, - nested, and sibling classes do not gain access to that field. A same-class - method may access another instance's private fields; the receiver need not - be exactly `self`. -- A private class name is available in its declaring scope and within the - class itself. For example, `App` may name its child `_Cache`, but that does - not grant `App` access to `_Cache._data`. Code outside that declaring scope - cannot bypass the restriction with a dotted path. Private ancestors also - keep their descendants out of the public interface. Check both type names - and class-value references, and distinguish naming a private class from - using a public value returned by a factory. -- Reads, writes, updates, and method references follow the same rule. - Preserve the source owner and location when receiver types are unresolved; - inlining must not change who has access. An unused result or a folded Load - must not erase an illegal source access. -- Native declarations obey the same source rules. A public wrapper or alias - may deliberately expose a private native binding; no exception for `sys`. - -Source audit: - -- `sys/io.smp` calls `libc._exit` twice. Keep the native `_exit` declaration - private in `sys/libc.smp`, add `val exit = { i32 status -> _exit(status); };`, - and use `libc.exit` in IO. This preserves the native symbol and behavior. -- Bitset's `_bits` and `_grow`, including `_grow(bit)._bits`, are used within - the declaring struct and remain legal. Array helpers call their own `_grow`. - These are the other underscore names in the bundled module library. -- Several retained String spill fixtures use free functions outside `_String` - to access `_hashCode`. They will need an explicit adaptation in this snapshot - (for example, making that fixture field public), with any changed allocation - expectations measured. Do not silently grant file-wide access to pass them. -- The numbered Chapter 25 library has the same `_exit` accesses, and its - `docs/examples/FileIO.smp` also calls `sys.libc._exit`. - -After approval, implement and test in Modules, update its English/Japanese -READMEs with the explicit language boundary, and run the full module suite -including bundled `sys` and native execution. Cover allowed same-class access, -rejected cross-class access, private class lookup, and forward receiver types. -Carry the rule and library adaptations into Chapter 28b, with import tests -preserving the original source owner. Do not backport the new -language restriction into Chapters 10-24 or the named pre-module snapshots. - -## Investigate next - -Establish an earlier-chapter failure before changing compiler behavior. - -### Narrow stores in the ideal evaluator - -The prefix-update regression exposed an independent evaluator bug. In Chapter -23, this postfix-only example returns 25710 in Eval2 instead of 110: - -```simple -struct C { u8 x=255; val next_noInline={ -> x++; return x; }; }; -var c=new C; -val a=c.next_noInline(); -val b=c.next_noInline(); -return a+10*b+100*c.x; -``` - -`Eval2.store` copies the raw value into its Java object array. It does not -truncate to the field width, even though Store optimization removes masks that -machine stores perform implicitly. The native prefix-update regression checks -that the stored byte wraps and the expression returns the converted value. -Establish the first affected evaluator and check signed fields and floating -rounding before propagating a fix. Do not change valid generated code to match -an incorrect ideal evaluator. - -### SCCP function revival: Chapter 24 - -`Opto.linkCG` uses `code.link(fidx)`, which clears a linker entry when its -function is structurally dead. Chapter 25 uses the non-cleaning `_link(fidx)` -while discovering calls, so temporarily disconnected functions can be revived. -Chapter 24 already has `_link`; the question is whether its own unlinking and -SCCP sequence can expose the same failure. - -Trace a function from removal of its unknown-caller edge through discovery of -a real call. Preserve the distinction between unreachable control and a -structurally deleted node. No earlier-chapter failure is established yet. -Dependency propagation already exists in Chapter 24's SCCP; only port a -specific missing dependency with a reproducer. Keep Chapter 25's escape and -external-caller machinery out of this investigation. - -Investigation on 2026-10-19 found no cleanup loss. A shadow Opto checked the -linker entry before and after every `linkCG` lookup while running all 513 -Chapter 24 tests: 787 SCCP runs, 297 lookups, 74 links to functions with only -their null input remaining, and zero structurally dead targets or removed -entries. All tests passed. The remaining null lookups are not evidence of a -cleanup loss: their entries were already null before the lookup. - -The factorial trace shows the distinction: FunNode#1309 loses Start and has -one input, four uses, and type TOP; it then reaches XCONTROL before a real -call links it. `isDead()` stays false, since it requires no inputs, no uses, -and a null type. Thus the observed temporary lack of callers does not trigger -`link()` cleanup. Keep this candidate unconfirmed rather than backporting the -Chapter 25 lookup change on textual differences alone. - -## Future compiler work - -### Deterministic inlining - -The parse-size cutoff can reject a function whose optimized body is small. -After shrinking rewrites converge, choose one growing inline using current -body sizes and a deterministic ranking. Cache sizes per convergence round; -use stable call-site provenance to break ties, not worklist order or newly -allocated node IDs. Avoid per-node dependencies and repeated whole-body scans -while a large function gradually shrinks. - -The bounded boilerplate-inlining heuristic is already in Chapters 20-24. -This item is the larger Chapter 25 redesign. - -### Conversion and BOTTOM-on-error contracts - -Coordinate these changes with the incomplete-type/conversion split: - -- Chapter 24 Loads supply a typed result even when they report an error. - Changing that result to BOTTOM alone breaks incomplete loop typing and can - obscure the intended diagnostic. Chapter 25's Convert nodes provide the - surrounding typed result. Relevant existing cases include - `testBrainFuck`, `testWhileWithNullInside`, `testNullGuardErrors`, and - `testLinkedList0`. -- Chapter 24 arithmetic also needs its typed fallback while forward references - are unresolved. Returning BOTTOM confuses unresolved expressions with - uninitialized fields (`testCoRecur`) and loses arithmetic diagnostics. - -Preserve the current contracts until the parser, conversions, loop bounds, -and diagnostics can move together. Do not weaken narrowing assertions or -expected errors to make an isolated backport pass. - -### Representation changes - -The approved split keeps incomplete types, numeric modes, conversions, guards, -and symbolic fields together, then introduces private constructor memory in -the construction chapter. They are not independent fixes to copy into earlier -snapshots. - -Serialization, global identity remapping, and imported escape summaries belong -with separate compilation. Escape analysis now has its own earlier snapshot after -construction; keep its boundary open to review. - -### Selective loading of library IR - -Reliable incremental rebuilding is separate future work; see the -[incremental build design](incremental-builds.md) for the stale-dependency -example, required input tracking, rebuild propagation, and validation plan. - -The Separate Compilation README review found that importing only `sys.io.p` -still loads all ten units in the bundled `sys.o`, including `aryu8`, `aryi64`, -`scan`, and `adt.bitset`. All remain loaded through TypeCheck. Loading only -needed library members is a desired improvement, not current behavior. - -Selection works between independent objects: a client of `Counters.o` leaves -an unrelated `Unused.o` unloaded. Once selected, an object's entire saved IR -is reconstructed, and `ParseAll.loadDeps` follows its recorded dependencies. -Splitting the object alone is insufficient while the root's reexports and -unused function bodies still pull in those dependencies. Design member-level -IR loading and dependency discovery together; preserve type identity, -initialization rules, and imported escape summaries. This is separate from -whether imported code is initialized or emitted in the client's object. - -## Modules and separate compilation - -Class-cycle checking and CLInit runtime lowering belong in 28a/28b. The -[module README](../chapter28a/README.md) explains their semantics; remaining -argument, initialization and pointer plans live in the [roadmap](roadmap.md). -Native code cannot receive classes; a native-startup/class-handoff policy is -not planned. Current data pointers remain eight bytes. - -## Backport validation - -Introduce an independent fix in the earliest applicable chapter and propagate -it through every affected snapshot. For correctness fixes, first reproduce the -failure in the destination compiler. Preserve each chapter's representation -and tutorial boundary. - -Run focused checks and the existing full Make suites for the affected compiler -directories, for example: - -```sh -make -k tests CHAPTERS="chapter20 chapter21" -``` - -Chapter 25's inherited tests do not validate earlier compiler implementations. -Force rebuilds when older Makefiles miss dependencies, and rebuild serialized -libraries when their compiler or object format changes. Keep execution and -register-legality checks independent of spill expectations. Preserve unrelated -edits, keep text LF-only, and run `git diff --check`. diff --git a/docs/regalloc-spills.md b/docs/regalloc-spills.md index 71bab467..a4ca818f 100644 --- a/docs/regalloc-spills.md +++ b/docs/regalloc-spills.md @@ -3,7 +3,7 @@ This is the dated **pre-split** comparison. Its Ch 24/25 columns describe the retired combined snapshots, not today's Chapter 25 (incomplete types). Current measurement tools target 24b/28b and publish a -[separate table](appendices/current-spill-matrix.md). The historical numbers +[separate table](../skills/current-spill-matrix.md). The historical numbers below remain unchanged. Read a row left to right to follow a fixed cohort through successive compilers. diff --git a/docs/roadmap.ja.md b/docs/roadmap.ja.md index 0af228a5..a44afab5 100644 --- a/docs/roadmap.ja.md +++ b/docs/roadmap.ja.md @@ -1,455 +1,33 @@ -# ロードマップ - -この設計ノートは旧第25章から移したものです。初期の節には、現在の第25〜28b章で -実装済みの機能について検討した案も含みます。すべてが未実装という意味ではありません。 -現状と残る課題は[監査記録](stage-audit.md)、増分ビルドは[設計メモ](incremental-builds.md)を参照してください。 +# コンパイラのロードマップ [English](roadmap.md) | 日本語 -この文書は [ROADMAP.md](roadmap.md) の日本語訳です。原文の設計案と未解決の問いを、そのまま設計案として記しています。 - -## 分割コンパイル - -未知の前方参照をどう扱うか? H-M は使わないので、単一化も行わない。 -関数以外の前方参照を認めないことにする……関数なら、定義した時点ですぐにシグネチャを提供できる。 - -案 A:FRefNode を削除し、すべてのバグを直す。 -案 A の問題:相互再帰を使えるようにしたい。 -例:`val even = { int x -> x ? odd(x-1) : true; }; val odd = { int x -> x ? even(x-1) : false; };` - -案 B:型は順序どおりに定義する必要があるが、それ以外の関数には順序を要求しない。 -`new T` ならエラーにできるが、`fref.field`、`fref()`、`fref op fref`、`fref[]` ではできない。 - -あらゆる場所で Type.BOTTOM をパーサーの要件に合わせて「引き上げる」必要がある。 -FRef からフィールドをロードするには、FREF を FIELD のある TSTRUCT への TMP に引き上げる必要がある……。 -実際には、FREF が正しくなると局所的に仮定するだけだ。型は任意のコードから来るので、 -アクセスできず、引き上げられない。TMP は nullable かもしれない(後でエラーにすべき)。 -TSTRUCT は無名にでき、無名になるだろう。これは、すべての TSTRUCT を無名にすることを意味する。 - -問題は、未知・遠隔・外部・前方の参照にある。 - -FRef を*少しでも*認めると、すべての処理が FRef 型に遭遇することを考慮しなければならない。 -同じスコープの関数で認めると、上の odd/even の例が使える。 -同じスコープの構造体で認めると、相互再帰的な構造体が使える。 -スコープの外へ昇格させる際には外部の検索を試み、見つからなければ失敗する。 - -任意の使い方の例:`sys.fld`。`sys` の検索に失敗したら、次のどちらかを推測しなければならない。 -前方参照で問題なく、間もなく定義されるのか……それとも今すぐ外部を検索するのか。 -さらに、使い方によっては問題の発覚が遅れる。 -```java -// Extern or Future `sys` being returned. -val foo { -> return sys; } - -// foo() returns FRef, so Bottom, so the `.bar` has no type -// to do field lookups -val main { arg -> foo().bar } - -// HERE: Parser finds `sys` OR NOT?!?!?! -val sys??? = user_stuff....; - -``` - -行き着く先は、パーサーが一切エラーを出さず、盲目的な AST のように SSA を構築し、 -不適切な型だらけのグラフを結線するだけになることだ。 -必要なこと:パーサーを導く型をなくす! 構文だけを使う! - -(明らかな小さな問題:浮動小数点数へ拡張するかどうか。) -修正:たとえば整数演算を挿入する。両方の入力が isa-Int であり、isa-Float に引き上げられなくなるまでは、 -戻り値の型を Int と Float の MEET とする。 - -メソッド呼び出しで起こり得る問題: -`UNKNOWN_STRUCT_BOT.UNKNOWN_FIELD ( ARGS ); // Insert SELF OR NOT?` -— SELF を挿入するか? -修正案:常に SELF 引数があると仮定し、静的と証明できる場合は null にする。 -最適化:定数パラメータをシグネチャから削除するか? *定数*パラメータは、 -*パーサー*の時点で定数でなければならない。そうでなければ未知の呼び出し元がないことが必要になる -(したがって局所的な最適化になる)。 -修正案:SELF 引数を*最初*ではなく*最後*に置くと、不要になった場合に削除しやすいか? - -Load は ConFldOff を捨て、名前を保持する。 -Load はスケーリング前の添字を保持し、添字入力を持つ。構造体ではこの入力は null。 -Ptr Base が*確定*した後で基底の配置を行い、添字の計算を畳み込める。 - -- `Load(ctrl, base, null );` — フィールド名は内部に持つ。 -- `Load(ctrl, base, index_math );` — フィールド名は `[]`。 -- `Store(ctrl, base, null , val);` - -問題:エイリアス情報がない! - -案 C:直接呼び出し `odd()` と型では FRef を認める。それ以外はすべて*引き上げ*が必要。 - -## 構造体と配列の統合 - -構造体のフィールドは名前で索引付けされ、配列の要素は数値で索引付けされる。 -この2つの考え方は両立する、という発想です。 -C 言語では古くからこの考え方を使っています。通常の C の構造体の後に配列を続け、 -通常の配列構文で索引アクセスできます。 - -別の方法として、配列へのポインタをフィールドに保持すると、間接参照が1つ増える代わりに柔軟性が得られます。 -特に、配列を(通常はより大きな配列に)交換できます。 -これは、広く使われる汎用コンテナ `Vector` や `ArrayList` の基礎です。 - -```java -struct str { - int hash; // A cache of the strings hash code - u32 #; // The array length - u8 []; // Array of bytes -}; - -str X = "abc"; -print(X.hash); // Read the hash field -X[0]=='a'; // Read array element 0 -X#; // Length of the array - -``` - -ここでは、`#` というフィールドが配列の長さを保持し、`[]` というフィールドが実際の配列です。 -メモリ内では、この配列がほかのフィールドの直後に続きます。 -可変長配列の別の例を示します。 - -```java -struct vecInt { - u32 size; // Actual number of elements - u32 #; // The array length or capacity - int []; // Array of integers. -}; -``` - -明らかな制約として、配列のサイズを直接変更することはできません。 -先頭のフィールドと同じメモリブロックに実際に配置されているからです。 -このような `vecInt` を、より大きい新しい `vecInt` に*再割り当て*することはできますが、 -元の `vecInt` は解放されます。同じ `vecInt` の参照を永遠に使い続けることはできません。 - -名前付きフィールドはいくつあってもかまいません。 - -```java -struct Class { - !str !className; - !str !professor; - int time; - int credits; - !Class !prerequisite; - u16 #; // Limit of 65535 students per class - Student []; -}; -``` - -自己参照も使えます。 - -```java -struct NTree { - Some val; - u8 #; // Length is 0-255 - NTree []; // More NTrees -}; -``` - -構文規則は次のとおりです。 - -- 最後のフィールドの名前は `[]` で、どの型でもよい。 -- 最後から2番目のフィールドの名前は `#` で、配列の最大長を表す符号なし整数型でなければならない。 -- ` []` の前に空白が必要になるかもしれない。 - -### コンストラクタと配列 - -割り当てには配列の長さを含め、コンストラクタも含められます。 -`new NTree{val=17;}[2];` - -コンストラクタは省略できますが、配列の長さは省略できません。 - -配列を初期化するための任意の関数を使うと、一度だけ書き込む final 配列を作れます。 -`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]` - -### 配列の最大長の変更 - -範囲チェックのある小さな配列を大量に使う場合、配列の長さのために丸々4バイトを確保すると、 -配列のオーバーヘッドが大きく増えることが問題になります。 -Simple では最大長を小さくできます。ただし、最大長が異なる配列は別の型として扱います。 - -```java -struct x0 { u8 #; u8[]; }; // Array limited to 0-255 length -struct x1 { u16 #; u8[]; }; // Array limited to 0-65535 length -struct x2 { u32 #; u8[]; }; // -struct x3 { u64 #; u8[]; }; // - -x0 x = "abc"; // 4 bytes total: [3, 'a', 'b', 'c'] -x1 y = "def"; // 6 bytes total: [3, 0 , 'd', 'e', 'f', 0,]; 1 pad byte to acheieve 2-byte alignment -x = y; // ERROR, "x1 is not x0", no auto-widening -x += y; // OK, 7 bytes total: [6, 'a', 'b', 'c', 'd', 'e', 'f'] -y += x; // OK, 8 bytes total: [6, 0 , 'd', 'e', 'f', 'a', 'b', 'c'] - -x += "VeryLongString.... ....255_chars"; // Runtime RangeCheck - -// Explicit conversion to widen string -y = "" + x; -``` - -### 拡張可能な配列・文字列の2つの形:間接参照を増やすかどうか - -間接参照を1つ増やすと、Java の `ArrayList` や C++ の `Vector` のようになります。 - -増やさない場合、拡張可能な配列を伸ばす呼び出しは、新しいオブジェクトを返す可能性があります。 -拡張は必要な場合だけ行います。 - -```java -val vec1 = new vecInt[0].add(2); // len=1, capacity=1 -val vec2 = vec1.add(3); // NEW !vecInt !returned, only is deleted; len=2; capacity=2; -vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)` -vec2[0]; // OK -vec2[1]; // OK -vec2[2]; // Runtime error, AIOOBE -``` - -## 文字列の実装 - -既定の文字列は「str」「cstr」「ustr」などの名前を持ちます。 - -常に unicode8 を使い、u8 を明示的に操作します。 -Unicode の「文字」は、基礎となる実装の上に関数呼び出しとして提供します。 -おそらく別の文字列クラスで実装することになります。 - -## 既定の文字列 - -連結によって効率的に拡張でき、さまざまな IO/printf 関数をサポートします。 -明示的に要求すれば、基礎となる配列を可変な状態で共有できます。 -既定では常に、非共有時は可変、共有時は読み取り専用です。 - -```java -struct str { - - // Add a string to the string, possibly growing - val add = { str str -> - val self = this; - for( int i=0; i<str#; i++ ) - self = self.add(str[i]); - return self; - }; - - // Add an char to the string, possibly growing - val add = { u8 e -> - if( len >= # ) return copy(# ? #*2 : 1).add(e); - [size++] = e; - return this; - }; - - // Copy 'this' to a new larger size - val copy = { int sz2 -> - val v2 = new str[sz2]; - for( int i=0; i<len; i++ ) - v2[i] = [i]; - v2.len = len; - sys.libc.free(this); // Needs a better Mem Management solution - return v2; - }; - - val print { -> sys.libc.write(1/*stdout*/,[]/*array base as argument to C*/,len); return this; } - - u32 len; // In-use size - u32 #; // Max length is 4Gig, although variants can request smaller lengths - u8 []; // Character data -}; -``` - -### Final な文字列 - -**不変**な文字列を意図しており、**安全な**文字列の基礎にも使えるかもしれません。 -一度に全体を作成する必要があり、おそらく防御的コピーも必要になります。 - -```java -struct sstr { - // Possible static call syntax; no references to "this" - val make { str str -> new sstr[str#,str.at] }; - - // Array contents are immutable. - // Still figuring out good syntax for this. - u8 #; - u8 [~]; // Middlin '~'? Makes primitive array contents *immutable* and requires constructor syntax -}; - -// Construction requires a function that produces the array contents. -// Here we are defining a defensive copy over `buf`. -sstr safe = new sstr[buf.len,{ int idx -> buf[idx]; }]; -sstr safe = sstr.make("abc"); // Defensive copy is made -``` - -### 拡張可能な文字列(StringBuilder または StringBuffer) - -この API は `StringBuilder` スタイルの代替としてそのまま使えることを意図しており、 -使うたびに間接参照が1つ増えます。 - -```java -struct xstr { - !str !_str; // The extra indirection is here - val add = { str str -> _str = str.add2(str); }; - val write = { int fd -> _str.write(fd); }; -}; -``` - -### Unicode 文字列 - -この API は、通常の `str` の上で Unicode の文字を操作できるようにすることを意図しています。 - -```java -struct ustr { - // Assuming idx is at the start of a character, return the character - // as a 32b integer. Returns junk integer if not at a character start. - // For all ASCII strings this will amount to a simple byte load. - // For bytes with high order bits set, this may read 1-4 characters. - val at = { int idx -> ... }; -}; -// Unicode character iterator -struct ucharator { -// Return successive unicode character codes -}; -``` - -### 「C」文字列 - -ゼロ終端の文字列ですが、長さを直接取得できる必要があります。 - -```java -struct cstr { - u8 #; // length including the trailing zero - u8[]; // always ends in a zero byte; suitable for direct passing to C string functions -}; -``` - -### 「german string」:レジスタに格納できる非常に短い文字列 - -メモリ内では、長さを表す1バイト(3ビット?)に続いて0〜7文字を格納し、最大8文字です。 -64ビットのレジスタに収まります。 - -```java -struct gstr { // aka germanString, or blend string and register "streg" - u3 #; // Size is 0-7 bytes??? - u8 []; // Up to 7 bytes -} -// Using inlined object (see below). All these strings are short and are -// represented as packed integers in a 64b register. -*gstr prize1 = "gold"; -*gstr prize2 = "silver"; -*gstr prize3 = "bronze"; - -``` - -### Main は `val main = { str[] args -> ...}` になる - -## 構造体のインライン化 - -構造体は通常、参照として扱いますが、「内容(contents of)」と読む `*` 演算子を使って -値として扱う形へ変換できます(この名前は、たとえば `inline` に変えるかもしれません)。 - -これは名前空間の変更だけです。内部の構造体の寿命を完全に消費しない限り、そのアドレスは取得できません。 -「外部への流出」は認めません。 - -```java -// 2 64-bit words, no other overheads -struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; }; - -// Current Simple rules: always by-reference -struct ByRef { - !Complex !c; // 4-byte pointer to a Complex -}; -print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c - -// New behavior: by value (inlining a struct) -// The '*' syntax indicator can be something else, e.g. a keyword "inline" -// I am pronouncing '*' as "contents of" -struct ByValue { - *!Complex !c; // c is inlined, full 16 bytes into ByValue -}; -print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load - -// Mixing Refs and Struct Values. Basically, a ref can be converted -// to a value by taking the "contents of" the ref, and values cannot -// be *converted* to a ref, although they can be copied over a ref. -ref. c = val.c; // Error, mixing refs and values -ref.*c = val.c; // Allowed, whole structure copy. Does not allocate, requires ref.c not-null -val. c = ref.c; // Error, mixing refs and values -val. c = *ref.c; // Allowed, whole structure copy -ref. c = ref.c; // Allowed, pointer copy -val. c = val.c; // Allowed, whole structure copy. -val *c = val.c; // Error, cannot take "contents of" a value - -// Alternative syntax, to avoid the "contents of" being on LHS and field applying to being on RHS: -ref.c = val.*c; // Assign into ref.c the "contents of" val.c - -// Arrays of inlined structures -var ary = new *Complex[99]; // Array of 99 !Complex !objects, inlined - -// Calling methods has the same syntax -ref.c.len(); -val.c.len(); - -// Function arguments use the same typing, so can pass-by-value -val math.sin = { *Complex c -> ... }; // Passes a complex by value -math.sin(ref.*c); // Requires de-reference to pass by value -math.sin(val. c); // Pass by value - -``` - -## ほかの章のロードマップ - -`&&` と `||`。 -`int` を `i32` に縮小する。 -`TypeMemPtr` を4バイトに縮小する。`mmap` を使い、ヒープを下位4Gの領域に置く。 -パーサーの既定の for ループ構築、または部分的なループ剥離。 -`for( init; test; next ) body` は、次のようになる。 -```java -{ init; // Normal scope entry to bound lifetime of index variable -if( !test ) { // Zero-trip count test - do { - body; - next; - } while( test ); // Exit test at loop bottom by default -} }; -``` - -## `&&` と `||` - -論理演算子 &&(AND)と ||(OR)は短絡評価を行います。 -最初の条件だけで結果が決まるときは、第2の条件の評価を省略できます。 - -- `&&` は最初のオペランドが偽なら評価を止めます。 -- `||` は最初のオペランドが真なら評価を止めます。 - -これらは制御フローを読みやすくする糖衣構文であり、実行モデルに新しいノードを作りません。 - -`&&` と `||` の構文は、制御フローだけを変える単純な糖衣構文であり、新しいノードを導入しません。 - -次の例を考えます。 -```java -int a = 1; -int b = 0; +未完了の作業と将来の言語設計案の要約です。現在の言語仕様や実装済みの機能を +示すものではありません。詳細と最新の判断は[英語版](roadmap.md)を参照してください。 -if(a++ || b++ ) { - if(b == 0 && a == 2) { - sys.io.p("Or"); - } -} else{ - sys.io.p("And"); -} -return 0; -``` +## 正確性とコンパイル -- `a++` は1(真)と評価されるので、`b++` は評価されません。 -- その結果、`b` は0のままで、`a` は2になり、「Or」が表示されます。 +- 理想 IR の評価器で、狭いフィールドへの Store に必要な切り詰めと丸めを行う。 +- 依存関係全体を調べ、変更を伝播してから保存 IR を再利用する、正しい増分ビルドを実装する。 +- ライブラリオブジェクト全体ではなく、必要なメンバーの IR を選択してロードする。 +- 最適化後の関数サイズと安定した順位を使い、インライン化の選択を決定的にする。 +- CFG の実行頻度推定、ループの回転、定数インスタンスフィールドの格納省略を検討する。 -```java -int a = 1; -int b = 1; +## 言語とランタイム -int x=1; -int y=1; -int z=0; +- アンダースコアによる公開インターフェースの選別に加え、ソースの private アクセス規則を決めて強制する。 +- 未完成の self をヘルパーへ渡せる条件と、コンストラクタの完了検査を行う時点を検討する。 +- ELF シンボルの可視性と、未知の呼び出し元・コード出力の所有権を区別する。 +- クラス初期化子の整数引数を引数配列に置き換える。 +- 初期化全体を静的データで表せるクラスについて、実行時の初期化呼び出しと状態を省略する。 +- コンパクトなコード/データ領域、ネイティブポインタ型、GC に依存しない寿命管理を設計する。 -int g = x++ && y++ && z++; -... -``` +## データとライブラリの設計案 -この例では、次のようになります。 +- 名前付きフィールドの末尾に配列を配置する構造体と、異なる幅の長さフィールド。 +- 要素を生成する関数を使う配列初期化。 +- 可変・不変・C 互換・Unicode・短い値文字列の各用途に応じた文字列ライブラリ。 +- 構造体のインライン格納と値渡し。 +- `int` の既定幅を現在の `i64` から `i32` に変えるかどうか。 -- `x++` は真(1)なので、`y++` を評価します。それも真です。 -- 続いて `z++` を評価します。これは偽(0)です。 -- `&&` はすべての条件が真であることを要求するため、最終結果 `g` は0になります。 -- 偽になるオペランドまでのすべてのオペランドを評価するため、`x`、`y`、`z` はすべてインクリメントされます。 +これらの言語変更には、それぞれ設計判断と導入する章の境界が必要です。 diff --git a/docs/roadmap.md b/docs/roadmap.md index aad2d9ca..d441abb9 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,533 +1,285 @@ -# Compiler roadmap and design notebook - -This notebook preserves the design discussion from the former combined Chapter -25. Early sections include alternatives considered while implementing features -now taught in Chapters 25–28b; they are not all pending tasks. The -[stage audit](stage-audit.md) distinguishes current behavior from remaining gaps. -The [incremental-build design](incremental-builds.md) is a separate future item. -The final section records the outstanding class-initialization and compiler -follow-ups. Keep speculative language/library designs as proposals. - -## Separate Compilation +# Compiler roadmap English | [日本語](roadmap.ja.md) +Open compiler work and language proposals, reviewed against the current +snapshots on 2026-10-25. These are future changes, not promises about the +language implemented by the tutorial. Language changes still need their own +design decisions and chapter boundaries. -How to handle unknown forward ref? -No H-M, so no unification. -Demand no forward refs beyond functions... which will provide immediate signatures when def'd. - -Plan A: Remove FRefNode; fix all my bugs. -Fail A: I want mutual co-recurusion to work -Example: `val even = { int x -> x ? odd(x-1) : true; }; val odd = { int x -> x ? even(x-1) : false; };` - -Plan B: Require types ordered, but not any other fcn. `new T` can complain, -but not `fref.field` or `fref()` or `fref op fref` or `fref[]` - -Requires "lifting" Type.BOTTOM to the parser requirements everywhere. -So loading a field from an FRef requires: lift the FREF to a -TMP-to-TSTRUCT-with-FIELD... actually, just locally assume FREF will become -correct. Can't Lift it, no access, since type comes from random code. -TMP might be nullable (should error later). -TSTRUCT can/will be unnamed. Implies ALL TSTRUCT is unnamed. - -Issue is some unknown/remote/external/forward-ref. - -Allowing an FRef *at all* means everybody has to deal with seeing an FRef Type. -Allowing it for functions in same scope allow the above odd/even case . -Allowing it for structs in the same scope allows for co-recursive structs. -Promoting out-of-scope attempts the external lookup, then fails. - -Random junk usage: `sys.fld`. Looking up `sys` fails, now has to -guess: fref is ok, will be defined shortly... OR extern lookup now. -Then some usage delays trouble: -```java -// Extern or Future `sys` being returned. -val foo { -> return sys; } - -// foo() returns FRef, so Bottom, so the `.bar` has no type -// to do field lookups -val main { arg -> foo().bar } - -// HERE: Parser finds `sys` OR NOT?!?!?! -val sys??? = user_stuff....; - -``` - -Which gets to: parser never complains, builds SSA like a blind AST, -just wires up a graph with bad types all around. Requires: No Types -to guide parser! Just Syntax! - - (Obvious minor fail: widen floats- or-not) Fix: insert e.g. int-oper, return - type is MEET of Int,Float until both inputs are isa-Int and cannot lift to be - isa-Float - - Possible method call fail: - UNKNOWN_STRUCT_BOT.UNKNOWN_FIELD ( ARGS ); // Insert SELF OR NOT? - Possible fix: always assume a SELF arg, which is null for provable statics. - Optimization: constant parms get dropped from sig? *Constant* parms - must be constant from *Parser*, or else no unknown callers (so local opts). - Possible fix: SELF arg is *last* not *first*, easier to drop if dead? - - Load drops ConFldOff, keeps name. - Load keeps index unscaled, has index input, which is null for structs. - After Ptr Base is *closed*, base layout can happen, can fold index math. - - Load(ctrl, base, null ); // field name internal - - Load(ctrl, base, index_math ); // field name=='[]' - - Store(ctrl, base, null , val); - - FAIL: No Alias Info! - - -Plan C: - FRefs allowed for direct calls `odd()`, and for Types. - All other cases must *lift*. - - -## Unifying Structs and Arrays - -The idea is that structs have fields indexed by names, and arrays have fields -indexed by numbers, and these ideas are compatible with each other. This idea -has been used by the C language for a long time - an array can follow a normal -struct C and can index it with the usual array syntax. - -The alternative, using a field to hold a pointer to an array, exchanges some -flexibility for an extra indirection. The obvious flexibility allows swapping -the array out (generally for a larger one) and this is the basis of very common -`Vector` or `ArrayList` generic containers. - - -```java -struct str { - int hash; // A cache of the strings hash code - u32 #; // The array length - u8 []; // Array of bytes -}; - -str X = "abc"; -print(X.hash); // Read the hash field -X[0]=='a'; // Read array element 0 -X#; // Length of the array - -``` - -Here, the field named `#` holds the array length and the field named `[]` is -the actual array, which follows the other fields directly in memory. -Here is another example for variable length array: - -```java -struct vecInt { - u32 size; // Actual number of elements - u32 #; // The array length or capacity - int []; // Array of integers. -}; -``` - -The obvious limit here is that the array cannot be directly resized; it is -literally in the same memory block as the leading fields. Such a `vecInt` can -be *re-allocated* to a new larger `vecInt`, but the original `vecInt` will be -reclaimed - you cannot use the same `vecInt` reference forever. - -Any number of named fields are allowed: - -```java -struct Class { - !str !className; - !str !professor; - int time; - int credits; - !Class !prerequisite; - u16 #; // Limit of 65535 students per class - Student []; -}; -``` - -Or self-reference: - -```java -struct NTree { - Some val; - u8 #; // Length is 0-255 - NTree []; // More NTrees -}; -``` - -The syntax rules are: -- the last field is named `[]` and can be of any type. -- the second-to-last field is named `#` and must be some unsigned integer type - representing the maximum array length. -- may require a leading space in front of ` []` - - - -### Constructors and Arrays - -The allocation includes the array length, and can include a constructor: -`new NTree{val=17;}[2];` - -The constructor is optional, the array length is not. - -Optional function for initializing arrays, allows for write-once final arrays: -`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]` - - - -### Changing array max length - -An issue for large numbers of range-checked small arrays is that reserving a -full 4 bytes for array length can substantially increase the array overhead. -Simple allows for arrays to have smaller max lengths, although these are -treated as a different type than arrays with different max lengths. - -```java -struct x0 { u8 #; u8[]; }; // Array limited to 0-255 length -struct x1 { u16 #; u8[]; }; // Array limited to 0-65535 length -struct x2 { u32 #; u8[]; }; // -struct x3 { u64 #; u8[]; }; // - -x0 x = "abc"; // 4 bytes total: [3, 'a', 'b', 'c'] -x1 y = "def"; // 6 bytes total: [3, 0 , 'd', 'e', 'f', 0,]; 1 pad byte to acheieve 2-byte alignment -x = y; // ERROR, "x1 is not x0", no auto-widening -x += y; // OK, 7 bytes total: [6, 'a', 'b', 'c', 'd', 'e', 'f'] -y += x; // OK, 8 bytes total: [6, 0 , 'd', 'e', 'f', 'a', 'b', 'c'] - -x += "VeryLongString.... ....255_chars"; // Runtime RangeCheck - -// Explicit conversion to widen string -y = "" + x; -``` - - -### Two flavors of extendable arrays/strings: an extra indirection or not - -With an extra indirection you get e.g. Java `ArrayList` or C++ `Vector`. - -Without, then any call which extends the extendable array return a potentially -new object, only growing as needed. - -```java -val vec1 = new vecInt[0].add(2); // len=1, capacity=1 -val vec2 = vec1.add(3); // NEW !vecInt !returned, only is deleted; len=2; capacity=2; -vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)` -vec2[0]; // OK -vec2[1]; // OK -vec2[2]; // Runtime error, AIOOBE -``` - - -## A String Implementation - -Default strings have names like "str", "cstr", "ustr" or such. - -Always unicode8 with explicit u8 manipulations. Unicode "characters" will come -as function calls over the base implementation, probably in another string class. - -## Default string - -Efficiently growable via concantenation. Supports a variety of IO/printf functions. -The underlying array can be shared mutable if you ask for this. -The defaults will always be private mutable and read-only shared. - -```java -struct str { - - // Add a string to the string, possibly growing - val add = { str str -> - val self = this; - for( int i=0; i<str#; i++ ) - self = self.add(str[i]); - return self; - }; - - // Add an char to the string, possibly growing - val add = { u8 e -> - if( len >= # ) return copy(# ? #*2 : 1).add(e); - [size++] = e; - return this; - }; - - // Copy 'this' to a new larger size - val copy = { int sz2 -> - val v2 = new str[sz2]; - for( int i=0; i<len; i++ ) - v2[i] = [i]; - v2.len = len; - sys.libc.free(this); // Needs a better Mem Management solution - return v2; - }; - - val print { -> sys.libc.write(1/*stdout*/,[]/*array base as argument to C*/,len); return this; } - - u32 len; // In-use size - u32 #; // Max length is 4Gig, although variants can request smaller lengths - u8 []; // Character data -}; -``` - - -### A Final String - -Intended to be a **immutable** string, possibly used as a base for a **secure** -string. This needs to be created all at once, and probably requires a -defensive copy. - -```java -struct sstr { - // Possible static call syntax; no references to "this" - val make { str str -> new sstr[str#,str.at] }; - - // Array contents are immutable. - // Still figuring out good syntax for this. - u8 #; - u8 [~]; // Middlin '~'? Makes primitive array contents *immutable* and requires constructor syntax -}; - -// Construction requires a function that produces the array contents. -// Here we are defining a defensive copy over `buf`. -sstr safe = new sstr[buf.len,{ int idx -> buf[idx]; }]; -sstr safe = sstr.make("abc"); // Defensive copy is made -``` - - -### A Extendable String (aka StringBuilder or StringBuffer) - -This API is intended to be a drop-in for a `StringBuilder` style, and takes an -extra indirection with each usage. - -```java -struct xstr { - !str !_str; // The extra indirection is here - val add = { str str -> _str = str.add2(str); }; - val write = { int fd -> _str.write(fd); }; -}; -``` - -### A Unicode String - -This API is intended to support unicode character manipulation over a plain `str` - -```java -struct ustr { - // Assuming idx is at the start of a character, return the character - // as a 32b integer. Returns junk integer if not at a character start. - // For all ASCII strings this will amount to a simple byte load. - // For bytes with high order bits set, this may read 1-4 characters. - val at = { int idx -> ... }; -}; -// Unicode character iterator -struct ucharator { -// Return successive unicode character codes -}; -``` - -### A "C" string - -A zero-terminated string, but the length has to be available directly - -```java -struct cstr { - u8 #; // length including the trailing zero - u8[]; // always ends in a zero byte; suitable for direct passing to C string functions -}; -``` - -### A "german string": a very short string that can exist in a register +## Correctness -Length in memory is 1 byte (3 bits?) of length, then 0-7 chars; max 8 chars. -Fits in a 64 bit register. +### Narrow stores in the ideal evaluator -```java -struct gstr { // aka germanString, or blend string and register "streg" - u3 #; // Size is 0-7 bytes??? - u8 []; // Up to 7 bytes -} -// Using inlined object (see below). All these strings are short and are -// represented as packed integers in a 64b register. -*gstr prize1 = "gold"; -*gstr prize2 = "silver"; -*gstr prize3 = "bronze"; +Chapter 23's `Eval2.store` stores the raw value in its Java object array, +without the truncation performed by a machine Store. This example still +returns 25710 in the evaluator instead of 110: +```simple +struct C { u8 x=255; val next_noInline={ -> x++; return x; }; }; +var c=new C; +val a=c.next_noInline(); +val b=c.next_noInline(); +return a+10*b+100*c.x; ``` -### Main becomes `val main = { str[] args -> ...}` - - -## Structs can be inlined - -Structs are normally by-reference, but can be converted to by-value by using -the `*` operator, pronounced "contents of" (this name might be changed to -e.g. `inline`). - -This is a name-space change only, you cannot take the address of the internal -struct without entirely consuming its lifetime. No "escaping". - -```java -// 2 64-bit words, no other overheads -struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; }; - -// Current Simple rules: always by-reference -struct ByRef { - !Complex !c; // 4-byte pointer to a Complex -}; -print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c - -// New behavior: by value (inlining a struct) -// The '*' syntax indicator can be something else, e.g. a keyword "inline" -// I am pronouncing '*' as "contents of" -struct ByValue { - *!Complex !c; // c is inlined, full 16 bytes into ByValue -}; -print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load - -// Mixing Refs and Struct Values. Basically, a ref can be converted -// to a value by taking the "contents of" the ref, and values cannot -// be *converted* to a ref, although they can be copied over a ref. -ref. c = val.c; // Error, mixing refs and values -ref.*c = val.c; // Allowed, whole structure copy. Does not allocate, requires ref.c not-null -val. c = ref.c; // Error, mixing refs and values -val. c = *ref.c; // Allowed, whole structure copy -ref. c = ref.c; // Allowed, pointer copy -val. c = val.c; // Allowed, whole structure copy. -val *c = val.c; // Error, cannot take "contents of" a value - -// Alternative syntax, to avoid the "contents of" being on LHS and field applying to being on RHS: -ref.c = val.*c; // Assign into ref.c the "contents of" val.c - -// Arrays of inlined structures -var ary = new *Complex[99]; // Array of 99 !Complex !objects, inlined - -// Calling methods has the same syntax -ref.c.len(); -val.c.len(); - -// Function arguments use the same typing, so can pass-by-value -val math.sin = { *Complex c -> ... }; // Passes a complex by value -math.sin(ref.*c); // Requires de-reference to pass by value -math.sin(val. c); // Pass by value - -``` - - - -## RoadMap for other chapters - -`&&` and `||` -Drop `int` to `i32`. -Drop `TypeMemPtr` to 4 bytes; using `mmap` to make heap in low 4G. -Default parser for-loop construction, or partial peel. - `for( init; test; next ) body` -becomes: -```java -{ init; // Normal scope entry to bound lifetime of index variable -if( !test ) { // Zero-trip count test - do { - body; - next; - } while( test ); // Exit test at loop bottom by default -} }; -``` - -## `&&` and `||` - -The logical operators && (AND) and || (OR) are short-circuiting, meaning they may skip evaluating the second condition if the first one already determines the result: +Find the earliest affected evaluator, reproduce the failure there, and carry +the correction through affected snapshots. Check signed fields and floating +rounding as well as unsigned bytes. Generated code must not be changed to +match an incorrect evaluator. + +## Compilation and optimization + +### Reliable incremental builds + +`CompUnit` checks source and dependency-object timestamps when a unit is +discovered. It can accept A's saved IR before discovering that B, whose body +was inlined into A, needs rebuilding. An unchanged public signature does not +make A current: optimization imports bodies and constants too. + +Discover the dependency graph and propagate rebuild decisions to a fixed +point before accepting saved IR. Handle cycles together, bundled source units, +missing inputs, and objects resolved through library search paths. Record +compiler/serialization versions, target, ABI, and relevant options; content +hashes can cover preserved timestamps. Distinguish rebuildable source from +incompatible source-free libraries. + +An unchanged build should do no compilation or linking. Publish outputs only +after successful compilation, and relink when an object or other link input +changes. Start with a small build driver and conservative rebuilding of every +dependent; finer interface/body fingerprints can follow. The +[incremental-build design](../skills/incremental-builds.md) records the input +ownership rules and validation cases. + +### Selective loading of library IR + +Importing `sys.io.p` currently loads all ten units bundled in `sys.o`. +Independent, unrelated objects can remain unloaded, but selecting an object +reconstructs all its saved IR and follows its recorded dependencies. + +Design member-level IR loading and dependency discovery together. Splitting +objects alone is insufficient while reexports and unused function bodies +still pull in their dependencies. Preserve type identity, initialization rules, +and imported escape summaries. Loading, initializing, and emitting a class +are separate decisions. This optimization is independent of making +incremental rebuilding correct. + +### Deterministic inlining + +The current inliner has a bounded exception for small initializer boilerplate, +but still uses parse-size estimates and worklist order. After shrinking +rewrites converge, choose one growing inline using current body sizes and a +deterministic ranking. Cache sizes per convergence round and use stable +call-site provenance to break ties, rather than newly allocated node IDs. +Avoid repeated whole-body scans and per-node dependencies while a large +function gradually shrinks. -- `&&` stops if the first operand is false. -- `||` stops if the first operand is true. - -They are syntactic sugar—used for cleaner control flow—but they don't create new nodes in the execution model. - -The `&&` and `||` constructs are simple syntactic sugars that only modify the control flow and do not -introduce new nodes. - -Consider this example: -```java -int a = 1; -int b = 0; +### CFG frequency estimates -if(a++ || b++ ) { - if(b == 0 && a == 2) { - sys.io.p("Or"); - } -} else{ - sys.io.p("And"); -} -return 0; -``` -- `a++` evaluates to 1 (true), so `b++` is not evaluated. -- As a result, `b` stays 0, `a` becomes 2, and "Or" is printed. +After lowering and loop-tree construction, estimate block frequencies for GCM +and code layout. Treat initialization and known-failure paths as rare; use +bounded loop-trip estimates and branch probabilities when no profile is +available. Keep estimates consistent with control flow and shared between +scheduling and layout. These are backend estimates, not ideal-IR types. -```java -int a = 1; -int b = 1; +### Loop rotation -int x=1; -int y=1; -int z=0; +Consider lowering `for` loops with a zero-trip entry test and a bottom-tested +loop, or adding partial peeling. Preserve condition side effects, loop-local +scope, and the destinations of `break` and `continue`. The current parser +constructs a loop with its test at the head. -int g = x++ && y++ && z++; -... -``` -Here: -- `x++` is true (1), so it evaluates `y++`, which is also true. -- Then it evaluates `z++`, which is false (0). -- Since `&&` requires all conditions to be true, the final result `g` is 0. -- All operands up to the false one are evaluated, so `x`, `y`, and `z` are all incremented. +### Constant instance-field storage -## Class initialization and compiler follow-ups +When a final instance field has the same constant value in every instance, +fold accesses or store the value on the class object and omit the instance +slot. Preserve field-access semantics and public function reachability: +externally accessible function fields remain callable regardless of storage. +Existing static images for class constants do not implement this general +instance-layout optimization. + +## Language boundaries + +### Source privacy + +Underscores already select the public interface, but source access to another +class's private fields is not rejected. The proposal is to enforce privacy +starting in Chapter 28a, then preserve it across imports in 28b. This language +change still needs review; it is not a correction to impose on earlier chapters. + +- A field or method beginning with `_` is accessible only from its declaring + class, including constructors and methods. Enclosing, nested, and sibling + classes gain no special access. Same-class code may access another instance. +- A private class name is available in its declaring scope and inside the + class itself. Naming `App._Cache` must not grant access to `_Cache._data`. + Dotted lookup cannot bypass access rules, and private ancestors keep their + descendants out of the public interface. Distinguish naming a private class + from using a public value returned by a factory. +- Reads, writes, updates, method references, type names, and class-value + references must follow the same rule. Preserve source owner and location + through unresolved receiver types, inlining, and imports. Folding away a + Load or an unused result must not erase an illegal source access. +- Native declarations follow the same rules. Public wrappers may deliberately + expose private bindings; `sys` has no special exemption. + +The bundled `sys/io.smp` calls `libc._exit` directly. Keep that native binding +private, add a public `exit` wrapper in `sys/libc.smp`, and change IO to call +the wrapper. Bitset and array helpers access their own private fields and +remain legal. String spill fixtures with helpers outside `_String` accessing +`_hashCode` need explicit adaptation; do not grant file-wide access merely to +keep those fixtures unchanged. Validate native execution and imported source +ownership alongside the access diagnostics. + +### Constructor helpers and completion + +Local SSA field updates, including final fields, repeated assignments, and +loops, are implemented. The remaining question is broader: should constructors +be allowed to pass unfinished `self` to helpers that become safe after +inlining, and should completion checks wait until SCCP? + +Decide those rules before changing the current parser-time completion checks +and early-publication restrictions. Separate reading an initialized field of +an unfinished object from exposing that object to arbitrary code. The +construction/escape teaching boundary can then be reviewed against the chosen +semantics. + +### ELF visibility and unknown callers + +Escape analysis tracks functions reachable by unknown callers, while the ELF +writer exports retained named functions owned by the compilation unit. Decide +which symbols should be public, local, or hidden across separate compilations. +Keep source privacy, ownership for emission, and unknown-caller edges distinct; +keeping a body for linking must not imply arbitrary callers can reach it. + +## Initialization and runtime ### Entry arguments -Replace the integer initializer argument with an argument array. The selected -entry class receives a non-null array, empty when there are no arguments; -other class initializers receive null. Coordinate the parser, entry signature, -runtime driver, and separately compiled interfaces. +Replace the integer class-initializer argument with an argument array. The +selected entry class receives a non-null array, empty when there are no +arguments; other class initializers receive null. Coordinate the parser, +entry signature, runtime driver, and separately compiled interfaces. + +The native runtime already adapts process arguments for an explicit +`simple_main` function. That does not implement argument arrays for class +initializers. ### Pre-initialize classes from ELF data -Prove when a class's entire initialization can be represented by ELF static -data and relocations, with no remaining runtime effects. Emit its initial -contents and start its `$init` flag as true, so no initializer call is needed. -Carry that fact through separate compilation so importing compilers can remove -checks too. A constant field alone is not proof that the whole class can be -pre-initialized; I/O, argument-dependent work, and other observable effects -still require execution. Preserve any dynamic parent requirement, and do not -let cyclic dependencies justify their own pre-initialization. +Prove when a class's entire initialization can be represented by static data +and relocations, without remaining runtime effects. Emit those contents and +start its `$init` flag as true. Carry that fact through separate compilation +so importing compilers can remove checks too. -Mutable fields may have static initial contents without becoming constants. -Choose read-only storage only when no runtime writes remain. Static reference -cycles can use relocations without requiring a dynamic initialization cycle. +Constant final fields already have static images; that alone does not prove +the whole class can be pre-initialized. I/O and argument-dependent work still +require execution. Preserve dynamic parent requirements, and do not let cyclic +dependencies justify their own pre-initialization. Static reference cycles +can use relocations without requiring a dynamic initialization cycle. +Mutable fields may have static initial contents without becoming constants. For a wholly immutable, pre-initialized class, eliminate the mutable `$init` -state as well as the checks so the class image can reside in ROM. The native -function thunks preserve static initialization of function fields. +state as well as the checks so the class image can reside in ROM. Native +function thunks already preserve static initialization of function fields. ### Compact code and data arenas -Keep four-byte Simple references into bounded code and data arenas; the arenas -need not overlap or live near zero. Choose absolute addresses versus offsets -from an arena base when implementing placement and indirect calls. Native -function thunks can occupy the code arena while native libraries live elsewhere. -The current thunk implementation uses the existing low-address executable; +Use four-byte Simple references into bounded code and data arenas. The arenas +need not overlap or live near zero; choose absolute addresses or offsets from +an arena base when implementing placement and indirect calls. Native function +thunks can occupy the code arena while native libraries live elsewhere. +Currently function pointers occupy four bytes and use low-address thunks; ordinary data pointers still occupy eight bytes. Give full-width native pointers separate types and foreign lifetime contracts. -Opaque device addresses may need to be passed through without CPU dereference. -Simple object lifetimes should be managed automatically without a GC; -ownership, borrowing, cycles, and native retention still need design. Compact -storage alone does not establish those rules. Preserve static class images and -explicit native boundary conversions when choosing the arena representation. +Opaque device addresses may need to pass through without CPU dereference. +Automatic Simple object lifetimes without a GC still need a design for +ownership, borrowing, cycles, and native retention. Compact storage alone +does not establish those rules. Preserve static class images and explicit +native boundary conversions when choosing the representation. -### CFG frequency estimates +## Data and library proposals -After lowering and loop-tree construction, estimate block frequencies for GCM -and code layout. Treat initialization and known-failure paths as rare; use -bounded loop-trip estimates and branch probabilities when no profile is -available. Keep estimates consistent with control flow and shared between -scheduling and layout. These are backend estimates, not ideal-IR types. +These are language-design alternatives. The sketches below are not executable +examples of the current Simple grammar. -### Constant instance-field storage +### Structs with trailing arrays -When a final instance field has the same constant value in every instance, -fold accesses or store the value on the class object and omit the instance -slot. Preserve field-access semantics and public function reachability: -externally accessible function fields remain callable regardless of storage. +Allow named fields followed by an array length and inline element storage: + +```simple +struct vecInt { + u32 size; // Used elements + u32 #; // Capacity + int []; // Trailing elements in the same allocation +}; +``` + +The final field would be named `[]`, with an unsigned length field `#` +immediately before it. Element types could include references to the same +struct. Current arrays have a fixed `u32` length and a special two-field +layout; supporting arbitrary leading fields requires parser and layout work. + +Consider `u8`, `u16`, `u32`, or `u64` length fields to reduce small-array +overhead. Arrays with different length representations would be distinct +types, with explicit conversions and capacity checks. The spelling and +alignment rules remain to be decided. + +Growing such an object requires a replacement allocation. An extra level of +indirection, as in a growable-buffer wrapper, instead preserves the wrapper's +identity. Specify ownership before treating a growing call as consuming or +freeing its receiver. + +### Array initialization + +Allow an element-producing function at allocation, for example +`new u8[buffer#, { u32 idx -> buffer[idx] }]`. This would support final arrays +and fully initialized non-null reference arrays. Define when each element is +evaluated and when the array can be published. Existing zero-filled array +allocation does not provide this constructor operation. + +### String libraries + +Design a family of byte-based strings with UTF-8 decoding supplied by library +functions. Consider these distinct representations and interfaces: + +- A growable string with inline bytes and cached metadata, returning a + potentially new receiver when capacity increases. +- An immutable string constructed all at once, with defensive copying where + necessary. Read-only access alone does not establish exclusive ownership. +- A StringBuilder-style wrapper holding a replaceable pointer to its buffer. +- Unicode decoding and iteration over the byte representation, including + behavior at invalid character boundaries. +- A C-compatible string with a trailing zero and directly available length. +- A short string packed with its length into a machine word, passed by value. + +Keep mutable storage private by default and specify sharing, concatenation, +capacity growth, and native I/O contracts. Existing `sys` byte-array and +buffer helpers are a starting point, not this complete string design. + +### Struct values + +Allow a struct to be stored inline in another object, array, or argument list +rather than always through a reference. A proposed `*Complex` or `inline` +spelling would place both coordinates of `struct Complex { f64 x,y; };` +directly in the containing value. + +Define whole-value copying, reference-to-value conversion, and assignment +through an existing reference. A value must not silently become a reference +or expose an interior address with an invalid lifetime. Decide how consuming +an enclosing object's lifetime could permit such an address. Keep method +syntax uniform and define the calling convention for by-value arguments. + +### Default integer width + +Consider making `int` mean `i32` instead of the current `i64`. This changes +overflow behavior, layouts, APIs, and native interfaces, so it needs a language +decision and a deliberate compatibility boundary. + +### Parser Cleanup + +Get the Parsers' structure closer to an ideal R-D parser, and matching the +grammer doc. Move TYPES to CodeGen, and generally restructure for readability. +Ponder using try-w-resources to do the keep/unkeep pairing (do NOT do any +allocation in the `try`, just bring up an object w/auto-closable which tracks +the unkeeps) - see if that looks any cleaner.
\ No newline at end of file diff --git a/docs/chapter-split.md b/skills/chapter-split.md index 55551b27..67a974e4 100644 --- a/docs/chapter-split.md +++ b/skills/chapter-split.md @@ -24,8 +24,8 @@ their historical identities across snapshots. They are regression cohorts, not an index of the current teaching sequence. Historical allocator tables also retain their original labels and dates. -Native library construction and optimizer tuning are now [appendices](appendices/README.md). -The [roadmap](roadmap.md) and [backport queue](chapter-backports.md) own future work. +Native library construction and optimizer tuning are now [appendices](../docs/appendices/README.md). +The [roadmap](../docs/roadmap.md) owns future work. The teaching pattern is to introduce a difficult idea, then show how to make it useful in a larger setting. Each chapter must remain a complete, runnable @@ -128,7 +128,7 @@ Keeping a function for emission must not imply arbitrary callers can reach it. This split organizes implemented behavior. Deterministic inlining, argument arrays, static pre-initialization of classes, CFG frequency estimates, and constant instance-field storage remain future work. See the -[backport queue](chapter-backports.md) and [roadmap](roadmap.md). +[roadmap](../docs/roadmap.md). ## Renumbering and validation diff --git a/skills/chapter25-codex-notes.md b/skills/chapter25-codex-notes.md index 0e5a1957..8d0e43cb 100644 --- a/skills/chapter25-codex-notes.md +++ b/skills/chapter25-codex-notes.md @@ -4,18 +4,18 @@ This file is intentionally AI-facing. It records durable invariants, debugging habits, and project-owner preferences that are easy to miss when reading only the implementation. Check current code and Git state rather than treating old session handoffs as live status. -For cross-chapter work, read [the backport queue](../docs/chapter-backports.md). -It owns pending work; these notes capture reusable lessons. Completed work and -superseded validation history remain in Git. -The [completed split](../docs/chapter-split.md) maps the former combined 24/25 +The [roadmap](../docs/roadmap.md) owns unfinished compiler work and language +proposals. These notes capture reusable lessons. Completed work and superseded +validation history remain in Git. +The [completed split](chapter-split.md) maps the former combined 24/25 to 24a/24b (SCCP), 25 (incomplete types), 26 (construction), 27 (escape), and 28a/28b (modules/separate compilation). The old combined snapshots are retired. Earlier entries below retain historical chapter numbers and test names; old Chapter 25 implementation references describe the lineage now in 25–28b, with the complete implementation in 28b. Use current paths and teaching boundaries. The construction/escape border remains open to semantic review. -Future work belongs in the [roadmap](../docs/roadmap.md), including the separate -[incremental build design](../docs/incremental-builds.md). +The [incremental build design](incremental-builds.md) expands the roadmap's +dependency and output-ownership requirements. For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.md). ## Collaboration preferences @@ -79,9 +79,7 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m library/client example checks three seeds and repeated native calls; native callback exposure is also checked after saving and loading the library. -- The four README intention questions are kept in - [the review agenda](../docs/chapter-backports.md#four-readme-intentions-to-review). - Final data fields are writable constructor locals, including in loops; final +- Final data fields are writable constructor locals, including in loops; final local bindings and shared function declarations stay fixed. Do not classify a constant instance default as a shared field or skip its loop Phi merely because the published field is final. Class constants keep their static image. @@ -438,9 +436,9 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m top-level runner, e.g. `make -k tests CHAPTERS="chapter10 chapter11 chapter12"`, with all affected directories explicitly listed. Establish the destination baseline before changing it; older Makefiles may need a forced rebuild after API changes. -- Keep unrelated discoveries separate in `docs/chapter-backports.md`. Completed - fixes leave the pending queue; record validation in the change description. - Keep unresolved discoveries in the actual queue. A green suite +- Record confirmed unresolved bugs and future work in `docs/roadmap.md`. + Remove completed items and record validation in the change description. + Do not turn an unconfirmed textual difference into a backport task. A green suite after changing graph/test order does not prove an intermittent failure fixed. - Store disposable probes/logs under an appropriate ignored build directory. Preserve durable reproducers in tests or the review record, not only scratch @@ -829,6 +827,13 @@ Consequences: ## Types and monotonicity +- Chapters 24a/24b retain typed fallback results for erroneous or unresolved + Loads and arithmetic. Chapter 25 introduces the surrounding Convert nodes + and removes parser-supplied bounds. Do not backport BOTTOM-on-error in + isolation: parser, conversions, loop bounds, and diagnostics must agree. + Preserve `testBrainFuck`, `testWhileWithNullInside`, `testNullGuardErrors`, + `testLinkedList0`, and `testCoRecur`, including their expected errors. + - Integer widening starts in Chapter 24. `nonZero()` preserves `_widen` when narrowing a range. B10's correction is in 24, with `TypeTest.testNonZeroWidening` in 24-25. Both now use widening 0-3 and dual widening `3-widen`, preserve @@ -1071,8 +1076,8 @@ serialize/deserialization bijection after persisted graph changes Default fuzzer wrappers use explicit regression seed lists; exploratory fuzzing is opt-in. A passing wrapper with an empty list is not exploratory coverage, and `OPEN_FAILING_SEEDS` remain unresolved until explicitly verified and promoted. -For dated suite counts and known failures, consult the backport validation record -rather than treating an old count as the current baseline. +For dated suite counts, consult the validation record in Git history; current +known work is in the roadmap. Do not treat an old count as the current baseline. Peepholes are always enabled in every chapter, by Cliff's request on 2026-09-26. Do not reintroduce an unoptimized compiler mode. Incomplete Loop/Phi nodes still @@ -1093,7 +1098,7 @@ comparisons as equal runtime results. the SUB, not copy its inputs. ARM register 31 is XZR for load destinations, so narrow loads use WMASK, never RMASK. Logical-not uses CMP to XZR followed by CSET to its result register. Shared emulator fixes and Chapter 21-25 - backports are recorded in `docs/chapter-backports.md`. + backports are recorded in Git history. - Cohort 23's dynamic increase from compiler 24 to 25 is primarily class-field initialization: Ops 630->1106. RA 160->442 includes 224 extra pointer-address and 56 integer rematerialization instructions, but only two extra splits. diff --git a/docs/appendices/current-spill-matrix.md b/skills/current-spill-matrix.md index df253a46..ee77a77a 100644 --- a/docs/appendices/current-spill-matrix.md +++ b/skills/current-spill-matrix.md @@ -4,7 +4,7 @@ historical test cohorts. Cohort IDs and test names retain their old numbers; column headings identify the current compiler directories. -These are separate from the dated [pre-split measurements](../regalloc-spills.md). +These are separate from the dated [pre-split measurements](../docs/regalloc-spills.md). After a successful run, `python build-support/spill-matrix.py --reuse --update-docs` updates only this table. Fresh logs use 24b/28b names so old 24/25 results cannot silently become measurements of new compiler snapshots. diff --git a/skills/graph-viewer-codex-notes.md b/skills/graph-viewer-codex-notes.md index d76a949d..645efdd8 100644 --- a/skills/graph-viewer-codex-notes.md +++ b/skills/graph-viewer-codex-notes.md @@ -4,8 +4,8 @@ Updated 2026-09-26. Read graph/README.md for the current design and protocol. These are AI-facing continuity notes, not a new task list or authority to change compiler semantics. Cliff declared the viewer task complete on 2026-09-26. Peephole animations, inlining display, TypeCheck diagnostics, and arrow-key -playback are committed. Resume the independent bug-fix backport review from -docs/chapter-backports.md when requested. +playback are committed. Unfinished compiler work is tracked in +docs/roadmap.md. ## Working tree and latest change diff --git a/docs/incremental-builds.md b/skills/incremental-builds.md index 201922a0..201922a0 100644 --- a/docs/incremental-builds.md +++ b/skills/incremental-builds.md diff --git a/docs/japanese-translation.md b/skills/japanese-translation.md index 39383345..39383345 100644 --- a/docs/japanese-translation.md +++ b/skills/japanese-translation.md diff --git a/docs/stage-audit.md b/skills/stage-audit.md index abf90ace..194a5178 100644 --- a/docs/stage-audit.md +++ b/skills/stage-audit.md @@ -18,8 +18,8 @@ regression identities; they do not identify current chapter boundaries. comparisons using the former left-associative grammar. It is retained as an ignored `testJigFuzzer`, consistent with the scratch-test policy; its mixed directions are no longer valid source. Later snapshots already omitted it. -- Native-library and tuning material is preserved in [appendices](appendices/README.md), - and the former roadmap is now [docs/roadmap.md](roadmap.md). +- Native-library and tuning material is preserved in [appendices](../docs/appendices/README.md), + and unfinished work is collected in the [roadmap](../docs/roadmap.md). - The recursive-return diagnostic already passed. It remains enabled in the earlier snapshots and is enabled again in 28a/28b. The mutual-recursion test had an obsolete graph-string expectation; it now checks its existing four execution results in 24b–28b. @@ -83,7 +83,7 @@ the corresponding earlier-chapter probes pass in 23/24a/24b. - The ideal evaluator does not truncate narrow stores in Chapter 23. A byte update can therefore disagree with generated code; the postfix-only - reproducer is retained in the [queue](chapter-backports.md). + reproducer is retained in the [roadmap](../docs/roadmap.md#narrow-stores-in-the-ideal-evaluator). - `testModule0` is an ignored historical incremental-build experiment. It assumes per-source output objects and unchanged explicit-input builds, neither of which matches the present compiler's contract. |
