@@ -3029,14 +3029,47 @@ bool ConformanceChecker::checkActorIsolation(
3029
3029
bool isDistributed = refResult.isolation .isDistributedActor () &&
3030
3030
!witness->getAttrs ().hasAttribute <NonisolatedAttr>();
3031
3031
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
+ }
3040
3073
}
3041
3074
3042
3075
// If we aren't missing anything, do a Sendable check and move on.
0 commit comments