Skip to content

Commit 598ac05

Browse files
authored
Merge pull request #63783 from gottesmm/no-escaping-closure-part-1
[move-only] Implement escaping closure semantics part 1
2 parents 4bfd4e3 + 7d5476d commit 598ac05

37 files changed

+1623
-642
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -760,12 +760,11 @@ ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_global_var,
760760
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_global_let, none,
761761
"'%0' was consumed but it is illegal to consume a noncopyable global let. One can only read from it",
762762
(StringRef))
763-
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_escaping_let, none,
764-
"'%0' was consumed but it is illegal to consume a noncopyable escaping immutable capture. One can only read from it",
765-
(StringRef))
766763
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_escaping_var, none,
767-
"'%0' was consumed but it is illegal to consume a noncopyable escaping mutable capture. One can only read from it or assign over it",
764+
"'%0' was consumed but it is illegal to consume a noncopyable mutable capture of an escaping closure. One can only read from it or assign over it",
768765
(StringRef))
766+
ERROR(sil_moveonlychecker_let_capture_consumed, none,
767+
"'%0' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it", (StringRef))
769768

770769
NOTE(sil_moveonlychecker_moveonly_field_consumed_here, none,
771770
"move only field consumed here", ())

include/swift/AST/SemanticAttrs.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,9 @@ SEMANTICS_ATTR(LIFETIMEMANAGEMENT_COPY, "lifetimemanagement.copy")
120120

121121
SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")
122122

123+
// A flag used to turn off moveonly diagnostics on functions that allocbox to
124+
// stack specialized.
125+
SEMANTICS_ATTR(NO_MOVEONLY_DIAGNOSTICS, "sil.optimizer.moveonly.diagnostic.ignore")
126+
123127
#undef SEMANTICS_ATTR
124128

lib/SIL/IR/TypeLowering.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,26 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture,
112112
assert(var->hasStorage() &&
113113
"should not have attempted to directly capture this variable");
114114

115+
auto &lowering = getTypeLowering(
116+
var->getType(), TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(
117+
expansion.getResilienceExpansion()));
118+
119+
// If this is a noncopyable 'let' constant that is not a shared paramdecl or
120+
// used by a noescape capture, then we know it is boxed and want to pass it in
121+
// its boxed form so we can obey Swift's capture reference semantics.
122+
if (!var->supportsMutation() && lowering.getLoweredType().isPureMoveOnly() &&
123+
!capture.isNoEscape()) {
124+
auto *param = dyn_cast<ParamDecl>(var);
125+
if (!param || param->getValueOwnership() != ValueOwnership::Shared) {
126+
return CaptureKind::ImmutableBox;
127+
}
128+
}
129+
115130
// If this is a non-address-only stored 'let' constant, we can capture it
116131
// by value. If it is address-only, then we can't load it, so capture it
117132
// by its address (like a var) instead.
118-
if (!var->supportsMutation() &&
119-
!getTypeLowering(var->getType(),
120-
TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(
121-
expansion.getResilienceExpansion()))
122-
.isAddressOnly())
123-
return CaptureKind::Constant;
133+
if (!var->supportsMutation() && !lowering.isAddressOnly())
134+
return CaptureKind::Constant;
124135

125136
// In-out parameters are captured by address.
126137
if (auto *param = dyn_cast<ParamDecl>(var)) {

lib/SILGen/SILGenConstructor.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,11 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
399399
// Allocate the local variable for 'self'.
400400
emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this);
401401

402-
SILValue selfLV = VarLocs[selfDecl].value;
402+
ManagedValue selfLV =
403+
maybeEmitValueOfLocalVarDecl(selfDecl, AccessKind::ReadWrite);
404+
if (!selfLV)
405+
selfLV = maybeEmitAddressForBoxOfLocalVarDecl(selfDecl, selfDecl);
406+
assert(selfLV);
403407

