feat(joinir): Add Phase 40 implementation templates and integration points

Phase 35-39整理(C): JoinIR Frontend拡張雛形作成
- ast_lowerer.rs: extract_if_in_loop_modified_vars() template (~80 lines)
- ast_lowerer.rs: extract_assigned_vars_from_body() template (~80 lines)
- ast_lowerer.rs: lower_loop_case_a_simple() integration point marked
- join_ir_vm_bridge.rs: convert_join_function_to_mir() extension template (~100 lines)

Templates include:
- Detailed purpose and implementation plan
- Integration points in existing code
- Example usage (array_ext.filter)
- Links to Phase 39 design docs
- TODO(Phase 40-1/2/3/4) markers

Effect: Phase 40 implementation ready to start (追加箇所明確、実装漏れ防止)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-28 07:34:26 +09:00
parent 231eb6e138
commit a3f617874d
2 changed files with 281 additions and 1 deletions

View File

@ -20,7 +20,7 @@
//! - **A/B テスト可能**: 既存経路と新経路の両方で実行して比較検証
use crate::mir::join_ir::{ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, VarId};
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};
/// Phase 34-5: 式から値を抽出する際のコンテキスト
///
@ -334,7 +334,24 @@ impl AstToJoinIrLowerer {
/// - k_exit: 結果を return
///
/// Phase 34-7.4b: Local ノード処理(同名再宣言 = 再代入)
/// ループ本体loweringPhase 34実装
///
/// # Phase 40拡張予定
///
/// この関数に以下を追加:
/// 1. `extract_if_in_loop_modified_vars()`呼び出し
/// 2. 検出された変数をloop exit PHI生成に使用
///
/// ```rust,ignore
/// // TODO(Phase 40-1): Add if-in-loop variable tracking
/// // let loop_vars = self.get_loop_carried_vars(&loop_ctx);
/// // let modified_in_if = self.extract_if_in_loop_modified_vars(&loop_body, &loop_vars);
/// // Pass modified_in_if to create_exit_function() for PHI generation
/// ```
fn lower_loop_case_a_simple(&mut self, program_json: &serde_json::Value) -> JoinModule {
// TODO(Phase 40-1): Add if-in-loop variable tracking here
// Integration point for extract_if_in_loop_modified_vars()
// 1. Program(JSON) から defs を取得
let defs = program_json["defs"]
.as_array()
@ -1228,6 +1245,166 @@ impl AstToJoinIrLowerer {
_ => panic!("Unsupported expr type: {}", expr_type),
}
}
// ========================================
// Phase 40拡張予定: If-in-loop AST Lowering
// ========================================
/// Phase 40-1で実装予定: ループ内if文の変数追跡
///
/// # Purpose
///
/// array_ext.filter等のif-in-loopパターンで、ループ内で修正される変数を
/// 追跡し、ループ出口PHI生成に使用する。
///
/// # Implementation Plan (Phase 40-1)
///
/// ## Input
/// - `loop_body`: ループ本体ASTJSON v0形式
/// - `loop_vars`: ループで使用される変数Header PHIで定義
///
/// ## Output
/// - `HashSet<String>`: if分岐内で修正された変数名セット
///
/// ## Logic
/// 1. Recursive AST walk (helper: `extract_assigned_vars_from_body`)
/// 2. Detect assignments in if branches only
/// 3. Filter for variables in `loop_vars` (loop-carried variables)
/// 4. Return set of modified variable names
///
/// ## Integration Point
/// - Call from: `lower_loop_case_a_simple()` or similar loop lowering
/// - Use output for: Loop exit PHI generation in `create_exit_function()`
///
/// # Example
///
/// ```nyash,ignore
/// local out = new ArrayBox() // loop_vars = {out, i}
/// local i = 0
/// loop(i < n) {
/// if fn(arr[i]) { // ← この中の代入を検出
/// out.push(arr[i]) // ← out修正検出
/// }
/// i = i + 1
/// }
/// // Result: extract_if_in_loop_modified_vars() = {out}
/// // → Loop exit PHI: phi out_exit = (out_header, out_loop_modified)
/// ```
///
/// # Replaces (Phase 40-1削除対象)
///
/// - `if_phi::collect_assigned_vars()` (32 lines)
/// - Current callsites: loop_builder.rs:1069, 1075
/// - この関数実装でcallsites削除可能
///
/// # See Also
///
/// - Design: `docs/.../phase-39-if-phi-level2/joinir_extension_design.md`
/// - A/B test: array_ext.filter (Primary representative function)
///
/// # TODO(Phase 40-1)
///
/// ```rust,ignore
/// fn extract_if_in_loop_modified_vars(
/// &mut self,
/// loop_body: &serde_json::Value,
/// loop_vars: &HashSet<String>,
/// ) -> HashSet<String> {
/// // Step 1: Recursive AST walk
/// let all_assigned = self.extract_assigned_vars_from_body(loop_body);
///
/// // Step 2: Filter for if-statement assignments only
/// let if_assigned = all_assigned.iter()
/// .filter(|var| self.is_assigned_in_if_branch(loop_body, var))
/// .cloned()
/// .collect::<HashSet<_>>();
///
/// // Step 3: Filter for loop-carried variables
/// if_assigned.intersection(loop_vars).cloned().collect()
/// }
/// ```
#[allow(dead_code)]
fn extract_if_in_loop_modified_vars(
&mut self,
_loop_body: &serde_json::Value,
_loop_vars: &HashSet<String>,
) -> HashSet<String> {
// TODO(Phase 40-1): Implement if-in-loop variable tracking
// See: docs/.../phase-39-if-phi-level2/joinir_extension_design.md
unimplemented!("Phase 40-1: If-in-loop variable tracking for array_ext.filter")
}
/// Phase 40-1で実装予定: 再帰的AST走査代入検出
///
/// # Purpose
///
/// AST bodyを再帰的に走査し、代入文を検出する。
///
/// # Implementation Plan
///
/// ## Recursive Descent
/// - Handle: "Local" assignments (`local x = ...` or `x = ...`)
/// - Handle: Nested blocks (`{ ... }`)
/// - Handle: If/Loop bodies (recursive call)
///
/// ## Return
/// - `HashSet<String>`: 代入された変数名全て
///
/// # Example
///
/// ```json
/// {
/// "type": "Block",
/// "body": [
/// {"type": "Local", "name": "x", "expr": ...}, // x assigned
/// {"type": "If", "cond": ..., "then": [
/// {"type": "Local", "name": "y", "expr": ...} // y assigned
/// ]}
/// ]
/// }
/// ```
/// Result: {x, y}
///
/// # TODO(Phase 40-1)
///
/// ```rust,ignore
/// fn extract_assigned_vars_from_body(
/// &mut self,
/// body: &serde_json::Value,
/// ) -> HashSet<String> {
/// let mut assigned = HashSet::new();
///
/// if let Some(stmts) = body.get("body").and_then(|b| b.as_array()) {
/// for stmt in stmts {
/// match stmt.get("type").and_then(|t| t.as_str()) {
/// Some("Local") => {
/// if let Some(name) = stmt.get("name").and_then(|n| n.as_str()) {
/// assigned.insert(name.to_string());
/// }
/// }
/// Some("If") => {
/// // Recursive: then/else branches
/// if let Some(then_body) = stmt.get("then") {
/// assigned.extend(self.extract_assigned_vars_from_body(then_body));
/// }
/// }
/// // ... other cases ...
/// _ => {}
/// }
/// }
/// }
///
/// assigned
/// }
/// ```
#[allow(dead_code)]
fn extract_assigned_vars_from_body(
&mut self,
_body: &serde_json::Value,
) -> HashSet<String> {
// TODO(Phase 40-1): Implement recursive AST assignment detection
unimplemented!("Phase 40-1: Recursive AST assignment detection")
}
}
impl Default for AstToJoinIrLowerer {

View File

@ -166,9 +166,112 @@ pub(crate) fn convert_joinir_to_mir(join_module: &JoinModule) -> Result<MirModul
}
/// JoinFunction → MirFunction 変換
///
/// # Phase 40拡張予定: Loop Exit PHI for If-in-loop
///
/// ## Current Implementation (Phase 34)
///
/// - Header PHI: ループ開始時の変数(ループ不変変数含む)
/// - Exit PHI: ループ終了時の変数(単純パターンのみ)
///
/// ## Phase 40 Extension Required
///
/// ### Problem
///
/// 現在、if-in-loopで修正される変数のloop exit PHIが生成されない。
///
/// ```nyash,ignore
/// local out = new ArrayBox()
/// loop(i < n) {
/// if fn(arr[i]) { out.push(arr[i]) } // ← out修正
/// i = i + 1
/// }
/// // Missing: phi out_exit = (out_header, out_if_modified)
/// ```
///
/// ### Solution (Phase 40-1)
///
/// JoinIR Frontend AST loweringで検出したif-in-loop修正変数を
/// JoinModule metadataに格納し、MIR loweringで読み取る。
///
/// ### Implementation Plan
///
/// 1. JoinModule metadataに`if_in_loop_modified_vars: HashSet<String>`追加
/// 2. AST lowering時に`extract_if_in_loop_modified_vars()`結果をmetadataに格納
/// 3. MIR lowering時にmetadataから読み取り、loop exit PHI生成
///
/// ### Code Location
///
/// この関数内で以下を追加:
///
/// ```rust,ignore
/// // TODO(Phase 40-1): Generate loop exit PHI for if-in-loop modified variables
/// if let Some(modified_vars) = join_func.metadata.get("if_in_loop_modified_vars") {
/// for var_name in modified_vars.as_array().unwrap() {
/// let var_name_str = var_name.as_str().unwrap();
///
/// // Get header value (loop entry)
/// let header_value = self.get_var_value_at_header(var_name_str);
///
/// // Get exit value (loop body last modification)
/// let exit_value = self.get_var_value_at_exit(var_name_str);
///
/// // Emit PHI at loop exit block
/// self.emit_phi(
/// exit_block_id,
/// var_name_str,
/// vec![header_value, exit_value],
/// );
/// }
/// }
/// ```
///
/// ### Example (array_ext.filter)
///
/// ```nyash,ignore
/// // Input AST
/// local out = new ArrayBox()
/// local i = 0
/// loop(i < arr.len()) {
/// if fn(arr[i]) { out.push(arr[i]) } // out modified in if
/// i = i + 1
/// }
/// return out
///
/// // JoinIR (Phase 40)
/// entry():
/// out_init = new ArrayBox
/// i_init = 0
/// call loop_step(i_init, out_init, arr)
///
/// loop_step(i, out, arr):
/// exit_cond = i >= arr.len()
/// jump k_exit(out) if exit_cond // ← out needs PHI at k_exit
/// elem = arr[i]
/// should_push = fn(elem)
/// out_next = select(should_push, out.push(elem), out) // conditional modification
/// i_next = i + 1
/// call loop_step(i_next, out_next, arr)
///
/// k_exit(out_final):
/// ret out_final
///
/// // MIR (Phase 40)
/// // At loop exit block:
/// %out_exit = phi [%out_init, entry], [%out_next, loop_step] // ← Phase 40で生成
/// ```
///
/// # See Also
///
/// - AST lowering: `ast_lowerer.rs::extract_if_in_loop_modified_vars()`
/// - Design: `docs/.../phase-39-if-phi-level2/joinir_extension_design.md`
fn convert_join_function_to_mir(
join_func: &crate::mir::join_ir::JoinFunction,
) -> Result<MirFunction, JoinIrVmBridgeError> {
// TODO(Phase 40-1): Generate loop exit PHI for if-in-loop modified variables
// See comment above for implementation details
// Integration point: After loop body lowering, before exit block finalization
// Phase 27-shortterm S-4.4: skip_ws パターン対応版
// - Call (tail call): MIR Call に変換
// - Jump (conditional exit): Branch + Return に変換