docs(phase131): LLVM SSOT強化 + ConsoleBox調査完了 + Phase86-90要約

Phase 131-1 完了: LLVM exe line SSOT 強化
- phase87-selfhost-llvm-exe-line.md に 4セクション追加(+300行)
  - 環境変数リファレンス(14変数)
  - 成功/失敗基準(exit code 0/1/2/3)
  - コンパイラモード説明(harness vs crate)
  - デバッグセクション拡張
- "1コマンドで再現" 可能な状態を確立

Phase 131-2 完了: ConsoleBox 問題調査
- VM の 3重登録経路を特定(BoxFactoryRegistry/UnifiedRegistry/Builtin)
- LLVM backend は Phase 133 で解決済み
- 3つのドキュメント作成:
  - phase131-2-consolebox-investigation.md(詳細調査)
  - phase131-2-summary.md(エグゼクティブサマリ)
  - phase131-2-box-resolution-map.md(Box 解決マップ)

Phase 86-90 完了: Loop frontends 要約
- phase86-90-loop-frontends-summary.md 追加
- Pattern4/ContinueReturn/ParseStringComposite の経緯を1枚に集約
- INDEX から導線追加

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-14 05:24:31 +09:00
parent 2a18a66154
commit 05c5021147
9 changed files with 1584 additions and 133 deletions

View File

@ -11,7 +11,7 @@
---
## 今の状態Phase 7489 まで到達)
## 今の状態Phase 7490 まで到達)
- Scope/BindingId の段階移行dev-onlyは Pattern2/3/4 まで配線済みdual-path 維持)。
- Pattern2 の promoted carriersDigitPos/Trimについて ExitLine 契約ConditionOnly を exit PHI から除外)を E2E で固定済み。
@ -19,10 +19,12 @@
- **LLVM exe line SSOT 確立**: `tools/build_llvm.sh` を使用した .hako → executable パイプライン標準化完了。
- **Phase 88 完了**: continue + 可変ステップi=i+const 差分)を dev-only fixture で固定、StepCalculator Box 抽出。
- **Phase 89 完了**: P0ContinueReturn detector+ P1lowering 実装)完了。
- **Phase 90 完了**: ParseStringComposite + `Null` literal + ContinueReturn同一値の複数 return-ifを dev-only fixture で固定。
- `cargo test --release --lib` は PASS を維持993 passed、退行なし
参照:
- `docs/development/current/main/10-Now.md`
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
- `docs/development/current/main/phase73-scope-manager-design.md`
- `docs/development/current/main/phase80-bindingid-p3p4-plan.md`
- `docs/development/current/main/phase81-pattern2-exitline-contract.md`
@ -33,30 +35,30 @@
## 次の指示書(優先順位)
### P0: Phase 90 - _parse_string 合成 fixture
### P0: Docs 整備(数の増殖を止める)
**目的**: continue(escape) + return(close quote) の合成パターンを dev-only fixture で固定
**目的**: SSOT への集約と導線整備Phase 8690 の情報が散らばらない状態にする)
**実装順序**(最短・事故減):
1. 合成 fixture 設計(制御構造のみ、文字列処理なし
- continue(escape) + return(close quote) + str += ... + p += 1/2
- 期待値が一意(例: n=10 で return 前に acc=4
2. ShapeGuard 追加(誤爆防止)
- PatternParseStringCompositeMinimal (dev shape)
- P4/ContinueReturn と区別する条件
3. lowering 実装(新しい箱)
- parse_string_composite_pattern.rs
- Fail-Fast: 対応形以外は即エラー
4. normalized_dev テスト2本
- structured vs canonical 一致
- 期待値一致
やること:
1. Phase 8690 の要約を 1 ファイルに集約SSOT
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
2. INDEX から要約へ導線を追加(迷子対策
- `docs/development/current/main/01-JoinIR-Selfhost-INDEX.md`
3. `10-Now.md` / `CURRENT_TASK.md` の断定・重複を “要約へのリンク” に寄せる
**受け入れ基準**:
- `NYASH_JOINIR_NORMALIZED_DEV_RUN=1 cargo test --features normalized_dev --test normalized_joinir_min` PASS
- `cargo test --release --lib` PASS退行なし
- dev-only のみcanonical には入れない)
- 読み始め導線が `01-JoinIR-Selfhost-INDEX.md` から辿れる
- `10-Now.md``CURRENT_TASK.md` は “最新の入口” として機能する(詳細は要約へ
### P1: JoinIR / Selfhost depth-2 の前進Phase 91 候補
### P1: Loop Canonicalizer の設計docs-only、外部検討待ち
**目的**: ループ形状の組み合わせ爆発を抑えるための “前処理パス” を設計し、SSOT を定める
注意:
- 実装は急がず、設計SSOTを先に固める
- 既定挙動は変えないdev-only で段階投入する)
### P2: JoinIR / Selfhost depth-2 の前進Phase 91 候補)
目的:
- JsonParserBox の残り複合ループを JoinIR 対応する。
@ -70,7 +72,7 @@
受け入れ基準:
- 代表ケースが 1 コマンドで再現可能CI は増やさない、quick を重くしない)。
### P2: Ownership/Relay runtime 対応の再開Phase 92 候補)
### P3: Ownership/Relay runtime 対応の再開Phase 92 候補)
目的:
- multihop/merge relay を “runtime でも” 受理できるところまで契約を伸ばすFail-Fast の段階解除)。
@ -78,23 +80,9 @@
受け入れ基準:
- dev-only 既定OFF のまま、既存ラインを壊さない。
### Done: Phase 88-89 完了
### Done: Phase 8690Loop frontends
**Phase 88**:
- continue + 可変ステップi=i+const 差分)を dev-only fixture で固定
- StepCalculator Box 抽出(+6 unit tests、再利用可能
- エラーメッセージ詳細化、Fail-Fast 仕様固定
**Phase 89 P0**:
- Pattern4 detector 締めSelect 必須 + conditional Jump exactly 1
- LoopPattern::ContinueReturn enum + shape 追加
- canonical には入れないdev-only、誤爆防止
**Phase 89 P1**:
- continue_return_pattern.rs 実装457行、StepCalculator 再利用)
- normalized_dev tests +2vm_bridge + 期待値)
**Impact**: 993 lib tests + 61 normalized_dev tests 全パス、箱化スコア 10/10
- まとめSSOT: `docs/development/current/main/phase86-90-loop-frontends-summary.md`
---

View File

@ -19,6 +19,8 @@ Scope: JoinIR と SelfhostStageB/Stage1/Stage3に関する「最
- `docs/development/current/main/10-Now.md`
- 「JoinIR / Loop / If ライン」
- 「JsonParser / Selfhost depth2 ライン」
- Phase 8690Loop frontendsの要約1枚
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
---
@ -62,6 +64,8 @@ JoinIR の箱構造と責務、ループ/if の lowering パターンを把握
- `docs/development/current/main/phase33-16-INDEX.md`
- `docs/development/current/main/phase33-17-joinir-modularization-analysis.md`
- `docs/development/current/main/phase183-selfhost-depth2-joinir-status.md`
9. Phase 8690Loop frontendsの要約1枚
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計を確認した上で、必要なときだけ掘る」という前提で読んでね。
@ -92,6 +96,9 @@ Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計
- → 2章の 1〜3 をこの順番で読む。
- Selfhost のビルド / 実行フローで迷っているとき
- → 3章の 1〜3 をこの順番で読む。
- VM backend の Box 解決ConsoleBox / plugin / builtinで迷っているとき
-`docs/development/current/main/phase131-2-box-resolution-map.md`(経路図)
-`docs/development/current/main/phase131-2-summary.md`(要点)
- 「この Phase 文書は現役か?」で迷ったとき
- → まず `docs/development/current/main/10-Now.md`
`docs/development/current/main/30-Backlog.md` を確認し、そこで名前が挙がっている Phase 文書を優先して読んでね。

View File

