mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
In a designated initializer of a non-root class, 'self' becomes fully initialized after the 'super.init' call, at which point escaping uses of 'self' become valid, and releases of 'self' are lowered to a 'strong_release' instruction, which runs the deinitializer. In a root class, 'self' becomes fully initialized after all stored properties have been initialized, at which point escaping uses of 'self' become valid. However, DI would still lower a conditional destroy of 'self' by destroying any initialized stored properties and freeing the object with 'dealloc_partial_ref'. This is incorrect, because 'self' may have escaped. In the non-conditional destroy case, we correctly lowered the release to a 'strong_release' if all stored properties are known to be initialized. Fix DI to handle the conditional destroy case by first checking if all bits in the control variable are set, and releasing the instance with 'strong_release' if so. The 'dealloc_partial_ref' is only emitted if not at least one stored property was not initialized. This ensures that we don't deallocate an instance that may have escaped. Fixes <https://bugs.swift.org/browse/SR-13439>, <rdar://problem/67746791>, <https://bugs.swift.org/browse/SR-13355>, <rdar://problem/67361228>.
177 lines
2.5 KiB
Swift
177 lines
2.5 KiB
Swift
// RUN: %target-run-simple-swift
|
|
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
|
|
|
|
var FailableInitTestSuite = TestSuite("FailableInit")
|
|
|
|
var deinitCalled = 0
|
|
|
|
func mustFail<T>(f: () -> T?) {
|
|
if f() != nil {
|
|
preconditionFailure("Didn't fail")
|
|
}
|
|
}
|
|
|
|
func mustSucceed<T>(f: () -> T?) {
|
|
if f() == nil {
|
|
preconditionFailure("Didn't succeed")
|
|
}
|
|
}
|
|
|
|
class FirstClass {
|
|
var x: LifetimeTracked
|
|
|
|
init?(n: Int) {
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
x = LifetimeTracked(0)
|
|
|
|
if n == 1 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
deinitCalled += 1
|
|
}
|
|
}
|
|
|
|
FailableInitTestSuite.test("FirstClass") {
|
|
deinitCalled = 0
|
|
|
|
mustFail { FirstClass(n: 0) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { FirstClass(n: 1) }
|
|
expectEqual(1, deinitCalled)
|
|
|
|
mustSucceed { FirstClass(n: 2) }
|
|
expectEqual(2, deinitCalled)
|
|
}
|
|
|
|
class FirstClassTrivial {
|
|
var x: Int
|
|
|
|
init?(n: Int) {
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
x = 0
|
|
|
|
if n == 1 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
deinitCalled += 1
|
|
}
|
|
}
|
|
|
|
FailableInitTestSuite.test("FirstClassTrivial") {
|
|
deinitCalled = 0
|
|
|
|
mustFail { FirstClassTrivial(n: 0) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { FirstClassTrivial(n: 1) }
|
|
expectEqual(1, deinitCalled)
|
|
|
|
mustSucceed { FirstClassTrivial(n: 2) }
|
|
expectEqual(2, deinitCalled)
|
|
}
|
|
|
|
class SecondClass {
|
|
var x: LifetimeTracked
|
|
var y: LifetimeTracked
|
|
|
|
init?(n: Int) {
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
x = LifetimeTracked(0)
|
|
|
|
if n == 1 {
|
|
return nil
|
|
}
|
|
|
|
y = LifetimeTracked(0)
|
|
|
|
if n == 2 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
deinitCalled += 1
|
|
}
|
|
}
|
|
|
|
FailableInitTestSuite.test("SecondClass") {
|
|
deinitCalled = 0
|
|
|
|
mustFail { SecondClass(n: 0) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { SecondClass(n: 1) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { SecondClass(n: 2) }
|
|
expectEqual(1, deinitCalled)
|
|
|
|
mustSucceed { SecondClass(n: 3) }
|
|
expectEqual(2, deinitCalled)
|
|
}
|
|
|
|
class SecondClassTrivial {
|
|
var x: Int
|
|
var y: Int
|
|
|
|
init?(n: Int) {
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
x = 0
|
|
|
|
if n == 1 {
|
|
return nil
|
|
}
|
|
|
|
y = 0
|
|
|
|
if n == 2 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
deinitCalled += 1
|
|
}
|
|
}
|
|
|
|
FailableInitTestSuite.test("SecondClassTrivial") {
|
|
deinitCalled = 0
|
|
|
|
mustFail { SecondClassTrivial(n: 0) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { SecondClassTrivial(n: 1) }
|
|
expectEqual(0, deinitCalled)
|
|
|
|
mustFail { SecondClassTrivial(n: 2) }
|
|
expectEqual(1, deinitCalled)
|
|
|
|
mustSucceed { SecondClassTrivial(n: 3) }
|
|
expectEqual(2, deinitCalled)
|
|
}
|
|
|
|
runAllTests()
|