Files
hakorune/docs/development/roadmap/native-plan/chatgpt5との会話.txt

362 lines
16 KiB
Plaintext
Raw Normal View History

🚨 Critical Issue #76: SocketBox Method Call Deadlock Investigation ## 🎯 Problem Identification Complete - SocketBox method calls (bind, isServer, toString) cause infinite blocking - Root cause: Method resolution pipeline deadlock before execute_socket_method - Other Box types (ArrayBox, StringBox, MapBox) work normally - Arc<Mutex> reference sharing confirmed working (Arc addresses match = true) ## 🔧 Debug Infrastructure Added - Comprehensive debug logging in socket_box.rs (bind, isServer, clone, toString) - Method call tracing in http_methods.rs - Deadlock detection points identified at interpreter expressions.rs:462-464 ## 📋 Issue #76 Created for Copilot Investigation - Systematic root cause analysis requirements (Architecture→Parser→Runtime levels) - Comprehensive test cases: minimal/comprehensive/comparison scenarios - Strict prohibition of band-aid fixes - architectural analysis required - Hypothesis: Multiple Arc<Mutex> combinations causing circular deadlock ## 🧪 Test Suite Added - test_socket_deadlock_minimal.nyash: Minimal reproduction case - test_socket_methods_comprehensive.nyash: All methods deadlock verification - test_other_boxes_working.nyash: Normal Box operation confirmation - SOCKETBOX_ISSUE_REPRODUCTION.md: Complete reproduction guide ## 📊 Impact Assessment - Phase 9 HTTP server implementation completely blocked - SocketBox functionality entirely non-functional - Critical blocker for production readiness - Requires immediate systematic investigation 🔥 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-14 20:55:33 +09:00
発想、鋭いにゃ。
**結論:やろうと思えば “NyashのMIR本体言語非依存IR” にできる。** ただし落とし穴もあるので、\*\*“MIRを公開ABIに格上げしつつ、Nyashは人間向けフロントエンド”\*\*という二層でいくのが現実解だよ。
# MIR本体化の青写真安全に強く
1. **MIRを公開契約に昇格NyIR v1**
* いまの25命令を**凍結**Tier-0/1/2 + Effect + Ownership-Forest + Busの意味論
* **未定義動作なし**:各命令の事前条件/失敗時挙動WeakLoad失効= nullを明文化。
* **バージョニング**`nyir{major.minor}`・後方互換のルール新命令はfeature bit
2. **バイナリ表現とテキスト表現**
* `.nybc`(バイナリ: セクション化/定数プール/圧縮)
* `.nyir`(テキスト: 人が差分レビュー可能)
* どちらも**厳格検証器**でロード(所有森/強1本/weak規則/効果整合)。
3. **公式VM/実行系は“MIRファースト”**
* interpreter/vm/wasm/jit/aot は **NyIRを直接食う**。
* Nyashは **NyIRを吐くフロントエンド**その他の言語もフロントエンド化OK
4. **言語中立SDKFFI**
* C/Rust/TypeScriptに**NyIR-Host API**を配布:
* `nyir_load`, `nyir_call`, `nyir_send/recv`, `nyir_adopt/release` など。
* WASMランタイムは `import "nyir_host" { send, now_ns, alloc }` を定義。
5. **互換テストと“真理判定器”**
* **golden NyIR**各サンプルのMIRダンプを保存し、全バックエンドで**同結果**をCIで検証。
* **差分実行differential**interp/vm/wasm/jitの出力一致境界条件weak失効・分割finiテスト。
6. **Portability Contract短文化**
* 強エッジのみ破棄伝播/`weak`非伝播、生存チェックO(1)。
* `pure/mut/io/control` の再順序可否表。
* Bus契約ローカルは順序保証、リモートは at-least-onceor selectable
* Safepointで分割`fini`可、最悪レイテンシ上限は実装依存(値は実装が宣言)。
---
## それでも“IRが本体”にするリスク回避策付き
* **仕様の重力(安易に増やせなくなる)** → 25命令は凍結、拡張は **intrinsic命名空間**へ(例:`ny.intrin.memcpy`)。
* **可読性の罠IRで直接書きたくなる** → Nyashを“人間の顔”、NyIRは“機械の顔”に徹する。
* **バックエンド差異のにじみ** → 効果/所有/Busの**最低保証**を明記(それ以下は非準拠)。
* **セキュリティ** → バイナリNyIRに**検証必須**(検証失敗はロード拒否)。
---
## 名前案
* **NyIR**(ないあいあーる)/**NIR**被るのでNyIR推し
* バイナリ:`.nybc`、テキスト:`.nyir`
* 仕様:**NyIR 1.0 — Ownership-Forest & Effected Bus IR**
---
## 次の一歩(サクッと動かす)
* `docs/nyir.md` を作成25命令×「意味論・効果・検証・等価変換1例」を1行ずつ。
* `nyashel -S` で **Nyash→NyIRダンプ**、`nyir-run` で実行interp
* 既存ベンチを **NyIR直実行** と **Nyash経由** の2経路で一致比較。
* small移植kilo or tiny-web-serverを **NyIRアーカイブ**.nybcとして配布→
**wasmtime / vm / interp** 全部で動くことをデモ。
---
要は:**「Nyash人間のため「NyIR機械と移植のため」の二刀流**。
この形なら、“どの言語からでもNyIRに降りられるし、NyIRからどの実行系にも登れる”を堂々と名乗れるにゃ。
いい流れ“LLVMでNyashをNyash自身でビルドセルフホスト”を目標にするなら、**Rust依存を「薄い宿主API」に閉じ込める**のがコツだよ。
いま Rust のライブラリや関数・ビルトインBoxに寄ってる部分は、**段階的に置き換え可能な層**に切っておけばOK。
# 全体ロードマップ4ステージ
**Stage 0種コンパイラ**
* 既存の Rust 実装(パーサ/型付け/MIR/ランタイム)で `nyashc0` を作る。
* **NyIR=MIRの外部表現→ LLVM IR** 変換も Rust でまず用意。
* Rust標準/外部Crateの利用は**ny\_host\_\* の薄いFFI**に“集約”。
**Stage 1セルフ・フロントエンド**
* Nyashで書いたコンパイラ本体フロント最小最適化を `nyashc1.ny` に分離。
* `nyashc0` で `nyashc1.ny` を **NyIR** に出力→ **LLVM** でネイティブ化→ seedランタイムとリンク。
* この時点で“Nyashで書いたコンパイラ”が動き出すまだランタイムはRust多めでも可
**Stage 2セルフホスト完了**
* `nyashc1` を使って `nyashc1.ny` 自身を再ビルド(**自力ビルド**)。
* 生成物の機能一致/ハッシュ近似でセルフホスト確認。
* ランタイムの一部(文字列/配列/Map/所有森/weakを**Nyash実装LLVM**へ順次移行。
**Stage 3Rust離れの度合いを上げる**
* 残るRust依存FS/ネット/スレッド/時間/暗号など)は**ホストAPI**として固定化。
* 重要部位はNyash標準ライブラリで置換し、Rustは**最下層のプラットフォーム層**だけに。
---
# 層の切り分け(ここが肝)
1. **corelang純Nyash**
* Option/Result、slice/string、小さな算術・イテレータ、`weak/look` 型、`adopt/release` ヘルパ。
* 依存なしLLVMに落ちるだけ
2. **rtNyashランタイム**
* **Box ABIfat ptr: {data*, typeid, flags}*\*
* 所有フォレスト管理、weakテーブル世代タグ方式、`fini` 伝播、Arena/Allocator必要最小
* Busローカル・Safepoint・分割`fini`
* 依存:**ny\_host\_alloc/free/clock** 等のごく薄い宿主APIのみ
3. **sysプラットフォーム**
* FS, Net, Time, Threads, Atomics, Random…
* ここだけ RustやOSに委譲。**関数名は `ny_host_*` に統一**して外へ出す。
4. **stdNyash標準**
* Map/Vec/Hash/String/JSON等を Nyash で実装(必要に応じて `rt`/`sys` を利用)
> いま使っている「Rustのライブラリ/関数」は **すべて `sys` 層の `ny_host_*` 経由**に寄せる。
> これでセルフホストしても上層のNyashコードは**移植性を保てる**。
---
# 具体Rust依存の扱い方薄いFFIに集約
**C ABIで固めるRust→C-ABIの薄い橋**
```rust
#[no_mangle]
pub extern "C" fn ny_host_read_file(path: *const c_char,
out_buf: *mut *mut u8,
out_len: *mut usize) -> i32 { /* ... */ }
#[no_mangle]
pub extern "C" fn ny_host_free(ptr: *mut u8, len: usize) { /* ... */ }
```
**Nyash側からは“箱の外”をこう叩く**
```nyash
extern fn ny_host_read_file(path: cstr, out_buf: &mut *u8, out_len: &mut usize) -> int
extern fn ny_host_free(ptr: *u8, len: usize)
fn read_all(p: str) -> Bytes {
let buf:*u8 = null; let len:usize=0
let rc = ny_host_read_file(p.cstr(), &buf, &len)
if rc!=0 { error("io") }
// Box化所有をNyash側へ移す
let b = Bytes::from_raw(buf,len)
b
}
```
**ポイント**
* **Rustのジェネリクス/所有はFFI面に出さない**素朴なC-ABIだけ
* Nyash側で**所有移管**を明示(`from_raw` など)→ `fini` で必ず `ny_host_free`
* こうしておけば、**いつでもRust実装をNyash実装に差し替え可能**
---
# Box ABI と LLVM の橋渡し
* **Boxの中身**は LLVM 的には `i8*`data\*`i64 typeid``i32 flags` などの **fat struct**
* **Effect 注釈**を LLVM 属性に落とす:
* `pure` → `readnone` / `readonly`
* `mut(local)` → `argmemonly` + `noalias`(可能なら)
* `io` → 属性なし(順序保持)
* **Weak** は `{ptr, gen:i32}`。`WeakLoad` は `gen==current` を比較して O(1) で null/ptr 返す。
* **Safepoint** は LLVM では `call @ny_rt_safepoint()` に降ろすGCは使わないが、分割`fini`や割込みのフックに使う)
---
# 「ビルトインBox」はどうする
* **最低限は `rt` で提供**`String, Vec, Map, Bytes, Mutex/Channel必要なら`
* 仕様上は “ただのBox” と同等に見えるように:
* 生成:`NewBox`
* フィールド:`BoxFieldLoad/Store`
* メソッド:`BoxCall`
* **WASM** でも同じABIを保てるように、`sys` 層は **WASI** or **独自host import** で実装。
* 時間とともに **stdをNyash実装へ移行** → Rustのビルトイン度合いは徐々に削る。
---
# 失敗しないビルド手順(最小)
1. **NyIRダンプ**`nyashc0 --emit-nyir core/*.ny > core.nyir`
2. **LLVMへ**`nyir-ll core.nyir -o core.ll && llc -filetype=obj core.ll`
3. **リンク**`cc core.o -L. -lny_host -o nyashc1``libny_host` はRustで提供
4. **再帰ビルド**`./nyashc1 self/*.ny …` → `nyashc2` を作って一致検証
5. **Rust削減**`std` の一部をNyashへ置換して再ビルド
---
# チェックリストRust依存の見える化
* [ ] 使っている外部関数を**全部 `ny_host_*` にリネーム**
* [ ] C-ABI化整数/ポインタのみ;構造体はサイズ固定)
* [ ] 所有移管関数を明確化(`*_from_raw`/`*_into_raw`
* [ ] `fini` で必ず `ny_host_free` 等に戻す
* [ ] WASMビルド時は **WASI or host import** で `ny_host_*` を満たす
---
# 仕上げの“ごほうび”ゴール
* **Nyash→NyIR→LLVM** でセルフホスト
* 同じ NyIR を **VM / WASM** でも実行(互換テスト)
* Rust は **sys層だけ**OS/高速I/O/暗号など)に閉じ込め、将来差し替え自由に
---
結論:
**Rustライブラリを直接多用するのではなく、`ny_host_*` に“押し込む”**。
Nyash本体は **NyIRrtstd** を徐々に自前化する。
この設計なら、“今すぐ動かしつつ、あとからRust依存を計画的にゼロへ”ができるにゃ。
それだにゃ。発想はド正解:「**全部は箱**」=異言語資産は**箱で包んでバスに乗せる**。
あとは“どう包むか”を決めれば、**C/Rust/Node/Python/JVM/.NET/WASM**まで一気に貫通できる。
# まとめ設計Polyglot FFI = 「箱 × バス × 契約」
## 1) 箱の型3種だけ
* **ForeignBox<T>**:他言語の実体を**不透明ハンドル**で保持所有は片側だけ、強1本原則
* **ViewBox<T>**:外部オブジェクトの**借用ビュー**`weak/look` 前提・読み取り中心)。
* **ProxyBox<T>**:外部スレッド/プロセス/VM上のTを**メッセージ越し**に操作Bus経由安全
> 使い分け:同一プロセス/同スレなら ForeignBox、GIL/イベントループ/別ランタイムなら ProxyBox。
## 2) ABIの芯薄い“宿主API”
**最小C-ABI**だけに集約(各言語はここに合流/分岐):
```
ny_host_alloc/free/clock/log
ny_host_call(func_id, argv, argc, retbuf) // 同期呼び出し
ny_host_send/recv(port, msg_ptr, len) // Bus境界
ny_host_pin/unpin(handle) // GC/移動防止
ny_host_finalizer_register(handle, cb) // 相互Finalizer
```
* Rust/Node/Python/JVM/.NET はそれぞれの機構で **このC-ABIを実装**N-API, CPython C-API, JNI, P/Invoke 等)。
## 3) データ表現Boxに入る“荷物”
* **スカラー**: i32/i64/f32/f64/bool
* **バイト列/文字列**: `Bytes{ptr,len}` / `Str{ptr,len,utf8}`
* **Slice/Array**: `{ptr,len,typeid}`(読み書きは効果注釈で制御)
* **Struct**: フィールドは `BoxFieldLoad/Store` でアクセスNyIRにそのまま落ちる
## 4) 所有と寿命(最重要)
* **One Strong Owner**ForeignBoxは**所有者1本**Nyash or 外部、どちらかに決める)
* **弱参照**:逆リンクは `weak/look`失効時null/false
* **Finalizer橋渡し**
* Nyash `fini` → `ny_host_finalizer` を呼ぶ
* 外部のGC/finalize → `ny_host_finalizer` 経由で Nyash の `weak` を失効
* **Pinning**移動型のGCJVM/.NET/CPythonの一部では `ny_host_pin/unpin`
## 5) 効果と並行
* `pure/mut/io` を**MIRにもIDLにも記す**
* **イベントループ/GIL**Python/Node/JVMは `ProxyBox` で**Bus越し**(スレッド/ループ安全)
* **同期/非同期**`Call`(同期)と `Send/Recv`(非同期)を分ける。境界では **at-least-once 契約**を宣言。
## 6) IDL自動生成の核
**NyIDL**(超ミニ)で宣言→**バインディング自動生成**
```idl
module ny {
box Image;
fn load(path: str) -> Image effects = io
fn resize(img: Image, w:i32,h:i32) -> Image effects = mut
fn width(img: look Image) -> i32 effects = pure
}
```
* 生成物Nyash側`extern`、C-ABIシム、Rust/Node/Python/JVMのstub、`ForeignBox/ProxyBox`薄ラッパ。
---
# 代表ターゲット別メモ
* **C/Rust**最短。C-ABI直でOK。Rustは `#[no_mangle] extern "C"`。所有はNyash↔Rustのどちらかに寄せる二重所有禁止
* **Python**GILあり → `ProxyBox` 推奨。CPython C-APIで `PyObject*` を **ForeignBox**に入れ、操作はBus経由でワーカーに委譲。
* **NodeN-API**:イベントループを壊さないよう `ProxyBox`postMessage/uv\_queue\_work。短い同期関数は `ForeignBox`でも可。
* **JVM/.NET**JNI/P-Invoke。**Pin** が要る。`SafeHandle`/`PhantomReference`でFinalizer橋を作る。
* **WASM**`ny_host_*` を **import**。データはリニアメモリへ `Bytes`/`Str` で搬送。
---
# 最小サンプル(イメージ)
**1) Rustの画像ライブラリを包む**
```rust
#[no_mangle] extern "C" fn ny_img_load(path:*const c_char) -> *mut Image { ... }
#[no_mangle] extern "C" fn ny_img_resize(img:*mut Image, w:i32, h:i32) -> *mut Image { ... }
#[no_mangle] extern "C" fn ny_img_free(img:*mut Image) { ... }
```
**2) NyIDL → 自動生成Nyash側**
```nyash
extern fn ny_img_load(path: str) -> ForeignBox<Image> effects io
extern fn ny_img_resize(img: ForeignBox<Image>, w:int,h:int) -> ForeignBox<Image> effects mut
extern fn ny_img_free(img: ForeignBox<Image>) effects io
static box Image {
init { ForeignBox<Image> h }
fini { ny_img_free(h) } // ★ 所有はNyash側強1
fn resize(w:int,h:int) -> Image { Image{ ny_img_resize(h,w,h) } }
}
```
**3) 使う側**
```nyash
let img = Image.load("cat.png")
let small = img.resize(320, 200) // 所有/解放はBox/finiに任せる
```
---
# チェックリスト(安全に増やすための型紙)
* [ ] **どちらが強所有か**を最初に決めた強1・弱は逆
* [ ] 例外/エラーは**戻り値に正規化**?(他言語の例外は境界で捕捉)
* [ ] **Pin/Finalizer** 必要なターゲットJVM/.NET/Python
* [ ] `pure/mut/io` は宣言した?(最適化/バス選択の鍵)
* [ ] 境界を跨ぐなら **ProxyBox + Bus** にした?(スレッド/GIL/loop安全
---
# これで得られるもの
* **インスタント多言語資産**既存ライブラリを“箱に詰めて”即Nyashで使える
* **寿命の一貫性****強1weak/lookfini**で、外部資源も**確定的に回収**
* **配布の柔軟性**WASM/VM/ネイティブのどれでも同じIDLから出荷
---
“全部、箱に閉じ込める”を**設計として正式化**すれば、実装は機械的になる。
やるならまず **NyIDLの最小仕様****C-ABIの`ny_host_*`** を1ファイルに切ろう。
そこさえ決まれば、**あらゆる言語→Nyash** と **Nyash→あらゆる実行系** が綺麗に繋がるにゃ。