Skip to content

More preconcurrency fixes #73738

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
merged 2 commits into from
May 20, 2024
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
11 changes: 11 additions & 0 deletions include/swift/Sema/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
#ifndef SWIFT_SEMA_CONCURRENCY_H
#define SWIFT_SEMA_CONCURRENCY_H

#include <optional>

namespace swift {

class DeclContext;
class SourceFile;
class NominalTypeDecl;
class VarDecl;

enum class DiagnosticBehavior: uint8_t;

/// If any of the imports in this source file was @preconcurrency but there were
/// no diagnostics downgraded or suppressed due to that @preconcurrency, suggest
/// that the attribute be removed.
Expand All @@ -44,6 +48,13 @@ bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
bool diagnoseNonSendableFromDeinit(
SourceLoc refLoc, VarDecl *var, DeclContext *dc);

/// Determinate the appropriate diagnostic behavior when referencing
/// the given nominal type from the given declaration context.
std::optional<DiagnosticBehavior>
getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal,
const DeclContext *fromDC,
bool ignoreExplicitConformance = false);

} // namespace swift

#endif
15 changes: 1 addition & 14 deletions lib/SILOptimizer/Mandatory/TransferNonSendable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,7 @@ getDiagnosticBehaviorLimitForValue(SILValue value) {
return {};

auto *fromDC = declRef.getInnermostDeclContext();
auto attributedImport = nom->findImport(fromDC);
if (!attributedImport ||
!attributedImport->options.contains(ImportFlags::Preconcurrency))
return {};

if (auto *sourceFile = fromDC->getParentSourceFile())
sourceFile->setImportUsedPreconcurrency(*attributedImport);

if (hasExplicitSendableConformance(nom))
return DiagnosticBehavior::Warning;

return attributedImport->module.importedModule->isConcurrencyChecked()
? DiagnosticBehavior::Warning
: DiagnosticBehavior::Ignore;
return getConcurrencyDiagnosticBehaviorLimit(nom, fromDC);
}

static std::optional<SILDeclRef> getDeclRefForCallee(SILInstruction *inst) {
Expand Down
81 changes: 50 additions & 31 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -835,41 +835,52 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
}

std::optional<DiagnosticBehavior>
SendableCheckContext::preconcurrencyBehavior(Decl *decl) const {
if (!decl)
return std::nullopt;
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 (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
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 (!import || !import->options.contains(ImportFlags::Preconcurrency))
return std::nullopt;
if (sourceFile)
sourceFile->setImportUsedPreconcurrency(*import);

if (sourceFile)
sourceFile->setImportUsedPreconcurrency(*import);
importedModule = import->module.importedModule;
}

importedModule = import->module.importedModule;
}
// When the type is explicitly non-Sendable, @preconcurrency imports
// downgrade the diagnostic to a warning in Swift 6.
if (!ignoreExplicitConformance &&
hasExplicitSendableConformance(nominal))
return DiagnosticBehavior::Warning;

// When the type is explicitly non-Sendable, @preconcurrency imports
// downgrade the diagnostic to a warning in Swift 6.
if (hasExplicitSendableConformance(nominal))
return DiagnosticBehavior::Warning;
// When the type is implicitly non-Sendable, `@preconcurrency` suppresses
// diagnostics until the imported module enables Swift 6.
return importedModule->isConcurrencyChecked()
? DiagnosticBehavior::Warning
: DiagnosticBehavior::Ignore;
}

std::optional<DiagnosticBehavior>
SendableCheckContext::preconcurrencyBehavior(
Decl *decl,
bool ignoreExplicitConformance) const {
if (!decl)
return std::nullopt;

// When the type is implicitly non-Sendable, `@preconcurrency` suppresses
// diagnostics until the imported module enables Swift 6.
return importedModule->isConcurrencyChecked()
? DiagnosticBehavior::Warning
: DiagnosticBehavior::Ignore;
if (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
return getConcurrencyDiagnosticBehaviorLimit(nominal, fromDC,
ignoreExplicitConformance);
}

return std::nullopt;
Expand Down Expand Up @@ -5841,8 +5852,16 @@ bool swift::checkSendableConformance(

// Sendable can only be used in the same source file.
auto conformanceDecl = conformanceDC->getAsDecl();
auto behavior = SendableCheckContext(conformanceDC, check)
.defaultDiagnosticBehavior();
SendableCheckContext checkContext(conformanceDC, check);
DiagnosticBehavior behavior = checkContext.defaultDiagnosticBehavior();
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
conformance->getProtocol()->isSpecificProtocol(
KnownProtocolKind::Sendable)) {
if (auto optBehavior = checkContext.preconcurrencyBehavior(
nominal, /*ignoreExplicitConformance=*/true))
behavior = *optBehavior;
}

if (conformanceDC->getOutermostParentSourceFile() &&
conformanceDC->getOutermostParentSourceFile() !=
nominal->getOutermostParentSourceFile()) {
Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,9 @@ struct SendableCheckContext {
/// type in this context.
DiagnosticBehavior diagnosticBehavior(NominalTypeDecl *nominal) const;

std::optional<DiagnosticBehavior> preconcurrencyBehavior(Decl *decl) const;
std::optional<DiagnosticBehavior> preconcurrencyBehavior(
Decl *decl,
bool ignoreExplicitConformance = false) const;

/// Whether we are in an explicit conformance to Sendable.
bool isExplicitSendableConformance() const;
Expand Down
5 changes: 5 additions & 0 deletions test/Concurrency/Inputs/NonStrictModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ public protocol NonStrictProtocol {
func send(_ body: @Sendable () -> Void)
func dontSend(_ body: () -> Void)
}

open class NonStrictClass2 { }
open class NonStrictClass3 { }

public protocol MySendableProto: Sendable {}
5 changes: 5 additions & 0 deletions test/Concurrency/predates_concurrency_import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ struct HasStatics {
// expected-warning@-2{{static property 'ss' is not concurrency-safe because non-'Sendable' type 'StrictStruct' may have shared mutable state}}
// expected-note@-3{{isolate 'ss' to a global actor, or conform 'StrictStruct' to 'Sendable'}}
}

extension NonStrictClass2: @retroactive MySendableProto { }

extension NonStrictClass3: @retroactive Sendable { }
// expected-warning@-1{{conformance to 'Sendable' must occur in the same source file as class 'NonStrictClass3'; use '@unchecked Sendable' for retroactive conformance}}