diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 9547f4c3a06cd..c4c77a0ffa10a 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1094,6 +1094,13 @@ bool BindingSet::favoredOverConjunction(Constraint *conjunction) const { if (locator->directlyAt()) { auto *closure = castToExpr(locator->getAnchor()); + // If there are no bindings for the closure yet we cannot prioritize + // it because that runs into risk of missing a result builder transform. + if (TypeVar->getImpl().isClosureType()) { + if (Bindings.empty()) + return false; + } + if (auto transform = CS.getAppliedResultBuilderTransform(closure)) { // Conjunctions that represent closures with result builder transformed // bodies could be attempted right after their resolution if they meet diff --git a/test/Constraints/issue67363.swift b/test/Constraints/issue67363.swift new file mode 100644 index 0000000000000..b767bf646d729 --- /dev/null +++ b/test/Constraints/issue67363.swift @@ -0,0 +1,85 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking + +// https://github.com/apple/swift/issues/67363 + +protocol UIView { + init() +} + +class UILabel : UIView { + required init() {} +} + +class UIStackView : UIView { + required init() {} +} + +protocol ViewRepresentable { + associatedtype View: UIView + func configure(view: View) +} + +struct StyledString: ViewRepresentable { + let content: String + func configure(view: UILabel) {} +} + +class StackViewOne: UIStackView { + var first = First() +} + +struct Stack { + struct One: ViewRepresentable { + let first: First + func configure(view: StackViewOne) { + first.configure(view: view.first) + } + } + + @resultBuilder + enum Builder { + static func buildBlock(_ first: First) -> Stack.One { + Stack.One(first: first) + } + } + + static func vertical(@Builder build builder: () -> StackType) -> StackType { + builder() + } +} + +struct ListItem { + let body: any ViewRepresentable +} + +@resultBuilder +enum ListBuilder { + static func buildExpression(_ expression: View?) -> [ListItem?] { + [expression.map { .init(body: $0) }] + } + + static func buildBlock(_ components: [ListItem?]...) -> [ListItem] { + components.flatMap { $0.compactMap { $0 } } + } +} + +struct WithFooter: ViewRepresentable { + let body: T + let footer: () -> [ListItem] + func configure(view: T.View) {} +} + +extension ViewRepresentable { + func withFooter(@ListBuilder build: @escaping () -> [ListItem]) -> WithFooter { + .init(body: self, footer: build) + } +} + +func testThatResultBuilderIsAppliedToWithFooterArgument() -> some ViewRepresentable { + Stack.vertical() { + StyledString(content: "vertical") + } + .withFooter { + StyledString(content: "footer") + } +} diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index af4b1dd33c4f4..71b3987832876 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -694,3 +694,27 @@ func test_recursive_var_reference_in_multistatement_closure() { } } } + +// https://github.com/apple/swift/issues/67363 +func test_result_builder_in_member_chaining() { + @resultBuilder + struct Builder { + static func buildBlock(_: T) -> Int { 42 } + } + + struct Test { + static func test(fn: () -> T) -> T { + fn() + } + + func builder(@Builder _: () -> Int) {} + } + + Test.test { + let test = Test() + return test + }.builder { // Ok + let result = "" + result + } +}