@@ -650,10 +650,17 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
650
650
return defaultSendableDiagnosticBehavior (fromDC->getASTContext ().LangOpts );
651
651
}
652
652
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 ) {
656
657
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 ;
657
664
658
665
// Look for any conformance to `Sendable`.
659
666
auto proto = ctx.getProtocol (KnownProtocolKind::Sendable);
@@ -662,7 +669,7 @@ static bool hasExplicitSendableConformance(NominalTypeDecl *nominal) {
662
669
663
670
// Look for a conformance. If it's present and not (directly) missing,
664
671
// we're done.
665
- auto conformance = nominal-> getParentModule () ->lookupConformance (
672
+ auto conformance = nominalModule ->lookupConformance (
666
673
nominal->getDeclaredInterfaceType (), proto, /* allowMissing=*/ true );
667
674
return conformance &&
668
675
!(isa<BuiltinProtocolConformance>(conformance.getConcrete ()) &&
@@ -706,18 +713,13 @@ static Optional<AttributedImport<ImportedModule>> findImportFor(
706
713
// / nominal type.
707
714
DiagnosticBehavior SendableCheckContext::diagnosticBehavior (
708
715
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
-
714
716
// Determine whether this nominal type is visible via a @preconcurrency
715
717
// import.
716
718
auto import = findImportFor (nominal, fromDC);
719
+ auto sourceFile = fromDC->getParentSourceFile ();
717
720
718
721
// When the type is explicitly non-Sendable...
719
- auto sourceFile = fromDC->getParentSourceFile ();
720
- if (isExplicitlyNonSendable) {
722
+ if (hasExplicitSendableConformance (nominal)) {
721
723
// @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
722
724
if (import && import ->options .contains (ImportFlags::Preconcurrency)) {
723
725
if (sourceFile)
@@ -737,7 +739,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
737
739
if (sourceFile)
738
740
sourceFile->setImportUsedPreconcurrency (*import );
739
741
740
- return nominalModule ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
742
+ return nominal ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
741
743
? DiagnosticBehavior::Warning
742
744
: DiagnosticBehavior::Ignore;
743
745
}
@@ -755,48 +757,31 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
755
757
return defaultBehavior;
756
758
}
757
759
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) {
764
763
auto behavior = DiagnosticBehavior::Unspecified;
765
764
766
- auto module = fromContext.fromDC ->getParentModule ();
767
- ASTContext &ctx = module ->getASTContext ();
768
- auto nominal = type->getAnyNominal ();
769
765
if (nominal) {
770
766
behavior = fromContext.diagnosticBehavior (nominal);
771
767
} else {
772
768
behavior = fromContext.defaultDiagnosticBehavior ();
773
769
}
774
770
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);
799
783
784
+ if (emittedDiagnostics && nominalIsImportedAndHasImplicitSendability) {
800
785
// This type was imported from another module; try to find the
801
786
// corresponding import.
802
787
Optional<AttributedImport<swift::ImportedModule>> import ;
@@ -813,6 +798,8 @@ static bool diagnoseSingleNonSendableType(
813
798
import ->importLoc .isValid () && sourceFile &&
814
799
!sourceFile->hasImportUsedPreconcurrency (*import )) {
815
800
SourceLoc importLoc = import ->importLoc ;
801
+ ASTContext &ctx = nominal->getASTContext ();
802
+
816
803
ctx.Diags .diagnose (
817
804
importLoc, diag::add_predates_concurrency_import,
818
805
ctx.LangOpts .isSwiftVersionAtLeast (6 ),
@@ -826,6 +813,51 @@ static bool diagnoseSingleNonSendableType(
826
813
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
827
814
}
828
815
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
+
829
861
bool swift::diagnoseNonSendableTypes (
830
862
Type type, SendableCheckContext fromContext, SourceLoc loc,
831
863
llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
@@ -1036,7 +1068,7 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
1036
1068
return ;
1037
1069
1038
1070
// If the conformance is explicitly stated, do nothing.
1039
- if (hasExplicitSendableConformance (nominal))
1071
+ if (hasExplicitSendableConformance (nominal, /* applyModuleDefault= */ false ))
1040
1072
return ;
1041
1073
1042
1074
// Diagnose it.
0 commit comments