Skip to content

Commit 6f969be

Browse files
authored
Merge pull request #73738 from DougGregor/more-preconcurrency-fixes
More preconcurrency fixes
2 parents c7c244e + cc75d5e commit 6f969be

File tree

6 files changed

+75
-46
lines changed

6 files changed

+75
-46
lines changed

include/swift/Sema/Concurrency.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
#ifndef SWIFT_SEMA_CONCURRENCY_H
2121
#define SWIFT_SEMA_CONCURRENCY_H
2222

23+
#include <optional>
24+
2325
namespace swift {
2426

2527
class DeclContext;
2628
class SourceFile;
2729
class NominalTypeDecl;
2830
class VarDecl;
2931

32+
enum class DiagnosticBehavior: uint8_t;
33+
3034
/// If any of the imports in this source file was @preconcurrency but there were
3135
/// no diagnostics downgraded or suppressed due to that @preconcurrency, suggest
3236
/// that the attribute be removed.
@@ -44,6 +48,13 @@ bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
4448
bool diagnoseNonSendableFromDeinit(
4549
SourceLoc refLoc, VarDecl *var, DeclContext *dc);
4650

51+
/// Determinate the appropriate diagnostic behavior when referencing
52+
/// the given nominal type from the given declaration context.
53+
std::optional<DiagnosticBehavior>
54+
getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal,
55+
const DeclContext *fromDC,
56+
bool ignoreExplicitConformance = false);
57+
4758
} // namespace swift
4859

4960
#endif

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,7 @@ getDiagnosticBehaviorLimitForValue(SILValue value) {
6969
return {};
7070

7171
auto *fromDC = declRef.getInnermostDeclContext();
72-
auto attributedImport = nom->findImport(fromDC);
73-
if (!attributedImport ||
74-
!attributedImport->options.contains(ImportFlags::Preconcurrency))
75-
return {};
76-
77-
if (auto *sourceFile = fromDC->getParentSourceFile())
78-
sourceFile->setImportUsedPreconcurrency(*attributedImport);
79-
80-
if (hasExplicitSendableConformance(nom))
81-
return DiagnosticBehavior::Warning;
82-
83-
return attributedImport->module.importedModule->isConcurrencyChecked()
84-
? DiagnosticBehavior::Warning
85-
: DiagnosticBehavior::Ignore;
72+
return getConcurrencyDiagnosticBehaviorLimit(nom, fromDC);
8673
}
8774

8875
static std::optional<SILDeclRef> getDeclRefForCallee(SILInstruction *inst) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -835,41 +835,52 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
835835
}
836836

