Skip to content

Commit 2fdae64

Browse files
authored
Merge pull request #26201 from hborla/protocol-requires-class-request
Sema: implement `requiresClass` using a request evaluator.
2 parents d3193f8 + 71fb262 commit 2fdae64

File tree

10 files changed

+115
-57
lines changed

10 files changed

+115
-57
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4105,7 +4105,20 @@ class ProtocolDecl final : public NominalTypeDecl {
41054105
/// by this protocol.
41064106
const Requirement *RequirementSignature = nullptr;
41074107

4108-
bool requiresClassSlow();
4108+
/// Returns the cached result of \c requiresClass or \c None if it hasn't yet
4109+
/// been computed.
4110+
Optional<bool> getCachedRequiresClass() const {
4111+
if (Bits.ProtocolDecl.RequiresClassValid)
4112+
return Bits.ProtocolDecl.RequiresClass;
4113+
4114+
return None;
4115+
}
4116+
4117+
/// Caches the result of \c requiresClass
4118+
void setCachedRequiresClass(bool requiresClass) {
4119+
Bits.ProtocolDecl.RequiresClassValid = true;
4120+
Bits.ProtocolDecl.RequiresClass = requiresClass;
4121+
}
41094122

41104123
bool existentialConformsToSelfSlow();
41114124

@@ -4120,6 +4133,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41204133
friend class SuperclassDeclRequest;
41214134
friend class SuperclassTypeRequest;
41224135
friend class RequirementSignatureRequest;
4136+
friend class ProtocolRequiresClassRequest;
41234137
friend class TypeChecker;
41244138

41254139
public:
@@ -4183,19 +4197,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41834197
}
41844198

41854199
/// True if this protocol can only be conformed to by class types.
4186-
bool requiresClass() const {
4187-
if (Bits.ProtocolDecl.RequiresClassValid)
4188-
return Bits.ProtocolDecl.RequiresClass;
4189-
4190-
return const_cast<ProtocolDecl *>(this)->requiresClassSlow();
4191-
}
4192-
4193-
/// Specify that this protocol is class-bounded, e.g., because it was
4194-
/// annotated with the 'class' keyword.
4195-
void setRequiresClass(bool requiresClass = true) {
4196-
Bits.ProtocolDecl.RequiresClassValid = true;
4197-
Bits.ProtocolDecl.RequiresClass = requiresClass;
4198-
}
4200+
bool requiresClass() const;
41994201

42004202
/// Determine whether an existential conforming to this protocol can be
42014203
/// matched with a generic type parameter constrained to this protocol.

