Skip to content

[codex] add logical OR support#57

Merged
thiremani merged 20 commits into
masterfrom
codex/add-logical-or-support
Jun 22, 2026
Merged

[codex] add logical OR support#57
thiremani merged 20 commits into
masterfrom
codex/add-logical-or-support

Conversation

@thiremani

Copy link
Copy Markdown
Owner

Summary

  • Add || as a logical OR operator distinct from bitwise |.
  • Support || in statement conditions with scalar I1 OR lowering.
  • Support value-position || as left-biased conditional fallback, including plain fallback values such as a < 5 || 10.

Validation

  • python3 test.py tests

thiremani added 13 commits May 3, 2026 23:13
Add || parsing and typing for statement conditions and value-position conditional expressions. In value position, logical OR now preserves Pluto's conditional value semantics by returning the first admitted value and supporting plain fallback values when the left condition fails.

Covers parser behavior, bitwise OR non-regression, scalar/function-argument fallback, ranged array fallback, and ranged condition gates in E2E tests.
Rename the OR fallback lowering path around its onTrue/onFalse contract, share scalar comparison extraction, and gate base conditions once before entering OR fallback selection.

Add E2E coverage for heap-string fallback, tuple fallback, and nested value-position OR expressions.
Lower boolean-position || through explicit branching instead of the eager scalar operator table. Tighten the conditional LHS binding invariant, move the i1 predicate alongside type helpers, and add condition-gate regression coverage for RHS skipping.
Add parser and E2E coverage for expressions where || appears in both the statement condition and value expression. Remove defensive internal panics that were not needed for normal logical OR lowering.
Document why value-position || accepts a conditional left operand with a fallback right operand, and why the language does not add a matching && form in this PR.
Rename ExprInfo.HasCondOr to HasFallbackOr so boolean logical OR dispatch reads in terms of fallback-OR semantics rather than the internal CondOr lowering mode.
Remove redundant arity panics from boolean logical OR lowering; the solver already rejects multi-output operands before compilation.
Remove a defensive nil check in fallback OR detection; compiler lowering already assumes typed expressions are present in ExprCache.
Add ast.IsLogicalOr and use it at AST-expression logical OR checks so compiler conditional lowering does not import token just to inspect operator spelling.
Use InfixExpression.IsLogicalOr when callers already have an infix node, while keeping ast.IsLogicalOr for arbitrary expression checks.
Collapse the WithFailure variant into compileCondExprBranch by making the false continuation explicit at each call site.
Rename extractCondExprSelf to extractInfixCondExpr to distinguish recursive expression-tree extraction from extracting one infix comparison node.
Rename conditional OR token/parser labels and shorten the fallback-yield lowering helpers so the control flow is easier to follow. Keep comments focused on ownership and value-position behavior rather than restating helper names.

@thiremani thiremani left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: logical OR support

Overview

This PR adds || as a logical OR operator distinct from bitwise |, in three roles:

  1. Statement conditions — short-circuit scalar i1 OR (x = a > 2 || b < 5 99)
  2. Value-position fallback — left-biased conditional value selection (x = b > 2 || d < 10)
  3. Plain fallback / default values — (x = b > 2 || -1), giving Pluto a default-value idiom

Are the changes useful/needed? Yes.

  • Before this PR Pluto had no OR at all for conditions — comma gives AND, but expressing "either condition" required restructuring into multiple statements. This is a genuine expressiveness gap being filled.
  • The value-position fallback semantics are a natural extension of Pluto's existing conditional-value model (CondScalar LHS extraction), and the "default value" form (|| -1) is a particularly useful idiom — see the LogicalOrArrayDefault test.
  • Short-circuiting is real, not just documented: LogicalOrStmtShortCircuit proves the RHS (an out-of-bounds index) is never evaluated when LHS is true. That makes || usable as a guard, which bitwise | could never be.
  • The deliberate omission of && is well-reasoned in the code comment (comma conditions already provide statement-position AND; chained comparisons refine values). Worth documenting in user-facing docs, since C-family users will look for it.

Verified during review

  • Lexer: no lexer change needed — readOperator consumes maximal operator runs, so || lexes as one token. Confirmed 6 | 3 regression test guards bitwise OR.
  • Precedence: COND_OR sits between COMMA and BITWISE_OR, matching conventional C precedence (|| lowest of the operators). Correct placement.
  • Arity safety: typeLogicalOrExpression indexes right[i] by len(left), but the pre-existing length-mismatch check in TypeInfixExpression runs first, so no panic path. Multi-value fallback (Pair(...) > Pair(...) || Pair(7, 8)) is covered by tests.
  • CI: both test jobs green.

