Files
hakorune/docs/development/current/main/phases/phase-123

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 7Const + 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 == Integer
  • Variable < Integer
  • Variable > Integer
  • Variable <= Integer
  • Variable >= Integer
  • Variable != 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")

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" )

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_ast as "minimal Compare"
      • Generate JoinIR Compare instruction
      • then/else: Return(Integer literal) only (other → strict freeze)
      • Merge: join_k(env) tail-call (no PHI)
    • Minimal parsing:
      • flag == 1 / i < 3 (Variable vs Integer literal)
      • Other (compound, &&/||, method call) → cap_missing strict freeze

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 section
  • docs/development/current/main/10-Now.md: Update to Phase 123 complete
  • docs/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)