include/swift/AST/DiagnosticsCommon.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ ERROR(circular_class_inheritance,none,
153153
ERROR(circular_enum_inheritance,none,
154154
"%0 has a raw type that depends on itself", (Identifier))
155155

156+
ERROR(circular_protocol_def,none,
157+
"protocol %0 refines itself", (Identifier))
158+
159+
NOTE(kind_declname_declared_here,none,
160+
"%0 %1 declared here", (DescriptiveDeclKind, DeclName))
161+
156162
#ifndef DIAG_NO_UNDEF
157163
# if defined(DIAG)
158164
# undef DIAG

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
DIAG(NOTE,ID,Options,Text,Signature)
3838
#endif
3939

40-
NOTE(kind_declname_declared_here,none,
41-
"%0 %1 declared here", (DescriptiveDeclKind, DeclName))
4240
NOTE(decl_declared_here,none,
4341
"%0 declared here", (DeclName))
4442
NOTE(kind_declared_here,none,
@@ -2037,8 +2035,6 @@ ERROR(typealias_outside_of_protocol,none,
20372035
"type alias %0 can only be used with a concrete type or "
20382036
"generic parameter base", (Identifier))
20392037

2040-
ERROR(circular_protocol_def,none,
2041-
"protocol %0 refines itself", (Identifier))
20422038
ERROR(objc_protocol_inherits_non_objc_protocol,none,
20432039
"@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type))
20442040

include/swift/AST/TypeCheckRequests.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,31 @@ class IsObjCRequest :
176176
void cacheResult(bool value) const;
177177
};
178178

179+
/// Determine whether the given protocol declaration is class-bounded.
180+
class ProtocolRequiresClassRequest:
181+
public SimpleRequest<ProtocolRequiresClassRequest,
182+
bool(ProtocolDecl *),
183+
CacheKind::SeparatelyCached> {
184+
public:
185+
using SimpleRequest::SimpleRequest;
186+
187+
private:
188+
friend SimpleRequest;
189+
190+
// Evaluation.
191+
llvm::Expected<bool> evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;
192+
193+
public:
194+
// Cycle handling.
195+
void diagnoseCycle(DiagnosticEngine &diags) const;
196+
void noteCycleStep(DiagnosticEngine &diags) const;
197+
198+
// Separate caching.
199+
bool isCached() const { return true; }
200+
Optional<bool> getCachedResult() const;
201+
void cacheResult(bool value) const;
202+
};
203+
179204
/// Determine whether the given declaration is 'final'.
180205
class IsFinalRequest :
181206
public SimpleRequest<IsFinalRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SWIFT_TYPEID(SuperclassTypeRequest)
1919
SWIFT_TYPEID(EnumRawTypeRequest)
2020
SWIFT_TYPEID(OverriddenDeclsRequest)
2121
SWIFT_TYPEID(IsObjCRequest)
22+
SWIFT_TYPEID(ProtocolRequiresClassRequest)
2223
SWIFT_TYPEID(IsFinalRequest)
2324
SWIFT_TYPEID(IsDynamicRequest)
2425
SWIFT_TYPEID(RequirementRequest)

lib/AST/Decl.cpp

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4320,41 +4320,9 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
43204320
});
43214321
}
43224322

