📦 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>
This commit is contained in:
@ -2,6 +2,24 @@ use super::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModul
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
// Lifecycle routines extracted from builder.rs
|
||||
fn has_main_static(ast: &ASTNode) -> bool {
|
||||
use crate::ast::ASTNode as N;
|
||||
if let N::Program { statements, .. } = ast {
|
||||
for st in statements {
|
||||
if let N::BoxDeclaration { name, methods, is_static, .. } = st {
|
||||
if *is_static && name == "Main" {
|
||||
if let Some(m) = methods.get("main") {
|
||||
if let N::FunctionDeclaration { .. } = m {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl super::MirBuilder {
|
||||
/// Unified declaration indexing (Phase A): collect symbols before lowering
|
||||
/// - user_defined_boxes: non-static Box names (for NewBox birth() skip)
|
||||
@ -66,6 +84,15 @@ impl super::MirBuilder {
|
||||
let snapshot = ast.clone();
|
||||
// Phase A: collect declarations in one pass (symbols available to lowering)
|
||||
self.index_declarations(&snapshot);
|
||||
|
||||
// Decide root mode (App vs Script) once per module based on presence of static box Main.main
|
||||
// true => App mode (Main.main is entry)
|
||||
// false => Script/Test mode (top-level Program runs sequentially)
|
||||
let is_app_mode = self
|
||||
.root_is_app_mode
|
||||
.unwrap_or_else(|| has_main_static(&snapshot));
|
||||
self.root_is_app_mode = Some(is_app_mode);
|
||||
|
||||
// Phase B: top-level program lowering with declaration-first pass
|
||||
match ast {
|
||||
ASTNode::Program { statements, .. } => {
|
||||
@ -87,31 +114,34 @@ impl super::MirBuilder {
|
||||
if name == "Main" {
|
||||
main_static = Some((name.clone(), methods.clone()));
|
||||
} else {
|
||||
// Dev: trace which static box is being lowered (env-gated)
|
||||
self.trace_compile(format!("lower static box {}", name));
|
||||
// 🎯 箱理論: 各static boxに専用のコンパイルコンテキストを作成
|
||||
// これにより、using文や前のboxからのメタデータ汚染を構造的に防止
|
||||
// スコープを抜けると自動的にコンテキストが破棄される
|
||||
{
|
||||
let ctx = super::context::BoxCompilationContext::new();
|
||||
self.compilation_context = Some(ctx);
|
||||
// Script/Test モードでは static box の lowering は exprs.rs 側に任せる
|
||||
if is_app_mode {
|
||||
// Dev: trace which static box is being lowered (env-gated)
|
||||
self.trace_compile(format!("lower static box {}", name));
|
||||
// 🎯 箱理論: 各static boxに専用のコンパイルコンテキストを作成
|
||||
// これにより、using文や前のboxからのメタデータ汚染を構造的に防止
|
||||
// スコープを抜けると自動的にコンテキストが破棄される
|
||||
{
|
||||
let ctx = super::context::BoxCompilationContext::new();
|
||||
self.compilation_context = Some(ctx);
|
||||
|
||||
// Lower all static methods into standalone functions: BoxName.method/Arity
|
||||
for (mname, mast) in methods.iter() {
|
||||
if let N::FunctionDeclaration { params, body, .. } = mast {
|
||||
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
|
||||
self.lower_static_method_as_function(func_name, params.clone(), body.clone())?;
|
||||
self.static_method_index
|
||||
.entry(mname.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((name.clone(), params.len()));
|
||||
// Lower all static methods into standalone functions: BoxName.method/Arity
|
||||
for (mname, mast) in methods.iter() {
|
||||
if let N::FunctionDeclaration { params, body, .. } = mast {
|
||||
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
|
||||
self.lower_static_method_as_function(func_name, params.clone(), body.clone())?;
|
||||
self.static_method_index
|
||||
.entry(mname.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((name.clone(), params.len()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🎯 箱理論: コンテキストをクリア(スコープ終了で自動破棄)
|
||||
// これにより、次のstatic boxは汚染されていない状態から開始される
|
||||
self.compilation_context = None;
|
||||
// 🎯 箱理論: コンテキストをクリア(スコープ終了で自動破棄)
|
||||
// これにより、次のstatic boxは汚染されていない状態から開始される
|
||||
self.compilation_context = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Instance box: register type and lower instance methods/ctors as functions
|
||||
@ -152,11 +182,17 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: lower Main.main body as Program (entry). If absent, fall back to sequential block.
|
||||
if let Some((box_name, methods)) = main_static {
|
||||
self.build_static_main_box(box_name, methods)
|
||||
// Second pass: mode-dependent entry lowering
|
||||
if is_app_mode {
|
||||
// App モード: Main.main をエントリとして扱う
|
||||
if let Some((box_name, methods)) = main_static {
|
||||
self.build_static_main_box(box_name, methods)
|
||||
} else {
|
||||
// 理論上は起こりにくいが、安全のため Script モードと同じフォールバックにする
|
||||
self.cf_block(statements)
|
||||
}
|
||||
} else {
|
||||
// Fallback: sequential lowering (keeps legacy behavior for scripts without Main)
|
||||
// Script/Test モード: トップレベル Program をそのまま順次実行
|
||||
self.cf_block(statements)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user