Skip to content

Commit 10a4e3e

Browse files
authored
Merge pull request swiftlang#26278 from hborla/existential-type-supported-request
Sema: implement `existentialTypeSupported` using a request evaluator.
2 parents 3d6c5d8 + 9f19597 commit 10a4e3e

File tree

9 files changed

+110
-63
lines changed

9 files changed

+110
-63
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,7 +4122,20 @@ class ProtocolDecl final : public NominalTypeDecl {
41224122
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
41234123
}
41244124

4125-
bool existentialTypeSupportedSlow();
4125+
/// Returns the cached result of \c existentialTypeSupported or \c None if it
4126+
/// hasn't yet been computed.
4127+
Optional<bool> getCachedExistentialTypeSupported() {
4128+
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
4129+
return Bits.ProtocolDecl.ExistentialTypeSupported;
4130+
4131+
return None;
4132+
}
4133+
4134+
/// Caches the result of \c existentialTypeSupported
4135+
void setCachedExistentialTypeSupported(bool supported) {
4136+
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4137+
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
4138+
}
41264139

41274140
ArrayRef<ProtocolDecl *> getInheritedProtocolsSlow();
41284141

@@ -4135,6 +4148,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41354148
friend class RequirementSignatureRequest;
41364149
friend class ProtocolRequiresClassRequest;
41374150
friend class ExistentialConformsToSelfRequest;
4151+
friend class ExistentialTypeSupportedRequest;
41384152
friend class TypeChecker;
41394153

41404154
public:
@@ -4232,21 +4246,7 @@ class ProtocolDecl final : public NominalTypeDecl {
42324246
/// conforming to this protocol. This is only permitted if the types of
42334247
/// all the members do not contain any associated types, and do not
42344248
/// contain 'Self' in 'parameter' or 'other' position.
4235-
bool existentialTypeSupported() const {
4236-
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
4237-
return Bits.ProtocolDecl.ExistentialTypeSupported;
4238-
4239-
return const_cast<ProtocolDecl *>(this)
4240-
->existentialTypeSupportedSlow();
4241-
}
4242-
4243-
/// Explicitly set the existentialTypeSupported flag, without computing
4244-
/// it from members. Only called from deserialization, where the flag
4245-
/// was stored in the serialized record.
4246-
void setExistentialTypeSupported(bool supported) {
4247-
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
4248-
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4249-
}
4249+
bool existentialTypeSupported() const;
42504250

42514251
private:
42524252
void computeKnownProtocolKind() const;

include/swift/AST/TypeCheckRequests.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@ class ExistentialConformsToSelfRequest:
227227
void cacheResult(bool value) const;
228228
};
229229

230+
/// Determine whether we are allowed to refer to an existential type conforming
231+
/// to this protocol.
232+
class ExistentialTypeSupportedRequest:
233+
public SimpleRequest<ExistentialTypeSupportedRequest,
234+
bool(ProtocolDecl *),
235+
CacheKind::SeparatelyCached> {
236+
public:
237+
using SimpleRequest::SimpleRequest;
238+
239+
private:
240+
friend SimpleRequest;
241+
242+
// Evaluation.
243+
llvm::Expected<bool> evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;
244+
245+
public:
246+
// Cycle handling.
247+
void diagnoseCycle(DiagnosticEngine &diags) const;
248+
void noteCycleStep(DiagnosticEngine &diags) const;
249+
250+
// Separate caching.
251+
bool isCached() const { return true; }
252+
Optional<bool> getCachedResult() const;
253+
void cacheResult(bool value) const;
254+
};
255+
230256
/// Determine whether the given declaration is 'final'.
231257
class IsFinalRequest :
232258
public SimpleRequest<IsFinalRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ SWIFT_TYPEID(OverriddenDeclsRequest)
2121
SWIFT_TYPEID(IsObjCRequest)
2222
SWIFT_TYPEID(ProtocolRequiresClassRequest)
2323
SWIFT_TYPEID(ExistentialConformsToSelfRequest)
24+
SWIFT_TYPEID(ExistentialTypeSupportedRequest)
2425
SWIFT_TYPEID(IsFinalRequest)
2526
SWIFT_TYPEID(IsDynamicRequest)
2627
SWIFT_TYPEID(RequirementRequest)

lib/AST/Decl.cpp

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,47 +4519,9 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
45194519
return true;
45204520
}
45214521

