Skip to content

Commit 73ed03c

Browse files
authored
Merge pull request #73380 from jckarter/mark-captured-no-implicit-copy-parameters
Consistently mark `borrowing` and `consuming` parameters for move-only checking when captured.
2 parents 1e4f264 + 90e1ecb commit 73ed03c

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)) {
@@ -1007,6 +1012,7 @@ void UseState::initializeLiveness(
10071012
// Then check if our markedValue is from an argument that is in,
10081013
// in_guaranteed, inout, or inout_aliasable, consider the marked address to be
10091014
// the initialization point.
1015+
bool beginsInitialized = false;
10101016
{
10111017
SILValue operand = address->getOperand();
10121018
if (auto *c = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(operand))
@@ -1025,8 +1031,7 @@ void UseState::initializeLiveness(
10251031
"an init... adding mark_unresolved_non_copyable_value as "
10261032
"init!\n");
10271033
// We cheat here slightly and use our address's operand.
1028-
recordInitUse(address, address, liveness.getTopLevelSpan());
1029-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1034+
beginsInitialized = true;
10301035
break;
10311036
case swift::SILArgumentConvention::Indirect_Out:
10321037
llvm_unreachable("Should never have out addresses here");
@@ -1042,6 +1047,22 @@ void UseState::initializeLiveness(
10421047
}
10431048
}
10441049

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

@@ -1077,8 +1096,7 @@ void UseState::initializeLiveness(
10771096
LLVM_DEBUG(llvm::dbgs()
10781097
<< "Found ref_element_addr use... "
10791098
"adding mark_unresolved_non_copyable_value as init!\n");
1080-
recordInitUse(address, address, liveness.getTopLevelSpan());
1081-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1099+
beginsInitialized = true;
10821100
}
10831101

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

10951112
if (auto *ptai = dyn_cast<PointerToAddressInst>(
@@ -1098,24 +1115,21 @@ void UseState::initializeLiveness(
10981115
LLVM_DEBUG(llvm::dbgs()
10991116
<< "Found pointer to address use... "
11001117
"adding mark_unresolved_non_copyable_value as init!\n");
1101-
recordInitUse(address, address, liveness.getTopLevelSpan());
1102-
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1118+
beginsInitialized = true;
11031119
}
11041120

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

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

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

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

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

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

11561171
// 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)