Skip to content

Commit 22d1825

Browse files
authored
Merge pull request #85999 from gottesmm/pr-c29e9002447c10b77a408ede1f7b7e42c5034dbf
[rbi] When looking for closure uses, handle unresolved_non_copyable_value and store_borrow temporaries.
2 parents 79eee82 + d64fda4 commit 22d1825

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

lib/SILOptimizer/Mandatory/SendNonSendable.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,32 @@ inferNameAndRootHelper(SILValue value) {
217217
return VariableNameInferrer::inferNameAndRoot(value);
218218
}
219219

220+
/// Sometimes we use a store_borrow + temporary to materialize a borrowed value
221+
/// to be passed to another function. We want to emit the error on the function
222+
/// itself, not the store_borrow so we get the best location. We only do this if
223+
/// we can prove that we have a store_borrow to an alloc_stack, the store_borrow
224+
/// is the only thing stored into the alloc_stack, and except for the
225+
/// store_borrow, the alloc_stack has a single non_destroy user, a function we
226+
/// are calling.
227+
static Operand *
228+
findClosureUseThroughStoreBorrowTemporary(StoreBorrowInst *sbi) {
229+
auto *asi = dyn_cast<AllocStackInst>(sbi->getDest());
230+
if (!asi)
231+
return {};
232+
Operand *resultUse = nullptr;
233+
for (auto *use : asi->getUses()) {
234+
if (use == &sbi->getAllOperands()[StoreBorrowInst::Dest])
235+
continue;
236+
if (isa<EndBorrowInst>(use->getUser()))
237+
continue;
238+
auto fas = FullApplySite::isa(use->getUser());
239+
if (!fas)
240+
return {};
241+
resultUse = use;
242+
}
243+
return resultUse;
244+
}
245+
220246
/// Find a use corresponding to the potentially recursive capture of \p
221247
/// initialOperand that would be appropriate for diagnostics.
222248
///
@@ -261,9 +287,15 @@ findClosureUse(Operand *initialOperand) {
261287
if (isIncidentalUse(user) && !isa<IgnoredUseInst>(user))
262288
continue;
263289

290+
// Ignore some instructions we do not care about.
291+
if (isa<DestroyValueInst, EndBorrowInst, EndAccessInst, DeallocStackInst>(
292+
user))
293+
continue;
294+
264295
// Look through some insts we do not care about.
265296
if (isa<CopyValueInst, BeginBorrowInst, ProjectBoxInst, BeginAccessInst>(
266297
user) ||
298+
isa<MarkUnresolvedNonCopyableValueInst>(user) ||
267299
isMoveOnlyWrapperUse(user) ||
268300
// We want to treat move_value [var_decl] as a real use since we are
269301
// assigning to a var.
@@ -308,6 +340,17 @@ findClosureUse(Operand *initialOperand) {
308340
}
309341
}
310342

343+
// If we have a store_borrow, see if we are using it to just marshal a use
344+
// to a full apply site.
345+
if (auto *sbi = dyn_cast<StoreBorrowInst>(op->getUser());
346+
sbi && op == &sbi->getAllOperands()[StoreBorrowInst::Src]) {
347+
if (auto *use = findClosureUseThroughStoreBorrowTemporary(sbi)) {
348+
if (visitedOperand.insert(use).second)
349+
worklist.emplace_back(use, fArg);
350+
continue;
351+
}
352+
}
353+
311354
// Otherwise, we have a real use. Return it and the function argument that
312355
// it was derived from.
313356
return pair;

test/Concurrency/transfernonsendable.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ final class FinalMainActorIsolatedKlass {
6565
func useInOut<T>(_ x: inout T) {}
6666
func useValue<T>(_ x: T) {}
6767
func useValueAsync<T>(_ x: T) async {}
68+
func useValueNoncopyable<T : ~Copyable>(_ x: borrowing T) {}
6869
@concurrent func useValueAsyncConcurrent<T>(_ x: T) async {}
6970

7071
@MainActor func transferToMain<T>(_ t: T) async {}
@@ -90,12 +91,18 @@ struct SendableGenericStruct : Sendable {
9091
var x = SendableKlass()
9192
}
9293

94+
struct NoncopyableStructNonsendable : ~Copyable {
95+
var x = NonSendableKlass()
96+
}
97+
9398
enum MyEnum<T> {
9499
case none
95100
indirect case some(NonSendableKlass)
96101
case more(T)
97102
}
98103

104+
func nonescapingAsyncClosure(_ x: () async -> ()) {}
105+
99106
////////////////////////////
100107
// MARK: Actor Self Tests //
101108
////////////////////////////
@@ -501,6 +508,16 @@ extension MyActor {
501508
}
502509
}
503510

511+
func testNoncopyableNonsendableStructWithNonescapingMainActorAsync() {
512+
let x = NoncopyableStructNonsendable()
513+
let _ = {
514+
nonescapingAsyncClosure { @MainActor in
515+
useValueNoncopyable(x) // expected-warning {{sending 'x' risks causing data races}}
516+
// expected-note @-1 {{task-isolated 'x' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
517+
}
518+
}
519+
}
520+
504521
///////////////////////////////////
505522
// MARK: Sendable Function Tests //
506523
///////////////////////////////////

0 commit comments

Comments
 (0)