Skip to content

Commit 787cca9

Browse files
committed
Rewrite the type of nonisolated(nonsending) closures.
The constraint solver does not reliably give closures a function type that includes `nonisolated(noncaller)`, even when the immediate context requires a conversion to such a type. We were trying to work around this in SILGen, but the peephole only kicked in if the types matched exactly, so a contextual conversion that e.g. added `throws` was still emitting the closure as `@concurrent`, which is of course the wrong semantics. It's relatively easy to avoid all this by just rewriting the closure's type to include `nonisolated(nonsending)` at a point where we can reliably decide that, and then SILGen doesn't have to peephole anything for correctness. Fixes rdar://155313349
1 parent 73209e1 commit 787cca9

File tree

4 files changed

+70
-90
lines changed

4 files changed

+70
-90
lines changed

lib/SILGen/SILGenExpr.cpp

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,6 @@ namespace {
498498
emitFunctionCvtFromExecutionCallerToGlobalActor(FunctionConversionExpr *E,
499499
SGFContext C);
500500

501-
RValue emitFunctionCvtForNonisolatedNonsendingClosureExpr(
502-
FunctionConversionExpr *E, SGFContext C);
503-
504501
RValue visitActorIsolationErasureExpr(ActorIsolationErasureExpr *E,
505502
SGFContext C);
506503
RValue visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E,
@@ -2037,44 +2034,6 @@ RValueEmitter::emitFunctionCvtToExecutionCaller(FunctionConversionExpr *e,
20372034
return RValue(SGF, e, destType, result);
20382035
}
20392036

2040-
RValue RValueEmitter::emitFunctionCvtForNonisolatedNonsendingClosureExpr(
2041-
FunctionConversionExpr *E, SGFContext C) {
2042-
// The specific AST pattern for this looks as follows:
2043-
//
2044-
// (function_conversion_expr type="nonisolated(nonsending) () async -> Void"
2045-
// (closure_expr type="() async -> ()" isolated_to_caller_isolation))
2046-
CanAnyFunctionType destType =
2047-
cast<FunctionType>(E->getType()->getCanonicalType());
2048-
auto subExpr = E->getSubExpr()->getSemanticsProvidingExpr();
2049-
2050-
// If we do not have a closure or if that closure is not caller isolation
2051-
// inheriting, bail.
2052-
auto *closureExpr = dyn_cast<ClosureExpr>(subExpr);
2053-
if (!closureExpr ||
2054-
!closureExpr->getActorIsolation().isCallerIsolationInheriting())
2055-
return RValue();
2056-
2057-
// Then grab our closure type... make sure it is non isolated and then make
2058-
// sure it is the same as our destType but with nonisolated.
2059-
CanAnyFunctionType closureType =
2060-
cast<FunctionType>(closureExpr->getType()->getCanonicalType());
2061-
if (!closureType->getIsolation().isNonIsolated() ||
2062-
closureType !=
2063-
destType->withIsolation(FunctionTypeIsolation::forNonIsolated())
2064-
->getCanonicalType())
2065-
return RValue();
2066-
2067-
// NOTE: This is a partial inline of getClosureTypeInfo. We do this so we have
2068-
// more control and make this change less viral in the compiler for 6.2.
2069-
auto newExtInfo = closureType->getExtInfo().withIsolation(
2070-
FunctionTypeIsolation::forNonIsolatedCaller());
2071-
closureType = closureType.withExtInfo(newExtInfo);
2072-
auto info = SGF.getFunctionTypeInfo(closureType);
2073-
2074-
auto closure = emitClosureReference(closureExpr, info);
2075-
return RValue(SGF, closureExpr, destType, closure);
2076-
}
2077-
20782037
RValue RValueEmitter::emitFunctionCvtFromExecutionCallerToGlobalActor(
20792038
FunctionConversionExpr *e, SGFContext C) {
20802039
// We are pattern matching a conversion sequence like the following:
@@ -2187,26 +2146,7 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e,
21872146
// convention.
21882147
auto subExpr = e->getSubExpr()->getSemanticsProvidingExpr();
21892148

2190-
// Before we go any further into emitting the convert function expr, see if
2191-
// our SubExpr is a ClosureExpr with the exact same type as our
2192-
// FunctionConversionExpr except with the FunctionConversionExpr adding
2193-
// nonisolated(nonsending). Then see if the ClosureExpr itself (even though it
2194-
// is not nonisolated(nonsending) typed is considered to have
2195-
// nonisolated(nonsending) isolation. In such a case, emit the closure
2196-
// directly. We are going to handle it especially in closure emission to work
2197-
// around the missing information in the type.
2198-
//
2199-
// DISCUSSION: We need to do this here since in the Expression TypeChecker we
2200-
// do not have access to capture information when we would normally want to
2201-
// mark the closure type as being nonisolated(nonsending). As a result, we
2202-
// cannot know if the nonisolated(nonsending) should be overridden by for
2203-
// example an actor that is captured by the closure. So to work around this in
2204-
// Sema, we still mark the ClosureExpr as having the appropriate isolation
2205-
// even though its type does not have it... and then we work around this here
2206-
// and also in getClosureTypeInfo.
2207-
if (destType->getIsolation().isNonIsolatedCaller())
2208-
if (auto rv = emitFunctionCvtForNonisolatedNonsendingClosureExpr(e, C))
2209-
return rv;
2149+
22102150

22112151
// Look through `as` type ascriptions that don't induce bridging too.
22122152
while (auto subCoerce = dyn_cast<CoerceExpr>(subExpr)) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3346,7 +3346,22 @@ namespace {
33463346
}
33473347

33483348
if (auto *closure = dyn_cast<AbstractClosureExpr>(expr)) {
3349-
closure->setActorIsolation(determineClosureIsolation(closure));
3349+
auto isolation = determineClosureIsolation(closure);
3350+
closure->setActorIsolation(isolation);
3351+
3352+
// There is a case in which the constraint solver cannot decide
3353+
// that a closure is `nonisolated(nonsending)` because it cannot
3354+
// analyze the captures, but the closure isolation logic can.
3355+
// Rewrite the closure type at this point.
3356+
if (isolation.isCallerIsolationInheriting()) {
3357+
auto fnType = closure->getType()->castTo<AnyFunctionType>();
3358+
if (!fnType->getIsolation().isNonIsolatedCaller()) {
3359+
fnType = fnType->withIsolation(
3360+
FunctionTypeIsolation::forNonIsolatedCaller());
3361+
closure->setType(fnType);
3362+
}
3363+
}
3364+
33503365
checkLocalCaptures(closure);
33513366
contextStack.push_back(closure);
33523367
return Action::Continue(expr);

test/Concurrency/attr_execution/attr_execution.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func callerTest() async {}
1818
struct Test {
1919
// CHECK-LABEL: // closure #1 in variable initialization expression of Test.x
2020
// CHECK: // Isolation: caller_isolation_inheriting
21-
// CHECK: sil private [ossa] @$s14attr_execution4TestV1xyyYaYCcvpfiyyYacfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
21+
// CHECK: sil private [ossa] @$s14attr_execution4TestV1xyyYaYCcvpfiyyYaYCcfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
2222
var x: () async -> Void = {}
2323

2424
// CHECK-LABEL: // Test.test()
@@ -67,15 +67,15 @@ func takesClosure(fn: () async -> Void) {
6767
}
6868

6969
// CHECK-LABEL: sil hidden [ossa] @$s14attr_execution11testClosureyyF : $@convention(thin) () -> ()
70-
// CHECK: [[CLOSURE:%.*]] = function_ref @$s14attr_execution11testClosureyyFyyYaXEfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
70+
// CHECK: [[CLOSURE:%.*]] = function_ref @$s14attr_execution11testClosureyyFyyYaYCXEfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
7171
// CHECK: [[THUNKED_CLOSURE:%.*]] = thin_to_thick_function %0 to $@noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
7272
// CHECK: [[TAKES_CLOSURE:%.*]] = function_ref @$s14attr_execution12takesClosure2fnyyyYaYCXE_tF : $@convention(thin) (@guaranteed @noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()) -> ()
7373
// CHECK: apply [[TAKES_CLOSURE]]([[THUNKED_CLOSURE]])
7474
// CHECK: } // end sil function '$s14attr_execution11testClosureyyF'
7575

7676
// CHECK-LABEL: // closure #1 in testClosure()
7777
// CHECK: // Isolation: caller_isolation_inheriting
78-
// CHECK: sil private [ossa] @$s14attr_execution11testClosureyyFyyYaXEfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
78+
// CHECK: sil private [ossa] @$s14attr_execution11testClosureyyFyyYaYCXEfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
7979
func testClosure() {
8080
takesClosure {
8181
}

test/Concurrency/attr_execution/conversions_silgen.swift

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -263,15 +263,6 @@ public func testConcurrentCallerLocalVariables(_ x: @escaping @concurrent () asy
263263

264264
// CHECK: } // end sil function '$s21attr_execution_silgen22globalActorConversionsyyyyYac_yyYaYCctYaF'
265265

266-
// FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIegHgIL_IegH_TRScMTU : $@convention(thin) @async (@guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()) -> () {
267-
// FIVE: bb0([[FUNC:%.*]] : @guaranteed
268-
// FIVE: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor
269-
// FIVE: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor
270-
// FIVE: [[E_OPT:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, [[E]]
271-
// FIVE: hop_to_executor [[E_OPT]]
272-
// FIVE: apply [[FUNC]]([[E_OPT]]) : $@async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()
273-
// FIVE: } // end sil function '$sScA_pSgIegHgIL_IegH_TRScMTU'
274-
275266
// SIX-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIetHgIL_IeghH_TRScMTU : $@convention(thin) @Sendable @async (@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()) -> () {
276267
// SIX: bb0([[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated
277268
// SIX: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor
@@ -515,7 +506,7 @@ func conversionsFromSyncToAsync(_ x: @escaping @Sendable (NonSendableKlass) -> V
515506
}
516507

517508
func testThatClosuresAssumeIsolation(fn: inout nonisolated(nonsending) (Int) async -> Void) {
518-
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYacfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
509+
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaYCcfU_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
519510
// CHECK: bb0([[EXECUTOR:%.*]] : @guaranteed $Optional<any Actor>):
520511
// CHECK: hop_to_executor [[EXECUTOR]]
521512
let _: nonisolated(nonsending) () async -> Void = {
@@ -524,31 +515,24 @@ func testThatClosuresAssumeIsolation(fn: inout nonisolated(nonsending) (Int) asy
524515

525516
func testParam(_: nonisolated(nonsending) () async throws -> Void) {}
526517

527-
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU0_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> () {
518+
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaYCXEfU0_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> @error any Error {
528519
// CHECK: bb0([[EXECUTOR:%.*]] : @guaranteed $Optional<any Actor>):
529520
// CHECK: hop_to_executor [[EXECUTOR]]
530-
// CHECK: } // end sil function '$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU0_'
521+
// CHECK: } // end sil function '$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaYCXEfU0_'
522+
testParam { 42 }
531523

532-
// FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIetHgIL_IegH_TR : $@convention(thin) @async (@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()) -> () {
533-
// FIVE: bb0([[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> ()):
534-
// FIVE: [[ACTOR:%.*]] = enum $Optional<any Actor>, #Optional.none!enumelt
535-
// FIVE: hop_to_executor [[ACTOR]]
536-
// FIVE: apply [[FUNC]]([[ACTOR]])
537-
// FIVE: } // end sil function '$sScA_pSgIetHgIL_IegH_TR'
524+
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU1_ : $@convention(thin) @async () -> ()
525+
// CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
526+
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]]
527+
testParam { @concurrent in 42 }
538528

539529
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sIgH_ScA_pSgs5Error_pIegHgILzo_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @guaranteed @noescape @async @callee_guaranteed () -> ()) -> @error any Error {
540530
// CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional<any Actor>, [[FUNC:%.*]] : @guaranteed $@noescape @async @callee_guaranteed () -> ()):
541531
// CHECK: apply [[FUNC]]()
542532
// CHECK: hop_to_executor [[ACTOR]]
543533
// CHECK: } // end sil function '$sIgH_ScA_pSgs5Error_pIegHgILzo_TR'
544-
testParam { 42 }
545534

546-
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU1_ : $@convention(thin) @async () -> ()
547-
// CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
548-
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]]
549-
testParam { @concurrent in 42 }
550-
551-
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFySiYacfU2_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, Int) -> () {
535+
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFySiYaYCcfU2_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, Int) -> () {
552536
// CHECK: bb0([[EXECUTOR:%.*]] : @guaranteed $Optional<any Actor>, %1 : $Int):
553537
// CHECK: hop_to_executor [[EXECUTOR]]
554538
fn = { _ in }
@@ -588,3 +572,44 @@ func testClosuresDontAssumeGlobalActorWithMarkedAsConcurrent() {
588572
test { @Sendable @concurrent in
589573
}
590574
}
575+
576+
nonisolated(nonsending)
577+
public func takesCallerIsolatedThrowingFunction<T>(
578+
_ operation: nonisolated(nonsending) () async throws -> T
579+
) async rethrows -> T {
580+
try await operation()
581+
}
582+
583+
func observe() {}
584+
585+
// Test that we emit closures with nonisolated(nonsending) isolation without
586+
// introducing an intermediate @concurrent closure function.
587+
func testConvertToThrowing(isolation: isolated (any Actor)? = #isolation) async {
588+
// CHECK-LABEL: sil hidden [ossa] @$s21attr_execution_silgen21testConvertToThrowing9isolationyScA_pSgYi_tYaF :
589+
// CHECK: [[ACTOR_COPY:%.*]] = copy_value %0
590+
// CHECK-NEXT: [[ACTOR_BORROW:%.*]] = begin_borrow [[ACTOR_COPY]]
591+
// CHECK-NEXT: hop_to_executor [[ACTOR_BORROW]]
592+
// CHECK: [[CLOSURE:%.*]] = function_ref @$s21attr_execution_silgen21testConvertToThrowing9isolationyScA_pSgYi_tYaFyyYaYCXEfU_ :
593+
// CHECK-NEXT: [[CLOSURE_VALUE:%.*]] = thin_to_thick_function [[CLOSURE]] to
594+
// CHECK-NEXT: // function_ref
595+
// CHECK-NEXT: [[FN:%.*]] = function_ref
596+
// CHECK-NEXT: try_apply [[FN]]<()>({{%.*}}, {{%.*}}, [[CLOSURE_VALUE]]) {{.*}}, normal bb1, error bb2
597+
// CHECK: bb1(
598+
// This hop is unnecessary because nonisolated(nonsending) should
599+
// preserve isolation on return.
600+
// CHECK-NEXT: hop_to_executor [[ACTOR_BORROW]]
601+
602+
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen21testConvertToThrowing9isolationyScA_pSgYi_tYaFyyYaYCXEfU_ : $@convention(thin) @async @substituted <τ_0_0> (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>) -> (@out τ_0_0, @error any Error) for <()>
603+
// CHECK: bb0(
604+
// CHECK-NEXT: debug_value
605+
// This hop is unnecessary because nonisolated(nonsending) should
606+
// ensure isolation before call.
607+
// CHECK-NEXT: hop_to_executor %1
608+
// CHECK-NEXT: // function_ref observe()
609+
// CHECK-NEXT: [[FN:%.*]] = function_ref @$s21attr_execution_silgen7observeyyF :
610+
// CHECK-NEXT: apply [[FN]]()
611+
612+
await takesCallerIsolatedThrowingFunction {
613+
observe()
614+
}
615+
}

0 commit comments

Comments
 (0)