Skip to content

Commit 486b7ce

Browse files
authored
Merge pull request #70394 from gottesmm/pr-f328e7893b344f5252d43a4358baf49fb4589abe
[region-isolation] Add the ability to write SIL tests for actor isolation
2 parents b6137dd + a43e5e6 commit 486b7ce

23 files changed

+1194
-689
lines changed

docs/SIL.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5927,11 +5927,28 @@ Function Application
59275927
These instructions call functions or wrap them in partial application or
59285928
specialization thunks.
59295929

5930+
In the following we allow for `apply`_, `begin_apply`_, and `try_apply`_ to have
5931+
a callee or caller actor isolation attached to them::
5932+
5933+
sil-actor-isolation ::= unspecified
5934+
::= actor_instance
5935+
::= nonisolated
5936+
::= nonisolated_unsafe
5937+
::= global_actor
5938+
::= global_actor_unsafe
5939+
5940+
sil-actor-isolation-callee ::= [callee_isolation=sil-actor-isolation]
5941+
sil-actor-isolation-caller ::= [caller_isolation=sil-actor-isolation]
5942+
5943+
These can be used to write test cases with actor isolation using these
5944+
instructions and is not intended to be used in SILGen today.
5945+
59305946
apply
59315947
`````
59325948
::
59335949

5934-
sil-instruction ::= 'apply' '[nothrow]'? sil-value
5950+
sil-instruction ::= 'apply' '[nothrow]'? sil-actor-isolation-callee?
5951+
sil-actor-isolation-caller? sil-value
59355952
sil-apply-substitution-list?
59365953
'(' (sil-value (',' sil-value)*)? ')'
59375954
':' sil-type

include/swift/AST/ActorIsolation.h

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
#include "swift/AST/Type.h"
2020
#include "llvm/ADT/Hashing.h"
21+
#include "llvm/ADT/StringSwitch.h"
22+
#include "llvm/Support/Debug.h"
23+
#include "llvm/Support/raw_ostream.h"
2124

2225
namespace llvm {
2326
class raw_ostream;
@@ -80,21 +83,25 @@ class ActorIsolation {
8083
};
8184
unsigned kind : 3;
8285
unsigned isolatedByPreconcurrency : 1;
83-
unsigned parameterIndex : 28;
86+
87+
/// Set to true if this was parsed from SIL.
88+
unsigned silParsed : 1;
89+
90+
unsigned parameterIndex : 27;
8491

8592
ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex);
8693

8794
ActorIsolation(Kind kind, VarDecl *capturedActor);
8895

8996
ActorIsolation(Kind kind, Type globalActor)
9097
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false),
91-
parameterIndex(0) { }
98+
silParsed(false), parameterIndex(0) {}
9299

93100
public:
94101
// No-argument constructor needed for DenseMap use in PostfixCompletion.cpp
95-
explicit ActorIsolation(Kind kind = Unspecified)
102+
explicit ActorIsolation(Kind kind = Unspecified, bool isSILParsed = false)
96103
: pointer(nullptr), kind(kind), isolatedByPreconcurrency(false),
97-
parameterIndex(0) { }
104+
silParsed(isSILParsed), parameterIndex(0) {}
98105

99106
static ActorIsolation forUnspecified() {
100107
return ActorIsolation(Unspecified, nullptr);
@@ -122,6 +129,27 @@ class ActorIsolation {
122129
unsafe ? GlobalActorUnsafe : GlobalActor, globalActor);
123130
}
124131

132+
static std::optional<ActorIsolation> forSILString(StringRef string) {
133+
auto kind =
134+
llvm::StringSwitch<std::optional<ActorIsolation::Kind>>(string)
135+
.Case("unspecified",
136+
std::optional<ActorIsolation>(ActorIsolation::Unspecified))
137+
.Case("actor_instance",
138+
std::optional<ActorIsolation>(ActorIsolation::ActorInstance))
139+
.Case("nonisolated",
140+
std::optional<ActorIsolation>(ActorIsolation::Nonisolated))
141+
.Case("nonisolated_unsafe", std::optional<ActorIsolation>(
142+
ActorIsolation::NonisolatedUnsafe))
143+
.Case("global_actor",
144+
std::optional<ActorIsolation>(ActorIsolation::GlobalActor))
145+
.Case("global_actor_unsafe", std::optional<ActorIsolation>(
146+
ActorIsolation::GlobalActorUnsafe))
147+
.Default(std::nullopt);
148+
if (kind == std::nullopt)
149+
return std::nullopt;
150+
return ActorIsolation(*kind, true /*is sil parsed*/);
151+
}
152+
125153
Kind getKind() const { return (Kind)kind; }
126154

127155
operator Kind() const { return getKind(); }
@@ -140,6 +168,8 @@ class ActorIsolation {
140168
return parameterIndex;
141169
}
142170

171+
bool isSILParsed() const { return silParsed; }
172+
143173
bool isActorIsolated() const {
144174
switch (getKind()) {
145175
case ActorInstance:
@@ -168,6 +198,10 @@ class ActorIsolation {
168198

169199
Type getGlobalActor() const {
170200
assert(isGlobalActor());
201+
202+
if (silParsed)
203+
return Type();
204+
171205
return globalActor;
172206
}
173207

@@ -222,6 +256,32 @@ class ActorIsolation {
222256
state.kind, state.pointer, state.isolatedByPreconcurrency,
223257
state.parameterIndex);
224258
}
259+
260+
void print(llvm::raw_ostream &os) const {
261+
switch (getKind()) {
262+
case Unspecified:
263+
os << "unspecified";
264+
return;
265+
case ActorInstance:
266+
os << "actor_instance";
267+
return;
268+
case Nonisolated:
269+
os << "nonisolated";
270+
return;
271+
case NonisolatedUnsafe:
272+
os << "nonisolated_unsafe";
273+
return;
274+
case GlobalActor:
275+
os << "global_actor";
276+
return;
277+
case GlobalActorUnsafe:
278+
os << "global_actor_unsafe";
279+
return;
280+
}
281+
llvm_unreachable("Covered switch isn't covered?!");
282+
}
283+
284+
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
225285
};
226286

227287
/// Determine how the given value declaration is isolated.
@@ -249,6 +309,51 @@ bool usesFlowSensitiveIsolation(AbstractFunctionDecl const *fn);
249309

250310
void simple_display(llvm::raw_ostream &out, const ActorIsolation &state);
251311

312+
// ApplyIsolationCrossing records the source and target of an isolation crossing
313+
// within an ApplyExpr. In particular, it stores the isolation of the caller
314+
// and the callee of the ApplyExpr, to be used for inserting implicit actor
315+
// hops for implicitly async functions and to be used for diagnosing potential
316+
// data races that could arise when non-Sendable values are passed to calls
317+
// that cross isolation domains.
318+
struct ApplyIsolationCrossing {
319+
ActorIsolation CallerIsolation;
320+
ActorIsolation CalleeIsolation;
321+
322+
ApplyIsolationCrossing()
323+
: CallerIsolation(ActorIsolation::forUnspecified()),
324+
CalleeIsolation(ActorIsolation::forUnspecified()) {}
325+
326+
ApplyIsolationCrossing(ActorIsolation CallerIsolation,
327+
ActorIsolation CalleeIsolation)
328+
: CallerIsolation(CallerIsolation), CalleeIsolation(CalleeIsolation) {}
329+
330+
// If the callee is not actor isolated, then this crossing exits isolation.
331+
// This method returns true iff this crossing exits isolation.
332+
bool exitsIsolation() const { return !CalleeIsolation.isActorIsolated(); }
333+
334+
// Whether to use the isolation of the caller or callee for generating
335+
// informative diagnostics depends on whether this crossing is an exit.
336+
// In particular, we tend to use the callee isolation for diagnostics,
337+
// but if this crossing is an exit from isolation then the callee isolation
338+
// is not very informative, so we use the caller isolation instead.
339+
ActorIsolation getDiagnoseIsolation() const {
340+
return exitsIsolation() ? CallerIsolation : CalleeIsolation;
341+
}
342+
343+
ActorIsolation getCallerIsolation() const { return CallerIsolation; }
344+
ActorIsolation getCalleeIsolation() const { return CalleeIsolation; }
345+
};
346+
252347
} // end namespace swift
253348

349+
namespace llvm {
350+
351+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
352+
const swift::ActorIsolation &other) {
353+
other.print(os);
354+
return os;
355+
}
356+
357+
} // namespace llvm
358+
254359
#endif /* SWIFT_AST_ACTORISOLATIONSTATE_H */

include/swift/AST/Expr.h

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,41 +4639,6 @@ class DefaultArgumentExpr final : public Expr {
46394639
}
46404640
};
46414641

4642-
// ApplyIsolationCrossing records the source and target of an isolation crossing
4643-
// within an ApplyExpr. In particular, it stores the isolation of the caller
4644-
// and the callee of the ApplyExpr, to be used for inserting implicit actor
4645-
// hops for implicitly async functions and to be used for diagnosing potential
4646-
// data races that could arise when non-Sendable values are passed to calls
4647-
// that cross isolation domains.
4648-
struct ApplyIsolationCrossing {
4649-
ActorIsolation CallerIsolation;
4650-
ActorIsolation CalleeIsolation;
4651-
4652-
ApplyIsolationCrossing()
4653-
: CallerIsolation(ActorIsolation::forUnspecified()),
4654-
CalleeIsolation(ActorIsolation::forUnspecified()) {}
4655-
4656-
ApplyIsolationCrossing(ActorIsolation CallerIsolation,
4657-
ActorIsolation CalleeIsolation)
4658-
: CallerIsolation(CallerIsolation), CalleeIsolation(CalleeIsolation) {}
4659-
4660-
// If the callee is not actor isolated, then this crossing exits isolation.
4661-
// This method returns true iff this crossing exits isolation.
4662-
bool exitsIsolation() const { return !CalleeIsolation.isActorIsolated(); }
4663-
4664-
// Whether to use the isolation of the caller or callee for generating
4665-
// informative diagnostics depends on whether this crossing is an exit.
4666-
// In particular, we tend to use the callee isolation for diagnostics,
4667-
// but if this crossing is an exit from isolation then the callee isolation
4668-
// is not very informative, so we use the caller isolation instead.
4669-
ActorIsolation getDiagnoseIsolation() const {
4670-
return exitsIsolation() ? CallerIsolation : CalleeIsolation;
4671-
}
4672-
4673-
ActorIsolation getCallerIsolation() const { return CallerIsolation; }
4674-
ActorIsolation getCalleeIsolation() const {return CalleeIsolation; }
4675-
};
4676-
46774642
/// ApplyExpr - Superclass of various function calls, which apply an argument to
46784643
/// a function to get a result.
46794644
class ApplyExpr : public Expr {

include/swift/SIL/ApplySite.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,17 @@ class FullApplySite : public ApplySite {
778778
&& (getCalleeArgIndex(op) < getNumIndirectSILResults() + getNumIndirectSILErrorResults());
779779
}
780780

781+
std::optional<ApplyIsolationCrossing> getIsolationCrossing() const {
782+
switch (getKind()) {
783+
case FullApplySiteKind::ApplyInst:
784+
return cast<ApplyInst>(**this)->getIsolationCrossing();
785+
case FullApplySiteKind::TryApplyInst:
786+
return cast<TryApplyInst>(**this)->getIsolationCrossing();
787+
case FullApplySiteKind::BeginApplyInst:
788+
return cast<BeginApplyInst>(**this)->getIsolationCrossing();
789+
}
790+
}
791+
781792
static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }
782793

783794
static bool classof(const SILInstruction *inst) {

include/swift/SIL/SILBuilder.h

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -523,23 +523,24 @@ class SILBuilder {
523523
}
524524

525525
ApplyInst *createApply(
526-
SILLocation Loc, SILValue Fn, SubstitutionMap Subs,
527-
ArrayRef<SILValue> Args,
528-
ApplyOptions options,
529-
const GenericSpecializationInformation *SpecializationInfo = nullptr) {
530-
return insert(ApplyInst::create(getSILDebugLocation(Loc), Fn, Subs, Args,
531-
options, C.silConv, *F,
532-
SpecializationInfo));
526+
SILLocation loc, SILValue callee, SubstitutionMap subs,
527+
ArrayRef<SILValue> args, ApplyOptions options,
528+
const GenericSpecializationInformation *specializationInfo = nullptr,
529+
std::optional<ApplyIsolationCrossing> isolationCrossing = std::nullopt) {
530+
return insert(ApplyInst::create(getSILDebugLocation(loc), callee, subs,
531+
args, options, C.silConv, *F,
532+
specializationInfo, isolationCrossing));
533533
}
534534

535535
TryApplyInst *createTryApply(
536-
SILLocation Loc, SILValue fn, SubstitutionMap subs,
536+
SILLocation loc, SILValue callee, SubstitutionMap subs,
537537
ArrayRef<SILValue> args, SILBasicBlock *normalBB, SILBasicBlock *errorBB,
538538
ApplyOptions options = ApplyOptions(),
539-
const GenericSpecializationInformation *SpecializationInfo = nullptr) {
539+
const GenericSpecializationInformation *specializationInfo = nullptr,
540+
std::optional<ApplyIsolationCrossing> isolationCrossing = std::nullopt) {
540541
return insertTerminator(TryApplyInst::create(
541-
getSILDebugLocation(Loc), fn, subs, args, normalBB, errorBB,
542-
options, *F, SpecializationInfo));
542+
getSILDebugLocation(loc), callee, subs, args, normalBB, errorBB,
543+
options, *F, specializationInfo, isolationCrossing));
543544
}
544545

545546
PartialApplyInst *createPartialApply(
@@ -561,12 +562,13 @@ class SILBuilder {
561562
}
562563

563564
BeginApplyInst *createBeginApply(
564-
SILLocation Loc, SILValue Fn, SubstitutionMap Subs,
565-
ArrayRef<SILValue> Args, ApplyOptions options = ApplyOptions(),
566-
const GenericSpecializationInformation *SpecializationInfo = nullptr) {
565+
SILLocation loc, SILValue callee, SubstitutionMap subs,
566+
ArrayRef<SILValue> args, ApplyOptions options = ApplyOptions(),
567+
const GenericSpecializationInformation *specializationInfo = nullptr,
568+
std::optional<ApplyIsolationCrossing> isolationCrossing = std::nullopt) {
567569
return insert(BeginApplyInst::create(
568-
getSILDebugLocation(Loc), Fn, Subs, Args, options, C.silConv, *F,
569-
SpecializationInfo));
570+
getSILDebugLocation(loc), callee, subs, args, options, C.silConv, *F,
571+
specializationInfo, isolationCrossing));
570572
}
571573

572574
AbortApplyInst *createAbortApply(SILLocation loc, SILValue beginApply) {

0 commit comments

Comments
 (0)