feat(mir): Phase 136 P0 - ValueId allocator SSOT 徹底(関数内経路から value_gen.next() 掃討)

## Summary
Eliminates remaining `value_gen.next()` calls from function-context code paths,
unifying all ValueId allocation through `MirBuilder::next_value_id()` SSOT allocator.

## Changes

### 1. Fixed `new_typed_value()` (src/mir/builder.rs:1068)
**Before**: `let id = self.value_gen.next();` (bypasses function context)
**After**: `let id = self.next_value_id();` (respects function context)

This is a public API used in function context, so must use SSOT allocator
to avoid collisions with reserved PHI dsts and function params.

### 2. Fixed test code (src/mir/builder.rs)
**test_shadowing_binding_restore** (lines 1161, 1171):
- Simulates function scope with `push_lexical_scope()`
- Changed to `builder.next_value_id()` for function scope simulation

**test_valueid_binding_parallel_allocation** (lines 1196-1216):
- Tests ValueId/BindingId independence
- Changed to `builder.next_value_id()` with note that Module context fallback preserves test intent

### 3. Verified Module context fallbacks (OK, no change needed)
These already check `current_function.is_some()` and use `value_gen.next()` only as Module context fallback:
- `src/mir/builder/utils.rs:43` - next_value_id() SSOT implementation
- `src/mir/builder/utils.rs:436` - pin_to_slot()
- `src/mir/builder/utils.rs:467` - materialize_local()
- `src/mir/utils/phi_helpers.rs:69` - insert_phi_unified()

## Verification
```bash
rg -n "value_gen\.next\(" src/mir --type rust | grep -v "Module context" | grep -v "//"
# Result: Only comments/docs remain
```

## Acceptance
 cargo test --release --lib - 997 passed
 phase135_trim_mir_verify.sh - PASS
 phase132_exit_phi_parity.sh - 3/3 PASS
 All function-context `value_gen.next()` eliminated

## Effect
- **Collision prevention**: No more ValueId collisions between function-level allocations and reserved PHI dsts
- **SSOT compliance**: All ValueId allocation flows through single allocator
- **Contract enforcement**: Phase 135 P1 contract_checks will catch violations immediately

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-15 19:37:30 +09:00
parent 22b0c14adb
commit 455d511187
4 changed files with 53 additions and 9 deletions

View File

@ -26,7 +26,8 @@
- debug-only: Exit PHI collision を早期検出する verifier を追加LLVM 実行時に壊れる前に Fail-Fast - debug-only: Exit PHI collision を早期検出する verifier を追加LLVM 実行時に壊れる前に Fail-Fast
- **Phase 133 完了**: Promoted carrier の `join_id` 解決Trimを SSOT に寄せて根治smoke は compile-only - **Phase 133 完了**: Promoted carrier の `join_id` 解決Trimを SSOT に寄せて根治smoke は compile-only
- **Phase 134 完了**: Plugin loader best-effort loading決定的順序 + failure 集約 + 継続)を導入。 - **Phase 134 完了**: Plugin loader best-effort loading決定的順序 + failure 集約 + 継続)を導入。
- **Phase 135 実装**: ConditionLoweringBox allocator SSOT を無視して ValueId 衝突を起こす問題を根治(検証はローカルで実施)。 - **Phase 135 完了**: ConditionLoweringBox allocator SSOTP0: 根治修正 + P1: contract_checks Fail-Fast 強化)。
- **Phase 136 完了**: ValueId allocator SSOT 徹底(関数内経路から `value_gen.next()` 掃討完了)。
- **Phase 88 完了**: continue + 可変ステップi=i+const 差分)を dev-only fixture で固定、StepCalculator Box 抽出。 - **Phase 88 完了**: continue + 可変ステップi=i+const 差分)を dev-only fixture で固定、StepCalculator Box 抽出。
- **Phase 89 完了**: P0ContinueReturn detector+ P1lowering 実装)完了。 - **Phase 89 完了**: P0ContinueReturn detector+ P1lowering 実装)完了。
- **Phase 90 完了**: ParseStringComposite + `Null` literal + ContinueReturn同一値の複数 return-ifを dev-only fixture で固定。 - **Phase 90 完了**: ParseStringComposite + `Null` literal + ContinueReturn同一値の複数 return-ifを dev-only fixture で固定。

View File

@ -8,6 +8,7 @@
- **Phase 133**: Promoted carrier join_idTrim修正 - **Phase 133**: Promoted carrier join_idTrim修正
- **Phase 134**: Plugin loader best-effort loading - **Phase 134**: Plugin loader best-effort loading
- **Phase 135**: ConditionLoweringBox allocator SSOTValueId 衝突の根治) - **Phase 135**: ConditionLoweringBox allocator SSOTValueId 衝突の根治)
- **Phase 136**: ValueId allocator SSOT 徹底(残存 value_gen.next() 掃討)
## Phase フォルダ構成(推奨) ## Phase フォルダ構成(推奨)

View File

