Skip to content

Commit 9f19597

Browse files
committed
Sema: implement existentialTypeSupported using a request evaluator.
Add the request `ExistentialTypeSupportedRequest` to lazily determine if we are allowed to refer to an existential type conforming to a protocol.
1 parent d71790b commit 9f19597

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
@@ -4135,7 +4135,20 @@ class ProtocolDecl final : public NominalTypeDecl {
41354135
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
41364136
}
41374137

4138-
bool existentialTypeSupportedSlow();
4138+
/// Returns the cached result of \c existentialTypeSupported or \c None if it
4139+
/// hasn't yet been computed.
4140+
Optional<bool> getCachedExistentialTypeSupported() {
4141+
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
4142+
return Bits.ProtocolDecl.ExistentialTypeSupported;
4143+
4144+
return None;
4145+
}
4146+
4147+
/// Caches the result of \c existentialTypeSupported
4148+
void setCachedExistentialTypeSupported(bool supported) {
4149+
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4150+
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
4151+
}
41394152

41404153
ArrayRef<ProtocolDecl *> getInheritedProtocolsSlow();
41414154

@@ -4148,6 +4161,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41484161
friend class RequirementSignatureRequest;
41494162
friend class ProtocolRequiresClassRequest;
41504163
friend class ExistentialConformsToSelfRequest;
4164+
friend class ExistentialTypeSupportedRequest;
41514165
friend class TypeChecker;
41524166

41534167
public:
@@ -4245,21 +4259,7 @@ class ProtocolDecl final : public NominalTypeDecl {
42454259
/// conforming to this protocol. This is only permitted if the types of
42464260
/// all the members do not contain any associated types, and do not
42474261
/// contain 'Self' in 'parameter' or 'other' position.
4248-
bool existentialTypeSupported() const {
4249-
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
4250-
return Bits.ProtocolDecl.ExistentialTypeSupported;
4251-
4252-
return const_cast<ProtocolDecl *>(this)
4253-
->existentialTypeSupportedSlow();
4254-
}
4255-
4256-
/// Explicitly set the existentialTypeSupported flag, without computing
4257-
/// it from members. Only called from deserialization, where the flag
4258-
/// was stored in the serialized record.
4259-
void setExistentialTypeSupported(bool supported) {
4260-
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
4261-
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4262-
}
4262+
bool existentialTypeSupported() const;
42634263

42644264
private:
42654265
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
@@ -4517,47 +4517,9 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
45174517
return true;
45184518
}
45194519

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

45634525
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
@@ -3325,7 +3325,8 @@ class swift::DeclDeserializer {
33253325

33263326
ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto},
33273327
std::move(isClassBounded));
3328-
proto->setExistentialTypeSupported(existentialTypeSupported);
3328+
ctx.evaluator.cacheOutput(ExistentialTypeSupportedRequest{proto},
3329+
std::move(existentialTypeSupported));
33293330

33303331
if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) {
33313332
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)