225
225
#include " swift/AST/AccessScope.h"
226
226
#include " swift/AST/DiagnosticEngine.h"
227
227
#include " swift/AST/DiagnosticsSIL.h"
228
+ #include " swift/AST/SemanticAttrs.h"
228
229
#include " swift/Basic/Debug.h"
229
230
#include " swift/Basic/Defer.h"
230
231
#include " swift/Basic/FrozenMultiMap.h"
@@ -492,6 +493,92 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(MarkMustCheckInst *markedAd
492
493
return false ;
493
494
}
494
495
496
+ // ===----------------------------------------------------------------------===//
497
+ // MARK: Partial Apply Utilities
498
+ // ===----------------------------------------------------------------------===//
499
+
500
+ static bool findNonEscapingPartialApplyUses (
501
+ PartialApplyInst *pai, TypeTreeLeafTypeRange leafRange,
502
+ llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4 >
503
+ &livenessUses) {
504
+ StackList<Operand *> worklist (pai->getFunction ());
505
+ for (auto *use : pai->getUses ())
506
+ worklist.push_back (use);
507
+
508
+ LLVM_DEBUG (llvm::dbgs () << " Searching for partial apply uses!\n " );
509
+ while (!worklist.empty ()) {
510
+ auto *use = worklist.pop_back_val ();
511
+
512
+ if (use->isTypeDependent ())
513
+ continue ;
514
+
515
+ auto *user = use->getUser ();
516
+
517
+ // These instructions do not cause us to escape.
518
+ if (isIncidentalUse (user) || isa<DestroyValueInst>(user))
519
+ continue ;
520
+
521
+ // Look through these instructions.
522
+ if (isa<BeginBorrowInst>(user) || isa<CopyValueInst>(user) ||
523
+ isa<MoveValueInst>(user) ||
524
+ // If we capture this partial_apply in another partial_apply, then we
525
+ // know that said partial_apply must not have escaped the value since
526
+ // otherwise we could not have an inout_aliasable argument or be
527
+ // on_stack. Process it recursively so that we treat uses of that
528
+ // partial_apply and applies of that partial_apply as uses of our
529
+ // partial_apply.
530
+ //
531
+ // We have this separately from the other look through sections so that
532
+ // we can make it clearer what we are doing here.
533
+ isa<PartialApplyInst>(user)) {
534
+ for (auto *use : cast<SingleValueInstruction>(user)->getUses ())
535
+ worklist.push_back (use);
536
+ continue ;
537
+ }
538
+
539
+ // If we have a mark_dependence and are the value, look through the
540
+ // mark_dependence.
541
+ if (auto *mdi = dyn_cast<MarkDependenceInst>(user)) {
542
+ if (mdi->getValue () == use->get ()) {
543
+ for (auto *use : mdi->getUses ())
544
+ worklist.push_back (use);
545
+ continue ;
546
+ }
547
+ }
548
+
549
+ if (auto apply = FullApplySite::isa (user)) {
550
+ // If we apply the function or pass the function off to an apply, then we
551
+ // need to treat the function application as a liveness use of the
552
+ // variable since if the partial_apply is invoked within the function
553
+ // application, we may access the captured variable.
554
+ livenessUses.insert ({user, leafRange});
555
+ if (apply.beginsCoroutineEvaluation ()) {
556
+ // If we have a coroutine, we need to treat the abort_apply and
557
+ // end_apply as liveness uses since once we execute one of those
558
+ // instructions, we have returned control to the coroutine which means
559
+ // that we could then access the captured variable again.
560
+ auto *bai = cast<BeginApplyInst>(user);
561
+ SmallVector<EndApplyInst *, 4 > endApplies;
562
+ SmallVector<AbortApplyInst *, 4 > abortApplies;
563
+ bai->getCoroutineEndPoints (endApplies, abortApplies);
564
+ for (auto *eai : endApplies)
565
+ livenessUses.insert ({eai, leafRange});
566
+ for (auto *aai : abortApplies)
567
+ livenessUses.insert ({aai, leafRange});
568
+ }
569
+ continue ;
570
+ }
571
+
572
+ LLVM_DEBUG (
573
+ llvm::dbgs ()
574
+ << " Found instruction we did not understand... returning false!\n " );
575
+ LLVM_DEBUG (llvm::dbgs () << " Instruction: " << *user);
576
+ return false ;
577
+ }
578
+
579
+ return true ;
580
+ }
581
+
495
582
// ===----------------------------------------------------------------------===//
496
583
// MARK: Find Candidate Mark Must Checks
497
584
// ===----------------------------------------------------------------------===//
@@ -1639,10 +1726,15 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1639
1726
<< " Found mark must check [nocopy] error: " << *user);
1640
1727
auto *fArg = dyn_cast<SILFunctionArgument>(
1641
1728
stripAccessMarkers (markedValue->getOperand ()));
1642
- if (fArg && fArg ->isClosureCapture () && fArg ->getType ().isAddress ()) {
1643
- moveChecker.diagnosticEmitter .emitPromotedBoxArgumentError (
1644
- markedValue, fArg );
1729
+ // If we have a closure captured that we specialized, we should have a
1730
+ // no consume or assign and should emit a normal guaranteed diagnostic.
1731
+ if (fArg && fArg ->isClosureCapture () &&
1732
+ fArg ->getArgumentConvention ().isInoutConvention ()) {
1733
+ assert (checkKind == MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
1734
+ moveChecker.diagnosticEmitter .emitObjectGuaranteedDiagnostic (
1735
+ markedValue);
1645
1736
} else {
1737
+ // Otherwise, we need to emit an escaping closure error.
1646
1738
moveChecker.diagnosticEmitter
1647
1739
.emitAddressEscapingClosureCaptureLoadedAndConsumed (markedValue);
1648
1740
}
@@ -1802,6 +1894,17 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1802
1894
}
1803
1895
1804
1896
if (auto *pas = dyn_cast<PartialApplyInst>(user)) {
1897
+ if (auto *fArg = dyn_cast<SILFunctionArgument>(
1898
+ stripAccessMarkers (markedValue->getOperand ()))) {
1899
+ // If we are processing an inout convention and we emitted an error on the
1900
+ // partial_apply, we shouldn't process this mark_must_check, but squelch
1901
+ // the compiler doesn't understand error.
1902
+ if (fArg ->getArgumentConvention ().isInoutConvention () &&
1903
+ pas->getCalleeFunction ()->hasSemanticsAttr (
1904
+ semantics::NO_MOVEONLY_DIAGNOSTICS)) {
1905
+ }
1906
+ }
1907
+
1805
1908
if (pas->isOnStack () ||
1806
1909
ApplySite (pas).getArgumentConvention (*op).isInoutConvention ()) {
1807
1910
LLVM_DEBUG (llvm::dbgs () << " Found on stack partial apply or inout usage!\n " );
@@ -1813,13 +1916,18 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1813
1916
return false ;
1814
1917
}
1815
1918
1816
- useState.livenessUses .insert ({user, *leafRange});
1817
- for (auto *use : pas->getConsumingUses ()) {
1818
- LLVM_DEBUG (llvm::dbgs ()
1819
- << " Adding consuming use of partial apply as liveness use: "
1820
- << *use->getUser ());
1821
- useState.livenessUses .insert ({use->getUser (), *leafRange});
1919
+ // Attempt to find calls of the non-escaping partial apply and places
1920
+ // where the partial apply is passed to a function. We treat those as
1921
+ // liveness uses. If we find a use we don't understand, we return false
1922
+ // here.
1923
+ if (!findNonEscapingPartialApplyUses (pas, *leafRange,
1924
+ useState.livenessUses )) {
1925
+ LLVM_DEBUG (
1926
+ llvm::dbgs ()
1927
+ << " Failed to understand use of a non-escaping partial apply?!\n " );
1928
+ return false ;
1822
1929
}
1930
+
1823
1931
return true ;
1824
1932
}
1825
1933
}
0 commit comments