@@ -770,10 +770,17 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
770
770
return defaultSendableDiagnosticBehavior (fromDC->getASTContext ().LangOpts );
771
771
}
772
772
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 ) {
776
777
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 ;
777
784
778
785
// Look for any conformance to `Sendable`.
779
786
auto proto = ctx.getProtocol (KnownProtocolKind::Sendable);
@@ -782,7 +789,7 @@ static bool hasExplicitSendableConformance(NominalTypeDecl *nominal) {
782
789
783
790
// Look for a conformance. If it's present and not (directly) missing,
784
791
// we're done.
785
- auto conformance = nominal-> getParentModule () ->lookupConformance (
792
+ auto conformance = nominalModule ->lookupConformance (
786
793
nominal->getDeclaredInterfaceType (), proto, /* allowMissing=*/ true );
787
794
return conformance &&
788
795
!(isa<BuiltinProtocolConformance>(conformance.getConcrete ()) &&
@@ -826,18 +833,13 @@ static Optional<AttributedImport<ImportedModule>> findImportFor(
826
833
// / nominal type.
827
834
DiagnosticBehavior SendableCheckContext::diagnosticBehavior (
828
835
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
-
834
836
// Determine whether this nominal type is visible via a @preconcurrency
835
837
// import.
836
838
auto import = findImportFor (nominal, fromDC);
839
+ auto sourceFile = fromDC->getParentSourceFile ();
837
840
838
841
// When the type is explicitly non-Sendable...
839
- auto sourceFile = fromDC->getParentSourceFile ();
840
- if (isExplicitlyNonSendable) {
842
+ if (hasExplicitSendableConformance (nominal)) {
841
843
// @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
842
844
if (import && import ->options .contains (ImportFlags::Preconcurrency)) {
843
845
if (sourceFile)
@@ -857,7 +859,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
857
859
if (sourceFile)
858
860
sourceFile->setImportUsedPreconcurrency (*import );
859
861
860
- return nominalModule ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
862
+ return nominal ->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )
861
863
? DiagnosticBehavior::Warning
862
864
: DiagnosticBehavior::Ignore;
863
865
}
@@ -875,48 +877,31 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
875
877
return defaultBehavior;
876
878
}
877
879
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) {
884
883
auto behavior = DiagnosticBehavior::Unspecified;
885
884
886
- auto module = fromContext.fromDC ->getParentModule ();
887
- ASTContext &ctx = module ->getASTContext ();
888
- auto nominal = type->getAnyNominal ();
889
885
if (nominal) {
890
886
behavior = fromContext.diagnosticBehavior (nominal);
891
887
} else {
892
888
behavior = fromContext.defaultDiagnosticBehavior ();
893
889
}
894
890
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);
919
892
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) {
920
905
// This type was imported from another module; try to find the
921
906
// corresponding import.
922
907
Optional<AttributedImport<swift::ImportedModule>> import ;
@@ -933,6 +918,8 @@ static bool diagnoseSingleNonSendableType(
933
918
import ->importLoc .isValid () && sourceFile &&
934
919
!sourceFile->hasImportUsedPreconcurrency (*import )) {
935
920
SourceLoc importLoc = import ->importLoc ;
921
+ ASTContext &ctx = nominal->getASTContext ();
922
+
936
923
ctx.Diags .diagnose (
937
924
importLoc, diag::add_predates_concurrency_import,
938
925
ctx.LangOpts .isSwiftVersionAtLeast (6 ),
@@ -946,6 +933,51 @@ static bool diagnoseSingleNonSendableType(
946
933
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
947
934
}
948
935
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
+
949
981
bool swift::diagnoseNonSendableTypes (
950
982
Type type, SendableCheckContext fromContext, SourceLoc loc,
951
983
llvm::function_ref<bool (Type, DiagnosticBehavior)> diagnose) {
@@ -1156,7 +1188,7 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
1156
1188
return ;
1157
1189
1158
1190
// If the conformance is explicitly stated, do nothing.
1159
- if (hasExplicitSendableConformance (nominal))
1191
+ if (hasExplicitSendableConformance (nominal, /* applyModuleDefault= */ false ))
1160
1192
return ;
1161
1193
1162
1194
// Diagnose it.
0 commit comments