Skip to content

Commit 4871212

Browse files
authored
Merge pull request swiftlang#39491 from slavapestov/structural-requirements
AST: Add request to compute protocol structural requirements and dependencies
2 parents 7812de8 + 1d427ea commit 4871212

File tree

6 files changed

+343
-0
lines changed

6 files changed

+343
-0
lines changed

include/swift/AST/Decl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4151,6 +4151,8 @@ class ProtocolDecl final : public NominalTypeDecl {
41514151

41524152
friend class SuperclassDeclRequest;
41534153
friend class SuperclassTypeRequest;
4154+
friend class StructuralRequirementsRequest;
4155+
friend class ProtocolDependenciesRequest;
41544156
friend class RequirementSignatureRequest;
41554157
friend class ProtocolRequiresClassRequest;
41564158
friend class ExistentialConformsToSelfRequest;
@@ -4338,6 +4340,17 @@ class ProtocolDecl final : public NominalTypeDecl {
43384340
/// with the Objective-C runtime.
43394341
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;
43404342

4343+
/// Retrieve the original requirements written in source, as structural types.
4344+
///
4345+
/// The requirement machine builds the requirement signature from structural
4346+
/// requirements. Almost everywhere else should use getRequirementSignature()
4347+
/// instead.
4348+
ArrayRef<StructuralRequirement> getStructuralRequirements() const;
4349+
4350+
/// Get the list of protocols appearing on the right hand side of conformance
4351+
/// requirements. Computed from the structural requirements, above.
4352+
ArrayRef<ProtocolDecl *> getProtocolDependencies() const;
4353+
43414354
/// Retrieve the requirements that describe this protocol.
43424355
///
43434356
/// These are the requirements including any inherited protocols

include/swift/AST/Requirement.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,25 @@ inline void simple_display(llvm::raw_ostream &out, const Requirement &req) {
105105
req.print(out, PrintOptions());
106106
}
107107

108+
/// A requirement as written in source, together with a source location. See
109+
/// ProtocolDecl::getStructuralRequirements().
110+
struct StructuralRequirement {
111+
/// The actual requirement, where the types were resolved with the
112+
/// 'Structural' type resolution stage.
113+
Requirement req;
114+
115+
/// The source location where the requirement is written, used for redundancy
116+
/// and conflict diagnostics.
117+
SourceLoc loc;
118+
119+
/// A flag indicating whether the requirement was inferred from the
120+
/// application of a type constructor. Also used for diagnostics, because
121+
/// an inferred requirement made redundant by an explicit requirement is not
122+
/// diagnosed as redundant, since we want to give users the option of
123+
/// spelling out these requirements explicitly.
124+
bool inferred = false;
125+
};
126+
108127
} // end namespace swift
109128

110129
#endif

include/swift/AST/TypeCheckRequests.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,44 @@ class IsDynamicRequest :
368368
void cacheResult(bool value) const;
369369
};
370370

