diff --git a/include/swift/AST/Concurrency.h b/include/swift/AST/Concurrency.h index 3d19fa40bbe80..c5e0de233c4b4 100644 --- a/include/swift/AST/Concurrency.h +++ b/include/swift/AST/Concurrency.h @@ -19,6 +19,11 @@ namespace swift { +/// Find the imported module that treats the given nominal type as "preconcurrency", or return `nullptr` +/// if there is no such module. +ModuleDecl *moduleImportForPreconcurrency(NominalTypeDecl *nominal, + const DeclContext *fromDC); + /// Determinate the appropriate diagnostic behavior to used when emitting /// concurrency diagnostics when referencing the given nominal type from the /// given declaration context. diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8dbf8e8014c5d..0c646d2c93458 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8591,6 +8591,9 @@ GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none, GROUPED_WARNING(isolated_conformance_will_become_nonisolated,IsolatedConformances,none, "conformance of %0 to %1 should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'", (const ValueDecl *, const ValueDecl *)) +GROUPED_ERROR(isolated_conformance_to_sendable_metatype,IsolatedConformances,none, + "cannot form %0 conformance of %1 to SendableMetatype-inheriting %kind2", + (ActorIsolation, Type, const ValueDecl *)) //===----------------------------------------------------------------------===// // MARK: @_inheritActorContext diff --git a/lib/AST/Concurrency.cpp b/lib/AST/Concurrency.cpp index 958ac706c692e..c2929dcf2c0dc 100644 --- a/lib/AST/Concurrency.cpp +++ b/lib/AST/Concurrency.cpp @@ -19,29 +19,35 @@ using namespace swift; -std::optional -swift::getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal, - const DeclContext *fromDC, - bool ignoreExplicitConformance) { - ModuleDecl *importedModule = nullptr; +ModuleDecl *swift::moduleImportForPreconcurrency( + NominalTypeDecl *nominal, const DeclContext *fromDC) { + // If the declaration itself has the @preconcurrency attribute, + // respect it. if (nominal->getAttrs().hasAttribute()) { - // If the declaration itself has the @preconcurrency attribute, - // respect it. - importedModule = nominal->getParentModule(); - } else { - // Determine whether this nominal type is visible via a @preconcurrency - // import. - auto import = nominal->findImport(fromDC); - auto sourceFile = fromDC->getParentSourceFile(); + return nominal->getParentModule(); + } - if (!import || !import->options.contains(ImportFlags::Preconcurrency)) - return std::nullopt; + // Determine whether this nominal type is visible via a @preconcurrency + // import. + auto import = nominal->findImport(fromDC); + auto sourceFile = fromDC->getParentSourceFile(); - if (sourceFile) - sourceFile->setImportUsedPreconcurrency(*import); + if (!import || !import->options.contains(ImportFlags::Preconcurrency)) + return nullptr; - importedModule = import->module.importedModule; - } + if (sourceFile) + sourceFile->setImportUsedPreconcurrency(*import); + + return import->module.importedModule; +} + +std::optional +swift::getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal, + const DeclContext *fromDC, + bool ignoreExplicitConformance) { + ModuleDecl *importedModule = moduleImportForPreconcurrency(nominal, fromDC); + if (!importedModule) + return std::nullopt; // When the type is explicitly non-Sendable, @preconcurrency imports // downgrade the diagnostic to a warning in Swift 6. diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 22ec5ab814e78..5ac71aee42138 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -8276,6 +8276,10 @@ RawConformanceIsolationRequest::evaluate( if (rootNormal->getOptions().contains(ProtocolConformanceFlags::Nonisolated)) return ActorIsolation::forNonisolated(false); + auto dc = conformance->getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + auto proto = conformance->getProtocol(); + // If there is an explicitly-specified global actor on the isolation, // resolve it and report it. if (auto globalActorTypeExpr = rootNormal->getExplicitGlobalActorIsolation()) { @@ -8295,16 +8299,29 @@ RawConformanceIsolationRequest::evaluate( globalActorTypeExpr->setType(MetatypeType::get(globalActorType)); } + // Cannot form an isolated conformance to a SendableMetatype-inheriting + // protocol. Diagnose it. + if (auto sendableMetatype = + ctx.getProtocol(KnownProtocolKind::SendableMetatype)) { + if (proto->inheritsFrom(sendableMetatype)) { + bool isPreconcurrency = moduleImportForPreconcurrency( + proto, conformance->getDeclContext()) != nullptr; + ctx.Diags.diagnose( + rootNormal->getLoc(), + diag::isolated_conformance_to_sendable_metatype, + ActorIsolation::forGlobalActor(globalActorType), + conformance->getType(), + proto) + .limitBehaviorIf(isPreconcurrency, DiagnosticBehavior::Warning); + } + } + // FIXME: Make sure the type actually is a global actor type, map it into // context, etc. return ActorIsolation::forGlobalActor(globalActorType); } - auto dc = rootNormal->getDeclContext(); - ASTContext &ctx = dc->getASTContext(); - auto proto = rootNormal->getProtocol(); - // If the protocol itself is isolated, don't infer isolation for the // conformance. if (getActorIsolation(proto).isActorIsolated()) diff --git a/test/Concurrency/isolated_conformance.swift b/test/Concurrency/isolated_conformance.swift index 52c3c83661424..8f71f601b24be 100644 --- a/test/Concurrency/isolated_conformance.swift +++ b/test/Concurrency/isolated_conformance.swift @@ -147,6 +147,15 @@ struct PSendableS: @MainActor PSendable { // expected-note{{requirement specifie func f() { } } +protocol R: SendableMetatype { + func f() +} + +// expected-error@+1{{cannot form main actor-isolated conformance of 'RSendableSMainActor' to SendableMetatype-inheriting protocol 'R'}} +@MainActor struct RSendableSMainActor: @MainActor R { + func f() { } +} + // ---------------------------------------------------------------------------- // Use checking of isolated conformances. // ---------------------------------------------------------------------------- diff --git a/test/Concurrency/isolated_conformance_6.swift b/test/Concurrency/isolated_conformance_6.swift new file mode 100644 index 0000000000000..c77dad3f1520b --- /dev/null +++ b/test/Concurrency/isolated_conformance_6.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 %s + +// REQUIRES: concurrency + +protocol P: SendableMetatype { + func f() +} + +@preconcurrency +protocol Q: SendableMetatype { + func f() +} + +// expected-error@+1{{cannot form main actor-isolated conformance of 'PSendableSMainActor' to SendableMetatype-inheriting protocol 'P'}} +@MainActor struct PSendableSMainActor: @MainActor P { + func f() { } +} + +// expected-warning@+1{{cannot form main actor-isolated conformance of 'QSendableSMainActor' to SendableMetatype-inheriting protocol 'Q'}} +@MainActor struct QSendableSMainActor: @MainActor Q { + func f() { } +}