@@ -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 }
@@ -875,48 +877,31 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
875877 return defaultBehavior;
876878}
877879
878- // / Produce a diagnostic for a single instance of a non-Sendable type where
879- // / a Sendable type is required.
880- static bool diagnoseSingleNonSendableType (
881- Type type, SendableCheckContext fromContext, SourceLoc loc,
882- llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
883-
880+ bool swift::diagnoseSendabilityErrorBasedOn (
881+ NominalTypeDecl *nominal, SendableCheckContext fromContext,
882+ llvm::function_ref<bool (DiagnosticBehavior)> diagnose) {
884883 auto behavior = DiagnosticBehavior::Unspecified;
885884
886- auto module = fromContext.fromDC ->getParentModule ();
887- ASTContext &ctx = module ->getASTContext ();
888- auto nominal = type->getAnyNominal ();
889885 if (nominal) {
890886 behavior = fromContext.diagnosticBehavior (nominal);
891887 } else {
892888 behavior = fromContext.defaultDiagnosticBehavior ();
893889 }
894890
895- bool wasSuppressed = diagnose (type, behavior);
896-
897- if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
898- // Don't emit any other diagnostics.
899- } else if (type->is <FunctionType>()) {
900- ctx.Diags .diagnose (loc, diag::nonsendable_function_type);
901- } else if (nominal && nominal->getParentModule () == module ) {
902- // If the nominal type is in the current module, suggest adding
903- // `Sendable` if it might make sense. Otherwise, just complain.
904- if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
905- auto note = nominal->diagnose (
906- diag::add_nominal_sendable_conformance,
907- nominal->getDescriptiveKind (), nominal->getName ());
908- addSendableFixIt (nominal, note, /* unchecked=*/ false );
909- } else {
910- nominal->diagnose (
911- diag::non_sendable_nominal, nominal->getDescriptiveKind (),
912- nominal->getName ());
913- }
914- } else if (nominal) {
915- // Note which nominal type does not conform to `Sendable`.
916- nominal->diagnose (
917- diag::non_sendable_nominal, nominal->getDescriptiveKind (),
918- nominal->getName ());
891+ bool wasSuppressed = diagnose (behavior);
919892
893+ bool emittedDiagnostics =
894+ behavior != DiagnosticBehavior::Ignore && !wasSuppressed;
895+
896+ // When the type is explicitly Sendable *or* explicitly non-Sendable, we
897+ // assume it has been audited and `@preconcurrency` is not recommended even
898+ // though it would actually affect the diagnostic.
899+ bool nominalIsImportedAndHasImplicitSendability =
900+ nominal &&
901+ nominal->getParentModule () != fromContext.fromDC ->getParentModule () &&
902+ !hasExplicitSendableConformance (nominal);
903+
904+ if (emittedDiagnostics && nominalIsImportedAndHasImplicitSendability) {
920905 // This type was imported from another module; try to find the
921906 // corresponding import.
922907 Optional<AttributedImport<swift::ImportedModule>> import ;
@@ -933,6 +918,8 @@ static bool diagnoseSingleNonSendableType(
933918 import ->importLoc .isValid () && sourceFile &&
934919 !sourceFile->hasImportUsedPreconcurrency (*import )) {
935920 SourceLoc importLoc = import ->importLoc ;
921+ ASTContext &ctx = nominal->getASTContext ();
922+
936923 ctx.Diags .diagnose (
937924 importLoc, diag::add_predates_concurrency_import,
938925 ctx.LangOpts .isSwiftVersionAtLeast (6 ),
@@ -946,6 +933,51 @@ static bool diagnoseSingleNonSendableType(
946933 return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
947934}
948935
936+ // / Produce a diagnostic for a single instance of a non-Sendable type where
937+ // / a Sendable type is required.
938+ static bool diagnoseSingleNonSendableType (
939+ Type type, SendableCheckContext fromContext, SourceLoc loc,
940+ llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
941+
942+ auto module = fromContext.fromDC ->getParentModule ();
943+ auto nominal = type->getAnyNominal ();
944+
945+ return diagnoseSendabilityErrorBasedOn (nominal, fromContext,
946+ [&](DiagnosticBehavior behavior) {
947+ bool wasSuppressed = diagnose (type, behavior);
948+
949+ // Don't emit the following notes if we didn't have any diagnostics to
950+ // attach them to.
951+ if (wasSuppressed || behavior == DiagnosticBehavior::Ignore)
952+ return true ;
953+
954+ if (type->is <FunctionType>()) {
955+ module ->getASTContext ().Diags
956+ .diagnose (loc, diag::nonsendable_function_type);
957+ } else if (nominal && nominal->getParentModule () == module ) {
958+ // If the nominal type is in the current module, suggest adding
959+ // `Sendable` if it might make sense. Otherwise, just complain.
960+ if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
961+ auto note = nominal->diagnose (
962+ diag::add_nominal_sendable_conformance,
963+ nominal->getDescriptiveKind (), nominal->getName ());
964+ addSendableFixIt (nominal, note, /* unchecked=*/ false );
965+ } else {
966+ nominal->diagnose (
967+ diag::non_sendable_nominal, nominal->getDescriptiveKind (),
968+ nominal->getName ());
969+ }
970+ } else if (nominal) {
971+ // Note which nominal type does not conform to `Sendable`.
972+ nominal->diagnose (
973+ diag::non_sendable_nominal, nominal->getDescriptiveKind (),
974+ nominal->getName ());
975+ }
976+
977+ return false ;
978+ });
979+ }
980+
949981bool swift::diagnoseNonSendableTypes (
950982 Type type, SendableCheckContext fromContext, SourceLoc loc,
951983 llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
@@ -1156,7 +1188,7 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
11561188 return ;
11571189
11581190 // If the conformance is explicitly stated, do nothing.
1159- if (hasExplicitSendableConformance (nominal))
1191+ if (hasExplicitSendableConformance (nominal, /* applyModuleDefault= */ false ))
11601192 return ;
11611193
11621194 // Diagnose it.
0 commit comments