Skip to content

Commit 25830d6

Browse files
authored
Merge pull request #74225 from DougGregor/flow-sensitive-actor-init-isolation
Teach `#isolation` to respect the flow-sensitive nature of actor initializers
2 parents 382a8e7 + d1ae73f commit 25830d6

13 files changed

+291
-1
lines changed

include/swift/AST/ASTSynthesis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enum SingletonTypeSynthesizer {
5656
_serialExecutor, // the '_Concurrency.SerialExecutor' protocol
5757
_taskExecutor, // the '_Concurrency.TaskExecutor' protocol
5858
_actor, // the '_Concurrency.Actor' protocol
59+
_distributedActor, // the 'Distributed.DistributedActor' protocol
5960
};
6061
inline Type synthesizeType(SynthesisContext &SC,
6162
SingletonTypeSynthesizer kind) {
@@ -82,6 +83,9 @@ inline Type synthesizeType(SynthesisContext &SC,
8283
case _actor:
8384
return SC.Context.getProtocol(KnownProtocolKind::Actor)
8485
->getDeclaredInterfaceType();
86+
case _distributedActor:
87+
return SC.Context.getProtocol(KnownProtocolKind::DistributedActor)
88+
->getDeclaredInterfaceType();
8589
case _copyable:
8690
return SC.Context.getProtocol(KnownProtocolKind::Copyable)
8791
->getDeclaredInterfaceType();

include/swift/AST/Builtins.def

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,25 @@ BUILTIN_MISC_OPERATION(StartAsyncLetWithLocalBuffer, "startAsyncLetWithLocalBuff
899899
/// This is only supported under the task-to-thread concurrency model.
900900
BUILTIN_MISC_OPERATION(TaskRunInline, "taskRunInline", "", Special)
901901

902+
/// flowSensitiveSelfIsolation<T: Actor>(_ actor: T) -> (any Actor)?
903+
///
904+
/// Used only in actor initializers, this builtin lowers to either 'actor'
905+
/// (wrapped in an optional) or 'nil' depending on whether 'self' has been
906+
/// initialized at this point. 'actor' is always an alias for the 'self'
907+
/// being initialized.
908+
BUILTIN_MISC_OPERATION(FlowSensitiveSelfIsolation, "flowSensitiveSelfIsolation", "", Special)
909+
910+
/// flowSensitiveDistributedSelfIsolation<T: DistributedActor>(
911+
/// _ actor: T
912+
/// ) -> (any Actor)?
913+
///
914+
/// Used only in distributed actor initializers, this builtin lowers to either
915+
/// 'actor.asLocalActor' or 'nil' depending on whether 'self' has been
916+
/// initialized at this point. 'actor' is always an alias for the 'self'
917+
/// being initialized.
918+
BUILTIN_MISC_OPERATION(FlowSensitiveDistributedSelfIsolation,
919+
"flowSensitiveDistributedSelfIsolation", "", Special)
920+
902921
/// endAsyncLet(): (Builtin.RawPointer) -> Void
903922
///
904923
/// DEPRECATED. The swift_asyncLet_finish intrinsic and endAsyncLetLifetime

lib/AST/Builtins.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,20 @@ static ValueDecl *getHopToActor(ASTContext &ctx, Identifier id) {
20892089
return builder.build(id);
20902090
}
20912091

2092+
static ValueDecl *getFlowSensitiveSelfIsolation(
2093+
ASTContext &ctx, Identifier id, bool isDistributed
2094+
) {
2095+
BuiltinFunctionBuilder builder(ctx);
2096+
return getBuiltinFunction(
2097+
ctx, id, _thin,
2098+
_generics(_unrestricted,
2099+
_conformsToDefaults(0),
2100+
_conformsTo(_typeparam(0),
2101+
isDistributed ? _distributedActor : _actor)),
2102+
_parameters(_typeparam(0)),
2103+
_optional(_existential(_actor)));
2104+
}
2105+
20922106
static ValueDecl *getDistributedActorAsAnyActor(ASTContext &ctx, Identifier id) {
20932107
BuiltinFunctionBuilder builder(ctx);
20942108
auto *distributedActorProto = ctx.getProtocol(KnownProtocolKind::DistributedActor);
@@ -3191,6 +3205,12 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
31913205
case BuiltinValueKind::HopToActor:
31923206
return getHopToActor(Context, Id);
31933207

3208+
case BuiltinValueKind::FlowSensitiveSelfIsolation:
3209+
return getFlowSensitiveSelfIsolation(Context, Id, false);
3210+
3211+
case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
3212+
return getFlowSensitiveSelfIsolation(Context, Id, true);
3213+
31943214
case BuiltinValueKind::AutoDiffCreateLinearMapContextWithType:
31953215
return getAutoDiffCreateLinearMapContext(Context, Id);
31963216

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,8 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLetLifetime)
907907
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroup)
908908
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroupWithFlags)
909909
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyTaskGroup)
910+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveSelfIsolation)
911+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveDistributedSelfIsolation)
910912

