2828#include " MoveOnlyDiagnostics.h"
2929#include " MoveOnlyObjectChecker.h"
3030
31+ #include " swift/Basic/BlotSetVector.h"
3132#include " swift/Basic/Defer.h"
33+ #include " swift/Basic/FrozenMultiMap.h"
34+ #include " swift/SIL/FieldSensitivePrunedLiveness.h"
3235#include " swift/SIL/SILBuilder.h"
3336#include " swift/SIL/SILInstruction.h"
3437#include " swift/SILOptimizer/Analysis/Analysis.h"
3538#include " swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
36- #include " swift/Basic/BlotSetVector.h"
37- #include " swift/Basic/FrozenMultiMap.h"
3839#include " swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
3940#include " swift/SILOptimizer/PassManager/Passes.h"
4041#include " swift/SILOptimizer/PassManager/Transforms.h"
4142#include " swift/SILOptimizer/Utils/CFGOptUtils.h"
4243#include " llvm/ADT/ArrayRef.h"
44+ #include " llvm/ADT/SmallBitVector.h"
4345
4446using namespace swift ;
4547using namespace swift ::siloptimizer;
@@ -143,6 +145,7 @@ bool BorrowToDestructureTransform::gatherUses(
143145 {nextUse, {*leafRange, false /* is lifetime ending*/ }});
144146 liveness.updateForUse (nextUse->getUser (), *leafRange,
145147 false /* is lifetime ending*/ );
148+ instToInterestingOperandIndexMap.insert (nextUse->getUser (), nextUse);
146149 continue ;
147150 }
148151
@@ -166,6 +169,7 @@ bool BorrowToDestructureTransform::gatherUses(
166169 {nextUse, {*leafRange, true /* is lifetime ending*/ }});
167170 liveness.updateForUse (nextUse->getUser (), *leafRange,
168171 true /* is lifetime ending*/ );
172+ instToInterestingOperandIndexMap.insert (nextUse->getUser (), nextUse);
169173 continue ;
170174 }
171175
@@ -198,39 +202,163 @@ bool BorrowToDestructureTransform::gatherUses(
198202 return true ;
199203}
200204
205+ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction () {
206+ // At this point, we have emitted all boundary checks. We also now need to
207+ // check if any of our consuming uses that are on the boundary are used by the
208+ // same instruction as a different consuming or non-consuming use.
209+ instToInterestingOperandIndexMap.setFrozen ();
210+ SmallBitVector usedBits (liveness.getNumSubElements ());
211+
212+ for (auto instRangePair : instToInterestingOperandIndexMap.getRange ()) {
213+ SWIFT_DEFER { usedBits.reset (); };
214+
215+ // First loop through our uses and handle any consuming twice errors. We
216+ // also setup usedBits to check for non-consuming uses that may overlap.
217+ Operand *badOperand = nullptr ;
218+ Optional<TypeTreeLeafTypeRange> badRange;
219+ for (auto *use : instRangePair.second ) {
220+ if (!use->isConsuming ())
221+ continue ;
222+
223+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
224+ for (unsigned index : destructureUseSpan.getRange ()) {
225+ if (usedBits[index]) {
226+ // If we get that we used the same bit twice, we have an error. We set
227+ // the badIndex error and break early.
228+ badOperand = use;
229+ badRange = destructureUseSpan;
230+ break ;
231+ }
232+
233+ usedBits[index] = true ;
234+ }
235+
236+ // If we set badOperand, break so we can emit an error for this
237+ // instruction.
238+ if (badOperand)
239+ break ;
240+ }
241+
242+ // If we did not set badIndex for consuming uses, we did not have any
243+ // conflicts among consuming uses. see if we have any conflicts with
244+ // non-consuming uses. Otherwise, we continue.
245+ if (!badOperand) {
246+ for (auto *use : instRangePair.second ) {
247+ if (use->isConsuming ())
248+ continue ;
249+
250+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
251+ for (unsigned index : destructureUseSpan.getRange ()) {
252+ if (!usedBits[index])
253+ continue ;
254+
255+ // If we get that we used the same bit twice, we have an error. We set
256+ // the badIndex error and break early.
257+ badOperand = use;
258+ badRange = destructureUseSpan;
259+ break ;
260+ }
261+
262+ // If we set badOperand, break so we can emit an error for this
263+ // instruction.
264+ if (badOperand)
265+ break ;
266+ }
267+
268+ // If we even did not find a non-consuming use that conflicts, then
269+ // continue.
270+ if (!badOperand)
271+ continue ;
272+ }
273+
274+ // If badIndex is set, we broke out of the inner loop and need to emit an
275+ // error. Use a little more compile time to identify the other operand that
276+ // caused the failure. NOTE: badOperand /could/ be a non-consuming use, but
277+ // the use we are identifying here will always be consuming.
278+ usedBits.reset ();
279+
280+ // Reinitialize use bits with the bad bits.
281+ for (unsigned index : badRange->getRange ())
282+ usedBits[index] = true ;
283+
284+ // Now loop back through looking for the original operand that set the used
285+ // bits. This will always be a consuming use.
286+ for (auto *use : instRangePair.second ) {
287+ if (!use->isConsuming ())
288+ continue ;
289+
290+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
291+ bool emittedError = false ;
292+ for (unsigned index : destructureUseSpan.getRange ()) {
293+ if (!usedBits[index])
294+ continue ;
295+
296+ if (badOperand->isConsuming ())
297+ diagnosticEmitter.emitObjectConsumesDestructuredValueTwice (
298+ mmci, use, badOperand);
299+ else
300+ diagnosticEmitter.emitObjectConsumesAndUsesDestructuredValue (
301+ mmci, use, badOperand);
302+ emittedError = true ;
303+ }
304+
305+ // Once we have emitted the error, just break out of the loop.
306+ if (emittedError)
307+ break ;
308+ }
309+ }
310+ }
311+
201312void BorrowToDestructureTransform::checkDestructureUsesOnBoundary () const {
202313 LLVM_DEBUG (llvm::dbgs () << " Checking destructure uses on boundary!\n " );
314+
203315 // Now that we have found all of our destructure needing uses and liveness
204316 // needing uses, make sure that none of our destructure needing uses are
205- // within our boundary. If so, we have an automatic error.
317+ // within our boundary. If so, we have an automatic error since we have a
318+ // use-after-free.
206319 for (auto *use : destructureNeedingUses) {
207320 LLVM_DEBUG (llvm::dbgs ()
208321 << " DestructureNeedingUse: " << *use->getUser ());
322+
209323 auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
210- if (liveness.isWithinBoundary (use->getUser (), destructureUseSpan)) {
211- LLVM_DEBUG (llvm::dbgs () << " Within boundary! Emitting error!\n " );
212- // Emit an error. We have a use after free.
213- //
214- // NOTE: Since we are going to emit an error here, we do the boundary
215- // computation to ensure that we only do the boundary computation once:
216- // when we emit an error or once we know we need to do rewriting.
217- //
218- // TODO: Fix diagnostic to use destructure needing use and boundary
219- // uses.
220- FieldSensitivePrunedLivenessBoundary boundary (
221- liveness.getNumSubElements ());
222- liveness.computeBoundary (boundary);
223- diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary (
224- mmci, use->getUser (), destructureUseSpan, boundary);
225- return ;
226- } else {
227- LLVM_DEBUG (llvm::dbgs () << " On boundary! No error!\n " );
324+ if (!liveness.isWithinBoundary (use->getUser (), destructureUseSpan)) {
325+ LLVM_DEBUG (llvm::dbgs ()
326+ << " On boundary or within boundary! No error!\n " );
327+ continue ;
228328 }
329+
330+ // Emit an error. We have a use after free.
331+ //
332+ // NOTE: Since we are going to emit an error here, we do the boundary
333+ // computation to ensure that we only do the boundary computation once:
334+ // when we emit an error or once we know we need to do rewriting.
335+ //
336+ // TODO: Fix diagnostic to use destructure needing use and boundary
337+ // uses.
338+ LLVM_DEBUG (llvm::dbgs () << " Within boundary! Emitting error!\n " );
339+ FieldSensitivePrunedLivenessBoundary boundary (liveness.getNumSubElements ());
340+ liveness.computeBoundary (boundary);
341+ diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary (
342+ mmci, use->getUser (), destructureUseSpan, boundary);
229343 }
230344}
231345
232346bool BorrowToDestructureTransform::gatherBorrows (
233347 MarkMustCheckInst *mmci, StackList<BeginBorrowInst *> &borrowWorklist) {
348+ // If we have a no implicit copy mark_must_check, we do not run the borrow to
349+ // destructure transform since:
350+ //
351+ // 1. If we have a move only type, we should have emitted an earlier error
352+ // saying that move only types should not be marked as no implicit copy.
353+ //
354+ // 2. If we do not have a move only type, then we know that all fields that we
355+ // access directly and would cause a need to destructure must be copyable,
356+ // so no transformation/error is needed.
357+ if (mmci->getType ().isMoveOnlyWrapped ()) {
358+ LLVM_DEBUG (llvm::dbgs () << " Skipping move only wrapped inst: " << *mmci);
359+ return true ;
360+ }
361+
234362 LLVM_DEBUG (llvm::dbgs () << " Searching for borrows for inst: " << *mmci);
235363
236364 StackList<Operand *> worklist (mmci->getFunction ());
0 commit comments