Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 9 additions & 36 deletions TSPL.docc/LanguageGuide/Concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -1324,52 +1324,25 @@ struct TemperatureReading {
-->

To explicitly mark a type as not being sendable,
overriding an implicit conformance to the `Sendable` protocol,
use an extension:
write `~Sendable` after the type:

```swift
struct FileDescriptor {
let rawValue: CInt
struct FileDescriptor: ~Sendable {
let rawValue: Int
}

@available(*, unavailable)
extension FileDescriptor: Sendable { }
```

<!--
The example above is abbreviated from a Swift System API.
The example above is based on a Swift System API.
https://github.com/apple/swift-system/blob/main/Sources/System/FileDescriptor.swift
-->

The code above shows part of a wrapper around POSIX file descriptors.
Even though interface for file descriptors uses integers
to identify and interact with open files,
and integer values are sendable,
a file descriptor isn't safe to send across concurrency domains.

<!--
- test: `suppressing-implied-sendable-conformance`

-> struct FileDescriptor {
-> let rawValue: CInt
-> }

-> @available(*, unavailable)
-> extension FileDescriptor: Sendable { }
>> let nonsendable: Sendable = FileDescriptor(rawValue: 10)
!$ warning: conformance of 'FileDescriptor' to 'Sendable' is unavailable; this is an error in Swift 6
!! let nonsendable: Sendable = FileDescriptor(rawValue: 10)
!! ^
!$ note: conformance of 'FileDescriptor' to 'Sendable' has been explicitly marked unavailable here
!! extension FileDescriptor: Sendable { }
!! ^
See also this PR that adds Sendable conformance to FileDescriptor:
https://github.com/apple/swift-system/pull/112
-->

In the code above,
the `FileDescriptor` is a structure
that meets the criteria to be implicitly sendable.
However, the extension makes its conformance to `Sendable` unavailable,
preventing the type from being sendable.
For more information about
suppressing an implicit conformance to a protocol,
see <doc:Protocols#Implicit-Conformance-to-a-Protocol>.

<!--
OUTLINE
Expand Down
69 changes: 69 additions & 0 deletions TSPL.docc/LanguageGuide/Generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,75 @@ Taken together, these constraints mean that
the value passed for the `indices` parameter
is a sequence of integers.

## Implicit Constraints

In addition to the constraints you write explicitly,
many places in your code
also implicitly include an constraint
that types conform to some very common protocols
like [`Copyable`][].
<!-- When SE-0446 is implemented, add Escapable above -->
For information on when a protocol is implied,
see the reference for that protocol.

[`Copyable`]: https://developer.apple.com/documentation/swift/copyable

This constraint is implicit because
almost all types in Swift conform to these protocols,
so you specify only the exceptions.
For example, both of the following function declarations
require `MyType` to be copyable:

```swift
function someFunction<MyType> { ... }
function someFunction<MyType: Copyable> { ... }
```

Both declarations of `someFunction()` in the code above
require the generic type parameter `MyType` to be copyable.
In the first version, the constraint is implicit;
the second version lists the explicitly.
In most code,
types also implicitly conform to these common protocol.
For more information,
see <doc:Protocols#Implicit-Conformance-to-a-Protocol>.

To suppress an implicit constraint,
you write the protocol name with a tilde (`~`) in front of it.
You can read `~Copyable` as "maybe copyable" ---
this suppressed constraint allows
both copyable and noncopyable types in this position.
Note that `~Copyable` doesn't *require* the type to be noncopyable.
For example:

```swift
func f<MyType>(x: inout MyType) {
let x1 = x // The value of x1 is a copy of x's value.
let x2 = x // The value of x2 is a copy of x's value.
}

func g<AnotherType: ~Copyable>(y: inout AnotherType) {
let y1 = y // The assignment consumes y's value.
let y2 = y // Error: Value consumed more than once.
}
```

In the code above,
the function `f()` implicitly requires `MyType` to be copyable.
Within the function body,
the value of `x` is copied to `x1` and `x2` in the assignment.
In contrast, `g()` suppresses the implicit constraint on `AnotherType`,
which allows you to pass either a copyable or noncopyable value.
Within the function body,
you can't copy the value of `y`
because `AnotherType` might be noncopyable.
Assignment consumes the value of `y`
and it's an error to consume that value more than once.
Noncopyable values like `y`
must be passed as in-out, borrowing, or consuming parameters ---
for more information,
see <doc:Declarations#Borrowing-and-Consuming-Parameters>.

<!--
TODO: Generic Enumerations
--------------------------
Expand Down
139 changes: 139 additions & 0 deletions TSPL.docc/LanguageGuide/Protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,59 @@ a nonfailable initializer or an implicitly unwrapped failable initializer.
```
-->

## Protocols That Don't Have Requirements

All of the example protocols above have some requirements,
but a protocol doesn't have to include any requirements.
You can use a protocol to mark types that satisfy *semantic* requirements,
not just requirements that you express in code.
<!--
Avoiding the term "marker protocol",
which more specifically refers to @_marker on a protocol.
-->
The Swift standard library defines several protocols
that don't have any required methods or properties:

- [`Copyable`][] for values that can be copied.
- [`Sendable`][] for values that can be shared across concurrency contexts.
- [`BitwiseCopyable`][] for values that con be copied, bit-by-bit.

[`Copyable`]: https://developer.apple.com/documentation/swift/copyable
[`Sendable`]: https://developer.apple.com/documentation/swift/sendable
[`BitwiseCopyable`]: https://developer.apple.com/documentation/swift/bitwisecopyable

<!--
These link definitions are also used in the section below,
Implicit Conformance to a Protocol.
-->

For more information about the semantic requirements,
see the protocols' documentation.

You use the same syntax as usual to adopt these protocols.
The only difference is that
there's no code to implement the protocol's requirements.

```swift
struct MyStruct: Copyable {
var counter = 12
}

extension MyStruct: BitwiseCopyable { }
```

The code above defines a new structure
Because `Copyable` has only semantic requirements,
there isn't any code in the structure declaration to adopt the protocol.
Likewise, because `BitwiseCopyable` has only semantic requirements,
the extension that adopts that protocol has an empty body.

You usually don't need to write conformance to these protocols ---
instead, Swift implicitly adds the conformance for you,
as described in <doc:Protocols#Implicit-Conformance-to-a-Protocol>.

<!-- XXX TR: Mention why you might define your own empty protocols? -->

## Protocols as Types

Protocols don't actually implement any functionality themselves.
Expand Down Expand Up @@ -1368,6 +1421,92 @@ for level in levels.sorted() {
```
-->

## Implicit Conformance to a Protocol

Some protocols are so common that you would write them on almost every type.
For the following protocols,
Swift automatically infers the conformance
when you define a type that implements the protocol's requirements:

- `Codable`
- `Copyable`
- `Sendable`
- `BitwiseCopyable`

[`Codable`]: https://developer.apple.com/documentation/swift/codable
<!--
The remaining definitions for the links in this list
are in the section above, Protocols That Don't Have Requirements.
-->

You can still write the conformance explicitly,
but it doesn't have any effect.
To suppress an implicit conformance,
write a tilde (`~`) before the protocol name in the conformance list:

```swift
struct FileDescriptor: ~Sendable {
let rawValue: Int
}
```

<!--
The example above is based on a Swift System API.
https://github.com/apple/swift-system/blob/main/Sources/System/FileDescriptor.swift

See also this PR that adds Sendable conformance to FileDescriptor:
https://github.com/apple/swift-system/pull/112

XXX SE-0390 uses the same example but ~Copyable -- is that better?
-->

The code above shows part of a wrapper around POSIX file descriptors.
The `FileDescriptor` structure
satisfies all of the requirements of the `Sendable` protocol,
which would normally make it sendable.
However,
writing `~Sendable` suppresses this implicit conformance.
Even though file descriptors use integers
to identify and interact with open files,
and integer values are sendable,
making it nonsendable can help avoid certain kinds of bugs.

Another way to suppress implicit conformance
is with an extension that you mark as unavailable:

```swift
@available(*, unavailable)
extension FileDescriptor Sendable { }
```

<!--
- test: `suppressing-implied-sendable-conformance`

-> struct FileDescriptor {
-> let rawValue: CInt
-> }

-> @available(*, unavailable)
-> extension FileDescriptor: Sendable { }
>> let nonsendable: Sendable = FileDescriptor(rawValue: 10)
!$ warning: conformance of 'FileDescriptor' to 'Sendable' is unavailable; this is an error in Swift 6
!! let nonsendable: Sendable = FileDescriptor(rawValue: 10)
!! ^
!$ note: conformance of 'FileDescriptor' to 'Sendable' has been explicitly marked unavailable here
!! extension FileDescriptor: Sendable { }
!! ^
-->

When you write `~Sendable` in one place in your code,
as in the previous example,
code elsewhere in your program can still
extend the `FileDescriptor` type to add `Sendable` conformance.
In contrast,
the unavailable extension in this example
suppresses the implicit conformance to `Sendable`
and also prevents any extensions elsewhere in your code
from adding `Sendable` conformance to the type.

## Collections of Protocol Types

A protocol can be used as the type to be stored in
Expand Down
2 changes: 2 additions & 0 deletions TSPL.docc/ReferenceManual/Declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,8 @@ if you want more specific control,
you can apply the `borrowing` or `consuming` parameter modifier.
In this case,
use `copy` to explicitly mark copy operations.
In addition,
values of a noncopyable type must be passed as either borrowing or consuming.

Regardless of whether you use the default rules,
Swift guarantees that object lifetime and
Expand Down