mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
This introduces support for converting a Swift closure that captures variables from its surrounding context into an instance of `std::function`, which is useful for working with C++ APIs that use callbacks. Each instantiation of `std::function` gets a synthesized Swift constructor that takes a Swift closure. Unlike the previous implementation, the closure is _not_ marked as `@convention(c)`. The body of the constructor is created lazily. Under the hood, the closure is bitcast to a pair of a function pointer and a context pointer, which are then wrapped in a C++ object, `__SwiftFunctionWrapper`, that manages the lifetime of the context object via calls to `swift_retain`/`swift_release` from the copy constructor and the destructor. The `__SwiftFunctionWrapper` class is templated, and is instantiated by ClangImporter. rdar://133777029
313 lines
9.8 KiB
Swift
313 lines
9.8 KiB
Swift
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
|
|
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=swift-6)
|
|
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift)
|
|
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++14)
|
|
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++17)
|
|
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++20)
|
|
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
import StdFunction
|
|
import CxxStdlib
|
|
|
|
var StdFunctionTestSuite = TestSuite("StdFunction")
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt.init()") {
|
|
let f = FunctionIntToInt()
|
|
expectTrue(isEmptyFunction(f))
|
|
|
|
let copied = f
|
|
expectTrue(isEmptyFunction(copied))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt.callAsFunction") {
|
|
let f = getIdentityFunction()
|
|
expectEqual(123, f(123))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt retrieve and pass back as parameter") {
|
|
let res = invokeFunction(getIdentityFunction(), 456)
|
|
expectEqual(456, res)
|
|
}
|
|
|
|
#if !os(Windows) // FIXME: rdar://103979602
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from closure and call") {
|
|
let cClosure: @convention(c) (Int32) -> Int32 = { $0 + 1 }
|
|
let f = FunctionIntToInt(cClosure)
|
|
expectEqual(1, f(0))
|
|
expectEqual(124, f(123))
|
|
expectEqual(0, f(-1))
|
|
|
|
let f2 = FunctionIntToInt({ $0 * 2 })
|
|
expectEqual(0, f2(0))
|
|
expectEqual(246, f2(123))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from closure and pass as parameter") {
|
|
let res = invokeFunction(.init({ $0 * 2 }), 111)
|
|
expectEqual(222, res)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionStringToString init from closure and pass as parameter") {
|
|
let res = invokeFunctionTwice(.init({ $0 + std.string("abc") }),
|
|
std.string("prefix"))
|
|
expectEqual(std.string("prefixabcabc"), res)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstRefStringToString init from closure and pass as parameter") {
|
|
let res = invokeFunctionTwiceConstRef(.init({ $0 + std.string("abc") }),
|
|
std.string("prefix"))
|
|
expectEqual(std.string("prefixabcabc"), res)
|
|
}
|
|
#endif
|
|
|
|
StdFunctionTestSuite.test("FunctionVoidToVoid init from thick closure and call") {
|
|
var counter = 0
|
|
let f1 = FunctionVoidToVoid { counter += 1 }
|
|
expectEqual(0, counter)
|
|
f1()
|
|
expectEqual(1, counter)
|
|
f1()
|
|
expectEqual(2, counter)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionVoidToInt init from thick closure and call") {
|
|
let external: Int32 = 123
|
|
let f1 = FunctionVoidToInt { external }
|
|
expectEqual(123, f1())
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToVoid init from thick closure and call") {
|
|
var counter: Int32 = 0
|
|
let f1 = FunctionIntToVoid { counter += $0 }
|
|
expectEqual(0, counter)
|
|
f1(5)
|
|
expectEqual(5, counter)
|
|
f1(10)
|
|
expectEqual(15, counter)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and call") {
|
|
let external: Int32 = 123
|
|
let f1 = FunctionIntToInt { $0 + external }
|
|
expectEqual(123, f1(0))
|
|
expectEqual(124, f1(1))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstIntToInt init from thick closure and call") {
|
|
let external: Int32 = 321
|
|
let f1 = FunctionConstIntToInt { $0 + external }
|
|
expectEqual(321, f1(0))
|
|
expectEqual(322, f1(1))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstRefIntToInt init from thick closure and call") {
|
|
let external: Int32 = 456
|
|
let f1 = FunctionConstIntToInt { $0 + external }
|
|
expectEqual(456, f1(0))
|
|
expectEqual(457, f1(1))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntIntToInt init from thick closure and call") {
|
|
let immutableExternal: Int32 = 10
|
|
let f1 = FunctionIntIntToInt({ a, b in
|
|
a - b + immutableExternal
|
|
})
|
|
expectEqual(11, f1(3, 2))
|
|
expectEqual(9, f1(2, 3))
|
|
|
|
var mutableExternal: Int32 = -100
|
|
let f2 = FunctionIntIntToInt({ a, b in
|
|
a - b + mutableExternal
|
|
})
|
|
mutableExternal = 20
|
|
expectEqual(21, f2(3, 2))
|
|
mutableExternal = 0
|
|
expectEqual(1, f2(3, 2))
|
|
|
|
var modifiedExternal: Int32 = 20
|
|
let f3 = FunctionIntIntToInt({ a, b in
|
|
let result = a - b + modifiedExternal
|
|
modifiedExternal = a + b
|
|
return result
|
|
})
|
|
expectEqual(21, f3(3, 2))
|
|
expectEqual(6, f3(3, 2))
|
|
expectEqual(4, f3(1, 2))
|
|
expectEqual(2, f3(1, 2))
|
|
|
|
struct MyContext {
|
|
var x: Int32
|
|
var y: Int32
|
|
var z: Int32
|
|
}
|
|
var external = MyContext(x: 123, y: 456, z: 789)
|
|
let closure: (Int32, Int32) -> Int32 = {
|
|
let r = $0 + $1 + external.x
|
|
external.y = r
|
|
return r
|
|
}
|
|
let f4 = FunctionIntIntToInt(closure)
|
|
expectEqual(246, f4(123, 0))
|
|
expectEqual(124, f4(1, 0))
|
|
expectEqual(122, f4(-1, 0))
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and pass as parameter") {
|
|
let immutableExternal: Int32 = 10
|
|
let res1 = invokeFunctionIntToIntTwice(.init({ $0 + immutableExternal }),
|
|
123)
|
|
expectEqual(143, res1)
|
|
|
|
var modifiedExternal: Int32 = 20
|
|
let res2 = invokeFunctionIntToIntTwice(.init({ a in
|
|
modifiedExternal += a
|
|
return a + modifiedExternal
|
|
}), 7)
|
|
expectEqual(95, res2)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and pass as const ref parameter") {
|
|
let immutableExternal: Int32 = 10
|
|
let res1 = invokeFunctionIntToIntByConstRefTwice(.init({ $0 + immutableExternal }),
|
|
123)
|
|
expectEqual(143, res1)
|
|
|
|
var modifiedExternal: Int32 = 20
|
|
let res2 = invokeFunctionIntToIntByConstRefTwice(.init({ a in
|
|
modifiedExternal += a
|
|
return a + modifiedExternal
|
|
}), 7)
|
|
expectEqual(95, res2)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and pass as rvalue ref parameter") {
|
|
let immutableExternal: Int32 = 10
|
|
let res1 = invokeFunctionIntToIntByRValueRefTwice(.init({ $0 + immutableExternal }),
|
|
123)
|
|
expectEqual(143, res1)
|
|
|
|
var modifiedExternal: Int32 = 20
|
|
let res2 = invokeFunctionIntToIntByRValueRefTwice(.init({ a in
|
|
modifiedExternal += a
|
|
return a + modifiedExternal
|
|
}), 7)
|
|
expectEqual(95, res2)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionStringToString init from thick closure and pass as parameter") {
|
|
var modifiedExternal = std.string()
|
|
let res = invokeFunctionTwice(.init({
|
|
let result = $0 + std.string("abc")
|
|
modifiedExternal = result
|
|
return result
|
|
}), std.string("prefix_"))
|
|
expectEqual(std.string("prefix_abcabc"), res)
|
|
expectEqual(std.string("prefix_abcabc"), modifiedExternal)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionStringToString init from thick closure and pass as const ref parameter") {
|
|
var modifiedExternal = std.string()
|
|
let res = invokeFunctionByConstRefTwice(.init({
|
|
let result = $0 + std.string("abc")
|
|
modifiedExternal = result
|
|
return result
|
|
}), std.string("prefix_"))
|
|
expectEqual(std.string("prefix_abcabc"), res)
|
|
expectEqual(std.string("prefix_abcabc"), modifiedExternal)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToHasDeletedCopyCtor init from thick closure and call") {
|
|
let f1 = FunctionIntToHasDeletedCopyCtor { HasDeletedCopyCtor($0) }
|
|
let res1 = f1(50)
|
|
expectEqual(50, res1.value)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstRefHasDeletedCopyCtorToInt init from thick closure and pass as parameter") {
|
|
var external: Int32 = 10
|
|
let f1 = FunctionConstRefHasDeletedCopyCtorToInt {
|
|
let res = $0.value + external
|
|
external += 5
|
|
return res
|
|
}
|
|
let res1 = invokeFunctionConstRefHasDeletedCopyCtorToInt(f1)
|
|
expectEqual(133, res1)
|
|
let res2 = invokeFunctionConstRefHasDeletedCopyCtorToInt(f1)
|
|
expectEqual(138, res2)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstRefHasDeletedCopyCtorToVoid init from thick closure and call") {
|
|
var counter: Int32 = 0
|
|
let f1 = FunctionConstRefHasDeletedCopyCtorToVoid { counter += $0.value }
|
|
expectEqual(0, counter)
|
|
f1(HasDeletedCopyCtor(50))
|
|
expectEqual(50, counter)
|
|
f1(HasDeletedCopyCtor(100))
|
|
expectEqual(150, counter)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionHasDeletedCopyCtor init from thick closure and call") {
|
|
var external: Int32 = 10
|
|
let f1 = FunctionHasDeletedCopyCtor({ h in
|
|
external += 10
|
|
return HasDeletedCopyCtor(h.value + external)
|
|
})
|
|
var x = HasDeletedCopyCtor(1)
|
|
x = f1(x)
|
|
expectEqual(21, x.value)
|
|
x = f1(x)
|
|
expectEqual(51, x.value)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToNonTrivialHasDeletedCopyCtor init from thick closure and call") {
|
|
let f1 = FunctionIntToNonTrivialHasDeletedCopyCtor { NonTrivialHasDeletedCopyCtor($0) }
|
|
let res1 = f1(150)
|
|
expectEqual(150, res1.value)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionConstRefNonTrivialHasDeletedCopyCtorToVoid init from thick closure and call") {
|
|
var counter: Int32 = 0
|
|
let f1 = FunctionConstRefNonTrivialHasDeletedCopyCtorToVoid { counter += $0.value }
|
|
expectEqual(0, counter)
|
|
f1(NonTrivialHasDeletedCopyCtor(50))
|
|
expectEqual(50, counter)
|
|
f1(NonTrivialHasDeletedCopyCtor(100))
|
|
expectEqual(150, counter)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionNonTrivialHasDeletedCopyCtor init from thick closure and call") {
|
|
var external: Int32 = 10
|
|
let f1 = FunctionNonTrivialHasDeletedCopyCtor({ h in
|
|
external += 10
|
|
return NonTrivialHasDeletedCopyCtor(h.value + external)
|
|
})
|
|
var x = NonTrivialHasDeletedCopyCtor(1)
|
|
x = f1(x)
|
|
expectEqual(21, x.value)
|
|
x = f1(x)
|
|
expectEqual(51, x.value)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and pass as templated callable parameter") {
|
|
var sum: Int32 = 0
|
|
let res1 = invokeTemplatedCallableIntToInt(FunctionIntToInt({ x in
|
|
sum += x
|
|
return x * 2
|
|
}))
|
|
expectEqual(246, res1)
|
|
expectEqual(123, sum)
|
|
}
|
|
|
|
StdFunctionTestSuite.test("FunctionIntToInt init from thick closure and pass as templated callable parameter by const ref") {
|
|
var sum: Int32 = 0
|
|
let res1 = invokeTemplatedCallableByConstRefIntToInt(FunctionIntToInt({ x in
|
|
sum += x
|
|
return x * 2
|
|
}))
|
|
expectEqual(642, res1)
|
|
expectEqual(321, sum)
|
|
}
|
|
|
|
runAllTests()
|