Skip to content

Commit 6381960

Browse files
committed
Align Sendable diagnostic behavior with incremental-adoption proposal.
Determine whether a particular missing Sendable diagnostic should be emitted as a warning or error, or even ignored entirely, based on the emerging rules from the proposal for incremental adoption of Sendable checking.
1 parent d08ecf3 commit 6381960

File tree

7 files changed

+157
-96
lines changed

7 files changed

+157
-96
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3526,9 +3526,9 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
35263526
}
35273527

35283528
static void diagnoseMissingConformance(
3529-
SourceLoc loc, Type type, ProtocolDecl *proto, ModuleDecl *module) {
3529+
SourceLoc loc, Type type, ProtocolDecl *proto, const DeclContext *fromDC) {
35303530
assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable));
3531-
diagnoseMissingSendableConformance(loc, type, module);
3531+
diagnoseMissingSendableConformance(loc, type, fromDC);
35323532
}
35333533

35343534
bool
@@ -3546,16 +3546,14 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
35463546

35473547
// Diagnose "missing" conformances where we needed a conformance but
35483548
// didn't have one.
3549+
auto *DC = where.getDeclContext();
35493550
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
35503551
if (builtinConformance->isMissing()) {
35513552
diagnoseMissingConformance(loc, builtinConformance->getType(),
3552-
builtinConformance->getProtocol(),
3553-
where.getDeclContext()->getParentModule());
3553+
builtinConformance->getProtocol(), DC);
35543554
}
35553555
}
35563556