@ -1,6 +1,6 @@
# Self Current Task — Now (main)
## 20251206:現状サマリ
## 20251214:現状サマリ
### JoinIR / Loop / If ライン
@ -17,103 +17,13 @@
- 残課題JoinIR ライン):
- JoinIR→MIR merge の一般化(複雑な Select/PHI パターンの統合)
- JsonParserBox など実アプリ側での長期運用テスト
- Phase 8690Loop frontendsまとめ1枚:
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
### Phase 86: Carrier Init Builder + Error Tags (2025-12-13) ✅
### Phase 8690: Loop frontends要約
**Status**: COMPLETE - SSOT 確立完了
**Achievements**:
- Carrier Initialization Builder: CarrierInit → ValueId 生成を単一関数化 (carrier_init_builder.rs, +8 tests)
- Error Message Centralization: JoinIR エラータグを SSOT 化 (error_tags.rs, +5 tests)
- DebugOutputBox Migration: 全 JOINIR_DEBUG 使用箇所を DebugOutputBox に統一
**Impact**:
- Tests: 987/987 PASS (+13 unit tests from Phase 86)
- SSOT modules: carrier_init_builder, error_tags (一貫性向上)
- Code quality: Single Responsibility, Testability First principles validated
**Reference**: phase78-85-boxification-feedback.md, carrier_init_builder.rs, error_tags.rs
### Phase 87: LLVM Exe Line SSOT (2025-12-13) ✅
**Status**: COMPLETE - Pipeline 確立完了
**Achievements**:
- SSOT established: `tools/build_llvm.sh` - Single pipeline for .hako → executable
- Integration smoke: phase87_llvm_exe_min.sh (exit code 42 verification)
- Minimal fixture: apps/tests/phase87_llvm_exe_min.hako (5 lines, return 42)
- Full documentation: phase87-selfhost-llvm-exe-line.md (troubleshooting + advanced usage)
**Policy**:
- ✅ Use build_llvm.sh SSOT (no duplication/fragmentation)
- ✅ Integration smoke only (not in quick profile)
- ✅ SKIP if LLVM unavailable (graceful degradation)
- ✅ Exit code verification (stdout-independent testing)
**Impact**:
- Standard procedure: `tools/build_llvm.sh input.hako -o output_exe`
- Prerequisites documented: llvm-config-18, llvmlite, LLVM features
- Integration test: PASS (or SKIP if no LLVM)
**Reference**: phase87-selfhost-llvm-exe-line.md
### Phase 88: Continue + 可変ステップ (2025-12-14) ✅
**Status**: COMPLETE - dev-only fixture 固定完了
**Achievements**:
- continue + 可変ステップi=i+const 差分加算)を dev-only fixture で固定
- continue 分岐側での acc 更新を許可
- Fail-Fast 仕様固定(非 const の i 更新を拒否)
- StepCalculator Box 抽出(+6 unit tests、再利用可能な純関数設計
- エラーメッセージ詳細化Expected/Found/Hint 形式)
**Impact**:
- Tests: 993 lib tests PASS, 60 normalized_dev tests PASS
- 箱化スコア: 9/10Phase 33 Box 理論の模範実装)
- コード削減: 5%321→305行、重複ロジック削除
**Reference**:
- continue_pattern.rs, step_calculator.rs
- jsonparser_unescape_string_step2_min.program.json
### Phase 89 P0: Continue + Early Return Pattern Detector (2025-12-14) ✅
**Status**: COMPLETE - Dev-only detector 確立完了
**Achievements**:
- Pattern4 detector 締めSelect 必須 + conditional Jump exactly 1
- LoopPattern::ContinueReturn enum 追加
- NormalizedDevShape::PatternContinueReturnMinimal 追加detector: Select + conditional Jumps >= 2
- Fail-Fast: UnimplementedPattern errorフォールバックなし
- canonical には入れないdev-only、誤爆防止
**Impact**:
- Pattern4 誤爆防止continue+return を正しく区別)
- Tests: 987 lib tests PASS
**Reference**:
- shape_guard.rs, loop_patterns/mod.rs, analysis.rs
- pattern_continue_return_min.program.json (fixture)
### Phase 89 P1: ContinueReturn Lowering Implementation (2025-12-14) ✅
**Status**: COMPLETE - 独立箱実装完了
**Achievements**:
- continue_return_pattern.rs 実装457行、StepCalculator 再利用)
- Continue + Early Return の JoinIR loweringloop 内 return 対応)
- Fail-Fast 原則徹底5箇所検証
- fixtures.rs に build_pattern_continue_return_min 追加
- normalized_dev tests +2vm_bridge + 期待値 n=10→acc=4
**Impact**:
- Tests: 993 lib tests PASS, 61 normalized_dev tests PASS (+2)
- 箱化原則:単一責任・境界明確・再利用性高
**Reference**:
- continue_return_pattern.rs, fixtures.rs
- tests/normalized_joinir_min/shapes.rs
- Phase 8690 は “dev-only fixtures + shape guard + fail-fast” で段階的に固定済み。
- 具体の fixture / shape / 未検証は `docs/development/current/main/phase86-90-loop-frontends-summary.md` を SSOT とする。
### Scope / BindingIddev-only の段階移行ライン)
@ -161,6 +71,8 @@
- Ring0.loginternal/dev
- println!test 専用)
の 3 層が `logging_policy.md` で整理済み。JoinIR/Loop trace も同ドキュメントに集約。
- VM backend の Box 解決UnifiedBoxRegistry / BoxFactoryRegistryの経路図:
- `docs/development/current/main/phase131-2-box-resolution-map.md`
---

View File

