Skip to content

Commit e72caad

Browse files
committed
Runtime: Fix problems with nil-to-nil casts
We have a special rule that Optional<T>.none successfully dynamically casts to Optional<U>.none for any T and U. However the implementation was incorrect if the source and destination types had a different size. We would initialize the source to nil, and then copy to the result. The correct implementation is to initialize the result using the result payload type directly, and not call _succeed() at all. Fixes <https://bugs.swift.org/browse/SR-1056>.
1 parent 23eb156 commit e72caad

File tree

3 files changed

+62
-43
lines changed

3 files changed

+62
-43
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,10 +2036,16 @@ checkDynamicCastFromOptional(OpaqueValue *dest,
20362036
_fail(src, srcType, targetType, flags);
20372037
return {false, nullptr};
20382038
}
2039+
2040+
// Get the destination payload type
2041+
const Metadata *targetPayloadType =
2042+
cast<EnumMetadata>(targetType)->getGenericArgs()[0];
2043+
20392044
// Inject the .none tag
2040-
swift_storeEnumTagSinglePayload(dest, payloadType, enumCase,
2045+
swift_storeEnumTagSinglePayload(dest, targetPayloadType, enumCase,
20412046
1 /*emptyCases=*/);
2042-
_succeed(dest, src, srcType, flags);
2047+
2048+
// We don't have to destroy the source, because it was nil.
20432049
return {true, nullptr};
20442050
}
20452051
// .Some

test/1_stdlib/Casts.swift

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,9 @@ import ObjectiveC
2828
let CastsTests = TestSuite("Casts")
2929

3030
// Test for SR-426: missing release for some types after failed conversion
31-
class DeinitTester {
32-
private let onDeinit: () -> ()
33-
34-
init(onDeinit: () -> ()) {
35-
self.onDeinit = onDeinit
36-
}
37-
deinit {
38-
onDeinit()
39-
}
40-
}
41-
42-
func testFailedTupleCast(onDeinit: () -> ()) {
43-
// This function is to establish a scope for t to
44-
// be deallocated at the end of.
45-
let t: Any = (1, DeinitTester(onDeinit: onDeinit))
46-
_ = t is Any.Type
47-
}
48-
4931
CastsTests.test("No leak for failed tuple casts") {
50-
var deinitRan = false
51-
testFailedTupleCast {
52-
deinitRan = true
53-
}
54-
expectTrue(deinitRan)
32+
let t: Any = (1, LifetimeTracked(0))
33+
expectFalse(t is Any.Type)
5534
}
5635

5736
protocol P {}

test/1_stdlib/Optional.swift

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@ import SwiftPrivate
1212
import ObjectiveC
1313
#endif
1414

15-
class DeinitTester {
16-
private let onDeinit: () -> ()
17-
18-
init(onDeinit: () -> ()) {
19-
self.onDeinit = onDeinit
20-
}
21-
deinit {
22-
onDeinit()
23-
}
24-
}
25-
2615
let OptionalTests = TestSuite("Optional")
2716

