Skip to content

Commit bd46bda

Browse files
committed
AST: Narrow the filtering of unavailable conformances to Sendable only
Remove the allowUnavailable parameter to lookupConformance(), and instead explicitly check the result for hasUnavailableConformance() in the places where we used to pass 'false'. Also, narrow down this check in those places to the Sendable protocol only, fixing a regression with Hashable conformance synthesis. Fixes rdar://problem/94460143.
1 parent b04ae71 commit bd46bda

File tree

10 files changed

+77
-50
lines changed

10 files changed

+77
-50
lines changed

include/swift/AST/Module.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -640,18 +640,11 @@ class ModuleDecl
640640
/// might include "missing" conformances, which are synthesized for some
641641
/// protocols as an error recovery mechanism.
642642
///
643-
/// \param allowUnavailable When \c true, the resulting conformance reference
644-
/// might include "unavailable" conformances, meaning that the conformance
645-
/// cannot actually be used and will be diagnosed if used later. Pass
646-
/// \c false here for queries that want to determine whether the conformance
647-
/// is likely to be usable.
648-
///
649643
/// \returns The result of the conformance search, which will be
650644
/// None if the type does not conform to the protocol or contain a
651645
/// ProtocolConformanceRef if it does conform.
652646
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol,
653-
bool allowMissing = false,
654-
bool allowUnavailable = true);
647+
bool allowMissing = false);
655648

656649
/// Look for the conformance of the given existential type to the given
657650
/// protocol.

lib/AST/ConformanceLookupTable.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,9 @@ DeclContext *ConformanceLookupTable::getConformingContext(
820820
if (superclassTy->is<ErrorType>())
821821
return nullptr;
822822
auto inheritedConformance = module->lookupConformance(
823-
superclassTy, protocol, /*allowMissing=*/false,
824-
/*allowUnavailable=*/false);
823+
superclassTy, protocol, /*allowMissing=*/false);
824+
if (inheritedConformance.hasUnavailableConformance())
825+
inheritedConformance = ProtocolConformanceRef::forInvalid();
825826
if (inheritedConformance)
826827
return superclassDecl;
827828
} while ((superclassDecl = superclassDecl->getSuperclassDecl()));

lib/AST/Module.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,9 +1020,13 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
10201020
// concretely.
10211021
if (auto superclass = layout.explicitSuperclass) {
10221022
if (auto result = lookupConformance(
1023-
superclass, protocol, /*allowMissing=*/false,
1024-
/*allowUnavailable=*/false))
1023+
superclass, protocol, /*allowMissing=*/false)) {
1024+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1025+
result.hasUnavailableConformance())
1026+
result = ProtocolConformanceRef::forInvalid();
1027+
10251028
return result;
1029+
}
10261030
}
10271031

10281032
// Otherwise, the existential might conform abstractly.
@@ -1077,8 +1081,7 @@ ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid(
10771081

10781082
ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
10791083
ProtocolDecl *protocol,
1080-
bool allowMissing,
1081-
bool allowUnavailable) {
1084+
bool allowMissing) {
10821085
// If we are recursively checking for implicit conformance of a nominal
10831086
// type to Sendable, fail without evaluating this request. This
10841087
// squashes cycles.
@@ -1102,11 +1105,6 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
11021105
result.hasMissingConformance(this))
11031106
return ProtocolConformanceRef::forInvalid();
11041107

1105-
// If we aren't supposed to allow unavailable conformances but we have one,
1106-
// replace the result with an "invalid" result.
1107-
if (!allowUnavailable && result.hasUnavailableConformance())
1108-
return ProtocolConformanceRef::forInvalid();
1109-
11101108
return result;
11111109
}
11121110

@@ -1260,9 +1258,12 @@ LookupConformanceInModuleRequest::evaluate(
12601258
// able to be resolved by a substitution that makes the archetype
12611259
// concrete.
12621260
if (auto super = archetype->getSuperclass()) {
1263-
if (auto inheritedConformance = mod->lookupConformance(
1264-
super, protocol, /*allowMissing=*/false,
1265-
/*allowUnavailable=*/false)) {
1261+
auto inheritedConformance = mod->lookupConformance(
1262+
super, protocol, /*allowMissing=*/false);
1263+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1264+
inheritedConformance.hasUnavailableConformance())
1265+
inheritedConformance = ProtocolConformanceRef::forInvalid();
1266+
if (inheritedConformance) {
12661267
return ProtocolConformanceRef(ctx.getInheritedConformance(
12671268
type, inheritedConformance.getConcrete()));
12681269
}

lib/AST/ProtocolConformance.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,9 @@ SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanc
17451745
}
17461746

17471747
bool ProtocolConformanceRef::hasUnavailableConformance() const {
1748+
if (isInvalid())
1749+
return false;
1750+
17481751
// Abstract conformances are never unavailable.
17491752
if (!isConcrete())
17501753
return false;

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -271,16 +271,18 @@ Optional<Type> ConcreteContraction::substTypeParameterRec(
271271
auto *proto = assocType->getProtocol();
272272
auto *module = proto->getParentModule();
273273

274-
bool allowUnavailable =
275-
!proto->isSpecificProtocol(KnownProtocolKind::Sendable);
276274
// The 'Sendable' protocol does not declare any associated types, so the
277275
// 'allowMissing' value here is actually irrelevant.
278276
auto conformance = ((*substBaseType)->isTypeParameter()
279277
? ProtocolConformanceRef(proto)
280278
: module->lookupConformance(
281279
*substBaseType, proto,
282-
/*allowMissing=*/false,
283-
allowUnavailable));
280+
/*allowMissing=*/false));
281+
282+
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
283+
conformance.hasUnavailableConformance()) {
284+
conformance = ProtocolConformanceRef::forInvalid();
285+
}
284286

