Skip to content

Commit 8518c71

Browse files
committed
MoveOnlyAddressChecker: More robust checking for consume-during-borrow.
- While an opaque borrow access occurs to part of a value, the entire scope of the access needs to be treated as a liveness range, so add the `EndAccess`es to the liveness range. - The SIL verifier may crash the compiler on SILGen-generated code when the developer's source contains consume-during-borrow code patterns. Allow `load_borrow` instructions to be marked `[unchecked]`, which suppresses verifier checks until the move checker runs and gets a chance to properly diagnose these errors. Fixes rdar://124360175.
1 parent b4a169e commit 8518c71

File tree

13 files changed

+154
-29
lines changed

13 files changed

+154
-29
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4588,14 +4588,29 @@ class EndBorrowInst;
45884588
/// instruction in its use-def list.
45894589
class LoadBorrowInst :
45904590
public UnaryInstructionBase<SILInstructionKind::LoadBorrowInst,
4591-
SingleValueInstruction> {
4591+
SingleValueInstruction>
4592+
{
45924593
friend class SILBuilder;
45934594

4595+
bool Unchecked = false;
4596+
45944597
public:
45954598
LoadBorrowInst(SILDebugLocation DebugLoc, SILValue LValue)
45964599
: UnaryInstructionBase(DebugLoc, LValue,
45974600
LValue->getType().getObjectType()) {}
45984601

4602+
// True if the invariants on `load_borrow` have not been checked and
4603+
// should not be strictly enforced.
4604+
//
4605+
// This can only occur during raw SIL before move-only checking occurs.
4606+
// Developers can write incorrect code using noncopyable types that
4607+
// consumes or mutates a memory location while that location is borrowed,
4608+
// but the move-only checker must diagnose those problems before canonical
4609+
// SIL is formed.
4610+
bool isUnchecked() const { return Unchecked; }
4611+
4612+
void setUnchecked(bool value) { Unchecked = value; }
4613+
45994614
using EndBorrowRange =
46004615
decltype(std::declval<ValueBase>().getUsersOfType<EndBorrowInst>());
46014616

lib/SIL/IR/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
17381738
}
17391739

17401740
void visitLoadBorrowInst(LoadBorrowInst *LBI) {
1741+
if (LBI->isUnchecked()) {
1742+
*this << "[unchecked] ";
1743+
}
17411744
*this << getIDAndType(LBI->getOperand());
17421745
}
17431746

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3727,12 +3727,28 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
37273727

37283728
case SILInstructionKind::LoadBorrowInst: {
37293729
SourceLoc AddrLoc;
3730+
3731+
bool IsUnchecked = false;
3732+
StringRef AttrName;
3733+
SourceLoc AttrLoc;
3734+
if (parseSILOptional(AttrName, AttrLoc, *this)) {
3735+
if (AttrName == "unchecked") {
3736+
IsUnchecked = true;
3737+
} else {
3738+
P.diagnose(InstLoc.getSourceLoc(),
3739+
diag::sil_invalid_attribute_for_instruction, AttrName,
3740+
"load_borrow");
3741+
return true;
3742+
}
3743+
}
37303744

37313745
if (parseTypedValueRef(Val, AddrLoc, B) ||
37323746
parseSILDebugLocation(InstLoc, B))
37333747
return true;
37343748

3735-
ResultVal = B.createLoadBorrow(InstLoc, Val);
3749+
auto LB = B.createLoadBorrow(InstLoc, Val);
3750+
LB->setUnchecked(IsUnchecked);
3751+
ResultVal = LB;
37363752
break;
37373753
}
37383754

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,9 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
781781
requireBitsSet(bits, sbi->getDest(), &I);
782782
locations.clearBits(bits, sbi->getDest());
783783
} else if (auto *lbi = dyn_cast<LoadBorrowInst>(ebi->getOperand())) {
784-
requireBitsSet(bits, lbi->getOperand(), &I);
784+
if (!lbi->isUnchecked()) {
785+
requireBitsSet(bits, lbi->getOperand(), &I);
786+
}
785787
}
786788
break;
787789
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2626,8 +2626,13 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
26262626
requireSameType(LBI->getOperand()->getType().getObjectType(),
26272627
LBI->getType(),
26282628
"Load operand type and result type mismatch");
2629-
require(loadBorrowImmutabilityAnalysis.isImmutable(LBI),
2630-
"Found load borrow that is invalidated by a local write?!");
2629+
if (LBI->isUnchecked()) {
2630+
require(LBI->getModule().getStage() == SILStage::Raw,
2631+
"load_borrow can only be [unchecked] in raw SIL");
2632+
} else {
2633+
require(loadBorrowImmutabilityAnalysis.isImmutable(LBI),
2634+
"Found load borrow that is invalidated by a local write?!");
2635+
}
26312636
}
26322637

