Skip to content

Commit d6254f2

Browse files
authored
Merge pull request #82536 from xedin/rdar-149811049-6.2
[6.2][ClangImporter] SE-0463: Implement `@Sendable` inference exception for global actor isolated functions
2 parents 9c2efc0 + 5638377 commit d6254f2

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
@@ -1712,9 +1712,7 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17121712
ImportTypeKind importKind,
17131713
ImportTypeAttrs &attrs, clang::QualType type) {
17141714
bool isMainActor = false;
1715-
bool isSendable =
1716-
SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
1717-
importKind == ImportTypeKind::CompletionHandlerParameter;
1715+
bool isSendable = false;
17181716
bool isNonSendable = false;
17191717

17201718
// Consider only immediate attributes, don't look through the typerefs
@@ -1733,8 +1731,10 @@ void swift::getConcurrencyAttrs(ASTContext &SwiftContext,
17331731
attrs |= ImportTypeAttr::MainActor;
17341732
if (isSendable)
17351733
attrs |= ImportTypeAttr::Sendable;
1736-
if (isNonSendable)
1734+
if (isNonSendable) {
17371735
attrs -= ImportTypeAttr::Sendable;
1736+
attrs -= ImportTypeAttr::DefaultsToSendable;
1737+
}
17381738
}
17391739

17401740
ImportedType ClangImporter::Implementation::importType(
@@ -2189,16 +2189,14 @@ applyImportTypeAttrs(ImportTypeAttrs attrs, Type type,
21892189
}
21902190
}
21912191

2192-
if (attrs.contains(ImportTypeAttr::Sendable)) {
2192+
if (attrs.contains(ImportTypeAttr::Sendable) ||
2193+
attrs.contains(ImportTypeAttr::DefaultsToSendable)) {
21932194
bool changed;
21942195
std::tie(type, changed) = GetSendableType(SwiftContext).convert(type);
21952196

21962197
// Diagnose if we couldn't find a place to add `Sendable` to the type.
21972198
if (!changed) {
21982199
addDiag(Diagnostic(diag::clang_ignored_sendable_attr, type));
2199-
2200-
if (attrs.contains(ImportTypeAttr::DefaultsToSendable))
2201-
addDiag(Diagnostic(diag::clang_param_should_be_implicitly_sendable));
22022200
}
22032201
}
22042202

@@ -2423,11 +2421,30 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
24232421
return {swiftResultTy, importedType.isImplicitlyUnwrapped()};
24242422
}
24252423

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+
24262442
std::optional<ClangImporter::Implementation::ImportParameterTypeResult>
24272443
ClangImporter::Implementation::importParameterType(
2428-
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
2429-
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
2430-
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,
24312448
std::optional<unsigned> completionHandlerErrorParamIndex,
24322449
ArrayRef<GenericTypeParamDecl *> genericParams,
24332450
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
@@ -2445,6 +2462,12 @@ ClangImporter::Implementation::importParameterType(
24452462
bool isConsuming = false;
24462463
bool isParamTypeImplicitlyUnwrapped = false;
24472464

2465+
if (SwiftContext.LangOpts.hasFeature(Feature::SendableCompletionHandlers) &&
2466+
paramIsCompletionHandler) {
2467+
if (!isParameterContextGlobalActorIsolated(dc, parent))
2468+
attrs |= ImportTypeAttr::DefaultsToSendable;
2469+
}
2470+
24482471
if (auto optionSetEnum = importer::findOptionSetEnum(paramTy, *this)) {
24492472
swiftParamTy = optionSetEnum.getType();
24502473
} else if (isa<clang::PointerType>(paramTy) &&
@@ -2719,13 +2742,13 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
27192742

27202743
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
27212744

2722-
auto swiftParamTyOpt =
2723-
importParameterType(param, optionalityOfParam, allowNSUIntegerAsInt,
2724-
/*isNSDictionarySubscriptGetter=*/false,
2725-
/*paramIsError=*/false,
2726-
/*paramIsCompletionHandler=*/false,
2727-
/*completionHandlerErrorParamIndex=*/std::nullopt,
2728-
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);
27292752
if (!swiftParamTyOpt) {
27302753
addImportDiagnostic(param,
27312754
Diagnostic(diag::parameter_type_not_imported, param),
@@ -3282,7 +3305,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
32823305
ImportDiagnosticAdder paramAddDiag(*this, clangDecl, param->getLocation());
32833306

32843307
auto swiftParamTyOpt = importParameterType(
3285-
param, optionalityOfParam, allowNSUIntegerAsIntInParam,
3308+
origDC, clangDecl, param, optionalityOfParam,
3309+
allowNSUIntegerAsIntInParam,
32863310
kind == SpecialMethodKind::NSDictionarySubscriptGetter, paramIsError,
32873311
paramIsCompletionHandler, completionHandlerErrorParamIndex,
32883312
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
@@ -1459,6 +1458,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14591458

14601459
/// Import a parameter type
14611460
///
1461+
/// \param dc The declaration context in which this parameter appears.
1462+
/// \param parent The declaration with which this parameter is associated.
14621463
/// \param param The underlaying parameter declaraction.
14631464
/// \param optionalityOfParam The kind of optionality for the parameter
14641465
/// being imported.
@@ -1486,6 +1487,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
14861487
///
14871488
/// \returns The imported parameter result on success, or None on failure.
14881489
std::optional<ImportParameterTypeResult> importParameterType(
1490+
DeclContext *dc, const clang::Decl *parent,
14891491
const clang::ParmVarDecl *param, OptionalTypeKind optionalityOfParam,
14901492
bool allowNSUIntegerAsInt, bool isNSDictionarySubscriptGetter,
14911493
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)