Skip to content

Commit 6dccf3d

Browse files
authored
Merge pull request #71493 from xedin/rdar-122580494
[TypeChecker] Adjust witness matching to strip concurrency from ObjC …
2 parents bd41dbf + 0314ad8 commit 6dccf3d

File tree

4 files changed

+326
-0
lines changed

4 files changed

+326
-0
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,43 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11741174
// Match a type in the requirement to a type in the witness.
11751175
auto matchTypes = [&](Type reqType,
11761176
Type witnessType) -> llvm::Optional<RequirementMatch> {
1177+
// `swift_attr` attributes in the type context were ignored before,
1178+
// which means that we need to maintain status quo to avoid breaking
1179+
// witness matching by stripping everything concurrency related from
1180+
// inner types.
1181+
auto shouldStripConcurrency = [&]() {
1182+
if (!req->isObjC())
1183+
return false;
1184+
1185+
auto &ctx = dc->getASTContext();
1186+
return !(ctx.isSwiftVersionAtLeast(6) ||
1187+
ctx.LangOpts.StrictConcurrencyLevel ==
1188+
StrictConcurrency::Complete);
1189+
};
1190+
1191+
if (shouldStripConcurrency()) {
1192+
if (reqType->is<FunctionType>()) {
1193+
auto *fnTy = reqType->castTo<FunctionType>();
1194+
SmallVector<AnyFunctionType::Param, 4> params;
1195+
llvm::transform(fnTy->getParams(), std::back_inserter(params),
1196+
[&](const AnyFunctionType::Param &param) {
1197+
return param.withType(
1198+
param.getPlainType()->stripConcurrency(
1199+
/*recursive=*/true,
1200+
/*dropGlobalActor=*/true));
1201+
});
1202+
1203+
auto resultTy =
1204+
fnTy->getResult()->stripConcurrency(/*recursive=*/true,
1205+
/*dropGlobalActor=*/true);
1206+
1207+
reqType = FunctionType::get(params, resultTy, fnTy->getExtInfo());
1208+
} else {
1209+
reqType = reqType->stripConcurrency(/*recursive=*/true,
1210+
/*dropGlobalActor=*/true);
1211+
}
1212+
}
1213+
11771214
cs->addConstraint(ConstraintKind::Bind, reqType, witnessType, locator);
11781215
// FIXME: Check whether this has already failed.
11791216
return llvm::None;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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 5 \
8+
// RUN: -enable-experimental-feature SendableCompletionHandlers \
9+
// RUN: -module-name main -I %t -verify
10+
11+
// REQUIRES: objc_interop
12+
// REQUIRES: asserts
13+
14+
//--- Test.h
15+
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
16+
#define NONSENDABLE __attribute__((__swift_attr__("@_nonSendable")))
17+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
18+
19+
#pragma clang assume_nonnull begin
20+
21+
@import Foundation;
22+
23+
@interface MyValue : NSObject
24+
@end
25+
26+
SWIFT_SENDABLE
27+
@protocol P <NSObject>
28+
@end
29+
30+
@interface SendableValue : NSObject<P>
31+
@end
32+
33+
SWIFT_SENDABLE
34+
@interface SendableMyValue : MyValue
35+
@end
36+
37+
typedef void (^CompletionHandler)(void (^ SWIFT_SENDABLE)(void)) SWIFT_SENDABLE;
38+
39+
@interface Test : NSObject
40+
-(void) makeRequest:
41+
(NSString *)typeIdentifier
42+
loadHandler:(void (^SWIFT_SENDABLE )(void (SWIFT_SENDABLE ^)(void)))loadHandler;
43+
-(void) withSendableId: (void (^)(SWIFT_SENDABLE id)) handler;
44+
-(void) withSendableCustom: (void (^)(MyValue *_Nullable SWIFT_SENDABLE)) handler;
45+
-(void) withNonSendable:(NSString *)operation completionHandler:(void (^ _Nullable NONSENDABLE)(NSString *_Nullable, NSError * _Nullable)) handler;
46+
-(void) withAliasCompletionHandler:(CompletionHandler)handler;
47+
@end
48+
49+
// Placement of SWIFT_SENDABLE matters here
50+
void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^block)(void));
51+
52+
@interface TestWithSendableID<T: SWIFT_SENDABLE id> : NSObject
53+
-(void) add: (T) object;
54+
@end
55+
56+
@interface TestWithSendableSuperclass<T: MyValue *SWIFT_SENDABLE> : NSObject
57+
-(void) add: (T) object;
58+
@end
59+
60+
@protocol InnerSendableTypes
61+
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
62+
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
63+
@end
64+
65+
#pragma clang assume_nonnull end
66+
67+
//--- main.swift
68+
69+
func test_sendable_attr_in_type_context(test: Test) {
70+
let fn: (String?, (any Error)?) -> Void = { _,_ in }
71+
72+
test.withNonSendable("", completionHandler: fn) // Ok
73+
74+
test.makeRequest("id") {
75+
doSomethingConcurrently($0) // Ok
76+
}
77+
78+
test.makeRequest("id") { callback in
79+
_ = { @Sendable in
80+
callback() // Ok
81+
}
82+
}
83+
84+
test.withSendableId { id in
85+
_ = { @Sendable in
86+
print(id) // Ok
87+
}
88+
}
89+
90+
test.withSendableCustom { val in
91+
_ = { @Sendable in
92+
print(val!)
93+
}
94+
}
95+
96+
let _: (@escaping @Sendable (@escaping @Sendable () -> Void) -> Void) -> Void = test.withAliasCompletionHandler
97+
98+
test.withAliasCompletionHandler { callback in
99+
doSomethingConcurrently(callback) // Ok
100+
}
101+
102+
_ = TestWithSendableID<SendableValue>() // Ok
103+
104+
TestWithSendableID().add(MyValue())
105+
106+
TestWithSendableSuperclass().add(SendableMyValue()) // Ok
107+
108+
TestWithSendableSuperclass().add(MyValue())
109+
}
110+
111+
class TestConformanceWithStripping : InnerSendableTypes {
112+
func test(_ options: [String: Any]) { // Ok
113+
}
114+
115+
func test(withCallback name: String, handler: @escaping @MainActor ([String : Any], (any Error)?) -> Void) { // Ok
116+
}
117+
}
118+
119+
class TestConformanceWithoutStripping : InnerSendableTypes {
120+
// expected-error@-1 {{type 'TestConformanceWithoutStripping' does not conform to protocol 'InnerSendableTypes'}}
121+
122+
func test(_ options: [String: any Sendable]) {
123+
// expected-note@-1 {{candidate has non-matching type '([String : any Sendable]) -> ()'}}
124+
}
125+
126+
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) {
127+
// expected-note@-1 {{candidate has non-matching type '(String, @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) -> ()'}}
128+
}
129+
}

