Skip to content

Commit b5ddffd

Browse files
authored
Merge pull request #68420 from hborla/async-closure-sendable-test
[Concurrency] Fix an issue with Sendable checking for function references.
2 parents c1b07f0 + 553fe45 commit b5ddffd

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
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: 22 additions & 1 deletion
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 3 {{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
@@ -284,3 +284,24 @@ func testNonSendableBaseArg() async {
284284
_ = await t.x
285285
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
286286
}
287+
288+
@available(SwiftStdlib 5.1, *)
289+
@Sendable
290+
func globalSendable(_ ns: NonSendable) async {}
291+
292+
@available(SwiftStdlib 5.1, *)
293+
@MainActor
294+
func callNonisolatedAsyncClosure(
295+
ns: NonSendable,
296+
g: (NonSendable) async -> Void
297+
) async {
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+
301+
await g(ns)
302+
// 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}}
307+
}

0 commit comments

Comments
 (0)