Skip to content

Commit d1ae73f

Browse files
committedJun 10, 2024··
Handle flow-sensitive #isolation in distributed actor initializers.
Distributed actors can be treated as actors by accessing the `asLocalActor` property. When lowering `#isolation` in a distributed actor initializer, use a separate builtin `flowSensitiveDistributedSelfIsolation` to capture the conformance to `DistributedActor`, and have Definite Initialization introduce the call to the `asLocalActor` getter when needed.

10 files changed

+144
-34
lines changed
 

‎include/swift/AST/ASTSynthesis.h

+4
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

+12-1
Original file line numberDiff line numberDiff line change
@@ -899,14 +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) -> T?
902+
/// flowSensitiveSelfIsolation<T: Actor>(_ actor: T) -> (any Actor)?
903903
///
904904
/// Used only in actor initializers, this builtin lowers to either 'actor'
905905
/// (wrapped in an optional) or 'nil' depending on whether 'self' has been
906906
/// initialized at this point. 'actor' is always an alias for the 'self'
907907
/// being initialized.
908908
BUILTIN_MISC_OPERATION(FlowSensitiveSelfIsolation, "flowSensitiveSelfIsolation", "", Special)
909909

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+
910921
/// endAsyncLet(): (Builtin.RawPointer) -> Void
911922
///
912923
/// DEPRECATED. The swift_asyncLet_finish intrinsic and endAsyncLetLifetime

‎lib/AST/Builtins.cpp

+15-8
Original file line numberDiff line numberDiff line change
@@ -2089,14 +2089,18 @@ static ValueDecl *getHopToActor(ASTContext &ctx, Identifier id) {
20892089
return builder.build(id);
20902090
}
20912091

2092-
static ValueDecl *getFlowSensitiveSelfIsolation(ASTContext &ctx, Identifier id) {
2092+
static ValueDecl *getFlowSensitiveSelfIsolation(
2093+
ASTContext &ctx, Identifier id, bool isDistributed
2094+
) {
20932095
BuiltinFunctionBuilder builder(ctx);
2094-
return getBuiltinFunction(ctx, id, _thin,
2095-
_generics(_unrestricted,
2096-
_conformsToDefaults(0),
2097-
_conformsTo(_typeparam(0), _actor)),
2098-
_parameters(_typeparam(0)),
2099-
_optional(_existential(_actor)));
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)));
21002104
}
21012105

21022106
static ValueDecl *getDistributedActorAsAnyActor(ASTContext &ctx, Identifier id) {
@@ -3202,7 +3206,10 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
32023206
return getHopToActor(Context, Id);
32033207

32043208
case BuiltinValueKind::FlowSensitiveSelfIsolation:
3205-
return getFlowSensitiveSelfIsolation(Context, Id);
3209+
return getFlowSensitiveSelfIsolation(Context, Id, false);
3210+
3211+
case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
3212+
return getFlowSensitiveSelfIsolation(Context, Id, true);
32063213

32073214
case BuiltinValueKind::AutoDiffCreateLinearMapContextWithType:
32083215
return getAutoDiffCreateLinearMapContext(Context, Id);

‎lib/SIL/IR/OperandOwnership.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroup)
907907
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroupWithFlags)
908908
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyTaskGroup)
909909
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveSelfIsolation)
910+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveDistributedSelfIsolation)
910911

911912
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, COWBufferForReading)
912913

‎lib/SIL/IR/ValueOwnership.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
631631
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
632632
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
633633
CONSTANT_OWNERSHIP_BUILTIN(Owned, FlowSensitiveSelfIsolation)
634+
CONSTANT_OWNERSHIP_BUILTIN(Owned, FlowSensitiveDistributedSelfIsolation)
634635
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
635636
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
636637
CONSTANT_OWNERSHIP_BUILTIN(Owned, DistributedActorAsAnyActor)

‎lib/SILGen/SILGenExpr.cpp

+27-8
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"
@@ -6755,23 +6756,41 @@ RValue RValueEmitter::visitCurrentContextIsolationExpr(
67556756
isolation.getActorInstance() == ctor->getImplicitSelfDecl()) {
67566757
ASTContext &ctx = SGF.getASTContext();
67576758
auto builtinName = ctx.getIdentifier(
6758-
getBuiltinName(BuiltinValueKind::FlowSensitiveSelfIsolation));
6759+
isolation.isDistributedActor()
6760+
? getBuiltinName(BuiltinValueKind::FlowSensitiveDistributedSelfIsolation)
6761+
: getBuiltinName(BuiltinValueKind::FlowSensitiveSelfIsolation));
67596762
SILType resultTy = SGF.getLoweredType(E->getType());
67606763

67616764
auto injection = cast<InjectIntoOptionalExpr>(E->getActor());
6762-
auto erasure = cast<ErasureExpr>(injection->getSubExpr());
6763-
auto conformance = erasure->getConformances()[0];
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+
}
67646786
SGF.SGM.useConformance(conformance);
67656787

6766-
auto origActorExpr = erasure->getSubExpr();
6767-
auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor);
67686788
SubstitutionMap subs = SubstitutionMap::getProtocolSubstitutions(
6769-
actorProto, origActorExpr->getType(), conformance);
6789+
conformance.getRequirement(), origActorExpr->getType(), conformance);
67706790
auto origActor = SGF.maybeEmitValueOfLocalVarDecl(
67716791
ctor->getImplicitSelfDecl(), AccessKind::Read).getValue();
67726792
auto call = SGF.B.createBuiltin(E, builtinName, resultTy, subs, origActor);
6773-
return RValue(SGF, E,
6774-
ManagedValue::forForwardedRValue(SGF, call));
6793+
return RValue(SGF, E, ManagedValue::forForwardedRValue(SGF, call));
67756794
}
67766795
}
67776796

