lowering: block-local functions carry their arity, so a partial application curries - #91
Merged
Merged
Conversation
…cation curries A function bound by a block-local `let` with parameters (or `let f = fun a b -> ...`) and then under-applied was emitted as a full Python call with too few arguments, passing `pyfun check` and dying at runtime with a TypeError. Arity came only from the module-level table, and a local name answered `None`. Add a block-scoped `local_arities` table kept in step with the fold pass's `local_fn_defs`: saved and restored per block (and around a CE body), displaced by parameters and match-arm binders, evicted by a non-function rebinding or a CE binder. A function `let` registers before its value lowers so a recursive local function can partially apply itself; a non-function `let` evicts after, since `let pair = pair 1` reads the previous binding. `lower_application` consults the table first, so both partial application (`functools.partial(pair, 10)`) and over-application (`k(1, 2)(x)`) of a local function now lower like a top-level one. Parameters stay n-ary.
simontreanor
added a commit
that referenced
this pull request
Aug 30, 2026
…after #90, #91 and #93 Resolves the overlap between the lookup peephole (#93) and the Option/Result ladder (#94): both hooks run in the match lowering, lookup first since it needs no Option at all; the two new list helpers return the _None_ singleton; tests whose scrutinee both passes rewrote now expect the combined output.
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.
What
A function bound by a block-local
letwith parameters, then applied to fewer (or more) arguments than it takes, now curries exactly like a top-level function:emits
_pf_map(functools.partial(pair, 10), ys)and prints[11, 12]. Before, the same program passedpyfun checkand died at runtime withTypeError: pair() missing 1 required positional argument, because lowering took arity only from the module-level table and a local name answered "unknown", which is emitted n-ary as written.How
A block-scoped
local_aritiestable lives beside the fold pass'slocal_fn_defsregistry and follows the same scope discipline, so an entry can only mean "this name resolves to that local function here":let f a b = …andlet f = fun a b -> …(parameter count of the binding or the lambda);save_local_scope/restore_local_scope), and around a computation expression body, which is its own Python function;shadow_local_fns/unshadow_local_fns, now carrying both registries);letor a CE binder rebinds the name.One ordering detail: a function
letregisters its arity before its value is lowered, so a recursive local function can partially apply itself; a non-functionletevicts after its value is lowered, sincelet pair = pair 1reads the previous binding.lower_applicationconsultslocal_aritiesfirst. Parameters have no entry and stay n-ary, so nothing changes for a callee whose arity really is unknown. Over-application of a local function (k 1 2 xwith a 2-arykreturning a function) now emitsk(1, 2)(x)instead of a three-argument call.Tests
tests/compile.rs: the issue's program (string-level and end-to-end), the lambda spelling, over-application, a parameter shadowing the local function, a match-arm binder shadowing it, a recursive local function partially applying itself, and a non-function rebinding evicting the arity.Docs: the
src/lowering/mod.rsheader comment and a new "Arity for the currying lowering" section inINTERNALS.md.Closes #84