## Phase 146 P0: ANF Routing SSOT Unified **Goal**: Unify ANF routing in `lower_expr_with_scope()` L54-84, remove legacy lowering **Changes**: - expr_lowerer_box.rs: Added scope check (PureOnly → skip ANF, WithImpure → try ANF) - post_if_post_k.rs: Removed legacy inline lowering (L271-285), added `lower_condition_legacy()` helper - contract.rs: Already had `CondLoweringFailed` out-of-scope reason **Test Results**: ✅ Phase 146 P0 smoke (exit 7), 0 regressions ## Phase 146 P1: Compare Operator Support **Goal**: Enable ANF for condition expressions with Compare operators **Changes**: - joinir_dev.rs: Added `anf_allow_pure_enabled()` (HAKO_ANF_ALLOW_PURE=1) - expr_lowerer_box.rs: PureOnly scope ANF support (L56-66) - execute_box.rs: Compare operator support (+122 lines) - `execute_compare_hoist()`, `execute_compare_recursive()`, `ast_compare_to_joinir()` - Extended `normalize_and_lower()` for Compare **Test Results**: ✅ Phase 146 P1 smoke (exit 7 with flags), 0 regressions ## Phase 147 P0: Recursive Comparison ANF **Goal**: Extend recursive ANF to Compare operators **Changes**: - contract.rs: Added `AnfParentKind::Compare` variant - plan_box.rs: Compare case in BinaryOp routing (L68-79, L134-139) - Distinguishes Compare vs arithmetic BinaryOp **Benefits**: Enables recursive ANF for comparisons - `s.length() == 3` → `t = s.length(); if (t == 3)` ✅ - `s1.length() < s2.length()` → `t1 = s1.length(); t2 = s2.length(); if (t1 < t2)` ✅ ## Implementation Summary **Files Modified** (9 files, +253 lines, -25 lines = +228 net): 1. src/config/env/joinir_dev.rs (+28 lines) 2. src/mir/control_tree/normalized_shadow/anf/contract.rs (+2 lines) 3. src/mir/control_tree/normalized_shadow/anf/execute_box.rs (+122 lines) 4. src/mir/control_tree/normalized_shadow/anf/plan_box.rs (+18 lines) 5. src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs (+18 lines, -0 lines) 6. src/mir/control_tree/normalized_shadow/post_if_post_k.rs (+44 lines, -25 lines) 7. CURRENT_TASK.md 8. docs/development/current/main/10-Now.md 9. docs/development/current/main/30-Backlog.md **Files Created** (7 files): - apps/tests/phase146_p0_if_cond_unified_min.hako - apps/tests/phase146_p1_if_cond_intrinsic_min.hako - tools/smokes/.../phase146_p0_if_cond_unified_vm.sh - tools/smokes/.../phase146_p0_if_cond_unified_llvm_exe.sh - tools/smokes/.../phase146_p1_if_cond_intrinsic_vm.sh - tools/smokes/.../phase146_p1_if_cond_intrinsic_llvm_exe.sh - docs/development/current/main/phases/phase-146/README.md **Acceptance Criteria**: ✅ All met - cargo build --release: PASS (0 errors, 0 warnings) - Phase 145 regressions: PASS (exit 12, 18, 5) - Phase 146 P0: PASS (exit 7) - Phase 146 P1: PASS (exit 7 with HAKO_ANF_ALLOW_PURE=1) **Architecture**: - SSOT: ANF routing only in `lower_expr_with_scope()` L54-84 - Box-First: Phase 145 `anf/` module extended - Legacy removed: post_if_post_k.rs unified with SSOT 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
298 lines
9.7 KiB
Rust
298 lines
9.7 KiB
Rust
//! JoinIR development / experimental flags (SSOT).
|
||
//! Phase 72-C: Consolidate all NYASH_JOINIR_* dev flags through centralized helpers.
|
||
//! Phase 45: JoinIR mode unification (StructuredOnly / NormalizedDev / NormalizedCanonical)
|
||
|
||
use crate::config::env::env_bool;
|
||
|
||
/// Phase 45: JoinIR execution mode enum
|
||
///
|
||
/// Centralizes all JoinIR routing decisions (Structured vs Normalized paths).
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum JoinIrMode {
|
||
/// Default mode: Structured→MIR direct (no Normalized layer)
|
||
StructuredOnly,
|
||
|
||
/// Development mode: Structured→Normalized→MIR(direct) for supported shapes
|
||
/// Requires `--features normalized_dev` + env var
|
||
NormalizedDev,
|
||
|
||
/// Future mode: All canonical shapes use Normalized→MIR(direct)
|
||
/// Currently unused, reserved for Phase 46+ canonical migration
|
||
NormalizedCanonical,
|
||
}
|
||
|
||
/// NYASH_JOINIR_LOWER_GENERIC=1 - Enable generic lowering path for JoinIR
|
||
/// (CRITICAL: 15 occurrences in codebase)
|
||
pub fn lower_generic_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_LOWER_GENERIC")
|
||
}
|
||
|
||
/// NYASH_JOINIR_MAINLINE_DEBUG=1 - Debug output for mainline JoinIR lowering
|
||
pub fn mainline_debug_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_MAINLINE_DEBUG")
|
||
}
|
||
|
||
/// NYASH_JOINIR_IF_MERGE=1 - Enable If-merge experimental mode
|
||
pub fn if_merge_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_IF_MERGE")
|
||
}
|
||
|
||
/// NYASH_JOINIR_DEBUG=1 - General debug mode (deprecated, prefer mainline_debug)
|
||
pub fn debug_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_DEBUG")
|
||
}
|
||
|
||
/// NYASH_JOINIR_VM_BRIDGE=1 - Enable VM bridge mode
|
||
pub fn vm_bridge_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_VM_BRIDGE")
|
||
}
|
||
|
||
/// HAKO_JOINIR_STRICT=1 or NYASH_JOINIR_STRICT=1 - Strict validation mode
|
||
///
|
||
/// Phase 138-P2-A: Supports both HAKO_ and NYASH_ prefixes for compatibility
|
||
pub fn strict_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_STRICT") || env_bool("NYASH_JOINIR_STRICT")
|
||
}
|
||
|
||
/// NYASH_JOINIR_SNAPSHOT_GENERATE=1 - Generate snapshot for testing
|
||
pub fn snapshot_generate_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_SNAPSHOT_GENERATE")
|
||
}
|
||
|
||
/// NYASH_JOINIR_SNAPSHOT_TEST=1 - Test using snapshot
|
||
pub fn snapshot_test_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_SNAPSHOT_TEST")
|
||
}
|
||
|
||
/// NYASH_JOINIR_INPUT=* - Input source or mode
|
||
pub fn input_mode() -> Option<String> {
|
||
std::env::var("NYASH_JOINIR_INPUT").ok()
|
||
}
|
||
|
||
/// NYASH_JOINIR_LOWER_FROM_MIR=1 - Enable lowering from MIR mode
|
||
pub fn lower_from_mir_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_LOWER_FROM_MIR")
|
||
}
|
||
|
||
/// NYASH_JOINIR_LLVM_EXPERIMENT=1 - LLVM experimental mode
|
||
pub fn llvm_experiment_enabled() -> bool {
|
||
env_bool("NYASH_JOINIR_LLVM_EXPERIMENT")
|
||
}
|
||
|
||
/// HAKO_JOINIR_IF_TOPLEVEL=1 - Enable If-select for top-level if statements
|
||
pub fn if_toplevel_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_IF_TOPLEVEL")
|
||
}
|
||
|
||
/// HAKO_JOINIR_IF_TOPLEVEL_TRACE=1 - Debug trace for top-level if
|
||
pub fn if_toplevel_trace_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_IF_TOPLEVEL_TRACE")
|
||
}
|
||
|
||
/// HAKO_JOINIR_IF_IN_LOOP_TRACE=1 - Debug trace for if in loop
|
||
pub fn if_in_loop_trace_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_IF_IN_LOOP_TRACE")
|
||
}
|
||
|
||
/// HAKO_JOINIR_NESTED_IF=1 - Enable nested if lowering
|
||
pub fn nested_if_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_NESTED_IF")
|
||
}
|
||
|
||
/// HAKO_JOINIR_PRINT_TOKENS_MAIN=1 - Print tokens for main
|
||
pub fn print_tokens_main_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_PRINT_TOKENS_MAIN")
|
||
}
|
||
|
||
/// HAKO_JOINIR_ARRAY_FILTER_MAIN=1 - Array filter main mode
|
||
pub fn array_filter_main_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_ARRAY_FILTER_MAIN")
|
||
}
|
||
|
||
/// HAKO_JOINIR_READ_QUOTED=1 - Read quoted mode
|
||
pub fn read_quoted_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_READ_QUOTED")
|
||
}
|
||
|
||
/// HAKO_JOINIR_READ_QUOTED_IFMERGE=1 - Read quoted with if-merge
|
||
pub fn read_quoted_ifmerge_enabled() -> bool {
|
||
env_bool("HAKO_JOINIR_READ_QUOTED_IFMERGE")
|
||
}
|
||
|
||
/// NYASH_JOINIR_NORMALIZED_DEV_RUN=1 - Run JoinIR runner through Normalized roundtrip (dev only)
|
||
pub fn joinir_normalized_dev_run_enabled() -> bool {
|
||
// Feature gate at call sites still applies; this helper just centralizes the env read.
|
||
env_bool("NYASH_JOINIR_NORMALIZED_DEV_RUN")
|
||
}
|
||
|
||
/// Phase 45: Get current JoinIR execution mode
|
||
///
|
||
/// Determines routing based on feature flags and environment variables:
|
||
/// - `--features normalized_dev` + `NYASH_JOINIR_NORMALIZED_DEV_RUN=1` → NormalizedDev
|
||
/// - Otherwise → StructuredOnly
|
||
///
|
||
/// Note: NormalizedCanonical is reserved for future canonical migration (Phase 46+)
|
||
pub fn current_joinir_mode() -> JoinIrMode {
|
||
#[cfg(feature = "normalized_dev")]
|
||
{
|
||
if joinir_normalized_dev_run_enabled() {
|
||
JoinIrMode::NormalizedDev
|
||
} else {
|
||
JoinIrMode::StructuredOnly
|
||
}
|
||
}
|
||
|
||
#[cfg(not(feature = "normalized_dev"))]
|
||
{
|
||
JoinIrMode::StructuredOnly
|
||
}
|
||
}
|
||
|
||
/// Unified switch for Normalized dev experiments (feature + env).
|
||
///
|
||
/// - Requires `--features normalized_dev`
|
||
/// - Requires `NYASH_JOINIR_NORMALIZED_DEV_RUN=1`
|
||
///
|
||
/// Phase 45: Now implemented as a thin wrapper over current_joinir_mode()
|
||
pub fn normalized_dev_enabled() -> bool {
|
||
matches!(current_joinir_mode(), JoinIrMode::NormalizedDev)
|
||
}
|
||
|
||
/// JOINIR_TEST_DEBUG=1 (or NYASH_JOINIR_TEST_DEBUG=1) - Verbose logging for normalized dev tests
|
||
pub fn joinir_test_debug_enabled() -> bool {
|
||
env_bool("JOINIR_TEST_DEBUG") || env_bool("NYASH_JOINIR_TEST_DEBUG")
|
||
}
|
||
|
||
/// Phase 82: NYASH_PHI_FALLBACK_DISABLED=1 - Disable if_phi fallback (dev mode)
|
||
///
|
||
/// lifecycle.rs の infer_type_from_phi* callsite を封じて、
|
||
/// 実際に呼ばれているかどうかを確認するためのフラグ。
|
||
///
|
||
/// # 使用方法
|
||
///
|
||
/// ```bash
|
||
/// NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release mir_joinir_if_select
|
||
/// ```
|
||
///
|
||
/// # 期待される動作
|
||
///
|
||
/// - callsite が呼ばれる → panic (関数名・ValueId・Case を出力)
|
||
/// - callsite が一度も呼ばれない → テスト成功(削除候補確定)
|
||
///
|
||
/// # Case 分類
|
||
///
|
||
/// - **Case A**: P1/P2/P3-A/P3-B + hint 成功(hint 即座に返す)
|
||
/// - **Case B**: P1/P2/P3-A/P3-B + hint 失敗(PHI 走査)
|
||
/// - **Case D**: P3-C + GenericTypeResolver 失敗(PHI 走査・無駄な再実行)
|
||
///
|
||
/// Phase 84-5: if_phi.rs 削除後は常に true
|
||
/// 環境変数は統計目的でのみ残す
|
||
pub fn phi_fallback_disabled() -> bool {
|
||
true // Phase 84-5: Always disabled after if_phi.rs deletion
|
||
}
|
||
|
||
/// Phase 84-5: 統計・メトリクス用(オプション)
|
||
#[allow(dead_code)]
|
||
pub fn phi_metrics_enabled() -> bool {
|
||
env_bool("NYASH_PHI_METRICS")
|
||
}
|
||
|
||
/// Phase 183: NYASH_LEGACY_LOOPBUILDER=1 - Legacy LoopBuilder 経路を明示的に opt-in
|
||
///
|
||
/// デフォルトはJoinIR優先。どうしても古いLoopBuilder経路を使う必要がある場合のみ設定。
|
||
/// 本線では使用しない開発専用フラグ。
|
||
pub fn legacy_loopbuilder_enabled() -> bool {
|
||
env_bool("NYASH_LEGACY_LOOPBUILDER")
|
||
}
|
||
|
||
/// Phase 145 P0: HAKO_ANF_DEV=1 - ANF (A-Normal Form) transformation development mode
|
||
///
|
||
/// Enables ANF transformation routing in NormalizedExprLowererBox.
|
||
/// P0: Debug logging only (execute_box is stub, returns Ok(None)).
|
||
/// P1+: Actual transformation (String.length() hoist, compound expression ANF).
|
||
///
|
||
/// # Usage
|
||
///
|
||
/// ```bash
|
||
/// HAKO_ANF_DEV=1 cargo test --release
|
||
/// HAKO_ANF_DEV=1 ./target/release/hakorune program.hako
|
||
/// ```
|
||
///
|
||
/// # Expected Behavior (P0)
|
||
///
|
||
/// - ANF routing enabled: AnfPlanBox detects impure expressions
|
||
/// - Debug log: "[phase145/debug] ANF plan found but execute returned None (P0 stub)"
|
||
/// - Existing behavior unchanged: execute_box returns Ok(None) → fallback to legacy
|
||
///
|
||
/// # Future Behavior (P1+)
|
||
///
|
||
/// - String.length() hoist: `x + s.length()` → ANF transformation
|
||
/// - Compound expression ANF: Recursive left-to-right linearization
|
||
pub fn anf_dev_enabled() -> bool {
|
||
env_bool("HAKO_ANF_DEV")
|
||
}
|
||
|
||
/// Phase 145 P2: ANF strict mode (fail-fast on violations)
|
||
///
|
||
/// When enabled, ANF transformation errors result in immediate failure
|
||
/// instead of graceful fallback to legacy lowering.
|
||
///
|
||
/// # Environment Variable
|
||
///
|
||
/// `HAKO_ANF_STRICT=1` enables strict mode (default: OFF)
|
||
///
|
||
/// # Behavior
|
||
///
|
||
/// - **ON**: ANF violations return Err() with detailed error tags
|
||
/// - **OFF**: ANF violations gracefully fallback to legacy lowering (Ok(None))
|
||
///
|
||
/// # Use Cases
|
||
///
|
||
/// - **Development**: Catch order violations, pure-required violations early
|
||
/// - **Testing**: Verify ANF transformation correctness with fail-fast
|
||
/// - **Production**: Keep OFF for backward compatibility
|
||
///
|
||
/// # Example
|
||
///
|
||
/// ```bash
|
||
/// # Strict mode: Fail on `f() + g()` without ANF
|
||
/// HAKO_ANF_STRICT=1 ./hakorune program.hako
|
||
/// # Error: [joinir/anf/order_violation] f() + g(): both calls not hoisted
|
||
///
|
||
/// # Graceful mode (default): Fallback to legacy
|
||
/// ./hakorune program.hako
|
||
/// # OK: Legacy lowering used
|
||
/// ```
|
||
pub fn anf_strict_enabled() -> bool {
|
||
env_bool("HAKO_ANF_STRICT")
|
||
}
|
||
|
||
/// Phase 146 P1: HAKO_ANF_ALLOW_PURE=1 - Allow ANF in PureOnly scope (dev-only)
|
||
///
|
||
/// Enables ANF transformation for PureOnly expression scopes (e.g., loop/if conditions).
|
||
/// Requires HAKO_ANF_DEV=1 to be set as well.
|
||
///
|
||
/// # Environment Variable
|
||
///
|
||
/// `HAKO_ANF_ALLOW_PURE=1` enables PureOnly ANF routing (default: OFF)
|
||
///
|
||
/// # Behavior
|
||
///
|
||
/// - **OFF (P0)**: ANF only for WithImpure scope (compound assignments)
|
||
/// - **ON (P1)**: ANF also for PureOnly scope (loop/if conditions with Compare)
|
||
///
|
||
/// # Use Cases
|
||
///
|
||
/// - **P1**: Enable `if (s.length() == 3)` ANF transformation
|
||
/// - **P147**: Enable `if (s1.length() < s2.length())` compound condition ANF
|
||
///
|
||
/// # Example
|
||
///
|
||
/// ```bash
|
||
/// # Enable condition ANF
|
||
/// HAKO_ANF_DEV=1 HAKO_ANF_ALLOW_PURE=1 ./hakorune program.hako
|
||
/// ```
|
||
pub fn anf_allow_pure_enabled() -> bool {
|
||
env_bool("HAKO_ANF_ALLOW_PURE")
|
||
}
|