Custard: fold projections of a let-bound constructor - #4553
Merged
Merged
Conversation
gebner
force-pushed
the
gebner_custard_letpatfold
branch
from
September 19, 2026 00:00
1ece7e1 to
c01be8c
Compare
gebner
enabled auto-merge
September 19, 2026 00:37
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Sep 19, 2026
Fixes #4548. `let (a, (b, _)) = p` in an inlined callee left the caller building a tuple, naming it and reading it back out. The rewrites were already there -- `iota` on a `match` over a constructor, `unbuild` on a projection out of one -- but F* puts a binding in front of the scrutinee, so neither ever sees a constructor. `reduce` now substitutes a let-bound constructor when every occurrence is destructed on the spot and every field is `reeval`; `unbuild` does the same for the projection-only shape that `depat` leaves behind. `reeval` is bounded, allocation-free and safe to repeat: variables, constants, projections, casts, operators, and constructors over them. A call or an allocation keeps its binding. Documented in section 129. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
gebner
force-pushed
the
gebner_custard_letpatfold
branch
from
September 19, 2026 02:01
c01be8c to
b9cadbb
Compare
gebner
enabled auto-merge
September 19, 2026 02:26
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.
Fixes #4548.
let (a, (b, _)) = pin aninline_for_extractioncallee left the caller building a tuple, naming it and reading it back out:Now it is
return (x + (x + 1));.The rewrites were already there --
iotaon amatchover a constructor,unbuildon a projection out of one -- but F* names the scrutinee first (let _letpattern = Mktuple2 ... in match _letpattern with ...), so neither ever sees a constructor.reduce'sELetcase substitutes a let-bound constructor when every occurrence is destructed on the spot (the scrutinee of amatch, or the target of a projection) and every field isreeval.unbuilddoes the same for the projection-only shape thatdepatleaves behind, usingpsubplus a guard that the body does not rebind the name.reevalis the condition the immediate case never needed: a field that is read moves, possibly more than once and possibly into a loop, so it is restricted to the bounded, allocation-free class -- variables, constants, projections, casts, coercions, tag reads, operators, and constructors over them. A call or an allocation keeps its binding, which is the issue's own caveat about discarded components. Constructors have to be in it or the rule does not fire on the nested tuple it was written for.No declared type changes: the one-field struct a
unittail leaves is still declared, it is just no longer built.Documented in section 129 of
doc/ref/custard.md. New regression testtests/custard/pulse/LetPatFold.fst. The full gate is green and no goldens moved.