// RUN: %target-run-simple-swift | %FileCheck %s // REQUIRES: executable_test enum Either { case first(T) case second(U) } @_functionBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) } static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) } static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) -> (T1, T2, T3) { return (t1, t2, t3) } static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) -> (T1, T2, T3, T4) { return (t1, t2, t3, t4) } static func buildBlock( _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 ) -> (T1, T2, T3, T4, T5) { return (t1, t2, t3, t4, t5) } static func buildDo(_ value: T) -> T { return value } static func buildIf(_ value: T?) -> T? { return value } static func buildEither(first value: T) -> Either { return .first(value) } static func buildEither(second value: U) -> Either { return .second(value) } } func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { print(body(cond)) } // CHECK: (17, 3.14159, "Hello, DSL", (["nested", "do"], 6), Optional((2.71828, ["if", "stmt"]))) let name = "dsl" tuplify(true) { 17 3.14159 "Hello, \(name.map { $0.uppercased() }.joined())" do { ["nested", "do"] 1 + 2 + 3 } if $0 { 2.71828 ["if", "stmt"] } } // CHECK: ("Empty optional", nil) tuplify(false) { "Empty optional" if $0 { 2.71828 ["if", "stmt"] } } // CHECK: ("chain0", main.Either<(Swift.String, Swift.Double), (Swift.Double, Swift.String)>.second(2.8, "capable")) tuplify(false) { "chain0" if $0 { "marginal" 2.9 } else { 2.8 "capable" } } // CHECK: ("chain1", nil) tuplify(false) { "chain1" if $0 { "marginal" 2.9 } else if $0 { 2.8 "capable" } } // CHECK: ("chain2", Optional(main.Either<(Swift.String, Swift.Double), (Swift.Double, Swift.String)>.first("marginal", 2.9))) tuplify(true) { "chain2" if $0 { "marginal" 2.9 } else if $0 { 2.8 "capable" } } // CHECK: ("chain3", main.Either, main.Either<(Swift.Double, Swift.Double), (Swift.String, Swift.String)>>.first(main.Either<(Swift.String, Swift.Double), (Swift.Double, Swift.String)>.first("marginal", 2.9))) tuplify(true) { "chain3" if $0 { "marginal" 2.9 } else if $0 { 2.8 "capable" } else if $0 { 2.8 1.0 } else { "wild" "broken" } } // CHECK: ("chain4", main.Either, main.Either<(Swift.String, Swift.Int), (Swift.String, Swift.Int)>>, main.Either, (Swift.String, Swift.Int)>>.first tuplify(true) { "chain4" if $0 { "0" 0 } else if $0 { "1" 1 } else if $0 { "2" 2 } else if $0 { "3" 3 } else if $0 { "4" 4 } else if $0 { "5" 5 } else { "6" 6 } } // rdar://50710698 // CHECK: ("chain5", 8, 9) tuplify(true) { "chain5" #if false 6 $0 #else 8 9 #endif } // CHECK: ("getterBuilder", 0, 4, 12) @TupleBuilder var globalBuilder: (String, Int, Int, Int) { "getterBuilder" 0 4 12 } print(globalBuilder) // CHECK: ("funcBuilder", 13, 45.0) @TupleBuilder func funcBuilder(d: Double) -> (String, Int, Double) { "funcBuilder" 13 d } print(funcBuilder(d: 45)) struct MemberBuilders { @TupleBuilder func methodBuilder(_ i: Int) -> (String, Int) { "methodBuilder" i } @TupleBuilder static func staticMethodBuilder(_ i: Int) -> (String, Int) { "staticMethodBuilder" i + 14 } @TupleBuilder var propertyBuilder: (String, Int) { "propertyBuilder" 12 } } // CHECK: ("staticMethodBuilder", 27) print(MemberBuilders.staticMethodBuilder(13)) let mbuilders = MemberBuilders() // CHECK: ("methodBuilder", 13) print(mbuilders.methodBuilder(13)) // CHECK: ("propertyBuilder", 12) print(mbuilders.propertyBuilder) // SR-11439: Operator builders infix operator ^^^ func ^^^ (lhs: Int, @TupleBuilder rhs: (Int) -> (String, Int)) -> (String, Int) { return rhs(lhs) } // CHECK: ("hello", 6) print(5 ^^^ { "hello" $0 + 1 }) struct Tagged { let tag: Tag let entity: Entity } protocol Taggable { } extension Taggable { func tag(_ tag: Tag) -> Tagged { return Tagged(tag: tag, entity: self) } } extension Int: Taggable { } extension String: Taggable { } extension Double: Taggable { } @_functionBuilder struct TaggedBuilder { static func buildBlock() -> () { } static func buildBlock(_ t1: Tagged) -> Tagged { return t1 } static func buildBlock(_ t1: Tagged, _ t2: Tagged) -> (Tagged, Tagged) { return (t1, t2) } static func buildBlock(_ t1: Tagged, _ t2: Tagged, _ t3: Tagged) -> (Tagged, Tagged, Tagged) { return (t1, t2, t3) } static func buildBlock(_ t1: Tagged, _ t2: Tagged, _ t3: Tagged, _ t4: Tagged) -> (Tagged, Tagged, Tagged, Tagged) { return (t1, t2, t3, t4) } static func buildBlock( _ t1: Tagged, _ t2: Tagged, _ t3: Tagged, _ t4: Tagged, _ t5: Tagged ) -> (Tagged, Tagged, Tagged, Tagged, Tagged) { return (t1, t2, t3, t4, t5) } static func buildIf(_ value: Tagged?) -> Tagged? { return value } } enum Color { case red, green, blue } func acceptColorTagged(@TaggedBuilder body: () -> Result) { print(body()) } struct TagAccepter { static func acceptTagged(@TaggedBuilder body: () -> Result) { print(body()) } } func testAcceptColorTagged(b: Bool, i: Int, s: String, d: Double) { // FIXME: When we support buildExpression, drop the "Color" prefix // CHECK: Tagged< acceptColorTagged { i.tag(Color.red) s.tag(Color.green) d.tag(Color.blue) } // FIXME: When we support buildExpression, drop the "Color" prefix // CHECK: Tagged< TagAccepter.acceptTagged { i.tag(Color.red) s.tag(Color.green) d.tag(Color.blue) } // CHECK: Tagged< TagAccepter.acceptTagged { () -> Tagged in if b { return i.tag(Color.green) } else { return i.tag(Color.blue) } } } testAcceptColorTagged(b: true, i: 17, s: "Hello", d: 3.14159) // Use buildExpression() when it's available. enum Component { case string(StaticString) case floating(Double) case color(Color) indirect case array([Component]) indirect case optional(Component?) } @_functionBuilder struct ComponentBuilder { static func buildExpression(_ string: StaticString) -> Component { return .string(string) } static func buildExpression(_ float: Double) -> Component { return .floating(float) } static func buildExpression(_ color: Color) -> Component { return .color(color) } static func buildBlock(_ components: Component...) -> Component { return .array(components) } static func buildIf(_ value: Component?) -> Component { return .optional(value) } } func acceptComponentBuilder(@ComponentBuilder _ body: () -> Component) { print(body()) } func colorWithAutoClosure(_ color: @autoclosure () -> Color) -> Color { return color() } var trueValue = true acceptComponentBuilder { "hello" if trueValue { 3.14159 colorWithAutoClosure(.red) } .red } // CHECK: array([main.Component.string("hello"), main.Component.optional(Optional(main.Component.array([main.Component.floating(3.14159), main.Component.color(main.Color.red)]))), main.Component.color(main.Color.red)]) // 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 { 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 { // 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")) func test_single_stmt_closure_support() { @_functionBuilder struct MyBuilder { static func buildBlock(_ numbers: Int...) -> Int { return 42 } } func test(@MyBuilder builder: () -> Int) -> Int { builder() } let _ = test { 0 } // ok } // Check a case involving nested closures that refer to parameters of their // enclosing closures. struct X { init(_ c: C, @TupleBuilder body: (C.Element) -> T) { } } struct Y { init(@TupleBuilder body: () -> T) { } } struct Z { init(@TupleBuilder body: () -> T) { } } func testNestedClosuresWithDependencies(cond: Bool) { tuplify(cond) { _ in X([1, 2, 3]) { x in Y { Z { x 1 } } } } } // Check that we can handle multiple conditions in an 'if' statement. func testIfConditions(cond: Bool, c1: Bool, i1: Int, i2: Int) { tuplify(cond) { x in "testIfConditions" if i1 == i2, c1, x { 1 "hello" } 3.14159 } } testIfConditions(cond: true, c1: true, i1: 1, i2: 1) // CHECK: testIfConditions // CHECK-SAME: hello // Use a "let" declaration within a function builder. tuplify(true) { c in "testLetDeclarations" let (a, b) = (c, c && true) if a == b { "hello" b } a } // CHECK: testLetDeclarations" // CHECK-SAME: hello // CHECK-SAME: true // Use if let / if case with various forms of decomposition. func getOptionalInt(_: Bool) -> Int? { return 25 } enum E { case a case b(Int, String?) } func getE(_ i: Int) -> E { switch i { case 0: return .a case 1: return .b(17, "hello") case 2: return .b(42, nil) default: fatalError("Unhandled case") } } tuplify(true) { c in "testIfLetMatching" if let theValue = getOptionalInt(c) { theValue + 17 } if case let .a = getE(0) { "matched without payload" } if case let .b(i, s?) = getE(1) { "matched with payload" s + "!" i + 17 } if case let .b(i, s?) = getE(2) { fatalError("cannot match this") } else { "intentional mismatch" } } // CHECK: testIfLetMatching // CHECK-SAME: Optional(42) // CHECK-SAME: Optional("matched without payload") // CHECK-SAME: "matched with payload", "hello!", 34 // CHECK-SAME: "intentional mismatch" class Super { } class Sub : Super { func subMethod() -> String { return "subMethod" } } func getSuper(wantSubclass: Bool) -> Super { return wantSubclass ? Sub() : Super() } tuplify(true) { c in "testIfLetAsMatching" if case let sub as Sub = getSuper(wantSubclass: true) { sub.subMethod() } if case let sub as Sub = getSuper(wantSubclass: false) { fatalError("cannot match this") } else { "Superclass instance" } } // CHECK: testIfLetAsMatching // CHECK-SAME: "subMethod" // CHECK-SAME: "Superclass instance" // switch statements func testSwitch(_ e: E) { tuplify(true) { c in "testSwitch" switch e { case .a: "a" case .b(let i, let s?): i * 2 s + "!" case .b(let i, nil): "just \(i)" } } } // CHECK: testSwitch // CHECK-SAME: first(main.Either.first("a")) testSwitch(getE(0)) // CHECK: testSwitch // CHECK-SAME: first(main.Either.second(34, "hello!")) testSwitch(getE(1)) // CHECK: testSwitch // CHECK-SAME: second("just 42") testSwitch(getE(2)) func testSwitchCombined(_ eIn: E) { var e = eIn tuplify(true) { c in "testSwitchCombined" switch e { case .a: "a" case .b(let i, _?), .b(let i, nil): "just \(i)" } } } // CHECK: testSwitchCombined // CHECK-SAME: main.Either.first("a") testSwitchCombined(getE(0)) // CHECK: testSwitchCombined // CHECK-SAME: second("just 17") testSwitchCombined(getE(1)) // CHECK: testSwitchCombined // CHECK-SAME: second("just 42") testSwitchCombined(getE(2)) // Test buildOptional(_:) as an alternative to buildIf(_:). @_functionBuilder struct TupleBuilderWithOpt { static func buildBlock(_ t1: T1) -> (T1) { return (t1) } static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) } static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) -> (T1, T2, T3) { return (t1, t2, t3) } static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) -> (T1, T2, T3, T4) { return (t1, t2, t3, t4) } static func buildBlock( _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 ) -> (T1, T2, T3, T4, T5) { return (t1, t2, t3, t4, t5) } static func buildDo(_ value: T) -> T { return value } static func buildOptional(_ value: T?) -> T? { return value } static func buildEither(first value: T) -> Either { return .first(value) } static func buildEither(second value: U) -> Either { return .second(value) } } func tuplifyWithOpt(_ cond: Bool, @TupleBuilderWithOpt body: (Bool) -> T) { print(body(cond)) } tuplifyWithOpt(true) { c in "1" 3.14159 }