Files
swift-mirror/test/Interop/Cxx/templates/function-template-closures.swift
John Hui 3f8e9cd993 [cxx-interop] Allow C++ function templates to be instantiated with Swift closures
Swift started to explicitly forbid the instantiation of C++ function
templates with arbitrary types in #77430, because many types cause the
Swift compiler to crash. However, those checks prevented them from being
instantiated with Swift closures (which were previously fine), causing
a regression.

This patch relaxes the convertTemplateArgument() function to also allow
converting Swift function types, and adds some tests to make sure doing
so is fine.

This patch also does some cleanup of existing tests checking the
instantiation of various types, and adds testing for C function pointers
and Obj-C blocks.

rdar://148124104
(cherry picked from commit 284de98744)
2025-04-30 13:49:23 -07:00

147 lines
5.0 KiB
Swift

// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=default)
// REQUIRES: executable_test
// REQUIRES: objc_interop
import FunctionTemplates
import StdlibUnittest
var FunctionTemplateClosuresTestSuite = TestSuite("Function Templates with Closures")
FunctionTemplateClosuresTestSuite.test("callFunction<F> where F == () -> ()") {
var value = false
callFunction({() in value = true})
expectTrue(value)
}
FunctionTemplateClosuresTestSuite.test("callFunction<F> where F == () -> Bool") {
var value = false
callFunction({() in value = true; return true})
expectTrue(value)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithParam<F, T> where F == (Int) -> (), T == Int") {
var value = 42
callFunctionWithParam({(x: Int) in value = x}, 24)
expectEqual(value, 24)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithParam<F, T> where F == (Int) -> Bool, T == Int") {
var value = 42
callFunctionWithParam({(x: Int) in value = x; return true}, 24)
expectEqual(value, 24)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithParam<F, T> where F == (CxxClass) -> (), T == CxxClass") {
var value: CInt = 42
// Read x via direct field access
callFunctionWithParam({(c: CxxClass) in value = c.x}, CxxClass(x: 24))
expectEqual(value, 24)
// Read x via getter method
callFunctionWithParam({(c: CxxClass) in value = c.getX()}, CxxClass(x: 22))
expectEqual(value, 22)
}
// TODO: this likely needs more design work
// FunctionTemplateClosuresTestSuite.test("callFunctionWithParam<F, T> where F == (()) -> (), T == ()") {
// var value = 42
// // None of these are quite right:
// // callFunctionWithParam({(_: ()) in value = 24}, ())
// // callFunctionWithParam({(_: Void) in value = 24}, ())
// // callFunctionWithParam({() in value = 24}, ())
// expectEqual(value, 24)
// }
FunctionTemplateClosuresTestSuite.test("callFunctionWithReturn<F, T> where F == () -> Int, T == Int") {
let val1: Int = callFunctionWithReturn({() in 111})
expectEqual(val1, 111)
let val2: Int = callFunctionWithReturn({() -> Int in 222})
expectEqual(val2, 222)
// Cannot infer return type without val3 explicitly annotated as Int
// let val3 = callFunctionWithReturn({() -> Int in 333})
// expectEqual(val3, 333)
// This works:
expectEqual(callFunctionWithReturn({() in 444}), 444)
// This too:
expectEqual(callFunctionWithReturn({() in true}), true)
// But this does not (probably a quirk of expectTrue())
// expectTrue(callFunctionWithReturn({() in true}))
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithReturn<F, T> where F == () -> CxxClass, T == CxxClass") {
let c: CxxClass = callFunctionWithReturn({() in CxxClass(x: 42)})
expectEqual(c.x, 42)
let d: CxxClass = callFunctionWithReturn({() in c})
expectEqual(d.x, 42)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithPassthrough<F, T> where F == (T) -> T, T == Int") {
expectEqual(callFunctionWithPassthrough({(x: Int) in x}, 42), 42)
expectEqual(callFunctionWithPassthrough({(x: Int) in 24}, 42), 24)
expectEqual(callFunctionWithPassthrough({(x: Int) in x + 42}, 42), 84)
}
FunctionTemplateClosuresTestSuite.test("indirectlyCallFunction<T> where T == () -> ()") {
var value = 42
indirectlyCallFunction({() in value = 111});
expectEqual(value, 111)
indirectlyCallFunctionTemplate({() in value = 222});
expectEqual(value, 222)
}
FunctionTemplateClosuresTestSuite.test("indirectlyCallFunction<T> where T == () -> ()") {
var value: CInt = 0
indirectlyCallFunctionWith42({(x: CInt) in value = x});
expectEqual(value, 42)
indirectlyCallFunctionWithCxxClass24({(c: CxxClass) in value = c.x});
expectEqual(value, 24)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithReturn<F, T> where F == void (^)(int), T == CInt") {
var value: CInt = 0
value = callFunctionWithReturn(blockReturns111!)
expectEqual(value, 111)
value = callFunctionWithReturn(nonNullBlockReturns222)
expectEqual(value, 222)
getConstantIntBlock(333, {cb in value = callFunctionWithReturn(cb) })
expectEqual(value, 333)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithPassthrough<F, T> where F == int (^)(int), T == CInt") {
expectEqual(callFunctionWithPassthrough(blockTripleInt!, 7), 21)
expectEqual(callFunctionWithPassthrough(nonNullBlockTripleInt, 8), 24)
expectEqual(getMultiplyIntBlock(9, {cb in callFunctionWithPassthrough(cb, 3)}), 27)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithReturn<T, B> where T == C function ptr, B == CInt") {
var value: CInt = 0
value = callFunctionWithReturn(get42)
expectEqual(value, 42)
value = callFunctionWithReturn(functionPtrGet42!)
expectEqual(value, 42)
value = callFunctionWithReturn(nonNullFunctionPtrGet42)
expectEqual(value, 42)
}
FunctionTemplateClosuresTestSuite.test("callFunctionWithPassthrough<F, T> where C function ptr, T == CInt") {
expectEqual(callFunctionWithPassthrough(tripleInt, 4), 12)
expectEqual(callFunctionWithPassthrough(functionPtrTripleInt!, 5), 15)
expectEqual(callFunctionWithPassthrough(nonNullFunctionPtrTripleInt, 6), 18)
}
runAllTests()