Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Concurrency] Perform Sendable checking on parameter and result types for async function conversions that change actor isolation. #72691

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Expand Up @@ -5611,6 +5611,14 @@ ERROR(non_sendable_param_type,none,
"in parameter of superclass method overridden by %3 %kind2|"
"in parameter of %3 '@objc' %kind2}1 cannot cross actor boundary",
(Type, unsigned, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_param_reference,none,
"non-sendable parameter type %0 in conversion from %1 function to "
"%2 function",
(Type, ActorIsolation, ActorIsolation))
ERROR(non_sendable_result_reference,none,
"non-sendable result type %0 in conversion from %1 function to "
"%2 function",
(Type, ActorIsolation, ActorIsolation))
ERROR(non_sendable_call_argument,none,
"passing argument of non-sendable type %0 %select{into %2 context|"
"outside of %2 context}1 may introduce data races",
Expand Down
38 changes: 30 additions & 8 deletions lib/Sema/TypeCheckConcurrency.cpp
Expand Up @@ -1917,7 +1917,8 @@ bool swift::isAsyncDecl(ConcreteDeclRef declRef) {
/// \param ty a function type where \c globalActor was removed from it.
/// \return true if it is safe to drop the global-actor qualifier.
static bool safeToDropGlobalActor(
DeclContext *dc, Type globalActor, Type ty, ApplyExpr *call) {
DeclContext *dc, Type globalActor, Type ty,
ApplyExpr *call, SourceLoc loc) {
auto funcTy = ty->getAs<AnyFunctionType>();
if (!funcTy)
return false;
Expand All @@ -1936,11 +1937,30 @@ static bool safeToDropGlobalActor(
if (otherIsolation.isErased())
return true;

// We currently allow unconditional dropping of global actors from
// async function types, despite this confusing Sendable checking
// in light of SE-338.
if (funcTy->isAsync())
// Converting from a global actor isolated type to an async function
// type is okay as long as the parameter and result types are 'Sendable',
// because the argument and result values will cross an isolation boundary
// in the conversion thunk.
if (funcTy->isAsync()) {
auto sourceIsolation = ActorIsolation::forGlobalActor(globalActor);
for (auto param : funcTy->getParams()) {
auto paramType = param.getPlainType();
diagnoseNonSendableTypes(
paramType, dc, /*inDerivedConformance*/Type(),
loc, diag::non_sendable_param_reference,
sourceIsolation,
ActorIsolation::forNonisolated(/*unsafe*/false));
}

auto resultType = funcTy->getResult();
diagnoseNonSendableTypes(
resultType, dc, /*inDerivedConformance*/Type(),
loc, diag::non_sendable_result_reference,
sourceIsolation,
ActorIsolation::forNonisolated(/*unsafe*/false));

return true;
}

// If the argument is passed over an isolation boundary, it's not
// safe to erase actor isolation, because the callee can call the
Expand Down Expand Up @@ -2365,13 +2385,15 @@ namespace {
if (auto fromActor = fromFnType->getGlobalActor()) {
if (auto toFnType = toType->getAs<FunctionType>()) {

// ignore some kinds of casts, as they're diagnosed elsewhere.
if (toFnType->hasGlobalActor() || toFnType->isAsync())
// The conversion is okay if the global actors match.
if (toFnType->hasGlobalActor() &&
fromActor->isEqual(toFnType->getGlobalActor()))
return;

auto dc = const_cast<DeclContext*>(getDeclContext());
if (!safeToDropGlobalActor(dc, fromActor, toType,
getImmediateApply())) {
getImmediateApply(),
funcConv->getLoc())) {
// otherwise, it's not a safe cast.
dc->getASTContext()
.Diags
Expand Down
14 changes: 14 additions & 0 deletions test/Concurrency/global_actor_function_types.swift
Expand Up @@ -404,3 +404,17 @@ extension GlobalType {
rhs: GlobalType
) -> Bool { true }
}

// expected-complete-tns-note@+1 2 {{class 'NotSendable' does not conform to the 'Sendable' protocol}}
class NotSendable {}

func testUnsafeAsyncFunctionConversions(
f: @escaping @MainActor (NotSendable) async -> Void,
g: @escaping @MainActor () async -> NotSendable
) {
// expected-complete-tns-warning@+1 {{non-sendable parameter type 'NotSendable' in conversion from main actor-isolated function to nonisolated function; this is an error in the Swift 6 language mode}}
let _: (NotSendable) async -> Void = f

// expected-complete-tns-warning@+1 {{non-sendable result type 'NotSendable' in conversion from main actor-isolated function to nonisolated function; this is an error in the Swift 6 language mode}}
let _: () async -> NotSendable = g
}