26332638
void checkBeginBorrowInst(BeginBorrowInst *bbi) {

lib/SILGen/SILGenLValue.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,11 @@ namespace {
12311231
return base;
12321232
}
12331233
auto result = SGF.B.createLoadBorrow(loc, base.getValue());
1234+
// Mark the load_borrow as unchecked. We can't stop the source code from
1235+
// trying to mutate or consume the same lvalue during this borrow, so
1236+
// we don't want verifiers to trip before the move checker gets a chance
1237+
// to diagnose these situations.
1238+
result->setUnchecked(true);
12341239
return SGF.emitFormalEvaluationManagedBorrowedRValueWithCleanup(loc,
12351240
base.getValue(), result);
12361241
}

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,23 +1257,29 @@ void UseState::initializeLiveness(
12571257
<< *livenessInstAndValue.first;
12581258
liveness.print(llvm::dbgs()));
12591259
}
1260+
1261+
auto updateForLivenessAccess = [&](BeginAccessInst *beginAccess,
1262+
const SmallBitVector &livenessMask) {
1263+
for (auto *endAccess : beginAccess->getEndAccesses()) {
1264+
liveness.updateForUse(endAccess, livenessMask, false /*lifetime ending*/);
1265+
}
1266+
};
12601267

12611268
for (auto livenessInstAndValue : nonconsumingUses) {
12621269
if (auto *lbi = dyn_cast<LoadBorrowInst>(livenessInstAndValue.first)) {
12631270
auto accessPathWithBase =
12641271
AccessPathWithBase::computeInScope(lbi->getOperand());
12651272
if (auto *beginAccess =
1266-
dyn_cast<BeginAccessInst>(accessPathWithBase.base)) {
1267-
for (auto *endAccess : beginAccess->getEndAccesses()) {
1268-
liveness.updateForUse(endAccess, livenessInstAndValue.second,
1269-
false /*lifetime ending*/);
1270-
}
1273+
dyn_cast_or_null<BeginAccessInst>(accessPathWithBase.base)) {
1274+
updateForLivenessAccess(beginAccess, livenessInstAndValue.second);
12711275
} else {
12721276
for (auto *ebi : lbi->getEndBorrows()) {
12731277
liveness.updateForUse(ebi, livenessInstAndValue.second,
12741278
false /*lifetime ending*/);
12751279
}
12761280
}
1281+
} else if (auto *bai = dyn_cast<BeginAccessInst>(livenessInstAndValue.first)) {
1282+
updateForLivenessAccess(bai, livenessInstAndValue.second);
12771283
} else {
12781284
liveness.updateForUse(livenessInstAndValue.first,
12791285
livenessInstAndValue.second,

lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ void MoveOnlyChecker::checkAddresses() {
203203

204204
namespace {
205205

206+
static bool canonicalizeLoadBorrows(SILFunction *F) {
207+
bool changed = false;
208+
for (auto &block : *F) {
209+
for (auto &inst : block) {
210+
if (auto *lbi = dyn_cast<LoadBorrowInst>(&inst)) {
211+
if (lbi->isUnchecked()) {
212+
changed = true;
213+
lbi->setUnchecked(false);
214+
}
215+
}
216+
}
217+
}
218+
219+
return changed;
220+
}
221+
206222
class MoveOnlyCheckerPass : public SILFunctionTransform {
207223
void run() override {
208224
auto *fn = getFunction();
@@ -217,8 +233,11 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
217233
// If an earlier pass told use to not emit diagnostics for this function,
218234
// clean up any copies, invalidate the analysis, and return early.
219235
if (fn->hasSemanticsAttr(semantics::NO_MOVEONLY_DIAGNOSTICS)) {
220-
if (cleanupNonCopyableCopiesAfterEmittingDiagnostic(getFunction()))
236+
bool didChange = canonicalizeLoadBorrows(fn);
237+
didChange |= cleanupNonCopyableCopiesAfterEmittingDiagnostic(getFunction());
238+
if (didChange) {
221239
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
240+
}
222241
return;
223242
}
224243

@@ -241,6 +260,11 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
241260
checker.diagnosticEmitter);
242261
}
243262

263+
// Remaining borrows
264+
// should be correctly immutable. We can canonicalize any remaining
265+
// `load_borrow [unchecked]` instructions.
266+
checker.madeChange |= canonicalizeLoadBorrows(fn);
267+
244268
checker.madeChange |=
245269
cleanupNonCopyableCopiesAfterEmittingDiagnostic(fn);
246270

lib/Serialization/DeserializeSIL.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2257,7 +2257,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
22572257
UNARY_INSTRUCTION(FixLifetime)
22582258
UNARY_INSTRUCTION(EndLifetime)
22592259
UNARY_INSTRUCTION(CopyBlock)
2260-
UNARY_INSTRUCTION(LoadBorrow)
22612260
UNARY_INSTRUCTION(EndInitLetRef)
22622261
REFCOUNTING_INSTRUCTION(StrongRetain)
22632262
REFCOUNTING_INSTRUCTION(StrongRelease)
@@ -2269,6 +2268,17 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
22692268
#undef UNARY_INSTRUCTION
22702269
#undef REFCOUNTING_INSTRUCTION
22712270

2271+
case SILInstructionKind::LoadBorrowInst: {
2272+
assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand.");
2273+
auto LB = Builder.createLoadBorrow(
2274+
Loc, getLocalValue(Builder.maybeGetFunction(), ValID,
2275+
getSILType(MF->getType(TyID),
2276+
(SILValueCategory)TyCategory, Fn)));
2277+
LB->setUnchecked(Attr != 0);
2278+
ResultInst = LB;
2279+
break;
2280+
}
2281+
22722282
case SILInstructionKind::BeginBorrowInst: {
22732283
assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand.");
22742284
auto isLexical = IsLexical_t(Attr & 0x1);

lib/Serialization/SerializeSIL.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
16011601
} else if (auto *I = dyn_cast<CopyableToMoveOnlyWrapperValueInst>(&SI)) {
16021602
Attr = I->getForwardingOwnershipKind() == OwnershipKind::Owned ? true
16031603
: false;
1604+
} else if (auto *LB = dyn_cast<LoadBorrowInst>(&SI)) {
1605+
Attr = LB->isUnchecked();
16041606
}
16051607
writeOneOperandLayout(SI.getKind(), Attr, SI.getOperand(0));
16061608
break;

test/SILGen/moveonly.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ public struct LoadableSubscriptGetOnlyTester : ~Copyable {
974974
// The get call
975975
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
976976
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
977-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
977+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
978978
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
979979
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
980980
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD_BORROW]])
@@ -989,7 +989,7 @@ public struct LoadableSubscriptGetOnlyTester : ~Copyable {
989989
// CHECK: [[M2_PROJECT:%.*]] = project_box [[M2_BORROW]]
990990
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
991991
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
992-
// CHECK: [[LOAD:%.*]] = load_borrow [[MARK]]
992+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[MARK]]
993993
// CHECK: apply {{%.*}}([[M2_PROJECT]], {{%.*}}, [[LOAD]])
994994
// CHECK: end_borrow [[LOAD]]
995995
// CHECK: end_access [[ACCESS]]
@@ -1030,7 +1030,7 @@ public func testSubscriptGetOnly_BaseLoadable_ResultAddressOnly_Let() {
10301030
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[ARG]]
10311031
//
10321032
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARK]]
1033-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[ACCESS]]
1033+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[ACCESS]]
10341034
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
10351035
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
10361036
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD_BORROW]])
@@ -1049,7 +1049,7 @@ public func testSubscriptGetOnly_BaseLoadable_ResultAddressOnly_InOut(m: inout L
10491049
// The get call
10501050
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL_ADDR]]
10511051
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
1052-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
1052+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
10531053
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
10541054
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
10551055
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD_BORROW]])
@@ -1078,7 +1078,7 @@ public struct LoadableSubscriptGetOnlyTesterNonCopyableStructParent : ~Copyable
10781078
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
10791079
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
10801080
// CHECK: [[GEP:%.*]] = struct_element_addr [[MARK]]
1081-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[GEP]]
1081+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[GEP]]
10821082
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
10831083
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
10841084
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD_BORROW]])
@@ -1090,7 +1090,7 @@ public struct LoadableSubscriptGetOnlyTesterNonCopyableStructParent : ~Copyable
10901090
// The second get call.
10911091
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
10921092
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
1093-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
1093+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
10941094
// CHECK: [[VALUE:%.*]] = apply {{%.*}}([[LOAD_BORROW]])
10951095
//
10961096
// CHECK: [[BORROWED_VALUE:%.*]] = begin_borrow [[VALUE]]
@@ -1137,7 +1137,7 @@ public func testSubscriptGetOnlyThroughNonCopyableParentStruct_BaseLoadable_Resu
11371137
//
11381138
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARK]]
11391139
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]]
1140-
// CHECK: [[LOAD:%.*]] = load_borrow [[GEP]]
1140+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[GEP]]
11411141
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
11421142
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
11431143
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD]])
@@ -1155,7 +1155,7 @@ public func testSubscriptGetOnlyThroughNonCopyableParentStruct_BaseLoadable_Resu
11551155
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL]]
11561156
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
11571157
// CHECK: [[GEP:%.*]] = struct_element_addr [[MARK]]
1158-
// CHECK: [[LOAD:%.*]] = load_borrow [[GEP]]
1158+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[GEP]]
11591159
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
11601160
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP]]
11611161
// CHECK: apply {{%.*}}([[TEMP_MARK]], {{%.*}}, [[LOAD]])
@@ -1192,7 +1192,7 @@ public class LoadableSubscriptGetOnlyTesterClassParent {
11921192
// CHECK: [[TEMP:%.*]] = alloc_stack $LoadableSubscriptGetOnlyTester
11931193
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[TEMP]]
11941194
// CHECK: [[TEMP_MARK_BORROW:%.*]] = store_borrow [[CORO_RESULT]] to [[TEMP_MARK]]
1195-
// CHECK: [[LOAD:%.*]] = load_borrow [[TEMP_MARK_BORROW]]
1195+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[TEMP_MARK_BORROW]]
11961196
// CHECK: [[TEMP2:%.*]] = alloc_stack $
11971197
// CHECK: [[TEMP2_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP2]]
11981198
// CHECK: apply {{%.*}}([[TEMP2_MARK]], {{%.*}}, [[LOAD]])
@@ -1214,7 +1214,7 @@ public class LoadableSubscriptGetOnlyTesterClassParent {
12141214
// CHECK: [[TEMP_MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[TEMP]]
12151215
// CHECK: [[TEMP_MARK_BORROW:%.*]] = store_borrow [[CORO_RESULT]] to [[TEMP_MARK]]
12161216
// CHECK: [[GEP:%.*]] = struct_element_addr [[TEMP_MARK_BORROW]]
1217-
// CHECK: [[LOAD:%.*]] = load_borrow [[GEP]]
1217+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[GEP]]
12181218
// CHECK: [[TEMP2:%.*]] = alloc_stack $
12191219
// CHECK: [[TEMP2_MARK:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[TEMP2]]
12201220
// CHECK: apply {{%.*}}([[TEMP2_MARK]], {{%.*}}, [[LOAD]])
@@ -1226,7 +1226,7 @@ public class LoadableSubscriptGetOnlyTesterClassParent {
12261226
// Third read.
12271227
//
12281228
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
1229-
// CHECK: [[LOAD:%.*]] = load_borrow [[ACCESS]]
1229+
// CHECK: [[LOAD:%.*]] = load_borrow [unchecked] [[ACCESS]]
12301230
// CHECK: ([[CORO_RESULT_ORIG:%.*]], [[CORO_TOKEN:%.*]]) = begin_apply {{%.*}}([[LOAD]])
12311231
// CHECK: [[CORO_RESULT_CP:%.*]] = copy_value [[CORO_RESULT_ORIG]]
12321232
// CHECK: [[CORO_RESULT_MK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[CORO_RESULT_CP]]
@@ -1455,7 +1455,7 @@ public struct LoadableSubscriptGetSetTesterNonCopyableStructParent : ~Copyable {
14551455
// The second get call.
14561456
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
14571457
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
1458-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
1458+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
14591459
// CHECK: [[VALUE:%.*]] = apply {{%.*}}([[LOAD_BORROW]])
14601460
// CHECK: [[BORROWED_VALUE:%.*]] = begin_borrow [[VALUE]]
14611461
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
@@ -1652,7 +1652,7 @@ public class LoadableSubscriptGetSetTesterClassParent {
16521652
// Third read.
16531653
//
16541654
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
1655-
// CHECK: [[CLASS:%.*]] = load_borrow [[ACCESS]]
1655+
// CHECK: [[CLASS:%.*]] = load_borrow [unchecked] [[ACCESS]]
16561656
// CHECK: ([[CORO_RESULT_ORIG:%.*]], [[CORO_TOKEN:%.*]]) = begin_apply {{%.*}}([[CLASS]])
16571657
// CHECK: [[CORO_RESULT_CP:%.*]] = copy_value [[CORO_RESULT_ORIG]]
16581658
// CHECK: [[CORO_RESULT_MK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[CORO_RESULT_CP]]
@@ -1911,7 +1911,7 @@ public struct LoadableSubscriptReadModifyTesterNonCopyableStructParent : ~Copyab
19111911
// The second get call.
19121912
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
19131913
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
1914-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
1914+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
19151915
// CHECK: [[VALUE:%.*]] = apply {{%.*}}([[LOAD_BORROW]])
19161916
// CHECK: [[BORROWED_VALUE:%.*]] = begin_borrow [[VALUE]]
19171917
// CHECK: ([[CORO_RESULT_ORIG:%.*]], [[CORO_TOKEN:%.*]]) = begin_apply {{%.*}}({{%.*}}, [[BORROWED_VALUE]])
@@ -2092,7 +2092,7 @@ public class LoadableSubscriptReadModifyTesterClassParent {
20922092
// Third read.
20932093
//
20942094
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
2095-
// CHECK: [[CLASS:%.*]] = load_borrow [[ACCESS]]
2095+
// CHECK: [[CLASS:%.*]] = load_borrow [unchecked] [[ACCESS]]
20962096
// CHECK: ([[CORO_RESULT_ORIG:%.*]], [[CORO_TOKEN:%.*]]) = begin_apply {{%.*}}([[CLASS]])
20972097
// CHECK: [[CORO_RESULT_CP:%.*]] = copy_value [[CORO_RESULT_ORIG]]
20982098
// CHECK: [[CORO_RESULT_MK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[CORO_RESULT_CP]]
@@ -2348,7 +2348,7 @@ public struct LoadableSubscriptGetModifyTesterNonCopyableStructParent : ~Copyabl
23482348
// The second get call.
23492349
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
23502350
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]]
2351-
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [[MARK]]
2351+
// CHECK: [[LOAD_BORROW:%.*]] = load_borrow [unchecked] [[MARK]]
23522352
// CHECK: [[VALUE:%.*]] = apply {{%.*}}([[LOAD_BORROW]])
23532353
// CHECK: [[BORROWED_VALUE:%.*]] = begin_borrow [[VALUE]]
23542354
// CHECK: [[TEMP:%.*]] = alloc_stack $AddressOnlyProtocol
@@ -2502,7 +2502,7 @@ public class LoadableSubscriptGetModifyTesterClassParent {
25022502
// Third read.
25032503
//
25042504
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT]]
2505-
// CHECK: [[CLASS:%.*]] = load_borrow [[ACCESS]]
2505+
// CHECK: [[CLASS:%.*]] = load_borrow [unchecked] [[ACCESS]]
25062506
// CHECK: ([[CORO_RESULT_ORIG:%.*]], [[CORO_TOKEN:%.*]]) = begin_apply {{%.*}}([[CLASS]])
25072507
// CHECK: [[CORO_RESULT_CP:%.*]] = copy_value [[CORO_RESULT_ORIG]]
25082508
// CHECK: [[CORO_RESULT_MK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[CORO_RESULT_CP]]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-frontend -emit-sil -verify %s
2+
3+
func foo(x: consuming Foo) { // expected-error{{'x' used after consume}}
4+
x.bar.foo(x) // expected-error{{overlapping accesses to 'x'}} expected-note 3 {{}}
5+
}
6+
7+
struct Foo: ~Copyable {
8+
var bar: Bar {
9+
_read { fatalError() }
10+
}
11+
}
12+
13+
struct Bar: ~Copyable {
14+
func foo(_: consuming Foo) {}
15+
}

0 commit comments

Comments
 (0)