Skip to content

[Sema]Skip Sendable conformance check when sending are added to parameters or return types of an actor-isolated function #78601

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

Merged
merged 4 commits into from
Apr 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 32 additions & 47 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,34 +1168,47 @@ bool swift::diagnoseNonSendableTypesInReference(
return true;
}

// For functions, check the parameter and result types.
// For functions or subscripts, check the parameter and result types.
SubstitutionMap subs = declRef.getSubstitutions();
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
auto decl = declRef.getDecl();
if (isa<AbstractFunctionDecl>(decl) || isa<SubscriptDecl>(decl)) {
if (funcCheckOptions.contains(FunctionCheckKind::Params)) {
// only check params if funcCheckKind specifies so
for (auto param : *function->getParameters()) {
ParameterList *paramList = nullptr;
if (auto function = dyn_cast<AbstractFunctionDecl>(decl)) {
paramList = function->getParameters();
} else if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
paramList = subscript->getIndices();
}

// Check params of this function or subscript override for sendability
for (auto param : *paramList) {
Type paramType = param->getInterfaceType().subst(subs);
if (diagnoseNonSendableTypes(
paramType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableParamDiag(refKind),
function, getActorIsolation()))
if (diagnoseNonSendableTypesWithSendingCheck(
param, paramType, fromDC, derivedConformanceType, refLoc,
diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableParamDiag(refKind), decl, getActorIsolation()))
return true;
}
}

// Check the result type of a function.
if (auto func = dyn_cast<FuncDecl>(function)) {
if (funcCheckOptions.contains(FunctionCheckKind::Results)) {
// only check results if funcCheckKind specifies so
Type resultType = func->getResultInterfaceType().subst(subs);
if (diagnoseNonSendableTypes(
resultType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableResultDiag(refKind),
func, getActorIsolation()))
return true;
// Check the result type of a function or subscript.
if (funcCheckOptions.contains(FunctionCheckKind::Results)) {
Type resultType;
if (auto func = dyn_cast<FuncDecl>(decl)) {
resultType = func->getResultInterfaceType().subst(subs);
decl = func;
} else if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
resultType = subscript->getElementInterfaceType().subst(subs);
}
if (!resultType) {
return false;
}
if (diagnoseNonSendableTypesWithSendingCheck(
decl, resultType, fromDC, derivedConformanceType, refLoc,
diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableResultDiag(refKind), decl, getActorIsolation()))
return true;
}

return false;
Expand All @@ -1213,34 +1226,6 @@ bool swift::diagnoseNonSendableTypesInReference(
return true;
}

if (auto subscript = dyn_cast<SubscriptDecl>(declRef.getDecl())) {
for (auto param : *subscript->getIndices()) {
if (funcCheckOptions.contains(FunctionCheckKind::Params)) {
// Check params of this subscript override for sendability
Type paramType = param->getInterfaceType().subst(subs);
if (diagnoseNonSendableTypes(
paramType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableParamDiag(refKind),
subscript, getActorIsolation()))
return true;
}
}

if (funcCheckOptions.contains(FunctionCheckKind::Results)) {
// Check the element type of a subscript.
Type resultType = subscript->getElementInterfaceType().subst(subs);
if (diagnoseNonSendableTypes(
resultType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
getSendableResultDiag(refKind),
subscript, getActorIsolation()))
return true;
}

return false;
}

return false;
}

Expand Down
32 changes: 32 additions & 0 deletions lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/Expr.h"
#include "swift/AST/Module.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Sema/Concurrency.h"

#include <cassert>
Expand Down Expand Up @@ -436,6 +437,37 @@ namespace detail {
};
}