Minor findings (none blocking)

  1. compileLogicalOrCondition (compiler/compiler.go): asymmetric freeTemporary. freeTemporary(expr.Left, left) is called only on the true path; on the RHS path the left operand's temporaries are never freed. This is harmless today because condition operands are type-constrained to i1 (never heap) — but by the same logic the true-path free is dead code. Suggest either removing it or freeing on both paths with a comment, so a future reader doesn't infer the wrong invariant.

  2. Code-size growth from continuation duplication. compileFallbackOr/compileYield compile the onTrue continuation once per success arm rather than merging arms with a phi before invoking it once. A chain a>1 || b>2 || c duplicates the consumer linearly (fine), but ORs in separate operands multiply: (b>2 || d<10) + (a>10 || c>2) compiles the add 4 times, and m OR-bearing operands give 2^m copies of the consumer. Realistic code won't hit this, but a phi-merge per OR (like compileLogicalOrCondition already does for conditions) would bound it if value-position ORs proliferate. Fine as a follow-up.

  3. Test coverage gap: no negative/type-error tests. All 26 new .spt cases are success paths. The new diagnostics are untested: non-i1 condition operands (1 || 2 in condition position), mismatched value types (a > 1 || "str"), and "logical OR in value position requires a conditional left operand" (5 || 10). Worth adding if there's an error-fixture mechanism.

  4. Nit: ast.IsLogicalOr (free function) and (*InfixExpression).IsLogicalOr (method) are near-duplicates; harmless, but the free function alone would suffice.

Code quality

The refactors are tasteful: compileCondExprBranchbranchCond gaining an onFalse hook is the minimal generalization needed, extractCondComparison is a clean extraction, and CondOr slots into the existing CondMode machinery rather than inventing a parallel path. The panic("internal: value-position logical OR must be lowered...") guard in compileInfixBasic is good defensive practice. Parser boundary handling (first || binds to the statement condition, second to the value) is subtle and well-tested.

Verdict: the feature is well-motivated and the implementation is solid. Recommend merging after considering the minor notes above (especially #3).

thiremani and others added 7 commits June 12, 2026 21:24
Add solver coverage for logical OR error paths and simplify boolean OR lowering by removing branch-local cleanup for i1 operands.
Split the token symbol grouping so conditional OR is not described as part of the bitwise operators.
Document how conditions behave by position: a statement gate keeps the
previous value on failure, while a value-position conditional is a
temporary that resolves to the zero value (or a || fallback) and composes
like a named temporary, so storing and inlining never differ.

Records the planned migration of nested value-position comparisons from
expression-wide propagation to local zero resolution, and delineates the
implemented surface from the designed (parenthesized explicit-value form,
per-cell array conditionals).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a worked contrast between `a > 3 b < 5 || 10` (gate plus value-position
fallback) and `(a > 3 b < 5) || 10` (one bracketed conditional), and note
that spacing splits array cells so brackets also control cell count.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the condition-position i1 validation into named bothResolved and
bothConditions locals and use the negated-conjunction form, so the check
reads as "both types known and not both conditions". Add a comment noting
the unresolved guard defers validation across solver passes. No behavior
change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The global/heap string flavor is an internal specialization detail. Make
StrG.String() and StrH.String() return the user-facing "Str" so type
mismatch and other diagnostics no longer leak StrG/StrH; the flavor is
still carried by Mangle() for specialization identity and by the
IsStrG/IsStrH predicates. Update the logical OR diagnostic test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`y > 2 3 || 7` is not "3 when y > 2, else 7" — at statement level `y > 2`
is the gate, leaving `3 || 7` as a value-position OR whose left operand `3`
cannot fail (a compile error). Replace with valid forms (`a > 2 || 7`, the
chain) and show that an explicit-value fallback needs parentheses,
`(a > 2 3) || 7`, with a note explaining the gate-vs-value distinction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiremani thiremani marked this pull request as ready for review June 22, 2026 07:40
@thiremani thiremani merged commit 5e52164 into master Jun 22, 2026
2 checks passed
@thiremani thiremani deleted the codex/add-logical-or-support branch June 22, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant