Skip to content

[5.9][move-only] Ban partial reinitialization after consuming a value. #67146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,12 @@ ERROR(sil_movechecking_cannot_destructure_has_deinit, none,
ERROR(sil_movechecking_cannot_destructure, none,
"cannot partially consume '%0'",
(StringRef))
ERROR(sil_movechecking_cannot_partially_reinit_has_deinit, none,
"cannot partially reinitialize '%0' when it has a deinitializer; only full reinitialization is allowed",
(StringRef))
ERROR(sil_movechecking_cannot_partially_reinit, none,
"cannot partially reinitialize '%0' after it has been consumed; only full reinitialization is allowed",
(StringRef))
ERROR(sil_movechecking_discard_missing_consume_self, none,
"must consume 'self' before exiting method that discards self", ())
ERROR(sil_movechecking_reinit_after_discard, none,
Expand Down
12 changes: 12 additions & 0 deletions include/swift/SIL/FieldSensitivePrunedLiveness.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "swift/Basic/Debug.h"
#include "swift/Basic/FrozenMultiMap.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
Expand Down Expand Up @@ -1036,6 +1037,7 @@ class FieldSensitivePrunedLivenessBoundary {
}

SmallBitVector &getDeadDefsBits(SILNode *def) {
assert(def->getParentBlock() && "Always expect to have a parent block!\n");
auto iter = deadDefs.insert({def, SmallBitVector()});
if (iter.second) {
iter.first->second.resize(numBits);
Expand Down Expand Up @@ -1358,6 +1360,16 @@ class FieldSensitiveMultiDefPrunedLiveRange
void
findBoundariesInBlock(SILBasicBlock *block, unsigned bitNo, bool isLiveOut,
FieldSensitivePrunedLivenessBoundary &boundary) const;

/// Walk from \p inst until we find a def for \p index. If we see a consuming
/// use, call \p callback. If \p callback returns true, then this is not the
/// consuming use we are looking for and we should keep on
/// searching. Otherwise, if it returns false, we bail early and return
/// false. If we find a def, we return true. If we stopped due to a consuming
/// use, we return false.
bool findEarlierConsumingUse(
SILInstruction *inst, unsigned index,
llvm::function_ref<bool(SILInstruction *)> callback) const;
};

} // namespace swift
Expand Down
106 changes: 103 additions & 3 deletions lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,107 @@ void FieldSensitiveMultiDefPrunedLiveRange::findBoundariesInBlock(
<< " Live at beginning of block! No dead args!\n");
}

assert((isLiveOut ||
prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
"findBoundariesInBlock must be called on a live block");
assert(
(isLiveOut || prevCount < boundary.getNumLastUsersAndDeadDefs(bitNo)) &&
"findBoundariesInBlock must be called on a live block");
}

bool FieldSensitiveMultiDefPrunedLiveRange::findEarlierConsumingUse(
SILInstruction *inst, unsigned index,
llvm::function_ref<bool(SILInstruction *)> callback) const {
PRUNED_LIVENESS_LOG(
llvm::dbgs()
<< "Performing single block search for consuming use for bit: " << index
<< "!\n");

// Walk our block back from inst looking for defs or a consuming use. If we
// see a def, return true. If we see a use, we keep processing if the callback
// returns true... and return false early if the callback returns false.
for (auto ii = std::next(inst->getReverseIterator()),
ie = inst->getParent()->rend();
ii != ie; ++ii) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
// If we have a def, then we are automatically done.
if (isDef(&*ii, index)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
return true;
}

// If we have a consuming use, emit the error.
if (isInterestingUser(&*ii, index) ==
IsInterestingUser::LifetimeEndingUse) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
if (!callback(&*ii)) {
PRUNED_LIVENESS_LOG(llvm::dbgs()
<< " Callback returned false... exiting!\n");
return false;
}
PRUNED_LIVENESS_LOG(llvm::dbgs()
<< " Callback returned true... continuing!\n");
}

// Otherwise, keep going.
}

// Then check our argument defs.
for (auto *arg : inst->getParent()->getArguments()) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
if (isDef(arg, index)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
return true;
}
}

PRUNED_LIVENESS_LOG(llvm::dbgs() << "Finished single block. Didn't find "
"anything... Performing interprocedural");

// Ok, we now know that we need to look further back.
BasicBlockWorklist worklist(inst->getFunction());
for (auto *predBlock : inst->getParent()->getPredecessorBlocks()) {
worklist.pushIfNotVisited(predBlock);
}

while (auto *next = worklist.pop()) {
PRUNED_LIVENESS_LOG(llvm::dbgs()
<< "Checking block bb" << next->getDebugID() << '\n');
for (auto ii = next->rbegin(), ie = next->rend(); ii != ie; ++ii) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << *ii);
// If we have a def, then we are automatically done.
if (isDef(&*ii, index)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Def! Returning true!\n");
return true;
}

// If we have a consuming use, emit the error.
if (isInterestingUser(&*ii, index) ==
IsInterestingUser::LifetimeEndingUse) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is Lifetime Ending Use!\n");
if (!callback(&*ii)) {
PRUNED_LIVENESS_LOG(llvm::dbgs()
<< " Callback returned false... exiting!\n");
return false;
}
PRUNED_LIVENESS_LOG(llvm::dbgs()
<< " Callback returned true... continuing!\n");
}

// Otherwise, keep going.
}

for (auto *arg : next->getArguments()) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting arg: " << *arg);
if (isDef(arg, index)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def. Returning true!\n");
return true;
}
}

PRUNED_LIVENESS_LOG(llvm::dbgs()
<< "Didn't find anything... visiting predecessors!\n");
for (auto *predBlock : next->getPredecessorBlocks()) {
worklist.pushIfNotVisited(predBlock);
}
}

return true;
}
Loading