Skip to content

Commit 5f91e49

Browse files
authored
Merge pull request #85105 from xedin/rdar-140928937
[AST/Sema] Allow `Sendable` suppression on Objective-C class declarations
2 parents 4be003b + 65599ce commit 5f91e49

File tree

9 files changed

+106
-16
lines changed

9 files changed

+106
-16
lines changed

include/swift/AST/Attr.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ class DeclAttribute : public AttributeBase {
256256
kind : NumExternKindBits
257257
);
258258

259-
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, 1,
260-
isUnchecked : 1
259+
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, 2,
260+
isUnchecked : 1,
261+
isSuppressed: 1
261262
);
262263

263264
SWIFT_INLINE_BITFIELD(ObjCImplementationAttr, DeclAttribute, 3,
@@ -1754,12 +1755,13 @@ class SynthesizedProtocolAttr : public DeclAttribute {
17541755

17551756
public:
17561757
SynthesizedProtocolAttr(ProtocolDecl *protocol, LazyConformanceLoader *Loader,
1757-
bool isUnchecked)
1758+
bool isUnchecked, bool isSuppressed)
17581759
: DeclAttribute(DeclAttrKind::SynthesizedProtocol, SourceLoc(),
17591760
SourceRange(),
17601761
/*Implicit=*/true),
17611762
Loader(Loader), protocol(protocol) {
17621763
Bits.SynthesizedProtocolAttr.isUnchecked = unsigned(isUnchecked);
1764+
Bits.SynthesizedProtocolAttr.isSuppressed = unsigned(isSuppressed);
17631765
}
17641766

17651767
/// Retrieve the known protocol kind naming the protocol to be
@@ -1772,6 +1774,10 @@ class SynthesizedProtocolAttr : public DeclAttribute {
17721774
return bool(Bits.SynthesizedProtocolAttr.isUnchecked);
17731775
}
17741776

1777+
bool isSuppressed() const {
1778+
return bool(Bits.SynthesizedProtocolAttr.isSuppressed);
1779+
}
1780+
17751781
/// Retrieve the lazy loader that will be used to populate the
17761782
/// synthesized conformance.
17771783
LazyConformanceLoader *getLazyLoader() const { return Loader; }
@@ -1782,12 +1788,13 @@ class SynthesizedProtocolAttr : public DeclAttribute {
17821788

17831789
SynthesizedProtocolAttr *clone(ASTContext &ctx) const {
17841790
return new (ctx) SynthesizedProtocolAttr(
1785-
protocol, getLazyLoader(), isUnchecked());
1791+
protocol, getLazyLoader(), isUnchecked(), isSuppressed());
17861792
}
17871793

17881794
bool isEquivalent(const SynthesizedProtocolAttr *other,
17891795
Decl *attachedTo) const {
17901796
return isUnchecked() == other->isUnchecked()
1797+
&& isSuppressed() == other->isSuppressed()
17911798
&& getProtocol() == other->getProtocol()
17921799
&& getLazyLoader() == other->getLazyLoader();
17931800
}

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8322,6 +8322,7 @@ swift::getInheritedForPrinting(
83228322
// Collect synthesized conformances.
83238323
llvm::SetVector<ProtocolDecl *> protocols;
83248324
llvm::TinyPtrVector<ProtocolDecl *> uncheckedProtocols;
8325+
llvm::TinyPtrVector<ProtocolDecl *> suppressedProtocols;
83258326
for (auto attr : decl->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
83268327
if (auto *proto = attr->getProtocol()) {
83278328
// FIXME: Reconstitute inverses here
@@ -8341,12 +8342,15 @@ swift::getInheritedForPrinting(
83418342
protocols.insert(proto);
83428343
if (attr->isUnchecked())
83438344
uncheckedProtocols.push_back(proto);
8345+
if (attr->isSuppressed())
8346+
suppressedProtocols.push_back(proto);
83448347
}
83458348
}
83468349

83478350
for (size_t i = 0; i < protocols.size(); i++) {
83488351
auto proto = protocols[i];
83498352
bool isUnchecked = llvm::is_contained(uncheckedProtocols, proto);
8353+
bool isSuppressed = llvm::is_contained(suppressedProtocols, proto);
83508354

83518355
if (!options.shouldPrint(proto)) {
83528356
// If private stdlib protocols are skipped and this is a private stdlib
@@ -8371,7 +8375,7 @@ swift::getInheritedForPrinting(
83718375
if (isUnchecked)
83728376
options |= ProtocolConformanceFlags::Unchecked;
83738377
Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
8374-
options});
8378+
options, isSuppressed});
83758379
}
83768380
}
83778381

