Phase 123: if-only Normalized Semantics (dev-only)
Status: In Progress Started: 2025-12-18 Scope: if-only patterns with normalized semantics
Goals
Phase 122 completed "JoinModule generation + structure validation". Phase 123 adds meaningful normalized semantics to if-only patterns.
Scope
In-Scope (Phase 123)
- Return(Integer literal):
return 7→Const + Ret(Some(vid)) - Return(Variable): Fail-Fast with strict mode (needs reads facts - Phase 124)
- If(cond_ast): Minimal Compare lowering (
flag == 1,i < 3)- Variable vs Integer literal comparisons only
- then/else: Return(Integer literal) only
- Merge:
join_k(env)tail-call (no PHI)
Out-of-Scope (Future Phases)
- Return(Variable): Requires reads facts (Phase 124)
- Complex conditions: Compound expressions, &&/||, method calls
- Loop/Break/Continue: Capability insufficient (future)
Design Principles
- Box-First: Modular separation of concerns
- Fail-Fast: No fallbacks, explicit errors with hints
- SSOT: No re-analysis of facts/contracts (use computed values only)
- Dev-only: Default behavior unchanged
- Contract-based: Use contract information, not AST inference
Node Support (Phase 123)
Return Nodes
Return(Integer literal) - P1
return 7
→ Const { dst: v1, value: Integer(7) }
→ Ret { value: Some(v1) }
Return(Variable) - P2 (Fail-Fast)
return x
→ freeze_with_hint(
"phase123/return/var_unsupported",
"Phase 123 only supports return with integer literals",
"Add reads fact (Phase 124) or return literal only"
)
If Nodes - P3
Minimal Compare Support
if (flag == 1) {
return 2
} else {
return 3
}
→ Compare { ... }
→ Branch { ... }
→ Ret { ... } (in then block)
→ Ret { ... } (in else block)
→ join_k(env) (merge continuation)
Supported comparisons:
Variable == IntegerVariable < IntegerVariable > IntegerVariable <= IntegerVariable >= IntegerVariable != Integer
Not supported (cap_missing):
- Compound expressions:
a == 1 && b == 2 - Method calls:
s.length() == 0 - Complex expressions:
a + b == 5
Implementation Plan
P0: docs-only (Plan & Scope)
Docs:
docs/development/current/main/phases/phase-123/README.md(this file)- Update
docs/development/current/main/10-Now.md: Next: Phase 123 - Update
docs/development/current/main/30-Backlog.md: Next candidate → Phase 123
Commit: docs: Phase 123 plan (if-only normalized semantics)
P1: Return payload (Integer literal)
Implementation:
src/mir/control_tree/normalized_shadow/builder.rs- Return node →
JoinInst::Ret { value: Some(v) } - Literal(Integer) →
Const+Ret(Some(const_vid)) - Other literals → strict
freeze_with_hint("Phase 123 integer only")
- Return node →
Unit tests:
- "return 7" generates
Const(Integer 7)+Ret(Some(...))
Commit: feat(control_tree): Phase 123 return integer literal in Normalized if-only
P2: Return payload (Variable) - Fail-Fast (A plan)
Design Decision:
- A plan (recommended): Adopted
- Do not add "reads" to StepTreeFacts
- Phase 123 does not allow Return(Variable)
- To support Return(Variable), add reads facts in Phase 124
Implementation:
src/mir/control_tree/normalized_shadow/builder.rs- Return(Variable) → strict
freeze_with_hint( "phase123/return/var_unsupported", "Phase 123 only supports return with integer literals", "Add reads fact (Phase 124) or return literal only" )
- Return(Variable) → strict
Unit tests:
- Verify strict mode fails with appropriate hint
Commit: feat(control_tree): Phase 123 fail-fast on return variable (needs reads fact)
P3: If(cond_ast) minimal lowering
Implementation:
src/mir/control_tree/normalized_shadow/builder.rs- If node:
- Parse
cond_astas "minimal Compare" - Generate JoinIR Compare instruction
- then/else: Return(Integer literal) only (other → strict freeze)
- Merge:
join_k(env)tail-call (no PHI)
- Parse
- Minimal parsing:
flag == 1/i < 3(Variable vs Integer literal)- Other (compound, &&/||, method call) → cap_missing strict freeze
- If node:
Unit tests:
- If(true/false equivalent comparison) generates correct then/else structure
Commit: feat(control_tree): Phase 123 if-only compare+return lowering (Normalized, dev-only)
P4: integration smoke (dev-only strict)
Fixture:
apps/tests/phase123_if_only_return_literal_min.hako(output: 7)
Smoke:
tools/smokes/v2/profiles/integration/apps/phase123_if_only_normalized_semantics_vm.sh- Target: New fixture
- Dev+strict:
NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 - Output validation:
output_validator.sh
Commit: test(joinir): Phase 123 normalized semantics smoke (VM)
P5: docs completion
Docs:
docs/development/current/main/phases/phase-123/README.md: Add DONE sectiondocs/development/current/main/10-Now.md: Update to Phase 123 completedocs/development/current/main/01-JoinIR-Selfhost-INDEX.md: Add Phase 123
Commit: docs: Phase 123 DONE
Verification Commands
# Unit tests
cargo test --lib
# Smoke tests
bash tools/smokes/v2/profiles/integration/apps/phase121_shadow_if_only_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase122_if_only_normalized_emit_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase123_if_only_normalized_semantics_vm.sh
Node Support Design
Return(Integer literal)
- Direct lowering to Const + Ret
- Single responsibility: literal → instruction generation
Return(Variable)
- Fail-Fast with structured error
- Clear migration path: Phase 124 reads facts
- No workarounds or fallbacks
If(cond_ast) - Minimal Compare
- Parse only simple binary comparisons
- Variable on left, Integer literal on right
- Explicit scope: no compound/complex expressions
- Merge via continuation:
join_k(env)tail-call
Success Criteria
- P0: Docs complete, scope frozen
- P1: Return(Integer literal) working with unit tests
- P2: Return(Variable) fails fast with hint
- P3: If(minimal compare) generates correct JoinIR
- P4: Integration smoke passing
- P5: Docs updated, Phase 123 recorded as complete
Progress
- 2025-12-18: P0 started (docs-only planning)