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