Skip to content

Commit c9c50b4

Browse files
committed
[Requirement machine] Ignore unavailable conformances on superclass constraints.
When determining whether a superclass conforms to a particular protocol, skip unavailable conformances. This way, we don't minimize away a constraint that might only apply to subclasses of the specified superclass. Fixes rdar://91853658.
1 parent f7f0a54 commit c9c50b4

File tree

12 files changed

+88
-38
lines changed

12 files changed

+88
-38
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/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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,28 @@ SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanc
17431743
return SourceLoc();
17441744
}
17451745

1746+
bool ProtocolConformanceRef::hasUnavailableConformance() const {
1747+
// Abstract conformances are never unavailable.
1748+
if (!isConcrete())
1749+
return false;
1750+
1751+
// Check whether this conformance is on an unavailable extension.
1752+
auto concrete = getConcrete();
1753+
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
1754+
if (ext && AvailableAttr::isUnavailable(ext))
1755+
return true;
1756+
1757+
// Check the conformances in the substitution map.
1758+
auto module = concrete->getDeclContext()->getParentModule();
1759+
auto subMap = concrete->getSubstitutions(module);
1760+
for (auto subConformance : subMap.getConformances()) {
1761+
if (subConformance.hasUnavailableConformance())
1762+
return true;
1763+
}
1764+
1765+
return false;
1766+
}
1767+
17461768
bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const {
17471769
return forEachMissingConformance(module,
17481770
[](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: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -593,29 +593,6 @@ static void addSendableFixIt(const GenericTypeParamDecl *genericArgument,
593593
}
594594
}
595595

596-
/// Determine whether there is an unavailable conformance here.
597-
static bool hasUnavailableConformance(ProtocolConformanceRef conformance) {
598-
// Abstract conformances are never unavailable.
599-
if (!conformance.isConcrete())
600-
return false;
601-
602-
// Check whether this conformance is on an unavailable extension.
603-
auto concrete = conformance.getConcrete();
604-
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
605-
if (ext && AvailableAttr::isUnavailable(ext))
606-
return true;
607-
608-
// Check the conformances in the substitution map.
609-
auto module = concrete->getDeclContext()->getParentModule();
610-
auto subMap = concrete->getSubstitutions(module);
611-
for (auto subConformance : subMap.getConformances()) {
612-
if (hasUnavailableConformance(subConformance))
613-
return true;
614-
}
615-
616-
return false;
617-
}
618-
619596
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
620597
return contextRequiresStrictConcurrencyChecking(dc, [](const AbstractClosureExpr *) {
621598
return Type();
@@ -913,7 +890,7 @@ bool swift::diagnoseNonSendableTypes(
913890

914891
// FIXME: More detail for unavailable conformances.
915892
auto conformance = TypeChecker::conformsToProtocol(type, proto, module);
916-
if (conformance.isInvalid() || hasUnavailableConformance(conformance)) {
893+
if (conformance.isInvalid() || conformance.hasUnavailableConformance()) {
917894
return diagnoseSingleNonSendableType(type, fromContext, loc, diagnose);
918895
}
919896

@@ -1062,7 +1039,7 @@ namespace {
10621039
return true;
10631040

10641041
// If there is an unavailable conformance here, fail.
1065-
if (hasUnavailableConformance(conformance))
1042+
if (conformance.hasUnavailableConformance())
10661043
return true;
10671044

10681045
// Look for missing Sendable conformances.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3389,7 +3389,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
33893389
auto overriddenConformance =
33903390
DC->getParentModule()->lookupConformance(Adoptee,
33913391
overridden->getProtocol(),
3392-
/*allowMissing=*/true);
3392+
/*allowMissing=*/true,
3393+
/*allowUnavailable=*/false);
33933394
if (overriddenConformance.isInvalid() ||
33943395
!overriddenConformance.isConcrete())
33953396
continue;

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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,12 @@ 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 { }

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 { }

test/decl/protocol/req/recursion.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protocol PI {
7373
}
7474

7575
struct SI<A: PI> : I where A : I, A.T == SI<A> {
76-
// expected-error@-1 3{{generic struct 'SI' has self-referential generic requirements}}
76+
// expected-error@-1 5{{generic struct 'SI' has self-referential generic requirements}}
7777
func ggg<T : I>(t: T.Type) -> T {
7878
return T()
7979
}
@@ -104,5 +104,5 @@ struct SU<A: P> where A.T == SU {
104104
}
105105

106106
struct SIU<A: PI> : I where A : I, A.T == SIU {
107-
// expected-error@-1 3{{generic struct 'SIU' has self-referential generic requirements}}
107+
// expected-error@-1 5{{generic struct 'SIU' has self-referential generic requirements}}
108108
}

0 commit comments

Comments
 (0)