@@ -770,10 +770,17 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
770770 return defaultSendableDiagnosticBehavior (fromDC->getASTContext ().LangOpts );
771771}
772772
773- // / Determine whether the given nominal type that is within the current module
774- // / has an explicit Sendable.
775- static bool hasExplicitSendableConformance (NominalTypeDecl *nominal) {
773+ // / Determine whether the given nominal type has an explicit Sendable
774+ // / conformance (regardless of its availability).
775+ static bool hasExplicitSendableConformance (NominalTypeDecl *nominal,
776+ bool applyModuleDefault = true ) {
776777 ASTContext &ctx = nominal->getASTContext ();
778+ auto nominalModule = nominal->getParentModule ();
779+
780+ // In a concurrency-checked module, a missing conformance is equivalent to
781+ // an explicitly unavailable one. If we want to apply this rule, do so now.
782+ if (applyModuleDefault && nominalModule->isConcurrencyChecked ())
783+ return true ;
777784
778785 // Look for any conformance to `Sendable`.
779786 auto proto = ctx.getProtocol (KnownProtocolKind::Sendable);
@@ -782,7 +789,7 @@ static bool hasExplicitSendableConformance(NominalTypeDecl *nominal) {
782789
783790 // Look for a conformance. If it's present and not (directly) missing,
784791 // we're done.
785- auto conformance = nominal-> getParentModule () ->lookupConformance (
792+ auto conformance = nominalModule ->lookupConformance (
786793 nominal->getDeclaredInterfaceType (), proto, /* allowMissing=*/ true );
787794 return conformance &&
788795 !(isa<BuiltinProtocolConformance>(conformance.getConcrete ()) &&
@@ -826,18 +833,13 @@ static Optional<AttributedImport<ImportedModule>> findImportFor(
826833// / nominal type.
827834DiagnosticBehavior SendableCheckContext::diagnosticBehavior (
828835 NominalTypeDecl *nominal) const {
829- // Determine whether the type was explicitly non-Sendable.
830- auto nominalModule = nominal->getParentModule ();
831- bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked () ||
832- hasExplicitSendableConformance (nominal);
833-
834836 // Determine whether this nominal type is visible via a @preconcurrency
835837 // import.
836838 auto import = findImportFor (nominal, fromDC);
839+ auto sourceFile = fromDC->getParentSourceFile ();
837840
838841 // When the type is explicitly non-Sendable...
839- auto sourceFile = fromDC->getParentSourceFile ();
840- if (isExplicitlyNonSendable) {
842+ if (hasExplicitSendableConformance (nominal)) {
841843 // @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
842844 if (import && import ->options .contains (ImportFlags::Preconcurrency)) {
843845 if (sourceFile)
@@ -857,7 +859,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
857859 if (sourceFile)
858860 sourceFile->setImportUsedPreconcurrency (*import );
859861
860- return nominalModule ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
862+ return nominal ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
861863 ? DiagnosticBehavior::Warning
862864 : DiagnosticBehavior::Ignore;
863865 }
@@ -917,29 +919,34 @@ static bool diagnoseSingleNonSendableType(
917919 diag::non_sendable_nominal, nominal->getDescriptiveKind (),
918920 nominal->getName ());
919921
920- // This type was imported from another module; try to find the
921- // corresponding import.
922- Optional<AttributedImport<swift::ImportedModule>> import ;
923- SourceFile *sourceFile = fromContext.fromDC ->getParentSourceFile ();
924- if (sourceFile) {
925- import = findImportFor (nominal, fromContext.fromDC );
926- }
922+ // When the type is explicitly Sendable *or* explicitly non-Sendable, we
923+ // assume it has been audited and `@preconcurrency` is not recommended even
924+ // though it would actually affect the diagnostic.
925+ if (!hasExplicitSendableConformance (nominal)) {
926+ // This type was imported from another module; try to find the
927+ // corresponding import.
928+ Optional<AttributedImport<swift::ImportedModule>> import ;
929+ SourceFile *sourceFile = fromContext.fromDC ->getParentSourceFile ();
930+ if (sourceFile) {
931+ import = findImportFor (nominal, fromContext.fromDC );
932+ }
927933
928- // If we found the import that makes this nominal type visible, remark
929- // that it can be @preconcurrency import.
930- // Only emit this remark once per source file, because it can happen a
931- // lot.
932- if (import && !import ->options .contains (ImportFlags::Preconcurrency) &&
933- import ->importLoc .isValid () && sourceFile &&
934- !sourceFile->hasImportUsedPreconcurrency (*import )) {
935- SourceLoc importLoc = import ->importLoc ;
936- ctx.Diags .diagnose (
937- importLoc, diag::add_predates_concurrency_import,
938- ctx.LangOpts .isSwiftVersionAtLeast (6 ),
939- nominal->getParentModule ()->getName ())
940- .fixItInsert (importLoc, " @preconcurrency " );
934+ // If we found the import that makes this nominal type visible, remark
935+ // that it can be @preconcurrency import.
936+ // Only emit this remark once per source file, because it can happen a
937+ // lot.
938+ if (import && !import ->options .contains (ImportFlags::Preconcurrency) &&
939+ import ->importLoc .isValid () && sourceFile &&
940+ !sourceFile->hasImportUsedPreconcurrency (*import )) {
941+ SourceLoc importLoc = import ->importLoc ;
942+ ctx.Diags .diagnose (
943+ importLoc, diag::add_predates_concurrency_import,
944+ ctx.LangOpts .isSwiftVersionAtLeast (6 ),
945+ nominal->getParentModule ()->getName ())
946+ .fixItInsert (importLoc, " @preconcurrency " );
941947
942- sourceFile->setImportUsedPreconcurrency (*import );
948+ sourceFile->setImportUsedPreconcurrency (*import );
949+ }
943950 }
944951 }
945952
@@ -1156,7 +1163,7 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
11561163 return ;
11571164
11581165 // If the conformance is explicitly stated, do nothing.
1159- if (hasExplicitSendableConformance (nominal))
1166+ if (hasExplicitSendableConformance (nominal, /* applyModuleDefault= */ false ))
11601167 return ;
11611168
11621169 // Diagnose it.
0 commit comments