Skip to content

Commit bbe08d7

Browse files
authored
Merge pull request #59132 from DougGregor/sendable-conformance-superclass-5.7
2 parents 44dff7c + 403c303 commit bbe08d7

File tree

16 files changed

+130
-56
lines changed

16 files changed

+130
-56
lines changed

include/swift/AST/Module.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,11 +631,18 @@ class ModuleDecl
631631
/// might include "missing" conformances, which are synthesized for some
632632
/// protocols as an error recovery mechanism.
633633
///
634+
/// \param allowUnavailable When \c true, the resulting conformance reference
635+
/// might include "unavailable" conformances, meaning that the conformance
636+
/// cannot actually be used and will be diagnosed if used later. Pass
637+
/// \c false here for queries that want to determine whether the conformance
638+
/// is likely to be usable.
639+
///
634640
/// \returns The result of the conformance search, which will be
635641
/// None if the type does not conform to the protocol or contain a
636642
/// ProtocolConformanceRef if it does conform.
637643
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol,
638-
bool allowMissing = false);
644+
bool allowMissing = false,
645+
bool allowUnavailable = true);
639646

640647
/// Look for the conformance of the given existential type to the given
641648
/// protocol.

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ class ProtocolConformanceRef {
106106
return Union.get<ProtocolDecl*>();
107107
}
108108

109+
/// Determine whether this conformance (or a conformance it depends on)
110+
/// involves an always-unavailable conformance.
111+
bool hasUnavailableConformance() const;
112+
109113
/// Determine whether this conformance (or a conformance it depends on)
110114
/// involves a "missing" conformance anywhere. Such conformances
111115
/// cannot be depended on to always exist.

lib/AST/ConformanceLookupTable.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const {
4141
return getImpliedSource()->Source.getDeclContext();
4242

4343
case ConformanceEntryKind::Synthesized:
44-
return getSynthesizedDecl();
44+
return getSynthesizedDeclContext();
4545
}
4646

4747
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
@@ -241,6 +241,14 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
241241
auto addInheritedConformance = [&](ConformanceEntry *entry) {
242242
auto protocol = entry->getProtocol();
243243

244+
// Don't add unavailable conformances.
245+
if (auto dc = entry->Source.getDeclContext()) {
246+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
247+
if (AvailableAttr::isUnavailable(ext))
248+
return;
249+
}
250+
}
251+
244252
// Don't add redundant conformances here. This is merely an
245253
// optimization; resolveConformances() would zap the duplicates
246254
// anyway.
@@ -812,7 +820,8 @@ DeclContext *ConformanceLookupTable::getConformingContext(
812820
if (superclassTy->is<ErrorType>())
813821
return nullptr;
814822
auto inheritedConformance = module->lookupConformance(
815-
superclassTy, protocol);
823+
superclassTy, protocol, /*allowMissing=*/false,
824+
/*allowUnavailable=*/false);
816825
if (inheritedConformance)
817826
return superclassDecl;
818827
} while ((superclassDecl = superclassDecl->getSuperclassDecl()));
@@ -927,10 +936,11 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
927936
return entry->Conformance.get<ProtocolConformance *>();
928937
}
929938

930-
void ConformanceLookupTable::addSynthesizedConformance(NominalTypeDecl *nominal,
931-
ProtocolDecl *protocol) {
939+
void ConformanceLookupTable::addSynthesizedConformance(
940+
NominalTypeDecl *nominal, ProtocolDecl *protocol,
941+
DeclContext *conformanceDC) {
932942
addProtocol(protocol, nominal->getLoc(),
933-
ConformanceSource::forSynthesized(nominal));
943+
ConformanceSource::forSynthesized(conformanceDC));
934944
}
935945

