Skip to content

Commit 604d403

Browse files
authored
Merge pull request #72710 from gottesmm/pr-feb1fe7a002640b270451f2187f141f4ed5da65d
[region-isolation] Propagate global actor-ness of functions/closures.
2 parents a4ac419 + 1cf4e99 commit 604d403

11 files changed

+368
-23
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ class SILFunction
348348
unsigned BlockListChangeIdx = 0;
349349

350350
/// The isolation of this function.
351-
std::optional<ActorIsolation> actorIsolation;
351+
ActorIsolation actorIsolation = ActorIsolation::forUnspecified();
352352

353353
/// The function's bare attribute. Bare means that the function is SIL-only
354354
/// and does not require debug info.
@@ -1374,9 +1374,7 @@ class SILFunction
13741374
actorIsolation = newActorIsolation;
13751375
}
13761376

1377-
std::optional<ActorIsolation> getActorIsolation() const {
1378-
return actorIsolation;
1379-
}
1377+
ActorIsolation getActorIsolation() const { return actorIsolation; }
13801378

13811379
//===--------------------------------------------------------------------===//
13821380
// Block List Access

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ class SILIsolationInfo {
208208
return SILIsolationInfo();
209209
}
210210

211+
static SILIsolationInfo getGlobalActorIsolated(Type globalActorType) {
212+
return getActorIsolated(ActorIsolation::forGlobalActor(globalActorType));
213+
}
214+
211215
static SILIsolationInfo getTaskIsolated(SILValue value) {
212216
return {Kind::Task, value};
213217
}
@@ -901,8 +905,8 @@ struct PartitionOpEvaluator {
901905
// our transferring operand. If so, we can squelch this.
902906
if (auto functionIsolation =
903907
transferringOp->getUser()->getFunction()->getActorIsolation()) {
904-
if (functionIsolation->isActorIsolated() &&
905-
SILIsolationInfo::getActorIsolated(*functionIsolation) ==
908+
if (functionIsolation.isActorIsolated() &&
909+
SILIsolationInfo::getActorIsolated(functionIsolation) ==
906910
SILIsolationInfo::get(transferringOp->getUser()))
907911
return;
908912
}

lib/SILGen/SILGen.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,13 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
725725
return IGM.getFunction(constant, NotForDefinition);
726726
});
727727

728+
// If we have global actor isolation for our constant, put the isolation onto
729+
// the function.
730+
if (auto isolation =
731+
getActorIsolationOfContext(constant.getInnermostDeclContext())) {
732+
F->setActorIsolation(isolation);
733+
}
734+
728735
assert(F && "SILFunction should have been defined");
729736

730737
emittedFunctions[constant] = F;
@@ -1217,9 +1224,12 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F,
12171224
if (F->getLoweredFunctionType()->isPolymorphic())
12181225
F->setGenericEnvironment(Types.getConstantGenericEnvironment(constant));
12191226

1220-
// Set the actor isolation of the function to its innermost decl context.
1221-
F->setActorIsolation(
1222-
getActorIsolationOfContext(constant.getInnermostDeclContext()));
1227+
// If we have global actor isolation for our constant, put the isolation onto
1228+
// the function.
1229+
if (auto isolation =
1230+
getActorIsolationOfContext(constant.getInnermostDeclContext())) {
1231+
F->setActorIsolation(isolation);
1232+
}
12231233

12241234
// Create a debug scope for the function using astNode as source location.
12251235
F->setDebugScope(new (M) SILDebugScope(Loc, F));

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,7 @@ class PartitionOpTranslator {
18481848
}
18491849

18501850
// If our self parameter was transferring, transfer it. Otherwise, just
1851-
// stick it in the non seld operand values array and run multiassign on
1851+
// stick it in the non self operand values array and run multiassign on
18521852
// it.
18531853
if (fas.hasSelfArgument()) {
18541854
auto &selfOperand = fas.getSelfArgumentOperand();
@@ -1864,6 +1864,12 @@ class PartitionOpTranslator {
18641864
}
18651865
}
18661866

1867+
// Add our callee to non-transferring parameters. This ensures that if it is
1868+
// actor isolated, that propagates into our results. This is especially
1869+
// important since our callee could be dynamically isolated and we cannot
1870+
// know that until we perform dataflow.
1871+
nonTransferringParameters.push_back(fas.getCallee());
1872+
18671873
SmallVector<SILValue, 8> applyResults;
18681874
getApplyResults(*fas, applyResults);
18691875

