@@ -3029,14 +3029,47 @@ bool ConformanceChecker::checkActorIsolation(
30293029 bool isDistributed = refResult.isolation .isDistributedActor () &&
30303030 !witness->getAttrs ().hasAttribute <NonisolatedAttr>();
30313031 if (isDistributed) {
3032- // If we're coming from a non-distributed requirement, then the requirement
3033- // must be 'throws' to accommodate failure.
3034- if (!isDistributedDecl (requirement) && !isThrowsDecl (requirement))
3035- missingOptions |= MissingFlags::RequirementThrows;
3036-
3037- if (!isDistributedDecl (witness) &&
3038- (isDistributedDecl (requirement) || !missingOptions))
3039- missingOptions |= MissingFlags::WitnessDistributed;
3032+ // Check if the protocol where the requirement originates from
3033+ // is a distributed actor constrained one.
3034+ auto proto = dyn_cast<ProtocolDecl>(requirement->getDeclContext ());
3035+ if (proto && proto->isDistributedActor ()) {
3036+ // The requirement was declared in a DistributedActor constrained proto.
3037+ //
3038+ // This means casting up to this `P` won't "strip off" the "distributed-ness"
3039+ // of the type, and all call-sites will be checking distributed isolation.
3040+ //
3041+ // This means that we can actually allow these specific requirements,
3042+ // to be witnessed without the distributed keyword (!), but they won't be
3043+ // possible to be called unless:
3044+ // - from inside the distributed actor (self),
3045+ // - on a known-to-be-local distributed actor reference.
3046+ //
3047+ // This allows us to implement protocols where a local distributed actor
3048+ // registers "call me when something happens", and that call can be
3049+ // expressed as non-distributed function which we are guaranteed to be
3050+ // able to call, since the whenLocal will give us access to this actor as
3051+ // known-to-be-local, so we can invoke this method.
3052+
3053+ // If the requirement is distributed, we still need to require it on the witness though.
3054+ // We DO allow a non-distributed requirement to be witnessed here though!
3055+ if (isDistributedDecl (requirement) &&
3056+ !isDistributedDecl (witness))
3057+ missingOptions |= MissingFlags::WitnessDistributed;
3058+ } else {
3059+ // The protocol requirement comes from a normal (non-distributed actor)
3060+ // protocol; so the only witnesses allowed are such that we can witness
3061+ // them using a distributed, or nonisolated functions.
3062+
3063+ // If we're coming from a non-distributed requirement,
3064+ // then the requirement must be 'throws' to accommodate failure.
3065+ if (!isDistributedDecl (requirement) && !isThrowsDecl (requirement))
3066+ missingOptions |= MissingFlags::RequirementThrows;
3067+
3068+ // If the requirement is distributed, we require a distributed witness
3069+ if (!isDistributedDecl (witness) &&
3070+ (isDistributedDecl (requirement) || !missingOptions))
3071+ missingOptions |= MissingFlags::WitnessDistributed;
3072+ }
30403073 }
30413074
30423075 // If we aren't missing anything, do a Sendable check and move on.
0 commit comments