Files
swift-mirror/test/Sema/moveonly_sendable.swift
Michael Gottesman 3ed4059a60 [sema] Change non-sendable -> non-Sendable in diagnostics.
This matches send non sendable but importantly also makes it clear that we are
talking about something that doesn't conform to the Sendable protocol which is
capitalized.

rdar://151802975
2025-05-22 11:37:58 -07:00

159 lines
6.3 KiB
Swift

// RUN: %target-typecheck-verify-swift -strict-concurrency=complete -target %target-swift-5.1-abi-triple
// REQUIRES: concurrency
// REQUIRES: asserts
struct CopyableStruct {}
class Ref { var x = 0 } // expected-note 3{{class 'Ref' does not conform to the 'Sendable' protocol}}
struct FileDescriptor: Sendable, ~Copyable {
var id = 0
}
enum MaybeFile: ~Copyable { // should implicitly conform
case available(FileDescriptor)
case closed
}
struct NotSendableMO: ~Copyable {
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()
await takeNotSendable(ns)
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.
struct RefPair: Sendable, ~Copyable {
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'}}
}
enum MaybeRef: Sendable, ~Copyable {
case ref(Ref) // expected-warning {{associated value 'ref' of 'Sendable'-conforming enum 'MaybeRef' has non-Sendable type 'Ref'}}
case null
}
enum OK_NoncopyableOption<T: Sendable> : Sendable, ~Copyable {
case some(T)
case none
}
enum Wrong_NoncopyableOption<T> : Sendable, ~Copyable { // 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'}}
func give() -> T
}
// make sure witnessing associatedtypes is still prevented, even though we meet the explicit constraint.
class Bad: GiveSendable {
// expected-error@-1 {{type 'Bad' does not conform to protocol 'GiveSendable'}}
// expected-note@-2 {{add stubs for conformance}}
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())
}
struct PaperAirplaneFile: ~Copyable {
var fd: FileDescriptor
}
extension PaperAirplaneFile: Sendable {}