Skip to content

Commit 04e2629

Browse files
authored
Merge pull request #73395 from jckarter/mark-captured-no-implicit-copy-parameters-6.0
[6.0] Consistently mark `borrowing` and `consuming` parameters for move-only checking when captured.
2 parents b852769 + bd9122c commit 04e2629

8 files changed

+232
-110
lines changed

lib/SILGen/SILGenLValue.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,8 +1150,12 @@ namespace {
11501150
// See if we have a noncopyable address from a project_box or global.
11511151
if (Value.getType().isAddress() && Value.getType().isMoveOnly()) {
11521152
SILValue addr = Value.getValue();
1153-
auto box = dyn_cast<ProjectBoxInst>(addr);
1154-
if (box || isa<GlobalAddrInst>(addr) || IsLazyInitializedGlobal) {
1153+
auto boxProject = addr;
1154+
if (auto m = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(boxProject)) {
1155+
boxProject = m->getOperand();
1156+
}
1157+
auto box = dyn_cast<ProjectBoxInst>(boxProject);
1158+
if (box || isa<GlobalAddrInst>(boxProject) || IsLazyInitializedGlobal) {
11551159
if (Enforcement)
11561160
addr = enterAccessScope(SGF, loc, base, addr, getTypeData(),
11571161
getAccessKind(), *Enforcement,

lib/SILGen/SILGenProlog.cpp

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,7 @@ static void emitCaptureArguments(SILGenFunction &SGF,
10541054
uint16_t ArgNo) {
10551055

10561056
auto *VD = cast<VarDecl>(capture.getDecl());
1057+
10571058
SILLocation Loc(VD);
10581059
Loc.markAsPrologue();
10591060

@@ -1082,6 +1083,30 @@ static void emitCaptureArguments(SILGenFunction &SGF,
10821083
auto &lowering = SGF.getTypeLowering(getVarTypeInCaptureContext());
10831084
SILType ty = lowering.getLoweredType();
10841085

1086+
bool isNoImplicitCopy;
1087+
1088+
if (ty.isTrivial(SGF.F) || ty.isMoveOnly()) {
1089+
isNoImplicitCopy = false;
1090+
} else if (VD->isNoImplicitCopy()) {
1091+
isNoImplicitCopy = true;
1092+
} else if (auto pd = dyn_cast<ParamDecl>(VD)) {
1093+
switch (pd->getSpecifier()) {
1094+
case ParamSpecifier::Borrowing:
1095+
case ParamSpecifier::Consuming:
1096+
isNoImplicitCopy = true;
1097+
break;
1098+
case ParamSpecifier::ImplicitlyCopyableConsuming:
1099+
case ParamSpecifier::Default:
1100+
case ParamSpecifier::InOut:
1101+
case ParamSpecifier::LegacyOwned:
1102+
case ParamSpecifier::LegacyShared:
1103+
isNoImplicitCopy = false;
1104+
break;
1105+
}
1106+
} else {
1107+
isNoImplicitCopy = false;
1108+
}
1109+
10851110
SILValue arg;
10861111
SILFunctionArgument *box = nullptr;
10871112

@@ -1116,6 +1141,10 @@ static void emitCaptureArguments(SILGenFunction &SGF,
11161141
addr->finishInitialization(SGF);
11171142
val = addr->getManagedAddress();
11181143
}
1144+
1145+
if (isNoImplicitCopy && !val.getType().isMoveOnly()) {
1146+
val = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(VD, val);
1147+
}
11191148

11201149
// If this constant is a move only type, we need to add no_consume_or_assign checking to
11211150
// ensure that we do not consume this captured value in the function. This
@@ -1149,6 +1178,9 @@ static void emitCaptureArguments(SILGenFunction &SGF,
11491178
SILType::getPrimitiveObjectType(boxTy), VD);
11501179
box->setClosureCapture(true);
11511180
arg = SGF.B.createProjectBox(VD, box, 0);
1181+
if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
1182+
arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(VD, arg);
1183+
}
11521184
break;
11531185
}
11541186
case CaptureKind::StorageAddress:
@@ -1169,6 +1201,33 @@ static void emitCaptureArguments(SILGenFunction &SGF,
11691201
auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
11701202
fArg->setClosureCapture(true);
11711203
arg = SILValue(fArg);
1204+
1205+
if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
1206+
switch (argConv) {
1207+
case SILArgumentConvention::Indirect_Inout:
1208+
case SILArgumentConvention::Indirect_InoutAliasable:
1209+
case SILArgumentConvention::Indirect_In:
1210+
case SILArgumentConvention::Indirect_In_Guaranteed:
1211+
case SILArgumentConvention::Pack_Inout:
1212+
case SILArgumentConvention::Pack_Owned:
1213+
case SILArgumentConvention::Pack_Guaranteed:
1214+
arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(VD, arg);
1215+
break;
1216+
1217+
case SILArgumentConvention::Direct_Owned:
1218+
arg = SGF.B.createOwnedCopyableToMoveOnlyWrapperValue(VD, arg);
1219+
break;
1220+
1221+
case SILArgumentConvention::Direct_Guaranteed:
1222+
arg = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(VD, arg);
1223+
break;
1224+
1225+
case SILArgumentConvention::Direct_Unowned:
1226+
case SILArgumentConvention::Indirect_Out:
1227+
case SILArgumentConvention::Pack_Out:
1228+
llvm_unreachable("should be impossible");
1229+
}
1230+
}
11721231

11731232
// If we have an inout noncopyable parameter, insert a consumable and
11741233
// assignable.
@@ -1177,7 +1236,7 @@ static void emitCaptureArguments(SILGenFunction &SGF,
11771236
// in SIL since it is illegal to capture an inout value in an escaping
11781237
// closure. The later code knows how to handle that we have the
11791238
// mark_unresolved_non_copyable_value here.
1180-
if (isInOut && ty.isMoveOnly(/*orWrapped=*/false)) {
1239+
if (isInOut && arg->getType().isMoveOnly()) {
11811240
arg = SGF.B.createMarkUnresolvedNonCopyableValueInst(
11821241
Loc, arg,
11831242
MarkUnresolvedNonCopyableValueInst::CheckKind::

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,11 @@ SourceAccess AccessEnforcementSelection::getSourceAccess(SILValue address) {
707707
if (auto *mmci = dyn_cast<MarkUnresolvedNonCopyableValueInst>(address))
708708
return getSourceAccess(mmci->getOperand());
709709

710-
// Recurse through moveonlywrapper_to_copyable_addr.
710+
// Recur through moveonlywrapper_to_copyable_addr or vice versa.
711711
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(address))
712712
return getSourceAccess(m->getOperand());
713+
if (auto *c = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(address))
714+
return getSourceAccess(c->getOperand());
713715

714716
// Recurse through drop_deinit.
715717
if (auto *ddi = dyn_cast<DropDeinitInst>(address))

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ static bool visitScopeEndsRequiringInit(
417417
llvm_unreachable("invalid check!?");
418418
}
419419

420+
// Look through wrappers.
421+
if (auto m = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(operand)) {
422+
operand = m->getOperand();
423+
}
424+
420425
// Check for inout types of arguments that are marked with consumable and
421426
// assignable.
422427
if (auto *fArg = dyn_cast<SILFunctionArgument>(operand)) {
@@ -1006,6 +1011,7 @@ void UseState::initializeLiveness(
10061011
// Then check if our markedValue is from an argument that is in,
10071012
// in_guaranteed, inout, or inout_aliasable, consider the marked address to be
10081013
// the initialization point.
1014+
bool beginsInitialized = false;
10091015
{
10101016
SILValue operand = address->getOperand();
10111017
if (auto *c = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(operand))
@@ -1024,8 +1030,7 @@ void UseState::initializeLiveness(
10241030
"an init... adding mark_unresolved_non_copyable_value as "
10251031
"init!\n");
10261032
// We cheat here slightly and use our address's operand.
1027-
recordInitUse(address, address, liveness.getTopLevelSpan());
1028-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1033+
beginsInitialized = true;
10291034
break;
10301035
case swift::SILArgumentConvention::Indirect_Out:
10311036
llvm_unreachable("Should never have out addresses here");
@@ -1041,6 +1046,22 @@ void UseState::initializeLiveness(
10411046
}
10421047
}
10431048

1049+
// A read or write access always begins on an initialized value.
1050+
if (auto access = dyn_cast<BeginAccessInst>(address->getOperand())) {
1051+
switch (access->getAccessKind()) {
1052+
case SILAccessKind::Deinit:
1053+
case SILAccessKind::Read:
1054+
case SILAccessKind::Modify:
1055+
LLVM_DEBUG(llvm::dbgs()
1056+
<< "Found move only arg closure box use... "
1057+
"adding mark_unresolved_non_copyable_value as init!\n");
1058+
beginsInitialized = true;
1059+
break;
1060+
case SILAccessKind::Init:
1061+
break;
1062+
}
1063+
}
1064+
10441065
// See if our address is from a closure guaranteed box that we did not promote
10451066
// to an address. In such a case, just treat our
10461067
// mark_unresolved_non_copyable_value as the init of our value.
@@ -1056,16 +1077,14 @@ void UseState::initializeLiveness(
10561077
LLVM_DEBUG(llvm::dbgs()
10571078
<< "Found move only arg closure box use... "
10581079
"adding mark_unresolved_non_copyable_value as init!\n");
1059-
recordInitUse(address, address, liveness.getTopLevelSpan());
1060-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1080+
beginsInitialized = true;
10611081
}
10621082
} else if (auto *box = dyn_cast<AllocBoxInst>(
10631083
lookThroughOwnershipInsts(projectBox->getOperand()))) {
10641084
LLVM_DEBUG(llvm::dbgs()
10651085
<< "Found move only var allocbox use... "
10661086
"adding mark_unresolved_non_copyable_value as init!\n");
1067-
recordInitUse(address, address, liveness.getTopLevelSpan());
1068-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1087+
beginsInitialized = true;
10691088
}
10701089
}
10711090

@@ -1076,8 +1095,7 @@ void UseState::initializeLiveness(
10761095
LLVM_DEBUG(llvm::dbgs()
10771096
<< "Found ref_element_addr use... "
10781097
"adding mark_unresolved_non_copyable_value as init!\n");
1079-
recordInitUse(address, address, liveness.getTopLevelSpan());
1080-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1098+
beginsInitialized = true;
10811099
}
10821100

10831101
// Check if our address is from a global_addr. In such a case, we treat the
@@ -1087,8 +1105,7 @@ void UseState::initializeLiveness(
10871105
LLVM_DEBUG(llvm::dbgs()
10881106
<< "Found global_addr use... "
10891107
"adding mark_unresolved_non_copyable_value as init!\n");
1090-
recordInitUse(address, address, liveness.getTopLevelSpan());
1091-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1108+
beginsInitialized = true;
10921109
}
10931110

10941111
if (auto *ptai = dyn_cast<PointerToAddressInst>(
@@ -1097,24 +1114,21 @@ void UseState::initializeLiveness(
10971114
LLVM_DEBUG(llvm::dbgs()
10981115
<< "Found pointer to address use... "
10991116
"adding mark_unresolved_non_copyable_value as init!\n");
1100-
recordInitUse(address, address, liveness.getTopLevelSpan());
1101-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1117+
beginsInitialized = true;
11021118
}
11031119

11041120
if (auto *bai = dyn_cast_or_null<BeginApplyInst>(
11051121
stripAccessMarkers(address->getOperand())->getDefiningInstruction())) {
11061122
LLVM_DEBUG(llvm::dbgs()
11071123
<< "Adding accessor coroutine begin_apply as init!\n");
1108-
recordInitUse(address, address, liveness.getTopLevelSpan());
1109-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1124+
beginsInitialized = true;
11101125
}
11111126

11121127
if (auto *eai = dyn_cast<UncheckedTakeEnumDataAddrInst>(
11131128
stripAccessMarkers(address->getOperand()))) {
11141129
LLVM_DEBUG(llvm::dbgs()
11151130
<< "Adding enum projection as init!\n");
1116-
recordInitUse(address, address, liveness.getTopLevelSpan());
1117-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1131+
beginsInitialized = true;
11181132
}
11191133

11201134
// Assume a strict check of a temporary or formal access is initialized
@@ -1124,32 +1138,33 @@ void UseState::initializeLiveness(
11241138
asi && address->isStrict()) {
11251139
LLVM_DEBUG(llvm::dbgs()
11261140
<< "Adding strict-marked alloc_stack as init!\n");
1127-
recordInitUse(address, address, liveness.getTopLevelSpan());
1128-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1141+
beginsInitialized = true;
11291142
}
11301143

11311144
// Assume a strict-checked value initialized before the check.
11321145
if (address->isStrict()) {
11331146
LLVM_DEBUG(llvm::dbgs()
11341147
<< "Adding strict marker as init!\n");
1135-
recordInitUse(address, address, liveness.getTopLevelSpan());
1136-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1148+
beginsInitialized = true;
11371149
}
11381150

11391151
// Assume a value whose deinit has been dropped has been initialized.
11401152
if (auto *ddi = dyn_cast<DropDeinitInst>(address->getOperand())) {
11411153
LLVM_DEBUG(llvm::dbgs()
11421154
<< "Adding copyable_to_move_only_wrapper as init!\n");
1143-
recordInitUse(address, address, liveness.getTopLevelSpan());
1144-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1155+
beginsInitialized = true;
11451156
}
11461157

11471158
// Assume a value wrapped in a MoveOnlyWrapper is initialized.
11481159
if (auto *m2c = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(address->getOperand())) {
11491160
LLVM_DEBUG(llvm::dbgs()
11501161
<< "Adding copyable_to_move_only_wrapper as init!\n");
1162+
beginsInitialized = true;
1163+
}
1164+
1165+
if (beginsInitialized) {
11511166
recordInitUse(address, address, liveness.getTopLevelSpan());
1152-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1167+
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
11531168
}
11541169

11551170
// Now that we have finished initialization of defs, change our multi-maps
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-swift-frontend -emit-sil -verify %s
2+
3+
class Class {}
4+
//struct Class : ~Copyable {}
5+
6+
func consume(_: consuming Class) {}
7+
func nonescapingClosure(_ body: () -> ()) {
8+
body()
9+
}
10+
11+
func testNonescapingCaptureConsuming(x: consuming Class) { // expected-error{{}}
12+
nonescapingClosure { consume(x) } // expected-note{{consumed here}}
13+
}
14+
15+
// TODO: `defer` should be allowed to consume local bindings
16+
func testDeferCaptureConsuming(x: consuming Class) { // expected-error{{}}
17+
defer { consume(x) } // expected-note{{consumed here}}
18+
do {}
19+
}
20+
21+
func testLocalFunctionCaptureConsuming(x: consuming Class) {
22+
func local() {
23+
consume(x) // expected-error{{cannot be consumed when captured by an escaping closure}}
24+
}
25+
}
26+
27+
func testNonescapingCaptureBorrowing(x: borrowing Class) { // expected-error{{}}
28+
nonescapingClosure { consume(x) } // expected-note{{consumed here}}
29+
}
30+
31+
func testDeferCaptureBorrowing(x: borrowing Class) { // expected-error{{}}
32+
defer { consume(x) } // expected-note{{consumed here}}
33+
do {}
34+
}
35+
36+
func testLocalFunctionCaptureBorrowing(x: borrowing Class) { // expected-error{{borrowed and cannot be consumed}}
37+
func local() {
38+
consume(x) // expected-note{{consumed here}}
39+
}
40+
}

0 commit comments

Comments
 (0)