Skip to content

Commit 5bc6ec6

Browse files
authored
Merge pull request #69890 from xedin/rdar-99758612
[ClangImporter] Add support for `swift_attr(“@sendable”)` on ObjC protocols
2 parents 420e0af + ad87a5c commit 5bc6ec6

File tree

6 files changed

+149
-15
lines changed

6 files changed

+149
-15
lines changed

lib/AST/NameLookup.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,18 +3186,16 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator,
31863186
ArrayRef<ProtocolDecl *>
31873187
InheritedProtocolsRequest::evaluate(Evaluator &evaluator,
31883188
ProtocolDecl *PD) const {
3189-
llvm::SmallVector<ProtocolDecl *, 2> result;
3190-
SmallPtrSet<const ProtocolDecl *, 2> known;
3191-
known.insert(PD);
3189+
llvm::SmallSetVector<ProtocolDecl *, 2> inherited;
31923190
bool anyObject = false;
3193-
for (const auto &found : getDirectlyInheritedNominalTypeDecls(PD, anyObject)) {
3194-
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
3195-
if (known.insert(proto).second)
3196-
result.push_back(proto);
3197-
}
3191+
for (const auto &found :
3192+
getDirectlyInheritedNominalTypeDecls(PD, anyObject)) {
3193+
auto proto = dyn_cast<ProtocolDecl>(found.Item);
3194+
if (proto && proto != PD)
3195+
inherited.insert(proto);
31983196
}
31993197

3200-
return PD->getASTContext().AllocateCopy(result);
3198+
return PD->getASTContext().AllocateCopy(inherited.getArrayRef());
32013199
}
32023200

32033201
ArrayRef<ValueDecl *>
@@ -3712,6 +3710,16 @@ swift::getDirectlyInheritedNominalTypeDecls(
37123710
return result;
37133711
}
37143712

3713+
// Check for SynthesizedProtocolAttrs on the protocol. ClangImporter uses
3714+
// these to add `Sendable` conformances to protocols without modifying the
3715+
// inherited type list.
3716+
for (auto attr :
3717+
protoDecl->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
3718+
auto loc = attr->getLocation();
3719+
result.push_back(
3720+
{attr->getProtocol(), loc, attr->isUnchecked() ? loc : SourceLoc()});
3721+
}
3722+
37153723
// Else we have access to this information on the where clause.
37163724
auto selfBounds = getSelfBoundsFromWhereClause(decl);
37173725
anyObject |= selfBounds.anyObject;

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ void swift::rewriting::expandDefaultRequirements(ASTContext &ctx,
858858
}
859859

860860
/// Collect structural requirements written in the inheritance clause of an
861-
/// AssociatedTypeDecl or GenericTypeParamDecl.
861+
/// AssociatedTypeDecl, GenericTypeParamDecl, or ProtocolDecl.
862862
void swift::rewriting::realizeInheritedRequirements(
863863
TypeDecl *decl, Type type, bool shouldInferRequirements,
864864
SmallVectorImpl<StructuralRequirement> &result,
@@ -891,6 +891,17 @@ void swift::rewriting::realizeInheritedRequirements(
891891

892892
realizeTypeRequirement(dc, type, inheritedType, loc, result, errors);
893893
}
894+
895+
// Also check for `SynthesizedProtocolAttr`s with additional constraints added
896+
// by ClangImporter. This is how imported protocols are marked `Sendable`
897+
// without changing their inheritance lists.
898+
auto attrs = decl->getAttrs().getAttributes<SynthesizedProtocolAttr>();
899+
for (auto attr : attrs) {
900+
auto inheritedType = attr->getProtocol()->getDeclaredType();
901+
auto loc = attr->getLocation();
902+
903+
realizeTypeRequirement(dc, type, inheritedType, loc, result, errors);
904+
}
894905
}
895906

