mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Introduce a builtin and API for getting the local actor from a distributed one
When an actual instance of a distributed actor is on the local node, it is
has the capabilities of `Actor`. This isn't expressible directly in the type
system, because not all `DistributedActor`s are `Actor`s, nor is the
opposite true.
Instead, provide an API `DistributedActor.asLocalActor` that can only
be executed when the distributed actor is known to be local (because
this API is not itself `distributed`), and produces an existential
`any Actor` referencing that actor. The resulting existential value
carries with it a special witness table that adapts any type
conforming to the DistributedActor protocol into a type that conforms
to the Actor protocol. It is "as if" one had written something like this:
extension DistributedActor: Actor { }
which, of course, is not permitted in the language. Nonetheless, we
lovingly craft such a witness table:
* The "type" being extended is represented as an extension context,
rather than as a type context. This hasn't been done before, all Swift
runtimes support it uniformly.
* A special witness is provided in the Distributed library to implement
the `Actor.unownedExecutor` operation. This witness back-deploys to the
Swift version were distributed actors were introduced (5.7). On Swift
5.9 runtimes (and newer), it will use
`DistributedActor.unownedExecutor` to support custom executors.
* The conformance of `Self: DistributedActor` is represented as a
conditional requirement, which gets satisfied by the witness table
that makes the type a `DistributedActor`. This makes the special
witness work.
* The witness table is *not* visible via any of the normal runtime
lookup tables, because doing so would allow any
`DistributedActor`-conforming type to conform to `Actor`, which would
break the safety model.
* The witness table is emitted on demand in any client that needs it.
In back-deployment configurations, there may be several witness tables
for the same concrete distributed actor conforming to `Actor`.
However, this duplication can only be observed under fairly extreme
circumstances (where one is opening the returned existential and
instantiating generic types with the distributed actor type as an
`Actor`, then performing dynamic type equivalence checks), and will
not be present with a new Swift runtime.
All of these tricks together mean that we need no runtime changes, and
`asLocalActor` back-deploys as far as distributed actors, allowing it's
use in `#isolation` and the async for...in loop.
This commit is contained in:
@@ -468,6 +468,10 @@ public:
|
||||
/// Get the conformance substitution map.
|
||||
SubstitutionMap getSubstitutionMap() const;
|
||||
|
||||
/// Whether this conformance was synthesized automatically and can have
|
||||
/// multiple copies in a single program.
|
||||
bool isSynthesized() const;
|
||||
|
||||
/// Apply the given function object to each value witness within this
|
||||
/// protocol conformance.
|
||||
///
|
||||
@@ -679,6 +683,16 @@ public:
|
||||
/// modules, but in a manner that ensures that all copies are equivalent.
|
||||
bool isSynthesizedNonUnique() const;
|
||||
|
||||
/// Whether this conformance represents the conformance of one protocol's
|
||||
/// conforming types to another protocol.
|
||||
///
|
||||
/// Such conformances cannot generally be written in the surface language, but
|
||||
/// can be made available for specific tasks. The only such instance at the
|
||||
/// time of this writing is that a (local) distributed actor can conform to
|
||||
/// a local actor, but the witness table can only be used via a specific
|
||||
/// builtin to form an existential.
|
||||
bool isConformanceOfProtocol() const;
|
||||
|
||||
/// Whether clients from outside the module can rely on the value witnesses
|
||||
/// being consistent across versions of the framework.
|
||||
bool isResilient() const;
|
||||
|
||||
Reference in New Issue
Block a user