@@ -3283,12 +3289,59 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
32833289
}
32843290
}
32853291

3286-
// Then see if we have a sendable value. By default we assume values are not
3287-
// sendable.
32883292
if (auto *defInst = value.getDefiningInstruction()) {
3289-
// Though these values are technically non-Sendable, we can safely and
3290-
// consistently treat them as Sendable.
3291-
if (isa<ClassMethodInst, FunctionRefInst>(defInst)) {
3293+
// Treat function ref as either actor isolated or sendable.
3294+
if (auto *fri = dyn_cast<FunctionRefInst>(defInst)) {
3295+
auto isolation = fri->getReferencedFunction()->getActorIsolation();
3296+
if (isolation.isActorIsolated()) {
3297+
iter.first->getSecond().mergeIsolationRegionInfo(
3298+
SILIsolationInfo::getActorIsolated(isolation));
3299+
return {iter.first->first, iter.first->second};
3300+
}
3301+
3302+
// Otherwise, lets look at the AST and see if our function ref is from an
3303+
// autoclosure.
3304+
if (auto *autoclosure = fri->getLoc().getAsASTNode<AutoClosureExpr>()) {
3305+
if (auto *funcType = autoclosure->getType()->getAs<AnyFunctionType>()) {
3306+
if (funcType->hasGlobalActor()) {
3307+
if (funcType->hasGlobalActor()) {
3308+
iter.first->getSecond().mergeIsolationRegionInfo(
3309+
SILIsolationInfo::getActorIsolated(
3310+
ActorIsolation::forGlobalActor(
3311+
funcType->getGlobalActor())));
3312+
return {iter.first->first, iter.first->second};
3313+
}
3314+
}
3315+
3316+
if (auto *resultFType =
3317+
funcType->getResult()->getAs<AnyFunctionType>()) {
3318+
if (resultFType->hasGlobalActor()) {
3319+
iter.first->getSecond().mergeIsolationRegionInfo(
3320+
SILIsolationInfo::getActorIsolated(
3321+
ActorIsolation::forGlobalActor(
3322+
resultFType->getGlobalActor())));
3323+
return {iter.first->first, iter.first->second};
3324+
}
3325+
}
3326+
}
3327+
}
3328+
3329+
iter.first->getSecond().addFlag(TrackableValueFlag::isSendable);
3330+
return {iter.first->first, iter.first->second};
3331+
}
3332+
3333+
if (auto *cmi = dyn_cast<ClassMethodInst>(defInst)) {
3334+
if (auto *declRefExpr = cmi->getLoc().getAsASTNode<DeclRefExpr>()) {
3335+
// See if we are actor isolated. If so, treat this as non-Sendable so we
3336+
// propagate actor isolation.
3337+
if (auto isolation = getActorIsolation(declRefExpr->getDecl())) {
3338+
if (isolation.isActorIsolated()) {
3339+
iter.first->getSecond().mergeIsolationRegionInfo(
3340+
SILIsolationInfo::getActorIsolated(isolation));
3341+
return {iter.first->first, iter.first->second};
3342+
}
3343+
}
3344+
}
32923345
iter.first->getSecond().addFlag(TrackableValueFlag::isSendable);
32933346
return {iter.first->first, iter.first->second};
32943347
}
@@ -3379,6 +3432,71 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
33793432
}
33803433
}
33813434