896907
/// StructuralRequirementsRequest realizes all the user-written requirements

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5132,11 +5132,14 @@ bool swift::checkSendableConformance(
51325132
// we allow `NSObject` for Objective-C interoperability.
51335133
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
51345134
if (!superclassDecl->isNSObject()) {
5135-
classDecl->diagnose(
5136-
diag::concurrent_value_inherit,
5137-
nominal->getASTContext().LangOpts.EnableObjCInterop,
5138-
classDecl->getName());
5139-
return true;
5135+
classDecl
5136+
->diagnose(diag::concurrent_value_inherit,
5137+
nominal->getASTContext().LangOpts.EnableObjCInterop,
5138+
classDecl->getName())
5139+
.limitBehavior(behavior);
5140+
5141+
if (behavior == DiagnosticBehavior::Unspecified)
5142+
return true;
51405143
}
51415144
}
51425145
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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: -strict-concurrency=complete \
8+
// RUN: -module-name main -I %t -verify
9+
10+
// REQUIRES: objc_interop
11+
// REQUIRES: concurrency
12+
13+
//--- Test.h
14+
#define NS_SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
15+
16+
#pragma clang assume_nonnull begin
17+
18+
@import Foundation;
19+
20+
NS_SWIFT_SENDABLE
21+
@protocol MyObjCProtocol <NSObject>
22+
@property (readonly, nonatomic) NSString *test;
23+
@end
24+
25+
@protocol MyRefinedObjCProtocol <MyObjCProtocol>
26+
@end
27+
28+
#pragma clang assume_nonnull end
29+
30+
//--- main.swift
31+
struct MyStruct: Sendable {
32+
let value: any MyObjCProtocol // Ok
33+
}
34+
35+
extension Sendable {
36+
func compute() {}
37+
}
38+
39+
extension MyObjCProtocol {
40+
func test() {
41+
compute() // Ok
42+
}
43+
}
44+
45+
class K : NSObject, MyObjCProtocol {
46+
// expected-warning@-1 {{non-final class 'K' cannot conform to 'Sendable'; use '@unchecked Sendable'}}
47+
let test: String = "k"
48+
}
49+
50+
class UncheckedK : NSObject, MyObjCProtocol, @unchecked Sendable { // ok
51+
let test: String = "unchecked"
52+
}
53+
54+
class KRefined : NSObject, MyRefinedObjCProtocol {
55+
// expected-warning@-1 {{non-final class 'KRefined' cannot conform to 'Sendable'; use '@unchecked Sendable'}}
56+
let test: String = "refined"
57+
}
58+
59+
class KUncheckedRefined : NSObject, MyRefinedObjCProtocol, @unchecked Sendable { // Ok
60+
let test: String = "refined unchecked"
61+
}
62+
63+
@preconcurrency
64+
protocol P : Sendable {
65+
}
66+
67+
protocol Q : Sendable {
68+
}
69+
70+
do {
71+
class A : NSObject {}
72+
73+
final class B : A, P {
74+
// expected-warning@-1 {{'Sendable' class 'B' cannot inherit from another class other than 'NSObject'}}
75+
}
76+
77+
final class UncheckedB : A, P, @unchecked Sendable { // Ok
78+
}
79+
80+
class C : A, MyObjCProtocol {
81+
// expected-warning@-1 {{non-final class 'C' cannot conform to 'Sendable'; use '@unchecked Sendable'}}
82+
// expected-warning@-2 {{'Sendable' class 'C' cannot inherit from another class other than 'NSObject'}}
83+
let test: String = "c"
84+
}
85+
86+
class UncheckedC : A, MyObjCProtocol, @unchecked Sendable { // Ok
87+
let test: String = "c"
88+
}
89+
90+
// A warning until `-swift-version 6`
91+
final class D : A, Q {
92+
// expected-warning@-1 {{'Sendable' class 'D' cannot inherit from another class other than 'NSObject'}}
93+
}
94+
95+
final class UncheckedD : A, Q, @unchecked Sendable { // Ok
96+
}
97+
}
98+
99+
do {
100+
func testSendable<T: Sendable>(_: T) {}
101+
102+
testSendable(K()) // Ok
103+
testSendable(UncheckedK()) // Ok
104+
testSendable(KRefined()) // Ok
105+
testSendable(KUncheckedRefined()) // Ok
106+
}

test/IDE/print_objc_concurrency_interface.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ import _Concurrency
4343
// CHECK: @available(*, unavailable)
4444
// CHECK-NEXT: extension AuditedBoth : @unchecked Sendable {
4545

46+
// CHECK-LABEL: public protocol SendableProtocol
47+
// CHECK-SAME: : Sendable
48+
4649
// CHECK-LABEL: enum SendableEnum :
4750
// CHECK-SAME: @unchecked Sendable
4851

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ SENDABLE @interface AuditedSendable : NSObject @end
225225
@interface AuditedNonSendable : NSObject @end
226226
NONSENDABLE SENDABLE @interface AuditedBoth : NSObject @end
227227

228+
SENDABLE @protocol SendableProtocol @end
229+
@protocol SendableProtocolRefined <SendableProtocol> @end
230+
228231
typedef NS_ENUM(unsigned, SendableEnum) {
229232
SendableEnumFoo, SendableEnumBar
230233
};

0 commit comments

Comments
 (0)