Skip to content

Commit 634c818

Browse files
authored
Merge pull request #84250 from rjmccall/isolation-fixed-6.2
[6.2] Isolation fixes for closures and defer bodies
2 parents cff1969 + 54d0305 commit 634c818

17 files changed

+409
-146
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
@@ -3241,6 +3241,9 @@ class ValueDecl : public Decl {
32413241
/// `AbstractStorageDecl`, returns `false`.
32423242
bool isAsync() const;
32433243

3244+
/// Returns whether this function represents a defer body.
3245+
bool isDeferBody() const;
3246+
32443247
private:
32453248
bool isObjCDynamic() const {
32463249
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;
@@ -498,6 +499,8 @@ struct SILDeclRef {
498499
return result;
499500
}
500501

502+
ActorIsolation getActorIsolation() const;
503+
501504
/// True if the decl ref references a thunk from a natively foreign
502505
/// declaration to Swift calling convention.
503506
bool isForeignToNativeThunk() const;

lib/AST/Decl.cpp

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

94079407
void ParamDecl::setTypeCheckedDefaultExpr(Expr *E) {
9408-
assert(E || getDefaultArgumentKind() == DefaultArgumentKind::Inherited);
9408+
// The type-checker will only produce a null expression here if the
9409+
// default argument is inherited, so if we're called with a null pointer
9410+
// in any other case, it must be from a request cycle. Don't crash;
9411+
// just wrap the original expression with an ErrorExpr and proceed.
9412+
if (!E && getDefaultArgumentKind() != DefaultArgumentKind::Inherited) {
9413+
auto *initExpr = getStructuralDefaultExpr();
9414+
assert(initExpr);
9415+
auto &ctx = getASTContext();
9416+
E = new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx),
9417+
initExpr);
9418+
}
9419+
94099420
setDefaultExpr(E);
94109421

94119422
auto *defaultInfo = DefaultValueAndFlags.getPointer();
@@ -11800,6 +11811,12 @@ PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const {
1180011811
nullptr);
1180111812
}
1180211813

11814+
bool ValueDecl::isDeferBody() const {
11815+
if (auto fn = dyn_cast<FuncDecl>(this))
11816+
return fn->isDeferBody();
11817+
return false;
11818+
}
11819+
1180311820
bool FuncDecl::isDeferBody() const {
1180411821
return getBaseIdentifier() == getASTContext().getIdentifier("$defer");
1180511822
}
@@ -11991,55 +12008,93 @@ ActorIsolation swift::getActorIsolationOfContext(
1199112008
DeclContext *dc,
1199212009
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
1199312010
getClosureActorIsolation) {
11994-
auto &ctx = dc->getASTContext();
1199512011
auto dcToUse = dc;
11996-
// Defer bodies share actor isolation of their enclosing context.
11997-
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
11998-
if (FD->isDeferBody()) {
11999-
dcToUse = FD->getDeclContext();
12000-
}
12012+
12013+
// Defer bodies share the actor isolation of their enclosing context.
12014+
// We don't actually have to do this check here because
12015+
// getActorIsolation does consider it already, but it's nice to
12016+
// avoid some extra request evaluation in a trivial case.
12017+
while (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
12018+
if (!FD->isDeferBody()) break;
12019+
dcToUse = FD->getDeclContext();
1200112020
}
12021+
1200212022
if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
1200312023
return getActorIsolation(vd);
1200412024

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

12029+
if (auto *init = dyn_cast<Initializer>(dcToUse)) {
12030+
// FIXME: force default argument initializers to report a meaningless
12031+
// isolation in order to break a bunch of cycles with the way that
12032+
// isolation is computed for them.
12033+
return getActorIsolation(init, /*ignoreDefaultArguments*/ true);
12034+
}
12035+
1202912036
if (isa<TopLevelCodeDecl>(dcToUse)) {
12037+
auto &ctx = dc->getASTContext();
1203012038
if (dcToUse->isAsyncContext() ||
12031-
dcToUse->getASTContext().LangOpts.StrictConcurrencyLevel >=
12032-
StrictConcurrency::Complete) {
12033-
if (Type mainActor = dcToUse->getASTContext().getMainActorType())
12039+
ctx.LangOpts.StrictConcurrencyLevel >= StrictConcurrency::Complete) {
12040+
if (Type mainActor = ctx.getMainActorType())
1203412041
return ActorIsolation::forGlobalActor(mainActor)
12035-
.withPreconcurrency(
12036-
!dcToUse->getASTContext().isSwiftVersionAtLeast(6));
12042+
.withPreconcurrency(!ctx.isSwiftVersionAtLeast(6));
1203712043
}
1203812044
}
1203912045

1204012046
return ActorIsolation::forUnspecified();
1204112047
}
1204212048

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

