Skip to content

Commit e5b6a88

Browse files
authored
Merge pull request #84181 from rjmccall/isolation-fixes
Isolation fixes for closures and defer bodies
2 parents 7668666 + 8301d53 commit e5b6a88

File tree

15 files changed

+301
-120
lines changed

15 files changed

+301
-120
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class raw_ostream;
2828

2929
namespace swift {
3030
class DeclContext;
31+
class Initializer;
3132
class ModuleDecl;
3233
class VarDecl;
3334
class NominalTypeDecl;
@@ -434,6 +435,10 @@ InferredActorIsolation getInferredActorIsolation(ValueDecl *value);
434435
ActorIsolation
435436
__AbstractClosureExpr_getActorIsolation(AbstractClosureExpr *CE);
436437

438+
/// Determine how the given initialization context is isolated.
439+
ActorIsolation getActorIsolation(Initializer *init,
440+
bool ignoreDefaultArguments = false);
441+
437442
/// Determine how the given declaration context is isolated.
438443
/// \p getClosureActorIsolation allows the specification of actor isolation for
439444
/// closures that haven't been saved been saved to the AST yet. This is useful

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3287,6 +3287,9 @@ class ValueDecl : public Decl {
32873287
/// `AbstractStorageDecl`, returns `false`.
32883288
bool isAsync() const;
32893289

3290+
/// Returns whether this function represents a defer body.
3291+
bool isDeferBody() const;
3292+
32903293
private:
32913294
bool isObjCDynamic() const {
32923295
return isObjC() && isDynamic();

include/swift/SIL/SILDeclRef.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace swift {
3838
enum class EffectsKind : uint8_t;
3939
class AbstractFunctionDecl;
4040
class AbstractClosureExpr;
41+
class ActorIsolation;
4142
class ValueDecl;
4243
class FuncDecl;
4344
class ClosureExpr;
@@ -508,6 +509,8 @@ struct SILDeclRef {
508509
return result;
509510
}
510511

512+
ActorIsolation getActorIsolation() const;
513+
511514
/// True if the decl ref references a thunk from a natively foreign
512515
/// declaration to Swift calling convention.
513516
bool isForeignToNativeThunk() const;

lib/AST/Decl.cpp

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9383,7 +9383,18 @@ void ParamDecl::setDefaultExpr(Expr *E) {
93839383
}
93849384

93859385
void ParamDecl::setTypeCheckedDefaultExpr(Expr *E) {
9386-
assert(E || getDefaultArgumentKind() == DefaultArgumentKind::Inherited);
9386+
// The type-checker will only produce a null expression here if the
9387+
// default argument is inherited, so if we're called with a null pointer
9388+
// in any other case, it must be from a request cycle. Don't crash;
9389+
// just wrap the original expression with an ErrorExpr and proceed.
9390+
if (!E && getDefaultArgumentKind() != DefaultArgumentKind::Inherited) {
9391+
auto *initExpr = getStructuralDefaultExpr();
9392+
assert(initExpr);
9393+
auto &ctx = getASTContext();
9394+
E = new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx),
9395+
initExpr);
9396+
}
9397+
93879398
setDefaultExpr(E);
93889399

93899400
auto *defaultInfo = DefaultValueAndFlags.getPointer();
@@ -11808,6 +11819,12 @@ PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const {
1180811819
nullptr);
1180911820
}
1181011821

11822+
bool ValueDecl::isDeferBody() const {
11823+
if (auto fn = dyn_cast<FuncDecl>(this))
11824+
return fn->isDeferBody();
11825+
return false;
11826+
}
11827+
1181111828
bool FuncDecl::isDeferBody() const {
1181211829
return getBaseIdentifier() == getASTContext().getIdentifier("$defer");
1181311830
}
@@ -11999,55 +12016,93 @@ ActorIsolation swift::getActorIsolationOfContext(
1199912016
DeclContext *dc,
1200012017
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1200112018
getClosureActorIsolation) {
12002-
auto &ctx = dc->getASTContext();
1200312019
auto dcToUse = dc;
12004-
// Defer bodies share actor isolation of their enclosing context.
12005-
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
12006-
if (FD->isDeferBody()) {
12007-
dcToUse = FD->getDeclContext();
12008-
}
12020+
12021+
// Defer bodies share the actor isolation of their enclosing context.
12022+
// We don't actually have to do this check here because
12023+
// getActorIsolation does consider it already, but it's nice to
12024+
// avoid some extra request evaluation in a trivial case.
12025+
while (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
12026+
if (!FD->isDeferBody()) break;
12027+
dcToUse = FD->getDeclContext();
1200912028
}
12029+
1201012030
if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
1201112031
return getActorIsolation(vd);
1201212032

12013-
// In the context of the initializing or default-value expression of a
12014-
// stored property:
12015-
// - For a static stored property, the isolation matches the VarDecl.
12016-
// Static properties are initialized upon first use, so the isolation
12017-
// of the initializer must match the isolation required to access the
12018-
// property.
12019-
// - For a field of a nominal type, the expression can require the same
12020-
// actor isolation as the field itself. That default expression may only
12021-
// be used from inits that meet the required isolation.
12022-
if (auto *var = dcToUse->getNonLocalVarDecl()) {
12023-
// If IsolatedDefaultValues are enabled, treat this context as having
12024-
// unspecified isolation. We'll compute the required isolation for
12025-
// the initializer and validate that it matches the isolation of the
12026-
// var itself in the DefaultInitializerIsolation request.
12027-
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
12028-
return ActorIsolation::forUnspecified();
12029-
12030-
return getActorIsolation(var);
12031-
}
12032-
1203312033
if (auto *closure = dyn_cast<AbstractClosureExpr>(dcToUse)) {
1203412034
return getClosureActorIsolation(closure);
1203512035
}
1203612036

12037+
if (auto *init = dyn_cast<Initializer>(dcToUse)) {
12038+
// FIXME: force default argument initializers to report a meaningless
12039+
// isolation in order to break a bunch of cycles with the way that
12040+
// isolation is computed for them.
12041+
return getActorIsolation(init, /*ignoreDefaultArguments*/ true);
12042+
}
12043+
1203712044
if (isa<TopLevelCodeDecl>(dcToUse)) {
12045+
auto &ctx = dc->getASTContext();
1203812046
if (dcToUse->isAsyncContext() ||
12039-
dcToUse->getASTContext().LangOpts.StrictConcurrencyLevel >=
12040-
StrictConcurrency::Complete) {
12041-
if (Type mainActor = dcToUse->getASTContext().getMainActorType())
12047+
ctx.LangOpts.StrictConcurrencyLevel >= StrictConcurrency::Complete) {
12048+
if (Type mainActor = ctx.getMainActorType())
1204212049
return ActorIsolation::forGlobalActor(mainActor)
12043-
.withPreconcurrency(
12044-
!dcToUse->getASTContext().isSwiftVersionAtLeast(6));
12050+
.withPreconcurrency(!ctx.isSwiftVersionAtLeast(6));
1204512051
}
1204612052
}
1204712053

1204812054
return ActorIsolation::forUnspecified();
1204912055
}
1205012056

12057+
ActorIsolation swift::getActorIsolation(Initializer *init,
12058+
bool ignoreDefaultArguments) {
12059+
switch (init->getInitializerKind()) {
12060+
case InitializerKind::PatternBinding:
12061+
// In the context of the initializing or default-value expression of a
12062+
// stored property:
12063+
// - For a static stored property, the isolation matches the VarDecl.
12064+
// Static properties are initialized upon first use, so the isolation
12065+
// of the initializer must match the isolation required to access the
12066+
// property.
12067+
// - For a field of a nominal type, the expression can require the same
12068+
// actor isolation as the field itself. That default expression may only
12069+
// be used from inits that meet the required isolation.
12070+
if (auto *var = init->getNonLocalVarDecl()) {
12071+
auto &ctx = var->getASTContext();
12072+
12073+
// If IsolatedDefaultValues are enabled, treat this context as having
12074+
// unspecified isolation. We'll compute the required isolation for
12075+
// the initializer and validate that it matches the isolation of the
12076+
// var itself in the DefaultInitializerIsolation request.
12077+
if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues))
12078+
return ActorIsolation::forUnspecified();
12079+
12080+
return getActorIsolation(var);
12081+
}
12082+
12083+
return ActorIsolation::forUnspecified();
12084+
12085+
case InitializerKind::DefaultArgument: {
12086+
auto defArgInit = cast<DefaultArgumentInitializer>(init);
12087+
12088+
// A hack when used from getActorIsolationOfContext to maintain the
12089+
// current behavior and avoid request cycles.
12090+
if (ignoreDefaultArguments)
12091+
return ActorIsolation::forUnspecified();
12092+
12093+
auto fn = cast<ValueDecl>(defArgInit->getParent()->getAsDecl());
12094+
auto param = getParameterAt(fn, defArgInit->getIndex());
12095+
assert(param);
12096+
return param->getInitializerIsolation();
12097+
}
12098+
12099+
case InitializerKind::PropertyWrapper:
12100+
case InitializerKind::CustomAttribute:
12101+
return ActorIsolation::forUnspecified();
12102+
}
12103+
llvm_unreachable("bad initializer kind");
12104+
}
12105+
1205112106
bool swift::isSameActorIsolated(ValueDecl *value, DeclContext *dc) {
1205212107
auto valueIsolation = getActorIsolation(value);
1205312108
auto dcIsolation = getActorIsolationOfContext(dc);

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,3 +1921,21 @@ bool SILDeclRef::isCalleeAllocatedCoroutine() const {
19211921

19221922
return getASTContext().SILOpts.CoroutineAccessorsUseYieldOnce2;
19231923
}
1924+
1925+
ActorIsolation SILDeclRef::getActorIsolation() const {
1926+
// Deallocating destructor is always nonisolated. Isolation of the deinit
1927+
// applies only to isolated deallocator and destroyer.
1928+
if (kind == SILDeclRef::Kind::Deallocator) {
1929+
return ActorIsolation::forNonisolated(false);
1930+
}
1931+
1932+
// Default argument generators use the isolation of the initializer,
1933+
// not the general isolation of the function.
1934+
if (isDefaultArgGenerator()) {
1935+
auto param = getParameterAt(getDecl(), defaultArgIndex);
1936+
assert(param);
1937+
return param->getInitializerIsolation();
1938+
}
1939+
1940+
return getActorIsolationOfContext(getInnermostDeclContext());
1941+
}

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,11 @@ class Conventions {
12611261

12621262
ConventionsKind getKind() const { return kind; }
12631263

1264+
bool hasCallerIsolationParameter() const {
1265+
return kind == ConventionsKind::Default ||
1266+
kind == ConventionsKind::Deallocator;
1267+
}
1268+
12641269
virtual ParameterConvention
12651270
getIndirectParameter(unsigned index,
12661271
const AbstractionPattern &type,
@@ -1700,14 +1705,10 @@ class DestructureInputs {
17001705
};
17011706
}
17021707

1703-
// If we are an async function that is unspecified or nonisolated, insert an
1704-
// isolated parameter if NonisolatedNonsendingByDefault is enabled.
1705-
//
1706-
// NOTE: The parameter is not inserted for async functions imported
1707-
// from ObjC because they are handled in a special way that doesn't
1708-
// require it.
1708+
// If the function has nonisolated(nonsending) isolation, insert the
1709+
// implicit isolation parameter.
17091710
if (IsolationInfo && IsolationInfo->isCallerIsolationInheriting() &&
1710-
extInfoBuilder.isAsync() && !Foreign.async) {
1711+
Convs.hasCallerIsolationParameter()) {
17111712
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
17121713
auto actorType =
17131714
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());
@@ -2412,32 +2413,7 @@ swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
24122413
}
24132414

24142415
if (constant) {
2415-
// TODO: It should to be possible to `getActorIsolation` if
2416-
// reference is to a decl instead of trying to get isolation
2417-
// from the reference kind, the attributes, or the context.
2418-
2419-
if (constant->kind == SILDeclRef::Kind::Deallocator) {
2420-
return ActorIsolation::forNonisolated(false);
2421-
}
2422-
2423-
if (auto *decl = constant->getAbstractFunctionDecl()) {
2424-
if (auto *nonisolatedAttr =
2425-
decl->getAttrs().getAttribute<NonisolatedAttr>()) {
2426-
if (nonisolatedAttr->isNonSending())
2427-
return ActorIsolation::forCallerIsolationInheriting();
2428-
}
2429-
2430-
if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
2431-
return ActorIsolation::forNonisolated(false /*unsafe*/);
2432-
}
2433-
}
2434-
2435-
if (auto *closure = constant->getAbstractClosureExpr()) {
2436-
if (auto isolation = closure->getActorIsolation())
2437-
return isolation;
2438-
}
2439-
2440-
return getActorIsolationOfContext(constant->getInnermostDeclContext());
2416+
return constant->getActorIsolation();
24412417
}
24422418

