Skip to content

Commit 7b3415a

Browse files
committed
Properly erase closure isolation to @isolated(any).
We do this by pushing the conversion down to the emission of the closure expression, then teaching closure emission to apply the isolation to the closure. Ideally, we combine the isolation along with the rest of the conversion peephole, but if necessary, we make sure we emit the isolation.
1 parent 89b5215 commit 7b3415a

12 files changed

+682
-134
lines changed

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,16 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
19001900
TypeExpansionContext expansion,
19011901
SmallVectorImpl<SILParameterInfo> &inputs) {
19021902

1903+
// If the function is a closure being converted to an @isolated(any) type,
1904+
// add the implicit isolation parameter.
1905+
if (auto closureInfo = TC.getClosureTypeInfo(function)) {
1906+
if (closureInfo->ExpectedLoweredType->hasErasedIsolation()) {
1907+
auto isolationTy = SILType::getOpaqueIsolationType(TC.Context);
1908+
inputs.push_back({isolationTy.getASTType(),
1909+
ParameterConvention::Direct_Guaranteed});
1910+
}
1911+
}
1912+
19031913
// NB: The generic signature may be elided from the lowered function type
19041914
// if the function is in a fully-specialized context, but we still need to
19051915
// canonicalize references to the generic parameters that may appear in

lib/SILGen/Cleanup.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ class LLVM_LIBRARY_VISIBILITY CleanupManager {
176176
/// we can only reap the cleanup stack up to the innermost depth
177177
/// that we've handed out as a Scope.
178178
Scope *innermostScope = nullptr;
179-
FormalEvaluationScope *innermostFormalScope = nullptr;
180179

181180
void popTopDeadCleanups();
182181
void emitCleanups(CleanupsDepth depth, CleanupLocation l,

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 149 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ SILValue SILGenFunction::emitGenericExecutor(SILLocation loc) {
235235
return B.createOptionalNone(loc, ty);
236236
}
237237

238+
ManagedValue SILGenFunction::emitNonIsolatedIsolation(SILLocation loc) {
239+
return B.createManagedOptionalNone(loc,
240+
SILType::getOpaqueIsolationType(getASTContext()));
241+
}
242+
238243
SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
239244
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
240245
auto actor = emitLoadOfGlobalActorShared(loc, globalActor->getCanonicalType());
@@ -268,8 +273,71 @@ SILGenFunction::emitLoadOfGlobalActorShared(SILLocation loc, CanType actorType)
268273
return actorInstance;
269274
}
270275

276+
ManagedValue
277+
SILGenFunction::emitGlobalActorIsolation(SILLocation loc,
278+
CanType globalActorType) {
279+
// GlobalActor.shared returns Self, so this should be a value of
280+
// GlobalActor type.
281+
auto actor = emitLoadOfGlobalActorShared(loc, globalActorType);
282+
283+
// Since it's just a normal actor instance, we can use the normal path.
284+
return emitActorInstanceIsolation(loc, actor, globalActorType);
285+
}
286+
287+
/// Given a value of some non-optional actor type, convert it to
288+
/// non-optional `any Actor` type.
289+
static ManagedValue
290+
emitNonOptionalActorInstanceIsolation(SILGenFunction &SGF, SILLocation loc,
291+
ManagedValue actor, CanType actorType,
292+
SILType anyActorTy) {
293+
// If we have an `any Actor` already, we're done.
294+
if (actor.getType() == anyActorTy)
295+
return actor;
296+
297+
CanType anyActorType = anyActorTy.getASTType();
298+
return SGF.emitTransformExistential(loc, actor, actorType, anyActorType);
299+
}
300+
301+
ManagedValue
302+
SILGenFunction::emitActorInstanceIsolation(SILLocation loc, ManagedValue actor,
303+
CanType actorType) {
304+
// $Optional<any Actor>
305+
auto optionalAnyActorTy = SILType::getOpaqueIsolationType(getASTContext());
306+
// Optional<any Actor> as a formal type (it's invariant to lowering)
307+
auto optionalAnyActorType = optionalAnyActorTy.getASTType();
308+
309+
// If we started with an Optional<any Actor>, we're done.
310+
if (actorType == optionalAnyActorType) {
311+
return actor;
312+
}
313+
314+
// Otherwise, if we have an optional value, we need to transform the payload.
315+
auto actorObjectType = actorType.getOptionalObjectType();
316+
if (actorObjectType) {
317+
return emitOptionalToOptional(loc, actor, optionalAnyActorTy,
318+
[&](SILGenFunction &SGF, SILLocation loc, ManagedValue actorObject,
319+
SILType anyActorTy, SGFContext C) {
320+
return emitNonOptionalActorInstanceIsolation(*this, loc, actorObject,
321+
actorObjectType, anyActorTy);
322+
});
323+
}
324+
325+
// Otherwise, transform the non-optional value we have, then inject that
326+
// into Optional.
327+
SILType anyActorTy = optionalAnyActorTy.getOptionalObjectType();
328+
ManagedValue anyActor =
329+
emitNonOptionalActorInstanceIsolation(*this, loc, actor, actorType,
330+
anyActorTy);
331+
332+
// Inject into `Optional`.
333+
auto result = B.createOptionalSome(loc, anyActor);
334+
return result;
335+
}
336+
271337
SILValue SILGenFunction::emitLoadActorExecutor(SILLocation loc,
272338
ManagedValue actor) {
339+
// FIXME: Checking for whether we're in a formal evaluation scope
340+
// like this doesn't seem like a good pattern.
273341
SILValue actorV;
274342
if (isInFormalEvaluationScope())
275343
actorV = actor.formalAccessBorrow(*this, loc).getValue();
@@ -291,23 +359,18 @@ SILValue SILGenFunction::emitLoadErasedExecutor(SILLocation loc,
291359
ManagedValue
292360
SILGenFunction::emitLoadErasedIsolation(SILLocation loc,
293361
ManagedValue fn) {
294-
if (isInFormalEvaluationScope())
295-
fn = fn.formalAccessBorrow(*this, loc);
296-
else
297-
fn = fn.borrow(*this, loc);
362+
fn = fn.borrow(*this, loc);
298363

299364
// This expects a borrowed function and returns a borrowed (any Actor)?.
300365
auto actor = B.createFunctionExtractIsolation(loc, fn.getValue());
301366

302367
return ManagedValue::forBorrowedObjectRValue(actor);
303368
}
304369

305-
/// The ownership of the value returned here is mixed; callers that need
306-
/// an owned value must call ensurePlusOne.
307370
ManagedValue
308-
SILGenFunction::emitLoadOfFunctionIsolation(SILLocation loc,
309-
FunctionTypeIsolation isolation,
310-
ManagedValue fn) {
371+
SILGenFunction::emitFunctionTypeIsolation(SILLocation loc,
372+
FunctionTypeIsolation isolation,
373+
ManagedValue fn) {
311374
switch (isolation.getKind()) {
312375

313376
// Parameter-isolated functions don't have a specific actor they're isolated
@@ -318,50 +381,96 @@ SILGenFunction::emitLoadOfFunctionIsolation(SILLocation loc,
318381

319382
// Emit nonisolated by simply emitting Optional.none in the result type.
320383
case FunctionTypeIsolation::Kind::NonIsolated:
321-
return B.createManagedOptionalNone(loc,
322-
SILType::getOpaqueIsolationType(getASTContext()));
384+
return emitNonIsolatedIsolation(loc);
323385

324386
// Emit global actor isolation by loading .shared from the global actor,
325387
// erasing it into `any Actor`, and injecting that into Optional.
326-
case FunctionTypeIsolation::Kind::GlobalActor: {
327-
auto concreteActorType =
328-
isolation.getGlobalActorType()->getCanonicalType();
388+
case FunctionTypeIsolation::Kind::GlobalActor:
389+
return emitGlobalActorIsolation(loc,
390+
isolation.getGlobalActorType()->getCanonicalType());
329391

330-
// GlobalActor.shared returns Self, so this should be a value of the
331-
// actor type.
332-
auto actor = emitLoadOfGlobalActorShared(loc, concreteActorType);
392+
// Emit @isolated(any) isolation by loading the actor reference from the
393+
// function.
394+
case FunctionTypeIsolation::Kind::Erased: {
395+
Scope scope(*this, CleanupLocation(loc));
396+
auto value = emitLoadErasedIsolation(loc, fn).copy(*this, loc);
397+
return scope.popPreservingValue(value);
398+
}
399+
}
333400

334-
auto optionalAnyActorTy = SILType::getOpaqueIsolationType(getASTContext());
335-
auto anyActorTy = optionalAnyActorTy.getOptionalObjectType();
336-
assert(anyActorTy);
401+
llvm_unreachable("bad kind");
402+
}
337403

404+
static ActorIsolation getClosureIsolationInfo(SILDeclRef constant) {
405+
if (auto closure = constant.getAbstractClosureExpr()) {
406+
return closure->getActorIsolation();
407+
}
408+
auto func = constant.getAbstractFunctionDecl();
409+
assert(func && "unexpected closure constant");
410+
return getActorIsolation(func);
411+
}
338412

339-
ArrayRef<ProtocolConformanceRef> conformances =
340-
SGM.SwiftModule->collectExistentialConformances(concreteActorType,
341-
anyActorTy.getASTType());
413+
static ManagedValue emitLoadOfCaptureIsolation(SILGenFunction &SGF,
414+
SILLocation loc,
415+
VarDecl *isolatedCapture,
416+
SILDeclRef constant,
417+
ArrayRef<ManagedValue> captureArgs) {
418+
auto &TC = SGF.SGM.Types;
419+
auto captureInfo = TC.getLoweredLocalCaptures(constant);
420+
421+
auto isolatedVarType =
422+
isolatedCapture->getInterfaceType()->getCanonicalType();
423+
424+
// Capture arguments are 1-1 with the lowered capture info.
425+
auto captures = captureInfo.getCaptures();
426+
for (auto i : indices(captures)) {
427+
const auto &capture = captures[i];
428+
if (capture.isDynamicSelfMetadata()) continue;
429+
auto capturedVar = capture.getDecl();
430+
if (capturedVar != isolatedCapture) continue;
431+
432+
// Captured actor references should always be captured as constants.
433+
assert(TC.getDeclCaptureKind(capture,
434+
TC.getCaptureTypeExpansionContext(constant))
435+
== CaptureKind::Constant);
436+
437+
auto value = captureArgs[i].copy(SGF, loc);
438+
return SGF.emitActorInstanceIsolation(loc, value, isolatedVarType);
439+
}
342440

343-
for (auto conf: conformances)
344-
SGM.useConformance(conf);
441+
// The capture not being a lowered capture can happen in global code.
442+
auto value = SGF.emitRValueForDecl(loc, isolatedCapture, isolatedVarType,
443+
AccessSemantics::Ordinary)
444+
.getAsSingleValue(SGF, loc);
445+
return SGF.emitActorInstanceIsolation(loc, value, isolatedVarType);
446+
}
345447

346-
// Erase to `any Actor`.
347-
assert(anyActorTy.getPreferredExistentialRepresentation(concreteActorType)
348-
== ExistentialRepresentation::Class);
349-
auto erasedActor = B.createInitExistentialRef(loc, anyActorTy,
350-
concreteActorType,
351-
actor, conformances);
448+
ManagedValue
449+
SILGenFunction::emitClosureIsolation(SILLocation loc, SILDeclRef constant,
450+
ArrayRef<ManagedValue> captures) {
451+
auto isolation = getClosureIsolationInfo(constant);
452+
switch (isolation) {
453+
case ActorIsolation::Unspecified:
454+
case ActorIsolation::Nonisolated:
455+
case ActorIsolation::NonisolatedUnsafe:
456+
return emitNonIsolatedIsolation(loc);
352457

353-
// Inject into `Optional`.
354-
auto result = B.createOptionalSome(loc, erasedActor);
458+
case ActorIsolation::Erased:
459+
llvm_unreachable("closures cannot directly have erased isolation");
355460

356-
return result;
357-
}
461+
case ActorIsolation::GlobalActor:
462+
return emitGlobalActorIsolation(loc,
463+
isolation.getGlobalActor()->getCanonicalType());
358464

359-
// Emit @isolated(any) isolation by loading the actor reference from the
360-
// function.
361-
case FunctionTypeIsolation::Kind::Erased:
362-
return emitLoadErasedIsolation(loc, fn);
465+
case ActorIsolation::ActorInstance: {
466+
// This should always be a capture. That's not expressed super-cleanly
467+
// in ActorIsolation, unfortunately.
468+
assert(isolation.getActorInstanceParameter() == 0);
469+
auto capture = isolation.getActorInstance();
470+
assert(capture);
471+
return emitLoadOfCaptureIsolation(*this, loc, capture, constant, captures);
472+
}
363473
}
364-
365474
llvm_unreachable("bad kind");
366475
}
367476

lib/SILGen/SILGenEpilog.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ using namespace Lowering;
2222

2323
void SILGenFunction::prepareEpilog(
2424
DeclContext *DC, std::optional<Type> directResultType,
25-
std::optional<Type> errorType, CleanupLocation CleanupL,
26-
std::optional<AbstractionPattern> origClosureType) {
25+
std::optional<Type> errorType, CleanupLocation CleanupL) {
2726
auto *epilogBB = createBasicBlock();
2827

2928
// If we have any direct results, receive them via BB arguments.
@@ -66,8 +65,8 @@ void SILGenFunction::prepareEpilog(
6665

6766
if (errorType) {
6867
auto genericSig = DC->getGenericSignatureOfContext();
69-
AbstractionPattern origErrorType = origClosureType
70-
? *origClosureType->getFunctionThrownErrorType()
68+
AbstractionPattern origErrorType = TypeContext
69+
? *TypeContext->OrigType.getFunctionThrownErrorType()
7170
: AbstractionPattern(genericSig.getCanonicalSignature(),
7271
(*errorType)->getCanonicalType());
7372

0 commit comments

Comments
 (0)