@ -1,5 +1,12 @@
# Self Current Task — Backlog (main)
直近のループ前線Phase 8690, dev-only fixturesは完了し、状況は `docs/development/current/main/10-Now.md`
`docs/development/current/main/phase86-90-loop-frontends-summary.md` に集約したよ。
設計検討docs-only, まだ未決定):
- Loop Canonicalizer / LoopSkeleton組み合わせ爆発を抑える前処理パス
- Loop Corpus Extractor実コード抽出→差分検知で fixture 化を支援)
短期JoinIR/selfhost ライン 第3章 - LLVM統合
- ✅ Phase 130: JoinIR → LLVM ベースライン確立(完了 2025-12-04
- 🎯 **Phase 131: JoinIR→LLVM 個別修正ライン**(次の最優先タスク)

View File

@ -0,0 +1,298 @@
# Phase 131-2: Box Resolution Mapping - 現状 vs 理想
## 🗺️ 現状マップVM Backend
```
MIR NewBox ConsoleBox
MirInterpreter::handle_new_box() (src/backend/mir_interpreter/handlers/boxes.rs)
[分岐点 1: Fast path?]
├─ Yes (NYASH_VM_FAST=1 + StringBox) → Direct creationbench/profile-only
└─ No → Continue
[分岐点 2: Provider Lock]
├─ guard_before_new_box() → OK/NG
└─ Continue
get_global_unified_registry() (src/runtime/unified_registry.rs)
UnifiedBoxRegistry::create_box("ConsoleBox", args) (src/box_factory/mod.rs)
[分岐点 3: FactoryPolicy による優先順位]
├─ BuiltinBoxFactory → builtin_implssrc/box_factory/builtin_impls/*
└─ PluginBoxFactory → BoxFactoryRegistry → PluginHost
(src/box_factory/plugin.rs) (src/runtime/box_registry.rs)
※ plugin_loader_unified が BoxFactoryRegistry を populate する
src/runtime/plugin_loader_unified.rs
```
**問題点**:
- ❌ 「Box 解決 SSOT」が 1 箇所に見えないUnifiedBoxRegistry と BoxFactoryRegistry に分散)
- ❌ 優先順位が “FactoryPolicy + provider mapping” の合成で、全体像が追いにくい
-`NYASH_VM_FAST` の特例が入口にあり、観測なしだと混乱しやすい
## 🎯 理想マップSSOT 化後)
```
MIR NewBox ConsoleBox
handle_new_box()
CoreBoxRegistry::create("ConsoleBox", args)
[SSOT: 登録情報検索]
CoreBoxId::Console in registry?
├─ Yes → Continue
└─ No → Error: "ConsoleBox not registered" (Fail-Fast!)
[優先順位: Plugin > Builtin]
registered_provider?
├─ Plugin → PluginHost.create_box()
│ ↓
│ TypeBox v2 FFI
│ ↓
│ console_invoke_id()
│ ↓
│ ConsoleInstance::new()
└─ Builtin → builtin::ConsoleBox::new()
VMValue::BoxRef(ConsoleBox)
```
**改善点**:
- ✅ 単一の Box 解決ルートSSOT
- ✅ CoreBoxId による型安全性
- ✅ 明確な優先順位Plugin > Builtin
- ✅ Fail-Fast見つからない = エラー)
## 📊 VM vs LLVM 比較表
| 項目 | VM Backend現状 | LLVM BackendPhase 133 | 理想SSOT化後 |
|------|-------------------|--------------------------|-----------------|
| **Box 登録** | ⚠️ UnifiedBoxRegistry + BoxFactoryRegistry | ✅ TypeRegistry + Plugin FFI | ✅ CoreBoxRegistry |
| **メソッド解決** | ❌ 複数経路boxes.rs 分岐) | ✅ ConsoleLlvmBridge 箱化 | ✅ 箱化モジュール |
| **ABI** | NyashBox trait | i8* + i64 (llvmlite) | ✅ 統一TypeRegistry SSOT |
| **優先順位** | ❓ 不明確 | ✅ Plugin > Builtin | ✅ 明示的優先順位 |
| **Fail-Fast** | ❌ フォールバック多数 | ✅ エラー即座に報告 | ✅ Fail-Fast 原則 |
| **型安全性** | ❌ 文字列ベース | ✅ TypeBox v2 | ✅ CoreBoxId enum |
| **SSOT 化** | ❌ 分散 | ✅ 完了Phase 133 | ✅ 完了(目標) |
## 🔍 登録システム詳細比較
### 現状VM Backend
```rust
// System 1: BoxFactoryRegistryplugin provider mapping
impl BoxFactoryRegistry {
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, String>
{
let provider = self.get_provider(name)?;
match provider {
BoxProvider::Builtin(constructor) => constructor(args),
BoxProvider::Plugin(plugin_name) =>
self.create_plugin_box(&plugin_name, name, args),
}
}
}
// System 2: UnifiedBoxRegistryVM NewBox の入口)
// src/backend/mir_interpreter/handlers/boxes.rs::handle_new_box
let reg = crate::runtime::unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
// Builtins は UnifiedBoxRegistry 内で BuiltinBoxFactory が担当し、
// builtin_impls/* の実装へ委譲される(外部フォールバックではない)。
```
**問題**:
- 3つのシステムの関係が不明
- どれが優先されるのか不明確
- エラーハンドリングが統一されていない
### Phase 133LLVM Backend- 成功モデル
```python
# ConsoleLlvmBridge: 単一の箱化モジュール
def emit_console_call(builder, module, method_name, args, ...):
if method_name not in CONSOLE_METHODS:
return False # Fail-Fast: 即座に不明通知
runtime_fn_name = CONSOLE_METHODS[method_name]
callee = _declare(module, runtime_fn_name, i64, [i8p])
builder.call(callee, [arg0_ptr])
return True # 成功
```
**成功要因**:
- ✅ CONSOLE_METHODS が SSOT唯一の真実
- ✅ 箱化モジュール1箇所に集約
- ✅ Fail-Fast不明メソッド = False 即座)
- ✅ TypeRegistry との ABI 一致
### 理想SSOT化後の VM Backend
```rust
// CoreBoxRegistry: 単一の SSOT
pub struct CoreBoxRegistry {
core_boxes: RwLock<HashMap<CoreBoxId, CoreBoxEntry>>,
user_boxes: RwLock<HashMap<String, UserBoxEntry>>,
}
impl CoreBoxRegistry {
pub fn create(&self, box_name: &str, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, RuntimeError>
{
// 1. CoreBoxId 変換
let box_id = CoreBoxId::from_name(box_name)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", box_name)
})?;
// 2. 登録情報取得SSOT
let entry = self.core_boxes.read().unwrap()
.get(&box_id)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Box not registered: {:?}", box_id)
})?
.clone();
// 3. 優先順位に従って生成
match entry.provider {
CoreBoxProvider::Plugin { plugin_name, type_id } => {
self.plugin_host.create_box(&plugin_name, type_id, args)
}
CoreBoxProvider::Builtin { constructor } => {
constructor(args)
}
}
}
}
```
**改善点**:
- ✅ CoreBoxId による型安全性
- ✅ 単一の登録マップSSOT
- ✅ 明示的な優先順位Plugin > Builtin
- ✅ Fail-Fast登録なし = エラー)
## 🎯 Phase 133 の教訓
### 成功パターン: 箱化モジュール化
```
Before (Phase 132): 40 行の分岐が boxcall.py に埋め込み
After (Phase 133): 1 行の箱化呼び出し
# Before
if method_name in ("print", "println", "log"):
# ... 40 行のロジック ...
# After
if emit_console_call(builder, module, method_name, args, ...):
return
```
**成果**:
- 分岐を 1 箇所に集約
- テスト容易性向上
- レガシー削除が簡単
### VM Backend への適用
```rust
// Before現状: 複数経路の分岐
let reg = unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
// AfterPhase 131-3: 箱化モジュール化
let created = CoreBoxRegistry::global()
.create(box_type, &converted)?;
```
## 📋 Phase 131-3 実装ガイド
### Step 1: 現状SSOTの所在を固定入口と接続
```bash
# VM NewBox の入口(ここから追う)
rg "fn handle_new_box\\(" src/backend/mir_interpreter/handlers/boxes.rs
# global accessor と registry 本体
rg "get_global_unified_registry\\(" src/runtime/unified_registry.rs
rg "struct UnifiedBoxRegistry" src/box_factory/mod.rs
# plugin 側の provider mapping
rg "struct BoxFactoryRegistry" src/runtime/box_registry.rs
```
**前提**:
- UnifiedBoxRegistry は既に存在するNewBox の入口)。
- BoxFactoryRegistry は PluginBoxFactory の provider mapping として間接利用される。
### Step 2: CoreBoxId 統合
```rust
// CoreBoxId に基づく検証ロジック追加
impl CoreBoxRegistry {
pub fn validate_on_startup(&self, profile: &RuntimeProfile)
-> Result<(), String>
{
for box_id in CoreBoxId::iter() {
if box_id.is_required_in(&profile) && !self.has(box_id) {
return Err(format!("Missing core_required box: {:?}", box_id));
}
}
Ok(())
}
}
```
### Step 3: プラグイン優先順位の明確化
```rust
// 登録時に優先順位を決定
impl CoreBoxRegistry {
/// プラグイン設定適用(既存ビルトインを上書き)
pub fn apply_plugin_config(&mut self, config: &PluginConfig) {
for (box_name, plugin_name) in &config.plugins {
if let Some(box_id) = CoreBoxId::from_name(box_name) {
self.register_plugin(box_id, plugin_name); // 上書き
}
}
}
}
```
### Step 4: Fail-Fast の徹底
```rust
// ❌ 削除対象: フォールバックロジック
if let Err(_) = create_plugin_box() {
create_builtin_box() // 隠蔽!
}
// ✅ 追加: 即座にエラー
create_plugin_box()
.map_err(|e| VMError::InvalidInstruction(
format!("ConsoleBox plugin failed: {:?}. Check nyash.toml", e)
))?
```
## ✅ チェックリストPhase 131-3
- [ ] UnifiedBoxRegistry / BoxFactoryRegistry の責務境界を SSOT として固定
- [ ] CoreBoxRegistry を新設するなら “入口SSOT” を 1 箇所にするNewBox から見える形)
- [ ] CoreBoxId 統合(型安全性)
- [ ] プラグイン優先順位の明確化Plugin > Builtin
- [ ] Fail-Fast 原則の徹底(フォールバック削除)
- [ ] 起動時検証テスト追加
- [ ] VM/LLVM 両方で ConsoleBox 生成確認
---
**Status**: Ready for Implementation
**Next Phase**: 131-3 (SSOT Implementation)
**Estimated Time**: 4-6 hours

View File

