Skip to content

Commit 5638377

Browse files
committed
[ClangImporter] SE-0463: Implement @Sendable inference exception for global actor isolated functions
Functions that are isolated to a global actor don't have completion handlers imported as `@Sendable`. Main actor isolated functions with completion handlers that are always called on the main actor is a very common Objective-C pattern, and this carve out will eliminate false positive warnings in the cases where the main actor annotation is missing on the completion handler parameter. Resolves: rdar://149811049 (cherry picked from commit ab227b4)
1 parent b2bcd21 commit 5638377

File tree

3 files changed

+138
-12
lines changed

3 files changed

+138
-12
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,11 +2421,30 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
24212421
return {swiftResultTy, importedType.isImplicitlyUnwrapped()};
24222422
}
24232423

2424+
static bool isParameterContextGlobalActorIsolated(DeclContext *dc,
2425+
const clang::Decl *parent) {
2426+
if (getActorIsolationOfContext(dc).isGlobalActor())
2427+
return true;
2428+
2429+
if (!parent->hasAttrs())
2430+
return false;
2431+
2432+
for (const auto *attr : parent->getAttrs()) {
2433+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
2434+
if (isMainActorAttr(swiftAttr))
2435+
return true;
2436+
}
2437+
}
2438+
2439+
return false;
2440+
}
2441+
24242442
std::optional<ClangImporter::Implementation::ImportParameterTypeResult>
24252443
ClangImporter::Implementation::importParameterType(
2426-
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
2427-
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
2428-
bool paramIsError, bool paramIsCompletionHandler,
2444+
DeclContext *dc, const clang::Decl *parent, const clang::ParmVarDecl *param,
2445+
OptionalTypeKind optionalityOfParam, bool allowNSUIntegerAsInt,
2446+
bool isNSDictionarySubscriptGetter, bool paramIsError,
2447+
bool paramIsCompletionHandler,
24292448
std::optional<unsigned> completionHandlerErrorParamIndex,
24302449
ArrayRef<GenericTypeParamDecl *> genericParams,
24312450
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
@@ -2445,7 +2464,8 @@ ClangImporter::Implementation::importParameterType(
24452464

24462465
if (SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
24472466
paramIsCompletionHandler) {
2448-
attrs |= ImportTypeAttr::DefaultsToSendable;
2467+
if (!isParameterContextGlobalActorIsolated(dc, parent))
2468+
attrs |= ImportTypeAttr::DefaultsToSendable;
24492469
}
24502470

24512471
if (auto optionSetEnum = importer::findOptionSetEnum(paramTy, *this)) {
@@ -2722,13 +2742,13 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
27222742

27232743
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
27242744

2725-
auto swiftParamTyOpt =
2726-
importParameterType(param, optionalityOfParam, allowNSUIntegerAsInt,
2727-
/*isNSDictionarySubscriptGetter=*/false,
2728-
/*paramIsError=*/false,
2729-
/*paramIsCompletionHandler=*/false,
2730-
/*completionHandlerErrorParamIndex=*/std::nullopt,
2731-
genericParams, paramAddDiag);
2745+
auto swiftParamTyOpt = importParameterType(
2746+
dc, clangDecl, param, optionalityOfParam, allowNSUIntegerAsInt,
2747+
/*isNSDictionarySubscriptGetter=*/false,
2748+
/*paramIsError=*/false,
2749+
/*paramIsCompletionHandler=*/false,
2750+
/*completionHandlerErrorParamIndex=*/std::nullopt, genericParams,
2751+
paramAddDiag);
27322752
if (!swiftParamTyOpt) {
27332753
addImportDiagnostic(param,
27342754
Diagnostic(diag::parameter_type_not_imported, param),
@@ -3285,7 +3305,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
32853305
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
32863306

32873307
auto swiftParamTyOpt = importParameterType(
3288-
param, optionalityOfParam, allowNSUIntegerAsIntInParam,
3308+
origDC, clangDecl, param, optionalityOfParam,
3309+
allowNSUIntegerAsIntInParam,
32893310
kind == SpecialMethodKind::NSDictionarySubscriptGetter, paramIsError,
32903311
paramIsCompletionHandler, completionHandlerErrorParamIndex,
32913312
ArrayRef<GenericTypeParamDecl *>(), paramAddDiag);

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14581458

14591459
/// Import a parameter type
14601460
///
1461+
/// \param dc The declaration context in which this parameter appears.
1462+
/// \param parent The declaration with which this parameter is associated.
14611463
/// \param param The underlaying parameter declaraction.
14621464
/// \param optionalityOfParam The kind of optionality for the parameter
14631465
/// being imported.
@@ -1485,6 +1487,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14851487
///
14861488
/// \returns The imported parameter result on success, or None on failure.
14871489
std::optional<ImportParameterTypeResult> importParameterType(
1490+
DeclContext *dc, const clang::Decl *parent,
14881491
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
14891492
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
14901493
bool paramIsError, bool paramIsCompletionHandler,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: %empty-directory(%t/sdk)
3+
// RUN: split-file %s %t/src
4+
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \
6+
// RUN: -import-objc-header %t/src/Test.h \
7+
// RUN: -swift-version 6 \
8+
// RUN: -module-name main -I %t -verify
9+
10+
// REQUIRES: objc_interop
11+
12+
//--- Test.h
13+
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
14+
#define NONSENDABLE __attribute__((__swift_attr__("@_nonSendable")))
15+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
16+
17+
#pragma clang assume_nonnull begin
18+
19+
@import Foundation;
20+
21+
MAIN_ACTOR
22+
@protocol P <NSObject>
23+
@end
24+
25+
@interface TestFromProtocol <P>
26+
-(void) compute: (void (^)(void)) completion;
27+
@end
28+
29+
MAIN_ACTOR
30+
@interface TestFromType <NSObject>
31+
-(void) compute: (void (^)(void)) completion;
32+
@end
33+
34+
@interface TestSubclass : TestFromType
35+
-(void) subCompute: (void (^)(void)) completion;
36+
@end
37+
38+
@interface TestFromMethod <NSObject>
39+
-(void) MAIN_ACTOR compute: (void (^)(void)) completion;
40+
+(void) MAIN_ACTOR computeStatic: (void (^)(void)) completion;
41+
@end
42+
43+
#pragma clang assume_nonnull end
44+
45+
//--- main.swift
46+
47+
func testFromProtocol(v: TestFromProtocol) {
48+
let _: Int = v.compute
49+
// expected-error@-1 {{cannot convert value of type '@MainActor @Sendable (@escaping () -> Void) -> Void' to specified type 'Int'}}
50+
}
51+
52+
func testFromType(v: TestFromType) {
53+
let _: Int = v.compute
54+
// expected-error@-1 {{annot convert value of type '@MainActor @Sendable (@escaping () -> Void) -> Void' to specified type 'Int'}}
55+
}
56+
57+
func testFromSuperclass(v: TestSubclass) {
58+
let _: Int = v.subCompute
59+
// expected-error@-1 {{cannot convert value of type '@MainActor @Sendable (@escaping () -> Void) -> Void' to specified type 'Int'}}
60+
}
61+
62+
func testFromMethod(v: TestFromMethod, t: TestFromMethod.Type) {
63+
let _: Int = v.compute
64+
// expected-error@-1 {{cannot convert value of type '@MainActor (@escaping () -> Void) -> Void' to specified type 'Int'}}
65+
66+
let _: Int = t.computeStatic
67+
// expected-error@-1 {{cannot convert value of type '@MainActor @Sendable (@escaping () -> Void) -> Void' to specified type 'Int'}}
68+
}
69+
70+
nonisolated func testUse(v1: TestFromProtocol, v2: TestFromType, v3: TestSubclass, v4: TestFromMethod, v5: TestFromMethod.Type) async {
71+
var val: Int = 0
72+
73+
await v1.compute { val += 1 } // No execution warning because parameter type isn't Sendable
74+
// expected-warning@-1 {{consider using asynchronous alternative function}}
75+
76+
await v1.compute { @Sendable in val += 1 } // expected-warning {{mutation of captured var 'val' in concurrently-executing code}}
77+
// expected-warning@-1 {{consider using asynchronous alternative function}}
78+
79+
await v2.compute { val += 1 } // No execution warning because parameter type isn't Sendable
80+
// expected-warning@-1 {{consider using asynchronous alternative function}}
81+
82+
await v2.compute { @Sendable in val += 1 } // expected-warning {{mutation of captured var 'val' in concurrently-executing code}}
83+
// expected-warning@-1 {{consider using asynchronous alternative function}}
84+
85+
await v3.subCompute { val += 1 } // No execution warning because parameter type isn't Sendable
86+
// expected-warning@-1 {{consider using asynchronous alternative function}}
87+
88+
await v3.subCompute { @Sendable in val += 1 } // expected-warning {{mutation of captured var 'val' in concurrently-executing code}}
89+
// expected-warning@-1 {{consider using asynchronous alternative function}}
90+
91+
await v4.compute { val += 1 } // No execution warning because parameter type isn't Sendable
92+
// expected-warning@-1 {{consider using asynchronous alternative function}}
93+
94+
await v4.compute { @Sendable in val += 1 } // expected-warning {{mutation of captured var 'val' in concurrently-executing code}}
95+
// expected-warning@-1 {{consider using asynchronous alternative function}}
96+
97+
await v5.computeStatic { val += 1 } // No execution warning because parameter type isn't Sendable
98+
// expected-warning@-1 {{consider using asynchronous alternative function}}
99+
100+
await v5.computeStatic { @Sendable in val += 1 } // expected-warning {{mutation of captured var 'val' in concurrently-executing code}}
101+
// expected-warning@-1 {{consider using asynchronous alternative function}}
102+
}

0 commit comments

Comments
 (0)