mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +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:
@@ -290,6 +290,13 @@ bool RootProtocolConformance::hasWitness(ValueDecl *requirement) const {
|
||||
ROOT_CONFORMANCE_SUBCLASS_DISPATCH(hasWitness, (requirement))
|
||||
}
|
||||
|
||||
bool RootProtocolConformance::isSynthesized() const {
|
||||
if (auto normal = dyn_cast<NormalProtocolConformance>(this))
|
||||
return normal->isSynthesizedNonUnique() || normal->isConformanceOfProtocol();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NormalProtocolConformance::isRetroactive() const {
|
||||
auto module = getDeclContext()->getParentModule();
|
||||
|
||||
@@ -321,11 +328,17 @@ bool NormalProtocolConformance::isRetroactive() const {
|
||||
}
|
||||
|
||||
bool NormalProtocolConformance::isSynthesizedNonUnique() const {
|
||||
// Check if the conformance was synthesized by the ClangImporter.
|
||||
if (auto *file = dyn_cast<FileUnit>(getDeclContext()->getModuleScopeContext()))
|
||||
return file->getKind() == FileUnitKind::ClangModule;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NormalProtocolConformance::isConformanceOfProtocol() const {
|
||||
return getDeclContext()->getSelfProtocolDecl() != nullptr;
|
||||
}
|
||||
|
||||
bool NormalProtocolConformance::isResilient() const {
|
||||
// If the type is non-resilient or the module we're in is non-resilient, the
|
||||
// conformance is non-resilient.
|
||||
|
||||
Reference in New Issue
Block a user