mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Function builders: pre-check the original closure body in-place.
Prior to this patch, we pre-checked the result of applying the function-builder transformation, but only when we hadn't already pre-checked the closure before. This causes two problems to arise when the transformation is applied to the same closure along multiple branches of a disjunction. The first is that any expressions that are synthesized by the transformation will not be pre-checked the second time through, which is a problem if we try to apply different builder types to the same closure (we do cache expressions for identical builder types). The second is that the pre-check will rewrite sub-expressions in place *in the synthesized expression*, which means that top-level expressions in the original closure body (including `if` conditions) that are now nested in the synthesized expression will not be rewritten in the original closure and therefore will be encountered in their raw state the second time through. This patch causes all expressions in the original closure body to be pre-checked before doing any other work. We then pre-check the synthesized expression immediately before generating constraints for it in order to set up the AST appropriately for CSGen; this could be skipped if we just synthesized expressions the way that CSGen wants them, but that seems to be somewhat involved. Pre-checking is safe to apply to an expression multiple times, so it's fine if we take this path and then decide not to use a function builder. I've also merged the check for `return` statements into this same walk, which was convenient. Fixes rdar://53325810 at least, and probably also some bugs with applying different function builders to the same closure.
This commit is contained in:
@@ -301,3 +301,52 @@ func testAcceptColorTagged(b: Bool, i: Int, s: String, d: Double) {
|
||||
}
|
||||
|
||||
testAcceptColorTagged(b: true, i: 17, s: "Hello", d: 3.14159)
|
||||
|
||||
// rdar://53325810
|
||||
|
||||
// Test that we don't have problems with expression pre-checking when
|
||||
// type-checking an overloaded function-builder call. In particular,
|
||||
// we need to make sure that expressions in the closure are pre-checked
|
||||
// before we build constraints for them. Note that top-level expressions
|
||||
// that need to be rewritten by expression prechecking (such as the operator
|
||||
// sequences in the boolean conditions and statements below) won't be
|
||||
// rewritten in the original closure body if we just precheck the
|
||||
// expressions produced by the function-builder transformation.
|
||||
struct ForEach1<Data : RandomAccessCollection, Content> {
|
||||
var data: Data
|
||||
var content: (Data.Element) -> Content
|
||||
|
||||
func show() {
|
||||
print(content(data.first!))
|
||||
print(content(data.last!))
|
||||
}
|
||||
}
|
||||
extension ForEach1 where Data.Element: StringProtocol {
|
||||
// Checking this overload shouldn't trigger inappropriate caching that
|
||||
// affects checking the next overload.
|
||||
init(_ data: Data,
|
||||
@TupleBuilder content: @escaping (Data.Element) -> Content) {
|
||||
self.init(data: data, content: content)
|
||||
}
|
||||
}
|
||||
extension ForEach1 where Data == Range<Int> {
|
||||
// This is the overload we actually want.
|
||||
init(_ data: Data,
|
||||
@TupleBuilder content: @escaping (Int) -> Content) {
|
||||
self.init(data: data, content: content)
|
||||
}
|
||||
}
|
||||
let testForEach1 = ForEach1(-10 ..< 10) { i in
|
||||
"testForEach1"
|
||||
if i < 0 {
|
||||
"begin"
|
||||
i < -5
|
||||
} else {
|
||||
i > 5
|
||||
"end"
|
||||
}
|
||||
}
|
||||
testForEach1.show()
|
||||
|
||||
// CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.first("begin", true))
|
||||
// CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.second(true, "end"))
|
||||
|
||||
Reference in New Issue
Block a user