From 18817b823faabc55414d74d2845e24a6e99ce432 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Jul 2025 17:13:53 -0700 Subject: [PATCH 1/2] [Concurrency] NonisolatedNonsendingByDefault: Extend `nonisolated(nonsending)` to `withoutActuallyEscaping` `withoutActuallyEscaping` is type-checked in a special way which means that we need to explicitly inject `nonisolated(nonsending)` isolation when forming a reference to this builtin. (cherry picked from commit 48f4d7b688c325a0b7d5a8721576d5bc46fa0c1d) --- lib/Sema/TypeCheckConcurrency.cpp | 22 +++++++++++++++ lib/Sema/TypeOfReference.cpp | 15 +++++++++++ .../attr_execution/attr_execution.swift | 27 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index a13432f2e20b9..0909926f0c362 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3543,6 +3543,28 @@ namespace { if (auto isolationExpr = dyn_cast(expr)) recordCurrentContextIsolation(isolationExpr); + // `withoutActuallyEscaping` parameter types are set to be + // `nonisolated(nonsending)` when the `NonisolatedNonsendingByDefault` + // feature is enabled, which means that we need to make the argument + // as `nonisolated(nonsending)` if it's a closure. This cannot be done + // sooner because we need to make sure that closure is definitely + // nonisolated and due to how AST is structured we cannot do this in + // `determineClosureIsolation`. + if (ctx.LangOpts.hasFeature(Feature::NonisolatedNonsendingByDefault)) { + if (auto *MTEE = dyn_cast(expr)) { + if (auto *call = dyn_cast(MTEE->getSubExpr())) { + if (auto *closure = dyn_cast(call->getFn())) { + if (auto closureTy = closure->getType()->getAs()) { + if (closureTy->isAsync() && + closure->getActorIsolation().isNonisolated()) + closure->setActorIsolation( + ActorIsolation::forCallerIsolationInheriting()); + } + } + } + } + } + return Action::Continue(expr); } diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index 376614b94898a..c9c35734f3dcc 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -2322,9 +2322,17 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( CS.getConstraintLocator(locator, ConstraintLocator::ThrownErrorType), 0); FunctionType::Param arg(escapeClosure); + + auto bodyParamIsolation = FunctionTypeIsolation::forNonIsolated(); + if (CS.getASTContext().LangOpts.hasFeature( + Feature::NonisolatedNonsendingByDefault)) { + bodyParamIsolation = FunctionTypeIsolation::forNonIsolatedCaller(); + } + auto bodyClosure = FunctionType::get(arg, result, FunctionType::ExtInfoBuilder() .withNoEscape(true) + .withIsolation(bodyParamIsolation) .withAsync(true) .withThrows(true, thrownError) .build()); @@ -2333,9 +2341,16 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), }; + auto withoutEscapingIsolation = FunctionTypeIsolation::forNonIsolated(); + if (CS.getASTContext().LangOpts.hasFeature( + Feature::NonisolatedNonsendingByDefault)) { + withoutEscapingIsolation = FunctionTypeIsolation::forNonIsolatedCaller(); + } + auto refType = FunctionType::get(args, result, FunctionType::ExtInfoBuilder() .withNoEscape(false) + .withIsolation(withoutEscapingIsolation) .withAsync(true) .withThrows(true, thrownError) .build()); diff --git a/test/Concurrency/attr_execution/attr_execution.swift b/test/Concurrency/attr_execution/attr_execution.swift index c9f855cb770c9..7c5b65ee9280f 100644 --- a/test/Concurrency/attr_execution/attr_execution.swift +++ b/test/Concurrency/attr_execution/attr_execution.swift @@ -80,3 +80,30 @@ func testClosure() { takesClosure { } } + +func testWithoutActuallyEscaping(_ f: () async -> ()) async { + // CHECK-LABEL: // closure #1 in testWithoutActuallyEscaping(_:) + // CHECK-NEXT: // Isolation: caller_isolation_inheriting + await withoutActuallyEscaping(f) { + await $0() + } + + // CHECK-LABEL: // closure #2 in testWithoutActuallyEscaping(_:) + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + await withoutActuallyEscaping(f) { @MainActor in + await $0() + } + + actor Test { + // CHECK-LABEL: // closure #1 in testActorIsolatedCapture() in Test #1 in testWithoutActuallyEscaping(_:) + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + func testActorIsolatedCapture() async { + await withoutActuallyEscaping(compute) { + _ = self + await $0() + } + } + + func compute() async {} + } +} From fb0daaa78ae7987b46072da34eceaa225e62fed0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Jul 2025 17:27:13 -0700 Subject: [PATCH 2/2] [Concurrency] NonisolatedNonsendingByDefault: Extend `nonisolated(nonsending)` to `_openExistential` `_openExistential` is type-checked in a special way which means that we need to explicitly inject `nonisolated(nonsending)` isolation when forming a reference to this builtin. (cherry picked from commit 358869ff542b93d7793b5fc7fdd31734a9cb52a0) --- lib/Sema/TypeOfReference.cpp | 16 ++++++++++++++++ .../attr_execution/attr_execution.swift | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index c9c35734f3dcc..508d3b4a327ef 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -2374,20 +2374,36 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics( CS.getConstraintLocator(locator, ConstraintLocator::ThrownErrorType), 0); FunctionType::Param bodyArgs[] = {FunctionType::Param(openedTy)}; + + auto bodyParamIsolation = FunctionTypeIsolation::forNonIsolated(); + if (CS.getASTContext().LangOpts.hasFeature( + Feature::NonisolatedNonsendingByDefault)) { + bodyParamIsolation = FunctionTypeIsolation::forNonIsolatedCaller(); + } + auto bodyClosure = FunctionType::get(bodyArgs, result, FunctionType::ExtInfoBuilder() .withNoEscape(true) .withThrows(true, thrownError) + .withIsolation(bodyParamIsolation) .withAsync(true) .build()); FunctionType::Param args[] = { FunctionType::Param(existentialTy), FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), }; + + auto openExistentialIsolation = FunctionTypeIsolation::forNonIsolated(); + if (CS.getASTContext().LangOpts.hasFeature( + Feature::NonisolatedNonsendingByDefault)) { + openExistentialIsolation = FunctionTypeIsolation::forNonIsolatedCaller(); + } + auto refType = FunctionType::get(args, result, FunctionType::ExtInfoBuilder() .withNoEscape(false) .withThrows(true, thrownError) + .withIsolation(openExistentialIsolation) .withAsync(true) .build()); return {refType, refType, refType, refType, Type()}; diff --git a/test/Concurrency/attr_execution/attr_execution.swift b/test/Concurrency/attr_execution/attr_execution.swift index 7c5b65ee9280f..430449c6ec30d 100644 --- a/test/Concurrency/attr_execution/attr_execution.swift +++ b/test/Concurrency/attr_execution/attr_execution.swift @@ -81,6 +81,20 @@ func testClosure() { } } +protocol P { +} + +func open(_: T) async {} + +// CHECK-LABEL: sil hidden [ossa] @$s14attr_execution19testOpenExistential11existentialyAA1P_p_tYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @in_guaranteed any P) -> () +// CHECK: bb0([[ISOLATION:%.*]] : @guaranteed $Optional, [[EXISTENTIAL:%.*]] : $*any P): +// CHECK: [[OPEN_REF:%.*]] = function_ref @$s14attr_execution4openyyxYaAA1PRzlF +// CHECK: apply [[OPEN_REF]]<@opened("{{.*}}", any P) Self>([[ISOLATION]], {{.*}}) +// CHECK: } // end sil function '$s14attr_execution19testOpenExistential11existentialyAA1P_p_tYaF' +func testOpenExistential(existential: any P) async { + await _openExistential(existential, do: open) +} + func testWithoutActuallyEscaping(_ f: () async -> ()) async { // CHECK-LABEL: // closure #1 in testWithoutActuallyEscaping(_:) // CHECK-NEXT: // Isolation: caller_isolation_inheriting