Add conditional expressions in value position#37
Open
Conversation
Comparisons inside the value side of let statements now act as conditional value extractors — returning the LHS operand when the comparison holds and skipping the entire statement when it fails. 🔧 How it works: x = a > 2 → x = a if a > 2; else skip (x = 0 if new) z = a > 2 + b < 10 → z = a + b if both hold; else skip res = f(i < 15) → call f(i) only when i < 15; else skip ⚡ Short-circuit cascade evaluation ensures later conditions are only evaluated when earlier ones pass. Undefined output variables are zero-initialized on skip (existing temp-slot seeding behaviour). 📝 Key changes: - solver.go: IsCondExpr flag on ExprInfo; InValueExpr context in TypeSolver so comparisons in value position return the LHS type instead of i1, while stmt.Condition comparisons remain unchanged. - cond.go: cascadeCondExprs emits cascading branches per comparison; compileCondExprStatement orchestrates temp slots, cascade, and assignment reusing the existing conditional infrastructure. - compiler.go: compileInfixExpression returns pre-extracted LHS values for conditional comparisons; compileLetStatement dispatches to the new path when embedded conditions are detected. 🧪 All 47 existing tests pass with zero regressions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l issues ## Changes ### Fix #1: Isolated cond-expr state per function context **Problem**: `condExprValues` was a shared compiler field that got clobbered when lazy function compilation occurred during cond-expr lowering. If a callee function had cond-exprs in its body, it would overwrite the caller's substitution map, causing the caller to see `nil` and fall through to normal comparison (producing i1). **Solution**: Moved LHS value storage from shared field to `ExprInfo.CondLHS`. Since `ExprCache` is keyed by `(FuncNameMangled, Expression)`, each function context has isolated entries—callee compilation can't clobber caller state. - Removed `condExprValues map[ast.Expression]*Symbol` from Compiler struct - Added `CondLHS *Symbol` to ExprInfo for per-expression LHS storage - Updated `cascadeCondExprs` to write to `info.CondLHS` - Updated `compileInfixExpression` to read from `info.CondLHS` - Removed init/cleanup of condExprValues from `compileCondExprStatement` ### Fix #2: Added RangeLiteral to cond-expr tree traversal **Problem**: `condExprChildren` didn't handle `*ast.RangeLiteral`, so comparisons nested in range bounds (e.g., `r = a > 3:10`) were invisible to `hasCondExprInTree` and `cascadeCondExprs`. Type solver marked them as extractors, but codegen emitted i1 comparisons instead of value extraction. **Solution**: Extended `condExprChildren` to return Start/Stop/Step expressions for RangeLiteral nodes. ### Test coverage - Added `CondDouble` function with cond-expr in body to cond_expr.pt - Added nested cond-expr test cases: function with cond-expr called from multi-target cond-expr context (`m, n = CondDouble(v > 0), v > 0`) - Verifies callee's cond-expr compilation doesn't corrupt caller's state All 47 tests pass with zero regressions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the multi-branch cascade approach with a simpler extract-and-AND architecture for conditional expression lowering: - extractCondExprs walks the expression tree, evaluates each comparison, ANDs all results into a single combined condition (including stmt conditions), and stores LHS values in ExprInfo.CondLHS for substitution - Single branch on the combined condition instead of N cascade branches - CondLHS changed from *Symbol to []*Symbol to support multi-return comparisons (element-wise AND, all-or-nothing skip) - Array types gracefully skipped in extraction (filtering deferred) - Removed hasStmtCond parameter since stmtCond is now naturally combined This is simpler (fewer basic blocks, no cascade state), produces equivalent results for scalar comparisons, and naturally extends to multi-return without additional branching complexity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…isons in value position Remove the single-result restriction from isCondExpr so multi-return comparisons like Pair(5,7) > Pair(1,2) use extraction semantics. Remove the compile error for array comparisons in value position; arrays now flow through normal infix as boolean arrays and are excluded from cond-expr tagging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… time Statement conditions must produce scalar i1 values for branching. Previously, array comparisons in condition position (e.g. x = a > b 5 where a,b are arrays) passed type-checking but produced invalid IR (br ptr). Now the solver emits a clear compile error instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements conditional expressions — comparisons in the value position of let statements that extract the LHS operand when the condition holds and skip the entire statement when it fails.
Semantics
x = a > 2→ ifa > 2, thenx = a; else skip (x = 0 if new variable)x = a > 2 + b < 10→x = a + bif both conditions hold; else skipres = f(i < 15)→ callf(i)only ifi < 15; else skipx = 5 > 3 a > 2→ statement condition AND embedded conditions both must holda + b > 3parses asa + (b > 3)→ returnsa + bifb > 32 < areturns 2 (always LHS operand)Implementation
solver.go): AddedInValueExprcontext flag andIsCondExprmetadata to distinguish value vs condition context, override comparison result types to LHS typecompiler.go): Dispatch tocompileCondExprStatementwhen embedded conditions detectedcond.go): Added cascade-then-compute lowering with pre-extracted LHS values stored inExprInfo.CondLHS(isolated per function via ExprKey)Test coverage
tests/cond_expr/with 24 test cases covering:All 47 existing tests pass with zero regressions.
Deferred
arr > 3→ element-wise filter) deferred for later🤖 Generated with Claude Code