fix(joinir): Phase 283 P0 - Pattern3 Undefined ValueId Bug Fix

- Fix: extract_if_condition() moved after local_cond_env construction
  (loop_with_if_phi_if_sum.rs:175)
  - Root cause: condition extraction before i_param/sum_param creation
  - Result: i % 2 referenced caller's ConditionEnv with unmapped ValueId
- Fail-Fast: Add condition_bindings validation in merge (mod.rs)
- Fixture: Update loop_if_phi.hako for C2 compatibility (sum.toString())
- Verified: VM execution outputs sum=9 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-23 08:14:27 +09:00
parent 32ccccd272
commit bfbc9b26bf
6 changed files with 114 additions and 31 deletions

View File

@ -7,8 +7,7 @@ static box Main {
if (i % 2 == 1) { sum = sum + i } else { sum = sum + 0 }
i = i + 1
}
console.println("sum=" + sum)
console.println("sum=" + sum.toString())
return 0
}
}

View File

@ -2,16 +2,15 @@
## Current Focus (next)
- Phase 281design-first: `docs/development/current/main/phases/phase-281/README.md`
- P1 planned: Pattern6early-exitを compose に寄せる(挙動不変で段階移行)
- SSOT: `docs/development/current/main/design/edgecfg-fragments.md`, `src/mir/builder/control_flow/edgecfg/api/compose.rs`
- Phase 282planned, design-first: Router shrinkage
- pattern番号を “症状ラベル(テスト名)” に降格し、合成SSOTへ寄せた後に router を縮退させる
- Phase 283bugfix: JoinIR if-condition remap fix: `docs/development/current/main/phases/phase-283/README.md`
- 目的: Pattern3if-sum`i % 2 == 1` が undefined `ValueId` を生成する不具合を根治する
- SSOT: `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs`ConditionEnv の正しい使用位置)
## Recently Completed (2025-12-23)
- Phase 282Router shrinkage + extraction-based migration P0P5: `docs/development/current/main/phases/phase-282/README.md`
- Phase 280Frag composition SSOT positioning: `docs/development/current/main/phases/phase-280/README.md`
- Phase 281 P0Pattern7 → compose::if_: `docs/development/current/main/phases/phase-281/README.md`
- Phase 281Pattern6/7 compose adoption, P0P3: `docs/development/current/main/phases/phase-281/README.md`
- Phase 273Plan line SSOT: `docs/development/current/main/phases/phase-273/README.md`
- Phase 275 P0A1/B2/C2 coercion SSOT: `docs/development/current/main/phases/phase-275/README.md`
- Phase 276 P0quick wins / type_helper SSOT: `docs/development/current/main/phases/phase-276/README.md`

View File

