@@ -1591,6 +1591,307 @@ You can also use an unavailable conformance
1591
1591
to suppress implicit conformance to a protocol,
1592
1592
as discussed in < doc:Protocols#Implicit-Conformance-to-a-Protocol > .
1593
1593
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
+
1594
1895
<!--
1595
1896
LEFTOVER OUTLINE BITS
1596
1897
0 commit comments