Skip to content

Commit fc35ea6

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 0eb649d commit fc35ea6

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
@@ -631,18 +631,11 @@ 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-
///
640634
/// \returns The result of the conformance search, which will be
641635
/// None if the type does not conform to the protocol or contain a
642636
/// ProtocolConformanceRef if it does conform.
643637
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol,
644-
bool allowMissing = false,
645-
bool allowUnavailable = true);
638+
bool allowMissing = false);
646639

647640
/// Look for the conformance of the given existential type to the given
648641
/// 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
@@ -1016,9 +1016,13 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
10161016
// concretely.
10171017
if (auto superclass = layout.explicitSuperclass) {
10181018
if (auto result = lookupConformance(
1019-
superclass, protocol, /*allowMissing=*/false,
1020-
/*allowUnavailable=*/false))
1019+
superclass, protocol, /*allowMissing=*/false)) {
1020+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1021+
result.hasUnavailableConformance())
1022+
result = ProtocolConformanceRef::forInvalid();
1023+
10211024
return result;
1025+
}
10221026
}
10231027

10241028
// Otherwise, the existential might conform abstractly.
@@ -1073,8 +1077,7 @@ ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid(
10731077

10741078
ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
10751079
ProtocolDecl *protocol,
1076-
bool allowMissing,
1077-
bool allowUnavailable) {
1080+
bool allowMissing) {
10781081
// If we are recursively checking for implicit conformance of a nominal
10791082
// type to Sendable, fail without evaluating this request. This
10801083
// squashes cycles.
@@ -1098,11 +1101,6 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
10981101
result.hasMissingConformance(this))
10991102
return ProtocolConformanceRef::forInvalid();
11001103

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-
11061104
return result;
11071105
}
11081106

@@ -1256,9 +1254,12 @@ LookupConformanceInModuleRequest::evaluate(
12561254
// able to be resolved by a substitution that makes the archetype
12571255
// concrete.
12581256
if (auto super = archetype->getSuperclass()) {
1259-
if (auto inheritedConformance = mod->lookupConformance(
1260-
super, protocol, /*allowMissing=*/false,
1261-
/*allowUnavailable=*/false)) {
1257+
auto inheritedConformance = mod->lookupConformance(
1258+
super, protocol, /*allowMissing=*/false);
1259+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
1260+
inheritedConformance.hasUnavailableConformance())
1261+
inheritedConformance = ProtocolConformanceRef::forInvalid();
1262+
if (inheritedConformance) {
12621263
return ProtocolConformanceRef(ctx.getInheritedConformance(
12631264
type, inheritedConformance.getConcrete()));
12641265
}

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
@@ -4357,10 +4357,13 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
43574357
if (classDecl) {
43584358
if (Type superclass = classDecl->getSuperclass()) {
43594359
auto classModule = classDecl->getParentModule();
4360-
if (auto inheritedConformance = TypeChecker::conformsToProtocol(
4361-
classDecl->mapTypeIntoContext(superclass),
4362-
proto, classModule, /*allowMissing=*/false,
4363-
/*allowUnavailable=*/false)) {
4360+
auto inheritedConformance = TypeChecker::conformsToProtocol(
4361+
classDecl->mapTypeIntoContext(superclass),
4362+
proto, classModule, /*allowMissing=*/false);
4363+
if (inheritedConformance.hasUnavailableConformance())
4364+
inheritedConformance = ProtocolConformanceRef::forInvalid();
4365+
4366+
if (inheritedConformance) {
43644367
inheritedConformance = inheritedConformance
43654368
.mapConformanceOutOfContext();
43664369
if (inheritedConformance.isConcrete()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,8 +3420,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
34203420
auto overriddenConformance =
34213421
DC->getParentModule()->lookupConformance(Adoptee,
34223422
overridden->getProtocol(),
3423-
/*allowMissing=*/true,
3424-
/*allowUnavailable=*/false);
3423+
/*allowMissing=*/true);
34253424
if (overriddenConformance.isInvalid() ||
34263425
!overriddenConformance.isConcrete())
34273426
continue;
@@ -5646,10 +5645,11 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
56465645

56475646
ProtocolConformanceRef
56485647
TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5649-
bool allowMissing, bool allowUnavailable) {
5648+
bool allowMissing) {
56505649
// Look up conformance in the module.
56515650
auto lookupResult = M->lookupConformance(
5652-
T, Proto, allowMissing, allowUnavailable);
5651+
T, Proto, allowMissing);
5652+
56535653
if (lookupResult.isInvalid()) {
56545654
return ProtocolConformanceRef::forInvalid();
56555655
}

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,8 +807,7 @@ ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto,
807807
/// protocol \c Proto, or \c None.
808808
ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto,
809809
ModuleDecl *M,
810-
bool allowMissing = true,
811-
bool allowUnavailable = true);
810+
bool allowMissing = true);
812811

813812
/// Check whether the type conforms to a given known protocol.
814813
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)