mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +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>())
|
if (auto sendableAttr = getAttribute<SendableAttr>())
|
||||||
return 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;
|
return assumedAttr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4168,7 +4168,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
|
|||||||
if (attr->isUnchecked())
|
if (attr->isUnchecked())
|
||||||
attributes.uncheckedLoc = loc;
|
attributes.uncheckedLoc = loc;
|
||||||
result.push_back({attr->getProtocol(), loc, /*inheritedTypeRepr=*/nullptr,
|
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.
|
// 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.
|
// Add protocols for any synthesized protocol attributes.
|
||||||
for (auto attr : getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
for (auto attr : getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||||
|
if (attr->isSuppressed())
|
||||||
|
continue;
|
||||||
addSynthesized(attr->getProtocol());
|
addSynthesized(attr->getProtocol());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6641,7 +6641,7 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
|
|||||||
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
for (auto attr : nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||||
auto *otherProto = attr->getProtocol();
|
auto *otherProto = attr->getProtocol();
|
||||||
if (otherProto == proto || otherProto->inheritsFrom(proto))
|
if (otherProto == proto || otherProto->inheritsFrom(proto))
|
||||||
return true;
|
return !attr->isSuppressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only consider extensions from the original module...or from an overlay
|
// 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;
|
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
|
||||||
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
|
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
|
||||||
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
|
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
|
||||||
|
const clang::SwiftAttrAttr *seenSendableSuppressionAttr = nullptr;
|
||||||
|
|
||||||
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
|
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
|
||||||
//
|
//
|
||||||
@@ -9041,6 +9042,18 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
|
|||||||
nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
|
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") {
|
if (swiftAttr->getAttribute() == "sending") {
|
||||||
// Swallow this if the feature is not enabled.
|
// Swallow this if the feature is not enabled.
|
||||||
if (!SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults))
|
if (!SwiftContext.LangOpts.hasFeature(Feature::SendingArgsAndResults))
|
||||||
@@ -9108,10 +9121,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
|
|||||||
MappedDecl->getAttrs().removeAttribute(attr);
|
MappedDecl->getAttrs().removeAttribute(attr);
|
||||||
|
|
||||||
// Some types have an implicit '@Sendable' attribute.
|
// Some types have an implicit '@Sendable' attribute.
|
||||||
if (ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
|
if ((ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
|
||||||
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
|
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
|
||||||
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
|
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
|
||||||
ClangDecl->hasAttr<clang::NSErrorDomainAttr>())
|
ClangDecl->hasAttr<clang::NSErrorDomainAttr>()) &&
|
||||||
|
!seenSendableSuppressionAttr)
|
||||||
MappedDecl->addAttribute(new (SwiftContext)
|
MappedDecl->addAttribute(new (SwiftContext)
|
||||||
SendableAttr(/*isImplicit=*/true));
|
SendableAttr(/*isImplicit=*/true));
|
||||||
|
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ bool SuppressesConformanceRequest::evaluate(Evaluator &evaluator,
|
|||||||
if (other == kp)
|
if (other == kp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto *attr :
|
||||||
|
nominal->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
|
||||||
|
if (attr->getProtocol()->isSpecificProtocol(kp) && attr->isSuppressed())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
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