diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a7e4fed203828..dce9a2789c38e 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6278,7 +6278,7 @@ ArgumentList *ExprRewriter::coerceCallArguments( if (auto declaredIn = decl->findImport(dc)) return !declaredIn->module.importedModule->isConcurrencyChecked(); - // Both the caller and the allee are in the same module. + // Both the caller and the callee are in the same module. if (dc->getParentModule() == decl->getModuleContext()) { return !dc->getASTContext().isSwiftVersionAtLeast(6); } @@ -6288,16 +6288,47 @@ ArgumentList *ExprRewriter::coerceCallArguments( return true; }(); - auto applyFlagsToArgument = [¶mInfo, - &closuresRequireDynamicIsolationChecking, - &locator](unsigned paramIdx, Expr *argument) { + auto isNonEscapingDirectlyDispatchedParameter = [&](unsigned paramIdx) -> bool { + auto *decl = callee.getDecl(); + if (!decl) return false; + + // If this is a non-@escaping synchronous function parameter, there's no need for + // an executor check as it will not escape. + auto isolation = getActorIsolation(decl); + switch (isolation) { + case ActorIsolation::Unspecified: + // Unspecified isolation would be an issue if this parameter was @escaping, but + // given that we will not be hopping to another executor, this is fine. + case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: + case ActorIsolation::CallerIsolationInheriting: + break; + case ActorIsolation::ActorInstance: + case ActorIsolation::Erased: + case ActorIsolation::GlobalActor: + return false; + } + + auto *param = getParameterAt(decl, paramIdx); + if (!param) return false; + + auto ty = param->getInterfaceType(); + auto *funcTy = dyn_cast(ty); + if (!funcTy) return false; + + return funcTy->isNoEscape() && !funcTy->isAsync(); + }; + + auto applyFlagsToArgument = [&](unsigned paramIdx, Expr *argument) { if (!isClosureLiteralExpr(argument)) return; bool isMacroArg = isExpr(locator.getAnchor()); + bool requiresDynamicIsolationChecking = closuresRequireDynamicIsolationChecking && + !isNonEscapingDirectlyDispatchedParameter(paramIdx); applyContextualClosureFlags(argument, paramIdx, paramInfo, - closuresRequireDynamicIsolationChecking, + requiresDynamicIsolationChecking, isMacroArg); }; diff --git a/test/Concurrency/nonisolated_synchronous_avoid_executor_check.swift b/test/Concurrency/nonisolated_synchronous_avoid_executor_check.swift new file mode 100644 index 0000000000000..cc639a2d80545 --- /dev/null +++ b/test/Concurrency/nonisolated_synchronous_avoid_executor_check.swift @@ -0,0 +1,17 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name output -emit-silgen -swift-version 6 | swift-demangle | %FileCheck %s + +// REQUIRES: concurrency + +@MainActor +public func isEven(_ x: Int) -> Bool { + x.isMultiple(of: 2) +} + +// CHECK: sil [ossa] @output.mainActorFunc(xs: [Swift.Int]) +// CHECK-NOT: _checkTaskExecutor +// CHECK: end sil function 'output.mainActorFunc(xs: [Swift.Int]) -> Swift.Int' +@MainActor +public func mainActorFunc(xs: [Int]) -> Int { + xs.count { isEven($0) } +}