285287
// The base type doesn't conform, in which case the requirement remains
286288
// unsubstituted.
@@ -393,16 +395,22 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
393395
if (ConcreteTypes.count(stripBoundDependentMemberTypes(firstType)) > 0)
394396
allowMissing = true;
395397

396-
bool allowUnavailable =
397-
!proto->isSpecificProtocol(KnownProtocolKind::Sendable);
398-
if (!substFirstType->isTypeParameter() &&
399-
!module->lookupConformance(substFirstType, proto,
400-
allowMissing, allowUnavailable)) {
401-
// Handle the case of <T where T : P, T : C> where C is a class and
402-
// C does not conform to P and only substitute the parent type of T
403-
// by pretending we have a same-type requirement here.
404-
substFirstType = substTypeParameter(
405-
firstType, Position::SameTypeRequirement);
398+
if (!substFirstType->isTypeParameter()) {
399+
auto conformance = module->lookupConformance(substFirstType, proto,
400+
allowMissing);
401+
402+
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
403+
conformance.hasUnavailableConformance()) {
404+
conformance = ProtocolConformanceRef::forInvalid();
405+
}
406+
407+
if (!conformance) {
408+
// Handle the case of <T where T : P, T : C> where C is a class and
409+
// C does not conform to P and only substitute the parent type of T
410+
// by pretending we have a same-type requirement here.
411+
substFirstType = substTypeParameter(
412+
firstType, Position::SameTypeRequirement);
413+
}
406414
}
407415

408416
// Otherwise, replace the generic parameter in the conformance

lib/AST/RequirementMachine/ConcreteTypeWitness.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,14 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
153153
// subclasses of 'C' which are 'Sendable'.
154154
bool allowMissing = (requirementKind == RequirementKind::SameType);
155155

156-
bool allowUnavailable =
157-
!proto->isSpecificProtocol(KnownProtocolKind::Sendable);
158156
auto conformance = module->lookupConformance(concreteType,
159157
const_cast<ProtocolDecl *>(proto),
160-
allowMissing,
161-
allowUnavailable);
158+
allowMissing);
159+
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
160+
conformance.hasUnavailableConformance()) {
161+
conformance = ProtocolConformanceRef::forInvalid();
162+
}
163+
162164
if (conformance.isInvalid()) {
163165
// For superclass rules, it is totally fine to have a signature like:
164166
//

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4408,10 +4408,13 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
44084408
if (classDecl) {
44094409
if (Type superclass = classDecl->getSuperclass()) {
44104410
auto classModule = classDecl->getParentModule();
4411-
if (auto inheritedConformance = TypeChecker::conformsToProtocol(
4412-
classDecl->mapTypeIntoContext(superclass),
4413-
proto, classModule, /*allowMissing=*/false,
4414-
/*allowUnavailable=*/false)) {
4411+
auto inheritedConformance = TypeChecker::conformsToProtocol(
4412+
classDecl->mapTypeIntoContext(superclass),
4413+
proto, classModule, /*allowMissing=*/false);
4414+
if (inheritedConformance.hasUnavailableConformance())
4415+
inheritedConformance = ProtocolConformanceRef::forInvalid();
4416+
4417+
if (inheritedConformance) {
44154418
inheritedConformance = inheritedConformance
44164419
.mapConformanceOutOfContext();
44174420
if (inheritedConformance.isConcrete()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3441,8 +3441,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
34413441
auto overriddenConformance =
34423442
DC->getParentModule()->lookupConformance(Adoptee,
34433443
overridden->getProtocol(),
3444-
/*allowMissing=*/true,
3445-
/*allowUnavailable=*/false);
3444+
/*allowMissing=*/true);
34463445
if (overriddenConformance.isInvalid() ||
34473446
!overriddenConformance.isConcrete())
34483447
continue;
@@ -5667,10 +5666,11 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
56675666

56685667
ProtocolConformanceRef
56695668
TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5670-
bool allowMissing, bool allowUnavailable) {
5669+
bool allowMissing) {
56715670
// Look up conformance in the module.
56725671
auto lookupResult = M->lookupConformance(
5673-
T, Proto, allowMissing, allowUnavailable);
5672+
T, Proto, allowMissing);
5673+
56745674
if (lookupResult.isInvalid()) {
56755675
return ProtocolConformanceRef::forInvalid();
56765676
}

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,7 @@ ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto,
804804
/// protocol \c Proto, or \c None.
805805
ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto,
806806
ModuleDecl *M,
807-
bool allowMissing = true,
808-
bool allowUnavailable = true);
807+
bool allowMissing = true);
809808

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

test/Generics/unavailable_conformances.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,20 @@ protocol Base {
3333

3434
@available(*, unavailable)
3535
extension Base where T == ConcreteP {}
36+
37+
// Hashable conformance synthesis ran into problems if the conformance was
38+
// unavailable (which is legal if the type is unavailable also).
39+
@available(*, unavailable)
40+
struct Foo {
41+
class Bar {}
42+
}
43+
44+
@available(*, unavailable)
45+
extension Foo.Bar: Equatable {
46+
static func == (lhs: Foo.Bar, rhs: Foo.Bar) -> Bool { return false }
47+
}
48+
49+
@available(*, unavailable)
50+
extension Foo.Bar: Hashable {
51+
func hash(into hasher: inout Hasher) {}
52+
}

0 commit comments

Comments
 (0)