Skip to content

Commit c5fde06

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 9cbac44 commit c5fde06

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
@@ -757,82 +757,107 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
757757
return defaultBehavior;
758758
}
759759

760-
/// Produce a diagnostic for a single instance of a non-Sendable type where
761-
/// a Sendable type is required.
762-
static bool diagnoseSingleNonSendableType(
763-
Type type, SendableCheckContext fromContext, SourceLoc loc,
764-
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose) {
765-
760+
bool swift::diagnoseSendabilityErrorBasedOn(
761+
NominalTypeDecl *nominal, SendableCheckContext fromContext,
762+
llvm::function_ref<bool(DiagnosticBehavior)> diagnose) {
766763
auto behavior = DiagnosticBehavior::Unspecified;
767764

768-
auto module = fromContext.fromDC->getParentModule();
769-
ASTContext &ctx = module->getASTContext();
770-
auto nominal = type->getAnyNominal();
771765
if (nominal) {
772766
behavior = fromContext.diagnosticBehavior(nominal);
773767
} else {
774768
behavior = fromContext.defaultDiagnosticBehavior();
775769
}
776770

777-
bool wasSuppressed = diagnose(type, behavior);
778-
779-
if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
780-
// Don't emit any other diagnostics.
781-
} else if (type->is<FunctionType>()) {
782-
ctx.Diags.diagnose(loc, diag::nonsendable_function_type);
783-
} else if (nominal && nominal->getParentModule() == module) {
784-
// If the nominal type is in the current module, suggest adding
785-
// `Sendable` if it might make sense. Otherwise, just complain.
786-
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
787-
auto note = nominal->diagnose(
788-
diag::add_nominal_sendable_conformance,
789-
nominal->getDescriptiveKind(), nominal->getName());
790-
addSendableFixIt(nominal, note, /*unchecked=*/false);
791-
} else {
792-
nominal->diagnose(
793-
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
794-
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);
783+
784+
if (emittedDiagnostics && nominalIsImportedAndHasImplicitSendability) {
785+
// This type was imported from another module; try to find the
786+
// corresponding import.
787+
Optional<AttributedImport<swift::ImportedModule>> import;
788+
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
789+
if (sourceFile) {
790+
import = findImportFor(nominal, fromContext.fromDC);
795791
}
796-
} else if (nominal) {
797-
// Note which nominal type does not conform to `Sendable`.
798-
nominal->diagnose(
799-
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
800-
nominal->getName());
801792

802-
// When the type is explicitly Sendable *or* explicitly non-Sendable, we
803-
// assume it has been audited and `@preconcurrency` is not recommended even
804-
// though it would actually affect the diagnostic.
805-
if (!hasExplicitSendableConformance(nominal)) {
806-
// This type was imported from another module; try to find the
807-
// corresponding import.
808-
Optional<AttributedImport<swift::ImportedModule>> import;
809-
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
810-
if (sourceFile) {
811-
import = findImportFor(nominal, fromContext.fromDC);
812-
}
793+
// If we found the import that makes this nominal type visible, remark
794+
// that it can be @preconcurrency import.
795+
// Only emit this remark once per source file, because it can happen a
796+
// lot.
797+
if (import && !import->options.contains(ImportFlags::Preconcurrency) &&
798+
import->importLoc.isValid() && sourceFile &&
799+
!sourceFile->hasImportUsedPreconcurrency(*import)) {
800+
SourceLoc importLoc = import->importLoc;
801+
ASTContext &ctx = nominal->getASTContext();
813802

814-
// If we found the import that makes this nominal type visible, remark
815-
// that it can be @preconcurrency import.
816-
// Only emit this remark once per source file, because it can happen a
817-
// lot.
818-
if (import && !import->options.contains(ImportFlags::Preconcurrency) &&
819-
import->importLoc.isValid() && sourceFile &&
820-
!sourceFile->hasImportUsedPreconcurrency(*import)) {
821-
SourceLoc importLoc = import->importLoc;
822-
ctx.Diags.diagnose(
823-
importLoc, diag::add_predates_concurrency_import,
824-
ctx.LangOpts.isSwiftVersionAtLeast(6),
825-
nominal->getParentModule()->getName())
826-
.fixItInsert(importLoc, "@preconcurrency ");
803+
ctx.Diags.diagnose(
804+
importLoc, diag::add_predates_concurrency_import,
805+
ctx.LangOpts.isSwiftVersionAtLeast(6),
806+
nominal->getParentModule()->getName())
807+
.fixItInsert(importLoc, "@preconcurrency ");
827808

828-
sourceFile->setImportUsedPreconcurrency(*import);
829-
}
809+
sourceFile->setImportUsedPreconcurrency(*import);
830810
}
831811
}
832812

833813
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
834814
}
835815

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+
836861
bool swift::diagnoseNonSendableTypes(
837862
Type type, SendableCheckContext fromContext, SourceLoc loc,
838863
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
@@ -353,9 +353,10 @@ struct SendableCheckContext {
353353
///
354354
/// \param diagnose Emit a diagnostic indicating that the current type
355355
/// is non-Sendable, with the suggested behavior limitation. Returns \c true
356-
/// if an error was produced.
356+
/// if it did not emit any diagnostics.
357357
///
358-
/// \returns \c true if any diagnostics were produced, \c false otherwise.
358+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
359+
/// only warnings and notes were produced.
359360
bool diagnoseNonSendableTypes(
360361
Type type, SendableCheckContext fromContext, SourceLoc loc,
361362
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose);
@@ -370,7 +371,8 @@ namespace detail {
370371
/// Diagnose any non-Sendable types that occur within the given type, using
371372
/// the given diagnostic.
372373
///
373-
/// \returns \c true if any errors were produced, \c false otherwise.
374+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
375+
/// only warnings and notes were produced.
374376
template<typename ...DiagArgs>
375377
bool diagnoseNonSendableTypes(
376378
Type type, SendableCheckContext fromContext, SourceLoc loc,
@@ -389,6 +391,26 @@ bool diagnoseNonSendableTypes(
389391
});
390392
}
391393

394+
/// Diagnose this sendability error with behavior based on the import of
395+
/// \p nominal . For instance, depending on how \p nominal is imported into
396+
/// \p fromContext , the diagnostic behavior limitation may be lower or the
397+
/// compiler might emit a fix-it adding \c \@preconcurrency to the \c import .
398+
///
399+
/// \param nominal The declaration whose import we should check, or \c nullptr
400+
/// to get behavior for a non-nominal type.
401+
///
402+
/// \param fromContext The context where the error will be emitted.
403+
///
404+
/// \param diagnose Emit a diagnostic indicating a sendability problem,
405+
/// with the suggested behavior limitation. Returns \c true
406+
/// if it did \em not emit any diagnostics.
407+
///
408+
/// \returns \c true if any errors were produced, \c false if no diagnostics or
409+
/// only warnings and notes were produced.
410+
bool diagnoseSendabilityErrorBasedOn(
411+
NominalTypeDecl *nominal, SendableCheckContext fromContext,
412+
llvm::function_ref<bool(DiagnosticBehavior)> diagnose);
413+
392414
/// Given a set of custom attributes, pick out the global actor attributes
393415
/// and perform any necessary resolution and diagnostics, returning the
394416
/// global actor attribute and type it refers to (or \c None).

0 commit comments

Comments
 (0)