diff --git a/src/mir/builder/control_flow/joinir/mod.rs b/src/mir/builder/control_flow/joinir/mod.rs index 7f64b44c..bb56a6ac 100644 --- a/src/mir/builder/control_flow/joinir/mod.rs +++ b/src/mir/builder/control_flow/joinir/mod.rs @@ -2,6 +2,7 @@ //! //! This module contains JoinIR-related control flow logic: //! - Pattern lowerers (patterns/) -//! - Routing logic (future Phase 3) +//! - Routing logic (routing.rs) ✅ pub(in crate::mir::builder) mod patterns; +pub(in crate::mir::builder) mod routing; diff --git a/src/mir/builder/control_flow/joinir/routing.rs b/src/mir/builder/control_flow/joinir/routing.rs new file mode 100644 index 00000000..cb6f673d --- /dev/null +++ b/src/mir/builder/control_flow/joinir/routing.rs @@ -0,0 +1,343 @@ +//! JoinIR routing logic for loop lowering + +use crate::ast::ASTNode; +use crate::mir::builder::MirBuilder; +use crate::mir::ValueId; + +impl MirBuilder { + /// Phase 49: Try JoinIR Frontend for mainline integration + /// + /// Returns `Ok(Some(value))` if the current function should use JoinIR Frontend, + /// `Ok(None)` to fall through to the legacy LoopBuilder path. + /// + /// # Phase 49-4: Multi-target support + /// + /// Targets are enabled via separate dev flags: + /// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0 + /// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2 + /// + /// Note: Arity in function names does NOT include implicit `me` receiver. + /// - Instance method `print_tokens()` → `/0` (no explicit params) + /// - Static method `filter(arr, pred)` → `/2` (two params) + pub(in crate::mir::builder) fn try_cf_loop_joinir( + &mut self, + condition: &ASTNode, + body: &[ASTNode], + ) -> Result, String> { + // Get current function name + let func_name = self + .current_function + .as_ref() + .map(|f| f.signature.name.clone()) + .unwrap_or_default(); + + eprintln!("[cf_loop/joinir/router] try_cf_loop_joinir called for function: '{}'", func_name); + + // Phase 188-Impl-3: Debug logging for function name routing + if std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok() { + eprintln!("[cf_loop/joinir/router] Current function name: '{}'", func_name); + } + + // Phase 49-4 + Phase 80: Multi-target routing + // - Core ON なら代表2本(print_tokens / ArrayExt.filter)は JoinIR を優先し、失敗したら LoopBuilder へフォールバック + // - Core OFF では従来通り dev フラグで opt-in + // Note: Arity does NOT include implicit `me` receiver + // Phase 188: Add "main" routing for loop pattern expansion + let core_on = crate::config::env::joinir_core_enabled(); + let is_target = match func_name.as_str() { + "main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1) + "JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2) + "JsonTokenizer.print_tokens/0" => { + if core_on { + true + } else { + std::env::var("HAKO_JOINIR_PRINT_TOKENS_MAIN") + .ok() + .as_deref() + == Some("1") + } + } + "ArrayExtBox.filter/2" => { + if core_on { + true + } else { + std::env::var("HAKO_JOINIR_ARRAY_FILTER_MAIN") + .ok() + .as_deref() + == Some("1") + } + } + _ => false, + }; + + if !is_target { + return Ok(None); + } + + // Debug log when routing through JoinIR Frontend + let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() + || std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok(); + eprintln!("[cf_loop/joinir] DEBUG FLAG STATUS: debug={}, NYASH_LOOPFORM_DEBUG={:?}, NYASH_JOINIR_MAINLINE_DEBUG={:?}", + debug, + std::env::var("NYASH_LOOPFORM_DEBUG"), + std::env::var("NYASH_JOINIR_MAINLINE_DEBUG")); + if debug { + eprintln!( + "[cf_loop/joinir] Routing {} through JoinIR Frontend mainline", + func_name + ); + } + + // Phase 49-3: Implement JoinIR Frontend integration + self.cf_loop_joinir_impl(condition, body, &func_name, debug) + } + + /// Phase 49-3: JoinIR Frontend integration implementation + /// + /// # Pipeline + /// 1. Build Loop AST → JSON v0 format (with "defs" array) + /// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule + /// 3. convert_join_module_to_mir_with_meta() → MirModule + /// 4. Merge MIR blocks into current_function + /// + /// # Phase 49-4 Note + /// + /// JoinIR Frontend expects a complete function definition with: + /// - local variable initializations + /// - loop body + /// - return statement + /// + /// Since cf_loop only has access to the loop condition and body, + /// we construct a minimal JSON v0 wrapper with function name "simple" + /// to match the JoinIR Frontend's expected pattern. + pub(in crate::mir::builder) fn cf_loop_joinir_impl( + &mut self, + condition: &ASTNode, + body: &[ASTNode], + func_name: &str, + debug: bool, + ) -> Result, String> { + use super::super::super::loop_frontend_binding::LoopFrontendBinding; + use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap}; + use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta; + use crate::mir::types::ConstValue; + use crate::mir::MirInstruction; + use crate::r#macro::ast_json::ast_to_json; + + // Phase 188-Impl-3: Route Pattern 3 (If-Else PHI) - detect by 'sum' variable presence + // This MUST come before Pattern 1 check to avoid incorrect routing + // Pattern 3 test (loop_if_phi.hako) uses "main" with 'sum' accumulator variable + if func_name == "main" && self.variable_map.contains_key("sum") { + if debug { + eprintln!("[cf_loop/joinir] Routing 'main' (with sum var) through Pattern 3 minimal lowerer"); + } + return self.cf_loop_pattern3_with_if_phi(condition, body, func_name, debug); + } + + // Phase 188-Impl-1-F: Route Pattern 1 (Simple While) - "main" without 'sum' variable + if func_name == "main" { + if debug { + eprintln!("[cf_loop/joinir] Routing '{}' through Pattern 1 minimal lowerer", func_name); + } + return self.cf_loop_pattern1_minimal(condition, body, func_name, debug); + } + + // Phase 188-Impl-2: Route "JoinIrMin.main/0" through Pattern 2 minimal lowerer (Loop with Break) + if func_name == "JoinIrMin.main/0" { + if debug { + eprintln!("[cf_loop/joinir] Routing 'JoinIrMin.main/0' through Pattern 2 minimal lowerer"); + } + return self.cf_loop_pattern2_with_break(condition, body, func_name, debug); + } + + // Phase 50: Create appropriate binding based on function name + let binding = match func_name { + "JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(), + "ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(), + _ => { + if debug { + eprintln!( + "[cf_loop/joinir] No binding defined for {}, falling back", + func_name + ); + } + return Ok(None); + } + }; + + if debug { + eprintln!( + "[cf_loop/joinir] Using binding: counter={}, acc={:?}, pattern={:?}", + binding.counter_var, binding.accumulator_var, binding.pattern + ); + } + + // Step 1: Convert condition and body to JSON + let condition_json = ast_to_json(condition); + let mut body_json: Vec = body.iter().map(|s| ast_to_json(s)).collect(); + + // Phase 50: Rename variables in body (e.g., "out" → "acc" for filter) + binding.rename_body_variables(&mut body_json); + + // Phase 50: Generate Local declarations from binding + let (i_local, acc_local, n_local) = binding.generate_local_declarations(); + + // Phase 52/56: Build params from external_refs + // Instance methods need `me`, static methods need their parameters (arr, pred, etc.) + let mut params: Vec = Vec::new(); + + // Phase 52: Add 'me' for instance methods + if binding.needs_me_receiver() { + if debug { + eprintln!("[cf_loop/joinir] Adding 'me' to params (instance method)"); + } + params.push(serde_json::json!("me")); + } + + // Phase 56: Add external_refs as parameters (arr, pred for filter) + for ext_ref in &binding.external_refs { + // Skip "me" and "me.*" as they're handled above + if ext_ref == "me" || ext_ref.starts_with("me.") { + continue; + } + if debug { + eprintln!( + "[cf_loop/joinir] Adding '{}' to params (external_ref)", + ext_ref + ); + } + params.push(serde_json::json!(ext_ref)); + } + + // Step 2: Construct JSON v0 format with "defs" array + // The function is named "simple" to match JoinIR Frontend's pattern matching + // Phase 50: Include i/acc/n Local declarations to satisfy JoinIR Frontend expectations + let program_json = serde_json::json!({ + "defs": [ + { + "name": "simple", + "params": params, + "body": { + "type": "Block", + "body": [ + // Phase 50: Inject i/acc/n Local declarations + i_local, + acc_local, + n_local, + { + "type": "Loop", + "cond": condition_json, // JoinIR Frontend expects "cond" not "condition" + "body": body_json + }, + // Return the accumulator (or null for side-effect loops) + { + "type": "Return", + "value": { "kind": "Variable", "name": "acc" } + } + ] + } + } + ] + }); + + if debug { + eprintln!( + "[cf_loop/joinir] Generated JSON v0 for {}: {}", + func_name, + serde_json::to_string_pretty(&program_json).unwrap_or_default() + ); + } + + // Step 3: Lower to JoinIR + // Phase 49-4: Use catch_unwind for graceful fallback on unsupported patterns + // The JoinIR Frontend may panic if the loop doesn't match expected patterns + // (e.g., missing variable initializations like "i must be initialized") + let join_module = { + let json_clone = program_json.clone(); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let mut lowerer = AstToJoinIrLowerer::new(); + lowerer.lower_program_json(&json_clone) + })); + + match result { + Ok(module) => module, + Err(e) => { + // Extract panic message for debugging + let panic_msg = if let Some(s) = e.downcast_ref::<&str>() { + s.to_string() + } else if let Some(s) = e.downcast_ref::() { + s.clone() + } else { + "unknown panic".to_string() + }; + + if debug { + eprintln!( + "[cf_loop/joinir] JoinIR lowering failed for {}: {}, falling back to legacy", + func_name, panic_msg + ); + } + // Return None to fall back to legacy LoopBuilder + return Ok(None); + } + } + }; + // Phase 49-3 MVP: Use empty meta map (full if-analysis is Phase 40+ territory) + let join_meta = JoinFuncMetaMap::new(); + + if debug { + eprintln!( + "[cf_loop/joinir] JoinModule has {} functions, entry={:?}", + join_module.functions.len(), + join_module.entry + ); + } + + // Step 4: Convert JoinModule to MIR + let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta) + .map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?; + + if debug { + eprintln!( + "[cf_loop/joinir] MirModule has {} functions", + mir_module.functions.len() + ); + for (name, func) in &mir_module.functions { + eprintln!( + "[cf_loop/joinir] - {}: {} blocks, entry={:?}", + name, + func.blocks.len(), + func.entry_block + ); + // Phase 189: Debug - show block contents + for (block_id, block) in &func.blocks { + eprintln!("[cf_loop/joinir] Block {:?}: {} instructions", block_id, block.instructions.len()); + for (i, inst) in block.instructions.iter().enumerate() { + eprintln!("[cf_loop/joinir] [{}] {:?}", i, inst); + } + if let Some(ref term) = block.terminator { + eprintln!("[cf_loop/joinir] terminator: {:?}", term); + } + } + } + } + + // Step 5: Merge MIR blocks into current_function + // For Phase 49-3, we'll use a simplified approach: + // - Add generated blocks to current_function + // - Jump from current_block to the entry of generated loop + // - The loop exit becomes the new current_block + // Phase 188-Impl-3: Pass None for boundary (legacy path without boundary) + // Phase 189: Discard exit PHI result (legacy path doesn't need it) + let _ = self.merge_joinir_mir_blocks(&mir_module, None, debug)?; + + // Return void for now (loop doesn't have a meaningful return value in this context) + let void_val = self.next_value_id(); + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: ConstValue::Void, + })?; + + Ok(Some(void_val)) + } +} diff --git a/src/mir/builder/control_flow/mod.rs b/src/mir/builder/control_flow/mod.rs index c0ad5b8c..df195477 100644 --- a/src/mir/builder/control_flow/mod.rs +++ b/src/mir/builder/control_flow/mod.rs @@ -3,7 +3,7 @@ //! This module is being modularized in phases: //! - Phase 1: Debug utilities (debug.rs) ✅ //! - Phase 2: Pattern lowerers (joinir/patterns/) ✅ -//! - Phase 3: JoinIR routing (future) +//! - Phase 3: JoinIR routing (joinir/routing.rs) ✅ //! - Phase 4-19: Additional modularization (future) use super::{Effect, EffectMask, MirInstruction, ValueId}; @@ -94,327 +94,6 @@ impl super::MirBuilder { /// Note: Arity in function names does NOT include implicit `me` receiver. /// - Instance method `print_tokens()` → `/0` (no explicit params) /// - Static method `filter(arr, pred)` → `/2` (two params) - fn try_cf_loop_joinir( - &mut self, - condition: &ASTNode, - body: &[ASTNode], - ) -> Result, String> { - // Get current function name - let func_name = self - .current_function - .as_ref() - .map(|f| f.signature.name.clone()) - .unwrap_or_default(); - - eprintln!("[cf_loop/joinir/router] try_cf_loop_joinir called for function: '{}'", func_name); - - // Phase 188-Impl-3: Debug logging for function name routing - if std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok() { - eprintln!("[cf_loop/joinir/router] Current function name: '{}'", func_name); - } - - // Phase 49-4 + Phase 80: Multi-target routing - // - Core ON なら代表2本(print_tokens / ArrayExt.filter)は JoinIR を優先し、失敗したら LoopBuilder へフォールバック - // - Core OFF では従来通り dev フラグで opt-in - // Note: Arity does NOT include implicit `me` receiver - // Phase 188: Add "main" routing for loop pattern expansion - let core_on = crate::config::env::joinir_core_enabled(); - let is_target = match func_name.as_str() { - "main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1) - "JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2) - "JsonTokenizer.print_tokens/0" => { - if core_on { - true - } else { - std::env::var("HAKO_JOINIR_PRINT_TOKENS_MAIN") - .ok() - .as_deref() - == Some("1") - } - } - "ArrayExtBox.filter/2" => { - if core_on { - true - } else { - std::env::var("HAKO_JOINIR_ARRAY_FILTER_MAIN") - .ok() - .as_deref() - == Some("1") - } - } - _ => false, - }; - - if !is_target { - return Ok(None); - } - - // Debug log when routing through JoinIR Frontend - let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() - || std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok(); - eprintln!("[cf_loop/joinir] DEBUG FLAG STATUS: debug={}, NYASH_LOOPFORM_DEBUG={:?}, NYASH_JOINIR_MAINLINE_DEBUG={:?}", - debug, - std::env::var("NYASH_LOOPFORM_DEBUG"), - std::env::var("NYASH_JOINIR_MAINLINE_DEBUG")); - if debug { - eprintln!( - "[cf_loop/joinir] Routing {} through JoinIR Frontend mainline", - func_name - ); - } - - // Phase 49-3: Implement JoinIR Frontend integration - self.cf_loop_joinir_impl(condition, body, &func_name, debug) - } - - /// Phase 49-3: JoinIR Frontend integration implementation - /// - /// # Pipeline - /// 1. Build Loop AST → JSON v0 format (with "defs" array) - /// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule - /// 3. convert_join_module_to_mir_with_meta() → MirModule - /// 4. Merge MIR blocks into current_function - /// - /// # Phase 49-4 Note - /// - /// JoinIR Frontend expects a complete function definition with: - /// - local variable initializations - /// - loop body - /// - return statement - /// - /// Since cf_loop only has access to the loop condition and body, - /// we construct a minimal JSON v0 wrapper with function name "simple" - /// to match the JoinIR Frontend's expected pattern. - fn cf_loop_joinir_impl( - &mut self, - condition: &ASTNode, - body: &[ASTNode], - func_name: &str, - debug: bool, - ) -> Result, String> { - use super::loop_frontend_binding::LoopFrontendBinding; - use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap}; - use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta; - use crate::mir::types::ConstValue; - use crate::r#macro::ast_json::ast_to_json; - - // Phase 188-Impl-3: Route Pattern 3 (If-Else PHI) - detect by 'sum' variable presence - // This MUST come before Pattern 1 check to avoid incorrect routing - // Pattern 3 test (loop_if_phi.hako) uses "main" with 'sum' accumulator variable - if func_name == "main" && self.variable_map.contains_key("sum") { - if debug { - eprintln!("[cf_loop/joinir] Routing 'main' (with sum var) through Pattern 3 minimal lowerer"); - } - return self.cf_loop_pattern3_with_if_phi(condition, body, func_name, debug); - } - - // Phase 188-Impl-1-F: Route Pattern 1 (Simple While) - "main" without 'sum' variable - if func_name == "main" { - if debug { - eprintln!("[cf_loop/joinir] Routing '{}' through Pattern 1 minimal lowerer", func_name); - } - return self.cf_loop_pattern1_minimal(condition, body, func_name, debug); - } - - // Phase 188-Impl-2: Route "JoinIrMin.main/0" through Pattern 2 minimal lowerer (Loop with Break) - if func_name == "JoinIrMin.main/0" { - if debug { - eprintln!("[cf_loop/joinir] Routing 'JoinIrMin.main/0' through Pattern 2 minimal lowerer"); - } - return self.cf_loop_pattern2_with_break(condition, body, func_name, debug); - } - - // Phase 50: Create appropriate binding based on function name - let binding = match func_name { - "JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(), - "ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(), - _ => { - if debug { - eprintln!( - "[cf_loop/joinir] No binding defined for {}, falling back", - func_name - ); - } - return Ok(None); - } - }; - - if debug { - eprintln!( - "[cf_loop/joinir] Using binding: counter={}, acc={:?}, pattern={:?}", - binding.counter_var, binding.accumulator_var, binding.pattern - ); - } - - // Step 1: Convert condition and body to JSON - let condition_json = ast_to_json(condition); - let mut body_json: Vec = body.iter().map(|s| ast_to_json(s)).collect(); - - // Phase 50: Rename variables in body (e.g., "out" → "acc" for filter) - binding.rename_body_variables(&mut body_json); - - // Phase 50: Generate Local declarations from binding - let (i_local, acc_local, n_local) = binding.generate_local_declarations(); - - // Phase 52/56: Build params from external_refs - // Instance methods need `me`, static methods need their parameters (arr, pred, etc.) - let mut params: Vec = Vec::new(); - - // Phase 52: Add 'me' for instance methods - if binding.needs_me_receiver() { - if debug { - eprintln!("[cf_loop/joinir] Adding 'me' to params (instance method)"); - } - params.push(serde_json::json!("me")); - } - - // Phase 56: Add external_refs as parameters (arr, pred for filter) - for ext_ref in &binding.external_refs { - // Skip "me" and "me.*" as they're handled above - if ext_ref == "me" || ext_ref.starts_with("me.") { - continue; - } - if debug { - eprintln!( - "[cf_loop/joinir] Adding '{}' to params (external_ref)", - ext_ref - ); - } - params.push(serde_json::json!(ext_ref)); - } - - // Step 2: Construct JSON v0 format with "defs" array - // The function is named "simple" to match JoinIR Frontend's pattern matching - // Phase 50: Include i/acc/n Local declarations to satisfy JoinIR Frontend expectations - let program_json = serde_json::json!({ - "defs": [ - { - "name": "simple", - "params": params, - "body": { - "type": "Block", - "body": [ - // Phase 50: Inject i/acc/n Local declarations - i_local, - acc_local, - n_local, - { - "type": "Loop", - "cond": condition_json, // JoinIR Frontend expects "cond" not "condition" - "body": body_json - }, - // Return the accumulator (or null for side-effect loops) - { - "type": "Return", - "value": { "kind": "Variable", "name": "acc" } - } - ] - } - } - ] - }); - - if debug { - eprintln!( - "[cf_loop/joinir] Generated JSON v0 for {}: {}", - func_name, - serde_json::to_string_pretty(&program_json).unwrap_or_default() - ); - } - - // Step 3: Lower to JoinIR - // Phase 49-4: Use catch_unwind for graceful fallback on unsupported patterns - // The JoinIR Frontend may panic if the loop doesn't match expected patterns - // (e.g., missing variable initializations like "i must be initialized") - let join_module = { - let json_clone = program_json.clone(); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - let mut lowerer = AstToJoinIrLowerer::new(); - lowerer.lower_program_json(&json_clone) - })); - - match result { - Ok(module) => module, - Err(e) => { - // Extract panic message for debugging - let panic_msg = if let Some(s) = e.downcast_ref::<&str>() { - s.to_string() - } else if let Some(s) = e.downcast_ref::() { - s.clone() - } else { - "unknown panic".to_string() - }; - - if debug { - eprintln!( - "[cf_loop/joinir] JoinIR lowering failed for {}: {}, falling back to legacy", - func_name, panic_msg - ); - } - // Return None to fall back to legacy LoopBuilder - return Ok(None); - } - } - }; - // Phase 49-3 MVP: Use empty meta map (full if-analysis is Phase 40+ territory) - let join_meta = JoinFuncMetaMap::new(); - - if debug { - eprintln!( - "[cf_loop/joinir] JoinModule has {} functions, entry={:?}", - join_module.functions.len(), - join_module.entry - ); - } - - // Step 4: Convert JoinModule to MIR - let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta) - .map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?; - - if debug { - eprintln!( - "[cf_loop/joinir] MirModule has {} functions", - mir_module.functions.len() - ); - for (name, func) in &mir_module.functions { - eprintln!( - "[cf_loop/joinir] - {}: {} blocks, entry={:?}", - name, - func.blocks.len(), - func.entry_block - ); - // Phase 189: Debug - show block contents - for (block_id, block) in &func.blocks { - eprintln!("[cf_loop/joinir] Block {:?}: {} instructions", block_id, block.instructions.len()); - for (i, inst) in block.instructions.iter().enumerate() { - eprintln!("[cf_loop/joinir] [{}] {:?}", i, inst); - } - if let Some(ref term) = block.terminator { - eprintln!("[cf_loop/joinir] terminator: {:?}", term); - } - } - } - } - - // Step 5: Merge MIR blocks into current_function - // For Phase 49-3, we'll use a simplified approach: - // - Add generated blocks to current_function - // - Jump from current_block to the entry of generated loop - // - The loop exit becomes the new current_block - // Phase 188-Impl-3: Pass None for boundary (legacy path without boundary) - // Phase 189: Discard exit PHI result (legacy path doesn't need it) - let _ = self.merge_joinir_mir_blocks(&mir_module, None, debug)?; - - // Return void for now (loop doesn't have a meaningful return value in this context) - let void_val = self.next_value_id(); - self.emit_instruction(MirInstruction::Const { - dst: void_val, - value: ConstValue::Void, - })?; - - Ok(Some(void_val)) - } - /// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function ///