Skip to content

Commit 553fe45

Browse files
committed
[Concurrency] Don't check parameter and result types of function references for
Sendability. Sendability of function references depends only on captures, and applies when the function itself is passed across isolation boundaries. Parameter and result values can only cross isolation boundaries when the function is called.
1 parent 9b16d47 commit 553fe45

File tree

3 files changed

+37
-19
lines changed

3 files changed

+37
-19
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ bool swift::diagnoseNonSendableTypesInReference(
10021002
Expr *base, ConcreteDeclRef declRef,
10031003
const DeclContext *fromDC, SourceLoc refLoc,
10041004
SendableCheckReason refKind, llvm::Optional<ActorIsolation> knownIsolation,
1005-
FunctionCheckKind funcCheckKind, SourceLoc diagnoseLoc) {
1005+
FunctionCheckOptions funcCheckOptions, SourceLoc diagnoseLoc) {
10061006
// Retrieve the actor isolation to use in diagnostics.
10071007
auto getActorIsolation = [&] {
10081008
if (knownIsolation)
@@ -1025,7 +1025,7 @@ bool swift::diagnoseNonSendableTypesInReference(
10251025
// For functions, check the parameter and result types.
10261026
SubstitutionMap subs = declRef.getSubstitutions();
10271027
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
1028-
if (funcCheckKind != FunctionCheckKind::Results) {
1028+
if (funcCheckOptions.contains(FunctionCheckKind::Params)) {
10291029
// only check params if funcCheckKind specifies so
10301030
for (auto param : *function->getParameters()) {
10311031
Type paramType = param->getInterfaceType().subst(subs);
@@ -1039,7 +1039,7 @@ bool swift::diagnoseNonSendableTypesInReference(
10391039

10401040
// Check the result type of a function.
10411041
if (auto func = dyn_cast<FuncDecl>(function)) {
1042-
if (funcCheckKind != FunctionCheckKind::Params) {
1042+
if (funcCheckOptions.contains(FunctionCheckKind::Results)) {
10431043
// only check results if funcCheckKind specifies so
10441044
Type resultType = func->getResultInterfaceType().subst(subs);
10451045
if (diagnoseNonSendableTypes(
@@ -1069,7 +1069,7 @@ bool swift::diagnoseNonSendableTypesInReference(
10691069

10701070
if (auto subscript = dyn_cast<SubscriptDecl>(declRef.getDecl())) {
10711071
for (auto param : *subscript->getIndices()) {
1072-
if (funcCheckKind != FunctionCheckKind::Results) {
1072+
if (funcCheckOptions.contains(FunctionCheckKind::Params)) {
10731073
// Check params of this subscript override for sendability
10741074
Type paramType = param->getInterfaceType().subst(subs);
10751075
if (diagnoseNonSendableTypes(
@@ -1080,7 +1080,7 @@ bool swift::diagnoseNonSendableTypesInReference(
10801080
}
10811081
}
10821082

1083-
if (funcCheckKind != FunctionCheckKind::Results) {
1083+
if (funcCheckOptions.contains(FunctionCheckKind::Results)) {
10841084
// Check the element type of a subscript.
10851085
Type resultType = subscript->getElementInterfaceType().subst(subs);
10861086
if (diagnoseNonSendableTypes(
@@ -3176,7 +3176,12 @@ namespace {
31763176
return diagnoseNonSendableTypesInReference(
31773177
base, declRef, getDeclContext(), loc,
31783178
SendableCheckReason::ExitingActor,
3179-
result.isolation);
3179+
result.isolation,
3180+
// Function reference sendability can only cross isolation
3181+
// boundaries when they're passed as an argument or called,
3182+
// and their Sendability depends only on captures; do not
3183+
// check the parameter or result types here.
3184+
FunctionCheckOptions());
31803185

31813186
case ActorReferenceResult::EntersActor:
31823187
// Handle all of the checking below.

lib/Sema/TypeCheckConcurrency.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -246,17 +246,18 @@ struct ActorReferenceResult {
246246
operator Kind() const { return kind; }
247247
};
248248

249-
/// Specifies whether checks applied to function types should
250-
/// apply to their params, results, or both
249+
/// Individual options used with \c FunctionCheckOptions
251250
enum class FunctionCheckKind {
252-
/// Check params and results
253-
ParamsResults,
254-
/// Check params only
255-
Params,
256-
/// Check results only
257-
Results,
251+
/// Check params
252+
Params = 1 << 0,
253+
/// Check results
254+
Results = 1 << 1,
258255
};
259256

257+
/// Specifies whether checks applied to function types should apply to
258+
/// their parameters, their results, both, or neither.
259+
using FunctionCheckOptions = OptionSet<FunctionCheckKind>;
260+
260261
/// Diagnose the presence of any non-sendable types when referencing a
261262
/// given declaration from a particular declaration context.
262263
///
@@ -280,7 +281,7 @@ enum class FunctionCheckKind {
280281
/// \param refKind Describes what kind of reference is being made, which is
281282
/// used to tailor the diagnostic.
282283
///
283-
/// \param funcCheckKind Describes whether function types in this reference
284+
/// \param funcCheckOptions Describes whether function types in this reference
284285
/// should be checked for sendability of their results, params, or both
285286
///
286287
/// \param diagnoseLoc Provides an alternative source location to `refLoc`
@@ -293,7 +294,10 @@ bool diagnoseNonSendableTypesInReference(
293294
const DeclContext *fromDC, SourceLoc refLoc,
294295
SendableCheckReason refKind,
295296
llvm::Optional<ActorIsolation> knownIsolation = llvm::None,
296-
FunctionCheckKind funcCheckKind = FunctionCheckKind::ParamsResults,
297+
FunctionCheckOptions funcCheckOptions =
298+
(FunctionCheckOptions() |
299+
FunctionCheckKind::Params |
300+
FunctionCheckKind::Results),
297301
SourceLoc diagnoseLoc = SourceLoc());
298302

299303
/// Produce a diagnostic for a missing conformance to Sendable.

test/Concurrency/sendable_checking.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func testConversionsAndSendable(a: MyActor, s: any Sendable, f: @Sendable () ->
249249
final class NonSendable {
250250
// expected-note @-1 3 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
251251
// SendNonSendable emits 3 fewer errors here.
252-
// expected-targeted-and-complete-note @-3 4 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
252+
// expected-targeted-and-complete-note @-3 5 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
253253
var value = ""
254254

255255
@MainActor
@@ -285,14 +285,23 @@ func testNonSendableBaseArg() async {
285285
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
286286
}
287287

288+
@available(SwiftStdlib 5.1, *)
289+
@Sendable
290+
func globalSendable(_ ns: NonSendable) async {}
291+
288292
@available(SwiftStdlib 5.1, *)
289293
@MainActor
290294
func callNonisolatedAsyncClosure(
291295
ns: NonSendable,
292296
g: (NonSendable) async -> Void
293297
) async {
294-
// FIXME: This should also produce a diagnostic with SendNonSendable, because
295-
// the 'ns' parameter should be merged into the MainActor's region.
298+
// FIXME: Both cases below should also produce a diagnostic with SendNonSendable,
299+
// because the 'ns' parameter should be merged into the MainActor's region.
300+
296301
await g(ns)
297302
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
303+
304+
let f: (NonSendable) async -> () = globalSendable // okay
305+
await f(ns)
306+
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
298307
}

0 commit comments

Comments
 (0)