Skip to content

Commit b725490

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 b725490

12 files changed

+681
-139
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: 148 additions & 45 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,13 +273,70 @@ 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) {
273-
SILValue actorV;
274-
if (isInFormalEvaluationScope())
275-
actorV = actor.formalAccessBorrow(*this, loc).getValue();
276-
else
277-
actorV = actor.borrow(*this, loc).getValue();
339+
SILValue actorV = actor.borrow(*this, loc).getValue();
278340

279341
// For now, we just want to emit a hop_to_executor directly to the
280342
// actor; LowerHopToActor will add the emission logic necessary later.
@@ -291,23 +353,18 @@ SILValue SILGenFunction::emitLoadErasedExecutor(SILLocation loc,
291353
ManagedValue
292354
SILGenFunction::emitLoadErasedIsolation(SILLocation loc,
293355
ManagedValue fn) {
294-
if (isInFormalEvaluationScope())
295-
fn = fn.formalAccessBorrow(*this, loc);
296-
else
297-
fn = fn.borrow(*this, loc);
356+
fn = fn.borrow(*this, loc);
298357

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

302361
return ManagedValue::forBorrowedObjectRValue(actor);
303362
}
304363

305-
/// The ownership of the value returned here is mixed; callers that need
306-
/// an owned value must call ensurePlusOne.
307364
ManagedValue
308-
SILGenFunction::emitLoadOfFunctionIsolation(SILLocation loc,
309-
FunctionTypeIsolation isolation,
310-
ManagedValue fn) {
365+
SILGenFunction::emitFunctionTypeIsolation(SILLocation loc,
366+
FunctionTypeIsolation isolation,
367+
ManagedValue fn) {
311368
switch (isolation.getKind()) {
312369

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

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

324380
// Emit global actor isolation by loading .shared from the global actor,
325381
// erasing it into `any Actor`, and injecting that into Optional.
326-
case FunctionTypeIsolation::Kind::GlobalActor: {
327-
auto concreteActorType =
328-
isolation.getGlobalActorType()->getCanonicalType();
382+
case FunctionTypeIsolation::Kind::GlobalActor:
383+
return emitGlobalActorIsolation(loc,
384+
isolation.getGlobalActorType()->getCanonicalType());
329385

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

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

398+
static ActorIsolation getClosureIsolationInfo(SILDeclRef constant) {
399+
if (auto closure = constant.getAbstractClosureExpr()) {
400+
return closure->getActorIsolation();
401+
}
402+
auto func = constant.getAbstractFunctionDecl();
403+
assert(func && "unexpected closure constant");
404+
return getActorIsolation(func);
405+
}
338406

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

343-
for (auto conf: conformances)
344-
SGM.useConformance(conf);
435+
// The capture not being a lowered capture can happen in global code.
436+
auto value = SGF.emitRValueForDecl(loc, isolatedCapture, isolatedVarType,
437+
AccessSemantics::Ordinary)
438+
.getAsSingleValue(SGF, loc);
439+
return SGF.emitActorInstanceIsolation(loc, value, isolatedVarType);
440+
}
441+
442+
ManagedValue
443+
SILGenFunction::emitClosureIsolation(SILLocation loc, SILDeclRef constant,
444+
ArrayRef<ManagedValue> captures) {
445+
auto isolation = getClosureIsolationInfo(constant);
446+
switch (isolation) {
447+
case ActorIsolation::Unspecified:
448+
case ActorIsolation::Nonisolated:
449+
case ActorIsolation::NonisolatedUnsafe:
450+
return emitNonIsolatedIsolation(loc);
345451

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);
452+
case ActorIsolation::Erased:
453+
llvm_unreachable("closures cannot directly have erased isolation");
352454

353-
// Inject into `Optional`.
354-
auto result = B.createOptionalSome(loc, erasedActor);
455+
case ActorIsolation::GlobalActor:
456+
return emitGlobalActorIsolation(loc,
457+
isolation.getGlobalActor()->getCanonicalType());
355458

356-
return result;
459+
case ActorIsolation::ActorInstance: {
460+
// This should always be a capture. That's not expressed super-cleanly
461+
// in ActorIsolation, unfortunately.
462+
assert(isolation.getActorInstanceParameter() == 0);
463+
auto capture = isolation.getActorInstance();
464+
assert(capture);
465+
return emitLoadOfCaptureIsolation(*this, loc, capture, constant, captures);
357466
}
358-
359-
// Emit @isolated(any) isolation by loading the actor reference from the
360-
// function.
361-
case FunctionTypeIsolation::Kind::Erased:
362-
return emitLoadErasedIsolation(loc, fn);
363467
}
364-
365468
llvm_unreachable("bad kind");
366469
}
367470

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)