Files
hakorune/src/mir/function.rs

629 lines
19 KiB
Rust
Raw Normal View History

/*!
* MIR Function and Module - High-level MIR organization
*
* Functions contain basic blocks and SSA values, modules contain functions
*/
refactor(mir): Phase 6-1 - Add BasicBlock/MirFunction helper methods (foundation for ~50 line reduction) 【目的】 JSON v0 Bridge(if_else.rs, try_catch.rs, loop_.rs)で重複するPHI生成・terminator設定パターンを統一するための基礎ヘルパーメソッド追加 【実装内容】 1. BasicBlock::update_phi_input()メソッド追加(17行) - loop back-edge PHI更新を簡略化 - 手動ループ検索パターンを統一化 - エラーハンドリング統一 2. MirFunction::set_jump_terminator()メソッド追加(14行) - if/else/loop降下での終端設定を簡略化 - 未終端チェックを内包 - Option処理を統一 3. MirFunction::set_branch_terminator()メソッド追加(15行) - if/else条件分岐の終端設定を簡略化 - Option処理を統一 【技術的改善】 - **Single Source of Truth**: 終端設定・PHI更新ロジックが一元化 - **エラーハンドリング統一**: Result型で明示的エラー処理 - **箱化**: 関連処理を BasicBlock/MirFunction に箱化 【修正箇所】 - src/mir/basic_block.rs: - HashMap import追加 - update_phi_input()メソッド追加(17行) - src/mir/function.rs: - MirInstruction import追加 - set_jump_terminator()メソッド追加(14行) - set_branch_terminator()メソッド追加(15行) 【テスト結果】 ✅ ビルド成功(0 errors) ✅ userbox_*スモークテスト: 全6テストPASS 【次のフェーズ(Phase 6-2予定)】 これらのヘルパーメソッドを使って以下を簡略化予定: - loop_.rs: ~10行削減(update_phi_input使用) - if_else.rs: ~5行削減(set_branch_terminator使用) - try_catch.rs: ~15行削減(両メソッド使用) - 合計: ~30行削減見込み 【Phase 15目標への寄与】 - フェーズ1完了(基礎ヘルパー追加) - フェーズ2準備完了(~150行削減可能な土台確立) - 箱理論準拠: 「箱にする」「境界を作る」「戻せる」完全実現 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 15:15:21 +09:00
use super::{BasicBlock, BasicBlockId, EffectMask, MirInstruction, MirType, ValueId};
use std::collections::{HashMap, HashSet};
use std::fmt;
/// Function signature for MIR functions
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionSignature {
/// Function name
pub name: String,
/// Parameter types
pub params: Vec<MirType>,
/// Return type
pub return_type: MirType,
/// Overall effect mask for the function
pub effects: EffectMask,
}
/// A MIR function in SSA form
#[derive(Debug, Clone)]
pub struct MirFunction {
/// Function signature
pub signature: FunctionSignature,
/// Basic blocks indexed by ID
pub blocks: HashMap<BasicBlockId, BasicBlock>,
/// Entry basic block ID
pub entry_block: BasicBlockId,
/// Local variable declarations (before SSA conversion)
pub locals: Vec<MirType>,
/// Parameter value IDs
pub params: Vec<ValueId>,
/// Next available value ID
pub next_value_id: u32,
/// Function-level metadata
pub metadata: FunctionMetadata,
}
/// Metadata for MIR functions
#[derive(Debug, Clone, Default)]
pub struct FunctionMetadata {
/// Source file location
pub source_file: Option<String>,
/// Line number in source
pub line_number: Option<u32>,
/// Whether this function is an entry point
pub is_entry_point: bool,
/// Whether this function is pure (no side effects)
pub is_pure: bool,
/// Optimization hints
pub optimization_hints: Vec<String>,
/// Optional per-value type map (for builders that annotate ValueId types)
pub value_types: std::collections::HashMap<ValueId, MirType>,
}
impl MirFunction {
/// Create a new MIR function
pub fn new(signature: FunctionSignature, entry_block: BasicBlockId) -> Self {
let mut blocks = HashMap::new();
blocks.insert(entry_block, BasicBlock::new(entry_block));
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// 📦 Hotfix 1 + 4 + 8: Reserve ValueIds for function parameters at creation time
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
// Parameter ValueIds are %0, %1, ..., %N-1 where N = signature.params.len()
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
//
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// 📦 Hotfix 8: Instance method receiver detection (Fixed Hotfix 4 logic inversion)
// - Instance methods (me.method()) have implicit 'me' receiver (params[0]: Box type)
// - Static methods (StaticBox.method()) have NO receiver (params[0]: non-Box type)
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
//
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// Detection: If function name contains "." (box method) AND
// params[0] IS a Box type → instance method → needs +1 for receiver
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
let param_count = signature.params.len() as u32;
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
let has_implicit_receiver = if signature.name.contains('.') {
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// Box method (instance or static)
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
if signature.params.is_empty() {
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// No params → static method with no receiver
// (e.g., StaticBox.method() with 0 params)
false
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
} else {
// Check if first param is Box type (instance method) or not (static method)
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// ✅ FIXED: Instance method (Box type) → true, Static method (non-Box) → false
matches!(signature.params[0], MirType::Box(_))
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
}
} else {
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
// Global function → no receiver
📦 Hotfix 4: Static Method Receiver ValueId Reservation **根本原因**: Static method (e.g., `collect_entries/1`) は暗黙の 'me' receiver を持つが、signature.params に含まれていない **問題の構造**: ``` static box Stage1UsingResolverFull { collect_entries(src_unused) { // signature: /1 (1 param) me._find_from(...) // 'me' を使用! } } ``` - Signature: params.len() = 1 (src_unused のみ) - 実際の ValueIds: 2つ必要 (%0 = receiver/me, %1 = src_unused) **修正内容**: src/mir/function.rs - MirFunction::new() で static method 判定追加: 1. **判定ロジック**: - Function name に "." が含まれる → box method or static method - params[0] が Box type でない → static method (implicit receiver) - → +1 ValueId for receiver 2. **予約計算**: ```rust let receiver_count = if has_implicit_receiver { 1 } else { 0 }; let total_value_ids = param_count + receiver_count; let initial_counter = total_value_ids.max(1); ``` **Test Result**: ``` [MirFunction::new] fn='Stage1UsingResolverFull.collect_entries/1' params=1, receiver=1, total=2, initial_counter=2 ✅ [MirFunction::new] fn='Stage1UsingResolverFull._find_from/3' params=3, receiver=1, total=4, initial_counter=4 ✅ ``` **進捗**: - ✅ Hotfix 1: Parameter ValueId reservation - ✅ Hotfix 2: Exit PHI validation - ✅ Hotfix 3: NewBox ValueId fix - ✅ Hotfix 4: Static method receiver reservation - ⏸️ Remaining: bb57 %0 undefined error (別の問題、次セッションで調査) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:43 +09:00
false
};
let receiver_count = if has_implicit_receiver { 1 } else { 0 };
let total_value_ids = param_count + receiver_count;
let initial_counter = total_value_ids.max(1); // At least 1 to reserve ValueId(0) as sentinel
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
✅ Hotfix 5: Pre-populate params vector in MirFunction::new() 📦 箱理論: Parameter ValueId完全予約システム確立 ## 🎯 根本原因 Hotfix 4でnext_value_id counterは予約したが、paramsベクトルが空のまま。 setup_function_params()が新規ValueIdをインクリメント済みcounterから割り当て。 結果: シグネチャは%0だが本体は%2を使用するミスマッチ発生。 ## ✅ 修正内容 ### 1. src/mir/function.rs - MirFunction::new() ```rust // 🔥 Hotfix 5: Pre-populate params vector with reserved ValueIds let mut pre_params = Vec::new(); for i in 0..total_value_ids { pre_params.push(ValueId::new(i)); } // ... params: pre_params, // ✅ Pre-populate instead of empty Vec ``` ### 2. src/mir/builder/calls/lowering.rs - setup_function_params() ```rust // 📦 Hotfix 5: Use pre-populated params from MirFunction::new() let receiver_offset = if f.params.is_empty() { 0 } else { if f.params.len() > params.len() { 1 } else { 0 } }; for (idx, p) in params.iter().enumerate() { let param_idx = receiver_offset + idx; let pid = if param_idx < f.params.len() { f.params[param_idx] // Use pre-allocated ValueId } else { let new_pid = f.next_value_id(); f.params.push(new_pid); new_pid }; // ... } ``` ## 📊 テスト結果 - ✅ mir_parserbox_parse_program2_harness_parses_minimal_source: PASS - ✅ mir_stage1_using_resolver_full_collect_entries_verifies: PASS - ⚠️ mir_stage1_using_resolver_min_fragment_verifies: 別問題(dominator violation) ## 🎉 成果 - **Parameter ValueId問題完全解決**: 0/3 → 2/3 tests passing - **Counter予約とVector実体の完全一致**: シグネチャと本体の整合性確保 - **Static method receiver完全対応**: 暗黙receiverも正しく予約 ## 🔧 次のステップ 残り1テストのdominator violation調査(LoopForm Exit PHI生成問題) Co-Authored-By: task先生 <task@anthropic.com>
2025-11-18 07:56:47 +09:00
// 🔥 Hotfix 5: Pre-populate params vector with reserved ValueIds
// Without this, setup_function_params() will allocate NEW ValueIds starting from
// the already-incremented counter, causing signature/body mismatch.
let mut pre_params = Vec::new();
for i in 0..total_value_ids {
pre_params.push(ValueId::new(i));
}
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
feat(hotfix-8): Fix static method receiver detection logic (Hotfix 4 inversion bug) ## Issue - StageBTraceBox.log("label") passed null for the label parameter - Static methods incorrectly detected as having implicit receiver - Hotfix 4 logic was inverted: `!matches!(params[0], MirType::Box(_))` ## Root Cause src/mir/function.rs:90-101 had inverted logic: - Instance methods (params[0]: Box type) → should have receiver - Static methods (params[0]: non-Box type) → should NOT have receiver - Previous code: `!matches!()` = true for non-Box → receiver=true (WRONG) ## Fix - Changed `!matches!(signature.params[0], MirType::Box(_))` to `matches!(signature.params[0], MirType::Box(_))` - Updated comments to clarify instance vs static method detection - Result: static methods now correctly have receiver=0 ## Verification Before: fn='StageBTraceBox.log/1' params=1, receiver=1, total=2 ❌ After: fn='StageBTraceBox.log/1' params=1, receiver=0, total=1 ✅ Test output: Before: [stageb/trace] # label=null After: [stageb/trace] test_label # label passed correctly ✅ ## Files Changed - src/mir/function.rs: Fixed has_implicit_receiver logic - lang/src/compiler/entry/compiler_stageb.hako: Added guaranteed entry marker and direct print traces for Phase 25.1c debugging - CURRENT_TASK.md: Updated task progress ## Phase 25.1c Progress - Hotfix 8 完了:static method parameter passing 修正 - Stage-B トレース正常動作確認 - 次のタスク:ParserBox.parse_program2 ハング問題調査 🐾 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 05:30:31 +09:00
eprintln!(
"[MirFunction::new] fn='{}' params={}, receiver={}, total={}, initial_counter={}, pre_params={:?}",
signature.name,
param_count,
receiver_count,
total_value_ids,
initial_counter,
pre_params
);
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
}
Self {
signature,
blocks,
entry_block,
locals: Vec::new(),
✅ Hotfix 5: Pre-populate params vector in MirFunction::new() 📦 箱理論: Parameter ValueId完全予約システム確立 ## 🎯 根本原因 Hotfix 4でnext_value_id counterは予約したが、paramsベクトルが空のまま。 setup_function_params()が新規ValueIdをインクリメント済みcounterから割り当て。 結果: シグネチャは%0だが本体は%2を使用するミスマッチ発生。 ## ✅ 修正内容 ### 1. src/mir/function.rs - MirFunction::new() ```rust // 🔥 Hotfix 5: Pre-populate params vector with reserved ValueIds let mut pre_params = Vec::new(); for i in 0..total_value_ids { pre_params.push(ValueId::new(i)); } // ... params: pre_params, // ✅ Pre-populate instead of empty Vec ``` ### 2. src/mir/builder/calls/lowering.rs - setup_function_params() ```rust // 📦 Hotfix 5: Use pre-populated params from MirFunction::new() let receiver_offset = if f.params.is_empty() { 0 } else { if f.params.len() > params.len() { 1 } else { 0 } }; for (idx, p) in params.iter().enumerate() { let param_idx = receiver_offset + idx; let pid = if param_idx < f.params.len() { f.params[param_idx] // Use pre-allocated ValueId } else { let new_pid = f.next_value_id(); f.params.push(new_pid); new_pid }; // ... } ``` ## 📊 テスト結果 - ✅ mir_parserbox_parse_program2_harness_parses_minimal_source: PASS - ✅ mir_stage1_using_resolver_full_collect_entries_verifies: PASS - ⚠️ mir_stage1_using_resolver_min_fragment_verifies: 別問題(dominator violation) ## 🎉 成果 - **Parameter ValueId問題完全解決**: 0/3 → 2/3 tests passing - **Counter予約とVector実体の完全一致**: シグネチャと本体の整合性確保 - **Static method receiver完全対応**: 暗黙receiverも正しく予約 ## 🔧 次のステップ 残り1テストのdominator violation調査(LoopForm Exit PHI生成問題) Co-Authored-By: task先生 <task@anthropic.com>
2025-11-18 07:56:47 +09:00
params: pre_params, // ✅ Pre-populate instead of empty Vec
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
next_value_id: initial_counter,
metadata: FunctionMetadata::default(),
}
}
/// Get the next available ValueId
pub fn next_value_id(&mut self) -> ValueId {
let id = ValueId::new(self.next_value_id);
self.next_value_id += 1;
id
}
📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory) **箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00
/// 📦 Reserve ValueIds for function parameters (Hotfix 1: Parameter ValueId Reservation)
///
/// Call this after setting params to ensure next_value_id doesn't overlap
/// with parameter ValueIds. This prevents SSA violations where local variables
/// overwrite parameter values.
///
/// # Box-First理論
/// - 「境界をはっきりさせる」: パラメータ予約を明示的に
/// - 「いつでも戻せる」: 呼び出しタイミングを制御可能
pub fn reserve_parameter_value_ids(&mut self) {
let param_count = self.signature.params.len();
if self.next_value_id < param_count as u32 {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[MirFunction::reserve_parameter_value_ids] fn='{}' reserving {} params, adjusting counter {} -> {}",
self.signature.name, param_count, self.next_value_id, param_count);
}
self.next_value_id = param_count as u32;
}
}
/// Add a new basic block
pub fn add_block(&mut self, block: BasicBlock) -> BasicBlockId {
let id = block.id;
if self.blocks.contains_key(&id) && std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[mir-function] replacing existing block {:?}", id);
}
self.blocks.insert(id, block);
id
}
/// Get a basic block by ID
pub fn get_block(&self, id: BasicBlockId) -> Option<&BasicBlock> {
self.blocks.get(&id)
}
/// Get a mutable basic block by ID
pub fn get_block_mut(&mut self, id: BasicBlockId) -> Option<&mut BasicBlock> {
self.blocks.get_mut(&id)
}
/// Get the entry block
pub fn entry_block(&self) -> &BasicBlock {
self.blocks
.get(&self.entry_block)
.expect("Entry block must exist")
}
/// Get all basic block IDs in insertion order
pub fn block_ids(&self) -> Vec<BasicBlockId> {
let mut ids: Vec<_> = self.blocks.keys().copied().collect();
ids.sort();
ids
}
/// Get all values defined in this function
pub fn defined_values(&self) -> Vec<ValueId> {
let mut values = Vec::new();
values.extend(&self.params);
for block in self.blocks.values() {
values.extend(block.defined_values());
}
values
}
/// Verify function integrity (basic checks)
pub fn verify(&self) -> Result<(), String> {
// Check entry block exists
if !self.blocks.contains_key(&self.entry_block) {
return Err("Entry block does not exist".to_string());
}
// Check all blocks are reachable from entry
let reachable = self.compute_reachable_blocks();
for (id, _block) in &self.blocks {
if !reachable.contains(id) {
eprintln!("Warning: Block {} is unreachable", id);
}
}
// Check terminator consistency
for block in self.blocks.values() {
if !block.is_terminated() && !block.is_empty() {
return Err(format!("Block {} is not properly terminated", block.id));
}
// Check successor/predecessor consistency
for successor_id in &block.successors {
if let Some(successor) = self.blocks.get(successor_id) {
if !successor.predecessors.contains(&block.id) {
return Err(format!(
"Inconsistent CFG: {} -> {} but {} doesn't have {} as predecessor",
block.id, successor_id, successor_id, block.id
));
}
} else {
return Err(format!(
"Block {} references non-existent successor {}",
block.id, successor_id
));
}
}
}
Ok(())
}
/// Compute reachable blocks from entry
fn compute_reachable_blocks(&self) -> std::collections::HashSet<BasicBlockId> {
let mut reachable = std::collections::HashSet::new();
let mut worklist = vec![self.entry_block];
while let Some(current) = worklist.pop() {
if reachable.insert(current) {
if let Some(block) = self.blocks.get(&current) {
worklist.extend(block.successors.iter());
}
}
}
reachable
}
/// Update predecessor/successor relationships
pub fn update_cfg(&mut self) {
// Clear all predecessors
for block in self.blocks.values_mut() {
block.predecessors.clear();
}
// Rebuild predecessors from successors
let edges: Vec<(BasicBlockId, BasicBlockId)> = self
.blocks
.values()
.flat_map(|block| block.successors.iter().map(move |&succ| (block.id, succ)))
.collect();
for (pred, succ) in edges {
if let Some(successor_block) = self.blocks.get_mut(&succ) {
successor_block.add_predecessor(pred);
}
}
}
/// Mark reachable blocks
pub fn mark_reachable_blocks(&mut self) {
let reachable = self.compute_reachable_blocks();
for (id, block) in &mut self.blocks {
if reachable.contains(id) {
block.mark_reachable();
}
}
}
/// Get function statistics
pub fn stats(&self) -> FunctionStats {
let instruction_count = self
.blocks
.values()
.map(|block| block.instructions.len() + if block.terminator.is_some() { 1 } else { 0 })
.sum();
let phi_count = self
.blocks
.values()
.map(|block| block.phi_instructions().count())
.sum();
FunctionStats {
block_count: self.blocks.len(),
instruction_count,
phi_count,
value_count: self.next_value_id as usize,
is_pure: self.signature.effects.is_pure(),
}
}
refactor(mir): Phase 6-1 - Add BasicBlock/MirFunction helper methods (foundation for ~50 line reduction) 【目的】 JSON v0 Bridge(if_else.rs, try_catch.rs, loop_.rs)で重複するPHI生成・terminator設定パターンを統一するための基礎ヘルパーメソッド追加 【実装内容】 1. BasicBlock::update_phi_input()メソッド追加(17行) - loop back-edge PHI更新を簡略化 - 手動ループ検索パターンを統一化 - エラーハンドリング統一 2. MirFunction::set_jump_terminator()メソッド追加(14行) - if/else/loop降下での終端設定を簡略化 - 未終端チェックを内包 - Option処理を統一 3. MirFunction::set_branch_terminator()メソッド追加(15行) - if/else条件分岐の終端設定を簡略化 - Option処理を統一 【技術的改善】 - **Single Source of Truth**: 終端設定・PHI更新ロジックが一元化 - **エラーハンドリング統一**: Result型で明示的エラー処理 - **箱化**: 関連処理を BasicBlock/MirFunction に箱化 【修正箇所】 - src/mir/basic_block.rs: - HashMap import追加 - update_phi_input()メソッド追加(17行) - src/mir/function.rs: - MirInstruction import追加 - set_jump_terminator()メソッド追加(14行) - set_branch_terminator()メソッド追加(15行) 【テスト結果】 ✅ ビルド成功(0 errors) ✅ userbox_*スモークテスト: 全6テストPASS 【次のフェーズ(Phase 6-2予定)】 これらのヘルパーメソッドを使って以下を簡略化予定: - loop_.rs: ~10行削減(update_phi_input使用) - if_else.rs: ~5行削減(set_branch_terminator使用) - try_catch.rs: ~15行削減(両メソッド使用) - 合計: ~30行削減見込み 【Phase 15目標への寄与】 - フェーズ1完了(基礎ヘルパー追加) - フェーズ2準備完了(~150行削減可能な土台確立) - 箱理論準拠: 「箱にする」「境界を作る」「戻せる」完全実現 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 15:15:21 +09:00
/// Set jump terminator if block is not already terminated
/// Helper for JSON v0 Bridge and loop lowering
pub fn set_jump_terminator(
&mut self,
bb_id: BasicBlockId,
target: BasicBlockId,
) -> Result<(), String> {
if let Some(bb) = self.get_block_mut(bb_id) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump { target });
}
Ok(())
} else {
Err(format!("Block {:?} not found", bb_id))
}
}
/// Set branch terminator
/// Helper for JSON v0 Bridge if/else lowering
pub fn set_branch_terminator(
&mut self,
bb_id: BasicBlockId,
condition: ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<(), String> {
if let Some(bb) = self.get_block_mut(bb_id) {
bb.set_terminator(MirInstruction::Branch {
condition,
then_bb,
else_bb,
});
Ok(())
} else {
Err(format!("Block {:?} not found", bb_id))
}
}
}
/// Function statistics for profiling and optimization
#[derive(Debug, Clone)]
pub struct FunctionStats {
pub block_count: usize,
pub instruction_count: usize,
pub phi_count: usize,
pub value_count: usize,
pub is_pure: bool,
}
/// A MIR module containing multiple functions
#[derive(Debug, Clone)]
pub struct MirModule {
/// Module name
pub name: String,
/// Functions in this module
pub functions: HashMap<String, MirFunction>,
/// Global constants/statics
pub globals: HashMap<String, super::ConstValue>,
/// Module metadata
pub metadata: ModuleMetadata,
}
/// Metadata for MIR modules
#[derive(Debug, Clone, Default)]
pub struct ModuleMetadata {
/// Source file this module was compiled from
pub source_file: Option<String>,
/// Compilation timestamp
pub compiled_at: Option<String>,
/// Compiler version
pub compiler_version: Option<String>,
/// Optimization level used
pub optimization_level: u32,
/// Dev idempotence markers for passes (optional; default empty)
/// Key format suggestion: "pass_name:function_name"
pub dev_processed_markers: HashSet<String>,
}
impl MirModule {
/// Create a new MIR module
pub fn new(name: String) -> Self {
Self {
name,
functions: HashMap::new(),
globals: HashMap::new(),
metadata: ModuleMetadata::default(),
}
}
/// Add a function to the module
pub fn add_function(&mut self, function: MirFunction) {
let name = function.signature.name.clone();
self.functions.insert(name, function);
}
/// Get a function by name
pub fn get_function(&self, name: &str) -> Option<&MirFunction> {
self.functions.get(name)
}
/// Get a mutable function by name
pub fn get_function_mut(&mut self, name: &str) -> Option<&mut MirFunction> {
self.functions.get_mut(name)
}
/// Get all function names
pub fn function_names(&self) -> Vec<&String> {
self.functions.keys().collect()
}
/// Add a global constant
pub fn add_global(&mut self, name: String, value: super::ConstValue) {
self.globals.insert(name, value);
}
/// Verify entire module
pub fn verify(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
for (name, function) in &self.functions {
if let Err(e) = function.verify() {
errors.push(format!("Function '{}': {}", name, e));
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
/// Get module statistics
pub fn stats(&self) -> ModuleStats {
let function_stats: Vec<_> = self.functions.values().map(|f| f.stats()).collect();
ModuleStats {
function_count: self.functions.len(),
global_count: self.globals.len(),
total_blocks: function_stats.iter().map(|s| s.block_count).sum(),
total_instructions: function_stats.iter().map(|s| s.instruction_count).sum(),
total_values: function_stats.iter().map(|s| s.value_count).sum(),
pure_functions: function_stats.iter().filter(|s| s.is_pure).count(),
}
}
}
/// Module statistics
#[derive(Debug, Clone)]
pub struct ModuleStats {
pub function_count: usize,
pub global_count: usize,
pub total_blocks: usize,
pub total_instructions: usize,
pub total_values: usize,
pub pure_functions: usize,
}
impl fmt::Display for MirFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"function {}({}) -> {} {{",
self.signature.name,
self.signature
.params
.iter()
.enumerate()
.map(|(i, ty)| format!("%{}: {:?}", i, ty))
.collect::<Vec<_>>()
.join(", "),
format!("{:?}", self.signature.return_type)
)?;
// Show effects if not pure
if !self.signature.effects.is_pure() {
writeln!(f, " ; effects: {}", self.signature.effects)?;
}
// Show blocks in order
let mut block_ids: Vec<_> = self.blocks.keys().copied().collect();
block_ids.sort();
for block_id in block_ids {
if let Some(block) = self.blocks.get(&block_id) {
write!(f, "{}", block)?;
}
}
writeln!(f, "}}")?;
Ok(())
}
}
impl fmt::Display for MirModule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "module {} {{", self.name)?;
// Show globals
if !self.globals.is_empty() {
writeln!(f, " ; globals:")?;
for (name, value) in &self.globals {
writeln!(f, " global {} = {}", name, value)?;
}
writeln!(f)?;
}
// Show functions
for function in self.functions.values() {
writeln!(f, "{}", function)?;
}
writeln!(f, "}}")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::{EffectMask, MirType};
#[test]
fn test_function_creation() {
let signature = FunctionSignature {
name: "test_func".to_string(),
params: vec![MirType::Integer, MirType::Float],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let entry_block = BasicBlockId::new(0);
let function = MirFunction::new(signature.clone(), entry_block);
assert_eq!(function.signature.name, "test_func");
assert_eq!(function.entry_block, entry_block);
assert!(function.blocks.contains_key(&entry_block));
}
#[test]
fn test_module_creation() {
let mut module = MirModule::new("test_module".to_string());
let signature = FunctionSignature {
name: "main".to_string(),
params: vec![],
return_type: MirType::Void,
effects: EffectMask::PURE,
};
let function = MirFunction::new(signature, BasicBlockId::new(0));
module.add_function(function);
assert_eq!(module.name, "test_module");
assert!(module.get_function("main").is_some());
assert_eq!(module.function_names().len(), 1);
}
#[test]
fn test_value_id_generation() {
let signature = FunctionSignature {
name: "test".to_string(),
params: vec![],
return_type: MirType::Void,
effects: EffectMask::PURE,
};
let mut function = MirFunction::new(signature, BasicBlockId::new(0));
let val1 = function.next_value_id();
let val2 = function.next_value_id();
let val3 = function.next_value_id();
assert_eq!(val1, ValueId::new(0));
assert_eq!(val2, ValueId::new(1));
assert_eq!(val3, ValueId::new(2));
}
#[test]
fn test_function_stats() {
let signature = FunctionSignature {
name: "test".to_string(),
params: vec![],
return_type: MirType::Void,
effects: EffectMask::PURE,
};
let function = MirFunction::new(signature, BasicBlockId::new(0));
let stats = function.stats();
assert_eq!(stats.block_count, 1);
assert_eq!(stats.instruction_count, 0);
assert_eq!(stats.value_count, 0);
assert!(stats.is_pure);
}
}