Skip to content

Sema: implement existentialConformsToSelf using a request evaluator. #26229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4120,7 +4120,20 @@ class ProtocolDecl final : public NominalTypeDecl {
Bits.ProtocolDecl.RequiresClass = requiresClass;
}

bool existentialConformsToSelfSlow();
/// Returns the cached result of \c existentialConformsToSelf or \c None if it
/// hasn't yet been computed.
Optional<bool> getCachedExistentialConformsToSelf() const {
if (Bits.ProtocolDecl.ExistentialConformsToSelfValid)
return Bits.ProtocolDecl.ExistentialConformsToSelf;

return None;
}

/// Caches the result of \c existentialConformsToSelf
void setCachedExistentialConformsToSelf(bool result) {
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
}

bool existentialTypeSupportedSlow();

Expand All @@ -4134,6 +4147,7 @@ class ProtocolDecl final : public NominalTypeDecl {
friend class SuperclassTypeRequest;
friend class RequirementSignatureRequest;
friend class ProtocolRequiresClassRequest;
friend class ExistentialConformsToSelfRequest;
friend class TypeChecker;

public:
Expand Down Expand Up @@ -4204,13 +4218,7 @@ class ProtocolDecl final : public NominalTypeDecl {
/// This is only permitted if there is nothing "non-trivial" that we
/// can do with the metatype, which means the protocol must not have
/// any static methods and must be declared @objc.
bool existentialConformsToSelf() const {
if (Bits.ProtocolDecl.ExistentialConformsToSelfValid)
return Bits.ProtocolDecl.ExistentialConformsToSelf;

return const_cast<ProtocolDecl *>(this)
->existentialConformsToSelfSlow();
}
bool existentialConformsToSelf() const;

/// Does this protocol require a self-conformance witness table?
bool requiresSelfConformanceWitnessTable() const;
Expand Down
26 changes: 26 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,32 @@ class ProtocolRequiresClassRequest:
void cacheResult(bool value) const;
};

/// Determine whether an existential conforming to a protocol can be matched
/// with a generic type parameter constrained to that protocol.
class ExistentialConformsToSelfRequest:
public SimpleRequest<ExistentialConformsToSelfRequest,
bool(ProtocolDecl *),
CacheKind::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<bool> evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;

public:
// Cycle handling.
void diagnoseCycle(DiagnosticEngine &diags) const;
void noteCycleStep(DiagnosticEngine &diags) const;

// Separate caching.
bool isCached() const { return true; }
Optional<bool> getCachedResult() const;
void cacheResult(bool value) const;
};

/// Determine whether the given declaration is 'final'.
class IsFinalRequest :
public SimpleRequest<IsFinalRequest,
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SWIFT_TYPEID(EnumRawTypeRequest)
SWIFT_TYPEID(OverriddenDeclsRequest)
SWIFT_TYPEID(IsObjCRequest)
SWIFT_TYPEID(ProtocolRequiresClassRequest)
SWIFT_TYPEID(ExistentialConformsToSelfRequest)
SWIFT_TYPEID(IsFinalRequest)
SWIFT_TYPEID(IsDynamicRequest)
SWIFT_TYPEID(RequirementRequest)
Expand Down
40 changes: 3 additions & 37 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4329,43 +4329,9 @@ bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {
return isSpecificProtocol(KnownProtocolKind::Error);
}

bool ProtocolDecl::existentialConformsToSelfSlow() {
// Assume for now that the existential conforms to itself; this
// prevents circularity issues.
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
Bits.ProtocolDecl.ExistentialConformsToSelf = true;

// If it's not @objc, it conforms to itself only if it has a
// self-conformance witness table.
if (!isObjC()) {
bool hasSelfConformance = requiresSelfConformanceWitnessTable();
Bits.ProtocolDecl.ExistentialConformsToSelf = hasSelfConformance;
return hasSelfConformance;
}

// Check whether this protocol conforms to itself.
for (auto member : getMembers()) {
if (member->isInvalid())
continue;

if (auto vd = dyn_cast<ValueDecl>(member)) {
if (!vd->isInstanceMember()) {
// A protocol cannot conform to itself if it has static members.
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
return false;
}
}
}

// Check whether any of the inherited protocols fail to conform to
// themselves.
for (auto proto : getInheritedProtocols()) {
if (!proto->existentialConformsToSelf()) {
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
return false;
}
}
return true;
bool ProtocolDecl::existentialConformsToSelf() const {
return evaluateOrDefault(getASTContext().evaluator,
ExistentialConformsToSelfRequest{const_cast<ProtocolDecl *>(this)}, true);
}

/// Classify usages of Self in the given type.
Expand Down
25 changes: 25 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,31 @@ void ProtocolRequiresClassRequest::cacheResult(bool value) const {
decl->setCachedRequiresClass(value);
}

//----------------------------------------------------------------------------//
// existentialConformsToSelf computation.
//----------------------------------------------------------------------------//

void ExistentialConformsToSelfRequest::diagnoseCycle(DiagnosticEngine &diags) const {
auto decl = std::get<0>(getStorage());
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
}

void ExistentialConformsToSelfRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto requirement = std::get<0>(getStorage());
diags.diagnose(requirement, diag::kind_declname_declared_here,
DescriptiveDeclKind::Protocol, requirement->getName());
}

Optional<bool> ExistentialConformsToSelfRequest::getCachedResult() const {
auto decl = std::get<0>(getStorage());
return decl->getCachedExistentialConformsToSelf();
}

void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
auto decl = std::get<0>(getStorage());
decl->setCachedExistentialConformsToSelf(value);
}

//----------------------------------------------------------------------------//
// isFinal computation.
//----------------------------------------------------------------------------//
Expand Down
28 changes: 28 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,34 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator,
return false;
}

llvm::Expected<bool>
ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
ProtocolDecl *decl) const {
// If it's not @objc, it conforms to itself only if it has a self-conformance
// witness table.
if (!decl->isObjC())
return decl->requiresSelfConformanceWitnessTable();

// Check whether this protocol conforms to itself.
for (auto member : decl->getMembers()) {
if (member->isInvalid()) continue;

if (auto vd = dyn_cast<ValueDecl>(member)) {
// A protocol cannot conform to itself if it has static members.
if (!vd->isInstanceMember())
return false;
}
}

// Check whether any of the inherited protocols fail to conform to themselves.
for (auto proto : decl->getInheritedProtocols()) {
if (!proto->existentialConformsToSelf())
return false;
}

return true;
}

llvm::Expected<bool>
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (isa<ClassDecl>(decl))
Expand Down