Skip to content

Commit 32a0d80

Browse files
authored
Merge pull request #82085 from xedin/rdar-149811049
[ClangImporter] SE-0463: Implement `@Sendable` inference exception for global actor isolated functions
2 parents c7d5f03 + ab227b4 commit 32a0d80

10 files changed

+206
-84
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ GROUPED_WARNING(clang_ignored_sendable_attr, ClangDeclarationImport, none,
9999
"cannot make type %0 sendable because '@Sendable' and '& Sendable' "
100100
"cannot be added to it",
101101
(Type))
102-
NOTE(clang_param_should_be_implicitly_sendable,none,
103-
"parameter should be implicitly 'Sendable' because it is a completion "
104-
"handler", ())
105102

106103
WARNING(implicit_bridging_header_imported_from_module,none,
107104
"implicit import of bridging header '%0' via module %1 "

lib/ClangImporter/ImportType.cpp

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,9 +1734,7 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17341734
ImportTypeKind importKind,
17351735
ImportTypeAttrs &attrs, clang::QualType type) {
17361736
bool isMainActor = false;
1737-
bool isSendable =
1738-
SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
1739-
importKind == ImportTypeKind::CompletionHandlerParameter;
1737+
bool isSendable = false;
17401738
bool isNonSendable = false;
17411739
bool isSending = false;
17421740

@@ -1759,8 +1757,10 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17591757
attrs |= ImportTypeAttr::MainActor;
17601758
if (isSendable)
17611759
attrs |= ImportTypeAttr::Sendable;
1762-
if (isNonSendable)
1760+
if (isNonSendable) {
17631761
attrs -= ImportTypeAttr::Sendable;
1762+
attrs -= ImportTypeAttr::DefaultsToSendable;
1763+
}
17641764
if (isSending)
17651765
attrs |= ImportTypeAttr::Sending;
17661766
}
@@ -2217,16 +2217,14 @@ applyImportTypeAttrs(ImportTypeAttrs attrs, Type type,
22172217
}
22182218
}
22192219

2220-
if (attrs.contains(ImportTypeAttr::Sendable)) {
2220+
if (attrs.contains(ImportTypeAttr::Sendable) ||
2221+
attrs.contains(ImportTypeAttr::DefaultsToSendable)) {
22212222
bool changed;
22222223
std::tie(type, changed) = GetSendableType(SwiftContext).convert(type);
22232224

22242225
// Diagnose if we couldn't find a place to add `Sendable` to the type.
22252226
if (!changed) {
22262227
addDiag(Diagnostic(diag::clang_ignored_sendable_attr, type));
2227-
2228-
if (attrs.contains(ImportTypeAttr::DefaultsToSendable))
2229-
addDiag(Diagnostic(diag::clang_param_should_be_implicitly_sendable));
22302228
}
22312229
}
22322230

@@ -2447,11 +2445,30 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
24472445
return {swiftResultTy, importedType.isImplicitlyUnwrapped()};
24482446
}
24492447

