@@ -691,6 +691,9 @@ struct UseState {
691
691
// / [assign] that are reinits that we will convert to inits and true reinits.
692
692
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4 > reinitInsts;
693
693
694
+ // / The set of drop_deinits of this mark_must_check
695
+ SmallSetVector<SILInstruction *, 2 > dropDeinitInsts;
696
+
694
697
// / A "inout terminator use" is an implicit liveness use of the entire value
695
698
// / placed on a terminator. We use this both so we add liveness for the
696
699
// / terminator user and so that we can use the set to quickly identify later
@@ -721,6 +724,7 @@ struct UseState {
721
724
takeInsts.clear ();
722
725
initInsts.clear ();
723
726
reinitInsts.clear ();
727
+ dropDeinitInsts.clear ();
724
728
inoutTermUsers.clear ();
725
729
debugValue = nullptr ;
726
730
}
@@ -755,6 +759,10 @@ struct UseState {
755
759
for (auto pair : reinitInsts) {
756
760
llvm::dbgs () << *pair.first ;
757
761
}
762
+ llvm::dbgs () << " DropDeinits:\n " ;
763
+ for (auto *inst : dropDeinitInsts) {
764
+ llvm::dbgs () << *inst;
765
+ }
758
766
llvm::dbgs () << " InOut Term Users:\n " ;
759
767
for (auto *inst : inoutTermUsers) {
760
768
llvm::dbgs () << *inst;
@@ -1737,6 +1745,12 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1737
1745
LLVM_DEBUG (llvm::dbgs () << " Running copy propagation!\n " );
1738
1746
moveChecker.changed |= moveChecker.canonicalizer .canonicalize ();
1739
1747
1748
+ // Export the drop_deinit's discovered by the ObjectChecker into the
1749
+ // AddressChecker to preserve it for later use. We need to do this since
1750
+ // the ObjectChecker's state gets cleared after running on this LoadInst.
1751
+ for (auto *dropDeinit : moveChecker.canonicalizer .getDropDeinitUses ())
1752
+ moveChecker.addressUseState .dropDeinitInsts .insert (dropDeinit);
1753
+
1740
1754
// If we are asked to perform no_consume_or_assign checking or
1741
1755
// assignable_but_not_consumable checking, if we found any consumes of our
1742
1756
// load, then we need to emit an error.
@@ -2458,10 +2472,26 @@ void MoveOnlyAddressCheckerPImpl::rewriteUses(
2458
2472
FieldSensitiveMultiDefPrunedLiveRange &liveness,
2459
2473
const FieldSensitivePrunedLivenessBoundary &boundary) {
2460
2474
LLVM_DEBUG (llvm::dbgs () << " MoveOnlyAddressChecker Rewrite Uses!\n " );
2475
+
2476
+ // / whether the marked value appeared in a discard statement.
2477
+ const bool isDiscardingContext = !addressUseState.dropDeinitInsts .empty ();
2478
+
2461
2479
// First remove all destroy_addr that have not been claimed.
2462
2480
for (auto destroyPair : addressUseState.destroys ) {
2463
2481
if (!consumes.claimConsume (destroyPair.first , destroyPair.second )) {
2464
2482
destroyPair.first ->eraseFromParent ();
2483
+ continue ;
2484
+ }
2485
+
2486
+ // Otherwise, if we're in a discarding context and checking 'self', flag
2487
+ // the claimed destroy_addr as a point where we're missing an explicit
2488
+ // `consume self`. The reasoning here is that if we leave behind one of
2489
+ // these destroys, then the 'self' didn't get explicitly consumed or
2490
+ // discarded along this path.
2491
+ if (isDiscardingContext) {
2492
+ auto *dropDeinit = addressUseState.dropDeinitInsts .front ();
2493
+ diagnosticEmitter.emitMissingConsumeInDiscardingContext (destroyPair.first ,
2494
+ dropDeinit);
2465
2495
}
2466
2496
}
2467
2497
@@ -2657,7 +2687,6 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
2657
2687
liveness.computeBoundary (boundary);
2658
2688
insertDestroysOnBoundary (markedAddress, liveness, boundary);
2659
2689
rewriteUses (markedAddress, liveness, boundary);
2660
-
2661
2690
return true ;
2662
2691
}
2663
2692
0 commit comments