Skip to content

Commit 89fd6e3

Browse files
committed
Respect @available(*, unavailable) in Sendable checking.
When a type has provided an unavailable `Sendable` conformance, don't check its instance storage for `Sendable`. Additionally, teach `-require-explicit-sendable` to avoid trying to add a `Sendable` conformance when some of the instance storage relies on unavailable `Sendable` conformances.
1 parent 610812a commit 89fd6e3

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,29 @@ void swift::diagnoseMissingSendableConformance(
730730
}
731731

732732
namespace {
733+
/// Determine whether there is an unavailable conformance here.
734+
static bool hasUnavailableConformance(ProtocolConformanceRef conformance) {
735+
// Abstract conformances are never unavailable.
736+
if (!conformance.isConcrete())
737+
return false;
738+
739+
// Check whether this conformance is on an unavailable extension.
740+
auto concrete = conformance.getConcrete();
741+
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
742+
if (ext && AvailableAttr::isUnavailable(ext))
743+
return true;
744+
745+
// Check the conformances in the substitution map.
746+
auto module = concrete->getDeclContext()->getParentModule();
747+
auto subMap = concrete->getSubstitutions(module);
748+
for (auto subConformance : subMap.getConformances()) {
749+
if (hasUnavailableConformance(subConformance))
750+
return true;
751+
}
752+
753+
return false;
754+
}
755+
733756
template<typename Visitor>
734757
bool visitInstanceStorage(
735758
NominalTypeDecl *nominal, DeclContext *dc, Visitor &visitor);
@@ -777,7 +800,9 @@ namespace {
777800
if (conformance.isInvalid())
778801
return true;
779802

780-
// FIXME: Look for unavailable conformances, too!
803+
// If there is an unavailable conformance here, fail.
804+
if (hasUnavailableConformance(conformance))
805+
return true;
781806

782807
// Look for missing Sendable conformances.
783808
return conformance.forEachMissingConformance(module,
@@ -3726,6 +3751,12 @@ bool swift::checkSendableConformance(
37263751
if (!nominal)
37273752
return false;
37283753

3754+
// If this is an always-unavailable conformance, there's nothing to check.
3755+
if (auto ext = dyn_cast<ExtensionDecl>(conformanceDC)) {
3756+
if (AvailableAttr::isUnavailable(ext))
3757+
return false;
3758+
}
3759+
37293760
auto classDecl = dyn_cast<ClassDecl>(nominal);
37303761
if (classDecl) {
37313762
// Actors implicitly conform to Sendable and protect their state.

test/Concurrency/concurrent_value_inference.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ class C3 { }
9696

9797
class C4: C3 { }
9898

99+
// Make Sendable unavailable, but be sure not to diagnose it.
100+
struct S2 {
101+
var c1: C1
102+
}
103+
104+
@available(*, unavailable)
105+
extension S2: Sendable { }
106+
99107
func testCV(
100108
c1: C1, c2: C2, c3: C3, c4: C4, s1: S1, e1: E1, e2: E2,
101109
gs1: GS1<Int>, gs2: GS2<Int>,

test/Concurrency/require-explicit-sendable.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,9 @@ public struct S9<T: P2 & Hashable> {
8787
// expected-note@-3{{make generic struct 'S9' explicitly non-Sendable to suppress this warning}}
8888
var dict: [T : T.A] = [:]
8989
}
90+
91+
public struct S10 { // expected-warning{{public struct 'S10' does not specify whether it is 'Sendable' or not}}
92+
// expected-note@-1{{add '@unchecked Sendable' conformance to struct 'S10' if this type manually implements concurrency safety}}
93+
// expected-note@-2{{make struct 'S10' explicitly non-Sendable to suppress this warning}}
94+
var s7: S7
95+
}

0 commit comments

Comments
 (0)