-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Substitute pack expansions by repeatedly substituting the pattern type #64578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
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
@swift-ci Please smoke test |
d1b941f
to
a142bbe
Compare
There are a lot of problems caused by our highly-abstract substitution subsystem. Most of them would be solved by a more semantic / holistic understanding of the active transformation, but that's difficult to do because we just pass around function_refs. The first step in fixing that is to pass around a better currency type. For now, it can just hold the function_refs (and the SubstOptions). I've set it up so that the places that just apply SubstitutionMaps are constructing the IFS in a standard way; that should make it easy to change those places in the future.
a142bbe
to
1266ca0
Compare
@swift-ci Please test |
InFlightSubstitution
type through the substitution subsystemSubstitution of a pack expansion type may now produce a pack type. We immediately expand that pack when transforming a tuple, a function parameter, or a pack. I had to duplicate the component-wise transformation logic in the simplifyType transform, which I'm not pleased about, but a little code duplication seemed a lot better than trying to unify the code in two very different places. I think we're very close to being able to assert that pack expansion shapes are either pack archetypes or pack parameters; unfortunately, the pack matchers intentionally produce expansions of packs, and I didn't want to add that to an already-large patch.
1266ca0
to
251c089
Compare
@swift-ci Please test |
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.
The existing type substitution logic performs pack expansion during substitution by applying a series of transforms:
repeat<T> (each T) -> Bool
might becomerepeat<Pack{Int, repeat<U> each U}> (each Pack{Int, repeat<U> each U}) -> Bool
.repeat<Pack{Int, repeat<U> each U}> Pack{(Int) -> Bool, repeat (each U) -> Bool}
.repeat
in our example above was a singleton tuple element, the tuple would become((Int) -> Bool, repeat<U> (each U) -> Bool)
, which is the correct final type.This approach works, and I think it could still be made to work in the future where our representation properly supports nested expansions with
PackElementType
. However, it has several flaws. First, it is a pretty intricate dance, and if the inputs are wrong (e.g. you set up a substitution incorrectly), the dance can fail in inexplicable ways. Second, it relies on pack types being temporary artifacts in the representation; if the second step ever sees a pack that is not a temporary artifact of this dance, it is likely to misbehave. Third, it temporarily constructs types that would otherwise be invalid, which means we cannot easily catch bugs by asserting that the representation is well-formed in ways that only apply to the "steady state", e.g. that types in certain positions are never pack types. Fourth, it generates a fair number of "garbage" types that will be unused after the traversal, which increases memory usage. And finally, when we move toPackElementType
, we will need to understand when we're substituting inside of a pack expansion anyway in order to adjust the replacement type, destroying any simplicity benefits of the representation.Instead, we need to handle pack expansions specially in substitution and recurse separately for each substituted component of the shape. Note that we recognize two kinds of pack reference: pack references that occur in the shape of a pack expansion, which always refer to the full pack, and pack references that occur in patterns of pack expansions, which refer to the current element of that expansion. This will become more explicit when we introduce
PackElementType
, which represents the latter.There were a couple places that were already using this new approach before this PR. When they needed to substitute a type (an unlowered one in the latter case), they passed functions down to the normal AST type substitution logic which projected the current component. This has a basic conceptual problem in that it cannot handle nested expansions correctly that occur entirely outside of the substituter. I ran into this in the SIL type substituter, but it could also have happened with conformance packs. The two approaches do not play nicely with each other, and expanding the AST expansion approach to SIL types would not have worked because SIL type substitution is inherently position-sensitive: we need to lower type substitutions that appear in lowered-type positions, but we cannot lower them in formal-type positions. Therefore we must change the existing AST substitution logic to start doing element-wise substitution. This requires preserving information throughout substitution about both the original pack substitution and the currently-selected element for a particular expansion.
Because the old expansion code also has to be replaced, type simplification in the constraint system needs to use similar element-wise resolution.