@@ -650,10 +650,17 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
650650 return defaultSendableDiagnosticBehavior (fromDC->getASTContext ().LangOpts );
651651}
652652
653- // / Determine whether the given nominal type that is within the current module
654- // / has an explicit Sendable.
655- static bool hasExplicitSendableConformance (NominalTypeDecl *nominal) {
653+ // / Determine whether the given nominal type has an explicit Sendable
654+ // / conformance (regardless of its availability).
655+ static bool hasExplicitSendableConformance (NominalTypeDecl *nominal,
656+ bool applyModuleDefault = true ) {
656657 ASTContext &ctx = nominal->getASTContext ();
658+ auto nominalModule = nominal->getParentModule ();
659+
660+ // In a concurrency-checked module, a missing conformance is equivalent to
661+ // an explicitly unavailable one. If we want to apply this rule, do so now.
662+ if (applyModuleDefault && nominalModule->isConcurrencyChecked ())
663+ return true ;
657664
658665 // Look for any conformance to `Sendable`.
659666 auto proto = ctx.getProtocol (KnownProtocolKind::Sendable);
@@ -662,7 +669,7 @@ static bool hasExplicitSendableConformance(NominalTypeDecl *nominal) {
662669
663670 // Look for a conformance. If it's present and not (directly) missing,
664671 // we're done.
665- auto conformance = nominal-> getParentModule () ->lookupConformance (
672+ auto conformance = nominalModule ->lookupConformance (
666673 nominal->getDeclaredInterfaceType (), proto, /* allowMissing=*/ true );
667674 return conformance &&
668675 !(isa<BuiltinProtocolConformance>(conformance.getConcrete ()) &&
@@ -706,18 +713,13 @@ static Optional<AttributedImport<ImportedModule>> findImportFor(
706713// / nominal type.
707714DiagnosticBehavior SendableCheckContext::diagnosticBehavior (
708715 NominalTypeDecl *nominal) const {
709- // Determine whether the type was explicitly non-Sendable.
710- auto nominalModule = nominal->getParentModule ();
711- bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked () ||
712- hasExplicitSendableConformance (nominal);
713-
714716 // Determine whether this nominal type is visible via a @preconcurrency
715717 // import.
716718 auto import = findImportFor (nominal, fromDC);
719+ auto sourceFile = fromDC->getParentSourceFile ();
717720
718721 // When the type is explicitly non-Sendable...
719- auto sourceFile = fromDC->getParentSourceFile ();
720- if (isExplicitlyNonSendable) {
722+ if (hasExplicitSendableConformance (nominal)) {
721723 // @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
722724 if (import && import ->options .contains (ImportFlags::Preconcurrency)) {
723725 if (sourceFile)
@@ -737,7 +739,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
737739 if (sourceFile)
738740 sourceFile->setImportUsedPreconcurrency (*import );
739741
740- return nominalModule ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
742+ return nominal ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
741743 ? DiagnosticBehavior::Warning
742744 : DiagnosticBehavior::Ignore;
743745 }
@@ -755,48 +757,31 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
755757 return defaultBehavior;
756758}
757759
758- // / Produce a diagnostic for a single instance of a non-Sendable type where
759- // / a Sendable type is required.
760- static bool diagnoseSingleNonSendableType (
761- Type type, SendableCheckContext fromContext, SourceLoc loc,
762- llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
763-
760+ bool swift::diagnoseSendabilityErrorBasedOn (
761+ NominalTypeDecl *nominal, SendableCheckContext fromContext,
762+ llvm::function_ref<bool (DiagnosticBehavior)> diagnose) {
764763 auto behavior = DiagnosticBehavior::Unspecified;
765764
766- auto module = fromContext.fromDC ->getParentModule ();
767- ASTContext &ctx = module ->getASTContext ();
768- auto nominal = type->getAnyNominal ();
769765 if (nominal) {
770766 behavior = fromContext.diagnosticBehavior (nominal);
771767 } else {
772768 behavior = fromContext.defaultDiagnosticBehavior ();
773769 }
774770
775- bool wasSuppressed = diagnose (type, behavior);
776-
777- if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
778- // Don't emit any other diagnostics.
779- } else if (type->is <FunctionType>()) {
780- ctx.Diags .diagnose (loc, diag::nonsendable_function_type);
781- } else if (nominal && nominal->getParentModule () == module ) {
782- // If the nominal type is in the current module, suggest adding
783- // `Sendable` if it might make sense. Otherwise, just complain.
784- if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
785- auto note = nominal->diagnose (
786- diag::add_nominal_sendable_conformance,
787- nominal->getDescriptiveKind (), nominal->getName ());
788- addSendableFixIt (nominal, note, /* unchecked=*/ false );
789- } else {
790- nominal->diagnose (
791- diag::non_sendable_nominal, nominal->getDescriptiveKind (),
792- nominal->getName ());
793- }
794- } else if (nominal) {
795- // Note which nominal type does not conform to `Sendable`.
796- nominal->diagnose (
797- diag::non_sendable_nominal, nominal->getDescriptiveKind (),
798- nominal->getName ());
771+ bool wasSuppressed = diagnose (behavior);
772+
773+ bool emittedDiagnostics =
774+ behavior != DiagnosticBehavior::Ignore && !wasSuppressed;
775+
776+ // When the type is explicitly Sendable *or* explicitly non-Sendable, we
777+ // assume it has been audited and `@preconcurrency` is not recommended even
778+ // though it would actually affect the diagnostic.
779+ bool nominalIsImportedAndHasImplicitSendability =
780+ nominal &&
781+ nominal->getParentModule () != fromContext.fromDC ->getParentModule () &&
782+ !hasExplicitSendableConformance (nominal);
799783
784+ if (emittedDiagnostics && nominalIsImportedAndHasImplicitSendability) {
800785 // This type was imported from another module; try to find the
801786 // corresponding import.
802787 Optional<AttributedImport<swift::ImportedModule>> import ;
@@ -813,6 +798,8 @@ static bool diagnoseSingleNonSendableType(
813798 import ->importLoc .isValid () && sourceFile &&
814799 !sourceFile->hasImportUsedPreconcurrency (*import )) {
815800 SourceLoc importLoc = import ->importLoc ;
801+ ASTContext &ctx = nominal->getASTContext ();
802+
816803 ctx.Diags .diagnose (
817804 importLoc, diag::add_predates_concurrency_import,
818805 ctx.LangOpts .isSwiftVersionAtLeast (6 ),
@@ -826,6 +813,51 @@ static bool diagnoseSingleNonSendableType(
826813 return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
827814}
828815
816+ // / Produce a diagnostic for a single instance of a non-Sendable type where
817+ // / a Sendable type is required.
818+ static bool diagnoseSingleNonSendableType (
819+ Type type, SendableCheckContext fromContext, SourceLoc loc,
820+ llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
821+
822+ auto module = fromContext.fromDC ->getParentModule ();
823+ auto nominal = type->getAnyNominal ();
824+
825+ return diagnoseSendabilityErrorBasedOn (nominal, fromContext,
826+ [&](DiagnosticBehavior behavior) {
827+ bool wasSuppressed = diagnose (type, behavior);
828+
829+ // Don't emit the following notes if we didn't have any diagnostics to
830+ // attach them to.
831+ if (wasSuppressed || behavior == DiagnosticBehavior::Ignore)
832+ return true ;
833+
834+ if (type->is <FunctionType>()) {
835+ module ->getASTContext ().Diags
836+ .diagnose (loc, diag::nonsendable_function_type);
837+ } else if (nominal && nominal->getParentModule () == module ) {
838+ // If the nominal type is in the current module, suggest adding
839+ // `Sendable` if it might make sense. Otherwise, just complain.
840+ if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
841+ auto note = nominal->diagnose (
842+ diag::add_nominal_sendable_conformance,
843+ nominal->getDescriptiveKind (), nominal->getName ());
844+ addSendableFixIt (nominal, note, /* unchecked=*/ false );
845+ } else {
846+ nominal->diagnose (
847+ diag::non_sendable_nominal, nominal->getDescriptiveKind (),
848+ nominal->getName ());
849+ }
850+ } else if (nominal) {
851+ // Note which nominal type does not conform to `Sendable`.
852+ nominal->diagnose (
853+ diag::non_sendable_nominal, nominal->getDescriptiveKind (),
854+ nominal->getName ());
855+ }
856+
857+ return false ;
858+ });
859+ }
860+
829861bool swift::diagnoseNonSendableTypes (
830862 Type type, SendableCheckContext fromContext, SourceLoc loc,
831863 llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
@@ -1036,7 +1068,7 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
10361068 return ;
10371069
10381070 // If the conformance is explicitly stated, do nothing.
1039- if (hasExplicitSendableConformance (nominal))
1071+ if (hasExplicitSendableConformance (nominal, /* applyModuleDefault= */ false ))
10401072 return ;
10411073
10421074 // Diagnose it.
0 commit comments