Skip to content

Commit 1186b1c

Browse files
authored
Merge pull request #40028 from kavon/flowsensitive-isolation
2 parents 8c9f0f5 + 67d7f95 commit 1186b1c

24 files changed

+2103
-1184
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ class raw_ostream;
2626
namespace swift {
2727
class DeclContext;
2828
class ModuleDecl;
29+
class VarDecl;
2930
class NominalTypeDecl;
3031
class SubstitutionMap;
32+
class AbstractFunctionDecl;
3133

3234
/// Determine whether the given types are (canonically) equal, declared here
3335
/// to avoid having to include Types.h.
@@ -36,6 +38,11 @@ bool areTypesEqual(Type type1, Type type2);
3638
/// Determine whether the given type is suitable as a concurrent value type.
3739
bool isSendableType(ModuleDecl *module, Type type);
3840

41+
/// Determines if the 'let' can be read from anywhere within the given module,
42+
/// regardless of the isolation or async-ness of the context in which
43+
/// the var is read.
44+
bool isLetAccessibleAnywhere(const ModuleDecl *fromModule, VarDecl *let);
45+
3946
/// Describes the actor isolation of a given declaration, which determines
4047
/// the actors with which it can interact.
4148
class ActorIsolation {
@@ -169,6 +176,9 @@ ActorIsolation getActorIsolation(ValueDecl *value);
169176
/// Determine how the given declaration context is isolated.
170177
ActorIsolation getActorIsolationOfContext(DeclContext *dc);
171178

179+
/// Determines whether this function's body uses flow-sensitive isolation.
180+
bool usesFlowSensitiveIsolation(AbstractFunctionDecl const *fn);
181+
172182
void simple_display(llvm::raw_ostream &out, const ActorIsolation &state);
173183

174184
} // end namespace swift

include/swift/AST/DiagnosticsSIL.def

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,6 @@ ERROR(variable_defer_use_uninit,none,
168168
ERROR(self_closure_use_uninit,none,
169169
"'self' captured by a closure before all members were initialized", ())
170170

171-
ERROR(self_disallowed_nonisolated_actor_init,none,
172-
"%select{|this use of }0actor 'self' cannot %2 %select{from|in}0 "
173-
"%select{a non-isolated, designated|a global-actor isolated}1 initializer",
174-
(bool, bool, StringRef))
175-
ERROR(self_disallowed_plain_actor_init,none,
176-
"%select{|this use of }0actor 'self' can only %1 %select{from|in}0 an async initializer",
177-
(bool, StringRef))
178-
NOTE(actor_convenience_init,none,
179-
"convenience initializers allow non-isolated use of 'self' once "
180-
"initialized",
181-
())
182-
183-
184171

185172

186173
ERROR(variable_addrtaken_before_initialized,none,
@@ -625,6 +612,16 @@ WARNING(warning_int_to_fp_inexact, none,
625612
"'%1' is not exactly representable as %0; it becomes '%2'",
626613
(Type, StringRef, StringRef))
627614

615+
// Flow-isolation diagnostics
616+
ERROR(isolated_after_nonisolated, none,
617+
"cannot access %1 %2 here in %select{non-isolated initializer|deinitializer}0",
618+
(bool, DescriptiveDeclKind, DeclName))
619+
NOTE(nonisolated_blame, none, "after %1%2 %3, "
620+
"only non-isolated properties of 'self' can be accessed from "
621+
"%select{this init|a deinit}0", (bool, StringRef, StringRef, DeclName))
622+
ERROR(non_sendable_from_deinit,none,
623+
"cannot access %1 %2 with a non-sendable type %0 from non-isolated deinit",
624+
(Type, DescriptiveDeclKind, DeclName))
628625

629626
// Yield usage errors
630627
ERROR(return_before_yield, none, "accessor must yield before returning",())

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4708,9 +4708,6 @@ ERROR(nonisolated_local_var,none,
47084708
ERROR(nonisolated_actor_sync_init,none,
47094709
"'nonisolated' on an actor's synchronous initializer is invalid",
47104710
())
4711-
ERROR(nonisolated_actor_convenience_init,none,
4712-
"'nonisolated' on an actor's convenience initializer is redundant",
4713-
())
47144711

47154712
ERROR(actor_instance_property_wrapper,none,
47164713
"%0 property in property wrapper type %1 cannot be isolated to "

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ PASS(EmitDFDiagnostics, "dataflow-diagnostics",
224224
"Emit SIL Diagnostics")
225225
PASS(EscapeAnalysisDumper, "escapes-dump",
226226
"Dump Escape Analysis Results")
227+
PASS(FlowIsolation, "flow-isolation",
228+
"Enforces flow-sensitive actor isolation rules")
227229
PASS(FunctionOrderPrinter, "function-order-printer",
228230
"Print Function Order for Testing")
229231
PASS(FunctionSignatureOpts, "function-signature-opts",

lib/AST/Decl.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,6 +1985,45 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) {
19851985
return false;
19861986
}
19871987

1988+
/// Returns true iff a defer's storage access kind should always
1989+
/// match the access kind of its immediately enclosing function.
1990+
///
1991+
/// In Swift 5 and earlier, this was not true, meaning that property observers,
1992+
/// etc, would be invoked in initializers or deinitializers if a property access
1993+
/// happens within a defer, but not when outside the defer.
1994+
static bool deferMatchesEnclosingAccess(const FuncDecl *defer) {
1995+
assert(defer->isDeferBody());
1996+
1997+
// In Swift 6+, then yes.
1998+
if (defer->getASTContext().isSwiftVersionAtLeast(6))
1999+
return true;
2000+
2001+
// If the defer is part of a function that is a member of an actor or
2002+
// concurrency-aware type, then yes.
2003+
if (auto *deferContext = defer->getParent()) {
2004+
if (auto *funcContext = deferContext->getParent()) {
2005+
if (auto *type = funcContext->getSelfNominalTypeDecl()) {
2006+
if (type->isAnyActor())
2007+
return true;
2008+
2009+
switch (getActorIsolation(type)) {
2010+
case swift::ActorIsolation::Unspecified:
2011+
case swift::ActorIsolation::GlobalActorUnsafe:
2012+
break;
2013+
2014+
case swift::ActorIsolation::ActorInstance:
2015+
case swift::ActorIsolation::DistributedActorInstance:
2016+
case swift::ActorIsolation::Independent:
2017+
case swift::ActorIsolation::GlobalActor:
2018+
return true;
2019+
}
2020+
}
2021+
}
2022+
}
2023+
2024+
return false;
2025+
}
2026+
19882027
static bool isDirectToStorageAccess(const DeclContext *UseDC,
19892028
const VarDecl *var, bool isAccessOnSelf) {
19902029
if (!var->hasStorage())
@@ -1994,6 +2033,11 @@ static bool isDirectToStorageAccess(const DeclContext *UseDC,
19942033
if (AFD == nullptr)
19952034
return false;
19962035

2036+
// Check if this is a function representing a defer.
2037+
if (auto *func = dyn_cast<FuncDecl>(AFD))
2038+
if (func->isDeferBody() && deferMatchesEnclosingAccess(func))
2039+
return isDirectToStorageAccess(func->getParent(), var, isAccessOnSelf);
2040+
19972041
// The property reference is for immediate class, not a derived class.
19982042
if (AFD->getParent()->getSelfNominalTypeDecl() !=
19992043
var->getDeclContext()->getSelfNominalTypeDecl())

lib/SILGen/SILGenConstructor.cpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,45 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
315315
return;
316316
}
317317

318+
// FIXME: the callers of ctorHopsInjectedByDefiniteInit is not correct (rdar://87485045)
319+
// we must still set the SGF.ExpectedExecutor field to say that we must
320+
// hop to the executor after every apply in the constructor. This seems to
321+
// happen for the main actor isolated async inits, but not for the plain ones,
322+
// where 'self' is not going to directly be the instance. We have to extend the
323+
// ExecutorBreadcrumb class to detect whether it needs to do a load or not
324+
// in it's emit method.
325+
//
326+
// So, the big problem right now is that for a delegating async actor init,
327+
// after calling an async function, no hop-back is being emitted.
328+
329+
/// Returns true if the given async constructor will have its
330+
/// required actor hops injected later by definite initialization.
331+
static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor,
332+
ActorIsolation const& isolation) {
333+
// must be async, but we can assume that.
334+
assert(ctor->hasAsync());
335+
336+
auto *dc = ctor->getDeclContext();
337+
auto selfClassDecl = dc->getSelfClassDecl();
338+
339+
// must be an actor
340+
if (!selfClassDecl || !selfClassDecl->isAnyActor())
341+
return false;
342+
343+
// must be instance isolated
344+
switch (isolation) {
345+
case ActorIsolation::ActorInstance:
346+
case ActorIsolation::DistributedActorInstance:
347+
return true;
348+
349+
case ActorIsolation::Unspecified:
350+
case ActorIsolation::Independent:
351+
case ActorIsolation::GlobalActor:
352+
case ActorIsolation::GlobalActorUnsafe:
353+
return false;
354+
}
355+
}
356+
318357
void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
319358
MagicFunctionName = SILGenModule::getMagicFunctionName(ctor);
320359

@@ -360,9 +399,13 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
360399

361400
// Make sure we've hopped to the right global actor, if any.
362401
if (ctor->hasAsync()) {
363-
SILLocation prologueLoc(selfDecl);
364-
prologueLoc.markAsPrologue();
365-
emitConstructorPrologActorHop(prologueLoc, getActorIsolation(ctor));
402+
auto isolation = getActorIsolation(ctor);
403+
// if it's not injected by definite init, we do it in the prologue now.
404+
if (!ctorHopsInjectedByDefiniteInit(ctor, isolation)) {
405+
SILLocation prologueLoc(selfDecl);
406+
prologueLoc.markAsPrologue();
407+
emitConstructorPrologActorHop(prologueLoc, isolation);
408+
}
366409
}
367410

368411
// Create a basic block to jump to for the implicit 'self' return.
@@ -754,10 +797,14 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
754797
selfClassDecl->isDistributedActor() && !isDelegating;
755798

756799
// Make sure we've hopped to the right global actor, if any.
757-
if (ctor->hasAsync() && !selfClassDecl->isActor()) {
758-
SILLocation prologueLoc(selfDecl);
759-
prologueLoc.markAsPrologue();
760-
emitConstructorPrologActorHop(prologueLoc, getActorIsolation(ctor));
800+
if (ctor->hasAsync()) {
801+
auto isolation = getActorIsolation(ctor);
802+
// if it's not injected by definite init, we do it in the prologue now.
803+
if (!ctorHopsInjectedByDefiniteInit(ctor, isolation)) {
804+
SILLocation prologueLoc(selfDecl);
805+
prologueLoc.markAsPrologue();
806+
emitConstructorPrologActorHop(prologueLoc, isolation);
807+
}
761808
}
762809

763810
if (!NeedsBoxForSelf) {

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ target_sources(swiftSILOptimizer PRIVATE
1414
DiagnoseStaticExclusivity.cpp
1515
DiagnoseUnreachable.cpp
1616
Differentiation.cpp
17+
FlowIsolation.cpp
1718
IRGenPrepare.cpp
1819
LexicalLifetimeEliminator.cpp
1920
LowerHopToActor.cpp

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,10 @@ ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const {
450450
// is it for an actor?
451451
if (decl->isAnyActor())
452452
if (auto *silFn = MemoryInst->getFunction())
453-
// is it a designated initializer?
453+
// are we in a constructor?
454454
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(
455455
silFn->getDeclContext()->getAsDecl()))
456-
if (ctor->isDesignatedInit())
457-
return ctor;
456+
return ctor;
458457

459458
return nullptr;
460459
}

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class DIMemoryObjectInfo {
169169
}
170170

171171
/// Returns the initializer if the memory use is 'self' and appears in an
172-
/// actor's designated initializer. Otherwise, returns nullptr.
172+
/// actor's initializer. Otherwise, returns nullptr.
173173
ConstructorDecl *getActorInitSelf() const;
174174

175175
/// True if this memory object is the 'self' of a derived class initializer.

0 commit comments

Comments
 (0)