Skip to content

Commit 57e1e39

Browse files
authored
Merge pull request #71726 from jckarter/borrowing-switch-9
MoveOnlyAddressChecker: Relax more checks to accommodate borrowing switch codegen.
2 parents f809406 + 01405ba commit 57e1e39

File tree

4 files changed

+179
-34
lines changed

4 files changed

+179
-34
lines changed

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
114114
unsigned finalSubElementOffset = 0;
115115
SILModule &mod = *rootAddress->getModule();
116116

117+
LLVM_DEBUG(llvm::dbgs() << "computing element offset for root:\n";
118+
rootAddress->print(llvm::dbgs()));
119+
117120
while (1) {
121+
LLVM_DEBUG(llvm::dbgs() << "projection: ";
122+
projectionDerivedFromRoot->print(llvm::dbgs()));
123+
118124
// If we got to the root, we're done.
119125
if (rootAddress == projectionDerivedFromRoot)
120126
return {SubElementOffset(finalSubElementOffset)};
@@ -195,6 +201,16 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
195201
projectionDerivedFromRoot = initData->getOperand();
196202
continue;
197203
}
204+
205+
// Look through wrappers.
206+
if (auto c2m = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(projectionDerivedFromRoot)) {
207+
projectionDerivedFromRoot = c2m->getOperand();
208+
continue;
209+
}
210+
if (auto m2c = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(projectionDerivedFromRoot)) {
211+
projectionDerivedFromRoot = m2c->getOperand();
212+
continue;
213+
}
198214

199215
// If we do not know how to handle this case, just return None.
200216
//

lib/SILGen/SILGenPattern.cpp

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,28 @@ void PatternMatchEmission::bindVariable(Pattern *pattern, VarDecl *var,
13981398
}
13991399
}
14001400

1401+
namespace {
1402+
class EndAccessCleanup final : public Cleanup {
1403+
SILValue beginAccess;
1404+
public:
1405+
EndAccessCleanup(SILValue beginAccess)
1406+
: beginAccess(beginAccess)
1407+
{}
1408+
1409+
void emit(SILGenFunction &SGF, CleanupLocation loc, ForUnwind_t forUnwind)
1410+
override {
1411+
SGF.B.createEndAccess(loc, beginAccess, /*aborted*/ false);
1412+
}
1413+
1414+
void dump(SILGenFunction &SGF) const override {
1415+
llvm::errs() << "EndAccessCleanup\n";
1416+
if (beginAccess) {
1417+
beginAccess->print(llvm::errs());
1418+
}
1419+
}
1420+
};
1421+
}
1422+
14011423
/// Bind a borrow binding into the current scope.
14021424
void PatternMatchEmission::bindBorrow(Pattern *pattern, VarDecl *var,
14031425
ConsumableManagedValue value) {
@@ -1419,6 +1441,14 @@ void PatternMatchEmission::bindBorrow(Pattern *pattern, VarDecl *var,
14191441
if (bindValue.getType().isObject()) {
14201442
// Create a notional copy for the borrow checker to use.
14211443
bindValue = bindValue.copy(SGF, pattern);
1444+
} else {
1445+
// Treat use of an address value in-place as a separate nested read access.
1446+
auto access = SGF.B.createBeginAccess(pattern, bindValue.getValue(),
1447+
SILAccessKind::Read,
1448+
SILAccessEnforcement::Static,
1449+
false, false);
1450+
SGF.Cleanups.pushCleanup<EndAccessCleanup>(access);
1451+
bindValue = ManagedValue::forBorrowedAddressRValue(access);
14221452
}
14231453
// We mark the borrow check as "strict" because we don't want to allow
14241454
// consumes through the binding, even if the original value manages to be
@@ -1789,16 +1819,13 @@ emitTupleDispatch(ArrayRef<RowToSpecialize> rows, ConsumableManagedValue src,
17891819
return getManagedSubobject(SGF, member, fieldTL,
17901820
src.getFinalConsumption());
17911821
}
1792-
case CastConsumptionKind::CopyOnSuccess: {
1822+
case CastConsumptionKind::CopyOnSuccess:
1823+
case CastConsumptionKind::BorrowAlways: {
17931824
// We translate copy_on_success => borrow_always.
17941825
auto memberMV = ManagedValue::forBorrowedAddressRValue(member);
17951826
return {SGF.B.createLoadBorrow(loc, memberMV),
17961827
CastConsumptionKind::BorrowAlways};
17971828
}
1798-
case CastConsumptionKind::BorrowAlways: {
1799-
llvm_unreachable(
1800-
"Borrow always can only occur along object only code paths");
1801-
}
18021829
}
18031830
llvm_unreachable("covered switch");
18041831
}());
@@ -3202,26 +3229,6 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
32023229
SGF.Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
32033230
}
32043231

