Skip to content

Commit 14a311b

Browse files
committed
[Global actors] Check calls to global-actor-qualified functions, not references
Check actor isolation of calls to functions with global-actor-qualified type. This closes a pre-existing loophole where a value of global-actor-qualified function type could be called from any context. Paired with this, references to global-actor-qualified function declarations will get global-actor-qualified function type whenever they are referenced within an experience, i.e., whenever we form a value of that type. Such references can occur anywhere (one does not need to be on the actor), and carrying the global actor along with the function type ensures that they can only be called from the right actor. For example: @mainactor func onlyOnMainActor() { ... } func callIt(_ fn: @mainactor () -> Void) { fn() // error: not on the main actor, so cannot synchronously call // this wasn't previously diagnosed } func passIt() { callIt(onlyOnMainActor) // okay to pass the function // used to be an error } While here, fix up some broken substitution logic for global-actor-qualified function types and "override" actor isolation.
1 parent cdb7de1 commit 14a311b

15 files changed

+293
-146
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4385,6 +4385,12 @@ ERROR(global_actor_from_nonactor_context,none,
43854385
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
43864386
" from %select{this|a non-isolated}3%select{| synchronous}5 context",
43874387
(DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool))
4388+
ERROR(actor_isolated_call,none,
4389+
"call to %0 function in a synchronous %1 context",
4390+
(ActorIsolation, ActorIsolation))
4391+
ERROR(actor_isolated_call_decl,none,
4392+
"call to %0 %1 %2 in a synchronous %3 context",
4393+
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
43884394
ERROR(actor_isolated_partial_apply,none,
43894395
"actor-isolated %0 %1 can not be partially applied",
43904396
(DescriptiveDeclKind, DeclName))

lib/AST/ASTContext.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3075,11 +3075,13 @@ DynamicSelfType *DynamicSelfType::get(Type selfType, const ASTContext &ctx) {
30753075

30763076
static RecursiveTypeProperties
30773077
getFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
3078-
Type result) {
3078+
Type result, Type globalActor) {
30793079
RecursiveTypeProperties properties;
30803080
for (auto param : params)
30813081
properties |= param.getPlainType()->getRecursiveProperties();
30823082
properties |= result->getRecursiveProperties();
3083+
if (globalActor)
3084+
properties |= globalActor->getRecursiveProperties();
30833085
properties &= ~RecursiveTypeProperties::IsLValue;
30843086
return properties;
30853087
}
@@ -3274,7 +3276,11 @@ void FunctionType::Profile(llvm::FoldingSetNodeID &ID,
32743276

32753277
FunctionType *FunctionType::get(ArrayRef<AnyFunctionType::Param> params,
32763278
Type result, Optional<ExtInfo> info) {
3277-
auto properties = getFunctionRecursiveProperties(params, result);
3279+
Type globalActor;
3280+
if (info.hasValue())
3281+
globalActor = info->getGlobalActor();
3282+
3283+
auto properties = getFunctionRecursiveProperties(params, result, globalActor);
32783284
auto arena = getArena(properties);
32793285

32803286
llvm::FoldingSetNodeID id;
@@ -3296,10 +3302,6 @@ FunctionType *FunctionType::get(ArrayRef<AnyFunctionType::Param> params,
32963302
bool hasClangInfo =
32973303
info.hasValue() && !info.getValue().getClangTypeInfo().empty();
32983304

3299-
Type globalActor;
3300-
if (info.hasValue())
3301-
globalActor = info->getGlobalActor();
3302-
33033305
size_t allocSize = totalSizeToAlloc<
33043306
AnyFunctionType::Param, ClangTypeInfo, Type
33053307
>(params.size(), hasClangInfo ? 1 : 0, globalActor ? 1 : 0);
@@ -3393,7 +3395,7 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig,
33933395
if (info.hasValue())
33943396
globalActor = info->getGlobalActor();
33953397

3396-
if (globalActor && !globalActor->isCanonical())
3398+
if (globalActor && !sig->isCanonicalTypeInContext(globalActor))
33973399
isCanonical = false;
33983400

33993401
size_t allocSize = totalSizeToAlloc<AnyFunctionType::Param, Type>(

lib/AST/DiagnosticEngine.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,18 @@ static Type getAkaTypeForDisplay(Type type) {
475475
});
476476
}
477477

478+
/// Determine whether this is the main actor type.
479+
static bool isMainActor(Type type) {
480+
if (auto nominal = type->getAnyNominal()) {
481+
if (nominal->getName().is("MainActor") &&
482+
nominal->getParentModule()->getName() ==
483+
nominal->getASTContext().Id_Concurrency)
484+
return true;
485+
}
486+
487+
return false;
488+
}
489+
478490
/// Format a single diagnostic argument and write it to the given
479491
/// stream.
480492
static void formatDiagnosticArgument(StringRef Modifier,
@@ -710,18 +722,21 @@ static void formatDiagnosticArgument(StringRef Modifier,
710722
break;
711723

712724
case ActorIsolation::GlobalActor:
713-
case ActorIsolation::GlobalActorUnsafe:
714-
Out << "global actor " << FormatOpts.OpeningQuotationMark
715-
<< isolation.getGlobalActor().getString()
716-
<< FormatOpts.ClosingQuotationMark << "-isolated";
725+
case ActorIsolation::GlobalActorUnsafe: {
726+
Type globalActor = isolation.getGlobalActor();
727+
if (isMainActor(globalActor)) {
728+
Out << "main actor-isolated";
729+
} else {
730+
Out << "global actor " << FormatOpts.OpeningQuotationMark
731+
<< globalActor.getString()
732+
<< FormatOpts.ClosingQuotationMark << "-isolated";
733+
}
717734
break;
735+
}
718736

719737
case ActorIsolation::Independent:
720-
Out << "actor-independent";
721-
break;
722-
723738
case ActorIsolation::Unspecified:
724-
Out << "unspecified";
739+
Out << "nonisolated";
725740
break;
726741
}
727742
}

lib/AST/Type.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4879,6 +4879,17 @@ case TypeKind::Id:
48794879
if (resultTy.getPointer() != function->getResult().getPointer())
48804880
isUnchanged = false;
48814881

4882+
// Transform the global actor.
4883+
Type globalActorType;
4884+
if (Type origGlobalActorType = function->getGlobalActor()) {
4885+
globalActorType = origGlobalActorType.transformRec(fn);
4886+
if (!globalActorType)
4887+
return Type();
4888+
4889+
if (globalActorType.getPointer() != origGlobalActorType.getPointer())
4890+
isUnchanged = false;
4891+
}
4892+
48824893
if (auto genericFnType = dyn_cast<GenericFunctionType>(base)) {
48834894
#ifndef NDEBUG
48844895
// Check that generic parameters won't be trasnformed.
@@ -4895,15 +4906,17 @@ case TypeKind::Id:
48954906
if (!function->hasExtInfo())
48964907
return GenericFunctionType::get(genericSig, substParams, resultTy);
48974908
return GenericFunctionType::get(genericSig, substParams, resultTy,
4898-
function->getExtInfo());
4909+
function->getExtInfo()
4910+
.withGlobalActor(globalActorType));
48994911
}
49004912

49014913
if (isUnchanged) return *this;
49024914

49034915
if (!function->hasExtInfo())
49044916
return FunctionType::get(substParams, resultTy);
49054917
return FunctionType::get(substParams, resultTy,
4906-
function->getExtInfo());
4918+
function->getExtInfo()
4919+
.withGlobalActor(globalActorType));
49074920
}
49084921

49094922
case TypeKind::ArraySlice: {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) {
587587

588588
/// Determine the isolation rules for a given declaration.
589589
ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
590-
ConcreteDeclRef declRef) {
590+
ConcreteDeclRef declRef, bool fromExpression) {
591591
auto decl = declRef.getDecl();
592592

593593
switch (decl->getKind()) {
@@ -673,6 +673,13 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
673673

674674
case ActorIsolation::GlobalActorUnsafe:
675675
case ActorIsolation::GlobalActor: {
676+
// A global-actor-isolated function referenced within an expression
677+
// carries the global actor into its function type. The actual
678+
// reference to the function is therefore not restricted, because the
679+
// call to the function is.
680+
if (fromExpression && isa<AbstractFunctionDecl>(decl))
681+
return forUnrestricted();
682+
676683
Type actorType = isolation.getGlobalActor();
677684
if (auto subs = declRef.getSubstitutions())
678685
actorType = actorType.subst(subs);
@@ -1276,6 +1283,9 @@ namespace {
12761283
if (auto apply = dyn_cast<ApplyExpr>(expr)) {
12771284
applyStack.push_back(apply); // record this encounter
12781285

1286+
// Check the call itself.
1287+
(void)checkApply(apply);
1288+
12791289
// If this is a call to a partial apply thunk, decompose it to check it
12801290
// like based on the original written syntax, e.g., "self.method".
12811291
if (auto partialApply = decomposePartialApplyThunk(
@@ -1411,6 +1421,33 @@ namespace {
14111421
return nullptr;
14121422
}
14131423

1424+
/// Note when the enclosing context could be put on a global actor.
1425+
void noteGlobalActorOnContext(DeclContext *dc, Type globalActor) {
1426+
// If we are in a synchronous function on the global actor,
1427+
// suggest annotating with the global actor itself.
1428+
if (auto fn = dyn_cast<FuncDecl>(dc)) {
1429+
if (!isa<AccessorDecl>(fn) && !fn->isAsyncContext()) {
1430+
switch (getActorIsolation(fn)) {
1431+
case ActorIsolation::ActorInstance:
1432+
case ActorIsolation::GlobalActor:
1433+
case ActorIsolation::GlobalActorUnsafe:
1434+
case ActorIsolation::Independent:
1435+
return;
1436+
1437+
case ActorIsolation::Unspecified:
1438+
fn->diagnose(diag::note_add_globalactor_to_function,
1439+
globalActor->getWithoutParens().getString(),
1440+
fn->getDescriptiveKind(),
1441+
fn->getName(),
1442+
globalActor)
1443+
.fixItInsert(fn->getAttributeInsertionLoc(false),
1444+
diag::insert_globalactor_attr, globalActor);
1445+
return;
1446+
}
1447+
}
1448+
}
1449+
}
1450+
14141451
/// Note that the given actor member is isolated.
14151452
/// @param context is allowed to be null if no context is appropriate.
14161453
void noteIsolatedActorMember(ValueDecl *decl, Expr *context) {
@@ -1672,6 +1709,86 @@ namespace {
16721709
return result;
16731710
}
16741711

1712+
/// Check actor isolation for a particular application.
1713+
bool checkApply(ApplyExpr *apply) {
1714+
auto fnExprType = apply->getFn()->getType();
1715+
if (!fnExprType)
1716+
return false;
1717+
1718+
auto fnType = fnExprType->getAs<FunctionType>();
1719+
if (!fnType)
1720+
return false;
1721+
1722+
// Handle calls to global-actor-qualified functions.
1723+
Type globalActor = fnType->getGlobalActor();
1724+
if (!globalActor)
1725+
return false;
1726+
1727+
auto declContext = const_cast<DeclContext *>(getDeclContext());
1728+
1729+
// Check whether we are within the same isolation context, in which
1730+
// case there is nothing further to check,
1731+
auto contextIsolation = getInnermostIsolatedContext(declContext);
1732+
if (contextIsolation.isGlobalActor() &&
1733+
contextIsolation.getGlobalActor()->isEqual(globalActor)) {
1734+
return false;
1735+
}
1736+
1737+
// From this point on, the only possibility is that we have an implicitly
1738+
// aynchronous call.
1739+
1740+
// If we are not in an asynchronous context, complain.
1741+
if (!isInAsynchronousContext()) {
1742+
auto isolation = ActorIsolation::forGlobalActor(
1743+
globalActor, /*unsafe=*/false);
1744+
if (auto calleeDecl = apply->getCalledValue()) {
1745+
ctx.Diags.diagnose(
1746+
apply->getLoc(), diag::actor_isolated_call_decl, isolation,
1747+
calleeDecl->getDescriptiveKind(), calleeDecl->getName(),
1748+
contextIsolation);
1749+
calleeDecl->diagnose(
1750+
diag::actor_isolated_sync_func, calleeDecl->getDescriptiveKind(),
1751+
calleeDecl->getName());
1752+
} else {
1753+
ctx.Diags.diagnose(
1754+
apply->getLoc(), diag::actor_isolated_call, isolation,
1755+
contextIsolation);
1756+
}
1757+
noteGlobalActorOnContext(
1758+
const_cast<DeclContext *>(getDeclContext()), globalActor);
1759+
1760+
return true;
1761+
}
1762+
1763+
// Mark as implicitly async.
1764+
apply->setImplicitlyAsync(true);
1765+
1766+
// If we don't need to check for sendability, we're done.
1767+
if (!shouldDiagnoseNonSendableViolations(ctx.LangOpts))
1768+
return false;
1769+
1770+
// Check for sendability of the parameter types.
1771+
for (const auto &param : fnType->getParams()) {
1772+
// FIXME: Dig out the locations of the corresponding arguments.
1773+
if (!isSendableType(getDeclContext(), param.getParameterType())) {
1774+
ctx.Diags.diagnose(
1775+
apply->getLoc(), diag::non_concurrent_param_type,
1776+
param.getParameterType());
1777+
return true;
1778+
}
1779+
}
1780+
1781+
// Check for sendability of the result type.
1782+
if (!isSendableType(getDeclContext(), fnType->getResult())) {
1783+
ctx.Diags.diagnose(
1784+
apply->getLoc(), diag::non_concurrent_result_type,
1785+
fnType->getResult());
1786+
return true;
1787+
}
1788+
1789+
return false;
1790+
}
1791+
16751792
/// Check a reference to an entity within a global actor.
16761793
bool checkGlobalActorReference(
16771794
ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
@@ -1763,21 +1880,7 @@ namespace {
17631880
value->getDescriptiveKind(), value->getName(), globalActor,
17641881
/*actorIndependent=*/false, useKind,
17651882
result == AsyncMarkingResult::SyncContext);
1766-
1767-
// If we are in a synchronous function on the global actor,
1768-
// suggest annotating with the global actor itself.
1769-
if (auto fn = dyn_cast<FuncDecl>(declContext)) {
1770-
if (!isa<AccessorDecl>(fn) && !fn->isAsyncContext()) {
1771-
fn->diagnose(diag::note_add_globalactor_to_function,
1772-
globalActor->getWithoutParens().getString(),
1773-
fn->getDescriptiveKind(),
1774-
fn->getName(),
1775-
globalActor)
1776-
.fixItInsert(fn->getAttributeInsertionLoc(false),
1777-
diag::insert_globalactor_attr, globalActor);
1778-
}
1779-
}
1780-
1883+
noteGlobalActorOnContext(declContext, globalActor);
17811884
noteIsolatedActorMember(value, context);
17821885

17831886
return true;
@@ -2733,10 +2836,10 @@ ActorIsolation ActorIsolationRequest::evaluate(
27332836
if (!isAsyncHandler(value)) {
27342837
auto isolation = getActorIsolation(overriddenValue);
27352838
SubstitutionMap subs;
2736-
if (auto env = value->getInnermostDeclContext()
2737-
->getGenericEnvironmentOfContext()) {
2738-
subs = SubstitutionMap::getOverrideSubstitutions(
2739-
overriddenValue, value, subs);
2839+
2840+
if (Type selfType = value->getDeclContext()->getSelfInterfaceType()) {
2841+
subs = selfType->getMemberSubstitutionMap(
2842+
value->getModuleContext(), overriddenValue);
27402843
}
27412844

27422845
return inferredIsolation(isolation.subst(subs));
@@ -2881,11 +2984,12 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
28812984

28822985
if (overriddenIsolation.requiresSubstitution()) {
28832986
SubstitutionMap subs;
2884-
if (auto env = value->getInnermostDeclContext()
2885-
->getGenericEnvironmentOfContext()) {
2886-
subs = SubstitutionMap::getOverrideSubstitutions(overridden, value, subs);
2887-
overriddenIsolation = overriddenIsolation.subst(subs);
2987+
if (Type selfType = value->getDeclContext()->getSelfInterfaceType()) {
2988+
subs = selfType->getMemberSubstitutionMap(
2989+
value->getModuleContext(), overridden);
28882990
}
2991+
2992+
overriddenIsolation = overriddenIsolation.subst(subs);
28892993
}
28902994

28912995
// If the isolation matches, we're done.

lib/Sema/TypeCheckConcurrency.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,11 @@ class ActorIsolationRestriction {
169169
}
170170

171171
/// Determine the isolation rules for a given declaration.
172-
static ActorIsolationRestriction forDeclaration(ConcreteDeclRef declRef);
172+
///
173+
/// \param fromExpression Indicates that the reference is coming from an
174+
/// expression.
175+
static ActorIsolationRestriction forDeclaration(
176+
ConcreteDeclRef declRef, bool fromExpression = true);
173177

174178
operator Kind() const { return kind; };
175179
};

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
403403
auto behavior = behaviorLimitForObjCReason(Reason, VD->getASTContext());
404404

405405
switch (auto restriction = ActorIsolationRestriction::forDeclaration(
406-
const_cast<ValueDecl *>(VD))) {
406+
const_cast<ValueDecl *>(VD), /*fromExpression=*/false)) {
407407
case ActorIsolationRestriction::CrossActorSelf:
408408
// FIXME: Substitution map?
409409
diagnoseNonConcurrentTypesInReference(

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2751,7 +2751,8 @@ bool ConformanceChecker::checkActorIsolation(
27512751
bool witnessIsUnsafe = false;
27522752
Type witnessGlobalActor;
27532753
switch (auto witnessRestriction =
2754-
ActorIsolationRestriction::forDeclaration(witness)) {
2754+
ActorIsolationRestriction::forDeclaration(
2755+
witness, /*fromExpression=*/false)) {
27552756
case ActorIsolationRestriction::ActorSelf: {
27562757
// An actor-isolated witness can only conform to an actor-isolated
27572758
// requirement.

0 commit comments

Comments
 (0)