lib/AST/Attr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,6 +3319,13 @@ DeclAttributes::getEffectiveSendableAttr() const {
33193319
if (auto sendableAttr = getAttribute<SendableAttr>())
33203320
return sendableAttr;
33213321

3322+
// ~Sendable on declarations imported from Objective-C.
3323+
for (auto *attr : getAttributes<SynthesizedProtocolAttr>()) {
3324+
if (attr->getProtocol()->isSpecificProtocol(KnownProtocolKind::Sendable) &&
3325+
attr->isSuppressed())
3326+
return nullptr;
3327+
}
3328+
33223329
return assumedAttr;
33233330
}
33243331

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4203,7 +4203,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
42034203
if (attr->isUnchecked())
42044204
attributes.uncheckedLoc = loc;
42054205
result.push_back({attr->getProtocol(), loc, /*inheritedTypeRepr=*/nullptr,
4206-
attributes, /*isSuppressed=*/false});
4206+
attributes, attr->isSuppressed()});
42074207
}
42084208

42094209
// Else we have access to this information on the where clause.

lib/AST/ProtocolConformance.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,8 @@ void NominalTypeDecl::prepareConformanceTable() const {
12481248

12491249
// Add protocols for any synthesized protocol attributes.
12501250
for (auto attr : getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
1251+
if (attr->isSuppressed())
1252+
continue;
12511253
addSynthesized(attr->getProtocol());
12521254
}
12531255

lib/ClangImporter/ImportDecl.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,8 @@ void ClangImporter::Implementation::addSynthesizedTypealias(
502502

503503
void ClangImporter::Implementation::addSynthesizedProtocolAttrs(
504504
NominalTypeDecl *nominal,
505-
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs, bool isUnchecked) {
505+
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs, bool isUnchecked,
506+
bool isSuppressed) {
506507
auto &ctx = nominal->getASTContext();
507508

508509
for (auto kind : synthesizedProtocolAttrs) {
@@ -511,7 +512,7 @@ void ClangImporter::Implementation::addSynthesizedProtocolAttrs(
511512
// ctx.getProtocol(kind) != nulltpr which would be nice.
512513
if (auto proto = ctx.getProtocol(kind))
513514
nominal->addAttribute(
514-
new (ctx) SynthesizedProtocolAttr(proto, this, isUnchecked));
515+
new (ctx) SynthesizedProtocolAttr(proto, this, isUnchecked, isSuppressed));
515516
}
516517
}
517518

@@ -6602,7 +6603,7 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
66026603
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
66036604
auto *otherProto = attr->getProtocol();
66046605
if (otherProto == proto || otherProto->inheritsFrom(proto))
6605-
return true;
6606+
return !attr->isSuppressed();
66066607
}
66076608

66086609
// Only consider extensions from the original module...or from an overlay
@@ -8910,6 +8911,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
89108911
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
89118912
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
89128913
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
8914+
const clang::SwiftAttrAttr *seenSendableSuppressionAttr = nullptr;
89138915

89148916
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
89158917
//
@@ -9002,6 +9004,18 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90029004
nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
90039005
}
90049006

9007+
if (swiftAttr->getAttribute() == "~Sendable") {
9008+
auto *nominal = dyn_cast<NominalTypeDecl>(MappedDecl);
9009+
if (!nominal)
9010+
continue;
9011+
9012+
seenSendableSuppressionAttr = swiftAttr;
9013+
addSynthesizedProtocolAttrs(nominal, {KnownProtocolKind::Sendable},
9014+
/*isUnchecked=*/false,
9015+
/*isSuppressed=*/true);
9016+
continue;
9017+
}
9018+
90059019
if (swiftAttr->getAttribute() == "sending") {
90069020
// Swallow this if the feature is not enabled.
90079021
if (!SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults))
@@ -9069,10 +9083,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90699083
MappedDecl->getAttrs().removeAttribute(attr);
90709084

