diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f79d13ab8af2a..ccf950a0054d1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2794,9 +2794,9 @@ NOTE(non_sendable_nominal,none, NOTE(add_nominal_sendable_conformance,none, "consider making %kind0 conform to the 'Sendable' protocol", (const ValueDecl *)) -NOTE(add_generic_parameter_sendable_conformance,none, - "consider making generic parameter %0 conform to the 'Sendable' protocol", - (Type)) +NOTE(add_generic_parameter_conformance,none, + "consider making generic parameter %0 conform to the %1 protocol", + (Type, ProtocolDecl *)) WARNING(add_predates_concurrency_import,none, "add '@preconcurrency' to %select{suppress|treat}0 " "'Sendable'-related %select{warnings|errors}0 from module %1" @@ -5941,6 +5941,8 @@ ERROR(concurrent_value_inherit,none, (bool, DeclName)) ERROR(non_sendable_type,none, "type %0 does not conform to the 'Sendable' protocol", (Type)) +GROUPED_ERROR(non_sendable_metatype_type,SendableMetatypes,none, + "type %0 does not conform to the 'SendableMetatype' protocol", (Type)) ERROR(sendable_raw_storage,none, "struct %0 with '@_rawLayout' does not conform to the 'Sendable' " "protocol", (DeclName)) diff --git a/lib/AST/ConformanceLookup.cpp b/lib/AST/ConformanceLookup.cpp index 671b2e2e3caa6..1a3db12e18fa0 100644 --- a/lib/AST/ConformanceLookup.cpp +++ b/lib/AST/ConformanceLookup.cpp @@ -173,6 +173,10 @@ static bool shouldCreateMissingConformances(Type type, ProtocolDecl *proto) { return true; } + // SendableMetatype behaves similarly to Sendable. + if (proto->isSpecificProtocol(KnownProtocolKind::SendableMetatype)) + return true; + return false; } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index d2df3648c01b3..acbe685f46d84 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3320,8 +3320,15 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, static void diagnoseMissingConformance( SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC, bool preconcurrency) { - assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable)); - diagnoseMissingSendableConformance(loc, type, fromDC, preconcurrency); + assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable) || + proto->isSpecificProtocol(KnownProtocolKind::SendableMetatype)); + + if (proto->isSpecificProtocol(KnownProtocolKind::Sendable)) + diagnoseMissingSendableConformance(loc, type, fromDC, preconcurrency); + + if (proto->isSpecificProtocol(KnownProtocolKind::SendableMetatype)) + diagnoseMissingSendableMetatypeConformance(loc, type, fromDC, + preconcurrency); } bool diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 69969c139ed6d..5ef6f99148c6a 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -764,18 +764,30 @@ static void addSendableFixIt( } } -/// Add Fix-It text for the given generic param declaration type to adopt -/// Sendable. -static void addSendableFixIt(const GenericTypeParamDecl *genericArgument, - InFlightDiagnostic &diag) { +static void addProtocolFixIt(const GenericTypeParamDecl *genericArgument, + InFlightDiagnostic &diag, StringRef protocolName) { if (genericArgument->getInherited().empty()) { auto fixItLoc = genericArgument->getLoc(); - diag.fixItInsertAfter(fixItLoc, ": Sendable"); + diag.fixItInsertAfter(fixItLoc, llvm::Twine(": ", protocolName).str()); } else { auto fixItLoc = genericArgument->getInherited().getEndLoc(); - diag.fixItInsertAfter(fixItLoc, " & Sendable"); + diag.fixItInsertAfter(fixItLoc, llvm::Twine(" & ", protocolName).str()); } } +/// Add Fix-It text for the given generic param declaration type to adopt +/// Sendable. +static void addSendableFixIt(const GenericTypeParamDecl *genericArgument, + InFlightDiagnostic &diag) { + addProtocolFixIt(genericArgument, diag, + getProtocolName(KnownProtocolKind::Sendable)); +} + +static void +addSendableMetatypeFixIt(const GenericTypeParamDecl *genericArgument, + InFlightDiagnostic &diag) { + addProtocolFixIt(genericArgument, diag, + getProtocolName(KnownProtocolKind::SendableMetatype)); +} static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) { return contextRequiresStrictConcurrencyChecking(dc, [](const AbstractClosureExpr *) { @@ -1025,7 +1037,8 @@ static bool diagnoseSingleNonSendableType( if (genericParamTypeDecl && genericParamTypeDecl->getModuleContext() == module) { auto diag = genericParamTypeDecl->diagnose( - diag::add_generic_parameter_sendable_conformance, type); + diag::add_generic_parameter_conformance, type, + ctx.getProtocol(KnownProtocolKind::Sendable)); addSendableFixIt(genericParamTypeDecl, diag); } } @@ -1242,6 +1255,32 @@ void swift::diagnoseMissingSendableConformance( loc, diag::non_sendable_type); } +void swift::diagnoseMissingSendableMetatypeConformance( + SourceLoc loc, Type type, const DeclContext *fromDC, bool preconcurrency) { + SendableCheckContext sendableContext(fromDC, preconcurrency); + DiagnosticBehavior behavior = + sendableContext.implicitSendableDiagnosticBehavior(); + + auto &ctx = type->getASTContext(); + ctx.Diags.diagnose(loc, diag::non_sendable_metatype_type, type) + .limitBehaviorWithPreconcurrency(behavior, preconcurrency); + + if (auto genericArchetype = type->getAs()) { + auto interfaceType = genericArchetype->getInterfaceType(); + if (auto genericParamType = interfaceType->getAs()) { + auto module = fromDC->getParentModule(); + auto *genericParamTypeDecl = genericParamType->getDecl(); + if (genericParamTypeDecl && + genericParamTypeDecl->getModuleContext() == module) { + auto diag = genericParamTypeDecl->diagnose( + diag::add_generic_parameter_conformance, type, + ctx.getProtocol(KnownProtocolKind::SendableMetatype)); + addSendableMetatypeFixIt(genericParamTypeDecl, diag); + } + } + } +} + namespace { /// Infer Sendable from the instance storage of the given nominal type. /// \returns \c std::nullopt if there is no way to make the type \c Sendable, diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 24498bf120269..7d91d09e528e2 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -327,6 +327,11 @@ bool diagnoseNonSendableTypesInReference( void diagnoseMissingSendableConformance( SourceLoc loc, Type type, const DeclContext *fromDC, bool preconcurrency); +/// Produce a diagnostic for a missing conformance to SendableMetatype +void diagnoseMissingSendableMetatypeConformance(SourceLoc loc, Type type, + const DeclContext *fromDC, + bool preconcurrency); + /// If the given nominal type is public and does not explicitly /// state whether it conforms to Sendable, provide a diagnostic. void diagnoseMissingExplicitSendable(NominalTypeDecl *nominal); diff --git a/test/Concurrency/predates_concurrency.swift b/test/Concurrency/predates_concurrency.swift index 4cbf2d64d4d71..f41695fa628c6 100644 --- a/test/Concurrency/predates_concurrency.swift +++ b/test/Concurrency/predates_concurrency.swift @@ -357,3 +357,16 @@ do { } } } + +func testSendableMetatypeDowngrades() { + @preconcurrency + func acceptsSendableMetatype(_: T.Type) {} + func acceptsSendableMetatypeStrict(_: T.Type) {} + + func test(t: T.Type) { // expected-complete-tns-note 2 {{consider making generic parameter 'T' conform to the 'SendableMetatype' protocol}} {{14-14=: SendableMetatype}} + acceptsSendableMetatype(t) + // expected-complete-tns-warning@-1 {{type 'T' does not conform to the 'SendableMetatype' protocol}} + acceptsSendableMetatypeStrict(t) + // expected-complete-tns-warning@-1 {{type 'T' does not conform to the 'SendableMetatype' protocol}} + } +} diff --git a/test/Concurrency/predates_concurrency_swift6.swift b/test/Concurrency/predates_concurrency_swift6.swift index 9ce556e9ba91b..b49d5d70175d5 100644 --- a/test/Concurrency/predates_concurrency_swift6.swift +++ b/test/Concurrency/predates_concurrency_swift6.swift @@ -323,3 +323,18 @@ do { case test(NS) // expected-warning {{associated value 'test' of 'Sendable'-conforming enum 'E' has non-Sendable type 'NS'}} } } + +func testSendableMetatypeDowngrades() { + @preconcurrency + func acceptsSendableMetatype(_: T.Type) {} + func acceptsSendableMetatypeStrict(_: T.Type) {} + + func testWarning(t: T.Type) { // expected-note 2 {{consider making generic parameter 'T' conform to the 'SendableMetatype' protocol}} {{21-21=: SendableMetatype}} + acceptsSendableMetatype(t) // expected-warning {{type 'T' does not conform to the 'SendableMetatype' protocol}} + acceptsSendableMetatypeStrict(t) // expected-error {{type 'T' does not conform to the 'SendableMetatype' protocol}} + } + + func testOK(t: T.Type) { + acceptsSendableMetatype(t) // Ok (because P is `Sendable` + } +} diff --git a/test/Concurrency/sendable_metatype.swift b/test/Concurrency/sendable_metatype.swift index 13b8ff91076a8..dbc590868e348 100644 --- a/test/Concurrency/sendable_metatype.swift +++ b/test/Concurrency/sendable_metatype.swift @@ -83,3 +83,18 @@ nonisolated func passMetaSmuggledAnyFromExistential(_ pqT: (P & Q).Type) { acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}} } } + + +func testSendableMetatypeDowngrades() { + @preconcurrency + func acceptsSendableMetatype(_: T.Type) { + } + + func testWarning(t: T.Type) { // expected-note {{consider making generic parameter 'T' conform to the 'SendableMetatype' protocol}} {{21-21=: SendableMetatype}} + acceptsSendableMetatype(t) // expected-warning {{type 'T' does not conform to the 'SendableMetatype' protocol}} + } + + func testWarning(t: T.Type) { // expected-note {{consider making generic parameter 'T' conform to the 'SendableMetatype' protocol}} {{24-24= & SendableMetatype}} + acceptsSendableMetatype(t) // expected-warning {{type 'T' does not conform to the 'SendableMetatype' protocol}} + } +}