3557-
auto *DC = where.getDeclContext();
3558-
35593557
auto maybeEmitAssociatedTypeNote = [&]() {
35603558
if (!depTy && !replacementTy)
35613559
return;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 104 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -601,46 +601,96 @@ static bool hasUnavailableConformance(ProtocolConformanceRef conformance) {
601601
return false;
602602
}
603603

604+
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
605+
if (dc->getParentModule()->isConcurrencyChecked())
606+
return true;
607+
608+
return contextUsesConcurrencyFeatures(dc);
609+
}
610+
611+
/// Determine the default diagnostic behavior for this language mode.
612+
static DiagnosticBehavior defaultSendableDiagnosticBehavior(
613+
const LangOptions &langOpts) {
614+
// Prior to Swift 6, all Sendable-related diagnostics are warnings.
615+
if (!langOpts.isSwiftVersionAtLeast(6))
616+
return DiagnosticBehavior::Warning;
617+
618+
return DiagnosticBehavior::Unspecified;
619+
}
620+
621+
DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
622+
// If we're not supposed to diagnose existing data races from this context,
623+
// ignore the diagnostic entirely.
624+
if (!shouldDiagnoseExistingDataRaces(fromDC))
625+
return DiagnosticBehavior::Ignore;
626+
627+
return defaultSendableDiagnosticBehavior(fromDC->getASTContext().LangOpts);
628+
}
629+
630+
/// Determine the diagnostic behavior for a Sendable reference to the given
631+
/// nominal type.
632+
DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
633+
NominalTypeDecl *nominal) const {
634+
// Determine whether the type was explicitly non-Sendable.
635+
auto nominalModule = nominal->getParentModule();
636+
bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked();
637+
638+
// If we are performing an explicit conformance check, always consider this
639+
// to be an explicitly non-Sendable type.
640+
if (conformanceCheck) {
641+
switch (*conformanceCheck) {
642+
case SendableCheck::Explicit:
643+
isExplicitlyNonSendable = true;
644+
break;
645+
646+
case SendableCheck::ImpliedByStandardProtocol:
647+
case SendableCheck::Implicit:
648+
break;
649+
}
650+
}
651+
652+
// Determine whether this nominal type is visible via a @_predatesConcurrency
653+
// import.
654+
ImportDecl *predatesConcurrencyImport = nullptr;
655+
656+
// When the type is explicitly non-Sendable...
657+
if (isExplicitlyNonSendable) {
658+
// @_predatesConcurrency imports downgrade the diagnostic to a warning.
659+
if (predatesConcurrencyImport) {
660+
// FIXME: Note that this @_predatesConcurrency import was "used".
661+
return DiagnosticBehavior::Warning;
662+
}
663+
664+
return defaultSendableDiagnosticBehavior(fromDC->getASTContext().LangOpts);
665+
}
666+
667+
// When the type is implicitly non-Sendable...
668+
669+
// @_predatesConcurrency always suppresses the diagnostic.
670+
if (predatesConcurrencyImport) {
671+
// FIXME: Note that this @_predatesConcurrency import was "used".
672+
return DiagnosticBehavior::Ignore;
673+
}
674+
675+
return defaultDiagnosticBehavior();
676+
}
677+
604678
/// Produce a diagnostic for a single instance of a non-Sendable type where
605679
/// a Sendable type is required.
606680
static bool diagnoseSingleNonSendableType(
607-
Type type, ModuleDecl *module, SourceLoc loc,
681+
Type type, SendableCheckContext fromContext, SourceLoc loc,
608682
llvm::function_ref<
609683
std::pair<DiagnosticBehavior, bool>(Type, DiagnosticBehavior)> diagnose) {
610684

611685
auto behavior = DiagnosticBehavior::Unspecified;
612686

687+
auto module = fromContext.fromDC->getParentModule();
613688
ASTContext &ctx = module->getASTContext();
614689
auto nominal = type->getAnyNominal();
615-
const LangOptions &langOpts = ctx.LangOpts;
616690
if (nominal) {
617-
// A nominal type that has not provided conformance to Sendable will be
618-
// diagnosed based on whether its defining module was consistently
619-
// checked for concurrency.
620-
auto nominalModule = nominal->getParentModule();
621-
622-
if (langOpts.isSwiftVersionAtLeast(6)) {
623-
// In Swift 6, error when the nominal type comes from a module that
624-
// had the concurrency checks consistently applied or from this module.
625-
// Otherwise, warn.
626-
if (nominalModule->isConcurrencyChecked() || nominalModule == module)
627-
behavior = DiagnosticBehavior::Unspecified;
628-
else
629-
behavior = DiagnosticBehavior::Warning;
630-
} else {
631-
// In Swift 5, warn if either the imported or importing model is
632-
// checking for concurrency, or if the nominal type comes from this
633-
// module. Otherwise, leave a safety hole.
634-
if (nominalModule->isConcurrencyChecked() ||
635-
nominalModule == module ||
636-
langOpts.WarnConcurrency)
637-
behavior = DiagnosticBehavior::Warning;
638-
else
639-
behavior = DiagnosticBehavior::Ignore;
640-
}
641-
} else if (!langOpts.isSwiftVersionAtLeast(6)) {
642-
// Always warn in Swift 5.
643-
behavior = DiagnosticBehavior::Warning;
691+
behavior = fromContext.diagnosticBehavior(nominal);
692+
} else {
693+
behavior = fromContext.defaultDiagnosticBehavior();
644694
}
645695

646696
DiagnosticBehavior actualBehavior;
@@ -667,9 +717,11 @@ static bool diagnoseSingleNonSendableType(
667717
}
668718

669719
bool swift::diagnoseNonSendableTypes(
670-
Type type, ModuleDecl *module, SourceLoc loc,
720+
Type type, SendableCheckContext fromContext, SourceLoc loc,
671721
llvm::function_ref<
672722
std::pair<DiagnosticBehavior, bool>(Type, DiagnosticBehavior)> diagnose) {
723+
auto module = fromContext.fromDC->getParentModule();
724+
673725
// If the Sendable protocol is missing, do nothing.
674726
auto proto = module->getASTContext().getProtocol(KnownProtocolKind::Sendable);
675727
if (!proto)
@@ -678,15 +730,15 @@ bool swift::diagnoseNonSendableTypes(
678730
// FIXME: More detail for unavailable conformances.
679731
auto conformance = TypeChecker::conformsToProtocol(type, proto, module);
680732
if (conformance.isInvalid() || hasUnavailableConformance(conformance)) {
681-
return diagnoseSingleNonSendableType(type, module, loc, diagnose);
733+
return diagnoseSingleNonSendableType(type, fromContext, loc, diagnose);
682734
}
683735

684736
// Walk the conformance, diagnosing any missing Sendable conformances.
685737
bool anyMissing = false;
686738
conformance.forEachMissingConformance(module,
687739
[&](BuiltinProtocolConformance *missing) {
688740
if (diagnoseSingleNonSendableType(
689-
missing->getType(), module, loc, diagnose)) {
741+
missing->getType(), fromContext, loc, diagnose)) {
690742
anyMissing = true;
691743
}
692744

@@ -697,23 +749,23 @@ bool swift::diagnoseNonSendableTypes(
697749
}
698750

699751
bool swift::diagnoseNonSendableTypesInReference(
700-
ConcreteDeclRef declRef, ModuleDecl *module, SourceLoc loc,
752+
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
701753
ConcurrentReferenceKind refKind) {
702754
// For functions, check the parameter and result types.
703755
SubstitutionMap subs = declRef.getSubstitutions();
704756
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
705757
for (auto param : *function->getParameters()) {
706758
Type paramType = param->getInterfaceType().subst(subs);
707759
if (diagnoseNonSendableTypes(
708-
paramType, module, loc, diag::non_sendable_param_type))
760+
paramType, fromDC, loc, diag::non_sendable_param_type))
709761
return true;
710762
}
711763

712764
// Check the result type of a function.
713765
if (auto func = dyn_cast<FuncDecl>(function)) {
714766
Type resultType = func->getResultInterfaceType().subst(subs);
715767
if (diagnoseNonSendableTypes(
716-
resultType, module, loc, diag::non_sendable_result_type))
768+
resultType, fromDC, loc, diag::non_sendable_result_type))
717769
return true;
718770
}
719771

@@ -725,7 +777,7 @@ bool swift::diagnoseNonSendableTypesInReference(
725777
? var->getType()
726778
: var->getValueInterfaceType().subst(subs);
727779
if (diagnoseNonSendableTypes(
728-
propertyType, module, loc,
780+
propertyType, fromDC, loc,
729781
diag::non_sendable_property_type,
730782
var->getDescriptiveKind(), var->getName(),
731783
var->isLocalCapture()))
@@ -736,14 +788,14 @@ bool swift::diagnoseNonSendableTypesInReference(
736788
for (auto param : *subscript->getIndices()) {
737789
Type paramType = param->getInterfaceType().subst(subs);
738790
if (diagnoseNonSendableTypes(
739-
paramType, module, loc, diag::non_sendable_param_type))
791+
paramType, fromDC, loc, diag::non_sendable_param_type))
740792
return true;
741793
}
742794

743795
// Check the element type of a subscript.
744796
Type resultType = subscript->getElementInterfaceType().subst(subs);
745797
if (diagnoseNonSendableTypes(
746-
resultType, module, loc, diag::non_sendable_result_type))
798+
resultType, fromDC, loc, diag::non_sendable_result_type))
747799
return true;
748800

