diff --git a/stdlib/public/SDK/ObjectiveC/ObjectiveC.swift b/stdlib/public/SDK/ObjectiveC/ObjectiveC.swift index 8839665049d97..d547adef51a51 100644 --- a/stdlib/public/SDK/ObjectiveC/ObjectiveC.swift +++ b/stdlib/public/SDK/ObjectiveC/ObjectiveC.swift @@ -181,7 +181,7 @@ public struct NSZone { typealias Zone = NSZone //===----------------------------------------------------------------------===// -// FIXME: @autoreleasepool substitute +// @autoreleasepool substitute //===----------------------------------------------------------------------===// @warn_unused_result @@ -191,10 +191,14 @@ func __pushAutoreleasePool() -> OpaquePointer @_silgen_name("_swift_objc_autoreleasePoolPop") func __popAutoreleasePool(_ pool: OpaquePointer) -public func autoreleasepool(_ code: @noescape () -> Void) { +public func autoreleasepool( + _ body: @noescape () throws -> Result +) rethrows -> Result { let pool = __pushAutoreleasePool() - code() - __popAutoreleasePool(pool) + defer { + __popAutoreleasePool(pool) + } + return try body() } //===----------------------------------------------------------------------===// diff --git a/test/Interpreter/SDK/autorelease.swift b/test/Interpreter/SDK/autorelease.swift index ef5436795c421..ee9a3b2583991 100644 --- a/test/Interpreter/SDK/autorelease.swift +++ b/test/Interpreter/SDK/autorelease.swift @@ -34,3 +34,39 @@ print("autorelease test end") // CHECK-NEXT: object died // CHECK-NEXT: after call to useTemp // CHECK-NEXT: autorelease test end + +// Using an @objc class to check that errors are retained across the pool +// boundaries. A classic crash is an error created inside a pool and then +// zombied before handling it outside the pool. +@objc class Error : NSObject, ErrorProtocol { + let message: String + init(message: String) { + self.message = message + } +} + +// Check that rethrow works. +func requireString(string: String?) throws -> String { + guard let string = string else { + throw Error(message: "no string") + } + print("returning \"\(string)\"") + return string +} +do { + try autoreleasepool { + try requireString(string: "ok") + try requireString(string: nil) + } +} catch let err as Error { + print("caught \"\(err.message)\"") +} +// CHECK-NEXT: returning "ok" +// CHECK-NEXT: caught "no string" + +// Check that a return value can be passed back. +let result = try autoreleasepool { + return "a string" +} +print("result = \"\(result)\"") +// CHECK-NEXT: result = "a string"