Skip to content

Commit 2225cb2

Browse files
committed
Sema: implement existentialConformsToSelf using a request evaluator.
Add the request `ExistentialConformsToSelfRequest` to lazily determine if an existential conforming to a protocol can be matched with a generic type parameter constrained to that protocol.
1 parent da61cc8 commit 2225cb2

File tree

6 files changed

+99
-45
lines changed

6 files changed

+99
-45
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4120,7 +4120,20 @@ class ProtocolDecl final : public NominalTypeDecl {
41204120
Bits.ProtocolDecl.RequiresClass = requiresClass;
41214121
}
41224122

4123-
bool existentialConformsToSelfSlow();
4123+
/// Returns the cached result of \c existentialConformsToSelf or \c None if it
4124+
/// hasn't yet been computed.
4125+
Optional<bool> getCachedExistentialConformsToSelf() const {
4126+
if (Bits.ProtocolDecl.ExistentialConformsToSelfValid)
4127+
return Bits.ProtocolDecl.ExistentialConformsToSelf;
4128+
4129+
return None;
4130+
}
4131+
4132+
/// Caches the result of \c existentialConformsToSelf
4133+
void setCachedExistentialConformsToSelf(bool result) {
4134+
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
4135+
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
4136+
}
41244137

41254138
bool existentialTypeSupportedSlow();
41264139

@@ -4134,6 +4147,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41344147
friend class SuperclassTypeRequest;
41354148
friend class RequirementSignatureRequest;
41364149
friend class ProtocolRequiresClassRequest;
4150+
friend class ExistentialConformsToSelfRequest;
41374151
friend class TypeChecker;
41384152

41394153
public:
@@ -4204,13 +4218,7 @@ class ProtocolDecl final : public NominalTypeDecl {
42044218
/// This is only permitted if there is nothing "non-trivial" that we
42054219
/// can do with the metatype, which means the protocol must not have
42064220
/// any static methods and must be declared @objc.
4207-
bool existentialConformsToSelf() const {
4208-
if (Bits.ProtocolDecl.ExistentialConformsToSelfValid)
4209-
return Bits.ProtocolDecl.ExistentialConformsToSelf;
4210-
4211-
return const_cast<ProtocolDecl *>(this)
4212-
->existentialConformsToSelfSlow();
4213-
}
4221+
bool existentialConformsToSelf() const;
42144222

42154223
/// Does this protocol require a self-conformance witness table?
42164224
bool requiresSelfConformanceWitnessTable() const;

include/swift/AST/TypeCheckRequests.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,32 @@ class ProtocolRequiresClassRequest:
201201
void cacheResult(bool value) const;
202202
};
203203

204+
/// Determine whether an existential conforming to a protocol can be matched
205+
/// with a generic type parameter constrained to that protocol.
206+
class ExistentialConformsToSelfRequest:
207+
public SimpleRequest<ExistentialConformsToSelfRequest,
208+
bool(ProtocolDecl *),
209+
CacheKind::SeparatelyCached> {
210+
public:
211+
using SimpleRequest::SimpleRequest;
212+
213+
private:
214+
friend SimpleRequest;
215+
216+
// Evaluation.
217+
llvm::Expected<bool> evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;
218+
219+
public:
220+
// Cycle handling.
221+
void diagnoseCycle(DiagnosticEngine &diags) const;
222+
void noteCycleStep(DiagnosticEngine &diags) const;
223+
224+
// Separate caching.
225+
bool isCached() const { return true; }
226+
Optional<bool> getCachedResult() const;
227+
void cacheResult(bool value) const;
228+
};
229+
204230
/// Determine whether the given declaration is 'final'.
205231
class IsFinalRequest :
206232
public SimpleRequest<IsFinalRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SWIFT_TYPEID(EnumRawTypeRequest)
2020
SWIFT_TYPEID(OverriddenDeclsRequest)
2121
SWIFT_TYPEID(IsObjCRequest)
2222
SWIFT_TYPEID(ProtocolRequiresClassRequest)
23+
SWIFT_TYPEID(ExistentialConformsToSelfRequest)
2324
SWIFT_TYPEID(IsFinalRequest)
2425
SWIFT_TYPEID(IsDynamicRequest)
2526
SWIFT_TYPEID(RequirementRequest)

lib/AST/Decl.cpp

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4329,43 +4329,9 @@ bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {
43294329
return isSpecificProtocol(KnownProtocolKind::Error);
43304330
}
43314331

