-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ResultBuilders] buildBlock(combining:into:)
for pairwise combination.
#40799
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
Conversation
buildBlock(joining:_:)
for pairwise combination.buildBlock(joining:_:)
for pairwise combination.
35afa20
to
6d87816
Compare
buildBlock(joining:_:)
for pairwise combination.buildBlock(_combining:into:)
for pairwise combination.
6d87816
to
8a329e4
Compare
To reviewers: If you agree this is the right direction, I'll go ahead and add more tests and hopefully more diagnostics. |
@swift-ci Please Build Toolchain macOS Platform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation looks really good. Approve from the PR-is-good-once-it-has-more-tests standpoint, but needs evolution.
lib/Sema/BuilderTransform.cpp
Outdated
// combining: arg_1, into: Builder.buildBlock(arg_0))... | ||
call = buildCallIfWanted( | ||
braceStmt->getStartLoc(), ctx.Id_buildBlock, {expressions.front()}, | ||
/*argLabels=*/{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be cleaner to say that we call buildBlock()
initially to get the first call
, then the expressions (if there are any) are combined with buildBlock(_combining:into:)
. That way, we don't need the expressions.empty()
check in the if
statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we called the nullary buildBlock()
as a base case, the result builder would have to to support an empty block. Should the developer be allowed to define a DSL that doesn't support empty blocks?
This idea has been around for quite a while.... see https://forums.swift.org/t/function-builders/25167/311. |
Cool. It now looks like the pairwise formulation is beyond a workaround for variadic generics. |
This is something that I'm concerned about with this changes, but I haven't had a chance to look at the PR yet. We need to set it up in a way where the chain builds up incrementally and doesn't need to re-typecheck its previous components otherwise we'd end up with giant constraint system which solver wouldn't be able to handle... |
@xedin does breaking it up to multiple statements sound good to you? let v0 = Builder.buildBlock(arg_0)
let v1 = Builder.buildBlock(_combining: arg_1, into: v0)
...
let vn = Builder.buildBlock(_combining: arg_n, into: ...)
return vn |
@rxwei Yeah, that sounds good. |
@DougGregor Regarding your suggestion to call nullary let v1 = Builder.buildBlock(_combining: arg_1, into: buildBlock()) Which approach is better? To me it seems like calling unary |
9ab4201
to
9c84d35
Compare
@swift-ci please build toolchain macOS platform |
macOS Toolchain Install command |
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
9c84d35
to
c821717
Compare
buildBlock(_combining:into:)
for pairwise combination.buildBlock(_combining:into:)
for pairwise combination.
I ran into a strange SILGen crash when generating additional let bindings and thought it's easier to add one-way constraints in between subexpressions, so I did that instead. This made swiftlang/swift-experimental-string-processing#114 compile in totally reasonable time. |
Allow a user-defined `buildBlock(combining:into:)` to combine subexpressions in a block pairwise top to bottom. To use `buildBlock(_combining:into:)`, the user also needs to provide a unary `buildBlock(_:)` as a base case. The feature is being gated under frontend flag `-enable-experimental-pairwise-build-block`. This will enable use cases in `RegexBuilder` in experimental declarative string processing, where we need to concatenate tuples and conditionally skip captureless regexes. For example: ```swift let regex = Regex { "a" // Regex<Substring> OneOrMore("b").capture() // Regex<(Substring, Substring)> "c" // Regex<Substring> Optionally("d".capture()) // Regex<(Substring, Substring?)> } // Regex<Tuple3<Substring, Substring, Substring?>> let result = "abc".firstMatch(of: regex) // MatchResult<(Substring, Substring, Substring?)> ``` In this example, patterns `"a"` and `"c"` have no captures, so we need to skip them. However with the existing result builder `buildBlock()` feature that builds a block wholesale from all subexpressions, we had to generate `2^arity` overloads accounting for any occurrences of captureless regexes. There are also other complexities such as having to drop-first from the tuple to obtain the capture type. Though these features could in theory be supported via variadic generics, we feel that allowing result builders to pairwise combine subexpressions in a block is a much simpler and potentially more useful approach. With `buildBlock(_combining:into:)`, the regex builders can be defined as the following, assuming we have variadic generics: ```swift enum RegexBuilder { static func buildBlock() -> Regex<Substring> static func buildBlock<Match>(_ x: Regex<Match>) -> Regex<Match> static func buildBlock< ExistingWholeMatch, NewWholeMatch, ExistingCaptures..., NewCaptures... >( _combining next: Regex<(NewWholeMatch, NewCaptures...)>, into combined: Regex<(ExistingWholeMatch, ExistingCaptures...)> ) -> Regex<Substring, ExistingCaptures..., NewCaptures...> } ``` Before we have variadic generics, we can define overloads of `buildBlock(_combining:into:)` for up to a certain arity. These overloads will be much fewer than `2^arity`.
@swift-ci please test |
c821717
to
b6e679f
Compare
Build failed |
buildBlock(_combining:into:)
for pairwise combination.buildBlock(combining:into:)
for pairwise combination.
@swift-ci please benchmark |
Performance (x86_64): -O
Code size: -OPerformance (x86_64): -Osize
Code size: -OsizePerformance (x86_64): -OnoneCode size: -swiftlibsHow to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
@swift-ci please smoke test compiler performance |
Oh, I triggered the wrong benchmark. |
!!! Couldn't read commit file !!! |
@swift-ci |
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time, and fixes an issue where nested captures did not have a flattened type. Before this patch, fixing this would need `O(arity!)` overloads. This also allows us to switch back to native tuples for `Match`.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time, and fixes an issue where nested captures did not have a flattened type. Before this patch, fixing this would need `O(arity!)` overloads. This also allows us to switch back to native tuples for `Match`.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time, and fixes an issue where nested captures did not have a flattened type. Before this patch, fixing this would need `O(arity!)` overloads. This also allows us to switch back to native tuples for `Match`.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time, and fixes an issue where nested captures did not have a flattened type. Before this patch, fixing this would need `O(arity!)` overloads. This also allows us to switch back to native tuples for `Match`.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time, and fixes an issue where nested captures did not have a flattened type. Before this patch, fixing this would need `O(arity!)` overloads. This also allows us to switch back to native tuples for `Match`. Also, rename `renderAsPattern` to `renderAsBuilderDSL` to work around a CI stale file issue.
…o:)`. This patch replaces `O(2^arity)` overloads of `buildBlock` with `O(arity^2)` overloads of `buildBlock(combining:into:)` with the language feature implemented in swiftlang/swift#40799. This improves library compilation time and fixes an issue where nested captures did not have a flattened type. This also allows us to switch back to native tuples for `Match`, which will be addressed in a follow-up patch.
Allow a user-defined
buildBlock(combining:into:)
to combine subexpressions in a block pairwise top to bottom. The feature is being gated under frontend flag-enable-experimental-pairwise-build-block
. To usebuildBlock(combining:into:)
, the user also needs to provide a unarybuildBlock(_:)
as a base case. WhenbuildBlock(combining:into:)
is provided, a block will be transformed to the following:This will enable use cases in
RegexBuilder
in experimental declarative string processing, where we need to concatenate tuples and conditionally skip captureless regexes. For example:In this example, patterns
"a"
and"c"
have no captures, so we need to skip their capture types in the resulting generic parameter type rather than record them as()
. However with the existing result builderbuildBlock()
feature that builds a block wholesale from all subexpressions, we had to generate2^arity
overloads accounting for any occurrences of captureless regexes. There are also other complexities such as having to drop-first from the tuple to obtain the capture type. Though these features could in theory be supported via variadic generics, we feel that allowing result builders to pairwise combine subexpressions in a block is a much simpler and potentially more useful approach.With
buildBlock(combining:into:)
, the regex builders can be defined as the following, assuming we have variadic generics:Before we have variadic generics, we can define overloads of
buildBlock(combining:into:)
for up to a certain arity. These overloads will be much fewer than2^arity
.