feat(optimizer): collapse Boolean pattern matches to a two-way if (#223) - #290
Merged
Conversation
A pattern match on a Boolean compiled to a three-way if with a
synthesized default: the scrutinee re-tested behind the first branch
(elseif false == a) and a dead error("No patterns matched") arm,
although true/false already cover the type. No existing rule had a
handle on the shape: the equality fold knew only the left-literal
true == b case, and the unreachable-branch rules need a literal
condition.
Two additions reduce it to if a then ... else ... end. The
boolean-equality fold in constantFolding is completed (false == b and
b == false fold to not b, b == true to b), and the new
propagateKnownCondIntoBranches rule — the Boolean sibling of
propagateKnownCtorThroughLet — substitutes a variable condition's
known value into its branches: c is true throughout the then branch
and false throughout the else branch, so the re-test folds to a
literal condition and the existing rules drop the dead default. The
rule runs after removeIfWithEqualBranches so equal branches still
collapse to one copy, fires only on a variable condition (re-reading
an immutable binding is free), and leaves no free occurrence behind,
so it cannot re-fire on its own result.
Unit tests pin both folds, the end-to-end collapse, and the guards: a
genuinely partial match keeps its default, and only a re-test of the
same variable folds. The elseif false == idiom disappears from every
golden that carried it, and the collapsed show bodies now clear the
call-site inline budget — in the NumberIsNaN golden that cascade
dead-codes the whole Ring dictionary. Eval goldens are unchanged.
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.
Closes #223.
What
A pattern match on a
Booleancompiled to a three-wayifwith a synthesized, unreachable default — the shape every inlinedshoworlogShowof a boolean produces:ais aBoolean, sotrue/falsealready cover it, but no existing rule had a handle on the shape:constantFoldingknew only the left-literaltrue == bcase, and the unreachable-branch rules need a literal condition. It now reduces to:How
Both pieces proposed in the issue, reusing the existing folds for the actual collapse:
The boolean-equality fold in
constantFoldingis completed.false == bandb == falsefold tonot b, andb == truefolds tob, mirroring the existing left-literaltrue == bcase (sound per Note [IR is assumed well-typed]).New rule
propagateKnownCondIntoBranches— the Boolean sibling ofpropagateKnownCtorThroughLet, the more general of the issue's two alternatives. Insideif c then t else ea variable conditioncistruethroughouttandfalsethroughoute, so its free occurrences in the branches are replaced with the matching literal; the existing folds then finish the job:The rule fires only on a variable condition (re-reading an immutable binding is free and yields the value the test just observed), leaves no free occurrence behind so it cannot re-fire on its own result, and never grows the tree (a
Refbecomes a literal node-for-node). It is placed afterremoveIfWithEqualBranchesin the rewrite chain: branches that are equal while still namingccollapse to a single copy, which substituting the two literals first would unequalize — a unit test pins this ordering.Tests
Written test-first (red before the fix): unit tests pin the three new equality folds, the end-to-end three-way→two-way collapse, then-branch propagation (
if a then a else e→a or e), and the guards — a genuinely partial match keeps its default, and a guard testing a different variable keeps theerrorarm. The randomized IR specs pass across 5 extra seeds.Goldens
The
elseif false ==idiom disappears from every golden that carried it (9 sites across 6 goldens at currentmain; the issue counted 16 against an older corpus). Sample fromGolden.RecursiveBindings.Test:The collapse also cascades: the shrunk boolean
showbodies now clear the call-site inline budget, sologShowpastes at its call sites in the GenericEq/NumberIsNaN goldens, and inGolden.NumberIsNaN.Testthat dead-codes the wholeRingdictionary:Golden.GenericEqTwoTypes.Testkeeps its 4 remainingerror("No patterns matched")defaults — those are constructor-tag matches on closed sums, explicitly out of scope per the issue. Eval goldens are unchanged, which is the semantic safety net: runtime output is identical, only the shape moved.