936946
void ConformanceLookupTable::registerProtocolConformance(
@@ -956,7 +966,7 @@ void ConformanceLookupTable::registerProtocolConformance(
956966
auto inherited = dyn_cast<InheritedProtocolConformance>(conformance);
957967
ConformanceSource source
958968
= inherited ? ConformanceSource::forInherited(cast<ClassDecl>(nominal)) :
959-
synthesized ? ConformanceSource::forSynthesized(nominal) :
969+
synthesized ? ConformanceSource::forSynthesized(dc) :
960970
ConformanceSource::forExplicit(dc);
961971

962972
ASTContext &ctx = nominal->getASTContext();

lib/AST/ConformanceLookupTable.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
120120

121121
/// Create a synthesized conformance.
122122
///
123-
/// The given nominal type declaration will get a synthesized
123+
/// The given declaration context (for a type) will get a synthesized
124124
/// conformance to the requested protocol.
125-
static ConformanceSource forSynthesized(NominalTypeDecl *typeDecl) {
126-
return ConformanceSource(typeDecl, ConformanceEntryKind::Synthesized);
125+
static ConformanceSource forSynthesized(DeclContext *dc) {
126+
return ConformanceSource(dc, ConformanceEntryKind::Synthesized);
127127
}
128128

129129
/// Return a new conformance source with the given location of "@unchecked".
@@ -188,9 +188,9 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
188188

189189
/// For a synthesized conformance, retrieve the nominal type decl
190190
/// that will receive the conformance.
191-
NominalTypeDecl *getSynthesizedDecl() const {
191+
DeclContext *getSynthesizedDeclContext() const {
192192
assert(getKind() == ConformanceEntryKind::Synthesized);
193-
return static_cast<NominalTypeDecl *>(Storage.getPointer());
193+
return static_cast<DeclContext *>(Storage.getPointer());
194194
}
195195

196196
/// Get the declaration context that this conformance will be
@@ -428,7 +428,8 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
428428

429429
/// Add a synthesized conformance to the lookup table.
430430
void addSynthesizedConformance(NominalTypeDecl *nominal,
431-
ProtocolDecl *protocol);
431+
ProtocolDecl *protocol,
432+
DeclContext *conformanceDC);
432433

433434
/// Register an externally-supplied protocol conformance.
434435
void registerProtocolConformance(ProtocolConformance *conformance,

lib/AST/Module.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,9 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
10151015
// If the existential is class-constrained, the class might conform
10161016
// concretely.
10171017
if (auto superclass = layout.explicitSuperclass) {
1018-
if (auto result = lookupConformance(superclass, protocol))
1018+
if (auto result = lookupConformance(
1019+
superclass, protocol, /*allowMissing=*/false,
1020+
/*allowUnavailable=*/false))
10191021
return result;
10201022
}
10211023

@@ -1071,7 +1073,8 @@ ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid(
10711073

10721074
ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
10731075
ProtocolDecl *protocol,
1074-
bool allowMissing) {
1076+
bool allowMissing,
1077+
bool allowUnavailable) {
10751078
// If we are recursively checking for implicit conformance of a nominal
10761079
// type to Sendable, fail without evaluating this request. This
10771080
// squashes cycles.
@@ -1088,13 +1091,18 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
10881091
auto result = evaluateOrDefault(
10891092
getASTContext().evaluator, request, ProtocolConformanceRef::forInvalid());
10901093

1091-
// If we aren't supposed to allow missing conformances through for this
1092-
// protocol, replace the result with an "invalid" result.
1094+
// If we aren't supposed to allow missing conformances but we have one,
1095+
// replace the result with an "invalid" result.
10931096
if (!allowMissing &&
10941097
shouldCreateMissingConformances(type, protocol) &&
10951098
result.hasMissingConformance(this))
10961099
return ProtocolConformanceRef::forInvalid();
10971100

1101+
// If we aren't supposed to allow unavailable conformances but we have one,
1102+
// replace the result with an "invalid" result.
1103+
if (!allowUnavailable && result.hasUnavailableConformance())
1104+
return ProtocolConformanceRef::forInvalid();
1105+
10981106
return result;
10991107
}
11001108

@@ -1248,7 +1256,9 @@ LookupConformanceInModuleRequest::evaluate(
12481256
// able to be resolved by a substitution that makes the archetype
12491257
// concrete.
12501258
if (auto super = archetype->getSuperclass()) {
1251-
if (auto inheritedConformance = mod->lookupConformance(super, protocol)) {
1259+
if (auto inheritedConformance = mod->lookupConformance(
1260+
super, protocol, /*allowMissing=*/false,
1261+
/*allowUnavailable=*/false)) {
12521262
return ProtocolConformanceRef(ctx.getInheritedConformance(
12531263
type, inheritedConformance.getConcrete()));
12541264
}

lib/AST/ProtocolConformance.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,8 @@ void NominalTypeDecl::prepareConformanceTable() const {
12701270
auto addSynthesized = [&](KnownProtocolKind kind) {
12711271
if (auto *proto = getASTContext().getProtocol(kind)) {
12721272
if (protocols.count(proto) == 0) {
1273-
ConformanceTable->addSynthesizedConformance(mutableThis, proto);
1273+
ConformanceTable->addSynthesizedConformance(
1274+
mutableThis, proto, mutableThis);
12741275
protocols.insert(proto);
12751276
}
12761277
}
@@ -1743,6 +1744,28 @@ SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanc
17431744
return SourceLoc();
17441745
}
17451746

1747+
bool ProtocolConformanceRef::hasUnavailableConformance() const {
1748+
// Abstract conformances are never unavailable.
1749+
if (!isConcrete())
1750+
return false;
1751+
1752+
// Check whether this conformance is on an unavailable extension.
1753+
auto concrete = getConcrete();
1754+
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
1755+
if (ext && AvailableAttr::isUnavailable(ext))
1756+
return true;
1757+
1758+
// Check the conformances in the substitution map.
1759+
auto module = concrete->getDeclContext()->getParentModule();
1760+
auto subMap = concrete->getSubstitutions(module);
1761+
for (auto subConformance : subMap.getConformances()) {
1762+
if (subConformance.hasUnavailableConformance())
1763+
return true;
1764+
}
1765+
1766+
return false;
1767+
}
1768+
17461769
bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const {
17471770
return forEachMissingConformance(module,
17481771
[](BuiltinProtocolConformance *builtin) {

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,10 @@ Optional<Type> ConcreteContraction::substTypeParameterRec(
275275
// 'allowMissing' value here is actually irrelevant.
276276
auto conformance = ((*substBaseType)->isTypeParameter()
277277
? ProtocolConformanceRef(proto)
278-
: module->lookupConformance(*substBaseType, proto,
279-
/*allowMissing=*/false));
278+
: module->lookupConformance(
279+
*substBaseType, proto,
280+
/*allowMissing=*/false,
281+
/*allowUnavailable=*/false));
280282

281283
// The base type doesn't conform, in which case the requirement remains
282284
// unsubstituted.
@@ -391,7 +393,7 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
391393

392394
if (!substFirstType->isTypeParameter() &&
393395
!module->lookupConformance(substFirstType, proto,
394-
allowMissing)) {
396+
allowMissing, /*allowUnavailable=*/false)) {
395397
// Handle the case of <T where T : P, T : C> where C is a class and
396398
// C does not conform to P by leaving the conformance requirement
397399
// unsubstituted.

lib/AST/RequirementMachine/ConcreteTypeWitness.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
156156
auto conformance = module->lookupConformance(concreteType,
157157
const_cast<ProtocolDecl *>(proto),
158158
allowMissing);
159-
if (conformance.isInvalid()) {
159+
if (conformance.isInvalid() || conformance.hasUnavailableConformance()) {
160160
// For superclass rules, it is totally fine to have a signature like:
161161
//
162162
// protocol P {}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -584,29 +584,6 @@ static void addSendableFixIt(
584584
}
585585
}
586586

587-
/// Determine whether there is an unavailable conformance here.
588-
static bool hasUnavailableConformance(ProtocolConformanceRef conformance) {
589-
// Abstract conformances are never unavailable.
590-
if (!conformance.isConcrete())
591-
return false;
592-
593-
// Check whether this conformance is on an unavailable extension.
594-
auto concrete = conformance.getConcrete();
595-
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
596-
if (ext && AvailableAttr::isUnavailable(ext))
597-
return true;
598-
599-
// Check the conformances in the substitution map.
600-
auto module = concrete->getDeclContext()->getParentModule();
601-
auto subMap = concrete->getSubstitutions(module);
602-
for (auto subConformance : subMap.getConformances()) {
603-
if (hasUnavailableConformance(subConformance))
604-
return true;
605-
}
606-
607-
return false;
608-
}
609-
610587
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
611588
return contextRequiresStrictConcurrencyChecking(dc, [](const AbstractClosureExpr *) {
612589
return Type();
@@ -892,7 +869,7 @@ bool swift::diagnoseNonSendableTypes(
892869

893870
// FIXME: More detail for unavailable conformances.
894871
auto conformance = TypeChecker::conformsToProtocol(type, proto, module);
895-
if (conformance.isInvalid() || hasUnavailableConformance(conformance)) {
872+
if (conformance.isInvalid() || conformance.hasUnavailableConformance()) {
896873
return diagnoseSingleNonSendableType(type, fromContext, loc, diagnose);
897874
}
898875

@@ -1041,7 +1018,7 @@ namespace {
10411018
return true;
10421019

10431020
// If there is an unavailable conformance here, fail.
1044-
if (hasUnavailableConformance(conformance))
1021+
if (conformance.hasUnavailableConformance())
10451022
return true;
10461023

10471024
// Look for missing Sendable conformances.
@@ -4382,7 +4359,8 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
43824359
auto classModule = classDecl->getParentModule();
43834360
if (auto inheritedConformance = TypeChecker::conformsToProtocol(
43844361
classDecl->mapTypeIntoContext(superclass),
4385-
proto, classModule, /*allowMissing=*/false)) {
4362+
proto, classModule, /*allowMissing=*/false,
4363+
/*allowUnavailable=*/false)) {
43864364
inheritedConformance = inheritedConformance
43874365
.mapConformanceOutOfContext();
43884366
if (inheritedConformance.isConcrete()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3388,7 +3388,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
33883388
auto overriddenConformance =
33893389
DC->getParentModule()->lookupConformance(Adoptee,
33903390
overridden->getProtocol(),
3391-
/*allowMissing=*/true);
3391+
/*allowMissing=*/true,
3392+
/*allowUnavailable=*/false);
33923393
if (overriddenConformance.isInvalid() ||
33933394
!overriddenConformance.isConcrete())
33943395
continue;
@@ -5613,9 +5614,10 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
56135614

56145615
ProtocolConformanceRef
56155616
TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5616-
bool allowMissing) {
5617+
bool allowMissing, bool allowUnavailable) {
56175618
// Look up conformance in the module.
5618-
auto lookupResult = M->lookupConformance(T, Proto, allowMissing);
5619+
auto lookupResult = M->lookupConformance(
5620+
T, Proto, allowMissing, allowUnavailable);
56195621
if (lookupResult.isInvalid()) {
56205622
return ProtocolConformanceRef::forInvalid();
56215623
}

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,8 @@ ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto,
803803
/// protocol \c Proto, or \c None.
804804
ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto,
805805
ModuleDecl *M,
806-
bool allowMissing = true);
806+
bool allowMissing = true,
807+
bool allowUnavailable = true);
807808

808809
/// Check whether the type conforms to a given known protocol.
809810
bool conformsToKnownProtocol(Type type, KnownProtocolKind protocol,

test/ClangImporter/objc_async.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,10 @@ func testSender(
374374
sender.sendPtr(ptr)
375375
sender.sendStringArray(stringArray)
376376
}
377+
378+
// Sendable checking
379+
public struct SomeWrapper<T: AuditedNonSendable> {
380+
public let unit: T
381+
}
382+
383+
extension SomeWrapper: Sendable where T: Sendable {}

test/Concurrency/sendable_checking.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,21 @@ func f() async {
9595
n.pointee += 1
9696
}
9797
}
98+
99+
// Make sure the generic signature doesn't minimize away Sendable requirements.
100+
@_nonSendable class NSClass { }
101+
102+
struct WrapClass<T: NSClass> {
103+
var t: T
104+
}
105+
106+
extension WrapClass: Sendable where T: Sendable { }
107+
108+
// Make sure we don't inherit the unavailable Sendable conformance from
109+
// our superclass.
110+
class SendableSubclass: NSClass, @unchecked Sendable { }
111+
112+
@available(SwiftStdlib 5.1, *)
113+
func testSubclassing(obj: SendableSubclass) async {
114+
acceptCV(obj) // okay!
115+
}

test/Generics/superclass_constraint.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,14 @@ public struct Barn<T: Teddy> {
221221
// CHECK: Generic signature: <T, S where T : Teddy>
222222
public func foo<S>(_: S, _: Barn<T>, _: Paddock<T>) {}
223223
}
224+
225+
226+
public class Animal { }
227+
228+
@available(*, unavailable, message: "Not a pony")
229+
extension Animal: Pony { }
230+
231+
public struct AnimalWrapper<Friend: Animal> { }
232+
233+
// CHECK: Generic signature: <Friend where Friend : Animal, Friend : Pony>
234+
extension AnimalWrapper: Pony where Friend: Pony { }

0 commit comments

Comments
 (0)