4332-
bool ProtocolDecl::existentialConformsToSelfSlow() {
4333-
// Assume for now that the existential conforms to itself; this
4334-
// prevents circularity issues.
4335-
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
4336-
Bits.ProtocolDecl.ExistentialConformsToSelf = true;
4337-
4338-
// If it's not @objc, it conforms to itself only if it has a
4339-
// self-conformance witness table.
4340-
if (!isObjC()) {
4341-
bool hasSelfConformance = requiresSelfConformanceWitnessTable();
4342-
Bits.ProtocolDecl.ExistentialConformsToSelf = hasSelfConformance;
4343-
return hasSelfConformance;
4344-
}
4345-
4346-
// Check whether this protocol conforms to itself.
4347-
for (auto member : getMembers()) {
4348-
if (member->isInvalid())
4349-
continue;
4350-
4351-
if (auto vd = dyn_cast<ValueDecl>(member)) {
4352-
if (!vd->isInstanceMember()) {
4353-
// A protocol cannot conform to itself if it has static members.
4354-
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
4355-
return false;
4356-
}
4357-
}
4358-
}
4359-
4360-
// Check whether any of the inherited protocols fail to conform to
4361-
// themselves.
4362-
for (auto proto : getInheritedProtocols()) {
4363-
if (!proto->existentialConformsToSelf()) {
4364-
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
4365-
return false;
4366-
}
4367-
}
4368-
return true;
4332+
bool ProtocolDecl::existentialConformsToSelf() const {
4333+
return evaluateOrDefault(getASTContext().evaluator,
4334+
ExistentialConformsToSelfRequest{const_cast<ProtocolDecl *>(this)}, true);
43694335
}
43704336

43714337
/// Classify usages of Self in the given type.

lib/AST/TypeCheckRequests.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,31 @@ void ProtocolRequiresClassRequest::cacheResult(bool value) const {
211211
decl->setCachedRequiresClass(value);
212212
}
213213

214+
//----------------------------------------------------------------------------//
215+
// existentialConformsToSelf computation.
216+
//----------------------------------------------------------------------------//
217+
218+
void ExistentialConformsToSelfRequest::diagnoseCycle(DiagnosticEngine &diags) const {
219+
auto decl = std::get<0>(getStorage());
220+
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
221+
}
222+
223+
void ExistentialConformsToSelfRequest::noteCycleStep(DiagnosticEngine &diags) const {
224+
auto requirement = std::get<0>(getStorage());
225+
diags.diagnose(requirement, diag::kind_declname_declared_here,
226+
DescriptiveDeclKind::Protocol, requirement->getName());
227+
}
228+
229+
Optional<bool> ExistentialConformsToSelfRequest::getCachedResult() const {
230+
auto decl = std::get<0>(getStorage());
231+
return decl->getCachedExistentialConformsToSelf();
232+
}
233+
234+
void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
235+
auto decl = std::get<0>(getStorage());
236+
decl->setCachedExistentialConformsToSelf(value);
237+
}
238+
214239
//----------------------------------------------------------------------------//
215240
// isFinal computation.
216241
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckDecl.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,34 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator,
12691269
return false;
12701270
}
12711271

1272+
llvm::Expected<bool>
1273+
ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
1274+
ProtocolDecl *decl) const {
1275+
// If it's not @objc, it conforms to itself only if it has a self-conformance
1276+
// witness table.
1277+
if (!decl->isObjC())
1278+
return decl->requiresSelfConformanceWitnessTable();
1279+
1280+
// Check whether this protocol conforms to itself.
1281+
for (auto member : decl->getMembers()) {
1282+
if (member->isInvalid()) continue;
1283+
1284+
if (auto vd = dyn_cast<ValueDecl>(member)) {
1285+
// A protocol cannot conform to itself if it has static members.
1286+
if (!vd->isInstanceMember())
1287+
return false;
1288+
}
1289+
}
1290+
1291+
// Check whether any of the inherited protocols fail to conform to themselves.
1292+
for (auto proto : decl->getInheritedProtocols()) {
1293+
if (!proto->existentialConformsToSelf())
1294+
return false;
1295+
}
1296+
1297+
return true;
1298+
}
1299+
12721300
llvm::Expected<bool>
12731301
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
12741302
if (isa<ClassDecl>(decl))

0 commit comments

Comments
 (0)