2448+
static bool isParameterContextGlobalActorIsolated(DeclContext *dc,
2449+
const clang::Decl *parent) {
2450+
if (getActorIsolationOfContext(dc).isGlobalActor())
2451+
return true;
2452+
2453+
if (!parent->hasAttrs())
2454+
return false;
2455+
2456+
for (const auto *attr : parent->getAttrs()) {
2457+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
2458+
if (isMainActorAttr(swiftAttr))
2459+
return true;
2460+
}
2461+
}
2462+
2463+
return false;
2464+
}
2465+
24502466
std::optional<ClangImporter::Implementation::ImportParameterTypeResult>
24512467
ClangImporter::Implementation::importParameterType(
2452-
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
2453-
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
2454-
bool paramIsError, bool paramIsCompletionHandler,
2468+
DeclContext *dc, const clang::Decl *parent, const clang::ParmVarDecl *param,
2469+
OptionalTypeKind optionalityOfParam, bool allowNSUIntegerAsInt,
2470+
bool isNSDictionarySubscriptGetter, bool paramIsError,
2471+
bool paramIsCompletionHandler,
24552472
std::optional<unsigned> completionHandlerErrorParamIndex,
24562473
ArrayRef<GenericTypeParamDecl *> genericParams,
24572474
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
@@ -2476,6 +2493,12 @@ ClangImporter::Implementation::importParameterType(
24762493
bool isConsuming = false;
24772494
bool isParamTypeImplicitlyUnwrapped = false;
24782495

2496+
if (SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
2497+
paramIsCompletionHandler) {
2498+
if (!isParameterContextGlobalActorIsolated(dc, parent))
2499+
attrs |= ImportTypeAttr::DefaultsToSendable;
2500+
}
2501+
24792502
if (auto optionSetEnum = importer::findOptionSetEnum(paramTy, *this)) {
24802503
swiftParamTy = optionSetEnum.getType();
24812504
} else if (isa<clang::PointerType>(paramTy) &&
@@ -2759,13 +2782,13 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
27592782

27602783
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
27612784

2762-
auto swiftParamTyOpt =
2763-
importParameterType(param, optionalityOfParam, allowNSUIntegerAsInt,
2764-
/*isNSDictionarySubscriptGetter=*/false,
2765-
/*paramIsError=*/false,
2766-
/*paramIsCompletionHandler=*/false,
2767-
/*completionHandlerErrorParamIndex=*/std::nullopt,
2768-
genericParams, paramAddDiag);
2785+
auto swiftParamTyOpt = importParameterType(
2786+
dc, clangDecl, param, optionalityOfParam, allowNSUIntegerAsInt,
2787+
/*isNSDictionarySubscriptGetter=*/false,
2788+
/*paramIsError=*/false,
2789+
/*paramIsCompletionHandler=*/false,
2790+
/*completionHandlerErrorParamIndex=*/std::nullopt, genericParams,
2791+
paramAddDiag);
27692792
if (!swiftParamTyOpt) {
27702793
addImportDiagnostic(param,
27712794
Diagnostic(diag::parameter_type_not_imported, param),
@@ -3317,7 +3340,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
33173340
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
33183341

33193342
auto swiftParamTyOpt = importParameterType(
3320-
param, optionalityOfParam, allowNSUIntegerAsIntInParam,
3343+
origDC, clangDecl, param, optionalityOfParam,
3344+
allowNSUIntegerAsIntInParam,
33213345
kind == SpecialMethodKind::NSDictionarySubscriptGetter, paramIsError,
33223346
paramIsCompletionHandler, completionHandlerErrorParamIndex,
33233347
ArrayRef<GenericTypeParamDecl *>(), paramAddDiag);

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,7 @@ enum class ImportTypeAttr : uint8_t {
202202
Sendable = 1 << 2,
203203

204204
/// Type is in a declaration where it would be imported as Sendable by
205-
/// default. This comes directly from the parameters to
206-
/// \c getImportTypeAttrs() and merely affects diagnostics.
205+
/// default. Currently used for completion handlers.
207206
DefaultsToSendable = 1 << 3,
208207

209208
/// Import the type of a parameter declared with
@@ -1467,6 +1466,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14671466

14681467
/// Import a parameter type
14691468
///
1469+
/// \param dc The declaration context in which this parameter appears.
1470+
/// \param parent The declaration with which this parameter is associated.
14701471
/// \param param The underlaying parameter declaraction.
14711472
/// \param optionalityOfParam The kind of optionality for the parameter
14721473
/// being imported.
@@ -1494,6 +1495,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14941495
///
14951496
/// \returns The imported parameter result on success, or None on failure.
14961497
std::optional<ImportParameterTypeResult> importParameterType(
1498+
DeclContext *dc, const clang::Decl *parent,
14971499
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
14981500
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
14991501
bool paramIsError, bool paramIsCompletionHandler,

test/ClangImporter/regionbasedisolation.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ extension ObjCObject {
7272
// CHECK: [[RESULT:%.*]] = alloc_stack $Array<NSObject>
7373

7474
// Our method.
75-
// CHECK: [[METHOD:%.*]] = objc_method [[SELF]], #ObjCObject.loadObjects2!foreign : (ObjCObject) -> () async throws -> [NSObject], $@convention(objc_method) (@convention(block) @Sendable (Optional<NSArray>, Optional<NSError>) -> (), ObjCObject) -> ()
75+
// CHECK: [[METHOD:%.*]] = objc_method [[SELF]], #ObjCObject.loadObjects2!foreign : (ObjCObject) -> () async throws -> [NSObject], $@convention(objc_method) (@convention(block) (Optional<NSArray>, Optional<NSError>) -> (), ObjCObject) -> ()
7676

7777
// Begin setting up the unsafe continuation for our method. Importantly note
7878
// that [[UNSAFE_CONT]] is Sendable, so we lose any connection from the
@@ -101,18 +101,17 @@ extension ObjCObject {
101101
// CHECK: copy_addr [take] [[CHECKED_CONT]] to [init] [[EXISTENTIAL_BLOCK_STORAGE]]
102102
// CHECK: merge_isolation_region [[BLOCK_STORAGE]], [[RESULT]]
103103

104-
// Then create the actual block. NOTE: Since the block is @Sendable, the block
105-
// does not propagate regions.
104+
// Then create the actual block. NOTE: Since the block is not @Sendable, the block does propagate regions.
106105
//
107-
// CHECK: [[COMPLETION_HANDLER_BLOCK:%.*]] = function_ref @$sSo7NSArrayCSgSo7NSErrorCSgIeyBhyy_SaySo8NSObjectCGTz_ : $@convention(c) @Sendable (@inout_aliasable @block_storage Any, Optional<NSArray>, Optional<NSError>) -> ()
106+
// CHECK: [[COMPLETION_HANDLER_BLOCK:%.*]] = function_ref @$sSo7NSArrayCSgSo7NSErrorCSgIeyByy_SaySo8NSObjectCGTz_ : $@convention(c) (@inout_aliasable @block_storage Any, Optional<NSArray>, Optional<NSError>) -> ()
108107
// CHECK: [[COMPLETION_BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]], invoke [[COMPLETION_HANDLER_BLOCK]]
109108
//
110-
// Since the block is @Sendable, it does not propagate the connection in
109+
// Since the block is not @Sendable, it does propagate the connection in
111110
// between self and the block storage when we just call the method. Thus we
112-
// need to perform a merge_isolation_region to communicate that the block
111+
// don't need to perform a merge_isolation_region to communicate that the block
113112
// storage and self are part of the same region.
114113
//
115-
// CHECK: merge_isolation_region [[SELF]], [[BLOCK_STORAGE]]
114+
// CHECK-NOT: merge_isolation_region [[SELF]], [[BLOCK_STORAGE]]
116115
//
117116
// Then call the method.
118117
// CHECK: apply [[METHOD]]([[COMPLETION_BLOCK]], [[SELF]])
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)