90719085
// Some types have an implicit '@Sendable' attribute.
9072-
if (ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
9073-
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
9074-
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
9075-
ClangDecl->hasAttr<clang::NSErrorDomainAttr>())
9086+
if ((ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
9087+
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
9088+
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
9089+
ClangDecl->hasAttr<clang::NSErrorDomainAttr>()) &&
9090+
!seenSendableSuppressionAttr)
90769091
MappedDecl->addAttribute(new (SwiftContext)
90779092
SendableAttr(/*isImplicit=*/true));
90789093

@@ -9179,8 +9194,8 @@ void ClangImporter::Implementation::addExplicitProtocolConformance(
91799194
decl->getDeclaredInterfaceType(), conformsToValue);
91809195
}
91819196

9182-
decl->addAttribute(new (SwiftContext)
9183-
SynthesizedProtocolAttr(protocol, this, false));
9197+
decl->addAttribute(new (SwiftContext) SynthesizedProtocolAttr(
9198+
protocol, this, /*isUnchecked=*/false, /*isSuppressed=*/false));
91849199
} else {
91859200
HeaderLoc attrLoc((conformsToAttr)->getLocation());
91869201
diagnose(attrLoc, diag::conforms_to_not_protocol, result,

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
12641264
void addSynthesizedProtocolAttrs(
12651265
NominalTypeDecl *nominal,
12661266
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
1267-
bool isUnchecked = false);
1267+
bool isUnchecked = false,
1268+
bool isSuppressed = false);
12681269

12691270
void makeComputed(AbstractStorageDecl *storage, AccessorDecl *getter,
12701271
AccessorDecl *setter);

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ bool SuppressesConformanceRequest::evaluate(Evaluator &evaluator,
172172
if (other == kp)
173173
return true;
174174
}
175+
176+
for (auto *attr :
177+
nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
178+
if (attr->getProtocol()->isSpecificProtocol(kp) && attr->isSuppressed())
179+
return true;
180+
}
181+
175182
return false;
176183
}
177184

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 TildeSendable \
9+
// RUN: -module-name main -I %t -verify -verify-ignore-unrelated
10+
11+
// REQUIRES: objc_interop
12+
// REQUIRES: swift_feature_TildeSendable
13+
14+
//--- Test.h
15+
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
16+
#define SWIFT_NONSENDABLE_ASSUMED __attribute__((__swift_attr__("@_nonSendable(_assumed)")))
17+
#define SWIFT_SUPPRESS_SENDABLE __attribute__((__swift_attr__("~Sendable")))
18+
19+
@import Foundation;
20+
21+
// Test that `~Sendable` superseeds `@_nonSendable(_assumed)` on classes.
22+
23+
SWIFT_NONSENDABLE_ASSUMED
24+
SWIFT_SUPPRESS_SENDABLE
25+
@interface Parent : NSObject
26+
@end
27+
28+
// Test that `Sendable` superseeds `@_nonSendable(_assumed)` and `~Sendable` from the parent.
29+
30+
SWIFT_NONSENDABLE_ASSUMED
31+
SWIFT_SENDABLE
32+
@interface SendableValue : Parent
33+
@end
34+
35+
SWIFT_NONSENDABLE_ASSUMED
36+
@interface NonSendableValue : Parent
37+
@end
38+
39+
//--- main.swift
40+
func testSendable<T: Sendable>(_: T) {}
41+
42+
public func test(p: Parent, v: SendableValue, ns: NonSendableValue) {
43+
testSendable(p) // expected-error {{type 'Parent' does not conform to the 'Sendable' protocol}}
44+
testSendable(v) // Ok (no diagnostics unable unavailable conformance associated with `Parent`).
45+
testSendable(ns) // expected-error {{conformance of 'NonSendableValue' to 'Sendable' is unavailable}}
46+
}
47+

0 commit comments

Comments
 (0)