Skip to content

Commit 1cc5a86

Browse files
authored
Merge pull request #82904 from DougGregor/region-isolation-isolated-conformances-6.2
[6.2] Extend region analysis to account for isolated conformances
2 parents 7c12be8 + 1d86ed5 commit 1cc5a86

File tree

6 files changed

+504
-36
lines changed

6 files changed

+504
-36
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,10 @@ NOTE(regionbasedisolation_typed_sendneversendable_via_arg_callee, none,
10811081
"sending %0 value of non-Sendable type %1 to %2 %3 %4 risks causing races in between %0 and %2 uses",
10821082
(StringRef, Type, ActorIsolation, DescriptiveDeclKind, DeclName))
10831083

1084+
NOTE(regionbasedisolation_isolated_conformance_introduced, none,
1085+
"isolated conformance to %kind0 can be introduced here",
1086+
(const ValueDecl *))
1087+
10841088
// Error that is only used when the send non sendable emitter cannot discover any
10851089
// information to give a better diagnostic.
10861090
ERROR(regionbasedisolation_task_or_actor_isolated_sent, none,

include/swift/SILOptimizer/Utils/SILIsolationInfo.h

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class SILIsolationInfo {
186186
UnappliedIsolatedAnyParameter = 0x2,
187187

188188
/// The maximum number of bits used by a Flag.
189-
MaxNumBits = 2,
189+
MaxNumBits = 3,
190190
};
191191

192192
using Options = OptionSet<Flag>;
@@ -204,13 +204,19 @@ class SILIsolationInfo {
204204
/// derived isolatedValue from.
205205
ActorInstance actorInstance;
206206

207+
/// When the isolation is introduced due to a (potentially) isolated
208+
/// conformance, the protocol whose conformance might be isolated.
209+
ProtocolDecl *isolatedConformance = nullptr;
210+
207211
unsigned kind : 8;
208212
unsigned options : 8;
209213

210214
SILIsolationInfo(SILValue isolatedValue, SILValue actorInstance,
211-
ActorIsolation actorIsolation, Options options = Options())
215+
ActorIsolation actorIsolation, Options options = Options(),
216+
ProtocolDecl *isolatedConformance = nullptr)
212217
: actorIsolation(actorIsolation), isolatedValue(isolatedValue),
213-
actorInstance(ActorInstance::getForValue(actorInstance)), kind(Actor),
218+
actorInstance(ActorInstance::getForValue(actorInstance)),
219+
isolatedConformance(isolatedConformance), kind(Actor),
214220
options(options.toRaw()) {
215221
assert((!actorInstance ||
216222
(actorIsolation.getKind() == ActorIsolation::ActorInstance &&
@@ -219,23 +225,39 @@ class SILIsolationInfo {
219225
->lookThroughAllOptionalTypes()
220226
->getAnyActor())) &&
221227
"actorInstance must be an actor if it is non-empty");
228+
assert((getKind() != Disconnected || isolatedConformance == nullptr) &&
229+
"isolated conformance cannot be introduced with disconnected region");
222230
}
223231

224232
SILIsolationInfo(SILValue isolatedValue, ActorInstance actorInstance,
225-
ActorIsolation actorIsolation, Options options = Options())
233+
ActorIsolation actorIsolation, Options options = Options(),
234+
ProtocolDecl *isolatedConformance = nullptr)
226235
: actorIsolation(actorIsolation), isolatedValue(isolatedValue),
227-
actorInstance(actorInstance), kind(Actor), options(options.toRaw()) {
236+
actorInstance(actorInstance), isolatedConformance(isolatedConformance),
237+
kind(Actor), options(options.toRaw())
238+
{
228239
assert(actorInstance);
229240
assert(actorIsolation.getKind() == ActorIsolation::ActorInstance);
230241
}
231242

232-
SILIsolationInfo(Kind kind, SILValue isolatedValue)
233-
: actorIsolation(), isolatedValue(isolatedValue), kind(kind), options(0) {
243+
SILIsolationInfo(Kind kind, SILValue isolatedValue,
244+
ProtocolDecl *isolatedConformance = nullptr)
245+
: actorIsolation(), isolatedValue(isolatedValue),
246+
isolatedConformance(isolatedConformance), kind(kind), options(0) {
234247
}
235248

236249
SILIsolationInfo(Kind kind, Options options = Options())
237250
: actorIsolation(), kind(kind), options(options.toRaw()) {}
238251

252+
/// Infer isolation region from the set of protocol conformances.
253+
static SILIsolationInfo getFromConformances(
254+
SILValue value, ArrayRef<ProtocolConformanceRef> conformances);
255+
256+
/// Determine the isolation of conformances that could be introduced by a
257+
/// cast from sourceType to destType.
258+
static SILIsolationInfo getForCastConformances(
259+
SILValue value, CanType sourceType, CanType destType);
260+
239261
public:
240262
SILIsolationInfo() : actorIsolation(), kind(Kind::Unknown), options(0) {}
241263

@@ -257,6 +279,12 @@ class SILIsolationInfo {
257279
return getOptions().contains(Flag::UnsafeNonIsolated);
258280
}
259281

282+
// Retrieve the protocol to which there is (or could be) an isolated
283+
// conformance.
284+
ProtocolDecl *getIsolatedConformance() const {
285+
return isolatedConformance;
286+
}
287+
260288
SILIsolationInfo withUnsafeNonIsolated(bool newValue = true) const {
261289
assert(*this && "Cannot be unknown");
262290
auto self = *this;
@@ -269,6 +297,26 @@ class SILIsolationInfo {
269297
return self;
270298
}
271299

300+
/// Produce a new isolation info value that merges in the given isolated
301+
/// conformance value.
302+
///
303+
/// If both isolation infos have an isolation conformance, pick one
304+
/// arbitrarily. Otherwise, the result has no isolated conformance.
305+
SILIsolationInfo
306+
withMergedIsolatedConformance(ProtocolDecl *newIsolatedConformance) const {
307+
SILIsolationInfo result(*this);
308+
if (!isolatedConformance || !newIsolatedConformance) {
309+
result.isolatedConformance = nullptr;
310+
return result;
311+
}
312+
313+
result.isolatedConformance =
314+
ProtocolDecl::compare(isolatedConformance, newIsolatedConformance) <= 0
315+
? isolatedConformance
316+
: newIsolatedConformance;
317+
return result;
318+
}
319+
272320
/// Returns true if this actor isolation is derived from an unapplied
273321
/// isolation parameter. When merging, we allow for this to be merged with a
274322
/// more specific isolation kind.
@@ -427,10 +475,13 @@ class SILIsolationInfo {
427475
Flag::UnappliedIsolatedAnyParameter};
428476
}
429477

430-
static SILIsolationInfo getGlobalActorIsolated(SILValue value,
431-
Type globalActorType) {
478+
static SILIsolationInfo getGlobalActorIsolated(
479+
SILValue value,
480+
Type globalActorType,
481+
ProtocolDecl *isolatedConformance = nullptr) {
432482
return {value, SILValue() /*no actor instance*/,
433-
ActorIsolation::forGlobalActor(globalActorType)};
483+
ActorIsolation::forGlobalActor(globalActorType),
484+
Options(), isolatedConformance};
434485
}
435486

436487
static SILIsolationInfo getGlobalActorIsolated(SILValue value,
@@ -442,8 +493,9 @@ class SILIsolationInfo {
442493
isolation.getGlobalActor());
443494
}
444495

445-
static SILIsolationInfo getTaskIsolated(SILValue value) {
446-
return {Kind::Task, value};
496+
static SILIsolationInfo getTaskIsolated(
497+
SILValue value, ProtocolDecl *isolatedConformance = nullptr) {
498+
return {Kind::Task, value, isolatedConformance};
447499
}
448500

449501
/// Attempt to infer the isolation region info for \p inst.
@@ -460,6 +512,9 @@ class SILIsolationInfo {
460512
return {};
461513
}
462514

515+
/// Infer isolation of conformances for the given instruction.
516+
static SILIsolationInfo getConformanceIsolation(SILInstruction *inst);
517+
463518
/// A helper that is used to ensure that we treat certain builtin values as
464519
/// non-Sendable that the AST level otherwise thinks are non-Sendable.
465520
///

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ static bool isStaticallyLookThroughInst(SILInstruction *inst) {
308308
case SILInstructionKind::StrongCopyUnmanagedValueInst:
309309
case SILInstructionKind::RefToUnmanagedInst:
310310
case SILInstructionKind::UnmanagedToRefInst:
311-
case SILInstructionKind::InitExistentialValueInst:
312311
case SILInstructionKind::UncheckedEnumDataInst:
313312
case SILInstructionKind::StructElementAddrInst:
314313
case SILInstructionKind::TupleElementAddrInst:
@@ -319,9 +318,17 @@ static bool isStaticallyLookThroughInst(SILInstruction *inst) {
319318
case SILInstructionKind::BeginBorrowInst:
320319
// Look through if it isn't from a var decl.
321320
return !cast<BeginBorrowInst>(inst)->isFromVarDecl();
321+
case SILInstructionKind::InitExistentialValueInst:
322+
return !SILIsolationInfo::getConformanceIsolation(inst);
322323
case SILInstructionKind::UnconditionalCheckedCastInst: {
323324
auto cast = SILDynamicCastInst::getAs(inst);
324325
assert(cast);
326+
327+
// If this cast introduces isolation due to conformances, we cannot look
328+
// through it to the source.
329+
if (SILIsolationInfo::getConformanceIsolation(inst))
330+
return false;
331+
325332
if (cast.isRCIdentityPreserving())
326333
return true;
327334
return false;
@@ -2822,13 +2829,19 @@ class PartitionOpTranslator {
28222829

28232830
template <typename Collection>
28242831
void translateSILMerge(SILValue dest, Collection srcCollection,
2825-
bool requireOperands = true) {
2826-
auto destResult = tryToTrackValue(dest);
2832+
bool requireOperands,
2833+
SILIsolationInfo resultIsolationInfoOverride = {}) {
2834+
auto destResult = tryToTrackValue(dest);
28272835
if (!destResult)
28282836
return;
28292837

28302838
if (requireOperands) {
28312839
builder.addRequire(*destResult);
2840+
2841+
if (resultIsolationInfoOverride && !srcCollection.empty()) {
2842+
using std::begin;
2843+
builder.addActorIntroducingInst(dest, *begin(srcCollection), resultIsolationInfoOverride);
2844+
}
28322845
}
28332846

28342847
for (Operand *op : srcCollection) {
@@ -2847,17 +2860,29 @@ class PartitionOpTranslator {
28472860

28482861
template <>
28492862
void translateSILMerge<Operand *>(SILValue dest, Operand *src,
2850-
bool requireOperands) {
2863+
bool requireOperands,
2864+
SILIsolationInfo resultIsolationInfoOverride) {
28512865
return translateSILMerge(dest, TinyPtrVector<Operand *>(src),
2852-
requireOperands);
2866+
requireOperands, resultIsolationInfoOverride);
28532867
}
28542868

28552869
void translateSILMerge(MutableArrayRef<Operand> array,
2856-
bool requireOperands = true) {
2870+
bool requireOperands,
2871+
SILIsolationInfo resultIsolationInfoOverride = {}) {
28572872
if (array.size() < 2)
28582873
return;
28592874

2860-
auto destResult = tryToTrackValue(array.front().get());
2875+
std::optional<TrackableValueLookupResult> destResult;
2876+
if (resultIsolationInfoOverride) {
2877+
if (auto nonSendableValue = initializeTrackedValue(
2878+
array.front().get(), resultIsolationInfoOverride)) {
2879+
destResult = TrackableValueLookupResult{
2880+
nonSendableValue->first, std::nullopt};
2881+
}
2882+
} else {
2883+
destResult = tryToTrackValue(array.front().get());
2884+
}
2885+
28612886
if (!destResult)
28622887
return;
28632888

@@ -2882,7 +2907,8 @@ class PartitionOpTranslator {
28822907
/// captures by applications), then these can be treated as assignments of \p
28832908
/// dest to src. If the \p dest could be aliased, then we must instead treat
28842909
/// them as merges, to ensure any aliases of \p dest are also updated.
2885-
void translateSILStore(Operand *dest, Operand *src) {
2910+
void translateSILStore(Operand *dest, Operand *src,
2911+
SILIsolationInfo resultIsolationInfoOverride = {}) {
28862912
SILValue destValue = dest->get();
28872913

28882914
if (auto destResult = tryToTrackValue(destValue)) {
@@ -2901,11 +2927,13 @@ class PartitionOpTranslator {
29012927
// TODO: Should this change if we have a Sendable address with a
29022928
// non-Sendable base.
29032929
if (destResult.value().value.isNoAlias() &&
2930+
!resultIsolationInfoOverride &&
29042931
!isProjectedFromAggregate(destValue))
29052932
return translateSILAssign(destValue, src);
29062933

29072934
// Stores to possibly aliased storage must be treated as merges.
2908-
return translateSILMerge(destValue, src);
2935+
return translateSILMerge(destValue, src, /*requireOperand*/true,
2936+
resultIsolationInfoOverride);
29092937
}
29102938

29112939
// Stores to storage of non-Sendable type can be ignored.
@@ -2935,7 +2963,8 @@ class PartitionOpTranslator {
29352963

29362964
// Stores to possibly aliased storage must be treated as merges.
29372965
return translateSILMerge(dest,
2938-
makeOperandRefRange(inst->getElementOperands()));
2966+
makeOperandRefRange(inst->getElementOperands()),
2967+
/*requireOperands=*/true);
29392968
}
29402969

29412970
// Stores to storage of non-Sendable type can be ignored.
@@ -2980,10 +3009,12 @@ class PartitionOpTranslator {
29803009
// and a pointer to the bb being branches to itself.
29813010
// this is handled as assigning to each possible arg being branched to the
29823011
// merge of all values that could be passed to it from this basic block.
2983-
void translateSILPhi(TermArgSources &argSources) {
3012+
void translateSILPhi(TermArgSources &argSources,
3013+
SILIsolationInfo resultIsolationInfoOverride = {}) {
29843014
argSources.argSources.setFrozen();
29853015
for (auto pair : argSources.argSources.getRange()) {
2986-
translateSILMultiAssign(TinyPtrVector<SILValue>(pair.first), pair.second);
3016+
translateSILMultiAssign(TinyPtrVector<SILValue>(pair.first), pair.second,
3017+
resultIsolationInfoOverride);
29873018
}
29883019
}
29893020

@@ -3068,7 +3099,8 @@ class PartitionOpTranslator {
30683099

30693100
case TranslationSemantics::Assign:
30703101
return translateSILMultiAssign(
3071-
inst->getResults(), makeOperandRefRange(inst->getAllOperands()));
3102+
inst->getResults(), makeOperandRefRange(inst->getAllOperands()),
3103+
SILIsolationInfo::getConformanceIsolation(inst));
30723104

30733105
case TranslationSemantics::Require:
30743106
for (auto op : inst->getOperandValues())
@@ -3085,7 +3117,8 @@ class PartitionOpTranslator {
30853117
case TranslationSemantics::Store:
30863118
return translateSILStore(
30873119
&inst->getAllOperands()[CopyLikeInstruction::Dest],
3088-
&inst->getAllOperands()[CopyLikeInstruction::Src]);
3120+
&inst->getAllOperands()[CopyLikeInstruction::Src],
3121+
SILIsolationInfo::getConformanceIsolation(inst));
30893122

30903123
case TranslationSemantics::Special:
30913124
return;
@@ -3096,7 +3129,8 @@ class PartitionOpTranslator {
30963129
case TranslationSemantics::TerminatorPhi: {
30973130
TermArgSources sources;
30983131
sources.init(inst);
3099-
return translateSILPhi(sources);
3132+
return translateSILPhi(
3133+
sources, SILIsolationInfo::getConformanceIsolation(inst));
31003134
}
31013135

31023136
case TranslationSemantics::Asserting:
@@ -3382,7 +3416,6 @@ CONSTANT_TRANSLATION(StrongCopyWeakValueInst, LookThrough)
33823416
CONSTANT_TRANSLATION(StrongCopyUnmanagedValueInst, LookThrough)
33833417
CONSTANT_TRANSLATION(RefToUnmanagedInst, LookThrough)
33843418
CONSTANT_TRANSLATION(UnmanagedToRefInst, LookThrough)
3385-
CONSTANT_TRANSLATION(InitExistentialValueInst, LookThrough)
33863419
CONSTANT_TRANSLATION(UncheckedEnumDataInst, LookThrough)
33873420
CONSTANT_TRANSLATION(TupleElementAddrInst, LookThrough)
33883421
CONSTANT_TRANSLATION(StructElementAddrInst, LookThrough)
@@ -3393,7 +3426,7 @@ CONSTANT_TRANSLATION(UncheckedTakeEnumDataAddrInst, LookThrough)
33933426
//
33943427

33953428
// These are treated as stores - meaning that they could write values into
3396-
// memory. The beahvior of this depends on whether the tgt addr is aliased,
3429+
// memory. The behavior of this depends on whether the tgt addr is aliased,
33973430
// but conservative behavior is to treat these as merges of the regions of
33983431
// the src value and tgt addr
33993432
CONSTANT_TRANSLATION(CopyAddrInst, Store)
@@ -3879,7 +3912,10 @@ PartitionOpTranslator::visitPointerToAddressInst(PointerToAddressInst *ptai) {
38793912

38803913
TranslationSemantics PartitionOpTranslator::visitUnconditionalCheckedCastInst(
38813914
UnconditionalCheckedCastInst *ucci) {
3882-
if (SILDynamicCastInst(ucci).isRCIdentityPreserving()) {
3915+
auto isolation = SILIsolationInfo::getConformanceIsolation(ucci);
3916+
3917+
if (!isolation &&
3918+
SILDynamicCastInst(ucci).isRCIdentityPreserving()) {
38833919
assert(isStaticallyLookThroughInst(ucci) && "Out of sync");
38843920
return TranslationSemantics::LookThrough;
38853921
}
@@ -3974,6 +4010,14 @@ PartitionOpTranslator::visitPartialApplyInst(PartialApplyInst *pai) {
39744010
return TranslationSemantics::Special;
39754011
}
39764012

4013+
TranslationSemantics
4014+
PartitionOpTranslator::visitInitExistentialValueInst(InitExistentialValueInst *ievi) {
4015+
if (isStaticallyLookThroughInst(ievi))
4016+
return TranslationSemantics::LookThrough;
4017+
4018+
return TranslationSemantics::Assign;
4019+
}
4020+
39774021
TranslationSemantics PartitionOpTranslator::visitCheckedCastAddrBranchInst(
39784022
CheckedCastAddrBranchInst *ccabi) {
39794023
assert(ccabi->getSuccessBB()->getNumArguments() <= 1);
@@ -3986,7 +4030,8 @@ TranslationSemantics PartitionOpTranslator::visitCheckedCastAddrBranchInst(
39864030
// is. For now just keep the current behavior. It is more conservative,
39874031
// but still correct.
39884032
translateSILMultiAssign(ArrayRef<SILValue>(),
3989-
makeOperandRefRange(ccabi->getAllOperands()));
4033+
makeOperandRefRange(ccabi->getAllOperands()),
4034+
SILIsolationInfo::getConformanceIsolation(ccabi));
39904035
return TranslationSemantics::Special;
39914036
}
39924037

0 commit comments

Comments
 (0)