Skip to content

Commit 7e601ad

Browse files
committed
Make sure that we capture the isolation of a defer function that
implicitly uses the isolation.
1 parent 59bc23f commit 7e601ad

File tree

2 files changed

+89
-8
lines changed

2 files changed

+89
-8
lines changed

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class FindCapturedVars : public ASTWalker {
6666
DeclContext *CurDC;
6767
bool NoEscape, ObjC;
6868
bool HasGenericParamCaptures;
69+
bool HasUsesOfCurrentIsolation = false;
6970

7071
public:
7172
FindCapturedVars(SourceLoc CaptureLoc,
@@ -91,6 +92,10 @@ class FindCapturedVars : public ASTWalker {
9192
CapturedTypes);
9293
}
9394

95+
bool hasUsesOfCurrentIsolation() const {
96+
return HasUsesOfCurrentIsolation;
97+
}
98+
9499
bool hasGenericParamCaptures() const {
95100
return HasGenericParamCaptures;
96101
}
@@ -694,6 +699,31 @@ class FindCapturedVars : public ASTWalker {
694699
checkType(typeValue->getParamType(), E->getLoc());
695700
}
696701

702+
// Record that we saw an #isolation expression that hasn't been filled in.
703+
if (auto currentIsolation = dyn_cast<CurrentContextIsolationExpr>(E)) {
704+
if (!currentIsolation->getActor())
705+
HasUsesOfCurrentIsolation = true;
706+
}
707+
708+
// Record that we saw an apply of a function with caller isolation.
709+
if (auto apply = dyn_cast<ApplyExpr>(E)) {
710+
if (auto type = apply->getFn()->getType()) {
711+
if (auto fnType = type->getAs<AnyFunctionType>();
712+
fnType && fnType->getIsolation().isNonIsolatedCaller()) {
713+
HasUsesOfCurrentIsolation = true;
714+
}
715+
}
716+
}
717+
718+
// Look into caller-side default arguments.
719+
if (auto defArg = dyn_cast<DefaultArgumentExpr>(E)) {
720+
if (defArg->isCallerSide()) {
721+
if (auto callerSideExpr = defArg->getCallerSideDefaultExpr()) {
722+
callerSideExpr->walk(*this);
723+
}
724+
}
725+
}
726+
697727
return Action::Continue(E);
698728
}
699729