@ -0,0 +1,570 @@
# Phase 131-2: ConsoleBox 問題根治調査レポート
## 🎯 調査目的
実アプリの E2E で最初に詰まる「Box 登録/認識」問題を特定し、VM/LLVM 共通の土台を確立する。
## 📊 調査結果サマリ
### 問題の本質
**ConsoleBox は既に両 backend で完全実装済み** - 問題は **登録メカニズムの分岐** にある:
1. **VM backend**: Box 解決が「UnifiedBoxRegistry入口 + BoxFactoryRegistryplugin provider mapping + VM fast path」の合成に見え、全体像が追いにくい
2. **LLVM backend**: 別の経路TypeRegistry/FFIを使用Phase 133 の事例あり)
3. **両者の規約不一致**: “どれをSSOTとして読むべきか” が docs/コードで分散している
### 重要な発見
#### ✅ LLVM 側は統合事例があるPhase 133 / archive
LLVM backend の ConsoleBox 問題は **Phase 133 で完全解決済み**:
- ConsoleLlvmBridge 箱化モジュール実装済み
- TypeRegistry との ABI 完全一致
- 7/7 テスト全て PASS
**結論**: LLVM 側は “参考モデル” として参照し、当面の焦点は VM 側の経路可視化と SSOT 明文化。
#### ⚠️ VM backend の Box 解決が「2層 + 特例」に見える(問題の根源)
VM backend では “入口” と “plugin provider mapping” が分かれており、加えて入口に特例がある:
```
1. BoxFactoryRegistry (src/runtime/box_registry.rs)
- Plugin-First アーキテクチャ
- プラグイン設定で上書き可能
- グローバルレジストリ
2. UnifiedBoxRegistry + global accessor
- global accessor: `src/runtime/unified_registry.rs`
- registry 本体: `src/box_factory/mod.rs`
- handle_new_box()VM NewBoxが使用
3. VM fast path特例
- `NYASH_VM_FAST=1` のとき `StringBox` をレジストリ経由せず生成bench/profile-only
```
## 🔍 詳細分析
### 1. VM Backend の Box 解決フロー
#### NewBox 命令処理src/backend/mir_interpreter/handlers/boxes.rs
```rust
pub(super) fn handle_new_box(
&mut self,
dst: ValueId,
box_type: &str,
args: &[ValueId],
) -> Result<(), VMError> {
// ① Provider Lock guard既定は挙動不変
provider_lock::guard_before_new_box(box_type)?;
// ② Fast path (StringBox のみ、NYASH_VM_FAST=1 時)
if box_type == "StringBox" { /* ... */ }
// ③ 統一レジストリ経由で生成 ← ここが主経路
let reg = unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
// ④ 生成結果を VMValue に変換して格納
self.regs.insert(dst, VMValue::from_nyash_box(created));
Ok(())
}
```
**問題点**:
- `unified_registry``src/runtime/unified_registry.rs` にあり、`UnifiedBoxRegistry``src/box_factory/mod.rs`
- `UnifiedBoxRegistry``PluginBoxFactory``src/box_factory/plugin.rs`)を通じて `BoxFactoryRegistry` を参照するため、間接的に両者が接続されている
- Provider Lock の役割が曖昧(「既定は挙動不変」)
#### BoxFactoryRegistry の設計src/runtime/box_registry.rs
```rust
pub struct BoxFactoryRegistry {
providers: RwLock<HashMap<String, BoxProvider>>,
}
pub enum BoxProvider {
Builtin(BoxConstructor), // 互換用(テスト専用)
Plugin(String), // プラグイン実装
}
```
**特徴**:
- Plugin-First 設計(プラグインがビルトインを上書き可能)
- `apply_plugin_config()` で nyash.toml から動的登録
- `create_plugin_box()` → PluginHost 経由で実際の生成
**疑問**:
- このレジストリは `PluginBoxFactory` の provider mapping として使用されるVM NewBox → UnifiedBoxRegistry → PluginBoxFactory → BoxFactoryRegistry
- provider の populate は `src/runtime/plugin_loader_unified.rs` が行う(`apply_plugin_config`
#### Builtin Fallbacksrc/box_factory/builtin_impls/console_box.rs
```rust
/// Create builtin ConsoleBox instance
///
/// Primary: nyash-console-plugin
/// Fallback: This builtin implementation (selfhost support)
pub fn create(_args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> {
// Phase 151: Quiet fallback (no deprecation warning)
Ok(Box::new(ConsoleBox::new()))
}
```
**用途**:
- セルフホスト Stage-3 パイプライン用
- プラグイン初期化失敗時のバックアップ
- Phase 151 で追加Phase 150 のエラー対処)
### 2. LLVM Backend の Box 解決フローPhase 133 完了済み)
#### ConsoleLlvmBridgesrc/llvm_py/console_bridge.py
```python
CONSOLE_METHODS = {
"log": "nyash.console.log",
"println": "nyash.console.log", # Phase 122: log のエイリアス
"warn": "nyash.console.warn",
"error": "nyash.console.error",
"clear": "nyash.console.clear",
}
def emit_console_call(builder, module, method_name, args, ...):
"""ConsoleBox method call to LLVM IR"""
if method_name not in CONSOLE_METHODS:
return False
runtime_fn_name = CONSOLE_METHODS[method_name]
# LLVM IR generation...
return True
```
**特徴**:
- Phase 133 で箱化モジュール化済み
- TypeRegistry の slot 400-403 と完全一致
- Phase 122 の println/log エイリアス統一を継承
#### TypeRegistry との連携src/runtime/type_registry.rs
```rust
// ConsoleBox 用 slot 定義
const CONSOLE_METHODS: &[MethodEntry] = &[
MethodEntry { name: "log", arity: 1, slot: 400 },
MethodEntry { name: "println", arity: 1, slot: 400 }, // エイリアス
MethodEntry { name: "warn", arity: 1, slot: 401 },
MethodEntry { name: "error", arity: 1, slot: 402 },
MethodEntry { name: "clear", arity: 0, slot: 403 },
];
```
**SSOT 化の成果**:
- ✅ メソッド名 → slot 番号のマッピングが一元管理
- ✅ VM/LLVM 両方で共通の型情報を参照
- ✅ Phase 122 のエイリアス統一が完全適用
### 3. Plugin System との連携
#### TypeBox v2 FFIplugins/nyash-console-plugin/src/lib.rs
```rust
#[no_mangle]
pub static nyash_typebox_ConsoleBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
abi_tag: 0x54594258, // 'TYBX'
version: 1,
name: b"ConsoleBox\0".as_ptr() as *const c_char,
resolve: Some(console_resolve),
invoke_id: Some(console_invoke_id),
capabilities: 0,
};
```
**特徴**:
- TypeBox v2 FFI を実装
- `resolve()` でメソッド名 → ID 変換
- `invoke_id()` で実際のメソッド呼び出し
**問題点**:
- VM backend の `unified_registry` がこの FFI を使っているか不明
- プラグインローダーとの接続点が見えない
### 4. CoreBoxId システムPhase 87
#### CoreBoxId 定義src/runtime/core_box_ids.rs
```rust
pub enum CoreBoxId {
String, Integer, Bool, Array, Map, Console, // core_required
Float, Null, File, Path, Regex, Math, Time, Json, Toml, // core_optional
Function, Result, Method, Missing, // 特殊型
}
impl CoreBoxId {
pub fn is_core_required(&self) -> bool {
matches!(self, String | Integer | Bool | Array | Map | Console | File)
}
pub fn is_required_in(&self, profile: &RuntimeProfile) -> bool {
match profile {
RuntimeProfile::Default => self.is_core_required(),
RuntimeProfile::NoFs => self.is_core_required() && *self != Self::File,
}
}
}
```
**設計思想**:
- Core Box を型安全な enum で管理
- Phase 85 の core_required/core_optional 分類を実装
- Runtime profile による動的要件変更
**問題点**:
- この enum が **Box 登録の強制力** を持っているか不明
- `is_core_required()` の結果が実際の登録処理に反映されているか要確認
### 5. CoreServicesPhase 91
#### CoreServices 定義src/runtime/core_services.rs
```rust
pub trait ConsoleService: Send + Sync {
fn println(&self, msg: &str);
fn print(&self, msg: &str);
}
pub struct CoreServices {
pub string: Option<Arc<dyn StringService>>,
pub integer: Option<Arc<dyn IntegerService>>,
pub bool: Option<Arc<dyn BoolService>>,
pub array: Option<Arc<dyn ArrayService>>,
pub map: Option<Arc<dyn MapService>>,
pub console: Option<Arc<dyn ConsoleService>>,
}
```
**特徴**:
- Ring1-Core の Service trait 群
- Phase 87 CoreBoxId の core_required をカバー
- Optional 化により Graceful Degradation 対応
**疑問**:
- この Service trait は **誰が実装しているのか?**
- VM の Box 呼び出しがこの trait を経由するのか?
## 🚨 発見した問題点
### 1. **SSOT の欠如(最重要)**
Box 登録規約が複数箇所に分散:
```
┌─ BoxFactoryRegistry (グローバル)
├─ UnifiedBoxRegistry (NewBox の入口)
├─ BuiltinBoxFactory (UnifiedBoxRegistry 内の factory)
├─ CoreBoxId enum (型定義)
├─ CoreServices (Service trait)
└─ TypeRegistry (メソッド情報)
```
**問題**:
- どのレジストリが「正」なのか不明
- 登録順序・優先度の規約がない
- 初期化タイミングが散在
### 2. **VM と LLVM の分岐**
| 項目 | VM Backend | LLVM Backend |
|------|-----------|--------------|
| Box 登録 | UnifiedBoxRegistry + BoxFactoryRegistry | TypeRegistry + Plugin FFI |
| メソッド解決 | BoxCall handler | ConsoleLlvmBridge |
| ABI | NyashBox trait | i8* + i64 |
| SSOT 化 | ❌ 不明 | ✅ Phase 133 完了 |
### 3. **失敗パターンの特定**
Phase 150/151 で報告されたエラー:
```
[ERROR] ❌ [rust-vm] VM error: Invalid instruction: NewBox ConsoleBox:
invalid operation: Unknown Box type: ConsoleBox. Available: Main
```
**原因推測**:
1. セルフホスト経由時にプラグイン初期化が失敗
2. plugin provider mappingBoxFactoryRegistryが未設定で PluginBoxFactory が失敗
3. BuiltinBoxFactory が無効(`plugins-only`)または該当 Box が builtin factory に登録されていない
**Phase 151 の対処**:
- `builtin_impls/console_box.rs` を builtin 実装として追加/整備UnifiedBoxRegistry 内の BuiltinBoxFactory 経由)
- "Available: Main" → ConsoleBox が登録されるようになった
## 💡 SSOT 化提案
### 設計原則
1. **Single Source of Truth**: Box 登録規約を一箇所に集約
2. **Explicit Dependencies**: 依存関係を明示化
3. **Fail-Fast**: エラーは早期に明示的に失敗
4. **Box-First**: Phase 33 の箱理論に基づく設計
### 提案 A: UnifiedBoxRegistry を入口SSOTとして強化保守的アプローチ
```rust
// src/runtime/unified_registry.rs / src/box_factory/mod.rs を “入口SSOT” として扱い、
// provider mapping や core_required 検証の責務をどこまで寄せるかを整理する。
pub struct UnifiedBoxRegistry {
// SSOT: すべての Box 登録情報
factories: RwLock<BTreeMap<String, BoxProvider>>,
// CoreBoxId による必須 Box 検証
core_validator: CoreBoxValidator,
// プラグインローダーとの接続
plugin_loader: Arc<PluginHost>,
}
impl UnifiedBoxRegistry {
/// 初期化時に core_required Box を強制登録
pub fn new(profile: RuntimeProfile) -> Self {
let mut reg = Self { /* ... */ };
// CoreBoxId に基づく必須 Box 登録
for box_id in CoreBoxId::iter() {
if box_id.is_required_in(&profile) {
reg.register_core_box(box_id)?;
}
}
reg
}
/// Box 生成(優先順位: Plugin > Builtin > Error
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, RuntimeError>
{
// 1. プラグイン検索
if let Some(provider) = self.factories.read().unwrap().get(name) {
match provider {
BoxProvider::Plugin(plugin_name) => {
return self.plugin_loader.create_box(name, args);
}
BoxProvider::Builtin(constructor) => {
return constructor(args);
}
}
}
// 2. エラーSSOT なので見つからない = 存在しない)
Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name)
})
}
/// プラグイン設定の適用nyash.toml から)
pub fn apply_plugin_config(&mut self, config: &PluginConfig) {
for (box_name, plugin_name) in &config.plugins {
self.factories.write().unwrap()
.insert(box_name.clone(), BoxProvider::Plugin(plugin_name.clone()));
}
}
}
```
**利点**:
- 既存コードへの影響最小
- CoreBoxId による型安全性
- プラグイン優先順位の明確化
### 提案 B: CoreBoxRegistry 新設(理想的アプローチ)
```rust
// src/runtime/core_box_registry.rs (新規)
/// Phase 131-2: Core Box 登録の SSOT
pub struct CoreBoxRegistry {
// CoreBoxId → 登録情報のマッピング
core_boxes: RwLock<HashMap<CoreBoxId, CoreBoxEntry>>,
// ユーザー定義 BoxCoreBoxId 以外)
user_boxes: RwLock<HashMap<String, UserBoxEntry>>,
}
struct CoreBoxEntry {
box_id: CoreBoxId,
provider: CoreBoxProvider,
metadata: CoreBoxMetadata,
}
enum CoreBoxProvider {
Plugin { plugin_name: String, type_id: u32 },
Builtin { constructor: fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> },
}
impl CoreBoxRegistry {
/// 初期化時の必須 Box 検証
pub fn validate_core_boxes(&self, profile: &RuntimeProfile) -> Result<(), String> {
let missing: Vec<_> = CoreBoxId::iter()
.filter(|id| id.is_required_in(profile))
.filter(|id| !self.has_core_box(*id))
.collect();
if !missing.is_empty() {
return Err(format!("Missing core_required boxes: {:?}", missing));
}
Ok(())
}
/// Box 生成(型安全)
pub fn create_core_box(&self, box_id: CoreBoxId, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, RuntimeError>
{
let entry = self.core_boxes.read().unwrap()
.get(&box_id)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Core box not registered: {:?}", box_id)
})?
.clone();
match entry.provider {
CoreBoxProvider::Plugin { plugin_name, type_id } => {
self.create_plugin_box(&plugin_name, type_id, args)
}
CoreBoxProvider::Builtin { constructor } => {
constructor(args)
}
}
}
}
```
**利点**:
- CoreBoxId による完全な型安全性
- 必須 Box の起動時検証
- プラグイン vs ビルトインの明確な分離
## 🎯 次のステップ
### Phase 131-3: SSOT 実装(推奨)
1. **入口SSOTの確定VM NewBox**
- 入口は `src/backend/mir_interpreter/handlers/boxes.rs::handle_new_box`
`src/runtime/unified_registry.rs` / `src/box_factory/mod.rs`UnifiedBoxRegistryで固定する
- BoxFactoryRegistry は “plugin provider mapping” として位置付け、関係図を SSOT 化する
2. **CoreBoxId との統合**
- `CoreBoxValidator` 実装
- 起動時の必須 Box 検証
3. **プラグインローダーとの接続明確化**
- plugin_loader_unified が BoxFactoryRegistry を populate し、
PluginBoxFactoryUnifiedBoxRegistry 内)が BoxFactoryRegistry を参照する流れを明文化
- TypeBox v2 FFI との整合method/type id の参照箇所)を整理
4. **既存 BoxFactoryRegistry の整理**
- provider mapping を BoxFactoryRegistry に閉じるか、UnifiedBoxRegistry に吸収するかを決める
- 移行ガイドの作成
### Phase 131-4: テストケース追加
```rust
#[test]
fn test_core_box_registration() {
let reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
// core_required が全て登録されていること
for box_id in CoreBoxId::iter() {
if box_id.is_core_required() {
assert!(reg.has_box(box_id.name()));
}
}
// ConsoleBox が生成できること
let console = reg.create_box("ConsoleBox", &[]).unwrap();
assert_eq!(console.type_name(), "ConsoleBox");
}
#[test]
fn test_plugin_priority() {
let mut reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
// ビルトイン ConsoleBox 登録済み
assert!(reg.has_box("ConsoleBox"));
// プラグイン設定で上書き
let mut config = PluginConfig::default();
config.plugins.insert("ConsoleBox".to_string(), "nyash-console".to_string());
reg.apply_plugin_config(&config);
// プラグイン版が優先されること
let console = reg.create_box("ConsoleBox", &[]).unwrap();
// プラグイン判定ロジック(実装依存)
}
```
## 📋 関連ファイル一覧
### VM Backend
- `src/runtime/box_registry.rs` - BoxFactoryRegistry
- `src/runtime/unified_registry.rs` - global UnifiedBoxRegistry accessor
- `src/box_factory/mod.rs` - UnifiedBoxRegistryFactoryPolicy / create_box
- `src/box_factory/plugin.rs` - PluginBoxFactoryBoxFactoryRegistry を参照)
- `src/runtime/core_box_ids.rs` - CoreBoxId enum
- `src/runtime/core_services.rs` - CoreServices trait
- `src/runtime/type_registry.rs` - TypeRegistry (メソッド情報)
- `src/backend/mir_interpreter/handlers/boxes.rs` - handle_new_box()
- `src/box_factory/builtin_impls/console_box.rs` - Builtin 実装BuiltinBoxFactory 経由)
### LLVM Backend
- `src/llvm_py/console_bridge.py` - ConsoleLlvmBridgePhase 133
- `src/llvm_py/instructions/boxcall.py` - BoxCall lowering
### Plugin System
- `plugins/nyash-console-plugin/src/lib.rs` - TypeBox v2 FFI
- `src/runtime/plugin_host.rs` - PluginHost
### Core Infrastructure
- `src/boxes/console_box.rs` - ConsoleBox 実装
- `crates/nyash_kernel/src/lib.rs` - NyRT ランタイム
## 🔍 未解決の疑問
1. **UnifiedBoxRegistry ↔ BoxFactoryRegistry の責務境界**
- provider mapping の SSOT をどこに置くかBoxFactoryRegistry を残す/吸収する)
- `UnifiedBoxRegistry::FactoryPolicy` と plugin_config の関係をどう可視化するか
2. **Provider Lock の役割**
- `provider_lock::guard_before_new_box()` の実装と目的
- 「既定は挙動不変」の意味
3. **CoreServices の実装者**
- ConsoleService trait を実装しているのは誰か
- VM の Box 呼び出しがこの trait を経由するのか
4. **初期化順序**
- プラグインローダー、レジストリ、サービスの初期化タイミング
- 循環依存の有無
## ✅ 成果
### 明確になった事実
1. **LLVM backend は問題なし** - Phase 133 で完全解決済み
2. **VM backend の Box 解決が分散して見える** - UnifiedBoxRegistry入口と BoxFactoryRegistryprovider mappingの関係を SSOT 化する必要がある
3. **ConsoleBox 自体は完全実装** - 登録メカニズムの問題
4. **CoreBoxId システム存在** - 型安全性の基盤あり
### 提案した解決策
1. **UnifiedBoxRegistry 強化** - 保守的アプローチ
2. **CoreBoxRegistry 新設** - 理想的アプローチ
3. **起動時検証** - Fail-Fast 原則の実現
---
**Status**: Investigation Complete - Ready for Implementation
**Next Phase**: 131-3 (SSOT Implementation)
**Estimated Time**: 4-6 hours

