Skip to content

[6.2] Diagnose isolated conformance to SendableMetatype-inheriting protocol #82848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 25 additions & 19 deletions lib/AST/Concurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,35 @@

using namespace swift;

std::optional<DiagnosticBehavior>
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<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();
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<DiagnosticBehavior>
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.
Expand Down
25 changes: 21 additions & 4 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand All @@ -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())
Expand Down
9 changes: 9 additions & 0 deletions test/Concurrency/isolated_conformance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// ----------------------------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions test/Concurrency/isolated_conformance_6.swift
Original file line number Diff line number Diff line change
@@ -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() { }
}