@@ -616,6 +616,69 @@ class Callee {
616
616
return result;
617
617
}
618
618
619
+
620
+ // / Determine if the target `func` should be replaced with a
621
+ // / 'distributed thunk'.
622
+ // /
623
+ // / This only applies to distributed functions when calls are made cross-actor
624
+ // / isolation. One notable exception is a distributed thunk calling the "real
625
+ // / underlying method", in which case (to avoid the thunk calling into itself,
626
+ // / the real method must be called).
627
+ // /
628
+ // / Witness calls which may need to be replaced with a distributed thunk call
629
+ // / happen either when the target type is generic, or if we are inside an
630
+ // / extension on a protocol. This method checks if we are in a context
631
+ // / where we should be calling the distributed thunk of the `func` or not.
632
+ // / Notably, if we are inside a distributed thunk already and are trying to
633
+ // / apply distributed method calls, all those must be to the "real" method,
634
+ // / because the thunks' responsibility is to call the real method, so this
635
+ // / replacement cannot be applied (or we'd recursively keep calling the same
636
+ // / thunk via witness).
637
+ // /
638
+ // / In situations which do not use a witness call, distributed methods are always
639
+ // / invoked Direct, and never ClassMethod, because distributed are effectively
640
+ // / final.
641
+ // /
642
+ // / \param constant the target that we want to dispatch to
643
+ // / \return true when the function should be considered for replacement
644
+ // / with distributed thunk when applying it
645
+ bool shouldDispatchWitnessViaDistributedThunk (
646
+ SILGenFunction &SGF,
647
+ std::optional<SILDeclRef> constant
648
+ ) const {
649
+ if (!constant.has_value ())
650
+ return false ;
651
+
652
+ auto func = dyn_cast<FuncDecl>(constant->getDecl ());
653
+ if (!func)
654
+ return false ;
655
+
656
+ auto isDistributedFuncOrAccessor =
657
+ func->isDistributed ();
658
+ if (auto acc = dyn_cast<AccessorDecl>(func)) {
659
+ isDistributedFuncOrAccessor =
660
+ acc->getStorage ()->isDistributed ();
661
+ }
662
+
663
+ if (!isDistributedFuncOrAccessor)
664
+ return false ;
665
+
666
+ // If we are inside a distributed thunk, we want to call the "real" method,
667
+ // in order to avoid infinitely recursively calling the thunk from itself.
668
+ if (SGF.F .isDistributed () && SGF.F .isThunk ())
669
+ return false ;
670
+
671
+ // If caller and called func are isolated to the same (distributed) actor,
672
+ // (i.e. we are "inside the distributed actor"), there is no need to call
673
+ // the thunk.
674
+ if (isSameActorIsolated (func, SGF.FunctionDC ))
675
+ return false ;
676
+
677
+ // In all other situations, we may have to replace the called function,
678
+ // depending on isolation (to be checked in SILGenApply).
679
+ return true ;
680
+ }
681
+
619
682
ManagedValue getFnValue (SILGenFunction &SGF,
620
683
std::optional<ManagedValue> borrowedSelf) const & {
621
684
std::optional<SILDeclRef> constant = std::nullopt;
@@ -681,22 +744,12 @@ class Callee {
681
744
return fn;
682
745
}
683
746
case Kind::WitnessMethod: {
684
- if (auto func = constant->getFuncDecl ()) {
685
- auto isDistributedFuncOrAccessor =
686
- func->isDistributed ();
687
- if (auto acc = dyn_cast<AccessorDecl>(func)) {
688
- isDistributedFuncOrAccessor =
689
- acc->getStorage ()->isDistributed ();
690
- }
691
- if (isa<ProtocolDecl>(func->getDeclContext ()) && isDistributedFuncOrAccessor) {
692
- // If we're calling cross-actor, we must always use a distributed thunk
693
- if (!isSameActorIsolated (func, SGF.FunctionDC )) {
694
- // the protocol witness must always be a distributed thunk, as we
695
- // may be crossing a remote boundary here.
696
- auto thunk = func->getDistributedThunk ();
697
- constant = SILDeclRef (thunk).asDistributed ();
698
- }
699
- }
747
+ if (shouldDispatchWitnessViaDistributedThunk (SGF, constant)) {
748
+ auto func = dyn_cast<FuncDecl>(constant->getDecl ());
749
+ assert (func); // guaranteed be non-null if shouldDispatch returned true
750
+
751
+ auto thunk = func->getDistributedThunk ();
752
+ constant = SILDeclRef (thunk).asDistributed ();
700
753
}
701
754
702
755
auto constantInfo =
@@ -783,12 +836,8 @@ class Callee {
783
836
}
784
837
case Kind::WitnessMethod: {
785
838
if (auto func = constant->getFuncDecl ()) {
786
- if (func->isDistributed () && isa<ProtocolDecl>(func->getDeclContext ())) {
787
- // If we're calling cross-actor, we must always use a distributed thunk
788
- if (!isSameActorIsolated (func, SGF.FunctionDC )) {
789
- // / We must adjust the constant to use a distributed thunk.
790
- constant = constant->asDistributed ();
791
- }
839
+ if (shouldDispatchWitnessViaDistributedThunk (SGF, constant)) {
840
+ constant = constant->asDistributed ();
792
841
}
793
842
}
794
843
0 commit comments