mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Track liveness of self so we don't accidentally think that such types can be memberwise reinitialized. Fixes rdar://110232973 ([move-only] Checker should distinguish in between field of single field struct vs parent field itself (was: mutation of field in noncopyable struct should not trigger deinit))
209 lines
4.5 KiB
Swift
209 lines
4.5 KiB
Swift
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-feature -Xfrontend MoveOnlyEnumDeinits -Xfrontend -sil-verify-all) | %FileCheck %s --implicit-check-not closing
|
|
// RUN: %target-run-simple-swift(-O -Xfrontend -enable-experimental-feature -Xfrontend MoveOnlyEnumDeinits -Xfrontend -sil-verify-all) | %FileCheck %s --implicit-check-not closing
|
|
|
|
// REQUIRES: executable_test
|
|
|
|
// NOTE: it's important that this test has the `--implicit-check-not closing` flag to catch double deinits!!
|
|
// you also want to make sure all messages start with "closing"
|
|
func posix_close(_ x: Int) { print("closing file descriptor: \(x)") }
|
|
func print_closing_MFD() { print("closing MaybeFileDescriptor") }
|
|
|
|
enum E: Error { case err }
|
|
|
|
struct FileDescriptor: ~Copyable {
|
|
var fd: Int
|
|
static var nextFD: Int = 0
|
|
|
|
consuming func discard() { discard self }
|
|
|
|
init() {
|
|
self.fd = FileDescriptor.nextFD
|
|
FileDescriptor.nextFD += 1
|
|
}
|
|
|
|
init(doDiscard: Bool) throws {
|
|
self.init()
|
|
if doDiscard {
|
|
discard()
|
|
throw E.err
|
|
}
|
|
}
|
|
|
|
__consuming func close() {
|
|
posix_close(fd)
|
|
discard self
|
|
}
|
|
|
|
__consuming func justDiscard() {
|
|
discard self
|
|
}
|
|
|
|
__consuming func empty() {}
|
|
|
|
var takeFileDescriptorWrong : Int {
|
|
__consuming get {
|
|
return fd
|
|
}
|
|
}
|
|
|
|
var takeFileDescriptorRight : Int {
|
|
__consuming get {
|
|
let x = fd
|
|
discard self
|
|
return x
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
posix_close(fd)
|
|
}
|
|
}
|
|
|
|
enum MaybeFileDescriptor: ~Copyable {
|
|
case some(FileDescriptor)
|
|
case nothing
|
|
|
|
consuming func discard() { discard self }
|
|
|
|
init(reinit: Bool) {
|
|
self = .some(FileDescriptor())
|
|
if reinit {
|
|
discard()
|
|
self = .some(FileDescriptor())
|
|
}
|
|
}
|
|
|
|
__consuming func skipDeinit() {
|
|
discard self
|
|
}
|
|
|
|
deinit {
|
|
// we can't do a borrowed switch yet so this is just printing some message
|
|
print_closing_MFD()
|
|
}
|
|
}
|
|
|
|
struct SillyEmptyGeneric<T>: ~Copyable {
|
|
consuming func identity(_ t: T) -> T {
|
|
discard self
|
|
return t
|
|
}
|
|
deinit { fatalError("ran unexpectedly!") }
|
|
}
|
|
|
|
struct SingleMutableField: ~Copyable {
|
|
var value = 0
|
|
|
|
consuming func justDiscard() {
|
|
discard self
|
|
}
|
|
|
|
deinit {
|
|
print("SingleMutableField.deinit")
|
|
}
|
|
}
|
|
|
|
// rdar://110232973 ([move-only] Checker should distinguish in between
|
|
// field of single field struct vs parent field itself (was: mutation
|
|
// of field in noncopyable struct should not trigger deinit))
|
|
//
|
|
// This test must not be in a closure.
|
|
@inline(never)
|
|
func testSingleMutableFieldNoMemberReinit() {
|
|
var x = SingleMutableField()
|
|
x.value = 20 // should not trigger deinit.
|
|
// CHECK-NOT: SingleMutableField.deinit
|
|
x.justDiscard()
|
|
}
|
|
|
|
func main() {
|
|
testSingleMutableFieldNoMemberReinit()
|
|
|
|
let _ = {
|
|
let x = FileDescriptor() // 0
|
|
x.close()
|
|
// CHECK: closing file descriptor: 0
|
|
}()
|
|
|
|
let _ = {
|
|
let _ = FileDescriptor() // 1
|
|
// CHECK: closing file descriptor: 1
|
|
}()
|
|
|
|
let _ = {
|
|
let x = FileDescriptor() // 2
|
|
x.justDiscard()
|
|
}()
|
|
|
|
let _ = {
|
|
let x = FileDescriptor() // 3
|
|
x.empty()
|
|
// CHECK: closing file descriptor: 3
|
|
}()
|
|
|
|
let _ = {
|
|
let x = FileDescriptor() // 4
|
|
_ = x.takeFileDescriptorWrong
|
|
// CHECK: closing file descriptor: 4
|
|
}()
|
|
|
|
let _ = {
|
|
let x = FileDescriptor() // 5
|
|
_ = x.takeFileDescriptorRight
|
|
}()
|
|
|
|
let _ = {
|
|
do {
|
|
// should throw before getting to close()
|
|
let x = try FileDescriptor(doDiscard: true) // 6
|
|
x.close()
|
|
} catch {}
|
|
}()
|
|
|
|
let _ = {
|
|
do {
|
|
let x = try FileDescriptor(doDiscard: false) // 7
|
|
x.close()
|
|
} catch {}
|
|
// CHECK: closing file descriptor: 7
|
|
}()
|
|
|
|
let _ = {
|
|
let _ = MaybeFileDescriptor(reinit: true) // 8 & 9
|
|
// CHECK: closing file descriptor: 8
|
|
// CHECK: closing MaybeFileDescriptor
|
|
// CHECK: closing file descriptor: 9
|
|
}()
|
|
|
|
let _ = {
|
|
let x = MaybeFileDescriptor(reinit: true) // 10 & 11
|
|
x.skipDeinit() // this skips the enum's deinit, not the file descriptor's!
|
|
// CHECK: closing file descriptor: 10
|
|
// CHECK: closing file descriptor: 11
|
|
}()
|
|
|
|
let _ = {
|
|
var maybe = MaybeFileDescriptor.nothing
|
|
maybe = .some(FileDescriptor()) // 12
|
|
// CHECK: closing MaybeFileDescriptor
|
|
// CHECK: closing MaybeFileDescriptor
|
|
// CHECK: closing file descriptor: 12
|
|
}()
|
|
|
|
let _ = {
|
|
let x = MaybeFileDescriptor(reinit: false) // 13
|
|
x.skipDeinit() // this skips the enum's deinit, not the file descriptor's!
|
|
// CHECK: closing file descriptor: 13
|
|
}()
|
|
|
|
let _ = {
|
|
let x = SillyEmptyGeneric<[Int]>()
|
|
let z = [1, 2]
|
|
let ans = x.identity(z)
|
|
assert(z == ans)
|
|
}()
|
|
|
|
}
|
|
|
|
main()
|