mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[AST/Sema] Allow Sendable suppression on Objective-C class declarations
Expressed as `__swift_attr__("~Sendable")` this acts like `: ~Sendable`
on Swift type declarations and supersedes `@_nonSendable(_assumed)`.
Resolves: rdar://140928937
This commit is contained in:
@@ -3306,6 +3306,13 @@ DeclAttributes::getEffectiveSendableAttr() const {
|
||||
if (auto sendableAttr = getAttribute<SendableAttr>())
|
||||
return sendableAttr;
|
||||
|
||||
// ~Sendable on declarations imported from Objective-C.
|
||||
for (auto *attr : getAttributes<SynthesizedProtocolAttr>()) {
|
||||
if (attr->getProtocol()->isSpecificProtocol(KnownProtocolKind::Sendable) &&
|
||||
attr->isSuppressed())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return assumedAttr;
|
||||
}
|
||||
|
||||
|
||||
@@ -4168,7 +4168,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
|
||||
if (attr->isUnchecked())
|
||||
attributes.uncheckedLoc = loc;
|
||||
result.push_back({attr->getProtocol(), loc, /*inheritedTypeRepr=*/nullptr,
|
||||
attributes, /*isSuppressed=*/false});
|
||||
attributes, attr->isSuppressed()});
|
||||
}
|
||||
|
||||
// Else we have access to this information on the where clause.
|
||||
|
||||
@@ -1248,6 +1248,8 @@ void NominalTypeDecl::prepareConformanceTable() const {
|
||||
|
||||
// Add protocols for any synthesized protocol attributes.
|
||||
for (auto attr : getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||
if (attr->isSuppressed())
|
||||
continue;
|
||||
addSynthesized(attr->getProtocol());
|
||||
}
|
||||
|
||||
|
||||
@@ -6641,7 +6641,7 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
|
||||
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||
auto *otherProto = attr->getProtocol();
|
||||
if (otherProto == proto || otherProto->inheritsFrom(proto))
|
||||
return true;
|
||||
return !attr->isSuppressed();
|
||||
}
|
||||
|
||||
// Only consider extensions from the original module...or from an overlay
|
||||
@@ -8949,6 +8949,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
|
||||
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
|
||||
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
|
||||
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
|
||||
const clang::SwiftAttrAttr *seenSendableSuppressionAttr = nullptr;
|
||||
|
||||
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
|
||||
//
|
||||
@@ -9041,6 +9042,18 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
|
||||
nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
|
||||
}
|
||||
|
||||
if (swiftAttr->getAttribute() == "~Sendable") {
|
||||
auto *nominal = dyn_cast<NominalTypeDecl>(MappedDecl);
|
||||
if (!nominal)
|
||||
continue;
|
||||
|
||||
seenSendableSuppressionAttr = swiftAttr;
|
||||
addSynthesizedProtocolAttrs(nominal, {KnownProtocolKind::Sendable},
|
||||
/*isUnchecked=*/false,
|
||||
/*isSuppressed=*/true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (swiftAttr->getAttribute() == "sending") {
|
||||
// Swallow this if the feature is not enabled.
|
||||
if (!SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults))
|
||||
@@ -9108,10 +9121,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
|
||||
MappedDecl->getAttrs().removeAttribute(attr);
|
||||
|
||||
// Some types have an implicit '@Sendable' attribute.
|
||||
if (ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
|
||||
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
|
||||
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
|
||||
ClangDecl->hasAttr<clang::NSErrorDomainAttr>())
|
||||
if ((ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
|
||||
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
|
||||
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
|
||||
ClangDecl->hasAttr<clang::NSErrorDomainAttr>()) &&
|
||||
!seenSendableSuppressionAttr)
|
||||
MappedDecl->addAttribute(new (SwiftContext)
|
||||
SendableAttr(/*isImplicit=*/true));
|
||||
|
||||
|
||||
@@ -172,6 +172,13 @@ bool SuppressesConformanceRequest::evaluate(Evaluator &evaluator,
|
||||
if (other == kp)
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto *attr :
|
||||
nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||
if (attr->getProtocol()->isSpecificProtocol(kp) && attr->isSuppressed())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
47
test/Concurrency/tilde_sendable_objc.swift
Normal file
47
test/Concurrency/tilde_sendable_objc.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
// RUN: %empty-directory(%t/src)
|
||||
// RUN: %empty-directory(%t/sdk)
|
||||
// RUN: split-file %s %t/src
|
||||
|
||||
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \
|
||||
// RUN: -import-objc-header %t/src/Test.h \
|
||||
// RUN: -swift-version 6 \
|
||||
// RUN: -enable-experimental-feature TildeSendable \
|
||||
// RUN: -module-name main -I %t -verify -verify-ignore-unrelated
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
// REQUIRES: swift_feature_TildeSendable
|
||||
|
||||
//--- Test.h
|
||||
#define SWIFT_SENDABLE __attribute__((__swift_attr__("@Sendable")))
|
||||
#define SWIFT_NONSENDABLE_ASSUMED __attribute__((__swift_attr__("@_nonSendable(_assumed)")))
|
||||
#define SWIFT_SUPPRESS_SENDABLE __attribute__((__swift_attr__("~Sendable")))
|
||||
|
||||
@import Foundation;
|
||||
|
||||
// Test that `~Sendable` superseeds `@_nonSendable(_assumed)` on classes.
|
||||
|
||||
SWIFT_NONSENDABLE_ASSUMED
|
||||
SWIFT_SUPPRESS_SENDABLE
|
||||
@interface Parent : NSObject
|
||||
@end
|
||||
|
||||
// Test that `Sendable` superseeds `@_nonSendable(_assumed)` and `~Sendable` from the parent.
|
||||
|
||||
SWIFT_NONSENDABLE_ASSUMED
|
||||
SWIFT_SENDABLE
|
||||
@interface SendableValue : Parent
|
||||
@end
|
||||
|
||||
SWIFT_NONSENDABLE_ASSUMED
|
||||
@interface NonSendableValue : Parent
|
||||
@end
|
||||
|
||||
//--- main.swift
|
||||
func testSendable<T: Sendable>(_: T) {}
|
||||
|
||||
public func test(p: Parent, v: SendableValue, ns: NonSendableValue) {
|
||||
testSendable(p) // expected-error {{type 'Parent' does not conform to the 'Sendable' protocol}}
|
||||
testSendable(v) // Ok (no diagnostics unable unavailable conformance associated with `Parent`).
|
||||
testSendable(ns) // expected-error {{conformance of 'NonSendableValue' to 'Sendable' is unavailable}}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user