911913
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, COWBufferForReading)
912914

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
630630
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
631631
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
632632
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
633+
CONSTANT_OWNERSHIP_BUILTIN(Owned, FlowSensitiveSelfIsolation)
634+
CONSTANT_OWNERSHIP_BUILTIN(Owned, FlowSensitiveDistributedSelfIsolation)
633635
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
634636
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
635637
CONSTANT_OWNERSHIP_BUILTIN(Owned, DistributedActorAsAnyActor)

lib/SILGen/SILGenExpr.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "swift/AST/CanTypeVisitor.h"
3232
#include "swift/AST/Decl.h"
3333
#include "swift/AST/DiagnosticsCommon.h"
34+
#include "swift/AST/DistributedDecl.h"
3435
#include "swift/AST/ExistentialLayout.h"
3536
#include "swift/AST/Expr.h"
3637
#include "swift/AST/ForeignErrorConvention.h"
@@ -6743,6 +6744,56 @@ RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E,
67436744

67446745
RValue RValueEmitter::visitCurrentContextIsolationExpr(
67456746
CurrentContextIsolationExpr *E, SGFContext C) {
6747+
// If we are in an actor initializer that is isolated to, the current context
6748+
// isolation flow-sensitive: before 'self' has been initialized, it will be
6749+
// nil. After 'self' has been initialized, it will be 'self'. Introduce a
6750+
// custom builtin that Definite Initialization will rewrite appropriately.
6751+
if (auto ctor = dyn_cast_or_null<ConstructorDecl>(
6752+
SGF.F.getDeclRef().getDecl())) {
6753+
auto isolation = getActorIsolation(ctor);
6754+
if (ctor->isDesignatedInit() &&
6755+
isolation == ActorIsolation::ActorInstance &&
6756+
isolation.getActorInstance() == ctor->getImplicitSelfDecl()) {
6757+
ASTContext &ctx = SGF.getASTContext();
6758+
auto builtinName = ctx.getIdentifier(
6759+
isolation.isDistributedActor()
6760+
? getBuiltinName(BuiltinValueKind::FlowSensitiveDistributedSelfIsolation)
6761+
: getBuiltinName(BuiltinValueKind::FlowSensitiveSelfIsolation));
6762+
SILType resultTy = SGF.getLoweredType(E->getType());
6763+
6764+
auto injection = cast<InjectIntoOptionalExpr>(E->getActor());
6765+
ProtocolConformanceRef conformance;
6766+
Expr *origActorExpr;
6767+
if (isolation.isDistributedActor()) {
6768+
// Create a reference to the asLocalActor getter.
6769+
auto asLocalActorDecl = getDistributedActorAsLocalActorComputedProperty(
6770+
SGF.F.getDeclContext()->getParentModule());
6771+
auto asLocalActorGetter = asLocalActorDecl->getAccessor(AccessorKind::Get);
6772+
SILDeclRef asLocalActorRef = SILDeclRef(
6773+
asLocalActorGetter, SILDeclRef::Kind::Func);
6774+
SGF.emitGlobalFunctionRef(E, asLocalActorRef);
6775+
6776+
// Extract the base ('self') and the DistributedActor conformance.
6777+
auto memberRef = cast<MemberRefExpr>(injection->getSubExpr());
6778+
conformance = memberRef->getDecl().getSubstitutions()
6779+
.getConformances()[0];
6780+
origActorExpr = memberRef->getBase();
6781+
} else {
6782+
auto erasure = cast<ErasureExpr>(injection->getSubExpr());
6783+
conformance = erasure->getConformances()[0];
6784+
origActorExpr = erasure->getSubExpr();
6785+
}
6786+
SGF.SGM.useConformance(conformance);
6787+
6788+
SubstitutionMap subs = SubstitutionMap::getProtocolSubstitutions(
6789+
conformance.getRequirement(), origActorExpr->getType(), conformance);
6790+
auto origActor = SGF.maybeEmitValueOfLocalVarDecl(
6791+
ctor->getImplicitSelfDecl(), AccessKind::Read).getValue();
6792+
auto call = SGF.B.createBuiltin(E, builtinName, resultTy, subs, origActor);
6793+
return RValue(SGF, E, ManagedValue::forForwardedRValue(SGF, call));
6794+
}
6795+
}
6796+
67466797
return visit(E->getActor(), C);
67476798
}
67486799

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,19 @@ void ElementUseCollector::collectClassSelfUses(
17091709
Kind = DIUseKind::Escape;
17101710
}
17111711

