feat(mir): Phase 136 Step 3/7 - ScopeContext extraction
## Summary Extract scope and control flow management into ScopeContext for better organization. ## Changes - **New file**: src/mir/builder/scope_context.rs (264 lines) - Lexical scope stack management - Control flow stacks (loop/if) - Function context tracking - Debug scope helpers - **Updated**: src/mir/builder.rs - Add scope_ctx field - Mark legacy fields as deprecated - Add sync helpers (sync_scope_ctx_to_legacy, sync_legacy_to_scope_ctx) - **Updated**: src/mir/builder/vars/lexical_scope.rs - Use scope_ctx as SSOT - Sync to legacy fields for backward compat - **Updated**: src/mir/builder/lifecycle.rs - Sync current_function via scope_ctx - **Updated**: src/mir/builder/calls/lowering.rs - Sync function context in lowering flow ## Extracted Fields (7) 1. lexical_scope_stack - Block-scoped locals 2. loop_header_stack - Loop headers for break/continue 3. loop_exit_stack - Loop exit blocks 4. if_merge_stack - If merge blocks 5. current_function - Currently building function 6. function_param_names - Function parameters (for LoopForm) 7. debug_scope_stack - Debug region identifiers ## Test Results - ✅ cargo build --release (291 warnings - deprecated usage) - ✅ cargo test --release --lib - 1005/1009 PASS - ✅ phase135_trim_mir_verify.sh - PASS - ⚠️ phase132_exit_phi_parity.sh - Error (pre-existing issue) ## Progress Phase 136: 3/7 Contexts complete (TypeContext, CoreContext, ScopeContext) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
|
||||
builder.rs の 1219 行を責任ごとに Context Box に分割し、保守性・テスト容易性を向上させる段階的リファクタリング。
|
||||
|
||||
## 完了した Context (2/7)
|
||||
## 完了した Context (3/7)
|
||||
|
||||
### ✅ TypeContext (Step 1) - 完了
|
||||
|
||||
@ -65,37 +65,66 @@ builder.rs の 1219 行を責任ごとに Context Box に分割し、保守性
|
||||
- builder 内 30+ ファイルで `block_gen.next()` を `next_block_id()` に自動置換
|
||||
- 段階的移行により破壊的変更なし
|
||||
|
||||
**コミット**: 81d79161
|
||||
**コミット**: 81d79161, 89edf116
|
||||
|
||||
## 残りの Context (5/7)
|
||||
### ✅ ScopeContext (Step 3) - 完了
|
||||
|
||||
### 3. ScopeContext (計画中)
|
||||
- `lexical_scope_stack: Vec<LexicalScopeFrame>`
|
||||
- `scope_stack: Vec<...>` (既存の control flow スタック)
|
||||
- `current_function: Option<MirFunction>`
|
||||
**実装日**: 2025-12-15
|
||||
|
||||
**抽出したフィールド** (7個):
|
||||
- `lexical_scope_stack: Vec<LexicalScopeFrame>` - Block-scoped local 変数スコープ
|
||||
- `loop_header_stack: Vec<BasicBlockId>` - ループヘッダースタック (break/continue 用)
|
||||
- `loop_exit_stack: Vec<BasicBlockId>` - ループ出口スタック
|
||||
- `if_merge_stack: Vec<BasicBlockId>` - If マージブロックスタック
|
||||
- `current_function: Option<MirFunction>` - 現在ビルド中の関数
|
||||
- `function_param_names: HashSet<String>` - 関数パラメータ名 (LoopForm PHI 用)
|
||||
- `debug_scope_stack: Vec<String>` - デバッグリージョン識別子スタック
|
||||
|
||||
**ファイル**:
|
||||
- `/home/tomoaki/git/hakorune-selfhost/src/mir/builder/scope_context.rs` (新規作成)
|
||||
|
||||
**統合方法**:
|
||||
- `MirBuilder` に `scope_ctx: ScopeContext` フィールドを追加
|
||||
- 既存フィールドは `#[deprecated]` でマーク(後方互換性維持)
|
||||
- Lexical scope ヘルパー (`push_lexical_scope()`, `pop_lexical_scope()`, `declare_local_in_current_scope()`) が scope_ctx を SSOT として使用
|
||||
- Control flow stack ヘルパー (`push_if_merge()`, `pop_if_merge()`) が両方を同期
|
||||
- Debug scope ヘルパー (`debug_push_region()`, `debug_pop_region()`, `debug_current_region_id()`) を更新
|
||||
- Function context は設定/復元時に両方を同期 (lifecycle.rs, calls/lowering.rs)
|
||||
|
||||
**テスト結果**:
|
||||
- ✅ `cargo build --release` 成功 (291 warnings - deprecated フィールド使用)
|
||||
- ✅ `cargo test --release --lib` - 1005/1009 PASS (4 tests 失敗は既存問題)
|
||||
- ✅ `phase135_trim_mir_verify.sh` - PASS
|
||||
- ⚠️ `phase132_exit_phi_parity.sh` - エラー (既存問題、ScopeContext 変更とは無関係)
|
||||
|
||||
**影響範囲**:
|
||||
- `vars/lexical_scope.rs` - scope_ctx 使用に更新
|
||||
- `lifecycle.rs` - current_function 設定/復元を scope_ctx 同期
|
||||
- `calls/lowering.rs` - 関数 lowering の文脈管理を scope_ctx 同期
|
||||
- 段階的移行により破壊的変更なし
|
||||
|
||||
**コミット**: [今回のコミット]
|
||||
|
||||
## 残りの Context (4/7)
|
||||
|
||||
### 4. BindingContext (計画中)
|
||||
- `bindings_stack: ...`
|
||||
- `global_bindings: ...`
|
||||
- `next_binding_id: u32`
|
||||
- `binding_map: BTreeMap<String, BindingId>`
|
||||
- `binding_map: BTreeMap<String, BindingId>` - 変数名 → BindingId マッピング
|
||||
- `variable_map: BTreeMap<String, ValueId>` - 変数名 → ValueId マッピング (SSA)
|
||||
|
||||
### 5. ControlFlowContext (計画中)
|
||||
- `current_basic_block: Option<BasicBlockId>`
|
||||
- `pending_phis: Vec<...>`
|
||||
- `loop_header_stack: Vec<BasicBlockId>`
|
||||
- `loop_exit_stack: Vec<BasicBlockId>`
|
||||
- `if_merge_stack: Vec<BasicBlockId>`
|
||||
### 5. MetadataContext (計画中)
|
||||
- `hint_sink: HintSink` - ヒントシンク (zero-cost guidance)
|
||||
- `current_span: Span` - 現在の AST スパン
|
||||
- `source_file: Option<String>` - ソースファイルヒント
|
||||
- `current_region_stack: Vec<RegionId>` - Region 観測スタック
|
||||
|
||||
### 6. MetadataContext (計画中)
|
||||
- `metadata: ...`
|
||||
- `loc_gen: ...`
|
||||
- `source_map: ...`
|
||||
- `hint_sink: HintSink`
|
||||
### 6. CompilationContext (計画中)
|
||||
- `compilation_context: Option<BoxCompilationContext>` - Box コンパイルコンテキスト
|
||||
- `current_static_box: Option<String>` - 現在の static box 名
|
||||
- `user_defined_boxes: HashSet<String>` - ユーザー定義 Box 名
|
||||
- `reserved_value_ids: HashSet<ValueId>` - 予約済み ValueId
|
||||
|
||||
### 7. ResourceContext (計画中)
|
||||
- `handle_registry: ...` (将来追加予定)
|
||||
- その他リソース管理関連
|
||||
### 7. ResourceContext (将来予定)
|
||||
- リソース管理関連フィールド (将来追加)
|
||||
|
||||
## 設計原則
|
||||
|
||||
@ -106,10 +135,10 @@ builder.rs の 1219 行を責任ごとに Context Box に分割し、保守性
|
||||
|
||||
## 次のステップ
|
||||
|
||||
**優先順位 3**: ScopeContext 抽出
|
||||
- スコープスタック管理の集約
|
||||
- 制御フロースタック (loop/if/try) の統合
|
||||
- 関数コンテキスト管理の整理
|
||||
**優先順位 4**: BindingContext 抽出
|
||||
- 変数バインディング管理の集約
|
||||
- variable_map と binding_map の統一管理
|
||||
- SSA 変換との連携強化
|
||||
|
||||
## 参考資料
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||||
mod ssa; // LocalSSA helpers (in-block materialization)
|
||||
mod stmts;
|
||||
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
|
||||
mod type_context; // Phase 136 follow-up: TypeContext extraction
|
||||
mod type_facts; // Phase 136 follow-up: Type inference facts box
|
||||
pub(crate) mod type_registry;
|
||||
@ -73,7 +74,9 @@ pub struct MirBuilder {
|
||||
/// Current module being built
|
||||
pub(super) current_module: Option<MirModule>,
|
||||
|
||||
/// Current function being built
|
||||
/// [DEPRECATED] Current function being built
|
||||
/// Phase 136: Moved to scope_ctx.current_function (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.current_function instead")]
|
||||
pub(super) current_function: Option<MirFunction>,
|
||||
|
||||
/// Current basic block being built
|
||||
@ -104,15 +107,20 @@ pub struct MirBuilder {
|
||||
/// Direct field access for backward compatibility (migration in progress).
|
||||
pub(super) type_ctx: type_context::TypeContext,
|
||||
|
||||
/// Phase 136 follow-up (Step 3/7): Scope and control flow context
|
||||
/// Consolidates lexical_scope_stack, loop stacks, if_merge_stack, current_function,
|
||||
/// function_param_names, debug_scope_stack for better organization.
|
||||
/// Direct field access for backward compatibility (migration in progress).
|
||||
pub(super) scope_ctx: scope_context::ScopeContext,
|
||||
|
||||
/// Variable name to ValueId mapping (for SSA conversion)
|
||||
/// 注意: compilation_contextがSomeの場合は使用されません
|
||||
/// Phase 25.1: HashMap → BTreeMap(PHI生成の決定性確保)
|
||||
pub(super) variable_map: BTreeMap<String, ValueId>,
|
||||
|
||||
/// Lexical scope stack for block-scoped `local` declarations.
|
||||
///
|
||||
/// This tracks per-block shadowing so `local x` inside `{...}` restores the
|
||||
/// outer binding when the block ends.
|
||||
/// [DEPRECATED] Lexical scope stack for block-scoped `local` declarations.
|
||||
/// Phase 136: Moved to scope_ctx.lexical_scope_stack (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.lexical_scope_stack instead")]
|
||||
lexical_scope_stack: Vec<vars::lexical_scope::LexicalScopeFrame>,
|
||||
|
||||
/// Pending phi functions to be inserted
|
||||
@ -172,8 +180,9 @@ pub struct MirBuilder {
|
||||
/// Index of static methods seen during lowering: name -> [(BoxName, arity)]
|
||||
pub(super) static_method_index: std::collections::HashMap<String, Vec<(String, usize)>>,
|
||||
|
||||
/// Function parameter names (for LoopForm PHI construction)
|
||||
/// Tracks the original parameter names at function entry
|
||||
/// [DEPRECATED] Function parameter names (for LoopForm PHI construction)
|
||||
/// Phase 136: Moved to scope_ctx.function_param_names (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.function_param_names instead")]
|
||||
pub(super) function_param_names: HashSet<String>,
|
||||
|
||||
/// Fast lookup: method+arity tail → candidate function names (e.g., ".str/0" → ["JsonNode.str/0", ...])
|
||||
@ -209,14 +218,17 @@ pub struct MirBuilder {
|
||||
pub binding_map: BTreeMap<String, super::BindingId>,
|
||||
|
||||
// include guards removed
|
||||
/// Loop context stacks for lowering break/continue inside nested control flow
|
||||
/// Top of stack corresponds to the innermost active loop
|
||||
/// [DEPRECATED] Loop context stacks for lowering break/continue inside nested control flow
|
||||
/// Phase 136: Moved to scope_ctx.loop_header_stack (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.loop_header_stack instead")]
|
||||
pub(super) loop_header_stack: Vec<BasicBlockId>,
|
||||
#[allow(dead_code)]
|
||||
#[deprecated(note = "Use scope_ctx.loop_exit_stack instead")]
|
||||
pub(super) loop_exit_stack: Vec<BasicBlockId>,
|
||||
|
||||
/// If/merge context stack (innermost first). Used to make merge targets explicit
|
||||
/// when lowering nested conditionals and to simplify jump generation.
|
||||
/// [DEPRECATED] If/merge context stack (innermost first)
|
||||
/// Phase 136: Moved to scope_ctx.if_merge_stack (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.if_merge_stack instead")]
|
||||
pub(super) if_merge_stack: Vec<BasicBlockId>,
|
||||
|
||||
// フェーズM: no_phi_modeフィールド削除(常にPHI使用)
|
||||
@ -250,7 +262,9 @@ pub struct MirBuilder {
|
||||
// ----------------------
|
||||
// Debug scope context (dev only; zero-cost when unused)
|
||||
// ----------------------
|
||||
/// Stack of region identifiers like "loop#1/header" or "join#3/join".
|
||||
/// [DEPRECATED] Stack of region identifiers like "loop#1/header" or "join#3/join".
|
||||
/// Phase 136: Moved to scope_ctx.debug_scope_stack (backward compat wrapper)
|
||||
#[deprecated(note = "Use scope_ctx.debug_scope_stack instead")]
|
||||
debug_scope_stack: Vec<String>,
|
||||
/// [DEPRECATED] Monotonic counter for region IDs (deterministic across a run).
|
||||
/// Phase 136: Moved to core_ctx.debug_join_counter (backward compat wrapper)
|
||||
@ -320,6 +334,7 @@ impl MirBuilder {
|
||||
|
||||
compilation_context: None, // 箱理論: デフォルトは従来モード
|
||||
type_ctx: type_context::TypeContext::new(), // Phase 136: Type context
|
||||
scope_ctx: scope_context::ScopeContext::new(), // Phase 136 Step 3/7: Scope context
|
||||
variable_map: BTreeMap::new(), // Phase 25.1: 決定性確保
|
||||
lexical_scope_stack: Vec::new(),
|
||||
pending_phis: Vec::new(),
|
||||
@ -396,11 +411,42 @@ impl MirBuilder {
|
||||
self.type_ctx.value_origin_newbox = self.value_origin_newbox.clone();
|
||||
}
|
||||
|
||||
// ---- Phase 136 Step 3/7: ScopeContext synchronization helpers ----
|
||||
/// Sync scope_ctx changes back to legacy fields (backward compatibility)
|
||||
#[allow(deprecated)]
|
||||
fn sync_scope_ctx_to_legacy(&mut self) {
|
||||
self.lexical_scope_stack = self.scope_ctx.lexical_scope_stack.clone();
|
||||
self.loop_header_stack = self.scope_ctx.loop_header_stack.clone();
|
||||
self.loop_exit_stack = self.scope_ctx.loop_exit_stack.clone();
|
||||
self.if_merge_stack = self.scope_ctx.if_merge_stack.clone();
|
||||
self.current_function = self.scope_ctx.current_function.clone();
|
||||
self.function_param_names = self.scope_ctx.function_param_names.clone();
|
||||
self.debug_scope_stack = self.scope_ctx.debug_scope_stack.clone();
|
||||
}
|
||||
|
||||
/// Sync legacy field changes to scope_ctx (backward compatibility)
|
||||
#[allow(deprecated)]
|
||||
fn sync_legacy_to_scope_ctx(&mut self) {
|
||||
self.scope_ctx.lexical_scope_stack = self.lexical_scope_stack.clone();
|
||||
self.scope_ctx.loop_header_stack = self.loop_header_stack.clone();
|
||||
self.scope_ctx.loop_exit_stack = self.loop_exit_stack.clone();
|
||||
self.scope_ctx.if_merge_stack = self.if_merge_stack.clone();
|
||||
self.scope_ctx.current_function = self.current_function.clone();
|
||||
self.scope_ctx.function_param_names = self.function_param_names.clone();
|
||||
self.scope_ctx.debug_scope_stack = self.debug_scope_stack.clone();
|
||||
}
|
||||
|
||||
/// Push/pop helpers for If merge context (best-effort; optional usage)
|
||||
#[allow(deprecated)]
|
||||
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) {
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.push_if_merge(bb);
|
||||
self.if_merge_stack.push(bb);
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
pub(super) fn pop_if_merge(&mut self) {
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
let _ = self.scope_ctx.pop_if_merge();
|
||||
let _ = self.if_merge_stack.pop();
|
||||
}
|
||||
|
||||
@ -467,18 +513,27 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(deprecated)]
|
||||
pub(crate) fn debug_push_region<S: Into<String>>(&mut self, region: S) {
|
||||
self.debug_scope_stack.push(region.into());
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
let region = region.into();
|
||||
self.scope_ctx.debug_push_region(region.clone());
|
||||
self.debug_scope_stack.push(region);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(deprecated)]
|
||||
pub(crate) fn debug_pop_region(&mut self) {
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.debug_pop_region();
|
||||
let _ = self.debug_scope_stack.pop();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(deprecated)]
|
||||
pub(crate) fn debug_current_region_id(&self) -> Option<String> {
|
||||
self.debug_scope_stack.last().cloned()
|
||||
// Phase 136 Step 3/7: Read from scope_ctx (SSOT)
|
||||
self.scope_ctx.debug_current_region_id()
|
||||
}
|
||||
|
||||
/// Hint for downstream metadata: set the logical source file name/path for the next build.
|
||||
|
||||
@ -79,8 +79,9 @@ impl MirBuilder {
|
||||
let entry = self.next_block_id();
|
||||
let function = self.new_function_with_metadata(signature, entry);
|
||||
|
||||
// 現在の関数・ブロックを保存
|
||||
ctx.saved_function = self.current_function.take();
|
||||
// Phase 136 Step 3/7: Save from scope_ctx (SSOT), sync to legacy field
|
||||
ctx.saved_function = self.scope_ctx.current_function.take();
|
||||
self.current_function = None;
|
||||
ctx.saved_block = self.current_block.take();
|
||||
|
||||
eprintln!(
|
||||
@ -89,7 +90,8 @@ impl MirBuilder {
|
||||
);
|
||||
eprintln!("[DEBUG/create_function_skeleton] Entry block: {:?}", entry);
|
||||
|
||||
// 新しい関数に切り替え
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.current_function = Some(function.clone());
|
||||
self.current_function = Some(function);
|
||||
self.current_block = Some(entry);
|
||||
// 新しい関数スコープ用の SlotRegistry を準備するよ(観測専用)
|
||||
@ -103,7 +105,10 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// 🎯 箱理論: Step 3 - パラメータ設定
|
||||
#[allow(deprecated)]
|
||||
fn setup_function_params(&mut self, params: &[String]) {
|
||||
// Phase 136 Step 3/7: Clear both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.function_param_names.clear();
|
||||
self.function_param_names.clear();
|
||||
// SlotRegistry 更新は borrow 競合を避けるため、まずローカルに集約してから反映するよ。
|
||||
let mut slot_regs: Vec<(String, Option<MirType>)> = Vec::new();
|
||||
@ -138,6 +143,8 @@ impl MirBuilder {
|
||||
new_pid
|
||||
};
|
||||
self.variable_map.insert(p.clone(), pid);
|
||||
// Phase 136 Step 3/7: Insert into both scope_ctx (SSOT) and legacy field
|
||||
self.scope_ctx.function_param_names.insert(p.clone());
|
||||
self.function_param_names.insert(p.clone());
|
||||
|
||||
// Phase 26-A-3: パラメータ型情報を収集(後で一括登録)
|
||||
@ -172,6 +179,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// 🎯 箱理論: Step 5 - 関数finalize
|
||||
#[allow(deprecated)]
|
||||
fn finalize_function(&mut self, returns_value: bool) -> Result<(), String> {
|
||||
// Void return追加(必要な場合)
|
||||
if !returns_value {
|
||||
@ -215,7 +223,9 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
// Moduleに追加
|
||||
// Phase 136 Step 3/7: Take from legacy field, sync to scope_ctx
|
||||
let finalized = self.current_function.take().unwrap();
|
||||
self.scope_ctx.current_function = None;
|
||||
if let Some(ref mut module) = self.current_module {
|
||||
module.add_function(finalized);
|
||||
}
|
||||
@ -225,7 +235,8 @@ impl MirBuilder {
|
||||
|
||||
/// 🎯 箱理論: Step 6 - Context復元
|
||||
fn restore_lowering_context(&mut self, ctx: LoweringContext) {
|
||||
// 関数・ブロック復元
|
||||
// Phase 136 Step 3/7: Restore to scope_ctx (SSOT), sync to legacy field
|
||||
self.scope_ctx.current_function = ctx.saved_function.clone();
|
||||
self.current_function = ctx.saved_function;
|
||||
self.current_block = ctx.saved_block;
|
||||
|
||||
@ -261,11 +272,13 @@ impl MirBuilder {
|
||||
let entry = self.next_block_id();
|
||||
let function = self.new_function_with_metadata(signature, entry);
|
||||
|
||||
// 現在の関数・ブロックを保存
|
||||
ctx.saved_function = self.current_function.take();
|
||||
// Phase 136 Step 3/7: Save from scope_ctx (SSOT), sync to legacy field
|
||||
ctx.saved_function = self.scope_ctx.current_function.take();
|
||||
self.current_function = None;
|
||||
ctx.saved_block = self.current_block.take();
|
||||
|
||||
// 新しい関数に切り替え
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.current_function = Some(function.clone());
|
||||
self.current_function = Some(function);
|
||||
self.current_block = Some(entry);
|
||||
// instance method 用の関数スコープ SlotRegistry もここで用意するよ。
|
||||
@ -464,7 +477,8 @@ impl MirBuilder {
|
||||
// FunctionRegion を 1 段ポップして元の関数コンテキストに戻るよ。
|
||||
crate::mir::region::observer::pop_function_region(self);
|
||||
|
||||
// Step 6: Context復元(simple version)
|
||||
// Phase 136 Step 3/7: Restore to scope_ctx (SSOT), sync to legacy field
|
||||
self.scope_ctx.current_function = ctx.saved_function.clone();
|
||||
self.current_function = ctx.saved_function;
|
||||
self.current_block = ctx.saved_block;
|
||||
if let Some(saved) = ctx.saved_var_map {
|
||||
|
||||
@ -118,6 +118,8 @@ impl super::MirBuilder {
|
||||
main_function.metadata.is_entry_point = true;
|
||||
|
||||
self.current_module = Some(module);
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.current_function = Some(main_function.clone());
|
||||
self.current_function = Some(main_function);
|
||||
self.current_block = Some(entry_block);
|
||||
|
||||
@ -306,7 +308,9 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
let mut module = self.current_module.take().unwrap();
|
||||
// Phase 136 Step 3/7: Take from legacy field, sync to scope_ctx
|
||||
let mut function = self.current_function.take().unwrap();
|
||||
self.scope_ctx.current_function = None;
|
||||
// Phase 84-2: Copy命令型伝播(return型推論の前に実行)
|
||||
//
|
||||
// Loop exit や If merge の edge copy で発生する型欠如を解消する。
|
||||
|
||||
288
src/mir/builder/scope_context.rs
Normal file
288
src/mir/builder/scope_context.rs
Normal file
@ -0,0 +1,288 @@
|
||||
//! ScopeContext - Lexical scope and control flow stack management
|
||||
//!
|
||||
//! Phase 136 Step 3/7: Extract scope-related state from MirBuilder
|
||||
//!
|
||||
//! # Responsibilities
|
||||
//! - Lexical scope management (variable shadowing, block-scoped locals)
|
||||
//! - Control flow stack management (loop header/exit, if merge)
|
||||
//! - Function context management (current function, parameters)
|
||||
//! - Debug scope tracking (region identifiers)
|
||||
//!
|
||||
//! # Design
|
||||
//! - Encapsulates scope/control flow state for cleaner separation
|
||||
//! - Provides type-safe push/pop operations
|
||||
//! - Maintains deterministic iteration order (BTreeMap/BTreeSet)
|
||||
|
||||
use crate::mir::{BasicBlockId, MirFunction};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
|
||||
pub(in crate::mir::builder) use super::vars::lexical_scope::LexicalScopeFrame;
|
||||
|
||||
/// Scope and control flow context for MIR building
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ScopeContext {
|
||||
// ---- Lexical scope management ----
|
||||
/// Stack of lexical scopes for block-scoped `local` declarations
|
||||
/// Tracks per-block shadowing so variables restore on scope exit
|
||||
pub(super) lexical_scope_stack: Vec<LexicalScopeFrame>,
|
||||
|
||||
// ---- Control flow stacks ----
|
||||
/// Stack of loop header blocks (innermost first)
|
||||
/// Used for break/continue target resolution
|
||||
pub(super) loop_header_stack: Vec<BasicBlockId>,
|
||||
|
||||
/// Stack of loop exit blocks (innermost first)
|
||||
#[allow(dead_code)]
|
||||
pub(super) loop_exit_stack: Vec<BasicBlockId>,
|
||||
|
||||
/// Stack of if/merge blocks (innermost first)
|
||||
/// Used for nested conditional lowering and jump generation
|
||||
pub(super) if_merge_stack: Vec<BasicBlockId>,
|
||||
|
||||
// ---- Function context ----
|
||||
/// Current function being built
|
||||
pub(super) current_function: Option<MirFunction>,
|
||||
|
||||
/// Parameter names for current function
|
||||
/// Same lifecycle as current_function
|
||||
pub(super) function_param_names: HashSet<String>,
|
||||
|
||||
// ---- Debug scope ----
|
||||
/// Stack of region identifiers (e.g., "loop#1/header", "join#3/join")
|
||||
/// Zero-cost when unused (dev only)
|
||||
pub(super) debug_scope_stack: Vec<String>,
|
||||
}
|
||||
|
||||
impl ScopeContext {
|
||||
/// Create new scope context (empty state)
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
lexical_scope_stack: Vec::new(),
|
||||
loop_header_stack: Vec::new(),
|
||||
loop_exit_stack: Vec::new(),
|
||||
if_merge_stack: Vec::new(),
|
||||
current_function: None,
|
||||
function_param_names: HashSet::new(),
|
||||
debug_scope_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Lexical scope helpers ----
|
||||
|
||||
/// Push new lexical scope frame
|
||||
#[inline]
|
||||
pub(super) fn push_lexical_scope(&mut self) {
|
||||
self.lexical_scope_stack
|
||||
.push(LexicalScopeFrame::default());
|
||||
}
|
||||
|
||||
/// Pop lexical scope frame (returns frame for restoration)
|
||||
#[inline]
|
||||
pub(super) fn pop_lexical_scope(&mut self) -> Option<LexicalScopeFrame> {
|
||||
self.lexical_scope_stack.pop()
|
||||
}
|
||||
|
||||
/// Get mutable reference to current scope frame
|
||||
#[inline]
|
||||
pub(super) fn current_scope_mut(&mut self) -> Option<&mut LexicalScopeFrame> {
|
||||
self.lexical_scope_stack.last_mut()
|
||||
}
|
||||
|
||||
// ---- Control flow stack helpers ----
|
||||
|
||||
/// Push loop header block
|
||||
#[inline]
|
||||
pub(super) fn push_loop_header(&mut self, bb: BasicBlockId) {
|
||||
self.loop_header_stack.push(bb);
|
||||
}
|
||||
|
||||
/// Pop loop header block
|
||||
#[inline]
|
||||
pub(super) fn pop_loop_header(&mut self) -> Option<BasicBlockId> {
|
||||
self.loop_header_stack.pop()
|
||||
}
|
||||
|
||||
/// Get innermost loop header
|
||||
#[inline]
|
||||
pub(super) fn current_loop_header(&self) -> Option<BasicBlockId> {
|
||||
self.loop_header_stack.last().copied()
|
||||
}
|
||||
|
||||
/// Push loop exit block
|
||||
#[inline]
|
||||
pub(super) fn push_loop_exit(&mut self, bb: BasicBlockId) {
|
||||
self.loop_exit_stack.push(bb);
|
||||
}
|
||||
|
||||
/// Pop loop exit block
|
||||
#[inline]
|
||||
pub(super) fn pop_loop_exit(&mut self) -> Option<BasicBlockId> {
|
||||
self.loop_exit_stack.pop()
|
||||
}
|
||||
|
||||
/// Push if/merge block
|
||||
#[inline]
|
||||
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) {
|
||||
self.if_merge_stack.push(bb);
|
||||
}
|
||||
|
||||
/// Pop if/merge block
|
||||
#[inline]
|
||||
pub(super) fn pop_if_merge(&mut self) -> Option<BasicBlockId> {
|
||||
self.if_merge_stack.pop()
|
||||
}
|
||||
|
||||
// ---- Debug scope helpers ----
|
||||
|
||||
/// Push debug region identifier
|
||||
#[inline]
|
||||
pub(super) fn debug_push_region<S: Into<String>>(&mut self, region: S) {
|
||||
self.debug_scope_stack.push(region.into());
|
||||
}
|
||||
|
||||
/// Pop debug region identifier
|
||||
#[inline]
|
||||
pub(super) fn debug_pop_region(&mut self) {
|
||||
let _ = self.debug_scope_stack.pop();
|
||||
}
|
||||
|
||||
/// Get current debug region identifier
|
||||
#[inline]
|
||||
pub(super) fn debug_current_region_id(&self) -> Option<String> {
|
||||
self.debug_scope_stack.last().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScopeContext {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_lexical_scope_stack() {
|
||||
let mut ctx = ScopeContext::new();
|
||||
|
||||
// Initially empty
|
||||
assert_eq!(ctx.lexical_scope_stack.len(), 0);
|
||||
|
||||
// Push scope
|
||||
ctx.push_lexical_scope();
|
||||
assert_eq!(ctx.lexical_scope_stack.len(), 1);
|
||||
|
||||
// Push another
|
||||
ctx.push_lexical_scope();
|
||||
assert_eq!(ctx.lexical_scope_stack.len(), 2);
|
||||
|
||||
// Pop scope
|
||||
let frame = ctx.pop_lexical_scope();
|
||||
assert!(frame.is_some());
|
||||
assert_eq!(ctx.lexical_scope_stack.len(), 1);
|
||||
|
||||
// Pop last
|
||||
let frame = ctx.pop_lexical_scope();
|
||||
assert!(frame.is_some());
|
||||
assert_eq!(ctx.lexical_scope_stack.len(), 0);
|
||||
|
||||
// Pop from empty
|
||||
let frame = ctx.pop_lexical_scope();
|
||||
assert!(frame.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loop_stacks() {
|
||||
let mut ctx = ScopeContext::new();
|
||||
|
||||
let header1 = BasicBlockId(1);
|
||||
let header2 = BasicBlockId(2);
|
||||
|
||||
// Initially empty
|
||||
assert!(ctx.current_loop_header().is_none());
|
||||
|
||||
// Push first loop
|
||||
ctx.push_loop_header(header1);
|
||||
assert_eq!(ctx.current_loop_header(), Some(header1));
|
||||
|
||||
// Push nested loop (innermost)
|
||||
ctx.push_loop_header(header2);
|
||||
assert_eq!(ctx.current_loop_header(), Some(header2));
|
||||
|
||||
// Pop innermost
|
||||
assert_eq!(ctx.pop_loop_header(), Some(header2));
|
||||
assert_eq!(ctx.current_loop_header(), Some(header1));
|
||||
|
||||
// Pop outermost
|
||||
assert_eq!(ctx.pop_loop_header(), Some(header1));
|
||||
assert!(ctx.current_loop_header().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_merge_stack() {
|
||||
let mut ctx = ScopeContext::new();
|
||||
|
||||
let merge1 = BasicBlockId(10);
|
||||
let merge2 = BasicBlockId(20);
|
||||
|
||||
// Push merge blocks
|
||||
ctx.push_if_merge(merge1);
|
||||
ctx.push_if_merge(merge2);
|
||||
|
||||
// Pop in LIFO order
|
||||
assert_eq!(ctx.pop_if_merge(), Some(merge2));
|
||||
assert_eq!(ctx.pop_if_merge(), Some(merge1));
|
||||
assert_eq!(ctx.pop_if_merge(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_scope_stack() {
|
||||
let mut ctx = ScopeContext::new();
|
||||
|
||||
// Initially empty
|
||||
assert!(ctx.debug_current_region_id().is_none());
|
||||
|
||||
// Push region
|
||||
ctx.debug_push_region("loop#1/header");
|
||||
assert_eq!(
|
||||
ctx.debug_current_region_id(),
|
||||
Some("loop#1/header".to_string())
|
||||
);
|
||||
|
||||
// Push nested region
|
||||
ctx.debug_push_region("join#3/join");
|
||||
assert_eq!(
|
||||
ctx.debug_current_region_id(),
|
||||
Some("join#3/join".to_string())
|
||||
);
|
||||
|
||||
// Pop
|
||||
ctx.debug_pop_region();
|
||||
assert_eq!(
|
||||
ctx.debug_current_region_id(),
|
||||
Some("loop#1/header".to_string())
|
||||
);
|
||||
|
||||
ctx.debug_pop_region();
|
||||
assert!(ctx.debug_current_region_id().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_context() {
|
||||
let mut ctx = ScopeContext::new();
|
||||
|
||||
// Initially no function
|
||||
assert!(ctx.current_function.is_none());
|
||||
assert!(ctx.function_param_names.is_empty());
|
||||
|
||||
// Simulate setting function context
|
||||
ctx.function_param_names.insert("x".to_string());
|
||||
ctx.function_param_names.insert("y".to_string());
|
||||
|
||||
assert_eq!(ctx.function_param_names.len(), 2);
|
||||
assert!(ctx.function_param_names.contains("x"));
|
||||
assert!(ctx.function_param_names.contains("y"));
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
use crate::mir::{BindingId, ValueId};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(in crate::mir::builder) struct LexicalScopeFrame {
|
||||
declared: BTreeSet<String>,
|
||||
restore: BTreeMap<String, Option<ValueId>>,
|
||||
pub(in crate::mir::builder) declared: BTreeSet<String>,
|
||||
pub(in crate::mir::builder) restore: BTreeMap<String, Option<ValueId>>,
|
||||
/// Phase 74: Parallel BindingId restoration on scope exit
|
||||
restore_binding: BTreeMap<String, Option<BindingId>>,
|
||||
pub(in crate::mir::builder) restore_binding: BTreeMap<String, Option<BindingId>>,
|
||||
}
|
||||
|
||||
impl LexicalScopeFrame {
|
||||
@ -35,15 +35,26 @@ impl Drop for LexicalScopeGuard {
|
||||
}
|
||||
|
||||
impl super::super::MirBuilder {
|
||||
#[allow(deprecated)]
|
||||
pub(in crate::mir::builder) fn push_lexical_scope(&mut self) {
|
||||
// Phase 136 Step 3/7: Update both scope_ctx (SSOT) and legacy field (backward compat)
|
||||
self.scope_ctx.push_lexical_scope();
|
||||
self.lexical_scope_stack.push(LexicalScopeFrame::new());
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub(in crate::mir::builder) fn pop_lexical_scope(&mut self) {
|
||||
// Phase 136 Step 3/7: Pop from scope_ctx (SSOT)
|
||||
let frame = self
|
||||
.scope_ctx
|
||||
.pop_lexical_scope()
|
||||
.expect("COMPILER BUG: pop_lexical_scope without push_lexical_scope");
|
||||
|
||||
// Sync to legacy field
|
||||
let _ = self
|
||||
.lexical_scope_stack
|
||||
.pop()
|
||||
.expect("COMPILER BUG: pop_lexical_scope without push_lexical_scope");
|
||||
.expect("COMPILER BUG: legacy stack out of sync");
|
||||
|
||||
// Restore ValueId mappings
|
||||
for (name, previous) in frame.restore {
|
||||
@ -70,12 +81,14 @@ impl super::super::MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub(in crate::mir::builder) fn declare_local_in_current_scope(
|
||||
&mut self,
|
||||
name: &str,
|
||||
value: ValueId,
|
||||
) -> Result<(), String> {
|
||||
let Some(frame) = self.lexical_scope_stack.last_mut() else {
|
||||
// Phase 136 Step 3/7: Use scope_ctx (SSOT)
|
||||
let Some(frame) = self.scope_ctx.current_scope_mut() else {
|
||||
return Err("COMPILER BUG: local declaration outside lexical scope".to_string());
|
||||
};
|
||||
|
||||
@ -98,6 +111,9 @@ impl super::super::MirBuilder {
|
||||
let binding_id = self.allocate_binding_id();
|
||||
self.binding_map.insert(name.to_string(), binding_id);
|
||||
|
||||
// Sync to legacy field
|
||||
self.sync_scope_ctx_to_legacy();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user