2025-12-13 17:26:15 +09:00
|
|
|
|
# Phase 80: BindingId P3/P4 Expansion Plan
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**Status**: Completed (commit `84129a7e`)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
**Created**: 2025-12-13
|
|
|
|
|
|
|
|
|
|
|
|
**Progress**:
|
|
|
|
|
|
- ✅ Task 80-0: Current Status Verification (complete)
|
|
|
|
|
|
- ✅ Task 80-A: SSOT Design Doc (complete)
|
2025-12-13 18:14:37 +09:00
|
|
|
|
- ✅ Task 80-B: Pattern3 BindingId Wiring (dev-only)
|
|
|
|
|
|
- ✅ Task 80-C: Pattern4 BindingId Wiring (dev-only)
|
|
|
|
|
|
- ✅ Task 80-D: E2E Tests (P3/P4, dev-only)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Task 80-0: Current Status Verification
|
|
|
|
|
|
|
|
|
|
|
|
### Test Results
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. Full lib test suite
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cargo test --release --lib
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Result**: ✅ **PASS** - 970 passed; 0 failed; 56 ignored
|
|
|
|
|
|
|
|
|
|
|
|
**Interpretation**: All production code is stable, Phase 79 changes are production-safe.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. normalized_dev tests
|
|
|
|
|
|
```bash
|
|
|
|
|
|
NYASH_JOINIR_NORMALIZED_DEV_RUN=1 RUST_TEST_THREADS=1 cargo test --features normalized_dev --test normalized_joinir_min -- --nocapture
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Result**: ✅ **PASS**(Phase 79 の “AST直組み E2E” は Normalized SSOT から外し、別途 Phase 80-D で整理予定)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. Quick smoke tests
|
|
|
|
|
|
```bash
|
|
|
|
|
|
tools/smokes/v2/run.sh --profile quick --filter "json_pp_vm"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Result**: ✅ **PASS** - Representative smoke test passes
|
|
|
|
|
|
|
|
|
|
|
|
**Details**:
|
|
|
|
|
|
- `json_pp_vm`: ✅ PASS (.005s)
|
|
|
|
|
|
- `json_lint_vm`: ❌ FAIL (pre-existing, unrelated to Phase 79/80)
|
|
|
|
|
|
- Error: "normalized Pattern2 verifier: function 'main' does not end with tail call/if"
|
|
|
|
|
|
- Also fails on clean Phase 79 commit `b7f78825`
|
|
|
|
|
|
- Classification: **Out of scope** for Phase 80
|
|
|
|
|
|
|
|
|
|
|
|
**Interpretation**: Core VM functionality stable, Phase 79/80 changes don't break smoke tests
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Decision: Defer Pattern2 E2E(DigitPos/Trim)until ExitLine contract is stabilized
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
Phase 80 の主目的は Pattern3/4 の BindingId 配線なので、Pattern2 の E2E は Phase 81+ に回し、
|
2025-12-13 17:26:15 +09:00
|
|
|
|
さらに “promoted carriers(DigitPos/Trim)を含む Pattern2 の ExitLine 接続” が安定してから固定する。
|
|
|
|
|
|
|
|
|
|
|
|
## Task 80-0: Summary
|
|
|
|
|
|
|
|
|
|
|
|
**Status**: ✅ **COMPLETE**
|
|
|
|
|
|
|
|
|
|
|
|
**Key Findings**:
|
|
|
|
|
|
1. ✅ **Production code stable** - 970/970 lib tests PASS
|
|
|
|
|
|
2. ✅ **Phase 79 changes production-safe** - No regressions in lib tests
|
|
|
|
|
|
3. ✅ **Core VM functionality stable** - Smoke tests pass
|
|
|
|
|
|
4. ⚠️ **Pattern2(DigitPos/Trim)E2E は保留** - promoted carriers を含む ExitLine 契約の安定化後に固定する
|
|
|
|
|
|
5. ❌ **Pre-existing smoke test failure** - `json_lint_vm` fails (unrelated)
|
|
|
|
|
|
|
|
|
|
|
|
**Classification**:
|
|
|
|
|
|
- **Phase 80 blockers**: NONE
|
|
|
|
|
|
- **Phase 80 out-of-scope**:
|
2025-12-13 18:14:37 +09:00
|
|
|
|
- Pattern2(DigitPos/Trim)promoted carriers の ExitLine 契約安定化(Phase 81+)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
- json_lint_vm smoke test failure (pre-existing Pattern2 verifier issue)
|
|
|
|
|
|
|
|
|
|
|
|
**Decision**: ✅ **Proceed to Task 80-A**
|
|
|
|
|
|
|
|
|
|
|
|
**Justification**:
|
|
|
|
|
|
- Production code is stable and safe
|
|
|
|
|
|
- Dev-only test failures are **isolated** and **documented**
|
|
|
|
|
|
- Phase 80-B/C を先に進め、E2E(80-D)は Pattern2 契約の整備後に固定する
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Next Steps
|
|
|
|
|
|
|
|
|
|
|
|
✅ Task 80-0 complete → Proceed to Task 80-A (Phase 80 SSOT design doc)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Task 80-A: SSOT Design Doc
|
|
|
|
|
|
|
|
|
|
|
|
**Goal**: Document Pattern3/4 BindingId wiring strategy BEFORE implementation
|
|
|
|
|
|
|
|
|
|
|
|
**Status**: ✅ **COMPLETE**
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 80 Scope
|
|
|
|
|
|
|
|
|
|
|
|
**In Scope**:
|
|
|
|
|
|
1. Pattern3 (if-sum) BindingId registration at ValueId determination point
|
|
|
|
|
|
2. Pattern4 (continue/Trim) BindingId registration at ValueId determination point
|
|
|
|
|
|
3. E2E verification tests (2 tests: P3 + P4)
|
|
|
|
|
|
4. Fallback detection capability (existing `[binding_pilot/fallback]` tags)
|
|
|
|
|
|
|
|
|
|
|
|
**Out of Scope** (deferred to Phase 81+):
|
|
|
|
|
|
- Name fallback removal (dual-path stays)
|
|
|
|
|
|
- BindingId mandatory enforcement
|
|
|
|
|
|
- Pattern1 Minimal (already has structural detection, doesn't use carriers)
|
|
|
|
|
|
- by-name rule branching (prohibited by invariant)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Design Principles (unchanged from Phase 74-79)
|
|
|
|
|
|
|
|
|
|
|
|
1. **Dev-only**: All code `#[cfg(feature = "normalized_dev")]` or `#[cfg(debug_assertions)]`
|
|
|
|
|
|
2. **Dual-path maintained**: BindingId priority + name fallback (no removal yet)
|
|
|
|
|
|
3. **Structural detection only**: NO by-name rule branching
|
|
|
|
|
|
4. **Fail-Fast**: Explicit errors with tags, no silent fallbacks
|
|
|
|
|
|
5. **Zero production impact**: All changes gated, 970/970 lib tests must PASS
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Pattern3 (if-sum) BindingId Wiring Strategy
|
|
|
|
|
|
|
|
|
|
|
|
**Entry point**: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
|
|
|
|
|
|
|
|
|
|
|
|
**Key function**: `lower_pattern3_if_sum()`
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**ConditionEnv creation**: `ConditionEnvBuilder` 生成直後(`lower_if_sum_pattern()` に渡す前)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
**BindingId registration points**:
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
1. **Loop var registration**(dev-only):
|
2025-12-13 17:26:15 +09:00
|
|
|
|
```rust
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
if let Some(loop_var_bid) = builder.binding_map.get(&loop_var_name).copied() {
|
|
|
|
|
|
cond_env.register_loop_var_binding(loop_var_bid, loop_var_join_id);
|
|
|
|
|
|
eprintln!("[phase80/p3] Registered loop var '{}' BindingId({}) -> ValueId({})",
|
|
|
|
|
|
loop_var_name, loop_var_bid.0, loop_var_join_id.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
2. **Condition bindings registration**(dev-only):
|
2025-12-13 17:26:15 +09:00
|
|
|
|
```rust
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
2025-12-13 18:14:37 +09:00
|
|
|
|
for binding in &inline_boundary.condition_bindings {
|
|
|
|
|
|
if let Some(bid) = builder.binding_map.get(&binding.name).copied() {
|
|
|
|
|
|
cond_env.register_condition_binding(bid, binding.join_value);
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[phase80/p3] Registered condition binding '{}' BindingId({}) -> ValueId({})",
|
|
|
|
|
|
binding.name, bid.0, binding.join_value.0
|
|
|
|
|
|
);
|
2025-12-13 17:26:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**Timing**: BEFORE condition lowering
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**Note (重要)**:
|
|
|
|
|
|
- Pattern3/4 は lowering の後段(ExitMeta / merge 側)で carrier join_id が確定するため、Phase 80 では **carriers の BindingId 登録はしない**。
|
|
|
|
|
|
- ここでの目的は「条件 lowering(lookup_with_binding)」の経路を先に BindingId 化して、shadowing の破綻を防ぐこと。
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Pattern4 (continue/Trim) BindingId Wiring Strategy
|
|
|
|
|
|
|
|
|
|
|
|
**Entry point**: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
|
|
|
|
|
|
|
|
|
|
|
|
**Key function**: `cf_loop_pattern4_with_continue()`
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**ConditionEnv creation**: `lower_loop_with_continue_minimal()` 内
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
**BindingId registration points**:
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
1. **Caller side**(dev-only): `binding_map` を lowerer に渡す
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
2. **Lowerer side**(dev-only): join_id が確定したタイミングで
|
|
|
|
|
|
- loop var / condition bindings / carriers を BindingId 登録する
|
|
|
|
|
|
- ログは `[phase80/p4]` タグで可視化する
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
**Special note**: Trim patterns (skip_whitespace) use promoted carriers, BindingId comes from `promoted_bindings` map via CarrierBindingAssigner
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
**Timing**: ValueId allocation の直後(condition lowering の前)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Fallback Detection Mechanism
|
|
|
|
|
|
|
|
|
|
|
|
**Existing infrastructure** (no changes needed):
|
|
|
|
|
|
- `ConditionEnv::lookup_with_binding()` - tries BindingId first, logs `[binding_pilot/hit]`
|
|
|
|
|
|
- `ConditionEnv::lookup_or_fallback()` - fallback to name, logs `[binding_pilot/fallback]`
|
|
|
|
|
|
|
|
|
|
|
|
**Detection strategy**:
|
2025-12-13 19:01:14 +09:00
|
|
|
|
1. Run tests with `HAKO_JOINIR_DEBUG=1` (or legacy `NYASH_JOINIR_DEBUG=1`)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
2. Check for `[binding_pilot/hit]` tags (BindingId path success)
|
|
|
|
|
|
3. Check for NO `[binding_pilot/fallback]` tags (name fallback NOT used)
|
|
|
|
|
|
4. If fallback occurs → test fails with diagnostic
|
|
|
|
|
|
|
2025-12-13 19:01:14 +09:00
|
|
|
|
**Note (Phase 82)**: Both `HAKO_JOINIR_DEBUG` and `NYASH_JOINIR_DEBUG` are supported.
|
|
|
|
|
|
Recommended: Use `HAKO_JOINIR_DEBUG=1` (NYASH_ variant is deprecated but still works)
|
|
|
|
|
|
|
2025-12-13 17:26:15 +09:00
|
|
|
|
**Acceptance criteria** (Task 80-D):
|
|
|
|
|
|
- P3 test: BindingId hit, NO fallback
|
|
|
|
|
|
- P4 test: BindingId hit, NO fallback
|
|
|
|
|
|
|
2025-12-13 19:01:14 +09:00
|
|
|
|
**Fallback Behavior Documentation (Phase 83)**:
|
|
|
|
|
|
|
|
|
|
|
|
**Expected**: Promoted carriers should ALWAYS use BindingId path, never fallback
|
|
|
|
|
|
- DigitPos carriers (`is_digit_pos`): ✅ BindingId hit only
|
|
|
|
|
|
- Trim carriers (`is_ch_match`): ✅ BindingId hit only
|
|
|
|
|
|
- Loop variables in P3/P4: ✅ BindingId hit only
|
|
|
|
|
|
|
|
|
|
|
|
**Verification**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Phase 80 tests verify BindingId resolution works (no runtime errors)
|
|
|
|
|
|
cargo test --features normalized_dev --test normalized_joinir_min test_phase80_p3_bindingid_lookup_works
|
|
|
|
|
|
cargo test --features normalized_dev --test normalized_joinir_min test_phase80_p4_bindingid_lookup_works
|
|
|
|
|
|
|
|
|
|
|
|
# Phase 81 tests verify ExitLine contract (promoted carriers handled correctly)
|
|
|
|
|
|
cargo test --features normalized_dev --test normalized_joinir_min test_phase81_digitpos_exitline_contract
|
|
|
|
|
|
cargo test --features normalized_dev --test normalized_joinir_min test_phase81_trim_exitline_contract
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Debug Tags** (dev-only, during MIR compilation):
|
|
|
|
|
|
- `[binding_pilot/hit]`: BindingId lookup succeeded ✅ (expected)
|
|
|
|
|
|
- `[binding_pilot/fallback]`: Name-based fallback occurred ❌ (should NOT appear for promoted carriers)
|
|
|
|
|
|
- `[binding_pilot/legacy]`: No BindingId provided, using name (legacy code paths only)
|
|
|
|
|
|
|
|
|
|
|
|
**Status (Phase 83)**: All Phase 80/81 tests PASS, indicating NO fallback to name-based lookup for promoted carriers.
|
|
|
|
|
|
|
2025-12-13 17:26:15 +09:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Implementation Order
|
|
|
|
|
|
|
|
|
|
|
|
1. ✅ Task 80-A: Design doc (this section)
|
2025-12-13 18:14:37 +09:00
|
|
|
|
2. ✅ Task 80-B: Pattern3 wiring(loop var + condition bindings)
|
|
|
|
|
|
3. ✅ Task 80-C: Pattern4 wiring(lowerer 側で登録)
|
|
|
|
|
|
4. ✅ Task 80-D: E2E tests(P3 1本 / P4 1本, `tests/normalized_joinir_min.rs`)
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Success Metrics
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
- [x] P3: 条件 lowering が BindingId 経路で解決できる(E2Eテストで固定)
|
|
|
|
|
|
- [x] P4: 条件 lowering が BindingId 経路で解決できる(E2Eテストで固定)
|
|
|
|
|
|
- [x] Fallback 検知(`[binding_pilot/fallback]` をテストで検知できる)
|
|
|
|
|
|
- [x] `cargo test --release --lib` が PASS(production 影響なし)
|
|
|
|
|
|
- [x] 追加コードは dev-only(`normalized_dev`)に閉じている
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Goal (unchanged from Phase 80 spec)
|
|
|
|
|
|
|
|
|
|
|
|
- Pattern3 (if-sum) の条件 lowering で lookup_with_binding() が効く
|
|
|
|
|
|
- Pattern4 (continue/skip_ws) の条件 lowering で lookup_with_binding() が効く
|
|
|
|
|
|
- Fallback 監視: name fallback に落ちたら分かる仕掛け(既存ログタグ活用)
|
|
|
|
|
|
|
|
|
|
|
|
## Non-Goal
|
|
|
|
|
|
|
|
|
|
|
|
- Name fallback の撤去はまだ(Phase 81+ で対応)
|
|
|
|
|
|
- BindingId 完全義務化はまだ(dual-path 維持)
|
|
|
|
|
|
|
|
|
|
|
|
## Invariant
|
|
|
|
|
|
|
|
|
|
|
|
- by-name ルール分岐禁止(structural detection のみ)
|
|
|
|
|
|
- binding_id_map 登録は「ValueId が確定した時点」のみ
|
|
|
|
|
|
- promoted が絡む場合は CarrierVar.binding_id / promoted_bindings を経由
|
|
|
|
|
|
|
|
|
|
|
|
## Implementation Tasks (pending Task 80-0 completion)
|
|
|
|
|
|
|
2025-12-13 18:14:37 +09:00
|
|
|
|
1. Pattern3 (if-sum) BindingId 登録配線 ✅
|
|
|
|
|
|
2. Pattern4 (continue) BindingId 登録配線 ✅
|
|
|
|
|
|
3. E2E tests (P3 1本 / P4 1本) ✅
|
2025-12-13 17:26:15 +09:00
|
|
|
|
|
|
|
|
|
|
## Success Metrics
|
|
|
|
|
|
|
|
|
|
|
|
- P3/P4 代表ケースで lookup_with_binding() 経路がヒット(ログ or テスト)
|
|
|
|
|
|
- Fallback 検知可能(既存 `[binding_pilot/fallback]` タグ活用)
|
2025-12-13 18:14:37 +09:00
|
|
|
|
- `cargo test --release --lib` PASS(退行なし)
|