3435+
// See if we have a convert function from a Sendable actor isolated function,
3436+
// we want to treat the result of the convert function as being actor isolated
3437+
// so that we cannot escape the value.
3438+
//
3439+
// NOTE: At this point, we already know that cfi's result is not sendable,
3440+
// since we would have exited above already.
3441+
if (auto *cfi = dyn_cast<ConvertFunctionInst>(iter.first->first.getValue())) {
3442+
SILValue operand = cfi->getOperand();
3443+
if (operand->getType().getAs<SILFunctionType>()->isSendable()) {
3444+
SILValue newValue = operand;
3445+
do {
3446+
operand = newValue;
3447+
3448+
newValue = lookThroughOwnershipInsts(operand);
3449+
if (auto *ttfi = dyn_cast<ThinToThickFunctionInst>(newValue)) {
3450+
newValue = ttfi->getOperand();
3451+
}
3452+
3453+
if (auto *cfi = dyn_cast<ConvertFunctionInst>(newValue)) {
3454+
newValue = cfi->getOperand();
3455+
}
3456+
3457+
if (auto *pai = dyn_cast<PartialApplyInst>(newValue)) {
3458+
newValue = pai->getCallee();
3459+
}
3460+
} while (newValue != operand);
3461+
3462+
if (auto *ai = dyn_cast<ApplyInst>(operand)) {
3463+
if (auto *callExpr = ai->getLoc().getAsASTNode<ApplyExpr>()) {
3464+
if (auto *callType = callExpr->getType()->getAs<AnyFunctionType>()) {
3465+
if (callType->hasGlobalActor()) {
3466+
iter.first->getSecond().mergeIsolationRegionInfo(
3467+
SILIsolationInfo::getGlobalActorIsolated(
3468+
callType->getGlobalActor()));
3469+
return {iter.first->first, iter.first->second};
3470+
}
3471+
}
3472+
}
3473+
}
3474+
3475+
if (auto *fri = dyn_cast<FunctionRefInst>(operand)) {
3476+
if (auto actorIsolation =
3477+
fri->getReferencedFunction()->getActorIsolation()) {
3478+
if (actorIsolation.isActorIsolated()) {
3479+
iter.first->getSecond().mergeIsolationRegionInfo(
3480+
SILIsolationInfo::getActorIsolated(actorIsolation));
3481+
return {iter.first->first, iter.first->second};
3482+
}
3483+
}
3484+
3485+
// See if the function ref statically is known to have actor isolation.
3486+
//
3487+
// TODO: We should make it so that the closure constructed has actor
3488+
// isolation.
3489+
if (auto value = tryToTrackValue(fri)) {
3490+
auto isolation = value->getIsolationRegionInfo();
3491+
if (isolation.isActorIsolated()) {
3492+
iter.first->getSecond().mergeIsolationRegionInfo(isolation);
3493+
return {iter.first->first, iter.first->second};
3494+
}
3495+
}
3496+
}
3497+
}
3498+
}
3499+
33823500
return {iter.first->first, iter.first->second};
33833501
}
33843502

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,13 +1152,18 @@ class TransferNonTransferrableDiagnosticInferrer {
11521152
bool run();
11531153

11541154
private:
1155-
bool initForIsolatedPartialApply(Operand *op, AbstractClosureExpr *ace);
1155+
/// \p actualCallerIsolation is used to override the caller isolation we use
1156+
/// when emitting the error if the closure would have the incorrect one.
1157+
bool initForIsolatedPartialApply(
1158+
Operand *op, AbstractClosureExpr *ace,
1159+
std::optional<ActorIsolation> actualCallerIsolation = {});
11561160
};
11571161

11581162
} // namespace
11591163

