Repository navigation
Defunctionalization: normalize aggregate closure capture environments - #3907
Draft
Ian Davis (idavis) wants to merge 1 commit into
Conversation
Ian Davis (idavis)
added this pull request to stack #3892
October 7, 2026 17:32
This branch has not been deployed
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.
Summary
The branch fixes bugs where aggregate closure captures could be hidden, reordered, read too late, reconstructed incorrectly, or inconsistently expanded across closure occurrences. It safely flattens eligible local tuples and UDTs into capture leaves while preserving snapshots, nominal identity, argument order, nested closures, functors, and cross-package correctness.
This branch fixes defunctionalization bugs involving closures that capture aggregates containing callable values, such as tuples, nested tuples, and local UDTs.
1. Callable values hidden inside aggregate captures
A callable nested inside a tuple or UDT could remain opaque to defunctionalization.
The branch exposes eligible aggregate fields as separate capture parameters, allowing the nested callable to be analyzed and specialized.
2. Closure and lifted-target signature mismatches
Expanding an aggregate capture changes the lifted target’s input layout. If closure occurrences retain the old layout, arguments can be shifted or mistyped.
Every closure occurrence sharing the lifted target must use the same expanded capture layout.
3. Capture snapshots changing after reassignment
Aggregate fields must be captured when the closure is created, not read when it is later invoked.
The branch projects aggregate leaves into immutable locals at closure creation.
4. Reordered or repeated field evaluation
Normalizing an environment must not replay or reorder the expressions that produced its fields.
first,captured, andlastmust each be evaluated once and in source order.5. Lost tuple shape or UDT identity
Flattening a captured UDT into leaves must not replace the UDT with an anonymous tuple inside the lifted body.
The branch reconstructs local UDTs through their original constructors.
6. Incorrect flattening of arrays
Callable arrays are runtime containers and cannot be expanded into a fixed number of capture parameters.
The branch keeps arrays opaque, preserving indexing and each element’s distinct capture environment.
7. Broken nested closure recaptures
An inner closure may recapture an aggregate from an outer lifted closure. Expanding only the outer capture can leave the inner closure referring to a removed aggregate local.
The branch forwards the outer closure’s replacement leaf captures into the inner closure.
8. Incompatible normalization across shared targets
Multiple closure instances can share one lifted target while capturing different environments.
The branch updates every occurrence consistently or declines normalization when it cannot safely update the shared target.
9. Capture and ordinary-argument slot reordering
Expanded capture leaves must remain separate from the lambda’s ordinary parameters.
The branch preserves depth-first field order, unaffected captures, and ordinary argument positions.
10. Controlled and adjoint calls receiving corrupted environments
Captured operation values and their associated data must remain correctly positioned under functor application.
The branch preserves the captured angle, operation value, ordinary target argument, and control register across body, adjoint, controlled, and controlled-adjoint forms.
11. Cross-package aggregate captures losing nominal ownership
A foreign UDT captured beside a callable must retain its original package-qualified identity.
The branch keeps the foreign UDT opaque while normalizing the callable sibling.