2817
protocol TestProtocol1 {}
@@ -93,6 +82,7 @@ OptionalTests.test("Equatable") {
9382

9483
struct X {}
9584
class C {}
85+
class D {}
9686

9787
class E : Equatable {}
9888
func == (_: E, _: E) -> Bool { return true }
@@ -190,11 +180,21 @@ OptionalTests.test("flatMap") {
190180
expectEmpty((3 as Int32?).flatMap(half))
191181
}
192182

183+
// FIXME: @inline(never) does not inhibit specialization
184+
193185
@inline(never)
194186
func anyToAny<T, U>(a: T, _ : U.Type) -> U {
195187
return a as! U
196188
}
197189
@inline(never)
190+
func anyToAnyIs<T, U>(a: T, _ : U.Type) -> Bool {
191+
return a is U
192+
}
193+
@inline(never)
194+
func anyToAnyIsOptional<T, U>(a: T?, _ : U.Type) -> Bool {
195+
return a is U?
196+
}
197+
@inline(never)
198198
func anyToAnyOrNil<T, U>(a: T, _ : U.Type) -> U? {
199199
return a as? U
200200
}
@@ -210,10 +210,27 @@ OptionalTests.test("Casting Optional") {
210210
let sx: C? = x
211211
let nx: C? = nil
212212
expectTrue(anyToAny(x, Optional<C>.self)! === x)
213+
expectTrue(anyToAnyIs(x, Optional<C>.self))
214+
expectFalse(anyToAnyIs(x, Optional<D>.self))
215+
213216
expectTrue(anyToAny(sx, C.self) === x)
217+
expectTrue(anyToAnyIs(sx, C.self))
218+
expectFalse(anyToAnyIs(sx, D.self))
219+
214220
expectTrue(anyToAny(sx, Optional<C>.self)! === x)
221+
expectTrue(anyToAnyIs(sx, Optional<C>.self))
222+
expectTrue(anyToAnyIsOptional(sx, C.self))
223+
expectFalse(anyToAnyIsOptional(sx, D.self))
215224

216225
expectTrue(anyToAny(nx, Optional<C>.self) == nil)
226+
expectTrue(anyToAnyIs(nx, Optional<C>.self))
227+
228+
// You can cast a nil of any type to a nil of any other type
229+
// successfully
230+
expectTrue(anyToAnyIs(nx, Optional<D>.self))
231+
232+
expectTrue(anyToAnyIsOptional(nx, C.self))
233+
217234
expectTrue(anyToAnyOrNil(nx, C.self) == nil)
218235

219236
let i = Int.max
@@ -232,20 +249,37 @@ OptionalTests.test("Casting Optional") {
232249
expectTrue(anyToAnyOrNil(ni, Int.self) == nil)
233250

234251
// Test for SR-459: Weakened optionals don't zero.
235-
var deinitRan = false
236-
do {
237-
var t = DeinitTester { deinitRan = true }
238-
_ = anyToAny(Optional(t), CustomDebugStringConvertible.self)
239-
}
240-
expectTrue(deinitRan)
252+
var t = LifetimeTracked(0)
253+
_ = anyToAny(Optional(t), CustomDebugStringConvertible.self)
254+
expectTrue(anyToAnyIs(Optional(t), CustomDebugStringConvertible.self))
241255

242256
// Test for SR-912: Runtime exception casting an Any nil to an Optional.
243257
let oi: Int? = nil
244258
expectTrue(anyToAny(oi as Any, Optional<Int>.self) == nil)
259+
expectTrue(anyToAnyIs(oi as Any, Optional<Int>.self))
260+
261+
// Double-wrapped optional
262+
expectTrue(anyToAnyIsOptional(oi as Any, Int.self))
263+
245264
// For good measure test an existential that Optional does not conform to.
246265
expectTrue(anyToAny(3 as TestExistential, Optional<Int>.self) == 3)
266+
267+
// Can't do existential + optional wrapping at once for some reason
268+
expectTrue(anyToAnyIs(3 as TestExistential, Optional<Int>.self))
269+
expectTrue(anyToAnyIsOptional(3 as TestExistential, Int.self))
270+
247271
// And a type that is not convertible to its target.
248-
anyToAny(nx as Any, Optional<Int>.self)
272+
expectTrue(anyToAny(nx as Any, Optional<Int>.self) == nil)
273+
expectTrue(anyToAnyIs(nx as Any, Optional<Int>.self))
274+
expectTrue(anyToAnyIsOptional(nx as Any, Int.self))
275+
276+
expectTrue(anyToAnyOrNil(sx as Any, Optional<Int>.self) == nil)
277+
expectFalse(anyToAnyIs(sx as Any, Optional<Int>.self))
278+
expectFalse(anyToAnyIsOptional(sx as Any, Int.self))
279+
280+
// OK to convert nil of any type to optional of any other type
281+
expectTrue(anyToAnyIs(Optional<(String, String)>.none, Optional<Bool>.self))
282+
expectTrue(anyToAnyIsOptional(Optional<(String, String)>.none, Bool.self))
249283
}
250284

251285
OptionalTests.test("Casting Optional Traps") {

0 commit comments

Comments
 (0)