diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 5becdd929d6dc..9447bbefa5941 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -2027,10 +2027,16 @@ checkDynamicCastFromOptional(OpaqueValue *dest, _fail(src, srcType, targetType, flags); return {false, nullptr}; } + + // Get the destination payload type + const Metadata *targetPayloadType = + cast(targetType)->getGenericArgs()[0]; + // Inject the .None tag - swift_storeEnumTagSinglePayload(dest, payloadType, enumCase, + swift_storeEnumTagSinglePayload(dest, targetPayloadType, enumCase, 1 /*emptyCases=*/); - _succeed(dest, src, srcType, flags); + + // We don't have to destroy the source, because it was nil. return {true, nullptr}; } // .Some @@ -2054,6 +2060,13 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, switch (targetType->getKind()) { // Handle wrapping an Optional target. case MetadataKind::Optional: { + // If the source is an existential, attempt to cast it first without + // unwrapping the target. This handles an optional source wrapped within an + // existential that Optional conforms to (Any). + if (auto srcExistentialType = dyn_cast(srcType)) { + return _dynamicCastFromExistential(dest, src, srcExistentialType, + targetType, flags); + } // Recursively cast into the layout compatible payload area. const Metadata *payloadType = cast(targetType)->getGenericArgs()[0]; diff --git a/test/1_stdlib/Casts.swift b/test/1_stdlib/Casts.swift index 72a0f9d9546b4..7712a65ffa719 100644 --- a/test/1_stdlib/Casts.swift +++ b/test/1_stdlib/Casts.swift @@ -28,30 +28,9 @@ import ObjectiveC let CastsTests = TestSuite("Casts") // Test for SR-426: missing release for some types after failed conversion -class DeinitTester { - private let onDeinit: () -> () - - init(onDeinit: () -> ()) { - self.onDeinit = onDeinit - } - deinit { - onDeinit() - } -} - -func testFailedTupleCast(onDeinit: () -> ()) { - // This function is to establish a scope for t to - // be deallocated at the end of. - let t: Any = (1, DeinitTester(onDeinit: onDeinit)) - _ = t is Any.Type -} - CastsTests.test("No leak for failed tuple casts") { - var deinitRan = false - testFailedTupleCast { - deinitRan = true - } - expectTrue(deinitRan) + let t: Any = (1, LifetimeTracked(0)) + expectFalse(t is Any.Type) } protocol P {} diff --git a/test/1_stdlib/Optional.swift b/test/1_stdlib/Optional.swift index 7476c42914ba7..0ba4cd00f6865 100644 --- a/test/1_stdlib/Optional.swift +++ b/test/1_stdlib/Optional.swift @@ -12,17 +12,6 @@ import SwiftPrivate import ObjectiveC #endif -class DeinitTester { - private let onDeinit: () -> () - - init(onDeinit: () -> ()) { - self.onDeinit = onDeinit - } - deinit { - onDeinit() - } -} - let OptionalTests = TestSuite("Optional") protocol TestProtocol1 {} @@ -93,6 +82,7 @@ OptionalTests.test("Equatable") { struct X {} class C {} +class D {} class E : Equatable {} func == (_: E, _: E) -> Bool { return true } @@ -190,27 +180,67 @@ OptionalTests.test("flatMap") { expectEmpty((3 as Int32?).flatMap(half)) } +// FIXME: @inline(never) does not inhibit specialization + @inline(never) +@_semantics("optimize.sil.never") func anyToAny(a: T, _ : U.Type) -> U { return a as! U } + +@inline(never) +@_semantics("optimize.sil.never") +func anyToAnyIs(a: T, _ : U.Type) -> Bool { + return a is U +} + +@inline(never) +@_semantics("optimize.sil.never") +func anyToAnyIsOptional(a: T?, _ : U.Type) -> Bool { + return a is U? +} + @inline(never) +@_semantics("optimize.sil.never") func anyToAnyOrNil(a: T, _ : U.Type) -> U? { return a as? U } + +@inline(never) +@_semantics("optimize.sil.never") func canGenericCast(a: T, _ ty : U.Type) -> Bool { return anyToAnyOrNil(a, ty) != nil } +protocol TestExistential {} +extension Int : TestExistential {} + OptionalTests.test("Casting Optional") { let x = C() let sx: C? = x let nx: C? = nil expectTrue(anyToAny(x, Optional.self)! === x) + expectTrue(anyToAnyIs(x, Optional.self)) + expectFalse(anyToAnyIs(x, Optional.self)) + expectTrue(anyToAny(sx, C.self) === x) + expectTrue(anyToAnyIs(sx, C.self)) + expectFalse(anyToAnyIs(sx, D.self)) + expectTrue(anyToAny(sx, Optional.self)! === x) + expectTrue(anyToAnyIs(sx, Optional.self)) + expectTrue(anyToAnyIsOptional(sx, C.self)) + expectFalse(anyToAnyIsOptional(sx, D.self)) expectTrue(anyToAny(nx, Optional.self) == nil) + expectTrue(anyToAnyIs(nx, Optional.self)) + + // You can cast a nil of any type to a nil of any other type + // successfully + expectTrue(anyToAnyIs(nx, Optional.self)) + + expectTrue(anyToAnyIsOptional(nx, C.self)) + expectTrue(anyToAnyOrNil(nx, C.self) == nil) let i = Int.max @@ -229,12 +259,34 @@ OptionalTests.test("Casting Optional") { expectTrue(anyToAnyOrNil(ni, Int.self) == nil) // Test for SR-459: Weakened optionals don't zero. - var deinitRan = false - do { - var t = DeinitTester { deinitRan = true } - _ = anyToAny(Optional(t), CustomDebugStringConvertible.self) - } - expectTrue(deinitRan) + var t = LifetimeTracked(0) + _ = anyToAny(Optional(t), CustomDebugStringConvertible.self) + expectTrue(anyToAnyIs(Optional(t), CustomDebugStringConvertible.self)) + + // Test for SR-912: Runtime exception casting an Any nil to an Optional. + let oi: Int? = nil + expectTrue(anyToAny(oi as Any, Optional.self) == nil) + expectTrue(anyToAnyIs(oi as Any, Optional.self)) + // For good measure test an existential that Optional does not conform to. + expectTrue(anyToAny(3 as TestExistential, Optional.self) == 3) + // And a type that is not convertible to its target. + anyToAny(nx as Any, Optional.self) + + // Double-wrapped optional + expectTrue(anyToAnyIsOptional(oi as Any, Int.self)) + + // And a type that is not convertible to its target. + expectTrue(anyToAny(nx as Any, Optional.self) == nil) + expectTrue(anyToAnyIs(nx as Any, Optional.self)) + expectTrue(anyToAnyIsOptional(nx as Any, Int.self)) + + expectTrue(anyToAnyOrNil(sx as Any, Optional.self) == nil) + expectFalse(anyToAnyIs(sx as Any, Optional.self)) + expectFalse(anyToAnyIsOptional(sx as Any, Int.self)) + + // OK to convert nil of any type to optional of any other type + expectTrue(anyToAnyIs(Optional<(String, String)>.None, Optional.self)) + expectTrue(anyToAnyIsOptional(Optional<(String, String)>.None, Bool.self)) } OptionalTests.test("Casting Optional Traps") { @@ -242,6 +294,11 @@ OptionalTests.test("Casting Optional Traps") { expectCrashLater() anyToAny(nx, Int.self) } +OptionalTests.test("Casting Optional Any Traps") { + let nx: X? = X() + expectCrashLater() + _blackHole(anyToAny(nx as Any, Optional.self)) +} class TestNoString {} class TestString : CustomStringConvertible, CustomDebugStringConvertible {