Skip to content

Commit ffc7982

Browse files
committed
[NFC] Helper for @preconcurrency-sensitive errors
`diagnoseAsNonSendableBasedOn()` extracts just the “make a diagnostic sensitive to @preconcurrency import of this type” logic from `diagnoseNonSendableTypes()`. We need it because conformance and override checking should be sensitive to whether the conformed-to protocol/base class was imported with @preconcurrency or not, although this change is a pure refactoring.
1 parent c4d6650 commit ffc7982

File tree

2 files changed

+108
-61
lines changed

2 files changed

+108
-61
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -877,82 +877,107 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
877877
return defaultBehavior;
878878
}
879879

880-
/// Produce a diagnostic for a single instance of a non-Sendable type where
881-
/// a Sendable type is required.
882-
static bool diagnoseSingleNonSendableType(
883-
Type type, SendableCheckContext fromContext, SourceLoc loc,
884-
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose) {
885-
880+
bool swift::diagnoseSendabilityErrorBasedOn(
881+
NominalTypeDecl *nominal, SendableCheckContext fromContext,
882+
llvm::function_ref<bool(DiagnosticBehavior)> diagnose) {
886883
auto behavior = DiagnosticBehavior::Unspecified;
887884

888-
auto module = fromContext.fromDC->getParentModule();
889-
ASTContext &ctx = module->getASTContext();
890-
auto nominal = type->getAnyNominal();
891885
if (nominal) {
892886
behavior = fromContext.diagnosticBehavior(nominal);
893887
} else {
894888
behavior = fromContext.defaultDiagnosticBehavior();
895889
}
896890

897-
bool wasSuppressed = diagnose(type, behavior);
898-
899-
if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
900-
// Don't emit any other diagnostics.
901-
} else if (type->is<FunctionType>()) {
902-
ctx.Diags.diagnose(loc, diag::nonsendable_function_type);
903-
} else if (nominal && nominal->getParentModule() == module) {
904-
// If the nominal type is in the current module, suggest adding
905-
// `Sendable` if it might make sense. Otherwise, just complain.
906-
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
907-
auto note = nominal->diagnose(
908-
diag::add_nominal_sendable_conformance,
909-
nominal->getDescriptiveKind(), nominal->getName());
910-
addSendableFixIt(nominal, note, /*unchecked=*/false);
911-
} else {
912-
nominal->diagnose(
913-
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
914-
nominal->getName());
891+
bool wasSuppressed = diagnose(behavior);
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) {
905+
// This type was imported from another module; try to find the
906+
// corresponding import.
907+
Optional<AttributedImport<swift::ImportedModule>> import;
908+
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
909+
if (sourceFile) {
910+
import = findImportFor(nominal, fromContext.fromDC);
915911
}
916-
} else if (nominal) {
917-
// Note which nominal type does not conform to `Sendable`.
918-
nominal->diagnose(
919-
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
920-
nominal->getName());
921912

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-
}
913+
// If we found the import that makes this nominal type visible, remark
914+
// that it can be @preconcurrency import.
915+
// Only emit this remark once per source file, because it can happen a
916+
// lot.
917+
if (import && !import->options.contains(ImportFlags::Preconcurrency) &&
918+
import->importLoc.isValid() && sourceFile &&
919+
!sourceFile->hasImportUsedPreconcurrency(*import)) {
920+
SourceLoc importLoc = import->importLoc;
921+
ASTContext &ctx = nominal->getASTContext();
933922

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 ");
923+
ctx.Diags.diagnose(
924+
importLoc, diag::add_predates_concurrency_import,
925+
ctx.LangOpts.isSwiftVersionAtLeast(6),
926+
nominal->getParentModule()->getName())
927+
.fixItInsert(importLoc, "@preconcurrency ");
947928

948-
sourceFile->setImportUsedPreconcurrency(*import);
949-
}
929+
sourceFile->setImportUsedPreconcurrency(*import);
950930
}
951931
}
952932

953933
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
954934
}
955935

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+
956981
bool swift::diagnoseNonSendableTypes(
957982
Type type, SendableCheckContext fromContext, SourceLoc loc,
958983
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose) {

lib/Sema/TypeCheckConcurrency.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,10 @@ struct SendableCheckContext {
313313
///
314314
/// \param diagnose Emit a diagnostic indicating that the current type
315315
/// is non-Sendable, with the suggested behavior limitation. Returns \c true
316-
/// if an error was produced.
316+
/// if it did not emit any diagnostics.
317317
///
318-
/// \returns \c true if any diagnostics were produced, \c false otherwise.
318+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
319+
/// only warnings and notes were produced.
319320
bool diagnoseNonSendableTypes(
320321
Type type, SendableCheckContext fromContext, SourceLoc loc,
321322
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose);
@@ -330,7 +331,8 @@ namespace detail {
330331
/// Diagnose any non-Sendable types that occur within the given type, using
331332
/// the given diagnostic.
332333
///
333-
/// \returns \c true if any errors were produced, \c false otherwise.
334+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
335+
/// only warnings and notes were produced.
334336
template<typename ...DiagArgs>
335337
bool diagnoseNonSendableTypes(
336338
Type type, SendableCheckContext fromContext, SourceLoc loc,
@@ -349,6 +351,26 @@ bool diagnoseNonSendableTypes(
349351
});
350352
}
351353

354+
/// Diagnose this sendability error with behavior based on the import of
355+
/// \p nominal . For instance, depending on how \p nominal is imported into
356+
/// \p fromContext , the diagnostic behavior limitation may be lower or the
357+
/// compiler might emit a fix-it adding \c \@preconcurrency to the \c import .
358+
///
359+
/// \param nominal The declaration whose import we should check, or \c nullptr
360+
/// to get behavior for a non-nominal type.
361+
///
362+
/// \param fromContext The context where the error will be emitted.
363+
///
364+
/// \param diagnose Emit a diagnostic indicating a sendability problem,
365+
/// with the suggested behavior limitation. Returns \c true
366+
/// if it did \em not emit any diagnostics.
367+
///
368+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
369+
/// only warnings and notes were produced.
370+
bool diagnoseSendabilityErrorBasedOn(
371+
NominalTypeDecl *nominal, SendableCheckContext fromContext,
372+
llvm::function_ref<bool(DiagnosticBehavior)> diagnose);
373+
352374
/// Given a set of custom attributes, pick out the global actor attributes
353375
/// and perform any necessary resolution and diagnostics, returning the
354376
/// global actor attribute and type it refers to (or \c None).

0 commit comments

Comments
 (0)