3205-
class EndAccessCleanup final : public Cleanup {
3206-
SILValue beginAccess;
3207-
public:
3208-
EndAccessCleanup(SILValue beginAccess)
3209-
: beginAccess(beginAccess)
3210-
{}
3211-
3212-
void emit(SILGenFunction &SGF, CleanupLocation loc, ForUnwind_t forUnwind)
3213-
override {
3214-
SGF.B.createEndAccess(loc, beginAccess, /*aborted*/ false);
3215-
}
3216-
3217-
void dump(SILGenFunction &SGF) const override {
3218-
llvm::errs() << "EndAccessCleanup\n";
3219-
if (beginAccess) {
3220-
beginAccess->print(llvm::errs());
3221-
}
3222-
}
3223-
};
3224-
32253232
void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
32263233
LLVM_DEBUG(llvm::dbgs() << "emitting switch stmt\n";
32273234
S->dump(llvm::dbgs());

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,14 +1128,21 @@ void UseState::initializeLiveness(
11281128
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
11291129
}
11301130

1131-
// Assume a strict check of a temporary is initialized before the check.
1132-
if (auto *asi = dyn_cast<BeginAccessInst>(address->getOperand());
1133-
asi && address->isStrict()) {
1131+
// Assume a strict-checked value initialized before the check.
1132+
if (address->isStrict()) {
11341133
LLVM_DEBUG(llvm::dbgs()
1135-
<< "Adding strict-marked begin_access as init!\n");
1134+
<< "Adding strict marker as init!\n");
11361135
recordInitUse(address, address, liveness.getTopLevelSpan());
11371136
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
11381137
}
1138+
1139+
// Assume a value wrapped in a MoveOnlyWrapper is initialized.
1140+
if (auto *m2c = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(address->getOperand())) {
1141+
LLVM_DEBUG(llvm::dbgs()
1142+
<< "Adding copyable_to_move_only_wrapper as init!\n");
1143+
recordInitUse(address, address, liveness.getTopLevelSpan());
1144+
liveness.initializeDef(SILValue(address), liveness.getTopLevelSpan());
1145+
}
11391146

11401147
// Now that we have finished initialization of defs, change our multi-maps
11411148
// from their array form to their map form.
@@ -2570,9 +2577,14 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
25702577

25712578
#ifndef NDEBUG
25722579
if (user->mayWriteToMemory()) {
2573-
llvm::errs() << "Found a write classified as a liveness use?!\n";
2574-
llvm::errs() << "Use: " << *user;
2575-
llvm_unreachable("standard failure");
2580+
// TODO: `unchecked_take_enum_addr` should inherently be understood as
2581+
// non-side-effecting when it's nondestructive.
2582+
auto ue = dyn_cast<UncheckedTakeEnumDataAddrInst>(user);
2583+
if (!ue || ue->isDestructive()) {
2584+
llvm::errs() << "Found a write classified as a liveness use?!\n";
2585+
llvm::errs() << "Use: " << *user;
2586+
llvm_unreachable("standard failure");
2587+
}
25762588
}
25772589
#endif
25782590
useState.recordLivenessUse(user, *leafRange);

test/SILOptimizer/moveonly_borrowing_switch.swift

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,119 @@ func testOuterAO(consuming bas: consuming AOBas) { // expected-error{{'bas' used
236236
break
237237
}
238238

239-
switch bas { // expected-note{{used here}}
240-
case _borrowing x: // expected-warning{{}}
239+
switch bas {
240+
case _borrowing x: // expected-warning{{}} expected-note{{used here}}
241241
break
242242
}
243243
}
244244

245+
enum E<T>: ~Copyable {
246+
case a(T)
247+
}
248+
249+
extension E {
250+
func f() {
251+
switch self {
252+
case .a:
253+
print("a")
254+
}
255+
}
256+
257+
func g() {
258+
switch self {
259+
case .a(_borrowing t): // expected-warning{{}}
260+
print("a")
261+
}
262+
}
263+
}
264+
265+
struct Box: ~Copyable {
266+
let ptr: UnsafeMutablePointer<Int>
267+
}
268+
269+
struct ChonkyBox: ~Copyable {
270+
let container: Any
271+
}
272+
273+
enum List<Element>: ~Copyable {
274+
case end
275+
case more(Element, Box)
276+
}
277+
278+
enum ChonkyList<Element>: ~Copyable {
279+
case end
280+
case more(Element, ChonkyBox)
281+
}
282+
283+
extension List {
284+
var isEmpty: Bool {
285+
switch self {
286+
case .end: true
287+
case .more: false
288+
}
289+
}
290+
291+
var peek: Box {
292+
_read {
293+
switch self {
294+
case .end:
295+
fatalError()
296+
case .more(_, _borrowing box):
297+
yield box
298+
}
299+
}
300+
}
301+
302+
/*
303+
TODO: type mismatch because of `@moveOnly` wrapper. yield needs to peel it
304+
off
305+
306+
var head: Element {
307+
_read {
308+
switch self {
309+
case .end:
310+
fatalError()
311+
case .more(_borrowing head, _):
312+
yield head
313+
}
314+
}
315+
}
316+
*/
317+
}
318+
319+
extension ChonkyList {
320+
var isEmpty: Bool {
321+
switch self {
322+
case .end: true
323+
case .more: false
324+
}
325+
}
326+
327+
var peek: ChonkyBox {
328+
_read {
329+
switch self {
330+
case .end:
331+
fatalError()
332+
case .more(_, _borrowing box):
333+
yield box
334+
}
335+
}
336+
}
337+
338+
/*
339+
TODO: type mismatch because of `@moveOnly` wrapper. yield needs to peel it
340+
off
341+
342+
var head: Element {
343+
_read {
344+
switch self {
345+
case .end:
346+
fatalError()
347+
case .more(_borrowing head, _):
348+
yield head
349+
}
350+
}
351+
}
352+
*/
353+
}
354+

0 commit comments

Comments
 (0)