4323-
bool ProtocolDecl::requiresClassSlow() {
4324-
// Set this first to catch (invalid) circular inheritance.
4325-
Bits.ProtocolDecl.RequiresClassValid = true;
4326-
Bits.ProtocolDecl.RequiresClass = false;
4327-
4328-
// Quick check: @objc protocols require a class.
4329-
if (isObjC())
4330-
return Bits.ProtocolDecl.RequiresClass = true;
4331-
4332-
// Determine the set of nominal types that this protocol inherits.
4333-
bool anyObject = false;
4334-
auto allInheritedNominals =
4335-
getDirectlyInheritedNominalTypeDecls(this, anyObject);
4336-
4337-
// Quick check: do we inherit AnyObject?
4338-
if (anyObject) {
4339-
Bits.ProtocolDecl.RequiresClass = true;
4340-
return true;
4341-
}
4342-
4343-
// Look through all of the inherited nominals for a superclass or a
4344-
// class-bound protocol.
4345-
for (const auto found : allInheritedNominals) {
4346-
// Superclass bound.
4347-
if (isa<ClassDecl>(found.second))
4348-
return Bits.ProtocolDecl.RequiresClass = true;
4349-
4350-
// A protocol that might be class-constrained;
4351-
if (auto proto = dyn_cast<ProtocolDecl>(found.second)) {
4352-
if (proto->requiresClass())
4353-
return Bits.ProtocolDecl.RequiresClass = true;
4354-
}
4355-
}
4356-
4357-
return Bits.ProtocolDecl.RequiresClass;
4323+
bool ProtocolDecl::requiresClass() const {
4324+
return evaluateOrDefault(getASTContext().evaluator,
4325+
ProtocolRequiresClassRequest{const_cast<ProtocolDecl *>(this)}, false);
43584326
}
43594327

43604328
bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {

lib/AST/TypeCheckRequests.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,32 @@ void IsObjCRequest::cacheResult(bool value) const {
185185
decl->setIsObjC(value);
186186
}
187187

188+
//----------------------------------------------------------------------------//
189+
// requiresClass computation.
190+
//----------------------------------------------------------------------------//
191+
192+
void ProtocolRequiresClassRequest::diagnoseCycle(DiagnosticEngine &diags) const {
193+
auto decl = std::get<0>(getStorage());
194+
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
195+
}
196+
197+
void ProtocolRequiresClassRequest::noteCycleStep(DiagnosticEngine &diags) const {
198+
auto requirement = std::get<0>(getStorage());
199+
diags.diagnose(requirement, diag::kind_declname_declared_here,
200+
DescriptiveDeclKind::Protocol,
201+
requirement->getName());
202+
}
203+
204+
Optional<bool> ProtocolRequiresClassRequest::getCachedResult() const {
205+
auto decl = std::get<0>(getStorage());
206+
return decl->getCachedRequiresClass();
207+
}
208+
209+
void ProtocolRequiresClassRequest::cacheResult(bool value) const {
210+
auto decl = std::get<0>(getStorage());
211+
decl->setCachedRequiresClass(value);
212+
}
213+
188214
//----------------------------------------------------------------------------//
189215
// isFinal computation.
190216
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckDecl.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,39 @@ static bool doesAccessorNeedDynamicAttribute(AccessorDecl *accessor) {
12361236
llvm_unreachable("covered switch");
12371237
}
12381238

1239+
llvm::Expected<bool>
1240+
ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator,
1241+
ProtocolDecl *decl) const {
1242+
// Quick check: @objc protocols require a class.
1243+
if (decl->isObjC())
1244+
return true;
1245+
1246+
// Determine the set of nominal types that this protocol inherits.
1247+
bool anyObject = false;
1248+
auto allInheritedNominals =
1249+
getDirectlyInheritedNominalTypeDecls(decl, anyObject);
1250+
1251+
// Quick check: do we inherit AnyObject?
1252+
if (anyObject)
1253+
return true;
1254+
1255+
// Look through all of the inherited nominals for a superclass or a
1256+
// class-bound protocol.
1257+
for (const auto found : allInheritedNominals) {
1258+
// Superclass bound.
1259+
if (isa<ClassDecl>(found.second))
1260+
return true;
1261+
1262+
// A protocol that might be class-constrained.
1263+
if (auto proto = dyn_cast<ProtocolDecl>(found.second)) {
1264+
if (proto->requiresClass())
1265+
return true;
1266+
}
1267+
}
1268+
1269+
return false;
1270+
}
1271+
12391272
llvm::Expected<bool>
12401273
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
12411274
if (isa<ClassDecl>(decl))

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3323,7 +3323,8 @@ class swift::DeclDeserializer {
33233323
None, /*TrailingWhere=*/nullptr);
33243324
declOrOffset = proto;
33253325

3326-
proto->setRequiresClass(isClassBounded);
3326+
ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto},
3327+
std::move(isClassBounded));
33273328
proto->setExistentialTypeSupported(existentialTypeSupported);
33283329

33293330
if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) {

test/decl/protocol/protocols.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ struct DoesNotConform : Up {
101101

102102
// Circular protocols
103103

104-
protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error {{protocol 'CircleMiddle' refines itself}}
104+
protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 2 {{protocol 'CircleMiddle' refines itself}}
105105
protocol CircleStart : CircleEnd { func circle_start() }
106-
// expected-note@-1{{protocol 'CircleStart' declared here}}
107-
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note{{protocol 'CircleEnd' declared here}}
106+
// expected-note@-1 2{{protocol 'CircleStart' declared here}}
107+
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 2 {{protocol 'CircleEnd' declared here}}
108108

109109
protocol CircleEntry : CircleTrivial { }
110110
protocol CircleTrivial : CircleTrivial { } // expected-error {{protocol 'CircleTrivial' refines itself}}

0 commit comments

Comments
 (0)