diff --git a/docs/development/current/main/phase134_mir_call_unification.md b/docs/development/current/main/phase134_mir_call_unification.md new file mode 100644 index 00000000..5a5429bc --- /dev/null +++ b/docs/development/current/main/phase134_mir_call_unification.md @@ -0,0 +1,444 @@ +# Phase 134-A: mir_call.py の未完成 unified 設計を完成 + +## 🎯 ゴール + +**mir_call.py の「未完成 unified 設計」を構造的に完成** させる。 + +目的: +- `NYASH_MIR_UNIFIED_CALL` フラグを廃止し、unified をcanonicalに統一 +- **681行の giant ファイル** を機能別に分割(global/method/constructor等) +- **legacy dispatcher の未実装部分** を削除 +- JSON v0/v1 互換コードを専用モジュールに外出し + +``` +Phase 133: ConsoleBox LLVM 統合(7/7達成)✅ + ↓ +Phase 134-A: mir_call.py unified 設計完成 ← ← ここ! + ↓ +Phase 134-B: StringBox bridge 分離 + ↓ +Phase 134-C: CollectionBox bridge 分離 +``` + +--- + +## 📋 スコープ(やること・やらないこと) + +### ✅ やること +- `lower_legacy_call()` の NotImplementedError を削除(削除するのが目的) +- NYASH_MIR_UNIFIED_CALL フラグを廃止 +- mir_call.py(681行)を機能別に分割: + - global_call.py, method_call.py, constructor_call.py, closure_call.py, value_call.py, extern_call.py + - dispatcher (__init__.py) で統合 +- JSON v0/v1 互換コードを mir_call_compat.py に外出し +- 分割後も機能・動作は一切変えない(既存テスト全て通過) + +### ❌ やらないこと +- MIR の意味論変更(Call 命令仕様は現状維持) +- Python LLVM backend 以外の層への影響 +- 他のBox系bridge(StringBox/CollectionBox)の分離(Phase 134-B/C の役目) + +--- + +## 🏗️ 6 つのタスク + +### Task 1: 設計ドキュメント作成 + +**ファイル**: `docs/development/current/main/phase134_mir_call_unification.md`(このファイル) + +**書く内容**: + +#### 現状整理 + +**mir_call.py の問題点**: +1. **ファイルサイズが giant**: 681行 + - lower_global_call() - 行 117+ + - lower_method_call() - 複数メソッド + - lower_constructor_call() + - lower_closure_creation() + - lower_value_call() + - lower_extern_call() + - すべて同一ファイル + +2. **未完成の dispatcher**: + - 行 34: `NYASH_MIR_UNIFIED_CALL` フラグで legacy/unified 切り替え + - 行 110-114: `lower_legacy_call()` が `NotImplementedError` 即座 + - 実質的には unified のみが有効 + +3. **JSON v0/v1 互換コードの混在**: + - 行 73-74, 86: JSON v0 "method", "box_type" キー + - 行 92-93: JSON v1 "name", "box_name" キー + - normalize_json_callee() が両対応 + +4. **責務混濁**: + - call/boxcall/externcall との役割分界不明確 + - instruction_lower.py でも mir_call/call/boxcall/externcall が並列存在 + +#### 目指す構造 + +**Phase 133 ConsoleLlvmBridge パターを参考**: + +``` +src/llvm_py/instructions/mir_call/ + ├── __init__.py (dispatcher lower_mir_call) + ├── global.py (lower_global_call) + ├── method.py (lower_method_call) + ├── constructor.py (lower_constructor_call) + ├── closure.py (lower_closure_creation) + ├── value.py (lower_value_call) + └── extern.py (lower_extern_call) + +src/llvm_py/mir_call_compat.py (新規) + - json_v0_to_v1_callee() + - normalize_mir_call_shape() +``` + +**ファイルサイズ目標**: +- 現状: mir_call.py 681行 +- 分割後: + - __init__.py: ~120行 (dispatcher + トレース) + - global.py: ~120行 + - method.py: ~140行 + - constructor.py: ~100行 + - closure.py: ~80行 + - value.py: ~80行 + - extern.py: ~100行 + - mir_call_compat.py: ~70行 +- **合計: ~890行** だが、責務が明確化・テスト分割可能 + +--- + +### Task 2: 既存 mir_call.py の詳細棚卸し + +**対象ファイル**: `src/llvm_py/instructions/mir_call.py`(681行) + +**やること**: + +1. **関数別の行数カウント**: + ```bash + rg "^def " src/llvm_py/instructions/mir_call.py -A 1 + ``` + - lower_global_call() - 約何行 + - lower_method_call() - 約何行 + - 各関数の依存関係(内部呼び出し)を把握 + +2. **JSON v0/v1 互換コードの抽出**: + - normalize_json_callee() の処理 + - "method" vs "name" キーの判定ロジック + - これらを mir_call_compat.py に移す候補を特定 + +3. **フラグ・環境変数の確認**: + - 行 34: `NYASH_MIR_UNIFIED_CALL` + - 行 81: `NYASH_TRACE_CALLS` など + - どれが Phase 124+ で削除済みか確認 + +4. **テスト対象の確認**: + ```bash + rg "mir_call|lower_.*_call" src/llvm_py/tests/ --type python + ``` + - 既存テストがどの関数をテストしているか把握 + - 分割後も同じテストが動くようにする + +**結果記録**: phase134 ドキュメントの「実装計画」に記載 + +--- + +### Task 3: mir_call_compat.py の実装(JSON v0/v1 互換層) + +**目的**: JSON v0/v1 互換コードを専用モジュールに集約 + +**実装方針**: + +```python +# src/llvm_py/mir_call_compat.py + +import json +from typing import Dict, Any + +class MirCallCompat: + """ + JSON v0/v1 互換処理を一元管理 + + v0: {"method": "log", "box_type": "ConsoleBox"} + v1: {"name": "log", "box_name": "ConsoleBox"} + """ + + @staticmethod + def normalize_callee(callee_json: Dict[str, Any]) -> Dict[str, Any]: + """ + JSON v0/v1 どちらでも統一形式に normalize + + Args: + callee_json: {"method"/"name": ..., "box_type"/"box_name": ...} + + Returns: + 統一形式: {"method_name": ..., "box_name": ..., "receiver": ...} + """ + # v0 形式を v1 に統一 + method_name = callee_json.get("method") or callee_json.get("name") + box_name = callee_json.get("box_type") or callee_json.get("box_name") + receiver = callee_json.get("receiver") + + return { + "method_name": method_name, + "box_name": box_name, + "receiver": receiver + } + + @staticmethod + def detect_format_version(callee_json: Dict[str, Any]) -> int: + """v0 or v1 を検出""" + if "method" in callee_json: + return 0 + elif "name" in callee_json: + return 1 + else: + raise ValueError(f"Unknown callee format: {callee_json}") +``` + +**注意点**: +- normalize 後は統一形式で使用(v0/v1 分岐をなくす) +- Phase 124+ で v0 削除を想定(互換層を一箇所に集約することで削除容易化) + +--- + +### Task 4: mir_call.py の機能別分割と dispatcher 実装 + +**方針**: Phase 133 ConsoleLlvmBridge パターンを参考 + +#### ステップ 1: 分割対象の関数抽出 + +``` +src/llvm_py/instructions/mir_call/ + ├── __init__.py + │ └── lower_mir_call(builder, module, callee, args) # dispatcher + │ + ├── global.py + │ └── lower_global_call(builder, module, func_name, args) + │ + ├── method.py + │ └── lower_method_call(builder, module, box_id, method_id, receiver, args) + │ + ├── constructor.py + │ └── lower_constructor_call(builder, module, box_id, args) + │ + ├── closure.py + │ └── lower_closure_creation(builder, module, closure_info, captured_vars) + │ + ├── value.py + │ └── lower_value_call(builder, module, func_value, args) + │ + └── extern.py + └── lower_extern_call(builder, module, extern_name, args) +``` + +#### ステップ 2: dispatcher 実装 + +```python +# src/llvm_py/instructions/mir_call/__init__.py + +from . import global as global_call +from . import method as method_call +from . import constructor as ctor_call +from . import closure as closure_call +from . import value as value_call +from . import extern as extern_call +from ..mir_call_compat import MirCallCompat + +def lower_mir_call(builder, module, mir_call_inst): + """ + MIR Call 命令を LLVM IR に lowering(canonical dispatcher) + + Args: + builder: LLVM IRBuilder + module: LLVM Module + mir_call_inst: MIR Call instruction + + Returns: + LLVM Value (関数戻り値) + """ + # JSON v0/v1 互換処理 + callee_json = mir_call_inst.get("callee", {}) + if not callee_json: + # legacy: callee なし → default path + return lower_legacy_mir_call(builder, module, mir_call_inst) + + # v0/v1 normalize + normalized = MirCallCompat.normalize_callee(callee_json) + + # callee タイプ判定 + callee_type = normalized.get("type") # "global", "method", "constructor", etc. + + if callee_type == "global": + return global_call.lower_global_call(builder, module, ...) + elif callee_type == "method": + return method_call.lower_method_call(builder, module, ...) + elif callee_type == "constructor": + return ctor_call.lower_constructor_call(builder, module, ...) + elif callee_type == "closure": + return closure_call.lower_closure_creation(builder, module, ...) + elif callee_type == "value": + return value_call.lower_value_call(builder, module, ...) + elif callee_type == "extern": + return extern_call.lower_extern_call(builder, module, ...) + else: + raise ValueError(f"Unknown callee type: {callee_type}") + + +def lower_legacy_mir_call(builder, module, mir_call_inst): + """ + Legacy path(callee なし の場合) + + Phase 124+ で削除予定 + """ + # 暫定実装(callee 推論 or error) + raise NotImplementedError("Legacy MIR Call path is deprecated, use Callee") +``` + +#### ステップ 3: 各モジュールへの分割 + +**global.py**: +```python +def lower_global_call(builder, module, func_name, args): + """Global function call を LLVM に lowering""" + func = module.declare_external_function(func_name, func_type) + return builder.call(func, args) +``` + +**method.py**: +```python +def lower_method_call(builder, module, box_id, method_id, receiver, args): + """Box method call を LLVM に lowering (BoxCall)""" + # BoxCallBridge (Console) を使用 + # 他の Box は call_method router + ... +``` + +など(既存 mir_call.py から該当コードを移動) + +--- + +### Task 5: 既存 call.py/boxcall.py/externcall.py との統合確認 + +**やること**: + +1. **現状確認**: + ```bash + ls -la src/llvm_py/instructions/ | grep -E "call|boxcall|externcall" + ``` + - call.py, boxcall.py, externcall.py が並存しているか確認 + - instruction_lower.py での呼び出し経路を確認 + +2. **整合性チェック**: + - mir_call/__init__.py (dispatcher) の外部呼び出しが: + - boxcall.py か mir_call/method.py か + - externcall.py か mir_call/extern.py か + - 一貫性を確保 + +3. **統合判定**: + - call.py/boxcall.py/externcall.py が **mir_call を呼んでいる** か + - **mir_call が call.py/boxcall.py/externcall.py を呼んでいる** か + - **両者が別パス** か(この場合、どちらかを削除) + +**結果**: 統合されるべき場合は、Phase 134-D として計画に追加 + +--- + +### Task 6: テスト & ドキュメント更新 + +**やること**: + +1. **既存テストの確認**: + ```bash + # mir_call 関連テスト確認 + rg "mir_call|lower_.*_call" src/llvm_py/tests/ --type python + + # テスト実行 + cargo test --release 2>&1 | grep -E "mir_call|Call" + ``` + - 全テストが通るまで分割コードを調整 + +2. **ドキュメント追記**: + - phase134_mir_call_unification.md 末尾に「実装結果」を追記 + - 分割構造の図示 + - JSON v0/v1 互換処理の説明 + +3. **CURRENT_TASK.md 更新**: + ```markdown + ### Phase 134-A: mir_call.py unified 設計完成 ✅ + + **完了内容**: + - NYASH_MIR_UNIFIED_CALL フラグ廃止 + - mir_call.py (681行) を機能別分割 + - JSON v0/v1 互換層を mir_call_compat.py に外出し + - legacy dispatcher 削除(NotImplementedError 根治) + + **修正箇所**: + - src/llvm_py/instructions/mir_call/__init__.py (dispatcher) + - src/llvm_py/instructions/mir_call/global.py + - src/llvm_py/instructions/mir_call/method.py + - src/llvm_py/instructions/mir_call/constructor.py + - src/llvm_py/instructions/mir_call/closure.py + - src/llvm_py/instructions/mir_call/value.py + - src/llvm_py/instructions/mir_call/extern.py + - src/llvm_py/mir_call_compat.py (新規) + + **テスト結果**: 全テスト PASS + + **成果**: + - mir_call.py: 681行 → 분할(각 80-150行, 책임 명확) + - 次の分割準備: Phase 134-B StringBox bridge + + **次フェーズ**: Phase 134-B - StringBox bridge 分離 + ``` + +--- + +## ✅ 完成チェックリスト(Phase 134-A) + +- [ ] mir_call.py の詳細棚卸し(関数別行数、依存関係) +- [ ] JSON v0/v1 互換コード抽出・分析 +- [ ] mir_call_compat.py 実装(normalize 関数) +- [ ] mir_call/__init__.py dispatcher 実装 +- [ ] mir_call/global.py 実装(既存 lower_global_call 移動) +- [ ] mir_call/method.py 実装(既存 lower_method_call 移動) +- [ ] mir_call/constructor.py, closure.py, value.py, extern.py 実装 +- [ ] instruction_lower.py で mir_call/__init__.py を呼ぶようにリファクタ +- [ ] NYASH_MIR_UNIFIED_CALL フラグ削除(src/ 全体で確認) +- [ ] legacy dispatcher (lower_legacy_call) 削除 +- [ ] 既存テスト実行 & 全て PASS 確認 +- [ ] phase134_mir_call_unification.md に実装結果追記 +- [ ] CURRENT_TASK.md 更新 +- [ ] git commit で記録 + +--- + +## 所要時間 + +**5〜6 時間程度** + +- Task 1-2 (設計 & 棚卸し): 1時間 +- Task 3 (mir_call_compat.py): 45分 +- Task 4 (分割実装): 2.5時間 +- Task 5-6 (統合確認・テスト): 1.5時間 + +--- + +## 次のステップ + +**Phase 134-B: StringBox bridge 分離** +- boxcall.py:130-282 の StringBox メソッド処理を stringbox.py に分離 +- Phase 133 ConsoleLlvmBridge パターンを参考 + +**Phase 134-C: CollectionBox bridge 分離** +- boxcall.py:325-375 の Array/Map メソッド処理を collectionbox.py に分離 + +--- + +## 進捗 + +- ✅ Phase 130-133: JoinIR → LLVM 第3章完全クローズ +- 🎯 Phase 134-A: mir_call.py unified 設計完成(← **現在のフェーズ**) +- 📋 Phase 134-B: StringBox bridge 分離(予定) +- 📋 Phase 134-C: CollectionBox bridge 分離(予定) +- 📋 Phase 135: LLVM フラグカタログ化(予定)