Files
swift-mirror/test/Sema/moveonly_sendable.swift
Kavon Farvardin c9cfe28e6d NCGenerics: improve diagnostics
Removing the old, ad-hoc diagnostics code improves the diagnostics we
emit, since the existing diagnostics for missing conformances is already
pretty good.

rdar://127369509
2024-06-04 15:06:32 -07:00

167 lines
7.0 KiB
Swift

// RUN: %target-typecheck-verify-swift -strict-concurrency=complete -disable-availability-checking -disable-region-based-isolation-with-strict-concurrency -verify-additional-prefix complete-
// 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 {}