[SE-0470] Promote isolated-conformance-to-sendable-metatype protocol to error

Forming an isolated conformance to a SendableMetatype-inherting
protocol opens up a soundness hole any time the conformance is used.
Reword the recently-introduced diagnostic for this case and promote it
to an error (except when it's preconcurrency).

Fixes rdar://154808002.
This commit is contained in:
Doug Gregor
2025-07-07 11:29:25 -07:00
parent 62770ff2d7
commit 56e38b33b6
6 changed files with 63 additions and 28 deletions

View File

@@ -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.

View File

@@ -8681,9 +8681,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_WARNING(isolated_conformance_to_sendable_metatype,IsolatedConformances,none,
"%0 conformance of %1 to SendableMetatype-inheriting %kind2 can never "
"be used with generic code", (ActorIsolation, Type, 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

View File

@@ -19,29 +19,35 @@
using namespace swift;
ModuleDecl *swift::moduleImportForPreconcurrency(
NominalTypeDecl *nominal, const DeclContext *fromDC) {
// If the declaration itself has the @preconcurrency attribute,
// respect it.
if (nominal->getAttrs().hasAttribute<PreconcurrencyAttr>()) {
return nominal->getParentModule();
}
// Determine whether this nominal type is visible via a @preconcurrency
// import.
auto import = nominal->findImport(fromDC);
auto sourceFile = fromDC->getParentSourceFile();
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
return nullptr;
if (sourceFile)
sourceFile->setImportUsedPreconcurrency(*import);
return import->module.importedModule;
}
std::optional<DiagnosticBehavior>
swift::getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal,
const DeclContext *fromDC,
bool ignoreExplicitConformance) {
ModuleDecl *importedModule = nullptr;
if (nominal->getAttrs().hasAttribute<PreconcurrencyAttr>()) {
// 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();
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
return std::nullopt;
if (sourceFile)
sourceFile->setImportUsedPreconcurrency(*import);
importedModule = import->module.importedModule;
}
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.

View File

@@ -8303,18 +8303,20 @@ RawConformanceIsolationRequest::evaluate(
globalActorTypeExpr->setType(MetatypeType::get(globalActorType));
}
// Isolated conformance to a SendableMetatype-inheriting protocol can
// never be used generically. Warn about it.
// Cannot form an isolated conformance to a SendableMetatype-inheriting
// protocol. Diagnose it.
if (auto sendableMetatype =
ctx.getProtocol(KnownProtocolKind::SendableMetatype)) {
if (proto->inheritsFrom(sendableMetatype) &&
!getActorIsolation(proto).preconcurrency()) {
if (proto->inheritsFrom(sendableMetatype)) {
bool isPreconcurrency = moduleImportForPreconcurrency(
proto, conformance->getDeclContext()) != nullptr;
ctx.Diags.diagnose(
conformance->getLoc(),
diag::isolated_conformance_to_sendable_metatype,
ActorIsolation::forGlobalActor(globalActorType),
conformance->getType(),
proto);
proto)
.limitBehaviorIf(isPreconcurrency, DiagnosticBehavior::Warning);
}
}

View File

@@ -151,7 +151,7 @@ protocol R: SendableMetatype {
func f()
}
// expected-warning@+1{{main actor-isolated conformance of 'RSendableSMainActor' to SendableMetatype-inheriting protocol 'R' can never be used with generic code}}
// expected-error@+1{{cannot form main actor-isolated conformance of 'RSendableSMainActor' to SendableMetatype-inheriting protocol 'R'}}
@MainActor struct RSendableSMainActor: @MainActor R {
func f() { }
}

View File

@@ -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() { }
}