|
| 1 | +// RUN: %target-typecheck-verify-swift -enable-experimental-move-only -strict-concurrency=complete -disable-availability-checking |
| 2 | + |
| 3 | +// REQUIRES: concurrency |
| 4 | + |
| 5 | + |
| 6 | +struct CopyableStruct {} |
| 7 | +class Ref { var x = 0 } // expected-note 3{{class 'Ref' does not conform to the 'Sendable' protocol}} |
| 8 | + |
| 9 | +@_moveOnly |
| 10 | +struct FileDescriptor: Sendable { |
| 11 | + var id = 0 |
| 12 | +} |
| 13 | + |
| 14 | +@_moveOnly |
| 15 | +enum MaybeFile { // should implicitly conform |
| 16 | + case available(FileDescriptor) |
| 17 | + case closed |
| 18 | +} |
| 19 | + |
| 20 | +@_moveOnly |
| 21 | +struct NotSendableMO { // expected-note 2{{consider making struct 'NotSendableMO' conform to the 'Sendable' protocol}} |
| 22 | + var ref: Ref |
| 23 | +} |
| 24 | + |
| 25 | +// expect no warnings about sendable conformance when crossing actor boundaries: |
| 26 | +func invalidFile() async -> FileDescriptor { |
| 27 | + return FileDescriptor(id: -1) |
| 28 | +} |
| 29 | + |
| 30 | +func takeNotSendable(_ nsmo: NotSendableMO) async {} |
| 31 | + |
| 32 | +actor A { |
| 33 | + init(_ t: FileDescriptor) {} |
| 34 | + init (_ t: MaybeFile) {} |
| 35 | + func takeFileDescriptor(_ fd: __owned FileDescriptor) {} |
| 36 | + func takeMaybeFile(_ mfd: __owned MaybeFile) {} |
| 37 | + func giveFileDescriptor() -> MaybeFile { |
| 38 | + return .closed |
| 39 | + } |
| 40 | + |
| 41 | + func getRef() -> NotSendableMO { return NotSendableMO(ref: Ref()) } |
| 42 | +} |
| 43 | + |
| 44 | +@MainActor |
| 45 | +func processFiles(_ a: A, _ anotherFile: FileDescriptor) async { |
| 46 | + let file = await invalidFile() |
| 47 | + await a.takeFileDescriptor(file) |
| 48 | + |
| 49 | + await a.takeMaybeFile(.available(anotherFile)) |
| 50 | + _ = A(.available(anotherFile)) |
| 51 | + |
| 52 | + let ns = await a.getRef() // expected-warning {{non-sendable type 'NotSendableMO' returned by implicitly asynchronous call to actor-isolated instance method 'getRef()' cannot cross actor boundary}} |
| 53 | + await takeNotSendable(ns) // expected-warning {{non-sendable type 'NotSendableMO' exiting main actor-isolated context in call to non-isolated global function 'takeNotSendable' cannot cross actor boundary}} |
| 54 | + |
| 55 | + switch (await a.giveFileDescriptor()) { |
| 56 | + case let .available(fd): |
| 57 | + await a.takeFileDescriptor(fd) |
| 58 | + default: |
| 59 | + break |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +func caller() async { |
| 64 | + await processFiles(A(invalidFile()), invalidFile()) |
| 65 | +} |
| 66 | + |
| 67 | +// now make sure you can't form a Sendable existential from a move-only type. |
| 68 | + |
| 69 | +@_moveOnly |
| 70 | +struct RefPair: Sendable { |
| 71 | + var left: Ref // expected-warning {{stored property 'left' of 'Sendable'-conforming struct 'RefPair' has non-sendable type 'Ref'}} |
| 72 | + var right: Ref // expected-warning {{stored property 'right' of 'Sendable'-conforming struct 'RefPair' has non-sendable type 'Ref'}} |
| 73 | +} |
| 74 | + |
| 75 | +@_moveOnly |
| 76 | +enum MaybeRef: Sendable { |
| 77 | + case ref(Ref) // expected-warning {{associated value 'ref' of 'Sendable'-conforming enum 'MaybeRef' has non-sendable type 'Ref'}} |
| 78 | + case null |
| 79 | +} |
| 80 | + |
| 81 | +@_moveOnly |
| 82 | +enum OK_NoncopyableOption<T: Sendable> : Sendable { |
| 83 | + case some(T) |
| 84 | + case none |
| 85 | +} |
| 86 | + |
| 87 | +@_moveOnly |
| 88 | +enum Wrong_NoncopyableOption<T> : Sendable { // expected-note {{consider making generic parameter 'T' conform to the 'Sendable' protocol}} |
| 89 | + case some(T) // expected-warning {{associated value 'some' of 'Sendable'-conforming generic enum 'Wrong_NoncopyableOption' has non-sendable type 'T'}} |
| 90 | + case none |
| 91 | +} |
| 92 | + |
| 93 | +func takeAnySendable(_ s: any Sendable) {} |
| 94 | +func takeSomeSendable(_ s: some Sendable) {} |
| 95 | + |
| 96 | +// expected-error@+1 {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 97 | +func mkSendable() -> Sendable { return FileDescriptor(id: 0) } |
| 98 | + |
| 99 | +func tryToCastIt(_ fd: FileDescriptor) { |
| 100 | + let _: any Sendable = fd // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 101 | + let _: Sendable = fd // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 102 | + |
| 103 | + takeAnySendable(fd) // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 104 | + takeSomeSendable(fd) // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 105 | + |
| 106 | + let _ = fd as Sendable // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 107 | + |
| 108 | + let _ = fd as? Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}} |
| 109 | + // expected-error@-1 {{marker protocol 'Sendable' cannot be used in a conditional cast}} |
| 110 | + |
| 111 | + let _ = fd as! Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}} |
| 112 | + |
| 113 | + let _ = fd is Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}} |
| 114 | + // expected-error@-1 {{marker protocol 'Sendable' cannot be used in a conditional cast}} |
| 115 | +} |
| 116 | + |
| 117 | +protocol GiveSendable<T> { |
| 118 | + associatedtype T: Sendable // expected-note {{protocol requires nested type 'T'; do you want to add it?}} |
| 119 | + func give() -> T |
| 120 | +} |
| 121 | + |
| 122 | +// make sure witnessing associatedtypes is still prevented, even though we meet the explicit constraint. |
| 123 | +class Bad: GiveSendable { // expected-error {{type 'Bad' does not conform to protocol 'GiveSendable'}} |
| 124 | + typealias T = FileDescriptor // expected-note {{possibly intended match 'Bad.T' (aka 'FileDescriptor') does not conform to '_Copyable'}} |
| 125 | + func give() -> FileDescriptor { return FileDescriptor(id: -1) } |
| 126 | +} |
| 127 | + |
| 128 | +class Ok: GiveSendable { |
| 129 | + typealias T = CopyableStruct |
| 130 | + func give() -> CopyableStruct { return CopyableStruct() } |
| 131 | +} |
| 132 | + |
| 133 | +class Container<T> where T:Sendable { |
| 134 | + var elm: T |
| 135 | + init(_ t: T) { self.elm = t } |
| 136 | +} |
| 137 | + |
| 138 | +func createContainer(_ fd: FileDescriptor) { |
| 139 | + let _: Container<Sendable> = Container(fd) // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 140 | + let _: Container<Sendable> = Container(CopyableStruct()) |
| 141 | +} |
| 142 | + |
| 143 | +func takeTwo<T: Sendable>(_ s1: T, _ s2: T) {} |
| 144 | + |
| 145 | +extension Sendable { |
| 146 | + func doIllegalThings() { |
| 147 | + return takeTwo(self, self) |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +func tryToDupe(_ fd: FileDescriptor) { |
| 152 | + fd.doIllegalThings() // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}} |
| 153 | +} |
0 commit comments