@@ -99,6 +99,26 @@ struct OSSACanonicalizer {
99
99
bool foundConsumingUseRequiringCopy () const {
100
100
return consumingUsesNeedingCopy.size ();
101
101
}
102
+
103
+ bool hasPartialApplyConsumingUse () const {
104
+ return llvm::any_of (consumingUsesNeedingCopy,
105
+ [](Operand *use) {
106
+ return isa<PartialApplyInst>(use->getUser ());
107
+ }) ||
108
+ llvm::any_of (finalConsumingUses, [](Operand *use) {
109
+ return isa<PartialApplyInst>(use->getUser ());
110
+ });
111
+ }
112
+
113
+ bool hasNonPartialApplyConsumingUse () const {
114
+ return llvm::any_of (consumingUsesNeedingCopy,
115
+ [](Operand *use) {
116
+ return !isa<PartialApplyInst>(use->getUser ());
117
+ }) ||
118
+ llvm::any_of (finalConsumingUses, [](Operand *use) {
119
+ return !isa<PartialApplyInst>(use->getUser ());
120
+ });
121
+ }
102
122
};
103
123
104
124
} // anonymous namespace
@@ -147,7 +167,12 @@ struct DiagnosticEmitter {
147
167
}
148
168
149
169
private:
150
- void emitDiagnosticsForFoundUses () const ;
170
+ // / Emit diagnostics for the final consuming uses and consuming uses needing
171
+ // / copy. If filter is non-null, allow for the caller to pre-process operands
172
+ // / and emit their own diagnostic. If filter returns true, then we assume that
173
+ // / the caller processed it correctly. false, then we continue to process it.
174
+ void emitDiagnosticsForFoundUses (bool ignorePartialApply = false ) const ;
175
+ void emitDiagnosticsForPartialApplyUses () const ;
151
176
};
152
177
153
178
} // anonymous namespace
@@ -172,11 +197,24 @@ void DiagnosticEmitter::emitGuaranteedDiagnostic(
172
197
auto &astContext = fn->getASTContext ();
173
198
StringRef varName = getVariableNameForValue (markedValue);
174
199
200
+ // See if we have any closure capture uses and emit a better diagnostic.
201
+ if (canonicalizer.hasPartialApplyConsumingUse ()) {
202
+ diagnose (astContext,
203
+ markedValue->getDefiningInstruction ()->getLoc ().getSourceLoc (),
204
+ diag::sil_moveonlychecker_guaranteed_value_captured_by_closure,
205
+ varName);
206
+ emitDiagnosticsForPartialApplyUses ();
207
+ valuesWithDiagnostics.insert (markedValue);
208
+ }
209
+
210
+ // If we do not have any non-partial apply consuming uses... just exit early.
211
+ if (!canonicalizer.hasNonPartialApplyConsumingUse ())
212
+ return ;
213
+
175
214
diagnose (astContext,
176
215
markedValue->getDefiningInstruction ()->getLoc ().getSourceLoc (),
177
216
diag::sil_moveonlychecker_guaranteed_value_consumed, varName);
178
-
179
- emitDiagnosticsForFoundUses ();
217
+ emitDiagnosticsForFoundUses (true /* ignore partial apply uses*/ );
180
218
valuesWithDiagnostics.insert (markedValue);
181
219
}
182
220
@@ -193,7 +231,8 @@ void DiagnosticEmitter::emitOwnedDiagnostic(MarkMustCheckInst *markedValue) {
193
231
valuesWithDiagnostics.insert (markedValue);
194
232
}
195
233
196
- void DiagnosticEmitter::emitDiagnosticsForFoundUses () const {
234
+ void DiagnosticEmitter::emitDiagnosticsForFoundUses (
235
+ bool ignorePartialApplyUses) const {
197
236
auto &astContext = fn->getASTContext ();
198
237
199
238
for (auto *consumingUse : canonicalizer.consumingUsesNeedingCopy ) {
@@ -209,6 +248,9 @@ void DiagnosticEmitter::emitDiagnosticsForFoundUses() const {
209
248
}
210
249
}
211
250
251
+ if (ignorePartialApplyUses &&
252
+ isa<PartialApplyInst>(consumingUse->getUser ()))
253
+ continue ;
212
254
diagnose (astContext, loc.getSourceLoc (),
213
255
diag::sil_moveonlychecker_consuming_use_here);
214
256
}
@@ -226,11 +268,58 @@ void DiagnosticEmitter::emitDiagnosticsForFoundUses() const {
226
268
}
227
269
}
228
270
271
+ if (ignorePartialApplyUses &&
272
+ isa<PartialApplyInst>(consumingUse->getUser ()))
273
+ continue ;
274
+
229
275
diagnose (astContext, loc.getSourceLoc (),
230
276
diag::sil_moveonlychecker_consuming_use_here);
231
277
}
232
278
}
233
279
280
+ void DiagnosticEmitter::emitDiagnosticsForPartialApplyUses () const {
281
+ auto &astContext = fn->getASTContext ();
282
+
283
+ for (auto *consumingUse : canonicalizer.consumingUsesNeedingCopy ) {
284
+ // See if the consuming use is an owned moveonly_to_copyable whose only
285
+ // user is a return. In that case, use the return loc instead. We do this
286
+ // b/c it is illegal to put a return value location on a non-return value
287
+ // instruction... so we have to hack around this slightly.
288
+ auto *user = consumingUse->getUser ();
289
+ auto loc = user->getLoc ();
290
+ if (auto *mtc = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(user)) {
291
+ if (auto *ri = mtc->getSingleUserOfType <ReturnInst>()) {
292
+ loc = ri->getLoc ();
293
+ }
294
+ }
295
+
296
+ if (!isa<PartialApplyInst>(consumingUse->getUser ()))
297
+ continue ;
298
+ diagnose (astContext, loc.getSourceLoc (),
299
+ diag::sil_moveonlychecker_consuming_closure_use_here);
300
+ }
301
+
302
+ for (auto *consumingUse : canonicalizer.finalConsumingUses ) {
303
+ // See if the consuming use is an owned moveonly_to_copyable whose only
304
+ // user is a return. In that case, use the return loc instead. We do this
305
+ // b/c it is illegal to put a return value location on a non-return value
306
+ // instruction... so we have to hack around this slightly.
307
+ auto *user = consumingUse->getUser ();
308
+ auto loc = user->getLoc ();
309
+ if (auto *mtc = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(user)) {
310
+ if (auto *ri = mtc->getSingleUserOfType <ReturnInst>()) {
311
+ loc = ri->getLoc ();
312
+ }
313
+ }
314
+
315
+ if (!isa<PartialApplyInst>(consumingUse->getUser ()))
316
+ continue ;
317
+
318
+ diagnose (astContext, loc.getSourceLoc (),
319
+ diag::sil_moveonlychecker_consuming_closure_use_here);
320
+ }
321
+ }
322
+
234
323
// ===----------------------------------------------------------------------===//
235
324
// MARK: Main Pass
236
325
// ===----------------------------------------------------------------------===//
0 commit comments