Skip to content

Commit 86ef555

Browse files
committed
[region-isolation] Clean up the handling of ref_element_addr let for Sendable types.
Specifically, if we access a Sendable type from a ref_element_addr and the field is mutable, we need to treat the use as a require since we could race on writing to the field. If the field is a let though (and thus immutable), we can still ignore it. I also used this as an opportunity to add SIL tests for ref_element_addr.
1 parent edf4543 commit 86ef555

File tree

2 files changed

+115
-18
lines changed

2 files changed

+115
-18
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,12 +2627,20 @@ TranslationSemantics PartitionOpTranslator::visitUnconditionalCheckedCastInst(
26272627
// ref_element_addr to be merged into.
26282628
TranslationSemantics
26292629
PartitionOpTranslator::visitRefElementAddrInst(RefElementAddrInst *reai) {
2630-
// If we are accessing a let of a Sendable type, do not treat the
2631-
// ref_element_addr as an assign.
2632-
if (reai->getField()->isLet() && !isNonSendableType(reai->getType())) {
2633-
LLVM_DEBUG(llvm::dbgs() << " Found a let! Not tracking!\n");
2634-
return TranslationSemantics::Ignored;
2630+
// If our field is a NonSendableType...
2631+
if (!isNonSendableType(reai->getType())) {
2632+
// And the field is a let... then ignore it. We know that we cannot race on
2633+
// any writes to the field.
2634+
if (reai->getField()->isLet()) {
2635+
LLVM_DEBUG(llvm::dbgs() << " Found a let! Not tracking!\n");
2636+
return TranslationSemantics::Ignored;
2637+
}
2638+
2639+
// Otherwise, we need to treat the access to the field as a require since we
2640+
// could have a race on assignment to the class.
2641+
return TranslationSemantics::Require;
26352642
}
2643+
26362644
return TranslationSemantics::Assign;
26372645
}
26382646

test/Concurrency/transfernonsendable_instruction_matching.sil

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,49 @@ import Builtin
1919

2020
class NonSendableKlass {}
2121

22+
sil @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
23+
sil @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
24+
sil @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
25+
2226
final class SendableKlass : Sendable {}
2327

28+
sil @transferSendableKlass : $@convention(thin) @async (@guaranteed SendableKlass) -> ()
29+
sil @constructSendableKlass : $@convention(thin) () -> @owned SendableKlass
30+
31+
final class KlassContainingKlasses {
32+
let nsImmutable : NonSendableKlass
33+
var nsMutable : NonSendableKlass
34+
let sImmutable : SendableKlass
35+
var sMutable : SendableKlass
36+
}
37+
38+
sil @transferKlassContainingKlasses : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> ()
39+
sil @useKlassContainingKlasses : $@convention(thin) (@guaranteed KlassContainingKlasses) -> ()
40+
sil @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
41+
2442
@_moveOnly
2543
struct NonSendableMoveOnlyStruct {
2644
var ns: NonSendableKlass
2745

2846
deinit
2947
}
3048

49+
sil @constructMoveOnlyStruct : $@convention(thin) () -> @owned NonSendableMoveOnlyStruct
50+
sil @transferMoveOnlyStruct : $@convention(thin) @async (@guaranteed NonSendableMoveOnlyStruct) -> ()
51+
3152
struct NonSendableStruct {
3253
var ns: NonSendableKlass
3354
}
3455

56+
sil @constructStruct : $@convention(thin) () -> @owned NonSendableStruct
57+
sil @transferStruct : $@convention(thin) @async (@guaranteed NonSendableStruct) -> ()
58+
3559
sil @transferRawPointer : $@convention(thin) @async (Builtin.RawPointer) -> ()
3660
sil @useRawPointer : $@convention(thin) (Builtin.RawPointer) -> ()
3761

38-
sil @transferSendableKlass : $@convention(thin) @async (@guaranteed SendableKlass) -> ()
39-
sil @constructSendableKlass : $@convention(thin) () -> @owned SendableKlass
40-
41-
sil @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
42-
sil @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
43-
sil @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
44-
4562
sil @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
4663
sil @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
4764

48-
sil @constructMoveOnlyStruct : $@convention(thin) () -> @owned NonSendableMoveOnlyStruct
49-
sil @transferMoveOnlyStruct : $@convention(thin) @async (@guaranteed NonSendableMoveOnlyStruct) -> ()
50-
51-
sil @constructStruct : $@convention(thin) () -> @owned NonSendableStruct
52-
sil @transferStruct : $@convention(thin) @async (@guaranteed NonSendableStruct) -> ()
53-
5465
enum FakeOptional<T> {
5566
case none
5667
case some(T)
@@ -1091,4 +1102,82 @@ bb0:
10911102
destroy_value %1 : $NonSendableKlass
10921103
%9999 = tuple ()
10931104
return %9999 : $()
1105+
}
1106+
1107+
sil [ossa] @ref_element_addr_test_nonsendable_mutable : $@async @convention(thin) () -> () {
1108+
bb0:
1109+
%0 = function_ref @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
1110+
%1 = apply %0() : $@convention(thin) () -> @owned KlassContainingKlasses
1111+
1112+
%2 = begin_borrow %1 : $KlassContainingKlasses
1113+
%3 = ref_element_addr %2 : $KlassContainingKlasses, #KlassContainingKlasses.nsMutable
1114+
1115+
%f2 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
1116+
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f2<NonSendableKlass>(%3) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () // expected-warning {{transferring value of non-Sendable type 'NonSendableKlass' from nonisolated context to global actor '<null>'-isolated context; later accesses could race}}
1117+
1118+
%f3 = function_ref @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
1119+
apply %f3<NonSendableKlass>(%3) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // expected-note {{access here could race}}
1120+
1121+
end_borrow %2 : $KlassContainingKlasses
1122+
1123+
destroy_value %1 : $KlassContainingKlasses
1124+
%9999 = tuple ()
1125+
return %9999 : $()
1126+
}
1127+
1128+
sil [ossa] @ref_element_addr_test_nonsendable_immutable : $@async @convention(thin) () -> () {
1129+
bb0:
1130+
%0 = function_ref @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
1131+
%1 = apply %0() : $@convention(thin) () -> @owned KlassContainingKlasses
1132+
1133+
%2 = begin_borrow %1 : $KlassContainingKlasses
1134+
%3 = ref_element_addr %2 : $KlassContainingKlasses, #KlassContainingKlasses.nsImmutable
1135+
1136+
%f2 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
1137+
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f2<NonSendableKlass>(%3) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () // expected-warning {{transferring value of non-Sendable type 'NonSendableKlass' from nonisolated context to global actor '<null>'-isolated context; later accesses could race}}
1138+
1139+
%f3 = function_ref @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
1140+
apply %f3<NonSendableKlass>(%3) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // expected-note {{access here could race}}
1141+
1142+
end_borrow %2 : $KlassContainingKlasses
1143+
1144+
destroy_value %1 : $KlassContainingKlasses
1145+
%9999 = tuple ()
1146+
return %9999 : $()
1147+
}
1148+
1149+
sil [ossa] @ref_element_addr_test_sendable_mutable : $@async @convention(thin) () -> () {
1150+
bb0:
1151+
%0 = function_ref @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
1152+
%1 = apply %0() : $@convention(thin) () -> @owned KlassContainingKlasses
1153+
1154+
%f = function_ref @transferKlassContainingKlasses : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> ()
1155+
// TODO: Improve diagnostic to make it clear that the reason we are failing is
1156+
// that we could race on a write to the ref_element_addr.
1157+
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f(%1) : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> () // expected-warning {{transferring value of non-Sendable type 'KlassContainingKlasses' from nonisolated context to global actor '<null>'-isolated context; later accesses could race}}
1158+
1159+
%2 = begin_borrow %1 : $KlassContainingKlasses
1160+
%3 = ref_element_addr %2 : $KlassContainingKlasses, #KlassContainingKlasses.sMutable // expected-note {{access here could race}}
1161+
end_borrow %2 : $KlassContainingKlasses
1162+
1163+
destroy_value %1 : $KlassContainingKlasses
1164+
%9999 = tuple ()
1165+
return %9999 : $()
1166+
}
1167+
1168+
sil [ossa] @ref_element_addr_test_sendable_immutable : $@async @convention(thin) () -> () {
1169+
bb0:
1170+
%0 = function_ref @constructKlassContainingKlasses : $@convention(thin) () -> @owned KlassContainingKlasses
1171+
%1 = apply %0() : $@convention(thin) () -> @owned KlassContainingKlasses
1172+
1173+
%f = function_ref @transferKlassContainingKlasses : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> ()
1174+
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f(%1) : $@convention(thin) @async (@guaranteed KlassContainingKlasses) -> ()
1175+
1176+
%2 = begin_borrow %1 : $KlassContainingKlasses
1177+
%3 = ref_element_addr %2 : $KlassContainingKlasses, #KlassContainingKlasses.sImmutable
1178+
end_borrow %2 : $KlassContainingKlasses
1179+
1180+
destroy_value %1 : $KlassContainingKlasses
1181+
%9999 = tuple ()
1182+
return %9999 : $()
10941183
}

0 commit comments

Comments
 (0)