371+
class StructuralRequirementsRequest :
372+
public SimpleRequest<StructuralRequirementsRequest,
373+
ArrayRef<StructuralRequirement>(ProtocolDecl *),
374+
RequestFlags::Cached> {
375+
public:
376+
using SimpleRequest::SimpleRequest;
377+
378+
private:
379+
friend SimpleRequest;
380+
381+
// Evaluation.
382+
ArrayRef<StructuralRequirement>
383+
evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
384+
385+
public:
386+
// Caching.
387+
bool isCached() const { return true; }
388+
};
389+
390+
class ProtocolDependenciesRequest :
391+
public SimpleRequest<ProtocolDependenciesRequest,
392+
ArrayRef<ProtocolDecl *>(ProtocolDecl *),
393+
RequestFlags::Cached> {
394+
public:
395+
using SimpleRequest::SimpleRequest;
396+
397+
private:
398+
friend SimpleRequest;
399+
400+
// Evaluation.
401+
ArrayRef<ProtocolDecl *>
402+
evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
403+
404+
public:
405+
// Caching.
406+
bool isCached() const { return true; }
407+
};
408+
371409
/// Compute the requirements that describe a protocol.
372410
class RequirementSignatureRequest :
373411
public SimpleRequest<RequirementSignatureRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,12 @@ SWIFT_REQUEST(TypeChecker, ProtocolRequiresClassRequest, bool(ProtocolDecl *),
217217
SWIFT_REQUEST(TypeChecker, RequirementRequest,
218218
Requirement(WhereClauseOwner, unsigned, TypeResolutionStage),
219219
Cached, HasNearestLocation)
220+
SWIFT_REQUEST(TypeChecker, StructuralRequirementsRequest,
221+
ArrayRef<StructuralRequirement>(ProtocolDecl *), Cached,
222+
HasNearestLocation)
223+
SWIFT_REQUEST(TypeChecker, ProtocolDependenciesRequest,
224+
ArrayRef<ProtocolDecl *>(ProtocolDecl *), Cached,
225+
HasNearestLocation)
220226
SWIFT_REQUEST(TypeChecker, RequirementSignatureRequest,
221227
ArrayRef<Requirement>(ProtocolDecl *), SeparatelyCached,
222228
NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5202,6 +5202,20 @@ StringRef ProtocolDecl::getObjCRuntimeName(
52025202
return mangleObjCRuntimeName(this, buffer);
52035203
}
52045204

5205+
ArrayRef<StructuralRequirement>
5206+
ProtocolDecl::getStructuralRequirements() const {
5207+
return evaluateOrDefault(getASTContext().evaluator,
5208+
StructuralRequirementsRequest { const_cast<ProtocolDecl *>(this) },
5209+
None);
5210+
}
5211+
5212+
ArrayRef<ProtocolDecl *>
5213+
ProtocolDecl::getProtocolDependencies() const {
5214+
return evaluateOrDefault(getASTContext().evaluator,
5215+
ProtocolDependenciesRequest { const_cast<ProtocolDecl *>(this) },
5216+
None);
5217+
}
5218+
52055219
ArrayRef<Requirement> ProtocolDecl::getRequirementSignature() const {
52065220
return evaluateOrDefault(getASTContext().evaluator,
52075221
RequirementSignatureRequest { const_cast<ProtocolDecl *>(this) },

lib/Sema/TypeCheckDecl.cpp

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/AST/PropertyWrappers.h"
4141
#include "swift/AST/ProtocolConformance.h"
4242
#include "swift/AST/SourceFile.h"
43+
#include "swift/AST/TypeMatcher.h"
4344
#include "swift/AST/TypeWalker.h"
4445
#include "swift/Basic/Statistic.h"
4546
#include "swift/Parse/Lexer.h"
@@ -869,6 +870,258 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
869870
return false;
870871
}
871872

873+
static void addTypeRequirement(Type subjectType, Type constraintType,
874+
SourceLoc loc, bool wasInferred,
875+
SmallVectorImpl<StructuralRequirement> &result) {
876+
// Check whether we have a reasonable constraint type at all.
877+
if (!constraintType->isExistentialType() &&
878+
!constraintType->getClassOrBoundGenericClass()) {
879+
// FIXME: Diagnose
880+
return;
881+
}
882+
883+
// Protocol requirements.
884+
if (constraintType->isExistentialType()) {
885+
auto layout = constraintType->getExistentialLayout();
886+
887+
if (auto layoutConstraint = layout.getLayoutConstraint()) {
888+
result.push_back({
889+
Requirement(RequirementKind::Layout, subjectType, layoutConstraint),
890+
loc, wasInferred});
891+
}
892+
893+
if (auto superclass = layout.explicitSuperclass) {
894+
result.push_back({
895+
Requirement(RequirementKind::Superclass, subjectType, superclass),
896+
loc, wasInferred});
897+
}
898+
899+
for (auto *proto : layout.getProtocols()) {
900+
result.push_back({
901+
Requirement(RequirementKind::Conformance, subjectType, proto),
902+
loc, wasInferred});
903+
}
904+
905+
return;
906+
}
907+
908+
// Superclass constraint.
909+
result.push_back({
910+
Requirement(RequirementKind::Superclass, subjectType, constraintType),
911+
loc, wasInferred});
912+
}
913+
914+
static void addSameTypeRequirement(Type lhs, Type rhs,
915+
SourceLoc loc, bool wasInferred,
916+
SmallVectorImpl<StructuralRequirement> &result) {
917+
class Matcher : public TypeMatcher<Matcher> {
918+
SourceLoc loc;
919+
bool wasInferred;
920+
SmallVectorImpl<StructuralRequirement> &result;
921+
922+
public:
923+
Matcher(SourceLoc loc, bool wasInferred,
924+
SmallVectorImpl<StructuralRequirement> &result)
925+
: loc(loc), wasInferred(wasInferred), result(result) {}
926+
927+
bool mismatch(TypeBase *firstType, TypeBase *secondType,
928+
Type sugaredFirstType) {
929+
if (firstType->isTypeParameter() && secondType->isTypeParameter()) {
930+
result.push_back({Requirement(RequirementKind::SameType,
931+
firstType, secondType),
932+
loc, wasInferred});
933+
return true;
934+
}
935+
936+
if (firstType->isTypeParameter()) {
937+
result.push_back({Requirement(RequirementKind::SameType,
938+
firstType, secondType),
939+
loc, wasInferred});
940+
return true;
941+
}
942+
943+
if (secondType->isTypeParameter()) {
944+
result.push_back({Requirement(RequirementKind::SameType,
945+
secondType, firstType),
946+
loc, wasInferred});
947+
return true;
948+
}
949+
950+
// FIXME: Diagnose concrete type conflict
951+
return true;
952+
}
953+
} matcher(loc, wasInferred, result);
954+
955+
// FIXME: If both sides concrete and was not inferred, diagnose redundancy
956+
// FIXME: If both sides are equal as type parameters and not inferred,
957+
// diagnose redundancy
958+
(void) matcher.match(lhs, rhs);
959+
}
960+
961+
static void inferRequirements(Type type, SourceLoc loc,
962+
SmallVectorImpl<StructuralRequirement> &result) {
963+
964+
}
965+
966+
static void addRequirement(Requirement req, RequirementRepr *reqRepr, bool infer,
967+
SmallVectorImpl<StructuralRequirement> &result) {
968+
auto firstType = req.getFirstType();
969+
if (infer) {
970+
auto firstLoc = (reqRepr ? reqRepr->getFirstTypeRepr()->getStartLoc()
971+
: SourceLoc());
972+
inferRequirements(firstType, firstLoc, result);
973+
}
974+
975+
auto loc = (reqRepr ? reqRepr->getSeparatorLoc() : SourceLoc());
976+
977+
switch (req.getKind()) {
978+
case RequirementKind::Superclass:
979+
case RequirementKind::Conformance: {
980+
if (!firstType->isTypeParameter()) {
981+
// FIXME: Warn about redundancy if not inferred, diagnose conflicts
982+
break;
983+
}
984+
985+
auto secondType = req.getSecondType();
986+
if (infer) {
987+
auto secondLoc = (reqRepr ? reqRepr->getSecondTypeRepr()->getStartLoc()
988+
: SourceLoc());
989+
inferRequirements(secondType, secondLoc, result);
990+
}
991+
992+
addTypeRequirement(firstType, secondType, loc, /*wasInferred=*/false,
993+
result);
994+
break;
995+
}
996+
997+
case RequirementKind::Layout:
998+
if (!firstType->isTypeParameter()) {
999+
// FIXME: Warn about redundancy if not inferred, diagnose conflicts
1000+
break;
1001+
}
1002+
1003+
result.push_back({req, loc, /*wasInferred=*/false});
1004+
break;
1005+
1006+
case RequirementKind::SameType: {
1007+
auto secondType = req.getSecondType();
1008+
if (infer) {
1009+
auto secondLoc = (reqRepr ? reqRepr->getSecondTypeRepr()->getStartLoc()
1010+
: SourceLoc());
1011+
inferRequirements(secondType, secondLoc, result);
1012+
}
1013+
1014+
addSameTypeRequirement(firstType, secondType, loc, /*wasInferred=*/false,
1015+
result);
1016+
break;
1017+
}
1018+
}
1019+
}
1020+
1021+
static void addInheritedRequirements(TypeDecl *decl, Type type, bool infer,
1022+
SmallVectorImpl<StructuralRequirement> &result) {
1023+
auto &ctx = decl->getASTContext();
1024+
auto inheritedTypes = decl->getInherited();
1025+
1026+
for (unsigned index : indices(inheritedTypes)) {
1027+
Type inheritedType
1028+
= evaluateOrDefault(ctx.evaluator,
1029+
InheritedTypeRequest{decl, index,
1030+
TypeResolutionStage::Structural},
1031+
Type());
1032+
if (!inheritedType) continue;
1033+
1034+
auto *typeRepr = inheritedTypes[index].getTypeRepr();
1035+
SourceLoc loc = (typeRepr ? typeRepr->getStartLoc() : SourceLoc());
1036+
if (infer) {
1037+
inferRequirements(inheritedType, loc, result);
1038+
}
1039+
1040+
addTypeRequirement(type, inheritedType, loc, /*wasInferred=*/false,
1041+
result);
1042+
}
1043+
}
1044+
1045+
ArrayRef<StructuralRequirement>
1046+
StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
1047+
ProtocolDecl *proto) const {
1048+
assert(!proto->hasLazyRequirementSignature());
1049+
1050+
SmallVector<StructuralRequirement, 4> result;
1051+
1052+
auto &ctx = proto->getASTContext();
1053+
1054+
auto selfTy = proto->getSelfInterfaceType();
1055+
1056+
addInheritedRequirements(proto, selfTy,
1057+
/*infer=*/false, result);
1058+
1059+
// Add requirements from the protocol's own 'where' clause.
1060+
WhereClauseOwner(proto).visitRequirements(TypeResolutionStage::Structural,
1061+
[&](const Requirement &req, RequirementRepr *reqRepr) {
1062+
addRequirement(req, reqRepr, /*infer=*/false, result);
1063+
return false;
1064+
});
1065+
1066+
if (proto->isObjC()) {
1067+
// @objc protocols have an implicit AnyObject requirement on Self.
1068+
auto layout = LayoutConstraint::getLayoutConstraint(
1069+
LayoutConstraintKind::Class, ctx);
1070+
result.push_back({Requirement(RequirementKind::Layout, selfTy, layout),
1071+
proto->getLoc(), /*inferred=*/true});
1072+
1073+
// Remaining logic is not relevant to @objc protocols.
1074+
return ctx.AllocateCopy(result);
1075+
}
1076+
1077+
// Add requirements for each of the associated types.
1078+
for (auto assocTypeDecl : proto->getAssociatedTypeMembers()) {
1079+
// Add requirements placed directly on this associated type.
1080+
auto assocType = assocTypeDecl->getDeclaredInterfaceType();
1081+
addInheritedRequirements(assocTypeDecl, assocType, /*infer=*/false,
1082+
result);
1083+
1084+
// Add requirements from this associated type's where clause.
1085+
WhereClauseOwner(assocTypeDecl).visitRequirements(
1086+
TypeResolutionStage::Structural,
1087+
[&](const Requirement &req, RequirementRepr *reqRepr) {
1088+
addRequirement(req, reqRepr, /*infer=*/false, result);
1089+
return false;
1090+
});
1091+
}
1092+
1093+
return ctx.AllocateCopy(result);
1094+
}
1095+
1096+
ArrayRef<ProtocolDecl *>
1097+
ProtocolDependenciesRequest::evaluate(Evaluator &evaluator,
1098+
ProtocolDecl *proto) const {
1099+
auto &ctx = proto->getASTContext();
1100+
SmallVector<ProtocolDecl *, 4> result;
1101+
1102+
// If we have a serialized requirement signature, deserialize it and
1103+
// look at conformance requirements.
1104+
if (proto->hasLazyRequirementSignature()) {
1105+
for (auto req : proto->getRequirementSignature()) {
1106+
if (req.getKind() == RequirementKind::Conformance) {
1107+
result.push_back(req.getProtocolDecl());
1108+
}
1109+
}
1110+
1111+
return ctx.AllocateCopy(result);
1112+
}
1113+
1114+
// Otherwise, we can't ask for the requirement signature, because
1115+
// this request is used as part of *building* the requirement
1116+
// signature. Look at the structural requirements instead.
1117+
for (auto req : proto->getStructuralRequirements()) {
1118+
if (req.req.getKind() == RequirementKind::Conformance)
1119+
result.push_back(req.req.getProtocolDecl());
1120+
}
1121+
1122+
return ctx.AllocateCopy(result);
1123+
}
1124+
8721125
ArrayRef<Requirement>
8731126
RequirementSignatureRequest::evaluate(Evaluator &evaluator,
8741127
ProtocolDecl *proto) const {

0 commit comments

Comments
 (0)