24432419
if (substFnInterfaceType->hasExtInfo() &&

lib/SILGen/SILGen.cpp

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -801,32 +801,6 @@ static bool isEmittedOnDemand(SILModule &M, SILDeclRef constant) {
801801
return false;
802802
}
803803

804-
static ActorIsolation getActorIsolationForFunction(SILFunction &fn) {
805-
if (auto constant = fn.getDeclRef()) {
806-
if (constant.kind == SILDeclRef::Kind::Deallocator) {
807-
// Deallocating destructor is always nonisolated. Isolation of the deinit
808-
// applies only to isolated deallocator and destroyer.
809-
return ActorIsolation::forNonisolated(false);
810-
}
811-
812-
// If we have a closure expr, check if our type is
813-
// nonisolated(nonsending). In that case, we use that instead.
814-
if (auto *closureExpr = constant.getAbstractClosureExpr()) {
815-
if (auto actorIsolation = closureExpr->getActorIsolation())
816-
return actorIsolation;
817-
}
818-
819-
// If we have actor isolation for our constant, put the isolation onto the
820-
// function. If the isolation is unspecified, we do not return it.
821-
if (auto isolation =
822-
getActorIsolationOfContext(constant.getInnermostDeclContext()))
823-
return isolation;
824-
}
825-
826-
// Otherwise, return for unspecified.
827-
return ActorIsolation::forUnspecified();
828-
}
829-
830804
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
831805
ForDefinition_t forDefinition) {
832806
// If we already emitted the function, return it.
@@ -853,7 +827,7 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
853827
});
854828