lib/SIL/IR/SILDeclRef.cpp

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

18741874
return getASTContext().SILOpts.CoroutineAccessorsUseYieldOnce2;
18751875
}
1876+
1877+
ActorIsolation SILDeclRef::getActorIsolation() const {
1878+
// Deallocating destructor is always nonisolated. Isolation of the deinit
1879+
// applies only to isolated deallocator and destroyer.
1880+
if (kind == SILDeclRef::Kind::Deallocator) {
1881+
return ActorIsolation::forNonisolated(false);
1882+
}
1883+
1884+
// Default argument generators use the isolation of the initializer,
1885+
// not the general isolation of the function.
1886+
if (isDefaultArgGenerator()) {
1887+
auto param = getParameterAt(getDecl(), defaultArgIndex);
1888+
assert(param);
1889+
return param->getInitializerIsolation();
1890+
}
1891+
1892+
return getActorIsolationOfContext(getInnermostDeclContext());
1893+
}

lib/SIL/IR/SILFunctionType.cpp

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

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

1265+
bool hasCallerIsolationParameter() const {
1266+
return kind == ConventionsKind::Default ||
1267+
kind == ConventionsKind::Deallocator;
1268+
}
1269+
12651270
virtual ParameterConvention
12661271
getIndirectParameter(unsigned index,
12671272
const AbstractionPattern &type,
@@ -1706,14 +1711,10 @@ class DestructureInputs {
17061711
};
17071712
}
17081713

1709-
// If we are an async function that is unspecified or nonisolated, insert an
1710-
// isolated parameter if NonisolatedNonsendingByDefault is enabled.
1711-
//
1712-
// NOTE: The parameter is not inserted for async functions imported
1713-
// from ObjC because they are handled in a special way that doesn't
1714-
// require it.
1714+
// If the function has nonisolated(nonsending) isolation, insert the
1715+
// implicit isolation parameter.
17151716
if (IsolationInfo && IsolationInfo->isCallerIsolationInheriting() &&
1716-
extInfoBuilder.isAsync() && !Foreign.async) {
1717+
Convs.hasCallerIsolationParameter()) {
17171718
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
17181719
auto actorType =
17191720
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());
@@ -2419,32 +2420,7 @@ swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
24192420
}
24202421

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

24502426
if (substFnInterfaceType->hasExtInfo() &&

lib/SILGen/SILGen.cpp

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

771-
static ActorIsolation getActorIsolationForFunction(SILFunction &fn) {
772-
if (auto constant = fn.getDeclRef()) {
773-
if (constant.kind == SILDeclRef::Kind::Deallocator) {
774-
// Deallocating destructor is always nonisolated. Isolation of the deinit
775-
// applies only to isolated deallocator and destroyer.
776-
return ActorIsolation::forNonisolated(false);
777-
}
778-
779-
// If we have a closure expr, check if our type is
780-
// nonisolated(nonsending). In that case, we use that instead.
781-
if (auto *closureExpr = constant.getAbstractClosureExpr()) {
782-
if (auto actorIsolation = closureExpr->getActorIsolation())
783-
return actorIsolation;
784-
}
785-
786-
// If we have actor isolation for our constant, put the isolation onto the
787-
// function. If the isolation is unspecified, we do not return it.
788-
if (auto isolation =
789-
getActorIsolationOfContext(constant.getInnermostDeclContext()))
790-
return isolation;
791-
}
792-
793-
// Otherwise, return for unspecified.
794-
return ActorIsolation::forUnspecified();
795-
}
796-
797771
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
798772
ForDefinition_t forDefinition) {
799773
// If we already emitted the function, return it.
@@ -820,7 +794,7 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
820794
});
821795

822796
F->setDeclRef(constant);
823-
F->setActorIsolation(getActorIsolationForFunction(*F));
797+
F->setActorIsolation(constant.getActorIsolation());
824798

825799
assert(F && "SILFunction should have been defined");
826800

@@ -1316,7 +1290,7 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F,
13161290
F->setDeclRef(constant);
13171291

13181292
// Set our actor isolation.
1319-
F->setActorIsolation(getActorIsolationForFunction(*F));
1293+
F->setActorIsolation(constant.getActorIsolation());
13201294

13211295
LLVM_DEBUG(llvm::dbgs() << "lowering ";
13221296
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)