Skip to content

Commit 052ecd1

Browse files
committed
[move-only] Do not attempt to process mark_must_check if we detect they have a partial_apply that was identified by an earlier diagnostic as escaping.
Previously, we would emit a "compiler doesn't understand error" since we would detect the escape and fail. That is the correct behavior here if the partial_apply is not already identified as escaping by an earlier pass. But in the case where we see that the partial_apply's callee was marked with semantics::NO_MOVEONLY_DIAGNOSTICS, then we: 1. Suppress the "compiler doesn't understand error" for this specific mark_must_check. 2. Suppress function wide the "copy of noncopyable type" error. Since we stopped processing the mark_must_check that was passed to the partial_apply, we may have left copies of noncopyable types on that mark_must_check value. This is ok since the user will get the error, will recompile, and if any further show up after they fix the inout escaping issue, they will be emitted appropriately. (cherry picked from commit 07979f0)
1 parent a6edf15 commit 052ecd1

7 files changed

+59
-17
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerTester.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,18 @@ class MoveOnlyAddressCheckerTesterPass : public SILFunctionTransform {
105105
<< '\n');
106106

107107
bool madeChange = false;
108-
unsigned diagCount = 0;
109108
if (moveIntroducersToProcess.empty()) {
110109
LLVM_DEBUG(llvm::dbgs() << "No move introducers found?!\n");
111110
} else {
112111
borrowtodestructure::IntervalMapAllocator allocator;
113112
MoveOnlyAddressChecker checker{getFunction(), diagnosticEmitter,
114113
allocator, domTree, poa};
115114
madeChange = checker.check(moveIntroducersToProcess);
116-
diagCount = checker.diagnosticEmitter.getDiagnosticCount();
117115
}
118116

119117
// If we did not emit any diagnostics, emit a diagnostic if we missed any
120118
// copies.
121-
if (!diagCount) {
119+
if (!diagnosticEmitter.emittedDiagnostic()) {
122120
emitCheckerMissedCopyOfNonCopyableTypeErrors(getFunction(),
123121
diagnosticEmitter);
124122
}

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,8 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
19021902
if (fArg->getArgumentConvention().isInoutConvention() &&
19031903
pas->getCalleeFunction()->hasSemanticsAttr(
19041904
semantics::NO_MOVEONLY_DIAGNOSTICS)) {
1905+
diagnosticEmitter.emitEarlierPassEmittedDiagnostic(markedValue);
1906+
return false;
19051907
}
19061908
}
19071909

@@ -2652,9 +2654,19 @@ bool MoveOnlyAddressChecker::check(
26522654
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *markedValue);
26532655

26542656
// Perform our address check.
2657+
unsigned diagnosticEmittedByEarlierPassCount =
2658+
diagnosticEmitter.getDiagnosticEmittedByEarlierPassCount();
26552659
if (!pimpl.performSingleCheck(markedValue)) {
2656-
LLVM_DEBUG(llvm::dbgs()
2657-
<< "Failed to perform single check! Emitting error!\n");
2660+
if (diagnosticEmittedByEarlierPassCount !=
2661+
diagnosticEmitter.getDiagnosticEmittedByEarlierPassCount()) {
2662+
LLVM_DEBUG(
2663+
llvm::dbgs()
2664+
<< "Failed to perform single check but found earlier emitted "
2665+
"error. Not emitting checker doesn't understand diagnostic!\n");
2666+
continue;
2667+
}
2668+
LLVM_DEBUG(llvm::dbgs() << "Failed to perform single check! Emitting "
2669+
"compiler doesn't understand diagnostic!\n");
26582670
// If we fail the address check in some way, set the diagnose!
26592671
diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(markedValue);
26602672
}

lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
179179
// remain. If we emitted a diagnostic, we just want to rewrite all of the
180180
// non-copyable copies into explicit variants below and let the user
181181
// recompile.
182-
if (!checker.diagnosticEmitter.getDiagnosticCount()) {
182+
if (!checker.diagnosticEmitter.emittedDiagnostic()) {
183183
emitCheckerMissedCopyOfNonCopyableTypeErrors(getFunction(),
184184
checker.diagnosticEmitter);
185185
}

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ class DiagnosticEmitter {
5252

5353
bool emittedCheckerDoesntUnderstandDiagnostic = false;
5454

55+
/// This is incremented every time that the checker determines that an earlier
56+
/// pass emitted a diagnostic while processing a mark_must_check. In such a
57+
/// case, we want to suppress:
58+
///
59+
/// 1. Emitting the compiler doesn't understand how to check error for the
60+
/// specific mark_must_check.
61+
///
62+
/// 2. The "copy of noncopyable type" error over the entire function since us
63+
/// stopping processing at some point may have left copies.
64+
///
65+
/// We use a counter rather than a boolean here so that a caller that is
66+
/// processing an individual mark_must_check can determine if the checker
67+
/// identified such an earlier pass diagnostic for the specific allocation so
68+
/// that we can still emit "compiler doesn't understand" errors for other
69+
/// allocations.
70+
unsigned diagnosticEmittedByEarlierPassCount = 0;
71+
5572
public:
5673
DiagnosticEmitter(SILFunction *inputFn) : fn(inputFn) {}
5774

@@ -67,11 +84,37 @@ class DiagnosticEmitter {
6784
return *canonicalizer.get();
6885
}
6986

87+
/// Returns true if when processing any allocation in the current function:
88+
///
89+
/// 1. This diagnostic emitter emitted a diagnostic.
90+
/// 2. The user of the diagnostic emitter signaled to the diagnostic emitter
91+
/// that it detected an earlier diagnostic was emitted that prevented it
92+
/// from performing checking.
93+
///
94+
/// DISCUSSION: This is used by the checker to decide whether or not it should
95+
/// emit "found copy of a noncopyable type" error. If the checker emitted one
96+
/// of these diagnostics, then the checker may have stopped processing early
97+
/// and left copies since it was no longer able to check. In such a case, we
98+
/// want the user to fix the pre-existing errors and re-run.
99+
bool emittedDiagnostic() const {
100+
return getDiagnosticCount() || getDiagnosticEmittedByEarlierPassCount();
101+
}
102+
70103
unsigned getDiagnosticCount() const { return diagnosticCount; }
104+
71105
bool didEmitCheckerDoesntUnderstandDiagnostic() const {
72106
return emittedCheckerDoesntUnderstandDiagnostic;
73107
}
74108

109+
bool getDiagnosticEmittedByEarlierPassCount() const {
110+
return diagnosticEmittedByEarlierPassCount;
111+
}
112+
113+
void emitEarlierPassEmittedDiagnostic(MarkMustCheckInst *mmci) {
114+
++diagnosticEmittedByEarlierPassCount;
115+
registerDiagnosticEmitted(mmci);
116+
}
117+
75118
/// Used at the end of the MoveOnlyAddressChecker to tell the user in a nice
76119
/// way to file a bug.
77120
void emitCheckedMissedCopyError(SILInstruction *copyInst);

test/SILGen/moveonly_escaping_closure.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,6 @@ func testConsumingEscapeClosureCaptureLet(_ f: consuming @escaping () -> ()) {
618618
var globalClosureCaptureInOut: () -> () = {}
619619
func testGlobalClosureCaptureInOut(_ x: inout SingleElt) {
620620
// expected-note @-1 {{'x' is declared 'inout'}}
621-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check}}
622621
globalClosureCaptureInOut = { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
623622
borrowVal(x) // expected-note {{captured here}}
624623
consumeVal(x) // expected-note {{captured here}}
@@ -708,7 +707,6 @@ func testLocalLetClosureCaptureInOut(_ x: inout SingleElt) {
708707
// CHECK: } // end sil function '$s16moveonly_closure31testLocalVarClosureCaptureInOutyyAA9SingleEltVzFyycfU_'
709708
func testLocalVarClosureCaptureInOut(_ x: inout SingleElt) {
710709
// expected-note @-1 {{'x' is declared 'inout'}}
711-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check}}
712710
var f = { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
713711
borrowVal(x) // expected-note {{captured here}}
714712
consumeVal(x) // expected-note {{captured here}}
@@ -757,7 +755,6 @@ func testLocalVarClosureCaptureInOut(_ x: inout SingleElt) {
757755
// CHECK: } // end sil function '$s16moveonly_closure026testInOutVarClosureCapturedE0yyyycz_AA9SingleEltVztFyycfU_'
758756
func testInOutVarClosureCaptureInOut(_ f: inout () -> (), _ x: inout SingleElt) {
759757
// expected-note @-1 {{'x' is declared 'inout'}}
760-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check}}
761758
f = { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
762759
borrowVal(x) // expected-note {{captured here}}
763760
consumeVal(x) // expected-note {{captured here}}
@@ -810,7 +807,6 @@ func testInOutVarClosureCaptureInOut(_ f: inout () -> (), _ x: inout SingleElt)
810807
// CHECK: } // end sil function '$s16moveonly_closure38testConsumingEscapeClosureCaptureInOutyyyycn_AA9SingleEltVztFyycfU_'
811808
func testConsumingEscapeClosureCaptureInOut(_ f: consuming @escaping () -> (), _ x: inout SingleElt) {
812809
// expected-note @-1 {{'x' is declared 'inout'}}
813-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check}}
814810
f = { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
815811
borrowVal(x) // expected-note {{captured here}}
816812
consumeVal(x) // expected-note {{captured here}}

test/SILOptimizer/moveonly_addresschecker_diagnostics.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,13 +3235,8 @@ public func closureVarCaptureClassUseAfterConsumeError() {
32353235
let _ = x3
32363236
}
32373237

3238-
// TODO: The reason why we emit the moveonly type is that correctly the checker
3239-
// realizes that the partial_apply has escaped, but does not understand that
3240-
// this case has already been diagnosed properly by the escaping inout
3241-
// diagnostic, so we shouldn't try to process it.
32423238
public func closureVarCaptureClassArgUseAfterConsume(_ x2: inout Klass) {
32433239
// expected-note @-1 {{'x2' is declared 'inout'}}
3244-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check!}}
32453240
var f = {}
32463241
f = {
32473242
// expected-error @-1 {{escaping closure captures 'inout' parameter 'x2'}}

test/SILOptimizer/moveonly_objectchecker_diagnostics.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,10 +2956,8 @@ public func closureVarCaptureClassUseAfterConsume1(_ x: borrowing Klass) { // ex
29562956
f()
29572957
}
29582958

2959-
// TODO: MG
29602959
public func closureVarCaptureClassUseAfterConsume2(_ x2: inout Klass) {
29612960
// expected-note @-1 {{'x2' is declared 'inout'}}
2962-
// expected-error @-2 {{Usage of a move only type that the move checker does not know how to check!}}
29632961
var f = {}
29642962
f = { // expected-error {{escaping closure captures 'inout' parameter 'x2'}}
29652963
borrowVal(x2) // expected-note {{captured here}}

0 commit comments

Comments
 (0)