@ -8,12 +8,9 @@ Related:
## 直近JoinIR/selfhost
- **Phase 281planned: Pattern6 を compose へ段階吸収**
- 入口: `docs/development/current/main/phases/phase-281/README.md`
- P0Pattern7✅ 完了、次は P1Pattern6 early-exit
- **Phase 282planned: Router shrinkage**
- 目的: pattern番号を “症状ラベル” に縮退させ、合成SSOTへ寄せた後に router の分岐を減らす
- **Phase 282planned, design-first: Router shrinkage**
- 入口: `docs/development/current/main/phases/phase-282/README.md`
- 目的: pattern番号を “症状ラベル” に縮退し、router の責務を「抽出の配線」へ収束させる
(✅ done**Phase 279 P0**: Type propagation pipeline SSOT 統一lifecycle / JoinIR / LLVM の二重化解消)
- 完了: `docs/development/current/main/phases/phase-279/README.md`
@ -27,6 +24,12 @@ Related:
- (✅ done**Phase 273**: Plan line SSOTPattern6/7
- 完了: `docs/development/current/main/phases/phase-273/README.md`
- (✅ done**Phase 280**: Frag composition SSOT positioning
- 完了: `docs/development/current/main/phases/phase-280/README.md`
- (✅ done**Phase 281**: compose adoptionPattern6/7, P0P3
- 完了: `docs/development/current/main/phases/phase-281/README.md`
- (✅ done**Phase 277/278**: PHI strict + env var 収束
- 完了: `docs/development/current/main/phases/phase-277/README.md`
- 完了: `docs/development/current/main/phases/phase-278/README.md`

View File

@ -0,0 +1,47 @@
# Phase 283: JoinIR If-Condition ValueId Remap Fix
Status: **P0 ✅ complete (2025-12-23)**
Goal:
- Fix a Pattern3 (if-sum) JoinIR lowering bug where complex if-conditions like `i % 2 == 1` could produce MIR that uses an undefined `ValueId`.
SSOT References:
- JoinIR lowering (if-sum): `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs`
- Condition value lowering: `src/mir/join_ir/lowering/condition_lowerer.rs`
- MIR verification (undefined values): `src/mir/verification/ssa.rs`
- Test fixture: `apps/tests/loop_if_phi.hako`
## Problem
Observed error (VM):
- `use of undefined value ValueId(...)` inside the loop body, originating from the modulo expression in the if-condition.
The symptom appeared in `apps/tests/loop_if_phi.hako`:
```hako
if (i % 2 == 1) { ... } else { ... }
```
## Root Cause
In `lower_if_sum_pattern`, the if-condition was extracted (and its JoinIR value-expression lowered) **before** `i_param` / `sum_param` existed, so `Variable("i")` inside complex expressions resolved through the caller-provided `ConditionEnv`.
That early `ConditionEnv` mapping could not be remapped by the boundary/merger, resulting in JoinIR ValueIds that became undefined host ValueIds in MIR.
## Fix (P0)
- Move `extract_if_condition(...)` so it runs **after** `local_cond_env` is constructed and shadowed:
- `local_cond_env.insert(loop_var, i_param)`
- `local_cond_env.insert(update_var, sum_param)`
- (and any condition bindings remapped to `main_params`)
This ensures complex expressions like `i % 2` resolve `i` to `i_param` (a loop_step param that the merger remaps), preventing undefined ValueIds.
## Fixture Note (Coercion SSOT)
After Phase 275 (C2), implicit `"String" + Integer` is a TypeError. The fixture uses:
- `sum.toString()` instead of `"sum=" + sum` or `str(sum)`.
## Acceptance
- `apps/tests/loop_if_phi.hako` runs on VM without undefined ValueId errors and prints `sum=9`.

View File

@ -239,7 +239,24 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.flat_map(|params| params.iter().copied())
.collect();
// Phase 283 P0 DEBUG: Log condition_bindings count
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 283 P0 DEBUG: Processing {} condition_bindings",
boundary.condition_bindings.len()
),
debug,
);
for binding in &boundary.condition_bindings {
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 283 P0 DEBUG: Checking binding '{}' join={:?}",
binding.name, binding.join_value
),
debug,
);
if all_params.contains(&binding.join_value) {
trace.stderr_if(
&format!(
@ -249,14 +266,27 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
debug,
);
} else {
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values",
// Phase 283 P0 FIX: Ensure remapper has valid mapping (Fail-Fast)
if let Some(host_id) = builder.variable_ctx.variable_map.get(&binding.name) {
// Variable exists in host context - map join_value to existing host_id
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 283 P0: ✅ Condition binding '{}' JoinIR {:?} → host {:?}",
binding.name, binding.join_value, host_id
),
debug,
);
remapper.set_value(binding.join_value, *host_id);
used_values.insert(binding.join_value);
} else {
// Fail-Fast: No host ValueId found → surface root cause immediately
return Err(format!(
"[merge/phase2.1] Condition variable '{}' (join={:?}) has no host ValueId in variable_map. \
This indicates the value was not properly supplied by boundary builder or cond_env. \
Check: (1) boundary builder supplies all condition vars, (2) cond_env correctly tracks host ValueIds.",
binding.name, binding.join_value
),
debug,
);
used_values.insert(binding.join_value);
));
}
}
}

View File

@ -102,16 +102,6 @@ pub fn lower_if_sum_pattern(
&format!("{} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0)
);
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
// Phase 220-D: Now returns ValueId and instructions
// Phase 242-EX-A: Now supports complex LHS
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
trace.log(
"if-cond",
&format!("{} {:?} ValueId({})", if_var, if_op, if_value_val.0)
);
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=<expr>)
// Phase 256.7: update_addend is now ASTNode (supports variables like separator)
let (update_var, update_addend_ast) = extract_then_update(if_stmt)?;
@ -182,6 +172,21 @@ pub fn lower_if_sum_pattern(
}
let cond_env = &local_cond_env; // Shadow the original cond_env
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
//
// IMPORTANT (Phase 283 P0): Extract using the local_cond_env.
// The pre-loop extraction phase runs before `i_param`/`sum_param` exist, so
// `i % 2 == 1` would resolve `i` via the caller's ConditionEnv and produce
// JoinIR ValueIds that the boundary cannot remap → undefined ValueId in MIR.
//
// By extracting here, Variable("i") resolves to `i_param` and remains remappable.
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
trace.log(
"if-cond",
&format!("{} {:?} ValueId({})", if_var, if_op, if_value_val.0)
);
// === main() function ===
let mut main_func = JoinFunction::new(main_id, "main".to_string(), main_params.clone());