4522-
bool ProtocolDecl::existentialTypeSupportedSlow() {
4523-
// Assume for now that the existential type is supported; this
4524-
// prevents circularity issues.
4525-
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4526-
Bits.ProtocolDecl.ExistentialTypeSupported = true;
4527-
4528-
// ObjC protocols can always be existential.
4529-
if (isObjC())
4530-
return true;
4531-
4532-
for (auto member : getMembers()) {
4533-
// Check for associated types.
4534-
if (isa<AssociatedTypeDecl>(member)) {
4535-
// An existential type cannot be used if the protocol has an
4536-
// associated type.
4537-
Bits.ProtocolDecl.ExistentialTypeSupported = false;
4538-
return false;
4539-
}
4540-
4541-
// For value members, look at their type signatures.
4542-
if (auto valueMember = dyn_cast<ValueDecl>(member)) {
4543-
if (!valueMember->hasInterfaceType())
4544-
if (auto *resolver = getASTContext().getLazyResolver())
4545-
resolver->resolveDeclSignature(valueMember);
4546-
4547-
if (!isAvailableInExistential(valueMember)) {
4548-
Bits.ProtocolDecl.ExistentialTypeSupported = false;
4549-
return false;
4550-
}
4551-
}
4552-
}
4553-
4554-
// Check whether all of the inherited protocols can have existential
4555-
// types themselves.
4556-
for (auto proto : getInheritedProtocols()) {
4557-
if (!proto->existentialTypeSupported()) {
4558-
Bits.ProtocolDecl.ExistentialTypeSupported = false;
4559-
return false;
4560-
}
4561-
}
4562-
return true;
4522+
bool ProtocolDecl::existentialTypeSupported() const {
4523+
return evaluateOrDefault(getASTContext().evaluator,
4524+
ExistentialTypeSupportedRequest{const_cast<ProtocolDecl *>(this)}, true);
45634525
}
45644526

45654527
StringRef ProtocolDecl::getObjCRuntimeName(

lib/AST/TypeCheckRequests.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,31 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
236236
decl->setCachedExistentialConformsToSelf(value);
237237
}
238238

239+
//----------------------------------------------------------------------------//
240+
// existentialTypeSupported computation.
241+
//----------------------------------------------------------------------------//
242+
243+
void ExistentialTypeSupportedRequest::diagnoseCycle(DiagnosticEngine &diags) const {
244+
auto decl = std::get<0>(getStorage());
245+
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
246+
}
247+
248+
void ExistentialTypeSupportedRequest::noteCycleStep(DiagnosticEngine &diags) const {
249+
auto requirement = std::get<0>(getStorage());
250+
diags.diagnose(requirement, diag::kind_declname_declared_here,
251+
DescriptiveDeclKind::Protocol, requirement->getName());
252+
}
253+
254+
Optional<bool> ExistentialTypeSupportedRequest::getCachedResult() const {
255+
auto decl = std::get<0>(getStorage());
256+
return decl->getCachedExistentialTypeSupported();
257+
}
258+
259+
void ExistentialTypeSupportedRequest::cacheResult(bool value) const {
260+
auto decl = std::get<0>(getStorage());
261+
decl->setCachedExistentialTypeSupported(value);
262+
}
263+
239264
//----------------------------------------------------------------------------//
240265
// isFinal computation.
241266
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckDecl.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,38 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
12971297
return true;
12981298
}
12991299

1300+
llvm::Expected<bool>
1301+
ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator,
1302+
ProtocolDecl *decl) const {
1303+
// ObjC protocols can always be existential.
1304+
if (decl->isObjC())
1305+
return true;
1306+
1307+
for (auto member : decl->getMembers()) {
1308+
// Existential types cannot be used if the protocol has an associated type.
1309+
if (isa<AssociatedTypeDecl>(member))
1310+
return false;
1311+
1312+
// For value members, look at their type signatures.
1313+
if (auto valueMember = dyn_cast<ValueDecl>(member)) {
1314+
if (!valueMember->hasInterfaceType())
1315+
if (auto *resolver = decl->getASTContext().getLazyResolver())
1316+
resolver->resolveDeclSignature(valueMember);
1317+
1318+
if (!decl->isAvailableInExistential(valueMember))
1319+
return false;
1320+
}
1321+
}
1322+
1323+
// Check whether all of the inherited protocols support existential types.
1324+
for (auto proto : decl->getInheritedProtocols()) {
1325+
if (!proto->existentialTypeSupported())
1326+
return false;
1327+
}
1328+
1329+
return true;
1330+
}
1331+
13001332
llvm::Expected<bool>
13011333
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
13021334
if (isa<ClassDecl>(decl))

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3340,7 +3340,8 @@ class swift::DeclDeserializer {
33403340

33413341
ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto},
33423342
std::move(isClassBounded));
3343-
proto->setExistentialTypeSupported(existentialTypeSupported);
3343+
ctx.evaluator.cacheOutput(ExistentialTypeSupportedRequest{proto},
3344+
std::move(existentialTypeSupported));
33443345

33453346
if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) {
33463347
proto->setAccess(*accessLevel);

test/decl/protocol/conforms/circular_validation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}}
1313
}
1414

1515
// FIXME: Lousy diagnostics on this case.
16-
protocol SR9224_Foo: SR9224_Foobar {} // expected-error 2 {{protocol 'SR9224_Foo' refines itself}}
17-
protocol SR9224_Bar: SR9224_Foobar {} // expected-error {{protocol 'SR9224_Bar' refines itself}} expected-note {{protocol 'SR9224_Bar' declared here}}
16+
protocol SR9224_Foo: SR9224_Foobar {} // expected-error 3 {{protocol 'SR9224_Foo' refines itself}}
17+
protocol SR9224_Bar: SR9224_Foobar {} // expected-error {{protocol 'SR9224_Bar' refines itself}} expected-note 2 {{protocol 'SR9224_Bar' declared here}}
1818
typealias SR9224_Foobar = SR9224_Foo & SR9224_Bar

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

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

0 commit comments

Comments
 (0)