Skip to content

Commit 6694bba

Browse files
Merge pull request #74360 from nate-chandler/rdar127518559
[ConsumeAddrChecker] Diagnose consumes of borrows.
2 parents 3837bb3 + cc40e29 commit 6694bba

File tree

3 files changed

+109
-9
lines changed

3 files changed

+109
-9
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,10 +887,10 @@ namespace swift {
887887
/// For convenience, encapsulate and AccessStorage value along with its
888888
/// accessed base address.
889889
struct AccessStorageWithBase {
890-
/// Identical to AccessStorage::compute but preserves the access base.
890+
/// Identical to AccessStorage::computeInScope but walks through begin_access.
891891
static AccessStorageWithBase compute(SILValue sourceAddress);
892892

893-
/// Identical to AccessStorage::computeInScope but preserves the base.
893+
/// Identical to AccessStorage::compute but stops at begin_access
894894
static AccessStorageWithBase computeInScope(SILValue sourceAddress);
895895

896896
AccessStorage storage;

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableAddressesChecker.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,12 +2080,98 @@ bool ConsumeOperatorCopyableAddressesChecker::performClosureDataflow(
20802080
closureConsumes);
20812081
}
20822082

2083+
struct MoveConstraint {
2084+
enum Value : uint8_t {
2085+
None,
2086+
RequiresReinit,
2087+
Illegal,
2088+
} value;
2089+
2090+
operator Value() { return value; }
2091+
MoveConstraint(Value value) : value(value) {}
2092+
2093+
static MoveConstraint forGuaranteed(bool guaranteed) {
2094+
return guaranteed ? Illegal : None;
2095+
}
2096+
2097+
bool isIllegal() { return value == Illegal; }
2098+
};
2099+
2100+
static MoveConstraint getMoveConstraint(SILValue addr) {
2101+
assert(addr->getType().isAddress());
2102+
auto access = AccessPathWithBase::computeInScope(addr);
2103+
auto base = access.getAccessBase();
2104+
switch (access.accessPath.getStorage().getKind()) {
2105+
case AccessRepresentation::Kind::Box:
2106+
// Even if the box is guaranteed, it may be permitted to consume its
2107+
// storage.
2108+
return MoveConstraint::None;
2109+
case AccessRepresentation::Kind::Stack: {
2110+
// An alloc_stack is guaranteed if it's a "store_borrow destination".
2111+
auto *asi = cast<AllocStackInst>(base.getBaseAddress());
2112+
return MoveConstraint::forGuaranteed(
2113+
!asi->getUsersOfType<StoreBorrowInst>().empty());
2114+
}
2115+
case AccessRepresentation::Kind::Global:
2116+
// A global can be consumed if it's reinitialized.
2117+
return MoveConstraint::RequiresReinit;
2118+
case AccessRepresentation::Kind::Class:
2119+
// A class field can be consumed if it's reinitialized.
2120+
return MoveConstraint::RequiresReinit;
2121+
case AccessRepresentation::Kind::Tail:
2122+
// A class field can be consumed if it's reinitialized.
2123+
return MoveConstraint::RequiresReinit;
2124+
case AccessRepresentation::Kind::Argument: {
2125+
// An indirect argument is guaranteed if it's @in_guaranteed.
2126+
auto *arg = base.getArgument();
2127+
return MoveConstraint::forGuaranteed(
2128+
arg->getArgumentConvention().isGuaranteedConvention());
2129+
}
2130+
case AccessRepresentation::Kind::Yield: {
2131+
auto baseAddr = base.getBaseAddress();
2132+
auto *bai = cast<BeginApplyInst>(
2133+
cast<MultipleValueInstructionResult>(baseAddr)->getParent());
2134+
auto index = *bai->getIndexOfResult(baseAddr);
2135+
auto info = bai->getSubstCalleeConv().getYieldInfoForOperandIndex(index);
2136+
return MoveConstraint::forGuaranteed(!info.isConsumed());
2137+
}
2138+
case AccessRepresentation::Kind::Nested: {
2139+
auto *bai = cast<BeginAccessInst>(base.getBaseAddress());
2140+
if (bai->getAccessKind() == SILAccessKind::Init ||
2141+
bai->getAccessKind() == SILAccessKind::Read)
2142+
return MoveConstraint::Illegal;
2143+
// Allow moves from both modify and deinit.
2144+
return MoveConstraint::None;
2145+
}
2146+
case AccessRepresentation::Kind::Unidentified:
2147+
// Conservatively reject for now.
2148+
return MoveConstraint::Illegal;
2149+
}
2150+
}
2151+
20832152
// Returns true if we emitted a diagnostic and handled the single block
20842153
// case. Returns false if we visited all of the uses and seeded the UseState
20852154
// struct with the information needed to perform our interprocedural dataflow.
20862155
bool ConsumeOperatorCopyableAddressesChecker::performSingleBasicBlockAnalysis(
20872156
SILValue address, DebugVarCarryingInst addressDebugInst,
20882157
MarkUnresolvedMoveAddrInst *mvi) {
2158+
if (getMoveConstraint(mvi->getSrc()).isIllegal()) {
2159+
auto &astCtx = mvi->getFunction()->getASTContext();
2160+
StringRef name = getDebugVarName(address);
2161+
diagnose(astCtx, getSourceLocFromValue(address),
2162+
diag::sil_movechecking_guaranteed_value_consumed, name);
2163+
diagnose(astCtx, mvi->getLoc().getSourceLoc(),
2164+
diag::sil_movechecking_consuming_use_here);
2165+
2166+
// Replace the marker instruction with a copy_addr to avoid subsequent
2167+
// diagnostics.
2168+
SILBuilderWithScope builder(mvi);
2169+
builder.createCopyAddr(mvi->getLoc(), mvi->getSrc(), mvi->getDest(),
2170+
IsNotTake, IsInitialization);
2171+
mvi->eraseFromParent();
2172+
2173+
return true;
2174+
}
20892175
// First scan downwards to make sure we are move out of this block.
20902176
auto &useState = dataflowState.useState;
20912177
auto &applySiteToPromotedArgIndices =

test/SILOptimizer/consume_operator_kills_copyable_addressonly_lets.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -verify %s -parse-stdlib -emit-sil -o /dev/null
1+
// RUN: %target-swift-frontend -sil-verify-all -verify %s -parse-stdlib -emit-sil -o /dev/null
22

33

44
import Swift
@@ -146,8 +146,8 @@ public func conditionalBadConsumingUseLoop2<T>(_ x: T) {
146146
// Parameters
147147

148148
// This is ok, no uses after.
149-
public func simpleMoveOfParameter<T>(_ x: T) -> () {
150-
let _ = consume x
149+
public func simpleMoveOfParameter<T>(_ x: T) -> () { // expected-error {{'x' is borrowed and cannot be consumed}}
150+
let _ = consume x // expected-note {{consumed here}}
151151
}
152152

153153
public func simpleMoveOfOwnedParameter<T>(_ x: __owned T) -> () {
@@ -214,12 +214,12 @@ func consumeOwned<T>(_ k: __owned T) {
214214
_ = consume k
215215
}
216216

217-
func consumeShared<T>(_ k: __shared T) {
218-
_ = consume k
217+
func consumeShared<T>(_ k: __shared T) { // expected-error {{'k' is borrowed and cannot be consumed}}
218+
_ = consume k // expected-note {{consumed here}}
219219
}
220220

221-
func consumeBare<T>(_ k: T) {
222-
_ = consume k
221+
func consumeBare<T>(_ k: T) { // expected-error {{'k' is borrowed and cannot be consumed}}
222+
_ = consume k // expected-note {{consumed here}}
223223
}
224224

225225
////////////////////////
@@ -384,6 +384,20 @@ public func castTestIfLet2(_ x : __owned EnumWithKlass) { // expected-error {{'x
384384
}
385385
}
386386

387+
enum rdar125817827<A, B> {
388+
case a(A)
389+
case b(B)
390+
}
391+
392+
extension rdar125817827 {
393+
func foo() { // expected-error {{'self' is borrowed and cannot be consumed}}
394+
switch consume self { // expected-note {{consumed here}}
395+
case let .a(a): print(a)
396+
case let .b(b): print(b)
397+
}
398+
}
399+
}
400+
387401
/////////////////////////
388402
// Partial Apply Tests //
389403
/////////////////////////

0 commit comments

Comments
 (0)