Skip to content

fix: preserve boolean checks through TCO#817

Merged
stephenamar-db merged 1 commit intodatabricks:masterfrom
He-Pin:fix/tco-tail-position-correctness
May 1, 2026
Merged

fix: preserve boolean checks through TCO#817
stephenamar-db merged 1 commit intodatabricks:masterfrom
He-Pin:fix/tco-tail-position-correctness

Conversation

@He-Pin
Copy link
Copy Markdown
Contributor

@He-Pin He-Pin commented May 1, 2026

Motivation

Recursive calls in tail-position && / || rhs can return through the TailCall trampoline. Before this change, direct non-boolean rhs values were rejected, but non-boolean values produced after resolving a TailCall could skip the normal boolean operator result check.

The obvious fix would be to call TailCall.resolve(tc).asBoolean inside visitAndRhsTailPos / visitOrRhsTailPos. That path is intentionally avoided: nested resolution re-enters the trampoline from inside the current function body, can restore stack growth for deep && / || chains, and can move the lazy evaluation boundary.

Modification

  • Carry && / || result validation as delayed TailCall state and apply it only when the outer TailCall.resolve loop produces the final value.
  • Keep auto-TCO arguments under TailstrictModeAutoTCO; the helper only attaches a delayed check and does not force nested TailCalls.
  • Add regression files for recursive TailCall rhs values resolving to non-booleans through both && and ||.

Result

  • Deep boolean auto-TCO chains remain stack-safe.
  • Lazy argument semantics are preserved; unused error arguments through both && and || still stay unevaluated.
  • Recursive rhs values that resolve to non-booleans now report the same type errors as direct true && 0 / false || 0 expressions.

Verification:

  • ./mill -i __.test (SUCCESS, 176s)
  • ./mill -i __.checkFormat (SUCCESS, 6s)
  • git diff --check

Risk

The main risk is that a future cleanup replaces the delayed check with immediate nested TailCall resolution. This PR documents the reason in code comments near both the && / || tail helpers and TailCall.BooleanCheck.

Error-value tail propagation is intentionally left as-is in this PR. The existing auto_tco_directional.jsonnet fixture entry is restored, but it is not asserted today; real coverage for that path should be handled in a follow-up so this PR stays focused on boolean operator correctness.

@He-Pin He-Pin force-pushed the fix/tco-tail-position-correctness branch 2 times, most recently from 8afc6d3 to 711e0e6 Compare May 1, 2026 04:04
Motivation:

The straightforward fix would be to resolve a TailCall inside visitAndRhsTailPos/visitOrRhsTailPos and then call asBoolean. That path was intentionally avoided: nested resolution re-enters the trampoline from inside the current function body, can restore stack growth for deep &&/|| chains, and can move the lazy evaluation boundary. We still need recursive TailCall rhs values to obey normal boolean operator result checks.

Modification:

Carry &&/|| result validation as delayed TailCall state and apply it only when the outer TailCall.resolve loop produces the final value. Keep auto-TCO arguments under TailstrictModeAutoTCO and add non-boolean TailCall rhs regression tests for both && and ||.

Result:

Deep boolean auto-TCO chains remain stack-safe and lazy, while recursive rhs values that resolve to non-booleans now report the same type errors as direct true && 0 / false || 0 expressions.

Risk:

The main risk is accidentally replacing this with immediate nested resolution later. Code comments now document why the delayed check lives on TailCall instead of resolving inside the &&/|| helpers. Error-value tail propagation is intentionally left as-is in this PR; the existing directional fixture is restored but remains a non-asserted coverage gap for a follow-up.
@He-Pin He-Pin force-pushed the fix/tco-tail-position-correctness branch from 711e0e6 to aeb1ec4 Compare May 1, 2026 04:13
@stephenamar-db stephenamar-db merged commit 44b3c6f into databricks:master May 1, 2026
4 of 5 checks passed
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.

2 participants