837837
std::optional<DiagnosticBehavior>
838-
SendableCheckContext::preconcurrencyBehavior(Decl *decl) const {
839-
if (!decl)
840-
return std::nullopt;
838+
swift::getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal,
839+
const DeclContext *fromDC,
840+
bool ignoreExplicitConformance) {
841+
ModuleDecl *importedModule = nullptr;
842+
if (nominal->getAttrs().hasAttribute<PreconcurrencyAttr>()) {
843+
// If the declaration itself has the @preconcurrency attribute,
844+
// respect it.
845+
importedModule = nominal->getParentModule();
846+
} else {
847+
// Determine whether this nominal type is visible via a @preconcurrency
848+
// import.
849+
auto import = nominal->findImport(fromDC);
850+
auto sourceFile = fromDC->getParentSourceFile();
841851

842-
if (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
843-
ModuleDecl *importedModule = nullptr;
844-
if (nominal->getAttrs().hasAttribute<PreconcurrencyAttr>()) {
845-
// If the declaration itself has the @preconcurrency attribute,
846-
// respect it.
847-
importedModule = nominal->getParentModule();
848-
} else {
849-
// Determine whether this nominal type is visible via a @preconcurrency
850-
// import.
851-
auto import = nominal->findImport(fromDC);
852-
auto sourceFile = fromDC->getParentSourceFile();
852+
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
853+
return std::nullopt;
853854

854-
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
855-
return std::nullopt;
855+
if (sourceFile)
856+
sourceFile->setImportUsedPreconcurrency(*import);
856857

857-
if (sourceFile)
858-
sourceFile->setImportUsedPreconcurrency(*import);
858+
importedModule = import->module.importedModule;
859+
}
859860

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

863-
// When the type is explicitly non-Sendable, @preconcurrency imports
864-
// downgrade the diagnostic to a warning in Swift 6.
865-
if (hasExplicitSendableConformance(nominal))
866-
return DiagnosticBehavior::Warning;
867+
// When the type is implicitly non-Sendable, `@preconcurrency` suppresses
868+
// diagnostics until the imported module enables Swift 6.
869+
return importedModule->isConcurrencyChecked()
870+
? DiagnosticBehavior::Warning
871+
: DiagnosticBehavior::Ignore;
872+
}
873+
874+
std::optional<DiagnosticBehavior>
875+
SendableCheckContext::preconcurrencyBehavior(
876+
Decl *decl,
877+
bool ignoreExplicitConformance) const {
878+
if (!decl)
879+
return std::nullopt;
867880

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

875886
return std::nullopt;
@@ -5841,8 +5852,16 @@ bool swift::checkSendableConformance(
58415852

58425853
// Sendable can only be used in the same source file.
58435854
auto conformanceDecl = conformanceDC->getAsDecl();
5844-
auto behavior = SendableCheckContext(conformanceDC, check)
5845-
.defaultDiagnosticBehavior();
5855+
SendableCheckContext checkContext(conformanceDC, check);
5856+
DiagnosticBehavior behavior = checkContext.defaultDiagnosticBehavior();
5857+
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
5858+
conformance->getProtocol()->isSpecificProtocol(
5859+
KnownProtocolKind::Sendable)) {
5860+
if (auto optBehavior = checkContext.preconcurrencyBehavior(
5861+
nominal, /*ignoreExplicitConformance=*/true))
5862+
behavior = *optBehavior;
5863+
}
5864+
58465865
if (conformanceDC->getOutermostParentSourceFile() &&
58475866
conformanceDC->getOutermostParentSourceFile() !=
58485867
nominal->getOutermostParentSourceFile()) {

lib/Sema/TypeCheckConcurrency.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,9 @@ struct SendableCheckContext {
391391
/// type in this context.
392392
DiagnosticBehavior diagnosticBehavior(NominalTypeDecl *nominal) const;
393393

394-
std::optional<DiagnosticBehavior> preconcurrencyBehavior(Decl *decl) const;
394+
std::optional<DiagnosticBehavior> preconcurrencyBehavior(
395+
Decl *decl,
396+
bool ignoreExplicitConformance = false) const;
395397

396398
/// Whether we are in an explicit conformance to Sendable.
397399
bool isExplicitSendableConformance() const;

test/Concurrency/Inputs/NonStrictModule.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ public protocol NonStrictProtocol {
1010
func send(_ body: @Sendable () -> Void)
1111
func dontSend(_ body: () -> Void)
1212
}
13+
14+
open class NonStrictClass2 { }
15+
open class NonStrictClass3 { }
16+
17+
public protocol MySendableProto: Sendable {}

test/Concurrency/predates_concurrency_import.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,8 @@ struct HasStatics {
6363
// expected-warning@-2{{static property 'ss' is not concurrency-safe because non-'Sendable' type 'StrictStruct' may have shared mutable state}}
6464
// expected-note@-3{{isolate 'ss' to a global actor, or conform 'StrictStruct' to 'Sendable'}}
6565
}
66+
67+
extension NonStrictClass2: @retroactive MySendableProto { }
68+
69+
extension NonStrictClass3: @retroactive Sendable { }
70+
// expected-warning@-1{{conformance to 'Sendable' must occur in the same source file as class 'NonStrictClass3'; use '@unchecked Sendable' for retroactive conformance}}

0 commit comments

Comments
 (0)