1712+
// Track flow-sensitive 'self' isolation builtins separately, because they
1713+
// aren't really uses of 'self' until after DI, once we've decided whether
1714+
// they have a fully-formed 'self' to use.
1715+
if (auto builtin = dyn_cast<BuiltinInst>(User)) {
1716+
if (auto builtinKind = builtin->getBuiltinKind()) {
1717+
if (*builtinKind == BuiltinValueKind::FlowSensitiveSelfIsolation ||
1718+
*builtinKind ==
1719+
BuiltinValueKind::FlowSensitiveDistributedSelfIsolation) {
1720+
Kind = DIUseKind::FlowSensitiveSelfIsolation;
1721+
}
1722+
}
1723+
}
1724+
17121725
trackUse(DIMemoryUse(User, Kind, 0, TheMemory.getNumElements()));
17131726
}
17141727
}

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,12 @@ enum DIUseKind {
298298
LoadForTypeOfSelf,
299299

300300
/// This instruction is a value_metatype on the address of 'self'.
301-
TypeOfSelf
301+
TypeOfSelf,
302+
303+
/// This instruction is the builtin for flow-sensitive current isolation
304+
/// within an actor initializer. It will be replaced with either a copy of
305+
/// its argument (injected into an (any Actor)?) or nil.
306+
FlowSensitiveSelfIsolation,
302307
};
303308

304309
/// This struct represents a single classified access to the memory object

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "DIMemoryUseCollector.h"
1616
#include "swift/AST/DiagnosticEngine.h"
1717
#include "swift/AST/DiagnosticsSIL.h"
18+
#include "swift/AST/DistributedDecl.h"
1819
#include "swift/AST/Expr.h"
1920
#include "swift/AST/Stmt.h"
2021
#include "swift/ClangImporter/ClangModule.h"
@@ -491,6 +492,7 @@ namespace {
491492
void handleTypeOfSelfUse(DIMemoryUse &Use);
492493
void handleInOutUse(const DIMemoryUse &Use);
493494
void handleEscapeUse(const DIMemoryUse &Use);
495+
void handleFlowSensitiveActorIsolationUse(const DIMemoryUse &Use);
494496

495497
bool diagnoseReturnWithoutInitializingStoredProperties(
496498
const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use);
@@ -565,6 +567,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
565567
case DIUseKind::LoadForTypeOfSelf:
566568
case DIUseKind::TypeOfSelf:
567569
case DIUseKind::Escape:
570+
case DIUseKind::FlowSensitiveSelfIsolation:
568571
continue;
569572
case DIUseKind::Assign:
570573
case DIUseKind::Set:
@@ -1160,6 +1163,10 @@ void LifetimeChecker::doIt() {
11601163
case DIUseKind::BadExplicitStore:
11611164
diagnoseBadExplicitStore(Inst);
11621165
break;
1166+
1167+
case DIUseKind::FlowSensitiveSelfIsolation:
1168+
handleFlowSensitiveActorIsolationUse(Use);
1169+
break;
11631170
}
11641171
}
11651172

