Skip to content

Commit f55786a

Browse files
authored
Add discussion of ~Protocol syntax [SE-0390] (#351)
Fixes: rdar://110848216
2 parents 996cac6 + 173520c commit f55786a

File tree

5 files changed

+233
-38
lines changed

5 files changed

+233
-38
lines changed

TSPL.docc/LanguageGuide/Concurrency.md

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,52 +1324,25 @@ struct TemperatureReading {
13241324
-->
13251325

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

13301329
```swift
1331-
struct FileDescriptor {
1332-
let rawValue: CInt
1330+
struct FileDescriptor: ~Sendable {
1331+
let rawValue: Int
13331332
}
1334-
1335-
@available(*, unavailable)
1336-
extension FileDescriptor: Sendable { }
13371333
```
13381334

13391335
<!--
1340-
The example above is abbreviated from a Swift System API.
1336+
The example above is based on a Swift System API.
13411337
https://github.com/apple/swift-system/blob/main/Sources/System/FileDescriptor.swift
1342-
-->
13431338
1344-
The code above shows part of a wrapper around POSIX file descriptors.
1345-
Even though interface for file descriptors uses integers
1346-
to identify and interact with open files,
1347-
and integer values are sendable,
1348-
a file descriptor isn't safe to send across concurrency domains.
1349-
1350-
<!--
1351-
- test: `suppressing-implied-sendable-conformance`
1352-
1353-
-> struct FileDescriptor {
1354-
-> let rawValue: CInt
1355-
-> }
1356-
1357-
-> @available(*, unavailable)
1358-
-> extension FileDescriptor: Sendable { }
1359-
>> let nonsendable: Sendable = FileDescriptor(rawValue: 10)
1360-
!$ warning: conformance of 'FileDescriptor' to 'Sendable' is unavailable; this is an error in Swift 6
1361-
!! let nonsendable: Sendable = FileDescriptor(rawValue: 10)
1362-
!! ^
1363-
!$ note: conformance of 'FileDescriptor' to 'Sendable' has been explicitly marked unavailable here
1364-
!! extension FileDescriptor: Sendable { }
1365-
!! ^
1339+
See also this PR that adds Sendable conformance to FileDescriptor:
1340+
https://github.com/apple/swift-system/pull/112
13661341
-->
13671342

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

13741347
<!--
13751348
OUTLINE

TSPL.docc/LanguageGuide/Generics.md

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ or indeed an array for any other type that can be created in Swift.
1919
Similarly, you can create a dictionary to store values of any specified type,
2020
and there are no limitations on what that type can be.
2121

22-
## The Problem That Generics Solve
22+
## The Problem that Generics Solve
2323

2424
Here's a standard, nongeneric function called `swapTwoInts(_:_:)`,
2525
which swaps two `Int` values:
@@ -1967,6 +1967,77 @@ Taken together, these constraints mean that
19671967
the value passed for the `indices` parameter
19681968
is a sequence of integers.
19691969

1970+
## Implicit Constraints
1971+
1972+
In addition to constraints you write explicitly,
1973+
many places in your generic code also implicitly require
1974+
conformance to some very common protocols like [`Copyable`][].
1975+
<!-- When SE-0446 is implemented, add Escapable above. -->
1976+
These generic constraints that you don't have to write
1977+
are known as *implicit constraints*.
1978+
For example, both of the following function declarations
1979+
require `MyType` to be copyable:
1980+
1981+
[`Copyable`]: https://developer.apple.com/documentation/swift/copyable
1982+
1983+
```swift
1984+
function someFunction<MyType> { ... }
1985+
function someFunction<MyType: Copyable> { ... }
1986+
```
1987+
1988+
Both declarations of `someFunction()` in the code above
1989+
require the generic type parameter `MyType` to be copyable.
1990+
In the first version, the constraint is implicit;
1991+
the second version lists the explicitly.
1992+
In most code,
1993+
types also implicitly conform to these common protocols.
1994+
For more information,
1995+
see <doc:Protocols#Implicit-Conformance-to-a-Protocol>.
1996+
1997+
Because most types in Swift conform to these protocols,
1998+
writing them almost everywhere would be repetitive.
1999+
Instead, by marking only the exceptions,
2000+
your call out the places that omit a common constraint.
2001+
To suppress an implicit constraint,
2002+
write the protocol name with a tilde (`~`) in front of it.
2003+
You can read `~Copyable` as "maybe copyable" ---
2004+
this suppressed constraint allows
2005+
both copyable and noncopyable types in this position.
2006+
Note that `~Copyable` doesn't *require* the type to be noncopyable.
2007+
For example:
2008+
2009+
```swift
2010+
func f<MyType>(x: inout MyType) {
2011+
let x1 = x // The value of x1 is a copy of x's value.
2012+
let x2 = x // The value of x2 is a copy of x's value.
2013+
}
2014+
2015+
func g<AnotherType: ~Copyable>(y: inout AnotherType) {
2016+
let y1 = y // The assignment consumes y's value.
2017+
let y2 = y // Error: Value consumed more than once.
2018+
}
2019+
```
2020+
2021+
In the code above,
2022+
the function `f()` implicitly requires `MyType` to be copyable.
2023+
Within the function body,
2024+
the value of `x` is copied to `x1` and `x2` in the assignment.
2025+
In contrast, `g()` suppresses the implicit constraint on `AnotherType`,
2026+
which allows you to pass either a copyable or noncopyable value.
2027+
Within the function body,
2028+
you can't copy the value of `y`
2029+
because `AnotherType` might be noncopyable.
2030+
Assignment consumes the value of `y`
2031+
and it's an error to consume that value more than once.
2032+
Noncopyable values like `y`
2033+
must be passed as in-out, borrowing, or consuming parameters ---
2034+
for more information,
2035+
see <doc:Declarations#Borrowing-and-Consuming-Parameters>.
2036+
2037+
For details about when generic code
2038+
includes an implicit constraint to a given protocol,
2039+
see the reference for that protocol.
2040+
19702041
<!--
19712042
TODO: Generic Enumerations
19722043
--------------------------

TSPL.docc/LanguageGuide/OpaqueTypes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Boxed protocol types don't preserve type identity ---
2323
the value's specific type isn't known until runtime,
2424
and it can change over time as different values are stored.
2525

26-
## The Problem That Opaque Types Solve
26+
## The Problem that Opaque Types Solve
2727

2828
For example,
2929
suppose you're writing a module that draws ASCII art shapes.

TSPL.docc/LanguageGuide/Protocols.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,65 @@ a nonfailable initializer or an implicitly unwrapped failable initializer.
752752
```
753753
-->
754754

755+
## Protocols that Have Only Semantic Requirements
756+
757+
All of the example protocols above require some methods or properties,
758+
but a protocol declaration doesn't have to include any requirements.
759+
You can also use a protocol to describe *semantic* requirements ---
760+
that is, requirements about how values of those types behave
761+
and about operations that they support.
762+
<!--
763+
Avoiding the term "marker protocol",
764+
which more specifically refers to @_marker on a protocol.
765+
-->
766+
The Swift standard library defines several protocols
767+
that don't have any required methods or properties:
768+
769+
- [`Sendable`][] for values that can be shared across concurrency domains,
770+
as discussed in <doc:Concurrency#Sendable-Types>.
771+
- [`Copyable`][] for values that Swift can copy
772+
when you pass them to a function,
773+
as discussed in <doc:Declarations#Borrowing-and-Consuming-Parameters>.
774+
- [`BitwiseCopyable`][] for values that can be copied, bit-by-bit.
775+
776+
[`BitwiseCopyable`]: https://developer.apple.com/documentation/swift/bitwisecopyable
777+
[`Copyable`]: https://developer.apple.com/documentation/swift/copyable
778+
[`Sendable`]: https://developer.apple.com/documentation/swift/sendable
779+
780+
<!--
781+
These link definitions are also used in the section below,
782+
Implicit Conformance to a Protocol.
783+
-->
784+
785+
For information about these protocols' requirements,
786+
see the overview in their documentation.
787+
788+
You use the same syntax to adopt these protocols
789+
as you do to adopt other protocols.
790+
The only difference is that you don't include
791+
method or property declarations that implement the protocol's requirements.
792+
For example:
793+
794+
```swift
795+
struct MyStruct: Copyable {
796+
var counter = 12
797+
}
798+
799+
extension MyStruct: BitwiseCopyable { }
800+
```
801+
802+
The code above defines a new structure.
803+
Because `Copyable` has only semantic requirements,
804+
there isn't any code in the structure declaration to adopt the protocol.
805+
Similarly, because `BitwiseCopyable` has only semantic requirements,
806+
the extension that adopts that protocol has an empty body.
807+
808+
You usually don't need to write conformance to these protocols ---
809+
instead, Swift implicitly adds the conformance for you,
810+
as described in <doc:Protocols#Implicit-Conformance-to-a-Protocol>.
811+
812+
<!-- TODO: Mention why you might define your own empty protocols. -->
813+
755814
## Protocols as Types
756815

757816
Protocols don't actually implement any functionality themselves.
@@ -1368,6 +1427,96 @@ for level in levels.sorted() {
13681427
```
13691428
-->
13701429

1430+
## Implicit Conformance to a Protocol
1431+
1432+
Some protocols are so common that you would write them
1433+
almost every time you declare a new type.
1434+
For the following protocols,
1435+
Swift automatically infers the conformance
1436+
when you define a type that implements the protocol's requirements,
1437+
so you don't have to write them yourself:
1438+
1439+
- [`Copyable`][]
1440+
- [`Sendable`][]
1441+
- [`BitwiseCopyable`][]
1442+
1443+
<!--
1444+
The definitions for the links in this list
1445+
are in the section above, Protocols That Have Semantic Requirements.
1446+
-->
1447+
1448+
You can still write the conformance explicitly,
1449+
but it doesn't change how your code behaves.
1450+
To suppress an implicit conformance,
1451+
write a tilde (`~`) before the protocol name in the conformance list:
1452+
1453+
```swift
1454+
struct FileDescriptor: ~Sendable {
1455+
let rawValue: Int
1456+
}
1457+
```
1458+
1459+
<!--
1460+
The example above is based on a Swift System API.
1461+
https://github.com/apple/swift-system/blob/main/Sources/System/FileDescriptor.swift
1462+
1463+
See also this PR that adds Sendable conformance to FileDescriptor:
1464+
https://github.com/apple/swift-system/pull/112
1465+
-->
1466+
1467+
The code above shows part of a wrapper around POSIX file descriptors.
1468+
The `FileDescriptor` structure
1469+
satisfies all of the requirements of the `Sendable` protocol,
1470+
which normally makes it sendable.
1471+
However,
1472+
writing `~Sendable` suppresses this implicit conformance.
1473+
Even though file descriptors use integers
1474+
to identify and interact with open files,
1475+
and integer values are sendable,
1476+
making it nonsendable can help avoid certain kinds of bugs.
1477+
1478+
Another way to suppress implicit conformance
1479+
is with an extension that you mark as unavailable:
1480+
1481+
```swift
1482+
@available(*, unavailable)
1483+
extension FileDescriptor Sendable { }
1484+
```
1485+
1486+
<!--
1487+
- test: `suppressing-implied-sendable-conformance`
1488+
1489+
-> struct FileDescriptor {
1490+
-> let rawValue: CInt
1491+
-> }
1492+
1493+
-> @available(*, unavailable)
1494+
-> extension FileDescriptor: Sendable { }
1495+
>> let nonsendable: Sendable = FileDescriptor(rawValue: 10)
1496+
!$ warning: conformance of 'FileDescriptor' to 'Sendable' is unavailable; this is an error in Swift 6
1497+
!! let nonsendable: Sendable = FileDescriptor(rawValue: 10)
1498+
!! ^
1499+
!$ note: conformance of 'FileDescriptor' to 'Sendable' has been explicitly marked unavailable here
1500+
!! extension FileDescriptor: Sendable { }
1501+
!! ^
1502+
-->
1503+
1504+
When you write `~Sendable` in one place in your code,
1505+
as in the previous example,
1506+
code elsewhere in your program can still
1507+
extend the `FileDescriptor` type to add `Sendable` conformance.
1508+
In contrast,
1509+
the unavailable extension in this example
1510+
suppresses the implicit conformance to `Sendable`
1511+
and also prevents any extensions elsewhere in your code
1512+
from adding `Sendable` conformance to the type.
1513+
1514+
> Note:
1515+
> In addition to the protocols discussed above,
1516+
> distributed actors implicitly conform to the [`Codable`][] protocol.
1517+
1518+
[`Codable`]: https://developer.apple.com/documentation/swift/codable
1519+
13711520
## Collections of Protocol Types
13721521

13731522
A protocol can be used as the type to be stored in

TSPL.docc/ReferenceManual/Declarations.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,8 @@ if you want more specific control,
10691069
you can apply the `borrowing` or `consuming` parameter modifier.
10701070
In this case,
10711071
use `copy` to explicitly mark copy operations.
1072+
In addition,
1073+
values of a noncopyable type must be passed as either borrowing or consuming.
10721074

10731075
Regardless of whether you use the default rules,
10741076
Swift guarantees that object lifetime and

0 commit comments

Comments
 (0)