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:
nyash-codex
2025-12-15 20:28:21 +09:00
parent 89edf11699
commit 3127ebb73d
6 changed files with 463 additions and 57 deletions

View File

@ -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 変換との連携強化
## 参考資料

View File

@ -53,6 +53,7 @@ mod router; // RouterPolicyBoxUnified 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 → BTreeMapPHI生成の決定性確保
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.

View File

@ -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 {

View File

@ -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 で発生する型欠如を解消する。

View 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"));
}
}

View File

@ -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(())
}
}