@@ -1344,6 +1351,73 @@ void LifetimeChecker::handleTypeOfSelfUse(DIMemoryUse &Use) {
13441351
}
13451352
}
13461353

1354+
void LifetimeChecker::handleFlowSensitiveActorIsolationUse(
1355+
const DIMemoryUse &Use) {
1356+
bool IsSuperInitComplete, FailedSelfUse;
1357+
1358+
ASTContext &ctx = F.getASTContext();
1359+
auto builtinInst = cast<BuiltinInst>(Use.Inst);
1360+
SILBuilderWithScope B(builtinInst);
1361+
SILValue replacement;
1362+
SILType optExistentialType = builtinInst->getType();
1363+
SILLocation loc = builtinInst->getLoc();
1364+
if (isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse)) {
1365+
// 'self' is initialized, so replace this builtin with the appropriate
1366+
// operation to produce `any Actor.
1367+
1368+
SILValue anyActorValue;
1369+
auto conformance = builtinInst->getSubstitutions().getConformances()[0];
1370+
if (builtinInst->getBuiltinKind() == BuiltinValueKind::FlowSensitiveSelfIsolation) {
1371+
// Create a copy of the actor argument, which we intentionally did not
1372+
// copy in SILGen.
1373+
SILValue actor = B.createCopyValue(loc, builtinInst->getArguments()[0]);
1374+
1375+
// Inject 'self' into 'any Actor'.
1376+
ProtocolConformanceRef conformances[1] = { conformance };
1377+
SILType existentialType = optExistentialType.getOptionalObjectType();
1378+
anyActorValue = B.createInitExistentialRef(
1379+
loc, existentialType, actor->getType().getASTType(), actor,
1380+
ctx.AllocateCopy(conformances));
1381+
} else {
1382+
// Borrow the actor argument, which we need to form the appropriate
1383+
// call to the asLocalActor getter.
1384+
SILValue actor = B.createBeginBorrow(loc, builtinInst->getArguments()[0]);
1385+
1386+
// Dig out the getter for asLocalActor.
1387+
auto asLocalActorDecl = getDistributedActorAsLocalActorComputedProperty(
1388+
F.getDeclContext()->getParentModule());
1389+
auto asLocalActorGetter = asLocalActorDecl->getAccessor(AccessorKind::Get);
1390+
SILDeclRef asLocalActorRef = SILDeclRef(
1391+
asLocalActorGetter, SILDeclRef::Kind::Func);
1392+
SILFunction *asLocalActorFunc = F.getModule()
1393+
.lookUpFunction(asLocalActorRef);
1394+
SILValue asLocalActorValue = B.createFunctionRef(loc, asLocalActorFunc);
1395+
1396+
// Call asLocalActor. It produces an 'any Actor'.
1397+
anyActorValue = B.createApply(
1398+
loc,
1399+
asLocalActorValue,
1400+
SubstitutionMap::get(asLocalActorGetter->getGenericSignature(),
1401+
{ actor->getType().getASTType() },
1402+
{ conformance }),
1403+
{ actor });
1404+
B.createEndBorrow(loc, actor);
1405+
}
1406+
1407+
// Then, wrap it in an optional.
1408+
replacement = B.createEnum(
1409+
loc, anyActorValue, ctx.getOptionalSomeDecl(), optExistentialType);
1410+
} else {
1411+
// 'self' is not initialized yet, so use 'nil'.
1412+
replacement = B.createEnum(
1413+
loc, SILValue(), ctx.getOptionalNoneDecl(), optExistentialType);
1414+
}
1415+
1416+
// Introduce the replacement.
1417+
InstModCallbacks callbacks;
1418+
replaceAllUsesAndErase(builtinInst, replacement, callbacks);
1419+
}
1420+
13471421
void LifetimeChecker::emitSelfConsumedDiagnostic(SILInstruction *Inst) {
13481422
if (!shouldEmitError(Inst))
13491423
return;

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ static bool isBarrier(SILInstruction *inst) {
159159
case BuiltinValueKind::GetEnumTag:
160160
case BuiltinValueKind::InjectEnumTag:
161161
case BuiltinValueKind::ExtractFunctionIsolation:
162+
case BuiltinValueKind::FlowSensitiveSelfIsolation:
163+
case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
162164
case BuiltinValueKind::AddressOfRawLayout:
163165
return false;
164166

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
@available(SwiftStdlib 5.1, *)
5+
func f(isolatedTo actor: isolated (any Actor)?) async -> Int { 0 }
6+
7+
@available(SwiftStdlib 5.1, *)
8+
actor A {
9+
let number: Int
10+
11+
// CHECK-LABEL: sil hidden{{.*}}[ossa] @$s4test1ACACyYacfc : $@convention(method) @async (@sil_isolated @owned A) -> @owned A
12+
init() async {
13+
// First use of #isolation.
14+
// CHECK: [[ISOLATION_1:%.*]] = builtin "flowSensitiveSelfIsolation"<A>
15+
// CHECK: [[F_1:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
16+
// CHECK-NEXT: [[F_RESULT:%.*]] = apply [[F_1]]([[ISOLATION_1]])
17+
18+
// Assignment to "number" of the result.
19+
// CHECK: [[NUMBER:%.*]] = ref_element_addr {{%.*}} : $A, #A.number
20+
// CHECK: assign [[F_RESULT]] to [[NUMBER]]
21+
self.number = await f(isolatedTo: #isolation)
22+
23+
// Second use of #isolation.
24+
// CHECK: [[ISOLATION_2:%.*]] = builtin "flowSensitiveSelfIsolation"<A>
25+
// CHECK: [[F_2:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
26+
// CHECK-NEXT: apply [[F_2]]([[ISOLATION_2]])
27+
_ = await f(isolatedTo: #isolation)
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-swift-frontend -emit-sil %s -module-name test -swift-version 5 -sil-verify-all | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
@available(SwiftStdlib 5.1, *)
5+
func f(isolatedTo actor: isolated (any Actor)?) async -> Int { 0 }
6+
7+
@available(SwiftStdlib 5.1, *)
8+
actor A {
9+
let number: Int
10+
11+
// CHECK-LABEL: sil hidden{{.*}}@$s4test1ACACyYacfc : $@convention(method) @async (@sil_isolated @owned A) -> @owned A
12+
init() async {
13+
// First use of #isolation, which is replaced by 'nil'.
14+
// CHECK: [[ISOLATION_1:%.*]] = enum $Optional<any Actor>, #Optional.none!enumelt
15+
// CHECK: [[F_1:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
16+
// CHECK-NEXT: [[F_RESULT:%.*]] = apply [[F_1]]([[ISOLATION_1]])
17+
18+
// Assignment to "number" of the result.
19+
// CHECK: [[NUMBER:%.*]] = ref_element_addr {{%.*}} : $A, #A.number
20+
// CHECK: store [[F_RESULT]] to [[NUMBER]]
21+
self.number = await f(isolatedTo: #isolation)
22+
23+
// Second use of #isolation, which uses 'self' injected into (any Actor)?.
24+
// CHECK: [[ACTOR_COPY:%.*]] = end_init_let_ref %0 : $A
25+
// CHECK: strong_retain [[ACTOR_COPY]] : $A
26+
// CHECK: [[ACTOR_EXISTENTIAL:%.*]] = init_existential_ref [[ACTOR_COPY]] : $A : $A, $any Actor
27+
// CHECK: [[ISOLATION_2:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, [[ACTOR_EXISTENTIAL]]
28+
// CHECK: [[F_2:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
29+
// CHECK-NEXT: apply [[F_2]]([[ISOLATION_2]])
30+
_ = await f(isolatedTo: #isolation)
31+
}
32+
}

0 commit comments

Comments
 (0)