@@ -1591,6 +1591,307 @@ You can also use an unavailable conformance
15911591to suppress implicit conformance to a protocol,
15921592as discussed in < doc:Protocols#Implicit-Conformance-to-a-Protocol > .
15931593
1594+ ## Isolated Protocol Conformances
1595+
1596+ Protocols that are nonisolated can be used from anywhere in a concurrent
1597+ program. An implementation of a nonisolated protocol conformance can still
1598+ use global actor isolated state. A conformance that needs global actor
1599+ isolated state is called an * isolated* conformance. When a conformance is
1600+ isolated, Swift prevents data races by ensuring that the conformance is only
1601+ used on the global actor that the conformance is isolated to.
1602+
1603+ ### Declaring an isolated conformance
1604+
1605+ You declare an isolated conformance by writing the global actor attribute
1606+ before the protocol name when you implement the conformance. The following
1607+ code example declares a main-actor isolated conformance to ` Equatable ` in
1608+ an extension:
1609+
1610+ ``` swift
1611+ @MainActor
1612+ class Person {
1613+ var id: Int
1614+ }
1615+
1616+ extension Person : @MainActor Equatable {
1617+ static func == (lhs : Person, rhs : Person) -> Bool {
1618+ return lhs.id == rhs.id
1619+ }
1620+ }
1621+ ```
1622+
1623+ This allows the implementation of the conformance to use global actor
1624+ isolated state while ensuring that state is only accessed from within the
1625+ actor. Isolated conformances are also inferred for global actor isolated
1626+ types. The following code example declares a conformance to ` Equatable ` for
1627+ a main-actor isolated class, and Swift infers main-actor isolation for the
1628+ conformance:
1629+
1630+ ``` swift
1631+ @MainActor
1632+ class Person {
1633+ var id: Int
1634+ }
1635+
1636+ // Inferred to be a @MainActor conformance to Equatable
1637+ extension Person : Equatable {
1638+ static func == (lhs : Person, rhs : Person) -> Bool {
1639+ return lhs.id == rhs.id
1640+ }
1641+ }
1642+ ```
1643+
1644+ You can opt out of this inference for a global-actor-isolated type by
1645+ explicitly declaring that a protocol conformance is nonisolated. The
1646+ following code example declares a nonisolated isolated conformance to
1647+ ` Equatable ` in an extension:
1648+
1649+ ``` swift
1650+ @MainActor
1651+ class Person {
1652+ let id: Int
1653+ }
1654+
1655+ extension Person : nonisolated Equatable {
1656+ nonisolated static func == (lhs : Person, rhs : Person) -> Bool {
1657+ return lhs.id == rhs.id
1658+ }
1659+ }
1660+ ```
1661+
1662+ ### Data-race safety for isolated conformances
1663+
1664+ Swift prevents data races for isolated conformances by ensuring that protocol
1665+ requirements are only called on the global actor that the conformance is
1666+ isolated to. In generic code, where the concrete conforming type is
1667+ abstracted away, protocol requirements can be called through type parameters
1668+ or ` any ` types.
1669+
1670+ #### Using isolated conformances
1671+
1672+ ##### Generic code
1673+
1674+ A conformance requirement to ` Sendable ` allows generic code to send parameter
1675+ values to concurrently-executing code. If generic code accepts non-` Sendable `
1676+ types, then the generic code can only use the input values from the current
1677+ isolation domain. These generic APIs can safely accept isolated conformances
1678+ and call protocol requirement as long as the caller is on the same global
1679+ actor that the conformance is isolated to. The following code has a protocol
1680+ ` P ` , a class ` C ` with a main-actor isolated conformance to ` P ` , and two
1681+ call-sites to a generic method that accepts ` some P ` :
1682+
1683+ ``` swift
1684+ protocol P {
1685+ func perform ()
1686+ }
1687+
1688+ func perform (_ p : some P) {
1689+ p.perform ()
1690+ }
1691+
1692+ @MainActor class C : P { ... }
1693+
1694+ Task { @MainActor in
1695+ let c = C ()
1696+ perform (c)
1697+ }
1698+
1699+ Task { @concurrent in
1700+ let c = C ()
1701+ perform (c) // Error
1702+ }
1703+ ```
1704+
1705+ The above code calls ` perform ` and provides an argument with a main-actor
1706+ isolated conformance to ` P ` . Calling ` perform ` from a main actor task is safe
1707+ because it matches the isolation of the conformance. Calling ` perform ` from a
1708+ concurrent task results in an error, because it would allow calling the main
1709+ actor isolated implementation of ` perform ` from outside the main actor.
1710+
1711+ ##### Dynamic casting
1712+
1713+ Generic code can check whether a value conforms to a protocol through dynamic
1714+ casting. The following code has a protocol ` P ` , and a method ` performIfP `
1715+ that accepts a parameter of type ` Any ` which is dynamic cast to ` any P ` in
1716+ the function body:
1717+
1718+ ``` swift
1719+ protocol P {
1720+ func perform ()
1721+ }
1722+
1723+ func performIfP (_ value : Any ) {
1724+ if let p = value as? any P {
1725+ p.perform ()
1726+ }
1727+ }
1728+ ```
1729+
1730+ Isolated conformances are only safe to use when the code is running on the
1731+ global actor that the conformance is isolated to, so the dynamic cast only
1732+ succeeds if the dynamic cast occurs on the global actor. If you declare a
1733+ main-actor isolated conformance to ` P ` and call ` performIfP ` with an instance
1734+ of the conforming type, the dynamic cast will only succeed when ` performIfP `
1735+ is called from the main actor:
1736+
1737+ ``` swift
1738+ @MainActor class C : P {
1739+ func perform () {
1740+ print (" C.perform" )
1741+ }
1742+ }
1743+
1744+ Task { @MainActor in
1745+ let c = C ()
1746+ performIfP (c) // Prints "C.perform"
1747+ }
1748+
1749+ Task { @concurrent in
1750+ let c = C ()
1751+ performIfP (c) // Prints nothing
1752+ }
1753+ ```
1754+
1755+ In the above code, the call to ` performIfP ` from a main-actor isolated task
1756+ matches the isolation of the conformance, so the dynamic cast succeeds. The
1757+ call to ` performIfP ` from a concurrent task happens outside the main actor,
1758+ so the dynamic cast fails and ` perform ` is not called.
1759+
1760+ #### Restricting isolated conformances in concurrent code
1761+
1762+ Protocol requirements can be called through instances of conforming types
1763+ and through metatype values. In generic code, a conformance requirement to
1764+ ` Sendable ` or ` SendableMetatype ` tells Swift that an instance or metatype
1765+ value is safe to use concurrently. To prevent isolated conformances from
1766+ being used outside of their actor, a type with an isolated conformance
1767+ cannot satisfy a conformance requirement to ` Sendable ` or ` SendableMetatype ` .
1768+
1769+ A conformance requirement to ` Sendable ` indicates that instances may be passed
1770+ across isolation boundaries and used concurrently:
1771+
1772+ ``` swift
1773+ protocol P {
1774+ func perform ()
1775+ }
1776+
1777+ func performConcurrently <T : P >(_ t : T) where T: Sendable {
1778+ Task { @concurrent in
1779+ t.perform ()
1780+ }
1781+ }
1782+ ```
1783+
1784+ The above code admits data races if the conformance to ` P ` is isolated,
1785+ because the implementation of ` perform ` may access global actor isolated
1786+ state. To prevent data races, Swift prohibits using an isolated conformance
1787+ when the type is also required to conform to ` Sendable ` :
1788+
1789+ ``` swift
1790+ @MainActor class C : P { ... }
1791+
1792+ let c = C ()
1793+ performConcurrently (c) // Error
1794+ ```
1795+
1796+ The above code results in an error because the conformance of ` C ` to ` P `
1797+ is main-actor isolated, which cannot satisfy the ` Sendable ` requirement
1798+ of ` performConcurrently ` .
1799+
1800+ Protocol requirements can also be called through metatype values. A
1801+ conformance to Sendable on the metatype type, such as ` Int.Type ` , indicates
1802+ that a metatype value is safe to pass across isolation boundaries and used
1803+ concurrently. Metatype types can conform to ` Sendable ` even when the type
1804+ does not conform to ` Sendable ` ; this means that only metatype values are safe
1805+ to share in concurrent code, but instances of the type are not.
1806+
1807+ In generic code, a conformance requirement to ` SendableMetatype ` indicates
1808+ that the metatype of a type conforms to ` Sendable ` , which allows the
1809+ implementation to share metatype values in concurrent code:
1810+
1811+ ``` swift
1812+ protocol P {
1813+ static func perform ()
1814+ }
1815+
1816+ func performConcurrently <T : P >(n : Int , for : T.Type ) async where T: SendableMetatype {
1817+ await withDiscardingTaskGroup { group in
1818+ for _ in 0 ..< n {
1819+ group.addTask {
1820+ T.perform ()
1821+ }
1822+ }
1823+ }
1824+ }
1825+ ```
1826+
1827+ Without a conformance to ` SendableMetatype ` , generic code must only use
1828+ metatype values in the current isolation domain. The following code
1829+ results in an error because the non-` Sendable ` metatype ` T ` is used from
1830+ concurrent child tasks:
1831+
1832+ ``` swift
1833+ protocol P {
1834+ static func perform ()
1835+ }
1836+
1837+ func performConcurrently <T : P >(n : Int , for : T.Type ) async {
1838+ await withDiscardingTaskGroup { group in
1839+ for _ in 0 ..< n {
1840+ group.addTask {
1841+ T.perform () // Error
1842+ }
1843+ }
1844+ }
1845+ }
1846+ ```
1847+
1848+ Note that Sendable requires ` SendableMetatype ` , so an explicit conformance to
1849+ ` SendableMetatype ` is only necessary if the type is non-` Sendable ` .
1850+
1851+ Types with isolated conformances cannot satisfy a ` SendableMetatype ` generic
1852+ requirement. Swift will prevent calling ` createParallel ` with a type that has
1853+ an isolated conformance to ` P ` :
1854+
1855+ ``` swift
1856+ @MainActor class C : P {
1857+ static func perform () { /* use main actor state */ }
1858+ }
1859+
1860+ let items = performConcurrently (n : 10 , for : C.self ) // Error
1861+ ```
1862+
1863+ ##### Protocols that require ` Sendable ` or ` SendableMetatype `
1864+
1865+ Protocols can directly require that conforming types also conform to
1866+ ` Sendable ` or ` SendableMetatype ` :
1867+
1868+ ``` swift
1869+ public protocol Error : Sendable {}
1870+
1871+ public protocol ModelFactory : SendableMetatype {
1872+ func create () -> Self
1873+ }
1874+ ```
1875+
1876+ Note that the ` Sendable ` protocol requires ` SendableMetatype ` ; if an instance
1877+ of a conforming type is safe to share across concurrent code, its metatype
1878+ must also be safe to share:
1879+
1880+ ``` swift
1881+ public protocol Sendable : SendableMetatype {}
1882+ ```
1883+
1884+ If a protocol requires ` Sendable ` , then any use of the protocol can freely
1885+ send instances across isolation boundaries. If a protocol requires
1886+ ` SendableMetatype ` , then uses of metatypes in generic code can cross
1887+ isolation boundaries. In both cases, Swift prevents declaring an isolated
1888+ conformance, because generic code can always call requirements concurrently.
1889+
1890+ ``` swift
1891+ @MainActor
1892+ enum MyError : @MainActor Error {} // Error
1893+ ```
1894+
15941895<!--
15951896 LEFTOVER OUTLINE BITS
15961897
0 commit comments