Skip to content

Commit efe20be

Browse files
authored
Merge pull request #67146 from gottesmm/release-5.9-111498740
[5.9][move-only] Ban partial reinitialization after consuming a value.
2 parents 444f62f + aab189d commit efe20be

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"
@@ -1036,6 +1037,7 @@ class FieldSensitivePrunedLivenessBoundary {
10361037
}
10371038

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

13631375
} // namespace swift

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

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

1119-
assert((isLiveOut ||
1120-
prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
1121-
"findBoundariesInBlock must be called on a live block");
1119+
assert(
1120+
(isLiveOut || prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
1121+
"findBoundariesInBlock must be called on a live block");
1122+
}
1123+
1124+
bool FieldSensitiveMultiDefPrunedLiveRange::findEarlierConsumingUse(
1125+
SILInstruction *inst, unsigned index,
1126+
llvm::function_ref<bool(SILInstruction *)> callback) const {
1127+
PRUNED_LIVENESS_LOG(
1128+
llvm::dbgs()
1129+
<< "Performing single block search for consuming use for bit: " << index
1130+
<< "!\n");
1131+
1132+
// Walk our block back from inst looking for defs or a consuming use. If we
1133+
// see a def, return true. If we see a use, we keep processing if the callback
1134+
// returns true... and return false early if the callback returns false.
1135+
for (auto ii = std::next(inst->getReverseIterator()),
1136+
ie = inst->getParent()->rend();
1137+
ii != ie; ++ii) {
1138+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
1139+
// If we have a def, then we are automatically done.
1140+
if (isDef(&*ii, index)) {
1141+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
1142+
return true;
1143+
}
1144+
1145+
// If we have a consuming use, emit the error.
1146+
if (isInterestingUser(&*ii, index) ==
1147+
IsInterestingUser::LifetimeEndingUse) {
1148+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
1149+
if (!callback(&*ii)) {
1150+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1151+
<< " Callback returned false... exiting!\n");
1152+
return false;
1153+
}
1154+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1155+
<< " Callback returned true... continuing!\n");
1156+
}
1157+
1158+
// Otherwise, keep going.
1159+
}
1160+
1161+
// Then check our argument defs.
1162+
for (auto *arg : inst->getParent()->getArguments()) {
1163+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
1164+
if (isDef(arg, index)) {
1165+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
1166+
return true;
1167+
}
1168+
}
1169+
1170+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Finished single block. Didn't find "
1171+
"anything... Performing interprocedural");
1172+
1173+
// Ok, we now know that we need to look further back.
1174+
BasicBlockWorklist worklist(inst->getFunction());
1175+
for (auto *predBlock : inst->getParent()->getPredecessorBlocks()) {
1176+
worklist.pushIfNotVisited(predBlock);
1177+
}
1178+
1179+
while (auto *next = worklist.pop()) {
1180+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1181+
<< "Checking block bb" << next->getDebugID() << '\n');
1182+
for (auto ii = next->rbegin(), ie = next->rend(); ii != ie; ++ii) {
1183+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
1184+
// If we have a def, then we are automatically done.
1185+
if (isDef(&*ii, index)) {
1186+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
1187+
return true;
1188+
}
1189+
1190+
// If we have a consuming use, emit the error.
1191+
if (isInterestingUser(&*ii, index) ==
1192+
IsInterestingUser::LifetimeEndingUse) {
1193+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
1194+
if (!callback(&*ii)) {
1195+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1196+
<< " Callback returned false... exiting!\n");
1197+
return false;
1198+
}
1199+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1200+
<< " Callback returned true... continuing!\n");
1201+
}
1202+
1203+
// Otherwise, keep going.
1204+
}
1205+
1206+
for (auto *arg : next->getArguments()) {
1207+
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
1208+
if (isDef(arg, index)) {
1209+
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
1210+
return true;
1211+
}
1212+
}
1213+
1214+
PRUNED_LIVENESS_LOG(llvm::dbgs()
1215+
<< "Didn't find anything... visiting predecessors!\n");
1216+
for (auto *predBlock : next->getPredecessorBlocks()) {
1217+
worklist.pushIfNotVisited(predBlock);
1218+
}
1219+
}
1220+
1221+
return true;
11221222
}

0 commit comments

Comments
 (0)