‎lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,9 @@ void ElementUseCollector::collectClassSelfUses(
17141714
// they have a fully-formed 'self' to use.
17151715
if (auto builtin = dyn_cast<BuiltinInst>(User)) {
17161716
if (auto builtinKind = builtin->getBuiltinKind()) {
1717-
if (*builtinKind == BuiltinValueKind::FlowSensitiveSelfIsolation) {
1717+
if (*builtinKind == BuiltinValueKind::FlowSensitiveSelfIsolation ||
1718+
*builtinKind ==
1719+
BuiltinValueKind::FlowSensitiveDistributedSelfIsolation) {
17181720
Kind = DIUseKind::FlowSensitiveSelfIsolation;
17191721
}
17201722
}

‎lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

+43-16
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"
@@ -1361,25 +1362,51 @@ void LifetimeChecker::handleFlowSensitiveActorIsolationUse(
13611362
SILType optExistentialType = builtinInst->getType();
13621363
SILLocation loc = builtinInst->getLoc();
13631364
if (isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse)) {
1364-
// 'self' is initialized, so replace this builtin with an injection of the
1365-
// argument into (any Actor)?.
1366-
1367-
// Create a copy of the actor argument, which we intentionally did not
1368-
// copy in SILGen.
1369-
SILValue actor = B.createCopyValue(loc, builtinInst->getArguments()[0]);
1370-
1371-
// Inject 'self' into 'any Actor'.
1372-
ProtocolConformanceRef conformances[1] = {
1373-
builtinInst->getSubstitutions().getConformances()[0]
1374-
};
1375-
SILType existentialType = optExistentialType.getOptionalObjectType();
1376-
SILValue existentialBox = B.createInitExistentialRef(
1377-
loc, existentialType, actor->getType().getASTType(), actor,
1378-
ctx.AllocateCopy(conformances));
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+
}
13791406

13801407
// Then, wrap it in an optional.
13811408
replacement = B.createEnum(
1382-
loc, existentialBox, ctx.getOptionalSomeDecl(), optExistentialType);
1409+
loc, anyActorValue, ctx.getOptionalSomeDecl(), optExistentialType);
13831410
} else {
13841411
// 'self' is not initialized yet, so use 'nil'.
13851412
replacement = B.createEnum(

‎lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ static bool isBarrier(SILInstruction *inst) {
160160
case BuiltinValueKind::InjectEnumTag:
161161
case BuiltinValueKind::ExtractFunctionIsolation:
162162
case BuiltinValueKind::FlowSensitiveSelfIsolation:
163+
case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
163164
case BuiltinValueKind::AddressOfRawLayout:
164165
return false;
165166

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -emit-sil %s -module-name test -swift-version 5 -sil-verify-all | %FileCheck %s
2+
// REQUIRES: concurrency, distributed
3+
4+
import Distributed
5+
6+
@available(SwiftStdlib 5.1, *)
7+
func f(isolatedTo actor: isolated (any Actor)?) async -> Int { 0 }
8+
9+
@available(SwiftStdlib 5.7, *)
10+
distributed actor NotCodableDA<ActorSystem>
11+
where ActorSystem: DistributedActorSystem<any Codable> {
12+
let number: Int
13+
14+
// CHECK-LABEL: sil hidden{{.*}}@$s4test12NotCodableDAC11actorSystemACyxGx_tYacfc : $@convention(method) @async <ActorSystem where ActorSystem : DistributedActorSystem, ActorSystem.SerializationRequirement == any Decodable & Encodable> (@in ActorSystem, @sil_isolated @owned NotCodableDA<ActorSystem>) -> @owned NotCodableDA<ActorSystem> {
15+
init(actorSystem: ActorSystem) async {
16+
self.actorSystem = actorSystem
17+
18+
// First use of #isolation, which is replaced by 'nil'.
19+
// CHECK: [[ISOLATION_1:%.*]] = enum $Optional<any Actor>, #Optional.none!enumelt
20+
// CHECK: [[F_1:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
21+
// CHECK-NEXT: [[F_RESULT:%.*]] = apply [[F_1]]([[ISOLATION_1]])
22+
23+
// Assignment to "number" of the result.
24+
// CHECK: [[NUMBER:%.*]] = ref_element_addr {{%.*}} : $NotCodableDA<ActorSystem>, #NotCodableDA.number
25+
// CHECK: store [[F_RESULT]] to [[NUMBER]]
26+
self.number = await f(isolatedTo: #isolation)
27+
28+
// Second use of #isolation, which uses 'self.asLocalActor''
29+
// CHECK: [[AS_LOCAL_ACTOR_FN:%.*]] = function_ref @$s11Distributed0A5ActorPAAE07asLocalB0ScA_pvg : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor> (@sil_isolated @guaranteed τ_0_0) -> @owned any Actor
30+
// CHECK-NEXT: [[ACTOR_EXISTENTIAL:%.*]] = apply [[AS_LOCAL_ACTOR_FN]]<NotCodableDA<ActorSystem>>(%1) : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor> (@sil_isolated @guaranteed τ_0_0) -> @owned any Actor
31+
// CHECK: [[ISOLATION_2:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, [[ACTOR_EXISTENTIAL]]
32+
// CHECK: [[F_2:%.*]] = function_ref @$s4test1f10isolatedToSiScA_pSgYi_tYaF
33+
// CHECK-NEXT: apply [[F_2]]([[ISOLATION_2]])
34+
35+
_ = await f(isolatedTo: #isolation)
36+
}
37+
}

0 commit comments

Comments
 (0)
Please sign in to comment.