/// Diagnose any non-Sendable types that occur within the given type, using
/// the given diagnostic.
///
/// \returns \c true if any errors were produced, \c false if no diagnostics or
/// only warnings and notes were produced or if a decl contains a sending
/// parameter or result
template <typename... DiagArgs>
bool diagnoseNonSendableTypesWithSendingCheck(
ValueDecl *decl, Type type, SendableCheckContext fromContext,
Type derivedConformance, SourceLoc typeLoc, SourceLoc diagnoseLoc,
Diag<Type, DiagArgs...> diag,
typename detail::Identity<DiagArgs>::type... diagArgs) {
if (auto param = dyn_cast<ParamDecl>(decl)) {
if (param->isSending()) {
return false;
}
}
if (auto *func = dyn_cast<FuncDecl>(decl)) {
if (func->hasSendingResult())
return false;
}
if (auto *subscript = dyn_cast<SubscriptDecl>(decl)) {
if (isa_and_nonnull<SendingTypeRepr>(subscript->getResultTypeRepr()))
return false;
}

return diagnoseNonSendableTypes(
type, fromContext, derivedConformance, typeLoc, diagnoseLoc, diag,
std::forward<decltype(diagArgs)>(diagArgs)...);
}

/// Diagnose any non-Sendable types that occur within the given type, using
/// the given diagnostic.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// RUN: %target-typecheck-verify-swift -swift-version 6

// https://github.com/swiftlang/swift/issues/76710

class NonSendableKlass1 {}

protocol P1 {
func bar(_ a: sending NonSendableKlass1) async -> sending NonSendableKlass1
}

@MainActor
class P1Class: P1 {
func bar(_ a: sending NonSendableKlass1) async -> sending NonSendableKlass1 { a }
}

class NonSendableKlass2 {}
// expected-note@-1 2{{class 'NonSendableKlass2' does not conform to the 'Sendable' protocol}}

protocol P2 {
func bar(_ a: NonSendableKlass2) async -> NonSendableKlass2
}

@MainActor
class P2Class: P2 {
func bar(_ a: NonSendableKlass2) async -> NonSendableKlass2 { a }
// expected-error@-1 {{non-sendable type 'NonSendableKlass2' cannot be returned from main actor-isolated implementation to caller of protocol requirement 'bar'}}
// expected-error@-2 {{non-sendable parameter type 'NonSendableKlass2' cannot be sent from caller of protocol requirement 'bar' into main actor-isolated implementation}}
}

class NonSendableKlass3 {}

protocol P3 {
func bar(_ a: sending NonSendableKlass3) async -> sending NonSendableKlass3
}

actor P3Actor: P3 {
func bar(_ a: sending NonSendableKlass3) async -> sending NonSendableKlass3 { NonSendableKlass3() }
}

class NonSendableKlass4 {}
// expected-note@-1 2{{class 'NonSendableKlass4' does not conform to the 'Sendable' protocol}}

protocol P4 {
func bar(_ a: NonSendableKlass4) async -> NonSendableKlass4
}

actor P4Actor: P4 {
func bar(_ a: NonSendableKlass4) async -> NonSendableKlass4 { NonSendableKlass4() }
// expected-error@-1 {{non-sendable type 'NonSendableKlass4' cannot be returned from actor-isolated implementation to caller of protocol requirement 'bar'}}
// expected-error@-2 {{non-sendable parameter type 'NonSendableKlass4' cannot be sent from caller of protocol requirement 'bar' into actor-isolated implementation}}
}

class NonSendableKlass5 {}
// expected-note@-1 {{class 'NonSendableKlass5' does not conform to the 'Sendable' protocol}}


protocol P5 {
func bar(_ a: sending NonSendableKlass5, _ b: NonSendableKlass5) async -> sending NonSendableKlass5
}

@MainActor
class P5Class: P5 {
func bar(_ a: sending NonSendableKlass5, _ b: NonSendableKlass5) async -> sending NonSendableKlass5 { a }
// expected-error@-1 {{non-sendable parameter type 'NonSendableKlass5' cannot be sent from caller of protocol requirement 'bar' into main actor-isolated implementation}}
}

class NonSendableKlass6 {}

protocol P6 {
func bar(_ a: sending NonSendableKlass6, _ b: sending NonSendableKlass6) async -> sending NonSendableKlass6
}

@MainActor
class P6Class: P6 {
func bar(_ a: sending NonSendableKlass6, _ b: sending NonSendableKlass6) async -> sending NonSendableKlass6 { a }
}

class NonSendableKlass7 {}

protocol P7 {
subscript(_: sending NonSendableKlass7) -> sending NonSendableKlass7 { get async }
}

@MainActor
struct S: P7 {
subscript(_: sending NonSendableKlass7) -> sending NonSendableKlass7 {
get async { NonSendableKlass7() }
}
}