Skip to content

Commit 823f008

Browse files
authored
Merge pull request #67145 from gottesmm/pr-694caf05ac09f8c08fb766c7589fe060c927f528
[move-only] Ban partial reinitialization after consuming a value.
2 parents 85396f2 + 69a03c9 commit 823f008

File tree

7 files changed

+1278
-98
lines changed

7 files changed

+1278
-98
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,12 @@ ERROR(sil_movechecking_cannot_destructure_has_deinit, none,
788788
ERROR(sil_movechecking_cannot_destructure, none,
789789
"cannot partially consume '%0'",
790790
(StringRef))
791+
ERROR(sil_movechecking_cannot_partially_reinit_has_deinit, none,
792+
"cannot partially reinitialize '%0' when it has a deinitializer; only full reinitialization is allowed",
793+
(StringRef))
794+
ERROR(sil_movechecking_cannot_partially_reinit, none,
795+
"cannot partially reinitialize '%0' after it has been consumed; only full reinitialization is allowed",
796+
(StringRef))
791797
ERROR(sil_movechecking_discard_missing_consume_self, none,
792798
"must consume 'self' before exiting method that discards self", ())
793799
ERROR(sil_movechecking_reinit_after_discard, none,

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/Basic/Debug.h"
2525
#include "swift/Basic/FrozenMultiMap.h"
2626
#include "swift/Basic/STLExtras.h"
27+
#include "swift/SIL/BasicBlockDatastructures.h"
2728
#include "swift/SIL/SILFunction.h"
2829
#include "swift/SIL/SILInstruction.h"
2930
#include "swift/SIL/SILValue.h"
@@ -1037,6 +1038,7 @@ class FieldSensitivePrunedLivenessBoundary {
10371038
}
10381039

10391040
SmallBitVector &getDeadDefsBits(SILNode *def) {
1041+
assert(def->getParentBlock() && "Always expect to have a parent block!\n");
10401042
auto iter = deadDefs.insert({def, SmallBitVector()});
10411043
if (iter.second) {
10421044
iter.first->second.resize(numBits);
@@ -1359,6 +1361,16 @@ class FieldSensitiveMultiDefPrunedLiveRange
13591361
void
13601362
findBoundariesInBlock(SILBasicBlock *block, unsigned bitNo, bool isLiveOut,
13611363
FieldSensitivePrunedLivenessBoundary &boundary) const;
1364+
1365+
/// Walk from \p inst until we find a def for \p index. If we see a consuming
1366+
/// use, call \p callback. If \p callback returns true, then this is not the
1367+
/// consuming use we are looking for and we should keep on
1368+
/// searching. Otherwise, if it returns false, we bail early and return
1369+
/// false. If we find a def, we return true. If we stopped due to a consuming
1370+
/// use, we return false.
1371+
bool findEarlierConsumingUse(
1372+
SILInstruction *inst, unsigned index,
1373+
llvm::function_ref<bool(SILInstruction *)> callback) const;
13621374
};
13631375

13641376
} // namespace swift

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,107 @@ void FieldSensitiveMultiDefPrunedLiveRange::findBoundariesInBlock(
11911191
<< " Live at beginning of block! No dead args!\n");
11921192
}
11931193

1194-
assert((isLiveOut ||
1195-
prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
1196-
"findBoundariesInBlock must be called on a live block");
1194+
assert(
1195+
(isLiveOut || prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
1196+
"findBoundariesInBlock must be called on a live block");
1197+
}
1198+
1199+
bool FieldSensitiveMultiDefPrunedLiveRange::findEarlierConsumingUse(
1200+
SILInstruction *inst, unsigned index,
1201+
llvm::function_ref<bool(SILInstruction *)> callback) const {
1202+
PRUNED_LIVENESS_LOG(
1203+
llvm::dbgs()
1204+
<< "Performing single block search for consuming use for bit: " << index
1205+
<< "!\n");
1206+
1207+
// Walk our block back from inst looking for defs or a consuming use. If we
1208+
// see a def, return true. If we see a use, we keep processing if the callback
1209+
// returns true... and return false early if the callback returns false.
1210+
for (auto ii = std::next(inst->getReverseIterator()),
1211+
ie = inst->getParent()->rend();
1212+
ii != ie; ++ii) {
1213+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
1214+
// If we have a def, then we are automatically done.
1215+
if (isDef(&*ii, index)) {
1216+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
1217+
return true;
1218+
}
1219+
1220+
// If we have a consuming use, emit the error.
1221+
if (isInterestingUser(&*ii, index) ==
1222+
IsInterestingUser::LifetimeEndingUse) {
1223+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
1224+
if (!callback(&*ii)) {
1225+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1226+
<< " Callback returned false... exiting!\n");
1227+
return false;
1228+
}
1229+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1230+
<< " Callback returned true... continuing!\n");
1231+
}
1232+
1233+
// Otherwise, keep going.
1234+
}
1235+
1236+
// Then check our argument defs.
1237+
for (auto *arg : inst->getParent()->getArguments()) {
1238+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
1239+
if (isDef(arg, index)) {
1240+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
1241+
return true;
1242+
}
1243+
}
1244+
1245+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Finished single block. Didn't find "
1246+
"anything... Performing interprocedural");
1247+
1248+
// Ok, we now know that we need to look further back.
1249+
BasicBlockWorklist worklist(inst->getFunction());
1250+
for (auto *predBlock : inst->getParent()->getPredecessorBlocks()) {
1251+
worklist.pushIfNotVisited(predBlock);
1252+
}
1253+
1254+
while (auto *next = worklist.pop()) {
1255+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1256+
<< "Checking block bb" << next->getDebugID() << '\n');
1257+
for (auto ii = next->rbegin(), ie = next->rend(); ii != ie; ++ii) {
1258+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
1259+
// If we have a def, then we are automatically done.
1260+
if (isDef(&*ii, index)) {
1261+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
1262+
return true;
1263+
}
1264+
1265+
// If we have a consuming use, emit the error.
1266+
if (isInterestingUser(&*ii, index) ==
1267+
IsInterestingUser::LifetimeEndingUse) {
1268+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
1269+
if (!callback(&*ii)) {
1270+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1271+
<< " Callback returned false... exiting!\n");
1272+
return false;
1273+
}
1274+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1275+
<< " Callback returned true... continuing!\n");
1276+
}
1277+
1278+
// Otherwise, keep going.
1279+
}
1280+
1281+
for (auto *arg : next->getArguments()) {
1282+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
1283+
if (isDef(arg, index)) {
1284+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
1285+
return true;
1286+
}
1287+
}
1288+
1289+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1290+
<< "Didn't find anything... visiting predecessors!\n");
1291+
for (auto *predBlock : next->getPredecessorBlocks()) {
1292+
worklist.pushIfNotVisited(predBlock);
1293+
}
1294+
}
1295+
1296+
return true;
11971297
}

0 commit comments

Comments
 (0)