Files
swift-mirror/test/Constraints/function_builder.swift
Pavel Yaskevich 65adc18139 [ConstraintSystem] Delay constraint generation from single-statement closure body
Attempt to infer a closure type based on its parameters and body
and put it aside until contextual type becomes available or it
has been determined that there is no context.

Once all appropriate conditions are met, generate constraints for
the body of the closure and bind it the previously inferred type.

Doing so makes it possible to pass single-statement closures as
an argument to a function builder parameter.

Resolves: SR-11628
Resolves: rdar://problem/56340587
2020-01-14 00:09:32 -08:00

427 lines
9.6 KiB
Swift

// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test
enum Either<T,U> {
case first(T)
case second(U)
}
@_functionBuilder
struct TupleBuilder {
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
return (t1, t2)
}
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
-> (T1, T2, T3) {
return (t1, t2, t3)
}
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
-> (T1, T2, T3, T4) {
return (t1, t2, t3, t4)
}
static func buildBlock<T1, T2, T3, T4, T5>(
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
) -> (T1, T2, T3, T4, T5) {
return (t1, t2, t3, t4, t5)
}
static func buildDo<T>(_ value: T) -> T { return value }
static func buildIf<T>(_ value: T?) -> T? { return value }
static func buildEither<T,U>(first value: T) -> Either<T,U> {
return .first(value)
}
static func buildEither<T,U>(second value: U) -> Either<T,U> {
return .second(value)
}
}
func tuplify<T>(_ 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.String, Swift.Double), (Swift.Double, Swift.String)>, 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<main.Either<(Swift.String, Swift.Int), (Swift.String, Swift.Int)>, main.Either<(Swift.String, Swift.Int), (Swift.String, Swift.Int)>>, main.Either<main.Either<(Swift.String, Swift.Int), (Swift.String, Swift.Int)>, (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<Tag, Entity> {
let tag: Tag
let entity: Entity
}
protocol Taggable {
}
extension Taggable {
func tag<Tag>(_ tag: Tag) -> Tagged<Tag, Self> {
return Tagged(tag: tag, entity: self)
}
}
extension Int: Taggable { }
extension String: Taggable { }
extension Double: Taggable { }
@_functionBuilder
struct TaggedBuilder<Tag> {
static func buildBlock() -> () { }
static func buildBlock<T1>(_ t1: Tagged<Tag, T1>) -> Tagged<Tag, T1> {
return t1
}
static func buildBlock<T1, T2>(_ t1: Tagged<Tag, T1>, _ t2: Tagged<Tag, T2>) -> (Tagged<Tag, T1>, Tagged<Tag, T2>) {
return (t1, t2)
}
static func buildBlock<T1, T2, T3>(_ t1: Tagged<Tag, T1>, _ t2: Tagged<Tag, T2>, _ t3: Tagged<Tag, T3>)
-> (Tagged<Tag, T1>, Tagged<Tag, T2>, Tagged<Tag, T3>) {
return (t1, t2, t3)
}
static func buildBlock<T1, T2, T3, T4>(_ t1: Tagged<Tag, T1>, _ t2: Tagged<Tag, T2>, _ t3: Tagged<Tag, T3>, _ t4: Tagged<Tag, T4>)
-> (Tagged<Tag, T1>, Tagged<Tag, T2>, Tagged<Tag, T3>, Tagged<Tag, T4>) {
return (t1, t2, t3, t4)
}
static func buildBlock<T1, T2, T3, T4, T5>(
_ t1: Tagged<Tag, T1>, _ t2: Tagged<Tag, T2>, _ t3: Tagged<Tag, T3>, _ t4: Tagged<Tag, T4>, _ t5: Tagged<Tag, T5>
) -> (Tagged<Tag, T1>, Tagged<Tag, T2>, Tagged<Tag, T3>, Tagged<Tag, T4>, Tagged<Tag, T5>) {
return (t1, t2, t3, t4, t5)
}
static func buildIf<T>(_ value: Tagged<Tag, T>?) -> Tagged<Tag, T>? { return value }
}
enum Color {
case red, green, blue
}
func acceptColorTagged<Result>(@TaggedBuilder<Color> body: () -> Result) {
print(body())
}
struct TagAccepter<Tag> {
static func acceptTagged<Result>(@TaggedBuilder<Tag> 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<Color>.acceptTagged {
i.tag(Color.red)
s.tag(Color.green)
d.tag(Color.blue)
}
// CHECK: Tagged<
TagAccepter<Color>.acceptTagged { () -> Tagged<Color, Int> 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())
}
acceptComponentBuilder {
"hello"
if true {
3.14159
}
.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)])
// 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"))
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
}