blob: e0b8859e1504acd11c9f35b99644dcc69ac38686 [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -o %t/Error -DPTR_SIZE_%target-ptrsize -module-name main %/s
// RUN: %target-codesign %t/Error
// RUN: %target-run %t/Error
// REQUIRES: executable_test
import StdlibUnittest
func shouldCheckErrorLocation() -> Bool {
// Location information for runtime traps is only emitted in debug builds.
guard _isDebugAssertConfiguration() else { return false }
// The runtime error location format changed after the 5.3 release.
// (https://github.com/apple/swift/pull/34665)
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
return true
} else {
return false
}
}
var ErrorTests = TestSuite("Error")
var NoisyErrorLifeCount = 0
var NoisyErrorDeathCount = 0
protocol OtherProtocol {
var otherProperty: String { get }
}
protocol OtherClassProtocol : AnyObject {
var otherClassProperty: String { get }
}
class NoisyError : Error, OtherProtocol, OtherClassProtocol {
init() { NoisyErrorLifeCount += 1 }
deinit { NoisyErrorDeathCount += 1 }
let _domain = "NoisyError"
let _code = 123
let otherProperty = "otherProperty"
let otherClassProperty = "otherClassProperty"
}
ErrorTests.test("erasure") {
NoisyErrorLifeCount = 0
NoisyErrorDeathCount = 0
do {
let e: Error = NoisyError()
expectEqual(e._domain, "NoisyError")
expectEqual(e._code, 123)
}
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
}
ErrorTests.test("reflection") {
NoisyErrorLifeCount = 0
NoisyErrorDeathCount = 0
do {
let ne = NoisyError()
let e: Error = ne
var neDump = "", eDump = ""
dump(ne, to: &neDump)
dump(e, to: &eDump)
expectEqual(eDump, neDump)
}
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
}
ErrorTests.test("dynamic casts") {
NoisyErrorLifeCount = 0
NoisyErrorDeathCount = 0
do {
let ne = NoisyError()
let e: Error = ne
expectTrue(e as! NoisyError === ne)
expectEqual((e as! OtherClassProtocol).otherClassProperty, "otherClassProperty")
expectEqual((e as! OtherProtocol).otherProperty, "otherProperty")
let op: OtherProtocol = ne
expectEqual((op as! Error)._domain, "NoisyError")
expectEqual((op as! Error)._code, 123)
let ocp: OtherClassProtocol = ne
expectEqual((ocp as! Error)._domain, "NoisyError")
expectEqual((ocp as! Error)._code, 123)
// Do the same with rvalues, so we exercise the
// take-on-success/destroy-on-failure paths.
expectEqual(((NoisyError() as Error) as! NoisyError)._domain, "NoisyError")
expectEqual(((NoisyError() as Error) as! OtherClassProtocol).otherClassProperty, "otherClassProperty")
expectEqual(((NoisyError() as Error) as! OtherProtocol).otherProperty, "otherProperty")
expectEqual(((NoisyError() as OtherProtocol) as! Error)._domain, "NoisyError")
expectEqual(((NoisyError() as OtherProtocol) as! Error)._code, 123)
expectEqual(((NoisyError() as OtherClassProtocol) as! Error)._domain, "NoisyError")
expectEqual(((NoisyError() as OtherClassProtocol) as! Error)._code, 123)
}
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
}
struct DefaultStruct : Error { }
class DefaultClass : Error { }
ErrorTests.test("default domain and code") {
expectEqual(DefaultStruct()._domain, "main.DefaultStruct")
expectEqual(DefaultStruct()._code, 1)
expectEqual(DefaultClass()._domain, "main.DefaultClass")
expectEqual(DefaultClass()._code, 1)
}
enum SillyError: Error { case JazzHands }
ErrorTests.test("try!")
.skip(.custom({ _isFastAssertConfiguration() },
reason: "trap is not guaranteed to happen in -Ounchecked"))
.crashOutputMatches(shouldCheckErrorLocation()
? "'try!' expression unexpectedly raised an error: "
+ "main.SillyError.JazzHands"
: "")
.code {
expectCrashLater()
let _: () = try! { throw SillyError.JazzHands }()
}
ErrorTests.test("try!/location")
.skip(.custom({ _isFastAssertConfiguration() },
reason: "trap is not guaranteed to happen in -Ounchecked"))
.crashOutputMatches(shouldCheckErrorLocation()
? "main/Error.swift:140"
: "")
.code {
expectCrashLater()
let _: () = try! { throw SillyError.JazzHands }()
}
ErrorTests.test("try?") {
var value = try? { () throws -> Int in return 1 }()
expectType(Optional<Int>.self, &value)
expectEqual(Optional(1), value)
expectNil(try? { () throws -> Int in throw SillyError.JazzHands }())
}
enum LifetimeError : Error {
case MistakeOfALifetime(LifetimeTracked, yearsIncarcerated: Int)
}
ErrorTests.test("existential in lvalue") {
expectEqual(0, LifetimeTracked.instances)
do {
var e: Error?
do {
throw LifetimeError.MistakeOfALifetime(LifetimeTracked(0),
yearsIncarcerated: 25)
} catch {
e = error
}
expectEqual(1, LifetimeTracked.instances)
expectEqual(0, e?._code)
}
expectEqual(0, LifetimeTracked.instances)
}
enum UnsignedError: UInt, Error {
#if PTR_SIZE_64
case negativeOne = 0xFFFFFFFFFFFFFFFF
#elseif PTR_SIZE_32
case negativeOne = 0xFFFFFFFF
#else
#error ("Unknown pointer size")
#endif
}
ErrorTests.test("unsigned raw value") {
let negOne: Error = UnsignedError.negativeOne
expectEqual(-1, negOne._code)
}
ErrorTests.test("test dealloc empty error box") {
struct Foo<T>: Error { let value: T }
func makeFoo<T>() throws -> Foo<T> {
throw Foo(value: "makeFoo throw error")
}
func makeError<T>(of: T.Type) throws -> Error {
return try makeFoo() as Foo<T>
}
do {
_ = try makeError(of: Int.self)
} catch let foo as Foo<String> {
expectEqual(foo.value, "makeFoo throw error")
} catch {
expectUnreachableCatch(error)
}
}
var errors: [Error] = []
@inline(never)
func throwNegativeOne() throws {
throw UnsignedError.negativeOne
}
@inline(never)
func throwJazzHands() throws {
throw SillyError.JazzHands
}
ErrorTests.test("willThrow") {
if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
// Error isn't allowed in a @convention(c) function when ObjC interop is
// not available, so pass it through an OpaquePointer.
typealias WillThrow = @convention(c) (OpaquePointer) -> Void
let willThrow = pointerToSwiftCoreSymbol(name: "_swift_willThrow")!
let callback: WillThrow = {
errors.append(unsafeBitCast($0, to: Error.self))
}
willThrow.storeBytes(of: callback, as: WillThrow.self)
expectTrue(errors.isEmpty)
do {
try throwNegativeOne()
} catch {}
expectEqual(UnsignedError.self, type(of: errors.last!))
do {
try throwJazzHands()
} catch {}
expectEqual(2, errors.count)
expectEqual(SillyError.self, type(of: errors.last!))
}
}
runAllTests()