@@ -748,13 +778,18 @@ class FindCapturedVars : public ASTWalker {
748778
/// Given that a local function is isolated to the given var, should we
749779
/// force a capture of the var?
750780
static bool shouldCaptureIsolationInLocalFunc(AbstractFunctionDecl *AFD,
751-
VarDecl *var) {
781+
VarDecl *var,
782+
bool hasUsesOfCurrentIsolation) {
752783
assert(isa<ParamDecl>(var));
753784

754785
// Don't try to capture an isolated parameter of the function itself.
755786
if (var->getDeclContext() == AFD)
756787
return false;
757788

789+
// Force capture if we have uses of the isolation in the function body.
790+
if (hasUsesOfCurrentIsolation)
791+
return true;
792+
758793
// We only *need* to force a capture of the isolation in an async function
759794
// (in which case it's needed for executor switching) or if we're in the
760795
// mode that forces an executor check in all synchronous functions. But
@@ -793,7 +828,8 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator,
793828
auto actorIsolation = getActorIsolation(AFD);
794829
if (actorIsolation.getKind() == ActorIsolation::ActorInstance) {
795830
if (auto *var = actorIsolation.getActorInstance()) {
796-
if (shouldCaptureIsolationInLocalFunc(AFD, var))
831+
if (shouldCaptureIsolationInLocalFunc(AFD, var,
832+
finder.hasUsesOfCurrentIsolation()))
797833
finder.addCapture(CapturedValue(var, 0, AFD->getLoc()));
798834
}
799835
}

test/Concurrency/isolated_nonsending_isolation_macro_sil.swift renamed to test/Concurrency/isolation_macro_sil.swift

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -parse-as-library -emit-sil %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -parse-as-library -emit-sil %s -module-name test | %FileCheck %s
22

33
// REQUIRES: concurrency
44

@@ -9,17 +9,19 @@ nonisolated(nonsending) func nonisolatedNonsending() async {
99

1010
func take(iso: (any Actor)?) {}
1111

12+
func takeDefaulted(iso: isolated (any Actor)? = #isolation) {}
13+
1214
// CHECK-LABEL: // nonisolatedNonsending()
1315
// CHECK-NEXT: // Isolation: caller_isolation_inheriting
14-
// CHECK-NEXT: sil hidden @$s39isolated_nonsending_isolation_macro_sil21nonisolatedNonsendingyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
16+
// CHECK-NEXT: sil hidden @$s4test21nonisolatedNonsendingyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
1517
// CHECK: bb0([[ACTOR:%.*]] : $Optional<any Actor>):
1618
// CHECK: hop_to_executor [[ACTOR]]
1719
// CHECK: retain_value [[ACTOR]]
1820
// CHECK: debug_value [[ACTOR]], let, name "iso"
19-
// CHECK: [[FUNC:%.*]] = function_ref @$s39isolated_nonsending_isolation_macro_sil4take3isoyScA_pSg_tF : $@convention(thin) (@guaranteed Optional<any Actor>) -> ()
21+
// CHECK: [[FUNC:%.*]] = function_ref @$s4test4take3isoyScA_pSg_tF : $@convention(thin) (@guaranteed Optional<any Actor>) -> ()
2022
// CHECK: apply [[FUNC]]([[ACTOR]]) : $@convention(thin) (@guaranteed Optional<any Actor>) -> ()
2123
// CHECK: release_value [[ACTOR]]
22-
// CHECK: } // end sil function '$s39isolated_nonsending_isolation_macro_sil21nonisolatedNonsendingyyYaF'
24+
// CHECK: } // end sil function '$s4test21nonisolatedNonsendingyyYaF'
2325

2426
// Check that we emit #isolation correctly in closures.
2527
// CHECK-LABEL: // closure #1 in containsClosure()
@@ -34,6 +36,49 @@ func containsClosure() {
3436
}
3537
}
3638

39+
// Check that we capture variables as necessary to emit #isolation
40+
// correctly in defer bodies.
41+
func deferWithIsolatedParam(_ iso: isolated (any Actor)) {
42+
defer {
43+
take(iso: #isolation)
44+
}
45+
do {}
46+
}
47+
// CHECK-LABEL: sil hidden @$s4test22deferWithIsolatedParamyyScA_pYiF :
48+
// CHECK: bb0(%0 : $any Actor)
49+
// CHECK: [[DEFER:%.*]] = function_ref @$s4test22deferWithIsolatedParamyyScA_pYiF6$deferL_yyF :
50+
// CHECK-NEXT: apply [[DEFER]](%0)
51+
52+
// CHECK-LABEL: sil private @$s4test22deferWithIsolatedParamyyScA_pYiF6$deferL_yyF :
53+
// CHECK: bb0(%0 : @closureCapture $any Actor):
54+
// CHECK: [[T0:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, %0
55+
// CHECK: [[FN:%.*]] = function_ref @$s4test4take3isoyScA_pSg_tF :
56+
// CHECK-NEXT: apply [[FN]]([[T0]])
57+
58+
// Check that that happens even with uses in caller-side default
59+
// arguments, which capture analysis was not previously walking into.
60+
func deferWithIsolatedParam_defaultedUse(_ iso: isolated (any Actor)) {
61+
defer {
62+
takeDefaulted()
63+
}
64+
do {}
65+
}
66+
67+
// CHECK-LABEL: sil hidden @$s4test35deferWithIsolatedParam_defaultedUseyyScA_pYiF :
68+
// CHECK: bb0(%0 : $any Actor):
69+
// CHECK: [[DEFER:%.*]] = function_ref @$s4test35deferWithIsolatedParam_defaultedUseyyScA_pYiF6$deferL_yyF :
70+
// CHECK-NEXT: apply [[DEFER]](%0)
71+
72+
// CHECK-LABEL: sil private @$s4test35deferWithIsolatedParam_defaultedUseyyScA_pYiF6$deferL_yyF :
73+
// CHECK: bb0(%0 : @closureCapture $any Actor):
74+
// CHECK: [[T0:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, %0
75+
// CHECK: [[FN:%.*]] = function_ref @$s4test13takeDefaulted3isoyScA_pSgYi_tF :
76+
// CHECK-NEXT: apply [[FN]]([[T0]])
77+
78+
// TODO: we can't currently call nonisolated(nonsending) functions in
79+
// defer bodies because they have to be async, but that should be
80+
// tested here as well.
81+
3782
// Check that we emit #isolation correctly in defer bodies.
3883
nonisolated(nonsending)
3984
func hasDefer() async {
@@ -42,7 +87,7 @@ func hasDefer() async {
4287
}
4388
do {}
4489
}
45-
// CHECK-LABEL: sil hidden @$s39isolated_nonsending_isolation_macro_sil8hasDeferyyYaF :
90+
// CHECK-LABEL: sil hidden @$s4test8hasDeferyyYaF :
4691
// CHECK: bb0(%0 : $Optional<any Actor>):
4792
// CHECK: // function_ref $defer
4893
// CHECK-NEXT: [[DEFER:%.*]] = function_ref
@@ -67,7 +112,7 @@ func hasNestedDefer() async {
67112
do {}
68113
}
69114

70-
// CHECK-LABEL: sil hidden @$s39isolated_nonsending_isolation_macro_sil14hasNestedDeferyyYaF :
115+
// CHECK-LABEL: sil hidden @$s4test14hasNestedDeferyyYaF :
71116
// CHECK: bb0(%0 : $Optional<any Actor>):
72117
// CHECK: // function_ref $defer #1 () in hasNestedDefer()
73118
// CHECK-NEXT: [[DEFER:%.*]] = function_ref

0 commit comments

Comments
 (0)