Files
swift-mirror/test/Interpreter/moveonly_discard.swift
Andrew Trick a8c45c55c4 Fix MoveOnlyAddressChecker to handle value deinits.
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))
2023-06-19 18:38:03 -07:00

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()