@ -0,0 +1,36 @@
# Phase 136: ValueId allocator SSOT 徹底(残存 value_gen.next() 掃討)
## Status
- 状態: ✅ P0 完了
- スコープ: 関数内 ValueId 発行を `MirBuilder::next_value_id()` に一本化し、予約PHI/引数/ローカルとの衝突余地を消す
## Problem
Phase 135 で契約allocator SSOTは固まったが、一部のコードで `builder.value_gen.next()` を直接使用していた。
これは関数内で走る場合、予約PHI dst や関数引数との ValueId 衝突を引き起こす可能性がある。
## Fix
### SSOT 化対象
1. **`src/mir/builder.rs:new_typed_value()`** - 型付きValueId発行API
- Before: `let id = self.value_gen.next();`
- After: `let id = self.next_value_id();` (関数コンテキストを尊重)
2. **テストコード内の `value_gen.next()`** - 関数スコープをシミュレート
- `test_shadowing_binding_restore`: 関数スコープシミュレーションで `next_value_id()` を使用
- `test_valueid_binding_parallel_allocation`: SSOT allocator を使用Module context フォールバック維持)
### OKModule context フォールバック)
以下は既に `if current_function.is_some()` で関数コンテキストを判定しており、Module context のフォールバックとして `value_gen.next()` を使用OK
- `src/mir/builder/utils.rs:next_value_id()` - SSOT allocator 自体43行目
- `src/mir/builder/utils.rs:pin_to_slot()` - 436行目
- `src/mir/builder/utils.rs:materialize_local()` - 467行目
- `src/mir/utils/phi_helpers.rs:insert_phi_unified()` - 69行目
## Acceptance
-`rg -n "value_gen\.next\(" src/mir` で関数内経路から消える
-`cargo test --release --lib` - 997 passed
-`phase135_trim_mir_verify.sh` - PASS
-`phase132_exit_phi_parity.sh` - 3/3 PASS
## Remaining Tasks残課題
なし。全ての関数内経路から `value_gen.next()` を排除完了。Module context のフォールバックは意図的に残す。

View File

@ -1064,8 +1064,9 @@ impl MirBuilder {
// ============================================================================ // ============================================================================
/// 型付きValueIdを発行新API /// 型付きValueIdを発行新API
/// Phase 136 P0: Use SSOT allocator (next_value_id) to respect function context
pub fn new_typed_value(&mut self, kind: super::MirValueKind) -> super::TypedValueId { pub fn new_typed_value(&mut self, kind: super::MirValueKind) -> super::TypedValueId {
let id = self.value_gen.next(); let id = self.next_value_id();
self.value_kinds.insert(id, kind); self.value_kinds.insert(id, kind);
super::TypedValueId::new(id, kind) super::TypedValueId::new(id, kind)
} }
@ -1156,7 +1157,8 @@ mod binding_id_tests {
builder.push_lexical_scope(); builder.push_lexical_scope();
// Declare outer x // Declare outer x
let outer_vid = builder.value_gen.next(); // Phase 136 P0: Use SSOT allocator for function scope simulation
let outer_vid = builder.next_value_id();
builder builder
.declare_local_in_current_scope("x", outer_vid) .declare_local_in_current_scope("x", outer_vid)
.unwrap(); .unwrap();
@ -1165,7 +1167,8 @@ mod binding_id_tests {
// Enter inner scope and shadow x // Enter inner scope and shadow x
builder.push_lexical_scope(); builder.push_lexical_scope();
let inner_vid = builder.value_gen.next(); // Phase 136 P0: Use SSOT allocator for function scope simulation
let inner_vid = builder.next_value_id();
builder builder
.declare_local_in_current_scope("x", inner_vid) .declare_local_in_current_scope("x", inner_vid)
.unwrap(); .unwrap();
@ -1186,10 +1189,13 @@ mod binding_id_tests {
fn test_valueid_binding_parallel_allocation() { fn test_valueid_binding_parallel_allocation() {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
// Phase 136 P0: Use SSOT allocator (next_value_id)
// Note: Without current_function, next_value_id() falls back to value_gen.next()
// so this test still validates ValueId/BindingId independence
// Allocate ValueIds and BindingIds in parallel // Allocate ValueIds and BindingIds in parallel
let vid0 = builder.value_gen.next(); let vid0 = builder.next_value_id();
let bid0 = builder.allocate_binding_id(); let bid0 = builder.allocate_binding_id();
let vid1 = builder.value_gen.next(); let vid1 = builder.next_value_id();
let bid1 = builder.allocate_binding_id(); let bid1 = builder.allocate_binding_id();
// ValueId and BindingId should be independent // ValueId and BindingId should be independent
@ -1199,15 +1205,15 @@ mod binding_id_tests {
assert_eq!(bid1.raw(), 1); assert_eq!(bid1.raw(), 1);
// Allocating more ValueIds should not affect BindingId counter // Allocating more ValueIds should not affect BindingId counter
let _ = builder.value_gen.next(); let _ = builder.next_value_id();
let _ = builder.value_gen.next(); let _ = builder.next_value_id();
let bid2 = builder.allocate_binding_id(); let bid2 = builder.allocate_binding_id();
assert_eq!(bid2.raw(), 2); // Still sequential assert_eq!(bid2.raw(), 2); // Still sequential
// Allocating more BindingIds should not affect ValueId counter // Allocating more BindingIds should not affect ValueId counter
let _ = builder.allocate_binding_id(); let _ = builder.allocate_binding_id();
let _ = builder.allocate_binding_id(); let _ = builder.allocate_binding_id();
let vid2 = builder.value_gen.next(); let vid2 = builder.next_value_id();
assert_eq!(vid2.0, 4); // Continues from where we left off assert_eq!(vid2.0, 4); // Continues from where we left off
} }
} }