Skip to content

Commit 3439e0c

Browse files
committed
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.
1 parent 2eee30d commit 3439e0c

File tree

10 files changed

+217
-114
lines changed

10 files changed

+217
-114
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ class ActorIsolation {
154154
explicit ActorIsolation(Kind kind = Unspecified, bool isSILParsed = false)
155155
: pointer(nullptr), kind(kind), isolatedByPreconcurrency(false),
156156
silParsed(isSILParsed), encodedParameterIndex(0) {
157-
assert(kind != ActorInstance);
157+
// SIL's use of this has weaker invariants for now.
158+
assert(kind != ActorInstance || isSILParsed);
158159
}
159160

160161
static ActorIsolation forUnspecified() {
@@ -209,21 +210,14 @@ class ActorIsolation {
209210
static std::optional<ActorIsolation> forSILString(StringRef string) {
210211
auto kind =
211212
llvm::StringSwitch<std::optional<ActorIsolation::Kind>>(string)
212-
.Case("unspecified",
213-
std::optional<ActorIsolation>(ActorIsolation::Unspecified))
214-
.Case("actor_instance",
215-
std::optional<ActorIsolation>(ActorIsolation::ActorInstance))
216-
.Case("nonisolated",
217-
std::optional<ActorIsolation>(ActorIsolation::Nonisolated))
218-
.Case("nonisolated_unsafe", std::optional<ActorIsolation>(
219-
ActorIsolation::NonisolatedUnsafe))
220-
.Case("global_actor",
221-
std::optional<ActorIsolation>(ActorIsolation::GlobalActor))
222-
.Case("global_actor_unsafe",
223-
std::optional<ActorIsolation>(ActorIsolation::GlobalActor))
213+
.Case("unspecified", ActorIsolation::Unspecified)
214+
.Case("actor_instance", ActorIsolation::ActorInstance)
215+
.Case("nonisolated", ActorIsolation::Nonisolated)
216+
.Case("nonisolated_unsafe", ActorIsolation::NonisolatedUnsafe)
217+
.Case("global_actor", ActorIsolation::GlobalActor)
218+
.Case("global_actor_unsafe", ActorIsolation::GlobalActor)
224219
.Case("caller_isolation_inheriting",
225-
std::optional<ActorIsolation>(
226-
ActorIsolation::CallerIsolationInheriting))
220+
ActorIsolation::CallerIsolationInheriting)
227221
.Default(std::nullopt);
228222
if (kind == std::nullopt)
229223
return std::nullopt;

lib/AST/ActorIsolation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ bool ActorIsolation::isEqual(const ActorIsolation &lhs,
191191
auto *lhsActor = lhs.getActorInstance();
192192
auto *rhsActor = rhs.getActorInstance();
193193
if (lhsActor && rhsActor) {
194+
if (lhsActor == rhsActor)
195+
return true;
196+
194197
// FIXME: This won't work for arbitrary isolated parameter captures.
195198
if ((lhsActor->isSelfParameter() && rhsActor->isSelfParamCapture()) ||
196199
(lhsActor->isSelfParamCapture() && rhsActor->isSelfParameter())) {

lib/AST/ConformanceLookup.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,13 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance(
269269
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
270270
}
271271

272+
// We can end up checking builtin conformances for generic function types
273+
// when e.g. we're checking whether a captured local func declaration is
274+
// sendable. That's fine, we can answer that question in the abstract
275+
// without needing to generally support conformances on generic function
276+
// types.
272277
using EitherFunctionType =
273-
llvm::PointerUnion<const SILFunctionType *, const FunctionType *>;
278+
llvm::PointerUnion<const SILFunctionType *, const AnyFunctionType *>;
274279

275280
/// Whether the given function type conforms to Sendable.
276281
static bool isSendableFunctionType(EitherFunctionType eitherFnTy) {
@@ -288,7 +293,7 @@ static bool isSendableFunctionType(EitherFunctionType eitherFnTy) {
288293
representation = *converted;
289294

290295
} else {
291-
auto functionType = eitherFnTy.get<const FunctionType *>();
296+
auto functionType = eitherFnTy.get<const AnyFunctionType *>();
292297

293298
if (functionType->isSendable())
294299
return true;
@@ -332,7 +337,7 @@ static bool isBitwiseCopyableFunctionType(EitherFunctionType eitherFnTy) {
332337
if (auto silFnTy = eitherFnTy.dyn_cast<const SILFunctionType *>()) {
333338
representation = silFnTy->getRepresentation();
334339
} else {
335-
auto fnTy = eitherFnTy.get<const FunctionType *>();
340+
auto fnTy = eitherFnTy.get<const AnyFunctionType *>();
336341
representation = convertRepresentation(fnTy->getRepresentation());
337342
}
338343

@@ -621,7 +626,7 @@ LookupConformanceInModuleRequest::evaluate(
621626
}
622627

623628
// Function types can conform to protocols.
624-
if (auto functionType = type->getAs<FunctionType>()) {
629+
if (auto functionType = type->getAs<AnyFunctionType>()) {
625630
return getBuiltinFunctionTypeConformance(type, functionType, protocol);
626631
}
627632

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ void SILGenFunction::emitExpectedExecutorProlog() {
195195

196196
case ActorIsolation::GlobalActor:
197197
if (F.isAsync() || wantDataRaceChecks) {
198-
setExpectedExecutorForGlobalActor(*this, actorIsolation.getGlobalActor());
198+
auto globalActorType = F.mapTypeIntoContext(actorIsolation.getGlobalActor());
199+
setExpectedExecutorForGlobalActor(*this, globalActorType);
199200
}
200201
break;
201202
}
@@ -226,7 +227,8 @@ void SILGenFunction::emitExpectedExecutorProlog() {
226227

227228
case ActorIsolation::GlobalActor:
228229
if (wantExecutor) {
229-
setExpectedExecutorForGlobalActor(*this, actorIsolation.getGlobalActor());
230+
auto globalActorType = F.mapTypeIntoContext(actorIsolation.getGlobalActor());
231+
setExpectedExecutorForGlobalActor(*this, globalActorType);
230232
break;
231233
}
232234
}
@@ -573,9 +575,10 @@ SILGenFunction::emitFunctionTypeIsolation(SILLocation loc,
573575

574576
// Emit global actor isolation by loading .shared from the global actor,
575577
// erasing it into `any Actor`, and injecting that into Optional.
576-
case FunctionTypeIsolation::Kind::GlobalActor:
578+
case FunctionTypeIsolation::Kind::GlobalActor: {
577579
return emitGlobalActorIsolation(loc,
578580
isolation.getGlobalActorType()->getCanonicalType());
581+
}
579582

580583
// Emit @isolated(any) isolation by loading the actor reference from the
581584
// function.
@@ -646,9 +649,11 @@ SILGenFunction::emitClosureIsolation(SILLocation loc, SILDeclRef constant,
646649
case ActorIsolation::Erased:
647650
llvm_unreachable("closures cannot directly have erased isolation");
648651

649-
case ActorIsolation::GlobalActor:
650-
return emitGlobalActorIsolation(loc,
651-
isolation.getGlobalActor()->getCanonicalType());
652+
case ActorIsolation::GlobalActor: {
653+
auto globalActorType = F.mapTypeIntoContext(isolation.getGlobalActor())
654+
->getCanonicalType();
655+
return emitGlobalActorIsolation(loc, globalActorType);
656+
}
652657

653658
case ActorIsolation::ActorInstance: {
654659
assert(isolation.isActorInstanceForCapture());
@@ -703,8 +708,10 @@ SILGenFunction::emitExecutor(SILLocation loc, ActorIsolation isolation,
703708
return emitLoadActorExecutor(loc, self);
704709
}
705710

706-
case ActorIsolation::GlobalActor:
707-
return emitLoadGlobalActorExecutor(isolation.getGlobalActor());
711+
case ActorIsolation::GlobalActor: {
712+
auto globalActorType = F.mapTypeIntoContext(isolation.getGlobalActor());
713+
return emitLoadGlobalActorExecutor(globalActorType);
714+
}
708715
}
709716
llvm_unreachable("covered switch");
710717
}

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator,
761761
finder.checkType(type, AFD->getLoc());
762762
}
763763

764-
if (AFD->isLocalCapture() && AFD->hasAsync()) {
764+
if (AFD->isLocalCapture()) {
765765
// If a local function inherits isolation from the enclosing context,
766766
// make sure we capture the isolated parameter, if we haven't already.
767767
auto actorIsolation = getActorIsolation(AFD);

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 91 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,13 +3029,13 @@ namespace {
30293029
// If the closure won't execute concurrently with the context in
30303030
// which the declaration occurred, it's okay.
30313031
auto decl = capture.getDecl();
3032-
auto isolation = getActorIsolation(decl);
30333032

30343033
// 'nonisolated' local variables are always okay to capture in
30353034
// 'Sendable' closures because they can be accessed from anywhere.
30363035
// Note that only 'nonisolated(unsafe)' can be applied to local
30373036
// variables.
3038-
if (isolation.isNonisolated())
3037+
if (isa<VarDecl>(decl) &&
3038+
getActorIsolation(decl).isNonisolated())
30393039
continue;
30403040

30413041
auto *context = localFunc.getAsDeclContext();
@@ -4815,6 +4815,70 @@ namespace {
48154815
};
48164816
} // end anonymous namespace
48174817

4818+
/// Compute the isolation of a closure or local function from its parent
4819+
/// isolation.
4820+
///
4821+
/// Doesn't apply preconcurrency because it's generally easier for the
4822+
/// caller to do so.
4823+
static ActorIsolation
4824+
computeClosureIsolationFromParent(DeclContext *closure,
4825+
ActorIsolation parentIsolation,
4826+
bool checkIsolatedCapture) {
4827+
// We must have parent isolation determined to get here.
4828+
switch (parentIsolation) {
4829+
case ActorIsolation::CallerIsolationInheriting:
4830+
case ActorIsolation::Nonisolated:
4831+
case ActorIsolation::NonisolatedUnsafe:
4832+
case ActorIsolation::Unspecified:
4833+
return ActorIsolation::forNonisolated(
4834+
parentIsolation == ActorIsolation::NonisolatedUnsafe);
4835+
4836+
case ActorIsolation::Erased:
4837+
llvm_unreachable("context cannot have erased isolation");
4838+
4839+
case ActorIsolation::GlobalActor:
4840+
// This should already be an interface type, so we don't need to remap
4841+
// it between the contexts.
4842+
return ActorIsolation::forGlobalActor(parentIsolation.getGlobalActor());
4843+
4844+
case ActorIsolation::ActorInstance: {
4845+
// In non-@Sendable local functions, we always inherit the enclosing
4846+
// isolation, forcing a capture of it if necessary.
4847+
if (isa<FuncDecl>(closure)) {
4848+
// We should always have a VarDecl in this case, where we got the
4849+
// ActorIsolation from a context; the non-VarDecl cases are only used
4850+
// locally within isolation checking.
4851+
auto actor = parentIsolation.getActorInstance();
4852+
assert(actor);
4853+
return ActorIsolation::forActorInstanceCapture(actor);
4854+
}
4855+
4856+
if (checkIsolatedCapture) {
4857+
auto closureAsFn = AnyFunctionRef::fromFunctionDeclContext(closure);
4858+
if (auto param = closureAsFn.getCaptureInfo().getIsolatedParamCapture())
4859+
return ActorIsolation::forActorInstanceCapture(param);
4860+
4861+
auto *explicitClosure = dyn_cast<ClosureExpr>(closure);
4862+
// @_inheritActorContext(always) forces the isolation capture.
4863+
if (explicitClosure && explicitClosure->alwaysInheritsActorContext()) {
4864+
if (parentIsolation.isActorInstanceIsolated()) {
4865+
if (auto *param = parentIsolation.getActorInstance())
4866+
return ActorIsolation::forActorInstanceCapture(param);
4867+
}
4868+
return parentIsolation;
4869+
}
4870+
} else {
4871+
// If we don't have capture information during code completion, assume
4872+
// that the closure captures the `isolated` parameter from the parent
4873+
// context.
4874+
return parentIsolation;
4875+
}
4876+
4877+
return ActorIsolation::forNonisolated(/*unsafe=*/false);
4878+
}
4879+
}
4880+
}
4881+
48184882
ActorIsolation ActorIsolationChecker::determineClosureIsolation(
48194883
AbstractClosureExpr *closure) const {
48204884
bool preconcurrency = false;
@@ -4871,48 +4935,8 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation(
48714935
closure->getParent(), getClosureActorIsolation);
48724936
preconcurrency |= parentIsolation.preconcurrency();
48734937

4874-
// We must have parent isolation determined to get here.
4875-
switch (parentIsolation) {
4876-
case ActorIsolation::CallerIsolationInheriting:
4877-
case ActorIsolation::Nonisolated:
4878-
case ActorIsolation::NonisolatedUnsafe:
4879-
case ActorIsolation::Unspecified:
4880-
return ActorIsolation::forNonisolated(
4881-
parentIsolation == ActorIsolation::NonisolatedUnsafe);
4882-
4883-
case ActorIsolation::Erased:
4884-
llvm_unreachable("context cannot have erased isolation");
4885-
4886-
case ActorIsolation::GlobalActor: {
4887-
Type globalActor = closure->mapTypeIntoContext(
4888-
parentIsolation.getGlobalActor()->mapTypeOutOfContext());
4889-
return ActorIsolation::forGlobalActor(globalActor);
4890-
}
4891-
4892-
case ActorIsolation::ActorInstance: {
4893-
if (checkIsolatedCapture) {
4894-
if (auto param = closure->getCaptureInfo().getIsolatedParamCapture())
4895-
return ActorIsolation::forActorInstanceCapture(param);
4896-
4897-
auto *explicitClosure = dyn_cast<ClosureExpr>(closure);
4898-
// @_inheritActorContext(always) forces the isolation capture.
4899-
if (explicitClosure && explicitClosure->alwaysInheritsActorContext()) {
4900-
if (parentIsolation.isActorInstanceIsolated()) {
4901-
if (auto *param = parentIsolation.getActorInstance())
4902-
return ActorIsolation::forActorInstanceCapture(param);
4903-
}
4904-
return parentIsolation;
4905-
}
4906-
} else {
4907-
// If we don't have capture information during code completion, assume
4908-
// that the closure captures the `isolated` parameter from the parent
4909-
// context.
4910-
return parentIsolation;
4911-
}
4912-
4913-
return ActorIsolation::forNonisolated(/*unsafe=*/false);
4914-
}
4915-
}
4938+
return computeClosureIsolationFromParent(closure, parentIsolation,
4939+
checkIsolatedCapture);
49164940
}();
49174941

49184942
// Apply computed preconcurrency.
@@ -5109,7 +5133,8 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
51095133
std::optional<ActorIsolation> result;
51105134
if (selfTypeDecl) {
51115135
if (selfTypeDecl->isAnyActor()) {
5112-
result = ActorIsolation::forActorInstanceSelf(selfTypeDecl);
5136+
result = ActorIsolation::forActorInstanceSelf(
5137+
const_cast<AbstractFunctionDecl*>(cast<AbstractFunctionDecl>(decl)));
51135138
} else {
51145139
// If the declaration is in an extension that has one of the isolation
51155140
// attributes, use that.
@@ -6227,50 +6252,37 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
62276252
return inferred;
62286253
};
62296254

6255+
// If this is an accessor, use the actor isolation of its storage
6256+
// declaration. All of the logic for FuncDecls below only applies to
6257+
// non-accessor functions.
6258+
if (auto accessor = dyn_cast<AccessorDecl>(value)) {
6259+
return getInferredActorIsolation(accessor->getStorage());
6260+
}
6261+
62306262
// If this is a local function, inherit the actor isolation from its
62316263
// context if it global or was captured.
62326264
if (auto func = dyn_cast<FuncDecl>(value)) {
6233-
if (func->isLocalCapture() && !func->isSendable()) {
6234-
auto *dc = func->getDeclContext();
6265+
auto *dc = func->getDeclContext();
6266+
if (dc->isLocalContext() && !func->isSendable()) {
62356267
llvm::PointerUnion<Decl *, AbstractClosureExpr *> inferenceSource;
62366268
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {
62376269
inferenceSource = closure;
62386270
} else {
62396271
inferenceSource = dc->getAsDecl();
62406272
}
62416273

6242-
switch (auto enclosingIsolation = getActorIsolationOfContext(dc)) {
6243-
case ActorIsolation::Nonisolated:
6244-
case ActorIsolation::CallerIsolationInheriting:
6245-
case ActorIsolation::NonisolatedUnsafe:
6246-
case ActorIsolation::Unspecified:
6247-
// Do nothing.
6248-
break;
6249-
6250-
case ActorIsolation::Erased:
6251-
llvm_unreachable("context cannot have erased isolation");
6252-
6253-
case ActorIsolation::ActorInstance:
6254-
return {
6255-
inferredIsolation(enclosingIsolation),
6256-
IsolationSource(inferenceSource, IsolationSource::LexicalContext)
6257-
};
6258-
6259-
case ActorIsolation::GlobalActor:
6260-
return {
6261-
inferredIsolation(enclosingIsolation),
6262-
IsolationSource(inferenceSource, IsolationSource::LexicalContext)
6263-
};
6264-
}
6274+
auto enclosingIsolation = getActorIsolationOfContext(dc);
6275+
auto isolation =
6276+
computeClosureIsolationFromParent(func, enclosingIsolation,
6277+
/*checkIsolatedCapture*/true)
6278+
.withPreconcurrency(enclosingIsolation.preconcurrency());
6279+
return {
6280+
inferredIsolation(isolation),
6281+
IsolationSource(inferenceSource, IsolationSource::LexicalContext)
6282+
};
62656283
}
62666284
}
62676285

6268-
// If this is an accessor, use the actor isolation of its storage
6269-
// declaration.
6270-
if (auto accessor = dyn_cast<AccessorDecl>(value)) {
6271-
return getInferredActorIsolation(accessor->getStorage());
6272-
}
6273-
62746286
if (auto var = dyn_cast<VarDecl>(value)) {
62756287
auto &ctx = var->getASTContext();
62766288
if (!ctx.LangOpts.isConcurrencyModelTaskToThread() &&
@@ -6512,7 +6524,8 @@ bool HasIsolatedSelfRequest::evaluate(
65126524
}
65136525
}
65146526
if (attrIsolation) {
6515-
return attrIsolation == ActorIsolation::forActorInstanceSelf(selfTypeDecl);
6527+
return attrIsolation->getKind() == ActorIsolation::ActorInstance &&
6528+
attrIsolation->isActorInstanceForSelfParameter();
65166529
}
65176530

65186531
// If this is a variable, check for a property wrapper that alters its
@@ -7776,7 +7789,7 @@ ActorIsolation swift::getActorIsolationForReference(ValueDecl *decl,
77767789
// as needing to enter the actor.
77777790
if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) {
77787791
if (nominal->isAnyActor())
7779-
return ActorIsolation::forActorInstanceSelf(decl);
7792+
return ActorIsolation::forActorInstanceSelf(ctor);
77807793
}
77817794

77827795
// Fall through to treat initializers like any other declaration.

0 commit comments

Comments
 (0)