@@ -691,6 +691,9 @@ struct UseState {
691691 // / [assign] that are reinits that we will convert to inits and true reinits.
692692 llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4 > reinitInsts;
693693
694+ // / The set of drop_deinits of this mark_must_check
695+ SmallSetVector<SILInstruction *, 2 > dropDeinitInsts;
696+
694697 // / A "inout terminator use" is an implicit liveness use of the entire value
695698 // / placed on a terminator. We use this both so we add liveness for the
696699 // / terminator user and so that we can use the set to quickly identify later
@@ -712,6 +715,31 @@ struct UseState {
712715 return inoutTermUsers.count (inst);
713716 }
714717
718+ // / Returns true if the given instruction is within the same block as a reinit
719+ // / and precedes a reinit instruction in that block.
720+ bool precedesReinitInSameBlock (SILInstruction *inst) const {
721+ SILBasicBlock *block = inst->getParent ();
722+ SmallSetVector<SILInstruction *, 8 > sameBlockReinits;
723+
724+ // First, search for all reinits that are within the same block.
725+ for (auto &reinit : reinitInsts) {
726+ if (reinit.first ->getParent () != block)
727+ continue ;
728+ sameBlockReinits.insert (reinit.first );
729+ }
730+
731+ if (sameBlockReinits.empty ())
732+ return false ;
733+
734+ // Walk down from the given instruction to see if we encounter a reinit.
735+ for (auto ii = std::next (inst->getIterator ()); ii != block->end (); ++ii) {
736+ if (sameBlockReinits.contains (&*ii))
737+ return true ;
738+ }
739+
740+ return false ;
741+ }
742+
715743 void clear () {
716744 address = nullptr ;
717745 destroys.clear ();
@@ -721,6 +749,7 @@ struct UseState {
721749 takeInsts.clear ();
722750 initInsts.clear ();
723751 reinitInsts.clear ();
752+ dropDeinitInsts.clear ();
724753 inoutTermUsers.clear ();
725754 debugValue = nullptr ;
726755 }
@@ -755,6 +784,10 @@ struct UseState {
755784 for (auto pair : reinitInsts) {
756785 llvm::dbgs () << *pair.first ;
757786 }
787+ llvm::dbgs () << " DropDeinits:\n " ;
788+ for (auto *inst : dropDeinitInsts) {
789+ llvm::dbgs () << *inst;
790+ }
758791 llvm::dbgs () << " InOut Term Users:\n " ;
759792 for (auto *inst : inoutTermUsers) {
760793 llvm::dbgs () << *inst;
@@ -1737,6 +1770,12 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
17371770 LLVM_DEBUG (llvm::dbgs () << " Running copy propagation!\n " );
17381771 moveChecker.changed |= moveChecker.canonicalizer .canonicalize ();
17391772
1773+ // Export the drop_deinit's discovered by the ObjectChecker into the
1774+ // AddressChecker to preserve it for later use. We need to do this since
1775+ // the ObjectChecker's state gets cleared after running on this LoadInst.
1776+ for (auto *dropDeinit : moveChecker.canonicalizer .getDropDeinitUses ())
1777+ moveChecker.addressUseState .dropDeinitInsts .insert (dropDeinit);
1778+
17401779 // If we are asked to perform no_consume_or_assign checking or
17411780 // assignable_but_not_consumable checking, if we found any consumes of our
17421781 // load, then we need to emit an error.
@@ -2458,10 +2497,41 @@ void MoveOnlyAddressCheckerPImpl::rewriteUses(
24582497 FieldSensitiveMultiDefPrunedLiveRange &liveness,
24592498 const FieldSensitivePrunedLivenessBoundary &boundary) {
24602499 LLVM_DEBUG (llvm::dbgs () << " MoveOnlyAddressChecker Rewrite Uses!\n " );
2461- // First remove all destroy_addr that have not been claimed.
2500+
2501+ // / Whether the marked value appeared in a discard statement.
2502+ const bool isDiscardingContext = !addressUseState.dropDeinitInsts .empty ();
2503+
2504+ // Process destroys
24622505 for (auto destroyPair : addressUseState.destroys ) {
2463- if (!consumes.claimConsume (destroyPair.first , destroyPair.second )) {
2506+ // / Is this destroy instruction a final consuming use?
2507+ bool isFinalConsume =
2508+ consumes.claimConsume (destroyPair.first , destroyPair.second );
2509+
2510+ // Remove destroys that are not the final consuming use.
2511+ if (!isFinalConsume) {
24642512 destroyPair.first ->eraseFromParent ();
2513+ continue ;
2514+ }
2515+
2516+ // Otherwise, if we're in a discarding context, flag this final destroy_addr
2517+ // as a point where we're missing an explicit `consume self`. The reasoning
2518+ // here is that if a destroy of self is the final consuming use,
2519+ // then these are the points where we implicitly destroy self to clean-up
2520+ // that self var before exiting the scope. An explicit 'consume self'
2521+ // that is thrown away is a consume of this mark_must_check'd var and not a
2522+ // destroy of it, according to the use classifier.
2523+ if (isDiscardingContext) {
2524+
2525+ // Since the boundary computations treat a newly-added destroy prior to
2526+ // a reinit within that same block as a "final consuming use", exclude
2527+ // such destroys-before-reinit. We are only interested in the final
2528+ // destroy of a var, not intermediate destroys of the var.
2529+ if (addressUseState.precedesReinitInSameBlock (destroyPair.first ))
2530+ continue ;
2531+
2532+ auto *dropDeinit = addressUseState.dropDeinitInsts .front ();
2533+ diagnosticEmitter.emitMissingConsumeInDiscardingContext (destroyPair.first ,
2534+ dropDeinit);
24652535 }
24662536 }
24672537
0 commit comments