View File

@ -0,0 +1,135 @@
# Phase 131-2: ConsoleBox 問題根治調査 - エグゼクティブサマリ
## 🎯 調査結論3行サマリ
1. **LLVM 側は既存の統合事例がある** - Phase 133archiveを参考にできる
2. **VM backend の Box 解決が分散して見える** - UnifiedBoxRegistry入口と BoxFactoryRegistryplugin provider mappingの関係が追いにくい
3. **ConsoleBox 自体は問題なし** - 迷子の原因は “どこを見ればよいか” の SSOT 不在
## 📊 問題の本質
### VM Backend の Box 解決が「2層 + 特例」に見える(問題の根源)
```
┌─ BoxFactoryRegistry (src/runtime/box_registry.rs)
│ └─ Plugin-First 設計、グローバルレジストリ
├─ UnifiedBoxRegistry (src/box_factory/mod.rs) + global accessor (src/runtime/unified_registry.rs)
│ └─ MIR `NewBox`VMの入口
└─ VM fast path (NYASH_VM_FAST=1 + StringBox)
└─ ベンチ/プロファイル用の最適化(入口で分岐するため観測なしだと混乱しやすい)
```
**問題**:
- どのレジストリが「正」なのか不明
- 登録順序・優先度の規約がない
- VM と LLVM で Box 解決方法が異なる
## ✅ LLVM Backend の成功事例(参考モデル)
Phase 133 で既に **完全な SSOT 化** を達成:
```python
# ConsoleLlvmBridge (src/llvm_py/console_bridge.py)
CONSOLE_METHODS = {
"log": "nyash.console.log",
"println": "nyash.console.log", # Phase 122: エイリアス統一
"warn": "nyash.console.warn",
"error": "nyash.console.error",
"clear": "nyash.console.clear",
}
```
**成果**:
- ✅ TypeRegistry との ABI 完全一致slot 400-403
- ✅ Phase 122 のエイリアス統一を継承
- ✅ 7/7 テスト全て PASS
## 💡 推奨アクション
### 優先度 1: 入口SSOTの所在確認VM NewBox
```bash
# 主要入口の所在この3箇所を見る
rg "get_global_unified_registry\\(|struct UnifiedBoxRegistry|struct BoxFactoryRegistry" src/ --type rust
```
**次の判断**:
- UnifiedBoxRegistry を “入口SSOT” として明文化し、BoxFactoryRegistry は plugin provider mapping として位置付ける
- その上で CoreBoxId / CoreServices との接続Fail-Fast の境界)を設計する
### 優先度 2: CoreBoxId との統合
```rust
// CoreBoxId に基づく必須 Box 検証
pub fn validate_core_boxes(&self, profile: &RuntimeProfile) -> Result<(), String> {
let missing: Vec<_> = CoreBoxId::iter()
.filter(|id| id.is_required_in(profile))
.filter(|id| !self.has_core_box(*id))
.collect();
if !missing.is_empty() {
return Err(format!("Missing core_required boxes: {:?}", missing));
}
Ok(())
}
```
### 優先度 3: Fail-Fast 原則の徹底
```rust
// ❌ 悪い例:フォールバック
if let Err(_) = create_plugin_box() {
create_builtin_box() // 隠蔽される!
}
// ✅ 良い例:即座に失敗
create_plugin_box()
.map_err(|e| format!("ConsoleBox plugin failed: {:?}", e))?
```
## 🔍 未解決の疑問Phase 131-3 で調査)
1. **UnifiedBoxRegistry ↔ BoxFactoryRegistry の責務境界**
- “plugin provider mapping” を BoxFactoryRegistry に閉じ込めるなら、どこまで公開するか(例: box_types 一覧)
- `NYASH_BOX_FACTORY_POLICY` と plugin_config の関係を SSOT 化する場所
2. **Provider Lock の役割**
- `provider_lock::guard_before_new_box()` の目的
- 「既定は挙動不変」の意味
3. **CoreServices の実装者**
- ConsoleService trait を誰が実装しているか
- Box 呼び出しが trait を経由するか
## 📋 関連ドキュメント
- **詳細レポート**: [phase131-2-consolebox-investigation.md](./phase131-2-consolebox-investigation.md)
- **Phase 133 成果**: [docs/archive/phases/phase-106-156/phase133_consolebox_llvm_integration.md](../../../archive/phases/phase-106-156/phase133_consolebox_llvm_integration.md)
- **CoreBoxId 設計**: `src/runtime/core_box_ids.rs`
- **TypeRegistry**: `src/runtime/type_registry.rs`
## ⏱️ 次のステップPhase 131-3
### タスク概要
1. SSOT 設計決定1時間
2. 実装2-3時間
3. テスト追加1時間
**合計見積もり**: 4-6時間
### 完成条件
- [ ] UnifiedBoxRegistry / BoxFactoryRegistry の責務境界を SSOT として固定
- [ ] CoreBoxId に基づく必須 Box 検証実装
- [ ] プラグイン vs ビルトイン優先順位の明確化
- [ ] 起動時の core_required Box 検証テスト追加
- [ ] VM/LLVM 両方で ConsoleBox 生成成功確認
---
**Status**: Investigation Complete ✅
**Next Phase**: 131-3 (SSOT Implementation)
**Owner**: ChatGPT + Claude 協働

