Skip to content

Commit d1870c1

Browse files
committed
[region-isolation] Fix return vs break logic error in the evaluator.
I am not sure if this would ever actually ever cause a bug... but we should be consistent. The issue here is that in the evaluator switch when processing transfers in certain cases when we emitted an early error due to an actor derived value we were performing a transfer and other times we weren't. With this patch: 1. We now consistently do not actually transfer the value if it is actor derived. It is always illegal to transfer an actor derived value... so we know that if we always just error on transferring it, we have a safe model. 2. The switch we breaking so that we could do an assert that we canonicalized the modified PartitionOp. Rather than do that and allow for people to potentially return, I moved the assert into a SWIFT_DEFER and added an assert in the continuation of the switch. This means that if an author ever breaks out of the switch, they will hit the assert implying that the author in all cases must explicitly return within the case statement guaranteeing this mistake does not happen again.
1 parent 18f91c0 commit d1870c1

File tree

2 files changed

+15
-15
lines changed

2 files changed

+15
-15
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ struct PartitionOpEvaluator {
810810
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << " After: ";
811811
p.print(llvm::dbgs()));
812812
}
813+
assert(p.is_canonical_correct());
813814
};
814815

815816
switch (op.getKind()) {
@@ -829,13 +830,13 @@ struct PartitionOpEvaluator {
829830
// assignment could have invalidated canonicality of either the old region
830831
// of op.getOpArgs()[0] or the region of op.getOpArgs()[1], or both
831832
p.canonical = false;
832-
break;
833+
return;
833834
case PartitionOpKind::AssignFresh:
834835
assert(op.getOpArgs().size() == 1 &&
835836
"AssignFresh PartitionOp should be passed 1 argument");
836837

837838
p.addElement(op.getOpArgs()[0]);
838-
break;
839+
return;
839840
case PartitionOpKind::Transfer: {
840841
assert(op.getOpArgs().size() == 1 &&
841842
"Transfer PartitionOp should be passed 1 argument");
@@ -853,8 +854,7 @@ struct PartitionOpEvaluator {
853854
if (!p.isTransferred(nonTransferrable) &&
854855
p.elementToRegionMap.at(nonTransferrable) ==
855856
p.elementToRegionMap.at(op.getOpArgs()[0])) {
856-
handleTransferNonTransferrable(op, nonTransferrable);
857-
break;
857+
return handleTransferNonTransferrable(op, nonTransferrable);
858858
}
859859
}
860860

@@ -874,7 +874,7 @@ struct PartitionOpEvaluator {
874874

875875
// Mark op.getOpArgs()[0] as transferred.
876876
p.markTransferred(op.getOpArgs()[0], op.getSourceOp());
877-
break;
877+
return;
878878
}
879879
case PartitionOpKind::UndoTransfer: {
880880
assert(op.getOpArgs().size() == 1 &&
@@ -884,7 +884,7 @@ struct PartitionOpEvaluator {
884884

885885
// Mark op.getOpArgs()[0] as not transferred.
886886
p.undoTransfer(op.getOpArgs()[0]);
887-
break;
887+
return;
888888
}
889889
case PartitionOpKind::Merge:
890890
assert(op.getOpArgs().size() == 2 &&
@@ -900,17 +900,18 @@ struct PartitionOpEvaluator {
900900
handleFailure(op, op.getOpArgs()[1], transferringInst);
901901

902902
p.merge(op.getOpArgs()[0], op.getOpArgs()[1]);
903-
break;
903+
return;
904904
case PartitionOpKind::Require:
905905
assert(op.getOpArgs().size() == 1 &&
906906
"Require PartitionOp should be passed 1 argument");
907907
assert(p.elementToRegionMap.count(op.getOpArgs()[0]) &&
908908
"Require PartitionOp's argument should already be tracked");
909909
if (auto *transferringInst = p.getTransferred(op.getOpArgs()[0]))
910910
handleFailure(op, op.getOpArgs()[0], transferringInst);
911+
return;
911912
}
912913

913-
assert(p.is_canonical_correct());
914+
llvm_unreachable("Covered switch isn't covered?!");
914915
}
915916

916917
void apply(std::initializer_list<PartitionOp> ops) {

test/Concurrency/sendable_checking.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,11 @@ final class NonSendable {
251251
func call() async {
252252
await update()
253253
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
254-
// expected-tns-warning@-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
255-
// expected-tns-warning@-3 {{passing argument of non-sendable type 'NonSendable' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}}
254+
// expected-tns-warning @-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
256255

257256
await self.update()
258257
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
259-
// expected-tns-note @-2 {{access here could race}}
258+
// expected-tns-warning @-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
260259

261260
_ = await x
262261
// expected-warning@-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
@@ -292,14 +291,14 @@ func callNonisolatedAsyncClosure(
292291
g: (NonSendable) async -> Void
293292
) async {
294293
await g(ns)
295-
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
296-
// expected-tns-warning@-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
297-
// expected-tns-warning@-3 {{passing argument of non-sendable type 'NonSendable' from main actor-isolated context to nonisolated context at this call site could yield a race with accesses later in this function}}
294+
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
295+
// expected-tns-warning @-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
296+
// expected-tns-warning @-3 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
298297

299298
let f: (NonSendable) async -> () = globalSendable // okay
300299
await f(ns)
301300
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
302-
// expected-tns-note@-2 {{access here could race}}
301+
// expected-tns-warning @-2 {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
303302

304303
}
305304

0 commit comments

Comments
 (0)