11601164
bool TransferNonTransferrableDiagnosticInferrer::initForIsolatedPartialApply(
1161-
Operand *op, AbstractClosureExpr *ace) {
1165+
Operand *op, AbstractClosureExpr *ace,
1166+
std::optional<ActorIsolation> actualCallerIsolation) {
11621167
SmallVector<std::tuple<CapturedValue, unsigned, ApplyIsolationCrossing>, 8>
11631168
foundCapturedIsolationCrossing;
11641169
ace->getIsolationCrossing(foundCapturedIsolationCrossing);
@@ -1169,8 +1174,15 @@ bool TransferNonTransferrableDiagnosticInferrer::initForIsolatedPartialApply(
11691174
for (auto &p : foundCapturedIsolationCrossing) {
11701175
if (std::get<1>(p) == opIndex) {
11711176
auto loc = RegularLocation(std::get<0>(p).getLoc());
1177+
auto crossing = std::get<2>(p);
1178+
auto declIsolation = crossing.getCallerIsolation();
1179+
auto closureIsolation = crossing.getCalleeIsolation();
1180+
if (!bool(declIsolation) && actualCallerIsolation) {
1181+
declIsolation = *actualCallerIsolation;
1182+
}
11721183
diagnosticEmitter.emitNamedFunctionArgumentClosure(
1173-
loc, std::get<0>(p).getDecl()->getBaseIdentifier(), std::get<2>(p));
1184+
loc, std::get<0>(p).getDecl()->getBaseIdentifier(),
1185+
ApplyIsolationCrossing(declIsolation, closureIsolation));
11741186
return true;
11751187
}
11761188
}
@@ -1220,6 +1232,15 @@ bool TransferNonTransferrableDiagnosticInferrer::run() {
12201232
}
12211233
assert(isolation && "Expected non-null");
12221234

1235+
// Then if we are calling a closure expr. If so, we should use the loc of
1236+
// the closure.
1237+
if (auto *closureExpr =
1238+
dyn_cast<AbstractClosureExpr>(sourceApply->getFn())) {
1239+
initForIsolatedPartialApply(op, closureExpr,
1240+
isolation->getCallerIsolation());
1241+
return true;
1242+
}
1243+
12231244
// See if we can infer a name from the value.
12241245
SmallString<64> resultingName;
12251246
if (auto name = inferNameFromValue(op->get())) {

lib/SILOptimizer/Utils/PartitionUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ SILIsolationInfo SILIsolationInfo::get(SILFunctionArgument *arg) {
9191
// should be marked as actor isolated.
9292
if (auto *self = arg->getFunction()->maybeGetSelfArgument()) {
9393
if (auto functionIsolation = arg->getFunction()->getActorIsolation()) {
94-
if (functionIsolation->isActorIsolated()) {
94+
if (functionIsolation.isActorIsolated()) {
9595
if (auto *nomDecl = self->getType().getNominalOrBoundGenericNominal()) {
9696
if (auto isolationInfo =
9797
SILIsolationInfo::getActorIsolated(nomDecl)) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
@MainActor public func mainActorFunction() {}

test/Concurrency/isolated_captures.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ class NotSendable {
3939
let ns = NotSendable(x: 0)
4040
MyActor.ns = ns
4141

42-
// expected-region-isolation-warning @+2 {{transferring 'ns' may cause a race}}
43-
// expected-region-isolation-note @+1 {{transferring global actor 'MyActor'-isolated 'ns' to global actor 'YourActor'-isolated callee could cause races between global actor 'YourActor'-isolated and global actor 'MyActor'-isolated uses}}
4442
await { @YourActor in
43+
// expected-region-isolation-warning @+3 {{transferring 'ns' may cause a race}}
44+
// expected-region-isolation-note @+2 {{global actor 'MyActor'-isolated 'ns' is captured by a global actor 'YourActor'-isolated closure. global actor 'YourActor'-isolated uses in closure may race against later global actor 'MyActor'-isolated uses}}
4545
// expected-complete-warning@+1 {{capture of 'ns' with non-sendable type 'NotSendable' in an isolated closure; this is an error in the Swift 6 language mode}}
4646
YourActor.ns = ns
4747
}()
@@ -61,8 +61,9 @@ class NotSendable {
6161
let ns = NotSendable(x: 0)
6262
ns.stash()
6363

64-
// FIXME: Region isolation should diagnose this (https://github.com/apple/swift/issues/71533)
6564
await { @YourActor in
65+
// expected-region-isolation-warning @+3 {{transferring 'ns' may cause a race}}
66+
// expected-region-isolation-note @+2 {{global actor 'MyActor'-isolated 'ns' is captured by a global actor 'YourActor'-isolated closure. global actor 'YourActor'-isolated uses in closure may race against later global actor 'MyActor'-isolated uses}}
6667
// expected-complete-warning@+1 {{capture of 'ns' with non-sendable type 'NotSendable' in an isolated closure; this is an error in the Swift 6 language mode}}
6768
YourActor.ns = ns
6869
}()
@@ -81,8 +82,9 @@ class NotSendable {
8182
@MyActor func exhibitRace3() async {
8283
let ns = NotSendable()
8384

84-
// FIXME: Region isolation should diagnose this (https://github.com/apple/swift/issues/71533)
8585
await { @YourActor in
86+
// expected-region-isolation-warning @+3 {{transferring 'ns' may cause a race}}
87+
// expected-region-isolation-note @+2 {{global actor 'MyActor'-isolated 'ns' is captured by a global actor 'YourActor'-isolated closure. global actor 'YourActor'-isolated uses in closure may race against later global actor 'MyActor'-isolated uses}}
8688
// expected-complete-warning@+1 {{capture of 'ns' with non-sendable type 'NotSendable' in an isolated closure; this is an error in the Swift 6 language mode}}
8789
YourActor.ns = ns
8890
}()

0 commit comments

Comments
 (0)