749801
return false;
@@ -753,9 +805,9 @@ bool swift::diagnoseNonSendableTypesInReference(
753805
}
754806

755807
void swift::diagnoseMissingSendableConformance(
756-
SourceLoc loc, Type type, ModuleDecl *module) {
808+
SourceLoc loc, Type type, const DeclContext *fromDC) {
757809
diagnoseNonSendableTypes(
758-
type, module, loc, diag::non_sendable_type);
810+
type, fromDC, loc, diag::non_sendable_type);
759811
}
760812

761813
namespace {
@@ -1893,7 +1945,7 @@ namespace {
18931945
// Check for non-sendable types.
18941946
bool problemFound =
18951947
diagnoseNonSendableTypesInReference(
1896-
concDeclRef, getDeclContext()->getParentModule(), declLoc,
1948+
concDeclRef, getDeclContext(), declLoc,
18971949
ConcurrentReferenceKind::SynchronousAsAsyncCall);
18981950
if (problemFound)
18991951
result = AsyncMarkingResult::NotSendable;
@@ -2023,14 +2075,14 @@ namespace {
20232075
for (const auto &param : fnType->getParams()) {
20242076
// FIXME: Dig out the locations of the corresponding arguments.
20252077
if (diagnoseNonSendableTypes(
2026-
param.getParameterType(), getParentModule(), apply->getLoc(),
2078+
param.getParameterType(), getDeclContext(), apply->getLoc(),
20272079
diag::non_sendable_param_type))
20282080
return true;
20292081
}
20302082

20312083
// Check for sendability of the result type.
20322084
if (diagnoseNonSendableTypes(
2033-
fnType->getResult(), getParentModule(), apply->getLoc(),
2085+
fnType->getResult(), getDeclContext(), apply->getLoc(),
20342086
diag::non_sendable_result_type))
20352087
return true;
20362088

@@ -2056,7 +2108,7 @@ namespace {
20562108
// A cross-actor access requires types to be concurrent-safe.
20572109
if (isCrossActor) {
20582110
return diagnoseNonSendableTypesInReference(
2059-
valueRef, getParentModule(), loc,
2111+
valueRef, getDeclContext(), loc,
20602112
ConcurrentReferenceKind::CrossActor);
20612113
}
20622114

@@ -2174,7 +2226,7 @@ namespace {
21742226
(ctx.LangOpts.EnableExperimentalFlowSensitiveConcurrentCaptures &&
21752227
parent.dyn_cast<LoadExpr *>())) {
21762228
return diagnoseNonSendableTypesInReference(
2177-
valueRef, getParentModule(), loc,
2229+
valueRef, getDeclContext(), loc,
21782230
ConcurrentReferenceKind::LocalCapture);
21792231
}
21802232

@@ -2226,7 +2278,7 @@ namespace {
22262278
auto type = component.getComponentType();
22272279
if (shouldDiagnoseExistingDataRaces(getDeclContext()) &&
22282280
diagnoseNonSendableTypes(
2229-
type, getParentModule(), component.getLoc(),
2281+
type, getDeclContext(), component.getLoc(),
22302282
diag::non_sendable_keypath_access))
22312283
return true;
22322284

@@ -2293,7 +2345,7 @@ namespace {
22932345
if (type &&
22942346
shouldDiagnoseExistingDataRaces(getDeclContext()) &&
22952347
diagnoseNonSendableTypes(
2296-
type, getParentModule(), component.getLoc(),
2348+
type, getDeclContext(), component.getLoc(),
22972349
diag::non_sendable_keypath_capture))
22982350
diagnosed = true;
22992351
}
@@ -2399,7 +2451,7 @@ namespace {
23992451
}
24002452

24012453
return diagnoseNonSendableTypesInReference(
2402-
memberRef, getDeclContext()->getParentModule(), memberLoc,
2454+
memberRef, getDeclContext(), memberLoc,
24032455
ConcurrentReferenceKind::CrossActor);
24042456
}
24052457

@@ -3172,9 +3224,8 @@ ActorIsolation ActorIsolationRequest::evaluate(
31723224
subs = genericEnv->getForwardingSubstitutionMap();
31733225
}
31743226
diagnoseNonSendableTypesInReference(
3175-
ConcreteDeclRef(value, subs),
3176-
value->getDeclContext()->getParentModule(), value->getLoc(),
3177-
ConcurrentReferenceKind::Nonisolated);
3227+
ConcreteDeclRef(value, subs), value->getDeclContext(),
3228+
value->getLoc(), ConcurrentReferenceKind::Nonisolated);
31783229
}
31793230

31803231
// Classes with global actors have additional rules regarding inheritance.
@@ -3589,13 +3640,6 @@ bool swift::contextUsesConcurrencyFeatures(const DeclContext *dc) {
35893640
return false;
35903641
}
35913642

3592-
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
3593-
if (dc->getParentModule()->isConcurrencyChecked())
3594-
return true;
3595-
3596-
return contextUsesConcurrencyFeatures(dc);
3597-
}
3598-
35993643
/// Limit the diagnostic behavior used when performing checks for the Sendable
36003644
/// instance storage of Sendable types.
36013645
///
@@ -3718,7 +3762,7 @@ static bool checkSendableInstanceStorage(
37183762

37193763
// Check that the property type is Sendable.
37203764
bool diagnosedProperty = diagnoseNonSendableTypes(
3721-
propertyType, dc->getParentModule(), property->getLoc(),
3765+
propertyType, SendableCheckContext(dc, check), property->getLoc(),
37223766
[&](Type type, DiagnosticBehavior suggestedBehavior) {
37233767
auto action = limitSendableInstanceBehavior(
37243768
langOpts, check, suggestedBehavior);
@@ -3748,7 +3792,7 @@ static bool checkSendableInstanceStorage(
37483792
/// Handle an enum associated value.
37493793
bool operator()(EnumElementDecl *element, Type elementType) {
37503794
bool diagnosedElement = diagnoseNonSendableTypes(
3751-
elementType, dc->getParentModule(), element->getLoc(),
3795+
elementType, SendableCheckContext(dc, check), element->getLoc(),
37523796
[&](Type type, DiagnosticBehavior suggestedBehavior) {
37533797
auto action = limitSendableInstanceBehavior(
37543798
langOpts, check, suggestedBehavior);

0 commit comments

Comments
 (0)