Skip to content

Commit 0d11e8e

Browse files
committed
[no-implicit-copy] Update SILGen/move checker to work with new patterns from copyable_to_moveonly and friends.
This involved doing the following: 1. Update the move only checker to look for new patterns. 2. Teach emitSemanticStore to use a moveonlywrapper_to_copyable to store moveonly values into memory. The various checkers will validate that this code is correct. 3. When emitting an apply, always unwrap move only variables. In the future, I am going to avoid this if a parameter is explicitly marked as also being moveonly (e.x.: @moveOnly parameter or @NoEscape argument). 4. Convert from moveOnly -> copyable on return inst automatically in SILGen. 5. Fix SILGenLValue emission so we emit an error diagnostic later rather than crash. This is needed to keep SILGen emitting move only addresses (that is no implicit copy address only lets) in a form that the move only checker then will error upon. Without this change, SILGen crashes instead of emitting an error diagnostic in the following test: .//test/SILOptimizer/move_only_checker_addressonly_fail.swift
1 parent 82032cd commit 0d11e8e

File tree

9 files changed

+437
-81
lines changed

9 files changed

+437
-81
lines changed

lib/SILGen/SILGenApply.cpp

+27-2
Original file line numberDiff line numberDiff line change
@@ -1784,8 +1784,33 @@ static void emitRawApply(SILGenFunction &SGF,
17841784

17851785
// Gather the arguments.
17861786
for (auto i : indices(args)) {
1787-
auto argValue = (inputParams[i].isConsumed() ? args[i].forward(SGF)
1788-
: args[i].getValue());
1787+
SILValue argValue;
1788+
if (inputParams[i].isConsumed()) {
1789+
argValue = args[i].forward(SGF);
1790+
if (argValue->getType().isMoveOnlyWrapped()) {
1791+
argValue =
1792+
SGF.B.createOwnedMoveOnlyWrapperToCopyableValue(loc, argValue);
1793+
}
1794+
} else {
1795+
ManagedValue arg = args[i];
1796+
1797+
// Move only is not represented in the Swift level type system, so if we
1798+
// have a move only value, convert it to a non-move only value. The
1799+
// move/is no escape checkers will ensure that it is legal to do this or
1800+
// will error. At this point we just want to make sure that the emitted
1801+
// types line up.
1802+
if (arg.getType().isMoveOnlyWrapped()) {
1803+
// We need to borrow so that we can convert from $@moveOnly T -> $T. Use
1804+
// a formal access borrow to ensure that we have tight scopes like we do
1805+
// when we borrow fn.
1806+
if (!arg.isPlusZero())
1807+
arg = arg.formalAccessBorrow(SGF, loc);
1808+
arg = SGF.B.createGuaranteedMoveOnlyWrapperToCopyableValue(loc, arg);
1809+
}
1810+
1811+
argValue = arg.getValue();
1812+
}
1813+
17891814
#ifndef NDEBUG
17901815
auto inputTy =
17911816
substFnConv.getSILType(inputParams[i], SGF.getTypeExpansionContext());

lib/SILGen/SILGenBuilder.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "SILGenBuilder.h"
1414
#include "ArgumentSource.h"
15+
#include "Cleanup.h"
1516
#include "RValue.h"
1617
#include "SILGenFunction.h"
1718
#include "Scope.h"
@@ -450,6 +451,9 @@ static ManagedValue createInputFunctionArgument(SILGenBuilder &B, SILType type,
450451
case SILArgumentConvention::Direct_Unowned:
451452
// Unowned parameters are only guaranteed at the instant of the call, so we
452453
// must retain them even if we're in a context that can accept a +0 value.
454+
//
455+
// NOTE: If we have a trivial value, the copy will do nothing, so this is
456+
// just a convenient way to avoid writing conditional code.
453457
return ManagedValue::forUnmanaged(arg).copy(SGF, loc);
454458

455459
case SILArgumentConvention::Direct_Owned:
@@ -882,3 +886,37 @@ ManagedValue SILGenBuilder::createMarkDependence(SILLocation loc,
882886
base.forward(getSILGenFunction()));
883887
return cloner.clone(mdi);
884888
}
889+
890+
ManagedValue SILGenBuilder::createBeginBorrow(SILLocation loc,
891+
ManagedValue value,
892+
bool isLexical) {
893+
auto *newValue =
894+
SILBuilder::createBeginBorrow(loc, value.getValue(), isLexical);
895+
SGF.emitManagedBorrowedRValueWithCleanup(newValue);
896+
return ManagedValue::forUnmanaged(newValue);
897+
}
898+
899+
ManagedValue SILGenBuilder::createMoveValue(SILLocation loc,
900+
ManagedValue value) {
901+
assert(value.isPlusOne(SGF) && "Must be +1 to be moved!");
902+
CleanupCloner cloner(*this, value);
903+
auto *mdi = createMoveValue(loc, value.forward(getSILGenFunction()));
904+
return cloner.clone(mdi);
905+
}
906+
907+
ManagedValue
908+
SILGenBuilder::createOwnedMoveOnlyWrapperToCopyableValue(SILLocation loc,
909+
ManagedValue value) {
910+
assert(value.isPlusOne(SGF) && "Argument must be at +1!");
911+
CleanupCloner cloner(*this, value);
912+
auto *mdi = createOwnedMoveOnlyWrapperToCopyableValue(
913+
loc, value.forward(getSILGenFunction()));
914+
return cloner.clone(mdi);
915+
}
916+
917+
ManagedValue SILGenBuilder::createGuaranteedMoveOnlyWrapperToCopyableValue(
918+
SILLocation loc, ManagedValue value) {
919+
auto *mdi =
920+
createGuaranteedMoveOnlyWrapperToCopyableValue(loc, value.getValue());
921+
return ManagedValue::forUnmanaged(mdi);
922+
}

lib/SILGen/SILGenBuilder.h

+31-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
#include "JumpDest.h"
2727
#include "ManagedValue.h"
2828
#include "RValue.h"
29-
#include "swift/SIL/SILBuilder.h"
3029
#include "swift/Basic/ProfileCounter.h"
30+
#include "swift/SIL/SILBuilder.h"
31+
#include "swift/SIL/SILLocation.h"
3132

3233
namespace swift {
3334
namespace Lowering {
@@ -362,7 +363,19 @@ class SILGenBuilder : public SILBuilder {
362363
BranchInst *createBranch(SILLocation Loc, SILBasicBlock *TargetBlock,
363364
ArrayRef<ManagedValue> Args);
364365

365-
using SILBuilder::createReturn;
366+
ReturnInst *createReturn(SILLocation Loc, SILValue ReturnValue) {
367+
// If we have a move only type as our "result type", convert it back to
368+
// being a copyable type. Move only types are never returned today and we
369+
// will rely on the SIL level move only and no escape checker to validate
370+
// that this is a correct usage. So just make the types line up.
371+
if (ReturnValue->getType().isMoveOnlyWrapped()) {
372+
auto cvtLoc = RegularLocation::getAutoGeneratedLocation();
373+
ReturnValue =
374+
createOwnedMoveOnlyWrapperToCopyableValue(cvtLoc, ReturnValue);
375+
}
376+
return SILBuilder::createReturn(Loc, ReturnValue);
377+
}
378+
366379
ReturnInst *createReturn(SILLocation Loc, ManagedValue ReturnValue);
367380

368381
ReturnInst *createReturn(SILLocation Loc, SILValue ReturnValue,
@@ -385,6 +398,22 @@ class SILGenBuilder : public SILBuilder {
385398
using SILBuilder::createMarkDependence;
386399
ManagedValue createMarkDependence(SILLocation loc, ManagedValue value,
387400
ManagedValue base);
401+
402+
using SILBuilder::createBeginBorrow;
403+
ManagedValue createBeginBorrow(SILLocation loc, ManagedValue value,
404+
bool isLexical = false);
405+
406+
using SILBuilder::createMoveValue;
407+
ManagedValue createMoveValue(SILLocation loc, ManagedValue value);
408+
409+
using SILBuilder::createOwnedMoveOnlyWrapperToCopyableValue;
410+
ManagedValue createOwnedMoveOnlyWrapperToCopyableValue(SILLocation loc,
411+
ManagedValue value);
412+
413+
using SILBuilder::createGuaranteedMoveOnlyWrapperToCopyableValue;
414+
ManagedValue
415+
createGuaranteedMoveOnlyWrapperToCopyableValue(SILLocation loc,
416+
ManagedValue value);
388417
};
389418

390419
} // namespace Lowering

lib/SILGen/SILGenDecl.cpp

+106-32
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,15 @@ class LetValueInitialization : public Initialization {
443443

444444
public:
445445
LetValueInitialization(VarDecl *vd, SILGenFunction &SGF) : vd(vd) {
446-
auto &lowering = SGF.getTypeLowering(vd->getType());
447-
446+
const TypeLowering *lowering = nullptr;
447+
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
448+
vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
449+
lowering = &SGF.getTypeLowering(
450+
SILMoveOnlyType::get(vd->getType()->getCanonicalType()));
451+
} else {
452+
lowering = &SGF.getTypeLowering(vd->getType());
453+
}
454+
448455
// Decide whether we need a temporary stack buffer to evaluate this 'let'.
449456
// There are four cases we need to handle here: parameters, initialized (or
450457
// bound) decls, uninitialized ones, and async let declarations.
@@ -469,13 +476,13 @@ class LetValueInitialization : public Initialization {
469476
// If this is a let with an initializer or bound value, we only need a
470477
// buffer if the type is address only.
471478
needsTemporaryBuffer =
472-
lowering.isAddressOnly() && SGF.silConv.useLoweredAddresses();
479+
lowering->isAddressOnly() && SGF.silConv.useLoweredAddresses();
473480
}
474481

475482
// Make sure that we have a non-address only type when binding a
476483
// @_noImplicitCopy let.
477-
if (SGF.getASTContext().LangOpts.hasFeature(Feature::MoveOnly) &&
478-
lowering.isAddressOnly() &&
484+
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
485+
lowering->isAddressOnly() &&
479486
vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
480487
auto d = diag::noimplicitcopy_used_on_generic_or_existential;
481488
diagnose(SGF.getASTContext(), vd->getLoc(), d);
@@ -485,13 +492,13 @@ class LetValueInitialization : public Initialization {
485492
bool isLexical =
486493
SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule());
487494
address =
488-
SGF.emitTemporaryAllocation(vd, lowering.getLoweredType(),
495+
SGF.emitTemporaryAllocation(vd, lowering->getLoweredType(),
489496
false /*hasDynamicLifetime*/, isLexical);
490497
if (isUninitialized)
491498
address = SGF.B.createMarkUninitializedVar(vd, address);
492-
DestroyCleanup = SGF.enterDormantTemporaryCleanup(address, lowering);
499+
DestroyCleanup = SGF.enterDormantTemporaryCleanup(address, *lowering);
493500
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(address);
494-
} else if (!lowering.isTrivial()) {
501+
} else if (!lowering->isTrivial()) {
495502
// Push a cleanup to destroy the let declaration. This has to be
496503
// inactive until the variable is initialized: if control flow exits the
497504
// before the value is bound, we don't want to destroy the value.
@@ -554,31 +561,68 @@ class LetValueInitialization : public Initialization {
554561
address = value;
555562
SILLocation PrologueLoc(vd);
556563

557-
if (SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule()) &&
558-
value->getOwnershipKind() != OwnershipKind::None) {
559-
if (!SGF.getASTContext().LangOpts.hasFeature(Feature::MoveOnly)) {
560-
value = SILValue(
561-
SGF.B.createBeginBorrow(PrologueLoc, value, /*isLexical*/ true));
562-
} else {
563-
// If we have an owned value that had a cleanup, then create a
564-
// move_value that acts as a consuming use of the value. The reason why
565-
// we want this is even if we are only performing a borrow for our
566-
// lexical lifetime, we want to ensure that our defs see this
567-
// initialization as consuming this value.
568-
if (value->getOwnershipKind() == OwnershipKind::Owned) {
569-
assert(wasPlusOne);
570-
value = SILValue(SGF.B.createMoveValue(PrologueLoc, value));
571-
}
564+
if (SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule())) {
565+
if (value->getOwnershipKind() != OwnershipKind::None) {
566+
if (!SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly)) {
567+
value =
568+
SGF.B.createBeginBorrow(PrologueLoc, value, /*isLexical*/ true);
569+
} else {
570+
// If we have an owned moveonly value that had a cleanup, then create
571+
// a move_value that acts as a consuming use of the value. The reason
572+
// why we want this is even if we are only performing a borrow for our
573+
// lexical lifetime, we want to ensure that our defs see this
574+
// initialization as consuming this value.
575+
if (value->getType().isMoveOnlyWrapped() &&
576+
value->getOwnershipKind() == OwnershipKind::Owned) {
577+
assert(wasPlusOne);
578+
// NOTE: If our type is trivial when not wrapped in a
579+
// SILMoveOnlyType, this will return a trivial value. We rely on the
580+
// checker to determine if this is an acceptable use of the value.
581+
value = SGF.B.createOwnedMoveOnlyWrapperToCopyableValue(PrologueLoc,
582+
value);
583+
}
572584

573-
if (vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
574-
value = SILValue(SGF.B.createBeginBorrow(PrologueLoc, value,
575-
/*isLexical*/ true));
576-
value = SGF.B.createCopyValue(PrologueLoc, value);
585+
// If we still have a non-trivial thing, emit code that will need to
586+
// be cleaned up. If we are now trivial, we do not need to cleanup
587+
// anything.
588+
if (!value->getType().isTrivial(SGF.F)) {
589+
if (vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
590+
value = SGF.B.createBeginBorrow(PrologueLoc, value,
591+
/*isLexical*/ true);
592+
value = SGF.B.createCopyValue(PrologueLoc, value);
593+
value = SGF.B.createCopyableToMoveOnlyWrapperValue(PrologueLoc,
594+
value);
595+
value = SGF.B.createMarkMustCheckInst(
596+
PrologueLoc, value,
597+
MarkMustCheckInst::CheckKind::NoImplicitCopy);
598+
} else {
599+
value = SGF.B.createBeginBorrow(PrologueLoc, value,
600+
/*isLexical*/ true);
601+
}
602+
}
603+
}
604+
} else {
605+
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
606+
vd->getAttrs().hasAttribute<NoImplicitCopyAttr>() &&
607+
value->getType().isTrivial(SGF.F)) {
608+
// We are abusing this. This should be a separate instruction just for
609+
// converting from copyable trivial to move only. I am abusing it
610+
// here by using it multiple times in different ways.
611+
value =
612+
SGF.B.createCopyableToMoveOnlyWrapperValue(PrologueLoc, value);
613+
value = SGF.B.createBeginBorrow(PrologueLoc, value,
614+
/*isLexical*/ true);
615+
// We use an explicit copy value since:
616+
//
617+
// 1. We already have a move only type here. So we can't use a normal
618+
// copy here due to the pattern we are creating. We could avoid this,
619+
// but it is not worth fixing b/c of point 2.
620+
//
621+
// 2. Since this is a trivial value, when we remove their move only
622+
// ness, this will become a no-op meaning no-overhead.
623+
value = SGF.B.createExplicitCopyValue(PrologueLoc, value);
577624
value = SGF.B.createMarkMustCheckInst(
578625
PrologueLoc, value, MarkMustCheckInst::CheckKind::NoImplicitCopy);
579-
} else {
580-
value = SILValue(
581-
SGF.B.createBeginBorrow(PrologueLoc, value, /*isLexical*/ true));
582626
}
583627
}
584628
}
@@ -608,7 +652,8 @@ class LetValueInitialization : public Initialization {
608652
// Disable the rvalue expression cleanup, since the let value
609653
// initialization has a cleanup that lives for the entire scope of the
610654
// let declaration.
611-
bindValue(value.forward(SGF), SGF, value.isPlusOne(SGF));
655+
bool isPlusOne = value.isPlusOne(SGF);
656+
bindValue(value.forward(SGF), SGF, isPlusOne);
612657
} else {
613658
// Disable the expression cleanup of the copy, since the let value
614659
// initialization has a cleanup that lives for the entire scope of the
@@ -631,8 +676,9 @@ class LetValueInitialization : public Initialization {
631676
SGF.Cleanups.forwardCleanup(cleanup);
632677

633678
// Activate the destroy cleanup.
634-
if (DestroyCleanup != CleanupHandle::invalid())
679+
if (DestroyCleanup != CleanupHandle::invalid()) {
635680
SGF.Cleanups.setCleanupState(DestroyCleanup, CleanupState::Active);
681+
}
636682

637683
DidFinish = true;
638684
}
@@ -1795,6 +1841,9 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
17951841
return;
17961842
}
17971843

1844+
// This handles any case where we copy + begin_borrow or copyable_to_moveonly
1845+
// + begin_borrow. In either case we just need to end the lifetime of the
1846+
// begin_borrow's operand.
17981847
if (auto *bbi = dyn_cast<BeginBorrowInst>(Val.getDefiningInstruction())) {
17991848
B.createEndBorrow(silLoc, bbi);
18001849
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
@@ -1814,6 +1863,31 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
18141863
}
18151864
}
18161865
}
1866+
1867+
if (auto *copyToMove = dyn_cast<CopyableToMoveOnlyWrapperValueInst>(
1868+
mvi->getOperand())) {
1869+
if (auto *cvi = dyn_cast<CopyValueInst>(copyToMove->getOperand())) {
1870+
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
1871+
if (bbi->isLexical()) {
1872+
B.emitDestroyValueOperation(silLoc, mvi);
1873+
B.createEndBorrow(silLoc, bbi);
1874+
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
1875+
return;
1876+
}
1877+
}
1878+
}
1879+
}
1880+
1881+
if (auto *cvi = dyn_cast<ExplicitCopyValueInst>(mvi->getOperand())) {
1882+
if (auto *bbi = dyn_cast<BeginBorrowInst>(cvi->getOperand())) {
1883+
if (bbi->isLexical()) {
1884+
B.emitDestroyValueOperation(silLoc, mvi);
1885+
B.createEndBorrow(silLoc, bbi);
1886+
B.emitDestroyValueOperation(silLoc, bbi->getOperand());
1887+
return;
1888+
}
1889+
}
1890+
}
18171891
}
18181892
}
18191893
}

0 commit comments

Comments
 (0)