View File

@ -0,0 +1,66 @@
# Phase 8690 Summary — Loop Frontends (dev-only fixtures)
目的: 実アプリ由来のループ形を「fixture + shape guard + fail-fast」で段階的に JoinIR frontend に取り込み、
Normalized-dev の回帰テストで固定する。
このファイルは Phase 8690 の“ループ前線”だけを 1 枚に集約するサマリ。
詳細ログや設計の背景は各 Phase 文書に委譲し、このサマリでは **到達点 / SSOT / fixture / 未検証**だけを書く。
## SSOT参照の優先順位
- JoinIR 全体SSOT: `docs/development/current/main/joinir-architecture-overview.md`
- いまの状態: `docs/development/current/main/10-Now.md`
- タスク優先度: `CURRENT_TASK.md`
## Phase 86 — Carrier Init Builder + Error Tags ✅
- 目的: ValueId 生成とエラー語彙を SSOT 化し、段階移行ラインの土台を固める
- SSOT modules:
- `src/mir/builder/control_flow/joinir/merge/carrier_init_builder.rs`
- `src/mir/join_ir/lowering/error_tags.rs`
## Phase 87 — LLVM exe line SSOT ✅
- 目的: `.hako → executable` の手順を `tools/build_llvm.sh` に統一し、Smoke を 1 本に固定する
- SSOT:
- `tools/build_llvm.sh`
- `docs/development/current/main/phase87-selfhost-llvm-exe-line.md`
## Phase 88 — continue + 可変ステップdev-only fixture
- 目的: `continue` 分岐で `i` が可変ステップ更新される形(`i = i + const`)を段階拡張し、回帰を固定する
- 追加: continue 分岐側での carrier 更新(例: `acc`)を許可
- Fail-Fast: const 以外の step 更新は拒否
- Fixture:
- `docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_unescape_string_step2_min.program.json`
## Phase 89 — ContinueReturndetector + lowering
- 目的: `continue + early return`loop 内 returnを Pattern4 と誤認しないように shape を分離し、frontend lowering を追加する
- Shape guard:
- Pattern4 detector を厳格化(誤爆防止)
- ContinueReturn 用 detector を追加dev-only
- Fixtures:
- `docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json`
- `docs/private/roadmap2/phases/normalized_dev/fixtures/continue_return_multi_min.program.json`(同一値の複数 return-if
## Phase 90 — ParseStringCompositedev-only fixture
- 目的: `_parse_string` の制御骨格escape continue + close-quote returnを “制御だけ抽出” した合成 fixture として固定する
- Fixture:
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_string_composite_min.program.json`
- 追加(実ループ寄せの土台、制御抽出):
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_array_min.program.json`
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_object_min.program.json`
## RefactorPhase 8990 の維持性向上)
- Fixture 名・パス・ルーティングの SSOT:
- `src/mir/join_ir/normalized/dev_fixtures.rs`
- Shape detector の共通化Inspector 等)は `shape_guard.rs` を参照
## 未検証SSOT にしない)
- 実コード(`tools/hako_shared/json_parser.hako`)の `_parse_string/_parse_array/_parse_object` を、
JoinIR frontend で “そのまま” E2E 実行するラインdev-only での段階投入)
- 文字列・配列・マップなど Box の意味論を含む大域 E2Efixture は制御抽出が主目的)

View File

@ -27,6 +27,130 @@ Establish single source of truth for `.hako → .o → executable → execution`
# Expected: 0.40.0 or newer
```
## Compiler Modes
`tools/build_llvm.sh` supports two compiler modes for LLVM object generation:
### Harness (Default) - Production Ready
**Python llvmlite-based LLVM IR generation**
- **Stability**: ✅ Proven stable, battle-tested
- **Build Time**: Fast (~1-3s for minimal programs)
- **Dependencies**: Python 3, llvmlite, LLVM 18
- **Use Case**: Default for all production builds
**Enable** (default behavior):
```bash
# Explicit mode selection (optional):
NYASH_LLVM_COMPILER=harness tools/build_llvm.sh program.hako -o output
# Default (no env var needed):
tools/build_llvm.sh program.hako -o output
```
**How it works**:
1. `hakorune --backend llvm` invokes Python harness
2. `src/llvm_py/llvm_builder.py` generates LLVM IR via llvmlite
3. `llc-18` compiles IR to object file
### Crate (Experimental) - Rust-native Compiler
**Pure Rust LLVM IR generation via crates/nyash-llvm-compiler**
- **Stability**: ⚠️ Experimental, under active development
- **Build Time**: Slower (~5-10s, requires crate compilation)
- **Dependencies**: LLVM 18 dev libraries, Rust toolchain
- **Use Case**: Advanced users, development/testing
**Enable**:
```bash
NYASH_LLVM_COMPILER=crate tools/build_llvm.sh program.hako -o output
```
**How it works**:
1. `hakorune --emit-mir-json` generates MIR JSON
2. `ny-llvmc` (Rust crate) reads JSON and emits LLVM IR
3. `llc-18` compiles IR to object file
**Advanced: Direct exe emission** (experimental):
```bash
NYASH_LLVM_COMPILER=crate NYASH_LLVM_EMIT=exe \
tools/build_llvm.sh program.hako -o output
# Skips separate linking step, emits executable directly
```
### Mode Comparison Table
| Feature | Harness (Default) | Crate (Experimental) |
|---------|------------------|----------------------|
| **Stability** | ✅ Production ready | ⚠️ Experimental |
| **Build Time** | Fast (1-3s) | Moderate (5-10s) |
| **Dependencies** | Python + llvmlite | LLVM dev + Rust |
| **MIR JSON** | Internal | Explicit generation |
| **Direct exe** | ❌ Not supported | ✅ Experimental |
| **Recommended For** | All users | Advanced/dev only |
**Default recommendation**: Use harness mode (no env vars needed).
## Environment Variables Reference
### Control Variables
These environment variables control the build pipeline behavior:
| Variable | Default | Purpose | Example |
|----------|---------|---------|---------|
| `NYASH_BIN` | `./target/release/hakorune` | Path to hakorune binary | `NYASH_BIN=/custom/path/hakorune` |
| `NYASH_LLVM_COMPILER` | `harness` | Compiler mode: `harness` or `crate` | `NYASH_LLVM_COMPILER=crate` |
| `NYASH_LLVM_FEATURE` | `llvm` | LLVM feature flag for cargo build | `NYASH_LLVM_FEATURE=llvm-inkwell-legacy` |
| `NYASH_LLVM_OBJ_OUT` | `target/aot_objects/<stem>.o` | Object file output path | `NYASH_LLVM_OBJ_OUT=/tmp/custom.o` |
| `NYASH_CLI_VERBOSE` | `0` | Enable verbose build output | `NYASH_CLI_VERBOSE=1` |
### Advanced Control Variables
For specialized use cases and debugging:
| Variable | Default | Purpose | Example |
|----------|---------|---------|---------|
| `NYASH_LLVM_SKIP_EMIT` | `0` | Skip object generation (use pre-generated .o) | `NYASH_LLVM_SKIP_EMIT=1` |
| `NYASH_LLVM_ONLY_OBJ` | `0` | Stop after object generation (skip linking) | `NYASH_LLVM_ONLY_OBJ=1` |
| `NYASH_LLVM_SKIP_NYRT_BUILD` | `0` | Skip Nyash Kernel runtime build | `NYASH_LLVM_SKIP_NYRT_BUILD=1` |
| `NYASH_LLVM_MIR_JSON` | (auto-generated) | Pre-generated MIR JSON path (crate mode) | `NYASH_LLVM_MIR_JSON=/tmp/mir.json` |
| `NYASH_LLVM_VALIDATE_JSON` | `0` | Validate MIR JSON schema (crate mode) | `NYASH_LLVM_VALIDATE_JSON=1` |
| `NYASH_LLVM_EMIT` | `obj` | Emission mode: `obj` or `exe` (crate only) | `NYASH_LLVM_EMIT=exe` |
| `NYASH_LLVM_NYRT` | `crates/nyash_kernel/target/release` | Nyash Kernel runtime path | `NYASH_LLVM_NYRT=/custom/nyrt` |
| `NYASH_LLVM_LIBS` | (empty) | Additional link libraries | `NYASH_LLVM_LIBS="-lcustom"` |
| `NYASH_LLVM_USE_HARNESS` | (auto-set) | Force Python harness usage | `NYASH_LLVM_USE_HARNESS=1` |
### Typical Usage Examples
**Basic build** (all defaults):
```bash
tools/build_llvm.sh program.hako -o output
```
**Verbose debugging**:
```bash
NYASH_CLI_VERBOSE=1 tools/build_llvm.sh program.hako -o output
```
**Use pre-generated object file**:
```bash
# Step 1: Generate object via smoke test
./tools/smokes/v2/run.sh --profile quick --filter 'smoke_obj_*'
# Step 2: Link using existing object
NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT=target/aot_objects/existing.o \
tools/build_llvm.sh program.hako -o output
```
**Crate mode with validation**:
```bash
NYASH_LLVM_COMPILER=crate NYASH_LLVM_VALIDATE_JSON=1 \
tools/build_llvm.sh program.hako -o output
```
## Standard Procedure
**Build and execute** a .hako program to native executable:
@ -107,6 +231,150 @@ clang-18 tmp/program.o -o tmp/program
**Output**: Native executable
## Success/Failure Criteria
### Success Indicators
A successful build via `tools/build_llvm.sh` exhibits:
**1. Exit Code**: `0`
```bash
tools/build_llvm.sh program.hako -o output
echo $? # Should output: 0
```
**2. All 4 Steps Complete**:
```
[1/4] Building hakorune (feature selectable) ...
[2/4] Emitting object (.o) via LLVM backend ...
[3/4] Building Nyash Kernel static runtime ...
[4/4] Linking output ...
✅ Done: output
```
**3. Executable Generated**:
```bash
ls -lh output
# Should exist and be executable
file output
# Output: ELF 64-bit LSB executable, x86-64, dynamically linked
```
**4. Executable Runs**:
```bash
./output
echo $?
# Should match expected exit code (e.g., 42 for phase87_llvm_exe_min.hako)
```
### Failure Modes
`build_llvm.sh` uses distinct exit codes for different failure types:
| Exit Code | Meaning | Common Cause |
|-----------|---------|--------------|
| **0** | ✅ Success | Build completed normally |
| **1** | Usage error | Missing input file or invalid arguments |
| **2** | Missing dependency | `llvm-config-18` not found |
| **3** | Compilation failure | Object file not generated (MIR/LLVM IR error) |
| **Other** | System/linking error | Linking failure, missing libraries |
**Exit Code 1** - Usage Error:
```bash
tools/build_llvm.sh
# Output: Usage: tools/build_llvm.sh <input.hako> [-o <output>]
# Exit: 1
```
**Exit Code 2** - Missing LLVM:
```bash
# When llvm-config-18 not installed
tools/build_llvm.sh program.hako -o output
# Output: error: llvm-config-18 not found (install LLVM 18 dev).
# Exit: 2
```
**Exit Code 3** - Object Generation Failed:
```bash
# When MIR/LLVM IR compilation fails
tools/build_llvm.sh bad_program.hako -o output
# Output: error: object not generated: target/aot_objects/bad_program.o
# Exit: 3
```
### Validation Commands
**Verify object file validity**:
```bash
# Check object file exists and has correct format
file target/aot_objects/program.o
# Expected: ELF 64-bit relocatable, x86-64
# Check object file symbols
nm target/aot_objects/program.o | grep -E '(main|nyash_)'
# Should show exported symbols
```
**Verify LLVM IR validity** (when using crate mode with JSON):
```bash
# Step 1: Generate LLVM IR (manual)
NYASH_LLVM_COMPILER=crate NYASH_LLVM_MIR_JSON=tmp/test.json \
tools/build_llvm.sh program.hako -o output
# Step 2: Validate LLVM IR
llvm-as-18 tmp/test.ll -o /dev/null
# Should complete without errors
# Step 3: Disassemble and inspect
llvm-dis-18 target/aot_objects/program.o -o - | less
# Should show valid LLVM IR
```
**Verify MIR JSON validity** (crate mode):
```bash
# Ensure MIR JSON is well-formed
jq . tmp/test.json > /dev/null
echo $? # Should output: 0
# Optional: Schema validation
NYASH_LLVM_VALIDATE_JSON=1 NYASH_LLVM_COMPILER=crate \
tools/build_llvm.sh program.hako -o output
```
### Build Time Expectations
Typical build times for `phase87_llvm_exe_min.hako` (minimal program):
| Step | Expected Time | Notes |
|------|---------------|-------|
| **[1/4] Build hakorune** | ~0.5-2s | Incremental build (release) |
| **[2/4] Emit object** | ~1-2s | Harness mode (llvmlite) |
| | ~5-10s | Crate mode (ny-llvmc) |
| **[3/4] Build Nyash Kernel** | ~1-3s | Incremental build (release) |
| **[4/4] Linking** | ~0.2-0.5s | Native linker (cc/clang) |
| **Total** | ~3-8s | Harness mode |
| | ~7-15s | Crate mode |
**First build**: Add ~30-60s for initial `cargo build --release` compilation.
**Performance factors**:
- **Parallel builds**: `-j 24` used by default (see `build_llvm.sh`)
- **Incremental builds**: `CARGO_INCREMENTAL=1` enabled
- **Cache hits**: Subsequent builds much faster (~1-3s total)
**Troubleshooting slow builds**:
```bash
# Check cargo cache status
cargo clean --release -p nyash-rust
cargo clean --release -p nyash-llvm-compiler
# Rebuild with timing information
time tools/build_llvm.sh program.hako -o output
# Verbose output for bottleneck analysis
NYASH_CLI_VERBOSE=1 time tools/build_llvm.sh program.hako -o output
```
## Troubleshooting
### Issue: llvm-config-18 not found
@ -181,6 +449,206 @@ llvm-as-18 test.ll -o /dev/null
# Should complete without errors
```
### Debugging Build Pipeline
When `build_llvm.sh` fails, use these techniques to isolate the problem:
#### Enable Verbose Mode
**Global verbose output**:
```bash
NYASH_CLI_VERBOSE=1 tools/build_llvm.sh program.hako -o output
# Shows detailed command execution via set -x
```
**Step-by-step verbosity**:
```bash
# Verbose hakorune compilation
NYASH_CLI_VERBOSE=1 ./target/release/hakorune --emit-mir-json tmp/debug.json program.hako
# Verbose Python LLVM builder
python3 -v src/llvm_py/llvm_builder.py tmp/debug.json -o tmp/debug.ll
# Verbose LLVM compilation
llc-18 -debug tmp/debug.ll -o tmp/debug.o -filetype=obj
# Verbose linking
cc -v tmp/debug.o -L crates/nyash_kernel/target/release -lnyash_kernel -o output
```
#### Manual Step Tracing
**Isolate each step** to find exact failure point:
**Step 1: Test MIR emission**:
```bash
./target/release/hakorune --emit-mir-json tmp/test.json program.hako
echo $? # Should be 0
jq . tmp/test.json # Validate JSON
```
**Step 2: Test LLVM IR generation**:
```bash
# Harness mode (default)
NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm program.hako
# Check exit code
# Crate mode
cargo build --release -p nyash-llvm-compiler
./target/release/ny-llvmc --in tmp/test.json --out tmp/test.o
file tmp/test.o # Should be ELF object
```
**Step 3: Test object compilation**:
```bash
# If .ll file available (crate mode intermediate)
llc-18 -filetype=obj tmp/test.ll -o tmp/test.o
file tmp/test.o # Verify ELF format
nm tmp/test.o # Check symbols
```
**Step 4: Test linking**:
```bash
# Ensure Nyash Kernel built
cd crates/nyash_kernel && cargo build --release
cd ../..
# Manual link
cc tmp/test.o \
-L crates/nyash_kernel/target/release \
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
-lpthread -ldl -lm -o tmp/manual_output
# Test execution
./tmp/manual_output
echo $?
```
#### Save Intermediate Files
**Preserve all build artifacts** for inspection:
```bash
# Create debug directory
mkdir -p debug_build
# Step 1: Emit MIR JSON
./target/release/hakorune --emit-mir-json debug_build/program.json program.hako
# Step 2: Generate LLVM IR (harness mode, manual Python call)
python3 src/llvm_py/llvm_builder.py debug_build/program.json -o debug_build/program.ll
# Step 3: Compile to object
llc-18 debug_build/program.ll -o debug_build/program.o -filetype=obj
# Step 4: Link
cc debug_build/program.o \
-L crates/nyash_kernel/target/release \
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
-lpthread -ldl -lm -o debug_build/program
# Inspect all intermediate files
ls -lh debug_build/
file debug_build/*
```
**Inspect saved artifacts**:
```bash
# View MIR JSON structure
jq '.functions[] | {name: .name, blocks: .blocks | length}' debug_build/program.json
# View LLVM IR
less debug_build/program.ll
# Disassemble object file
objdump -d debug_build/program.o | less
# Check symbols
nm debug_build/program.o
nm debug_build/program
```
#### Common LLVM IR Issues
**Problem 1: Invalid function signature**
```
error: expected type '...' but found '...'
```
**Diagnosis**: MIR → LLVM IR type mismatch
**Fix**: Check MIR JSON `functions[].signature`, ensure correct types
**Problem 2: Undefined symbol**
```
error: undefined reference to 'nyash_...'
```
**Diagnosis**: Missing Nyash Kernel runtime symbols
**Fix**:
```bash
# Rebuild Nyash Kernel
cd crates/nyash_kernel && cargo clean && cargo build --release
# Verify symbols available
nm crates/nyash_kernel/target/release/libnyash_kernel.a | grep nyash_
```
**Problem 3: Invalid IR instruction**
```
error: invalid IR instruction '...'
```
**Diagnosis**: Python llvm_builder.py bug or unsupported MIR instruction
**Fix**:
```bash
# Check LLVM IR syntax
llvm-as-18 -o /dev/null debug_build/program.ll
# Error message shows exact line number
# Inspect problematic instruction
sed -n '<line>p' debug_build/program.ll
```
**Problem 4: Linking failure**
```
ld: symbol(s) not found for architecture x86_64
```
**Diagnosis**: Missing system libraries or incorrect link order
**Fix**:
```bash
# Check what symbols are needed
nm -u debug_build/program.o
# Verify Nyash Kernel provides them
nm crates/nyash_kernel/target/release/libnyash_kernel.a | grep <symbol>
# If system library missing, add to NYASH_LLVM_LIBS
NYASH_LLVM_LIBS="-lmissing_lib" tools/build_llvm.sh program.hako -o output
```
#### Environment Variables for Debugging
Combine multiple debugging flags:
```bash
# Maximum verbosity + preserve artifacts
NYASH_CLI_VERBOSE=1 \
NYASH_LLVM_COMPILER=crate \
NYASH_LLVM_MIR_JSON=/tmp/debug.json \
NYASH_LLVM_VALIDATE_JSON=1 \
tools/build_llvm.sh program.hako -o /tmp/debug_output
# Then inspect intermediate files
ls -lh /tmp/debug*
jq . /tmp/debug.json
cat /tmp/debug.ll
```
**Recommended debugging workflow**:
1. Enable `NYASH_CLI_VERBOSE=1` for initial diagnosis
2. Use manual step tracing to isolate failure
3. Save intermediate files for inspection
4. Check LLVM IR validity with `llvm-as-18`
5. Verify object symbols with `nm`
6. Test linking manually with verbose `cc -v`
## What NOT to Do
❌ **DO NOT** create custom link procedures: