Files
swift-mirror/lib/AST/ActorIsolation.cpp
John McCall 06dec05473 Fix a bunch of bugs with the isolation of local funcs. Since we
use local funcs to implement `defer`, this also fixes several
bugs with that feature, such as it breaking in nonisolated
functions when a default isolation is in effect in the source file.

Change how we compute isolation of local funcs. The rule here is
supposed to be that non-`@Sendable` local funcs are isolated the
same as their enclosing context. Unlike closure expressions, this
is unconditional: in instance-isolated functions, the isolation
does not depend on whether `self` is captured. But the computation
was wrong: it didn't translate global actor isolation between
contexts, it didn't turn parameter isolation into capture isolation,
and it fell through for several other kinds of parent isolation,
causing the compiler to try to apply default isolation instead.
I've extracted the logic from the closure expression path into a
common function and used it for both paths.

The capture computation logic was forcing a capture of the
enclosing isolation in local funcs, but only for async functions.
Presumably this was conditional because async functions need the
isolation for actor hops, but sync functions don't really need it.
However, this was causing crashes with `-enable-actor-data-race-checks`.
(I didn't investigate whether it also failed with the similar
assertion we do with preconcurrency.) For now, I've switched this
to capture the isolated instance unconditionally. If we need to
be more conservative by either only capturing when data-race checks
are enabled or disabling the checks when the isolation isn't captured,
we can look into that.

Fix a bug in capture isolation checking. We were ignoring captures
of nonisolated declarations in order to implement the rule that
permits `nonisolated(unsafe)` variables to be captured in
non-sendable closures. This check needs to only apply to variables!
The isolation of a local func has nothing to do with its sendability
as a capture.

That fix exposed a problem where we were being unnecessarily
restrictive with generic local func declarations because we didn't
consider them to have sendable type. This was true even if the
genericity was purely from being declared in a generic context,
but it doesn't matter, they ought to be sendable regardless.

Finally, fix a handful of bugs where global actor types were not
remapped properly in SILGen.
2025-06-30 01:48:48 -04:00

214 lines
6.9 KiB
C++

//===--- ActorIsolation.cpp -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ActorIsolation.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
using namespace swift;
ActorIsolation ActorIsolation::forMainActor(ASTContext &ctx) {
return ActorIsolation::forGlobalActor(
ctx.getMainActorType()->mapTypeOutOfContext());
}
// These constructors are defined out-of-line so that including ActorIsolation.h
// doesn't require a bunch of other headers to be included.
ActorIsolation::ActorIsolation(Kind kind, NominalTypeDecl *actor,
EncodedParameterIndex parameterIndex)
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {}
ActorIsolation::ActorIsolation(Kind kind, VarDecl *actor,
EncodedParameterIndex parameterIndex)
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {}
ActorIsolation::ActorIsolation(Kind kind, Expr *actor,
EncodedParameterIndex parameterIndex)
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {}
ActorIsolation::ActorIsolation(Kind kind, Type globalActor)
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false),
silParsed(false), encodedParameterIndex(0) {}
ActorIsolation
ActorIsolation::forActorInstanceParameter(Expr *actor,
unsigned parameterIndex) {
auto &ctx = actor->getType()->getASTContext();
// An isolated value of `nil` is statically nonisolated.
// FIXME: Also allow 'Optional.none'
if (isa<NilLiteralExpr>(actor))
return ActorIsolation::forNonisolated(/*unsafe*/ false);
// An isolated value of `<global actor type>.shared` is statically
// global actor isolated.
if (auto *memberRef = dyn_cast<MemberRefExpr>(actor)) {
// Check that the member declaration witnesses the `shared`
// requirement of the `GlobalActor` protocol.
auto declRef = memberRef->getDecl();
auto baseType = memberRef->getBase()->getType()->getMetatypeInstanceType();
if (auto globalActor = ctx.getProtocol(KnownProtocolKind::GlobalActor)) {
auto conformance = checkConformance(baseType, globalActor);
if (conformance &&
conformance.getWitnessByName(baseType, ctx.Id_shared) == declRef) {
return ActorIsolation::forGlobalActor(baseType);
}
}
}
return ActorIsolation(ActorInstance, actor,
EncodedParameterIndex::parameter(parameterIndex));
}
ActorIsolation ActorIsolation::forActorInstanceSelf(ValueDecl *decl) {
if (auto *fn = dyn_cast<AbstractFunctionDecl>(decl))
return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(),
EncodedParameterIndex::self());
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl)) {
if (auto *fn = storage->getAccessor(AccessorKind::Get)) {
return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(),
EncodedParameterIndex::self());
}
}
auto *dc = decl->getDeclContext();
return ActorIsolation(ActorInstance, dc->getSelfNominalTypeDecl(),
EncodedParameterIndex::self());
}
ActorIsolation ActorIsolation::forActorInstanceSelf(NominalTypeDecl *selfDecl) {
return ActorIsolation(ActorInstance, selfDecl, EncodedParameterIndex::self());
}
NominalTypeDecl *ActorIsolation::getActor() const {
assert(getKind() == ActorInstance || getKind() == GlobalActor);
if (silParsed)
return nullptr;
if (getKind() == GlobalActor) {
return getGlobalActor()->getAnyNominal();
}
Type actorType;
if (auto *instance = actorInstance.dyn_cast<VarDecl *>()) {
actorType = instance->getTypeInContext();
} else if (auto *expr = actorInstance.dyn_cast<Expr *>()) {
actorType = expr->getType()->getRValueType();
}
if (actorType) {
if (auto wrapped = actorType->getOptionalObjectType()) {
actorType = wrapped;
}
return actorType->getReferenceStorageReferent()->getAnyActor();
}
return actorInstance.get<NominalTypeDecl *>();
}
VarDecl *ActorIsolation::getActorInstance() const {
assert(getKind() == ActorInstance);
if (silParsed)
return nullptr;
return actorInstance.dyn_cast<VarDecl *>();
}
Expr *ActorIsolation::getActorInstanceExpr() const {
assert(getKind() == ActorInstance);
if (silParsed)
return nullptr;
return actorInstance.dyn_cast<Expr *>();
}
bool ActorIsolation::isMainActor() const {
if (silParsed)
return false;
if (isGlobalActor()) {
if (auto *nominal = getGlobalActor()->getAnyNominal())
return nominal->isMainActor();
}
return false;
}
bool ActorIsolation::isDistributedActor() const {
if (silParsed)
return false;
if (getKind() != ActorInstance)
return false;
return getActor()->isDistributedActor();
}
bool ActorIsolation::isEqual(const ActorIsolation &lhs,
const ActorIsolation &rhs) {
if (lhs.getKind() != rhs.getKind())
return false;
switch (lhs.getKind()) {
case Nonisolated:
case NonisolatedUnsafe:
case Unspecified:
return true;
case Erased:
// Different functions with erased isolation have the same *kind* of
// isolation, but we must generally assume that they're not isolated
// the *same way*, which is what this function is apparently supposed
// to answer.
return false;
case CallerIsolationInheriting:
// This returns false for the same reason as erased. The caller has to check
// against the actual caller isolation.
return false;
case ActorInstance: {
auto *lhsActor = lhs.getActorInstance();
auto *rhsActor = rhs.getActorInstance();
if (lhsActor && rhsActor) {
if (lhsActor == rhsActor)
return true;
// FIXME: This won't work for arbitrary isolated parameter captures.
if ((lhsActor->isSelfParameter() && rhsActor->isSelfParamCapture()) ||
(lhsActor->isSelfParamCapture() && rhsActor->isSelfParameter())) {
return true;
}
}
// The parameter index doesn't matter; only the actor instance
// values must be equal.
return (lhs.getActor() == rhs.getActor() &&
lhs.actorInstance == rhs.actorInstance);
}
case GlobalActor:
return areTypesEqual(lhs.globalActor, rhs.globalActor);
}
}