test/Concurrency/sendable_objc_attr_in_type_context.swift renamed to test/Concurrency/sendable_objc_attr_in_type_context_swift5_strict.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \
66
// RUN: -import-objc-header %t/src/Test.h \
7+
// RUN: -swift-version 5 \
78
// RUN: -strict-concurrency=complete \
89
// RUN: -enable-experimental-feature SendableCompletionHandlers \
910
// RUN: -module-name main -I %t -verify
@@ -14,6 +15,7 @@
1415
//--- Test.h
1516
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
1617
#define NONSENDABLE __attribute__((__swift_attr__("@_nonSendable")))
18+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
1719

1820
#pragma clang assume_nonnull begin
1921

@@ -56,6 +58,11 @@ void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^blo
5658
-(void) add: (T) object;
5759
@end
5860

61+
@protocol InnerSendableTypes
62+
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
63+
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
64+
@end
65+
5966
#pragma clang assume_nonnull end
6067

6168
//--- main.swift
@@ -105,3 +112,23 @@ func test_sendable_attr_in_type_context(test: Test) {
105112
TestWithSendableSuperclass().add(MyValue())
106113
// expected-warning@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}}
107114
}
115+
116+
class TestConformanceWithStripping : InnerSendableTypes {
117+
// expected-error@-1 {{type 'TestConformanceWithStripping' does not conform to protocol 'InnerSendableTypes'}}
118+
119+
func test(_ options: [String: Any]) {
120+
// expected-note@-1 {{candidate has non-matching type '([String : Any]) -> ()'}}
121+
}
122+
123+
func test(withCallback name: String, handler: @escaping @MainActor ([String : Any], (any Error)?) -> Void) {
124+
// expected-note@-1 {{candidate has non-matching type '(String, @escaping @MainActor ([String : Any], (any Error)?) -> Void) -> ()'}}
125+
}
126+
}
127+
128+
class TestConformanceWithoutStripping : InnerSendableTypes {
129+
func test(_ options: [String: any Sendable]) { // Ok
130+
}
131+
132+
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) { // Ok
133+
}
134+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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: -enable-experimental-feature SendableCompletionHandlers \
9+
// RUN: -module-name main -I %t -verify
10+
11+
// REQUIRES: objc_interop
12+
// REQUIRES: asserts
13+
14+
//--- Test.h
15+
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
16+
#define NONSENDABLE __attribute__((__swift_attr__("@_nonSendable")))
17+
#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor")))
18+
19+
#pragma clang assume_nonnull begin
20+
21+
@import Foundation;
22+
23+
@interface MyValue : NSObject
24+
@end
25+
26+
SWIFT_SENDABLE
27+
@protocol P <NSObject>
28+
@end
29+
30+
@interface SendableValue : NSObject<P>
31+
@end
32+
33+
SWIFT_SENDABLE
34+
@interface SendableMyValue : MyValue
35+
@end
36+
37+
typedef void (^CompletionHandler)(void (^ SWIFT_SENDABLE)(void)) SWIFT_SENDABLE;
38+
39+
@interface Test : NSObject
40+
-(void) makeRequest:
41+
(NSString *)typeIdentifier
42+
loadHandler:(void (^SWIFT_SENDABLE )(void (SWIFT_SENDABLE ^)(void)))loadHandler;
43+
-(void) withSendableId: (void (^)(SWIFT_SENDABLE id)) handler;
44+
-(void) withSendableCustom: (void (^)(MyValue *_Nullable SWIFT_SENDABLE)) handler;
45+
-(void) withNonSendable:(NSString *)operation completionHandler:(void (^ _Nullable NONSENDABLE)(NSString *_Nullable, NSError * _Nullable)) handler;
46+
-(void) withAliasCompletionHandler:(CompletionHandler)handler;
47+
@end
48+
49+
// Placement of SWIFT_SENDABLE matters here
50+
void doSomethingConcurrently(__attribute__((noescape)) void SWIFT_SENDABLE (^block)(void));
51+
52+
@interface TestWithSendableID<T: SWIFT_SENDABLE id> : NSObject
53+
-(void) add: (T) object;
54+
@end
55+
56+
@interface TestWithSendableSuperclass<T: MyValue *SWIFT_SENDABLE> : NSObject
57+
-(void) add: (T) object;
58+
@end
59+
60+
@protocol InnerSendableTypes
61+
-(void) test:(NSDictionary<NSString *, SWIFT_SENDABLE id> *)options;
62+
-(void) testWithCallback:(NSString *)name handler:(MAIN_ACTOR void (^)(NSDictionary<NSString *, SWIFT_SENDABLE id> *, NSError * _Nullable))handler;
63+
@end
64+
65+
#pragma clang assume_nonnull end
66+
67+
//--- main.swift
68+
69+
func test_sendable_attr_in_type_context(test: Test) {
70+
let fn: (String?, (any Error)?) -> Void = { _,_ in }
71+
72+
test.withNonSendable("", completionHandler: fn) // Ok
73+
74+
test.makeRequest("id") {
75+
doSomethingConcurrently($0) // Ok
76+
}
77+
78+
test.makeRequest("id") { callback in
79+
_ = { @Sendable in
80+
callback() // Ok
81+
}
82+
}
83+
84+
test.withSendableId { id in
85+
_ = { @Sendable in
86+
print(id) // Ok
87+
}
88+
}
89+
90+
test.withSendableCustom { val in
91+
_ = { @Sendable in
92+
print(val!)
93+
}
94+
}
95+
96+
let _: (@escaping @Sendable (@escaping @Sendable () -> Void) -> Void) -> Void = test.withAliasCompletionHandler
97+
98+
test.withAliasCompletionHandler { callback in
99+
doSomethingConcurrently(callback) // Ok
100+
}
101+
102+
_ = TestWithSendableID<SendableValue>() // Ok
103+
104+
// TOOD(diagnostics): Duplicate diagnostics
105+
TestWithSendableID().add(MyValue())
106+
// expected-error@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}}
107+
108+
TestWithSendableSuperclass().add(SendableMyValue()) // Ok
109+
110+
// TOOD(diagnostics): Duplicate diagnostics
111+
TestWithSendableSuperclass().add(MyValue())
112+
// expected-error@-1 3 {{type 'MyValue' does not conform to the 'Sendable' protocol}}
113+
}
114+
115+
class TestConformanceWithStripping : InnerSendableTypes {
116+
// expected-error@-1 {{type 'TestConformanceWithStripping' does not conform to protocol 'InnerSendableTypes'}}
117+
118+
func test(_ options: [String: Any]) {
119+
// expected-note@-1 {{candidate has non-matching type '([String : Any]) -> ()'}}
120+
}
121+
122+
func test(withCallback name: String, handler: @escaping @MainActor ([String : Any], (any Error)?) -> Void) {
123+
// expected-note@-1 {{candidate has non-matching type '(String, @escaping @MainActor ([String : Any], (any Error)?) -> Void) -> ()'}}
124+
}
125+
}
126+
127+
class TestConformanceWithoutStripping : InnerSendableTypes {
128+
func test(_ options: [String: any Sendable]) { // Ok
129+
}
130+
131+
func test(withCallback name: String, handler: @escaping @MainActor ([String : any Sendable], (any Error)?) -> Void) { // Ok
132+
}
133+
}

0 commit comments

Comments
 (0)