855829
F->setDeclRef(constant);
856-
F->setActorIsolation(getActorIsolationForFunction(*F));
830+
F->setActorIsolation(constant.getActorIsolation());
857831

858832
assert(F && "SILFunction should have been defined");
859833

@@ -1384,7 +1358,7 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F,
13841358
F->setDeclRef(constant);
13851359

13861360
// Set our actor isolation.
1387-
F->setActorIsolation(getActorIsolationForFunction(*F));
1361+
F->setActorIsolation(constant.getActorIsolation());
13881362

13891363
LLVM_DEBUG(llvm::dbgs() << "lowering ";
13901364
F->printName(llvm::dbgs());

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ void SILGenFunction::emitExpectedExecutorProlog() {
190190
}
191191

192192
case ActorIsolation::CallerIsolationInheriting:
193-
assert(F.isAsync());
193+
assert(F.isAsync() || F.isDefer());
194194
setExpectedExecutorForParameterIsolation(*this, actorIsolation);
195195
break;
196196

@@ -255,7 +255,7 @@ void SILGenFunction::emitExpectedExecutorProlog() {
255255
RegularLocation::getDebugOnlyLocation(F.getLocation(), getModule()),
256256
executor,
257257
/*mandatory*/ false);
258-
} else {
258+
} else if (wantDataRaceChecks) {
259259
// For a synchronous function, check that we're on the same executor.
260260
// Note: if we "know" that the code is completely Sendable-safe, this
261261
// is unnecessary. The type checker will need to make this determination.

0 commit comments

Comments
 (0)