diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e54286ac100..9608755f57f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8950,6 +8950,13 @@ ERROR(tilde_sendable_requires_feature_flag,none, "'~Sendable' requires -enable-experimental-feature TildeSendable", ()) +NOTE(sendable_conformance_is_suppressed, none, + "%kind0 explicitly suppresses conformance to 'Sendable' protocol", + (const NominalTypeDecl *)) + +ERROR(non_sendable_type_suppressed,none, + "cannot both conform to and suppress conformance to 'Sendable'", ()) + ERROR(conformance_repression_only_on_struct_class_enum,none, "conformance to %0 can only be suppressed on structs, classes, and enums", (const ProtocolDecl *)) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 702a4aaed59..95b3cdbb6f1 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1016,12 +1016,15 @@ static bool diagnoseSingleNonSendableType( if (type->is()) { ctx.Diags.diagnose(loc, diag::nonsendable_function_type); + } else if (nominal && + nominal->suppressesConformance(KnownProtocolKind::Sendable)) { + nominal->diagnose(diag::sendable_conformance_is_suppressed, nominal); } else if (nominal && nominal->getParentModule() == module) { // If the nominal type is in the current module, suggest adding // `Sendable` if it might make sense. Otherwise, just complain. if (isa(nominal) || isa(nominal)) { - auto note = nominal->diagnose( - diag::add_nominal_sendable_conformance, nominal); + auto note = + nominal->diagnose(diag::add_nominal_sendable_conformance, nominal); addSendableFixIt(nominal, note, /*unchecked=*/false); } else { nominal->diagnose(diag::non_sendable_nominal, nominal); @@ -1302,6 +1305,9 @@ inferSendableFromInstanceStorage(NominalTypeDecl *nominal, } } + if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) + return std::nullopt; + class Visitor : public StorageVisitor { public: NominalTypeDecl *nominal; @@ -1395,6 +1401,10 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) { /*treatUsableFromInlineAsPublic=*/true).isPublic()) return; + // If `Sendable` conformance is explicitly suppressed, do nothing. + if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) + return; + // If the conformance is explicitly stated, do nothing. if (hasExplicitSendableConformance(nominal, /*applyModuleDefault=*/false)) return; @@ -7173,6 +7183,14 @@ static bool checkSendableInstanceStorage( } } + if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) { + auto *conformanceDecl = dc->getAsDecl() ? dc->getAsDecl() : nominal; + if (!isImplicitSendableCheck(check)) { + conformanceDecl->diagnose(diag::non_sendable_type_suppressed); + } + return true; + } + // Stored properties of structs and classes must have // Sendable-conforming types. class Visitor: public StorageVisitor { diff --git a/test/Sema/tilde_sendable.swift b/test/Sema/tilde_sendable.swift index e2f75edc5f7..65d5b5da38d 100644 --- a/test/Sema/tilde_sendable.swift +++ b/test/Sema/tilde_sendable.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-feature TildeSendable +// RUN: %target-typecheck-verify-swift -strict-concurrency=complete -swift-version 5 -enable-experimental-feature TildeSendable // REQUIRES: swift_feature_TildeSendable @@ -15,6 +15,7 @@ class B: ~Sendable {} enum C: ~Sendable {} struct E1: Sendable, ~Sendable {} +// expected-error@-1 {{cannot both conform to and suppress conformance to 'Sendable'}} enum E2: ~Sendable, ~Sendable {} // expected-warning {{already suppressed conformance to 'Sendable'}} @@ -32,3 +33,22 @@ struct Generic { // expected-error {{conformance to 'Sendable' can var x: some BinaryInteger & ~Sendable // expected-error {{type 'Sendable' cannot be suppressed}} +protocol W: Sendable { +} + +// Check that inference is suppressed by the annotation. +do { + struct NonSendable: ~Sendable { + // expected-note@-1 {{struct 'NonSendable' explicitly suppresses conformance to 'Sendable' protocol}} + let x: Int = 0 + } + + struct NoInference: W, ~Sendable { + // expected-error@-1 {{cannot both conform to and suppress conformance to 'Sendable'}} + } + + func check(_: T) {} + + check(NonSendable()) // expected-warning {{type 'NonSendable' does not conform to the 'Sendable' protocol}} + check(NoInference()) // Ok +}