refactor(joinir): Phase 255 P2 - loop_invariants + var() unification + Phase 256 prep
## Task A: Loop Invariants Architecture (Option A - Boundary Extension) Introduced explicit loop_invariants concept for variables that are: - Used in loop body but never modified (e.g., substring needle in index_of) - Need header PHI (all iterations use same value) - Do NOT need exit PHI (not a LoopState) Implementation: - Added `loop_invariants: Vec<(String, ValueId)>` field to JoinInlineBoundary - Added `with_loop_invariants()` method to JoinInlineBoundaryBuilder - Modified Pattern 6 to use loop_invariants instead of ConditionOnly misuse * s (haystack) and ch (needle) now properly classified * exit_bindings simplified to only LoopState carriers - Extended LoopHeaderPhiBuilder to generate invariant PHIs * Entry PHI: from host * Latch PHI: self-reference (same value every iteration) - Updated instruction_rewriter to handle invariant latch incoming Files Modified: - src/mir/join_ir/lowering/inline_boundary.rs (structure + builder) - src/mir/join_ir/lowering/inline_boundary_builder.rs (builder method) - src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs - src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs - src/mir/builder/control_flow/joinir/merge/mod.rs - src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs ## Task B: Code Quality - var() Helper Unification Created common module to eliminate var() duplication: - New module: src/mir/builder/control_flow/joinir/patterns/common/ - Centralized var() helper in ast_helpers.rs - Updated Pattern 6 and Pattern 2 to use common var() - Test code (5 occurrences) deferred to Phase 256+ per 80/20 rule Result: Eliminated 2 duplicate var() functions, 5 test occurrences remain Files Created: - src/mir/builder/control_flow/joinir/patterns/common/ast_helpers.rs - src/mir/builder/control_flow/joinir/patterns/common/mod.rs Files Modified: - src/mir/builder/control_flow/joinir/patterns/mod.rs - src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs - src/mir/builder/control_flow/joinir/patterns/policies/balanced_depth_scan_policy.rs ## Task C: Documentation - PostLoopEarlyReturnPlan Usage Examples Enhanced post_loop_early_return_plan.rs with: - Architecture explanation (exit PHI usage prevents DCE) - Pattern 2 example (Less: balanced_depth_scan) - Pattern 6 example (NotEqual: index_of) - Builder defer decision: when 4+ patterns emerge, add builder Files Modified: - src/mir/builder/control_flow/joinir/patterns/policies/post_loop_early_return_plan.rs ## Task D: Phase 256 Preparation Analyzed next failure from smoke tests: - `StringUtils.split/2` with variable-step loop - Not constant step (i += len or similar) - Three implementation options identified Created Phase 256 README with: - Minimal reproduction code - Root cause analysis - Implementation options with trade-offs - Clear next steps Files Created: - docs/development/current/main/phases/phase-256/README.md ## Verification Results ✅ All tests passing: - pattern254_p0_index_of_vm.sh: PASS - No regression in Pattern 1-5 - Smoke test progresses past json_lint_vm to next failure ✅ Architecture achievements: - Semantic clarity: loop_invariants vs exit_bindings properly separated - ConditionOnly misuse eliminated - PHI generation correct for all carrier types - Code quality: var() duplication reduced - Next phase clearly defined ## Summary Phase 255 P2 achieves root-cause fix for multi-param loop support: - Boundary concept expanded (loop_invariants field) - Pattern 6 architecture corrected (no ConditionOnly misuse) - Code quality improved (var() centralized) - Next pattern (variable-step) is now the challenge ✨ Box-first principles maintained: clean separation, explicit roles, minimal coupling 🧠 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -6,6 +6,7 @@
|
||||
//! - Fail-fast with tagged reasons when the shape is close but unsupported
|
||||
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::common::var; // Phase 255 P2: Use shared var() helper
|
||||
use crate::mir::join_ir::lowering::common::balanced_depth_scan_emitter::BalancedDepthScanRecipe;
|
||||
use crate::mir::join_ir::lowering::error_tags;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs};
|
||||
@ -449,12 +450,7 @@ fn is_var_plus_int(node: &ASTNode, var_name: &str, n: i64) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
fn var(name: &str) -> ASTNode {
|
||||
ASTNode::Variable {
|
||||
name: name.to_string(),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
// Phase 255 P2: var() function removed - now using crate::mir::builder::control_flow::joinir::patterns::common::var
|
||||
|
||||
fn eq_str(left: ASTNode, s: &str) -> ASTNode {
|
||||
ASTNode::BinaryOp {
|
||||
|
||||
@ -1,15 +1,67 @@
|
||||
//! Post-loop early return plan (policy-level SSOT)
|
||||
//! Phase 255 P2: Post-loop early return plan (policy-level SSOT)
|
||||
//!
|
||||
//! # Responsibility
|
||||
//!
|
||||
//! Responsibility:
|
||||
//! - Describe a post-loop guard that emulates an in-loop `return` without making
|
||||
//! Pattern2 lowering itself return-in-loop aware.
|
||||
//! - Keep the plan policy-agnostic so multiple Pattern2 families can reuse it.
|
||||
//!
|
||||
//! # Architecture
|
||||
//!
|
||||
//! Ensures exit PHI values are used (prevents DCE elimination). The post-loop
|
||||
//! guard creates a conditional that references the exit PHI value, forcing it
|
||||
//! to be live and preventing dead code elimination.
|
||||
//!
|
||||
//! # Usage Patterns
|
||||
//!
|
||||
//! ## Pattern 2: Less Than (balanced_depth_scan)
|
||||
//!
|
||||
//! Used in `json_cur.find_balanced_*` family functions.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! PostLoopEarlyReturnPlan {
|
||||
//! cond: BinaryOp { Less, var("i"), var("n") },
|
||||
//! ret_expr: var("i"),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Generated post-loop guard:
|
||||
//! ```nyash
|
||||
//! if i < n {
|
||||
//! return i
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Pattern 6: Not Equal (index_of)
|
||||
//!
|
||||
//! Used in `StringUtils.index_of` and similar search functions.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! PostLoopEarlyReturnPlan {
|
||||
//! cond: BinaryOp { NotEqual, var("i"), Literal(-1) },
|
||||
//! ret_expr: var("i"),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Generated post-loop guard:
|
||||
//! ```nyash
|
||||
//! if i != -1 {
|
||||
//! return i
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Builder Decision
|
||||
//!
|
||||
//! Deferred to Phase 256+. Currently 2 patterns use this (Pattern 2 and Pattern 6).
|
||||
//! Direct construction is acceptable. Will create builder when 4+ patterns use it.
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PostLoopEarlyReturnPlan {
|
||||
/// Condition for the post-loop guard (e.g., `i < n` or `i != -1`)
|
||||
pub cond: ASTNode,
|
||||
/// Expression to return if condition is true (e.g., `var("i")`)
|
||||
pub ret_expr: ASTNode,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user