[TypeChecker] Allow closures to assume nonisolated(nonsending)

Always infer `nonisolated(nonsending)` from context directly on
a closure unless the closure is marked as `@concurrent`, otherwise
the closure is not going to get correct isolation and going to hop
to the wrong executor in its preamble.

Resolves: rdar://149107104
(cherry picked from commit 3de72c5452)
This commit is contained in:
Pavel Yaskevich
2025-04-16 17:11:48 -07:00
parent c18e23193e
commit 134f5def36
3 changed files with 65 additions and 7 deletions

View File

@@ -6494,18 +6494,31 @@ ArgumentList *ExprRewriter::coerceCallArguments(
return ArgumentList::createTypeChecked(ctx, args, newArgs);
}
/// Looks through any non-semantic expressions and a capture list
/// to find out whether the given expression is an explicit closure.
static ClosureExpr *isExplicitClosureExpr(Expr *expr) {
if (auto IE = dyn_cast<IdentityExpr>(expr))
return isExplicitClosureExpr(IE->getSubExpr());
if (auto CLE = dyn_cast<CaptureListExpr>(expr))
return isExplicitClosureExpr(CLE->getClosureBody());
return dyn_cast<ClosureExpr>(expr);
}
/// Whether the given expression is a closure that should inherit
/// the actor context from where it was formed.
static bool closureInheritsActorContext(Expr *expr) {
if (auto IE = dyn_cast<IdentityExpr>(expr))
return closureInheritsActorContext(IE->getSubExpr());
if (auto CLE = dyn_cast<CaptureListExpr>(expr))
return closureInheritsActorContext(CLE->getClosureBody());
if (auto CE = dyn_cast<ClosureExpr>(expr))
if (auto *CE = isExplicitClosureExpr(expr))
return CE->inheritsActorContext();
return false;
}
/// Determine whether the given expression is a closure that
/// is explicitly marked as `@concurrent`.
static bool isClosureMarkedAsConcurrent(Expr *expr) {
if (auto *CE = isExplicitClosureExpr(expr))
return CE->getAttrs().hasAttribute<ConcurrentAttr>();
return false;
}
@@ -7747,6 +7760,23 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
}
}
// If we have a ClosureExpr, then we can safely propagate the
// 'nonisolated(nonsending)' isolation if it's not explicitly
// marked as `@concurrent`.
if (toEI.getIsolation().isNonIsolatedCaller() &&
(fromEI.getIsolation().isNonIsolated() &&
!isClosureMarkedAsConcurrent(expr))) {
auto newFromFuncType = fromFunc->withIsolation(
FunctionTypeIsolation::forNonIsolatedCaller());
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
fromFunc = newFromFuncType->castTo<FunctionType>();
// Propagating 'nonisolated(nonsending)' might have satisfied the entire
// conversion. If so, we're done, otherwise keep converting.
if (fromFunc->isEqual(toType))
return expr;
}
}
if (ctx.LangOpts.isDynamicActorIsolationCheckingEnabled()) {
// Passing a synchronous global actor-isolated function value and
// parameter that expects a synchronous non-isolated function type could