Skip to content

Commit 0b140a0

Browse files
committed
Mark certian actor-isolated ApplyExps as ImplicitlyAsync
Specifically, actor instance methods and global actor funcs. We also prevent partially-applied actor methods from being considered async.
1 parent 146f19c commit 0b140a0

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ namespace {
584584
class ActorIsolationChecker : public ASTWalker {
585585
ASTContext &ctx;
586586
SmallVector<const DeclContext *, 4> contextStack;
587+
SmallVector<ApplyExpr*, 4> applyStack;
587588

588589
const DeclContext *getDeclContext() const {
589590
return contextStack.back();
@@ -625,27 +626,45 @@ namespace {
625626
}
626627

627628
if (auto apply = dyn_cast<ApplyExpr>(expr)) {
629+
applyStack.push_back(apply); // record this encounter
630+
628631
// If this is a call to a partial apply thunk, decompose it to check it
629632
// like based on the original written syntax, e.g., "self.method".
630633
if (auto partialApply = decomposePartialApplyThunk(
631634
apply, Parent.getAsExpr())) {
632635
if (auto memberRef = findMemberReference(partialApply->fn)) {
636+
// NOTE: partially-applied thunks are never annotated as
637+
// implicitly async, regardless of whether they are escaping.
638+
// So, we do not pass the ApplyExpr along to checkMemberReference.
633639
checkMemberReference(
634640
partialApply->base, memberRef->first, memberRef->second,
635641
partialApply->isEscaping);
636642

637643
partialApply->base->walk(*this);
644+
645+
// manual clean-up since normal traversal is skipped
646+
assert(applyStack.back() == apply);
647+
applyStack.pop_back();
648+
638649
return { false, expr };
639650
}
640651
}
641652
}
642653

654+
// NOTE: SelfApplyExpr is a subtype of ApplyExpr
643655
if (auto call = dyn_cast<SelfApplyExpr>(expr)) {
644656
Expr *fn = call->getFn()->getValueProvidingExpr();
645657
if (auto memberRef = findMemberReference(fn)) {
646658
checkMemberReference(
647-
call->getArg(), memberRef->first, memberRef->second);
659+
call->getArg(), memberRef->first, memberRef->second,
660+
/*isEscapingPartialApply=*/false, call);
661+
648662
call->getArg()->walk(*this);
663+
664+
// manual clean-up since normal traversal is skipped
665+
assert(applyStack.back() == dyn_cast<ApplyExpr>(expr));
666+
applyStack.pop_back();
667+
649668
return { false, expr };
650669
}
651670
}
@@ -659,6 +678,11 @@ namespace {
659678
contextStack.pop_back();
660679
}
661680

681+
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
682+
assert(applyStack.back() == apply);
683+
applyStack.pop_back();
684+
}
685+
662686
return expr;
663687
}
664688

@@ -835,9 +859,39 @@ namespace {
835859
/// Check a reference to an entity within a global actor.
836860
bool checkGlobalActorReference(
837861
ValueDecl *value, SourceLoc loc, Type globalActor) {
862+
863+
/// Returns true if this global actor reference is the callee of an Apply.
864+
/// NOTE: This check mutates the identified ApplyExpr if it returns true!
865+
auto inspectForImplicitlyAsync = [&] () -> bool {
866+
867+
// Is this global actor reference outside of an ApplyExpr?
868+
if (applyStack.size() == 0)
869+
return false;
870+
871+
// Check our applyStack metadata from the traversal.
872+
// Our goal is to identify whether this global actor reference appears
873+
// as the called value of the enclosing ApplyExpr. We cannot simply
874+
// inspect Parent here because of expressions like (callee)()
875+
ApplyExpr *apply = applyStack.back();
876+
Expr *fn = apply->getFn()->getValueProvidingExpr();
877+
if (auto memberRef = findMemberReference(fn)) {
878+
auto concDecl = memberRef->first;
879+
if (value == concDecl.getDecl() && !apply->implicitlyAsync()) {
880+
// then this ValueDecl appears as the called value of the ApplyExpr.
881+
apply->setImplicitlyAsync(true);
882+
return true;
883+
}
884+
}
885+
886+
return false;
887+
};
888+
838889
switch (auto contextIsolation =
839890
getInnermostIsolatedContext(getDeclContext())) {
840891
case ActorIsolation::ActorInstance:
892+
if (inspectForImplicitlyAsync())
893+
return false;
894+
841895
ctx.Diags.diagnose(
842896
loc, diag::global_actor_from_instance_actor_context,
843897
value->getDescriptiveKind(), value->getName(), globalActor,
@@ -850,6 +904,12 @@ namespace {
850904
if (contextIsolation.getGlobalActor()->isEqual(globalActor))
851905
return false;
852906

907+
// Otherwise, we check if this decl reference is the callee of the
908+
// enclosing Apply, making it OK as an implicitly async call.
909+
if (inspectForImplicitlyAsync())
910+
return false;
911+
912+
// Otherwise, this is a problematic global actor decl reference.
853913
ctx.Diags.diagnose(
854914
loc, diag::global_actor_from_other_global_actor_context,
855915
value->getDescriptiveKind(), value->getName(), globalActor,
@@ -860,14 +920,18 @@ namespace {
860920

861921
case ActorIsolation::Independent:
862922
case ActorIsolation::IndependentUnsafe:
923+
if (inspectForImplicitlyAsync())
924+
return false;
925+
863926
ctx.Diags.diagnose(
864927
loc, diag::global_actor_from_independent_context,
865928
value->getDescriptiveKind(), value->getName(), globalActor);
866929
noteIsolatedActorMember(value);
867930
return true;
868931

869932
case ActorIsolation::Unspecified:
870-
// Okay.
933+
// Okay no matter what, but still must inspect for implicitly async.
934+
inspectForImplicitlyAsync();
871935
return false;
872936
}
873937
llvm_unreachable("unhandled actor isolation kind!");
@@ -918,9 +982,12 @@ namespace {
918982
}
919983

920984
/// Check a reference with the given base expression to the given member.
985+
/// Returns true iff the member reference refers to actor-isolated state
986+
/// in an invalid or unsafe way such that a diagnostic was emitted.
921987
bool checkMemberReference(
922988
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
923-
bool isEscapingPartialApply = false) {
989+
bool isEscapingPartialApply = false,
990+
ApplyExpr *maybeImplicitAsync = nullptr) {
924991
if (!base || !memberRef)
925992
return false;
926993

@@ -934,6 +1001,11 @@ namespace {
9341001
// Must reference actor-isolated state on 'self'.
9351002
auto selfVar = getSelfReference(base);
9361003
if (!selfVar) {
1004+
// actor-isolated non-self calls are implicitly async and thus OK.
1005+
if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1006+
maybeImplicitAsync->setImplicitlyAsync(true);
1007+
return false;
1008+
}
9371009
ctx.Diags.diagnose(
9381010
memberLoc, diag::actor_isolated_non_self_reference,
9391011
member->getDescriptiveKind(),

0 commit comments

Comments
 (0)