Skip to content

Commit 4b716eb

Browse files
authored
Merge pull request #59454 from slavapestov/lookup-conformance-unavailable-fix-5.7
AST: Narrow the filtering of unavailable conformances to Sendable only [5.7]
2 parents e0db905 + fc35ea6 commit 4b716eb

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
@@ -1758,6 +1758,9 @@ SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanc
17581758
}
17591759

17601760
bool ProtocolConformanceRef::hasUnavailableConformance() const {
1761+
if (isInvalid())
1762+
return false;
1763+
17611764
// Abstract conformances are never unavailable.
17621765
if (!isConcrete())
17631766
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
@@ -3423,8 +3423,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
34233423
auto overriddenConformance =
34243424
DC->getParentModule()->lookupConformance(Adoptee,
34253425
overridden->getProtocol(),
3426-
/*allowMissing=*/true,
3427-
/*allowUnavailable=*/false);
3426+
/*allowMissing=*/true);
34283427
if (overriddenConformance.isInvalid() ||
34293428
!overriddenConformance.isConcrete())
34303429
continue;
@@ -5654,10 +5653,11 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
56545653

56555654
ProtocolConformanceRef
56565655
TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5657-
bool allowMissing, bool allowUnavailable) {
5656+
bool allowMissing) {
56585657
// Look up conformance in the module.
56595658
auto lookupResult = M->lookupConformance(
5660-
T, Proto, allowMissing, allowUnavailable);
5659+
T, Proto, allowMissing);
5660+
56615661
if (lookupResult.isInvalid()) {
56625662
return ProtocolConformanceRef::forInvalid();
56635663
}

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)