mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
withoutActuallyEscaping checks that the passed closure has not beed escaped by ensuring that its reference count is exactly 1 at the end of the code block. So far this was only done in the regular return case. But if the code block throws, the check was not done. Fixes an undetected undefined behavior.
104 lines
2.1 KiB
Swift
104 lines
2.1 KiB
Swift
// RUN: %target-run-simple-swift
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
|
|
var WithoutEscapingSuite = TestSuite("WithoutActuallyEscaping")
|
|
|
|
var sink: Any = ()
|
|
|
|
func dontEscape(f: () -> ()) {
|
|
withoutActuallyEscaping(f) {
|
|
$0()
|
|
}
|
|
}
|
|
|
|
func letEscape(f: () -> ()) -> () -> () {
|
|
return withoutActuallyEscaping(f) { return $0 }
|
|
}
|
|
|
|
var testShouldThrow = false
|
|
|
|
struct MyError : Error {}
|
|
|
|
@inline(never)
|
|
func letEscapeThrowing(f: () -> ()) throws -> () -> () {
|
|
return try withoutActuallyEscaping(f) {
|
|
if testShouldThrow {
|
|
throw MyError()
|
|
}
|
|
return $0
|
|
}
|
|
}
|
|
|
|
@inline(never)
|
|
func letEscapeOnThrowingPath(f: () throws -> ()) throws {
|
|
try withoutActuallyEscaping(f) {
|
|
sink = $0
|
|
throw MyError()
|
|
}
|
|
}
|
|
|
|
|
|
WithoutEscapingSuite.test("ExpectNoCrash") {
|
|
dontEscape(f: { print("foo") })
|
|
}
|
|
|
|
WithoutEscapingSuite.test("ExpectDebugCrash") {
|
|
// Optimize versions pass a nil closure context.
|
|
if _isDebugAssertConfiguration() {
|
|
expectCrashLater()
|
|
}
|
|
sink = letEscape(f: { print("foo") })
|
|
}
|
|
|
|
struct Context {
|
|
var a = 0
|
|
var b = 1
|
|
}
|
|
|
|
WithoutEscapingSuite.test("ExpectCrash") {
|
|
expectCrashLater()
|
|
let context = Context()
|
|
sink = letEscape(f: { print("Context: \(context.a) \(context.b)") })
|
|
}
|
|
|
|
WithoutEscapingSuite.test("ExpectNonThrowingCrash") {
|
|
expectCrashLater()
|
|
let context = Context()
|
|
var testDidThrow = false
|
|
testShouldThrow = false
|
|
do {
|
|
sink = try letEscapeThrowing(f: { print("Context: \(context.a) \(context.b)") })
|
|
} catch {
|
|
testDidThrow = true
|
|
}
|
|
expectFalse(testDidThrow)
|
|
}
|
|
|
|
WithoutEscapingSuite.test("ExpectThrowingNoCrash") {
|
|
let context = Context()
|
|
var testDidThrow = false
|
|
testShouldThrow = true
|
|
do {
|
|
sink = try letEscapeThrowing(f: { print("Context: \(context.a) \(context.b)") })
|
|
} catch {
|
|
testDidThrow = true
|
|
}
|
|
expectTrue(testDidThrow)
|
|
}
|
|
|
|
WithoutEscapingSuite.test("ExpectThrowingCrash") {
|
|
expectCrashLater()
|
|
let context = Context()
|
|
var testDidThrow = false
|
|
do {
|
|
try letEscapeOnThrowingPath(f: { print("Context: \(context.a) \(context.b)") })
|
|
} catch {
|
|
testDidThrow = true
|
|
}
|
|
expectTrue(testDidThrow)
|
|
}
|
|
|
|
runAllTests()
|