404408
// Emit the prolog.
405409
emitBasicProlog(ctor->getParameters(),
@@ -491,7 +495,13 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
491495

492496
if (!F.getConventions().hasIndirectSILResults()) {
493497
// Otherwise, load and return the final 'self' value.
494-
selfValue = lowering.emitLoad(B, cleanupLoc, selfLV,
498+
if (selfLV.getType().isMoveOnly()) {
499+
selfLV = B.createMarkMustCheckInst(
500+
cleanupLoc, selfLV,
501+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable);
502+
}
503+
504+
selfValue = lowering.emitLoad(B, cleanupLoc, selfLV.getValue(),
495505
LoadOwnershipQualifier::Copy);
496506

497507
// Inject the self value into an optional if the constructor is failable.
@@ -513,17 +523,16 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
513523
returnAddress = completeReturnAddress;
514524
} else {
515525
// If this is a failable initializer, project out the payload.
516-
returnAddress = B.createInitEnumDataAddr(cleanupLoc,
517-
completeReturnAddress,
518-
getASTContext().getOptionalSomeDecl(),
519-
selfLV->getType());
526+
returnAddress = B.createInitEnumDataAddr(
527+
cleanupLoc, completeReturnAddress,
528+
getASTContext().getOptionalSomeDecl(), selfLV.getType());
520529
}
521530

522531
// We have to do a non-take copy because someone else may be using the
523532
// box (e.g. someone could have closed over it).
524-
B.createCopyAddr(cleanupLoc, selfLV, returnAddress,
533+
B.createCopyAddr(cleanupLoc, selfLV.getLValueAddress(), returnAddress,
525534
IsNotTake, IsInitialization);
526-
535+
527536
// Inject the enum tag if the result is optional because of failability.
528537
if (ctor->isFailable()) {
529538
// Inject the 'Some' tag.
@@ -1022,14 +1031,19 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
10221031
static ManagedValue emitSelfForMemberInit(SILGenFunction &SGF, SILLocation loc,
10231032
VarDecl *selfDecl) {
10241033
CanType selfFormalType = selfDecl->getType()->getCanonicalType();
1025-
if (selfFormalType->hasReferenceSemantics())
1034+
if (selfFormalType->hasReferenceSemantics()) {
10261035
return SGF.emitRValueForDecl(loc, selfDecl, selfFormalType,
10271036
AccessSemantics::DirectToStorage,
10281037
SGFContext::AllowImmediatePlusZero)
10291038
.getAsSingleValue(SGF, loc);
1030-
else
1039+
} else {
1040+
// First see if we have a variable that is boxed without a value.
1041+
if (auto value = SGF.maybeEmitAddressForBoxOfLocalVarDecl(loc, selfDecl))
1042+
return value;
1043+
// Otherwise, emit the address directly.
10311044
return SGF.emitAddressOfLocalVarDecl(loc, selfDecl, selfFormalType,
10321045
SGFAccessKind::Write);
1046+
}
10331047
}
10341048

10351049
// FIXME: Can emitMemberInit() share code with InitializationForPattern in

lib/SILGen/SILGenDecl.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -351,13 +351,13 @@ class LocalVariableInitialization : public SingleBufferInitialization {
351351
"can't emit a local var for a non-local var decl");
352352
assert(decl->hasStorage() && "can't emit storage for a computed variable");
353353
assert(!SGF.VarLocs.count(decl) && "Already have an entry for this decl?");
354+
354355
// The box type's context is lowered in the minimal resilience domain.
356+
auto instanceType = SGF.SGM.Types.getLoweredRValueType(
357+
TypeExpansionContext::minimal(), decl->getType());
355358
auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture(
356-
decl,
357-
SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
358-
decl->getType()),
359-
SGF.F.getGenericEnvironment(),
360-
/*mutable*/ true);
359+
decl, instanceType, SGF.F.getGenericEnvironment(),
360+
/*mutable*/ !instanceType->isPureMoveOnly() || !decl->isLet());
361361

362362
// The variable may have its lifetime extended by a closure, heap-allocate
363363
// it using a box.
@@ -376,12 +376,8 @@ class LocalVariableInitialization : public SingleBufferInitialization {
376376
}
377377
}
378378

379-
Addr = SGF.B.createProjectBox(decl, Box, 0);
380-
if (Addr->getType().isMoveOnly()) {
381-
// TODO: Handle no implicit copy here.
382-
Addr = SGF.B.createMarkMustCheckInst(
383-
decl, Addr, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
384-
}
379+
if (!Box->getType().isBoxedNonCopyableType(Box->getFunction()))
380+
Addr = SGF.B.createProjectBox(decl, Box, 0);
385381

386382
// Push a cleanup to destroy the local variable. This has to be
387383
// inactive until the variable is initialized.
@@ -401,16 +397,24 @@ class LocalVariableInitialization : public SingleBufferInitialization {
401397
}
402398

403399
SILValue getAddress() const {
400+
assert(Addr);
404401
return Addr;
405402
}
406403

404+
/// If we have an address, returns the address. Otherwise, if we only have a
405+
/// box, lazily projects it out and returns it.
407406
SILValue getAddressForInPlaceInitialization(SILGenFunction &SGF,
408407
SILLocation loc) override {
408+
if (!Addr && Box) {
409+
auto pbi = SGF.B.createProjectBox(loc, Box, 0);
410+
return pbi;
411+
}
412+
409413
return getAddress();
410414
}
411415

412416
bool isInPlaceInitializationOfGlobal() const override {
413-
return isa<GlobalAddrInst>(getAddress());
417+
return dyn_cast_or_null<GlobalAddrInst>(Addr);
414418
}
415419

416420
void finishUninitialized(SILGenFunction &SGF) override {
@@ -421,7 +425,11 @@ class LocalVariableInitialization : public SingleBufferInitialization {
421425
/// Remember that this is the memory location that we've emitted the
422426
/// decl to.
423427
assert(SGF.VarLocs.count(decl) == 0 && "Already emitted the local?");
424-
SGF.VarLocs[decl] = SILGenFunction::VarLoc::get(Addr, Box);
428+
429+
if (Addr)
430+
SGF.VarLocs[decl] = SILGenFunction::VarLoc::get(Addr, Box);
431+
else
432+
SGF.VarLocs[decl] = SILGenFunction::VarLoc::getForBox(Box);
425433

426434
SingleBufferInitialization::finishInitialization(SGF);
427435
assert(!DidFinish &&
@@ -485,9 +493,13 @@ class LetValueInitialization : public Initialization {
485493
isUninitialized = true;
486494
} else {
487495
// If this is a let with an initializer or bound value, we only need a
488-
// buffer if the type is address only.
496+
// buffer if the type is address only or is noncopyable.
497+
//
498+
// For noncopyable types, we always need to box them and eagerly
499+
// reproject.
489500
needsTemporaryBuffer =
490-
lowering->isAddressOnly() && SGF.silConv.useLoweredAddresses();
501+
(lowering->isAddressOnly() && SGF.silConv.useLoweredAddresses()) ||
502+
lowering->getLoweredType().isPureMoveOnly();
491503
}
492504

493505
// Make sure that we have a non-address only type when binding a
@@ -634,8 +646,7 @@ class LetValueInitialization : public Initialization {
634646
// We do this before the begin_borrow "normal" path below since move only
635647
// types do not have no implicit copy attr on them.
636648
if (value->getOwnershipKind() == OwnershipKind::Owned &&
637-
value->getType().isMoveOnly() &&
638-
!value->getType().isMoveOnlyWrapped()) {
649+
value->getType().isPureMoveOnly()) {
639650
value = SGF.B.createMoveValue(PrologueLoc, value, true /*isLexical*/);
640651
return SGF.B.createMarkMustCheckInst(
641652
PrologueLoc, value,
@@ -1253,10 +1264,10 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable) {
12531264

12541265
assert(!isa<InOutType>(varType) && "local variables should never be inout");
12551266

1256-
// If this is a 'let' initialization for a non-global, set up a
1257-
// let binding, which stores the initialization value into VarLocs directly.
1267+
// If this is a 'let' initialization for a copyable non-global, set up a let
1268+
// binding, which stores the initialization value into VarLocs directly.
12581269
if (forceImmutable && vd->getDeclContext()->isLocalContext() &&
1259-
!isa<ReferenceStorageType>(varType))
1270+
!isa<ReferenceStorageType>(varType) && !varType->isPureMoveOnly())
12601271
return InitializationPtr(new LetValueInitialization(vd, *this));
12611272

12621273
// If the variable has no initial value, emit a mark_uninitialized instruction

lib/SILGen/SILGenFunction.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ void SILGenFunction::emitCaptures(SILLocation loc,
357357

358358
// Get an address value for a SILValue if it is address only in an type
359359
// expansion context without opaque archetype substitution.
360-
auto getAddressValue = [&](SILValue entryValue) -> SILValue {
360+
auto getAddressValue = [&](VarLoc entryVarLoc) -> SILValue {
361+
SILValue entryValue = entryVarLoc.getValueOrBoxedValue(*this, vd);
361362
if (SGM.M.useLoweredAddresses()
362363
&& SGM.Types
363364
.getTypeLowering(
@@ -382,7 +383,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
382383
case CaptureKind::Constant: {
383384
// let declarations.
384385
auto &tl = getTypeLowering(valueType);
385-
SILValue Val = Entry.value;
386+
SILValue Val = Entry.getValueOrBoxedValue(*this);
386387
bool eliminateMoveOnlyWrapper =
387388
Val->getType().isMoveOnlyWrapped() &&
388389
!vd->getInterfaceType()->is<SILMoveOnlyWrappedType>();
@@ -409,6 +410,11 @@ void SILGenFunction::emitCaptures(SILLocation loc,
409410
} else {
410411
// If we have a mutable binding for a 'let', such as 'self' in an
411412
// 'init' method, load it.
413+
if (Val->getType().isMoveOnly()) {
414+
Val = B.createMarkMustCheckInst(
415+
loc, Val,
416+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable);
417+
}
412418
Val = emitLoad(loc, Val, tl, SGFContext(), IsNotTake).forward(*this);
413419
}
414420

@@ -425,14 +431,14 @@ void SILGenFunction::emitCaptures(SILLocation loc,
425431
if (canGuarantee) {
426432
// No-escaping stored declarations are captured as the
427433
// address of the value.
428-
auto entryValue = getAddressValue(Entry.value);
434+
auto entryValue = getAddressValue(Entry);
429435
capturedArgs.push_back(ManagedValue::forBorrowedRValue(entryValue));
430436
}
431437
else if (!silConv.useLoweredAddresses()) {
432438
capturedArgs.push_back(
433439
B.createCopyValue(loc, ManagedValue::forUnmanaged(Entry.value)));
434440
} else {
435-
auto entryValue = getAddressValue(Entry.value);
441+
auto entryValue = getAddressValue(Entry);
436442
// We cannot pass a valid SILDebugVariable while creating the temp here
437443
// See rdar://60425582
438444
auto addr = B.createAllocStack(loc, entryValue->getType().getObjectType());
@@ -443,7 +449,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
443449
break;
444450
}
445451
case CaptureKind::StorageAddress: {
446-
auto entryValue = getAddressValue(Entry.value);
452+
auto entryValue = getAddressValue(Entry);
447453
// No-escaping stored declarations are captured as the
448454
// address of the value.
449455
assert(entryValue->getType().isAddress() && "no address for captured var!");
@@ -452,7 +458,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
452458
}
453459

454460
case CaptureKind::Box: {
455-
auto entryValue = getAddressValue(Entry.value);
461+
auto entryValue = getAddressValue(Entry);
456462
// LValues are captured as both the box owning the value and the
457463
// address of the value.
458464
assert(entryValue->getType().isAddress() && "no address for captured var!");
@@ -501,7 +507,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
501507
break;
502508
}
503509
case CaptureKind::ImmutableBox: {
504-
auto entryValue = getAddressValue(Entry.value);
510+
auto entryValue = getAddressValue(Entry);
505511
// LValues are captured as both the box owning the value and the
506512
// address of the value.
507513
assert(entryValue->getType().isAddress() &&

0 commit comments

Comments
 (0)