Skip to content

Commit 13111c2

Browse files
authored
Merge pull request #7852 from rjmccall/sil-irgen-generalized-proto-requirements
2 parents 33d7246 + fe7915d commit 13111c2

27 files changed

+704
-361
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,8 @@ ERROR(sil_witness_protocol_not_found,none,
591591
"sil protocol not found %0", (Identifier))
592592
ERROR(sil_witness_assoc_not_found,none,
593593
"sil associated type decl not found %0", (Identifier))
594+
ERROR(sil_witness_assoc_conf_not_found,none,
595+
"sil associated type path for conformance not found %0", (StringRef))
594596
ERROR(sil_witness_protocol_conformance_not_found,none,
595597
"sil protocol conformance not found", ())
596598

include/swift/AST/ProtocolConformance.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,18 @@ class NormalProtocolConformance : public ProtocolConformance,
444444
const Substitution &substitution,
445445
TypeDecl *typeDecl) const;
446446

447+
/// Given a dependent type expressed in terms of the self parameter,
448+
/// map it into the context of this conformance.
449+
Type getAssociatedType(Type assocType,
450+
LazyResolver *resolver = nullptr) const;
451+
452+
/// Given that the requirement signature of the protocol directly states
453+
/// that the given dependent type must conform to the given protocol,
454+
/// return its associated conformance.
455+
ProtocolConformanceRef
456+
getAssociatedConformance(Type assocType, ProtocolDecl *protocol,
457+
LazyResolver *resolver = nullptr) const;
458+
447459
/// Retrieve the value witness corresponding to the given requirement.
448460
///
449461
/// Note that a generic witness will only be specialized if the conformance

include/swift/SIL/SILWitnessTable.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ class SILWitnessTable : public llvm::ilist_node<SILWitnessTable>,
6262
/// A witness table entry describing the witness for an associated type's
6363
/// protocol requirement.
6464
struct AssociatedTypeProtocolWitness {
65-
/// The associated type required.
66-
AssociatedTypeDecl *Requirement;
65+
/// The associated type required. A dependent type in the protocol's
66+
/// context.
67+
CanType Requirement;
6768
/// The protocol requirement on the type.
6869
ProtocolDecl *Protocol;
6970
/// The ProtocolConformance satisfying the requirement. Null if the

include/swift/SIL/SILWitnessVisitor.h

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,77 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
4949

5050
public:
5151
void visitProtocolDecl(ProtocolDecl *protocol) {
52-
// Visit inherited protocols.
53-
// TODO: We need to figure out all the guarantees we want here.
54-
// It would be abstractly good to allow conversion to a base
55-
// protocol to be trivial, but it's not clear that there's
56-
// really a structural guarantee we can rely on here.
57-
for (auto baseProto : protocol->getInheritedProtocols()) {
58-
// ObjC protocols do not have witnesses.
59-
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(baseProto))
52+
// Associated types get added after the inherited conformances, but
53+
// before all the function requirements.
54+
bool haveAddedAssociatedTypes = false;
55+
auto addAssociatedTypes = [&] {
56+
if (haveAddedAssociatedTypes) return;
57+
haveAddedAssociatedTypes = true;
58+
59+
for (Decl *member : protocol->getMembers()) {
60+
if (auto associatedType = dyn_cast<AssociatedTypeDecl>(member)) {
61+
// TODO: only add associated types when they're new?
62+
asDerived().addAssociatedType(associatedType);
63+
}
64+
}
65+
};
66+
67+
for (auto &reqt : protocol->getRequirementSignature()
68+
->getCanonicalSignature()->getRequirements()) {
69+
switch (reqt.getKind()) {
70+
// These requirements don't show up in the witness table.
71+
case RequirementKind::Superclass:
72+
case RequirementKind::SameType:
73+
case RequirementKind::Layout:
6074
continue;
6175

62-
asDerived().addOutOfLineBaseProtocol(baseProto);
76+
case RequirementKind::Conformance: {
77+
auto type = CanType(reqt.getFirstType());
78+
assert(type->isTypeParameter());
79+
auto requirement =
80+
cast<ProtocolType>(CanType(reqt.getSecondType()))->getDecl();
81+
82+
// ObjC protocols do not have witnesses.
83+
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(requirement))
84+
continue;
85+
86+
// If the type parameter is 'self', consider this to be protocol
87+
// inheritance. In the canonical signature, these should all
88+
// come before any protocol requirements on associated types.
89+
if (auto parameter = dyn_cast<GenericTypeParamType>(type)) {
90+
assert(type->isEqual(protocol->getSelfInterfaceType()));
91+
assert(!haveAddedAssociatedTypes &&
92+
"unexpected ordering of conformances");
93+
assert(parameter->getDepth() == 0 && parameter->getIndex() == 0 &&
94+
"non-self type parameter in protocol");
95+
asDerived().addOutOfLineBaseProtocol(requirement);
96+
continue;
97+
}
98+
99+
// Add the associated types if we haven't yet.
100+
addAssociatedTypes();
101+
102+
// Otherwise, add an associated requirement.
103+
asDerived().addAssociatedConformance(type, requirement);
104+
continue;
105+
}
106+
}
107+
llvm_unreachable("bad requirement kind");
63108
}
64109

65-
/// Visit the witnesses for the direct members of a protocol.
110+
// Add the associated types if we haven't yet.
111+
addAssociatedTypes();
112+
113+
// Visit the witnesses for the direct members of a protocol.
66114
for (Decl *member : protocol->getMembers())
67115
ASTVisitor<T>::visit(member);
68116
}
69117

70118
/// Fallback for unexpected protocol requirements.
71119
void visitDecl(Decl *d) {
120+
#ifndef NDEBUG
72121
d->print(llvm::errs());
122+
#endif
73123
llvm_unreachable("unhandled protocol requirement");
74124
}
75125

@@ -94,11 +144,7 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
94144
}
95145

96146
void visitAssociatedTypeDecl(AssociatedTypeDecl *td) {
97-
SmallVector<ProtocolDecl *, 4> protos;
98-
for (auto p : td->getConformingProtocols())
99-
protos.push_back(p);
100-
ProtocolType::canonicalizeProtocols(protos);
101-
asDerived().addAssociatedType(td, protos);
147+
// We already visited these in the first pass.
102148
}
103149

104150
void visitTypeAliasDecl(TypeAliasDecl *tad) {

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 323; // Last change: requirement conformances
57+
const uint16_t VERSION_MINOR = 324; // Last change: SIL associated conformances
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;

lib/AST/ProtocolConformance.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,103 @@ void NormalProtocolConformance::setTypeWitness(
336336
TypeWitnesses[assocType] = std::make_pair(substitution, typeDecl);
337337
}
338338

339+
/// TypeWitnesses is keyed by the protocol's own declarations, but
340+
/// DependentMemberTypes will sometimes store a base protocol's declaration.
341+
/// Map to the derived declaration if possible.
342+
static AssociatedTypeDecl *getOwnAssociatedTypeDecl(ProtocolDecl *protocol,
343+
AssociatedTypeDecl *assoc) {
344+
// Fast path.
345+
if (assoc->getProtocol() == protocol) return assoc;
346+
347+
// Search the protocol.
348+
for (auto member : protocol->getMembers()) {
349+
if (auto memberAssoc = dyn_cast<AssociatedTypeDecl>(member)) {
350+
if (memberAssoc->getName() == assoc->getName()) {
351+
return memberAssoc;
352+
}
353+
}
354+
}
355+
356+
// Just assume this is fine.
357+
return assoc;
358+
}
359+
360+
Type NormalProtocolConformance::getAssociatedType(Type assocType,
361+
LazyResolver *resolver) const {
362+
assert(assocType->isTypeParameter() &&
363+
"associated type must be a type parameter");
364+
365+
// Fast path.
366+
auto type = assocType->getCanonicalType();
367+
if (isa<GenericTypeParamType>(type)) {
368+
assert(type->isEqual(getProtocol()->getSelfInterfaceType()) &&
369+
"type parameter in protocol was not Self");
370+
return getType();
371+
}
372+
373+
auto memberType = cast<DependentMemberType>(type);
374+
375+
// TODO: make this handle multiple levels of dependent member type.
376+
assert(memberType.getBase()->isEqual(getProtocol()->getSelfInterfaceType()) &&
377+
"dependent member in protocol was not rooted in Self");
378+
379+
auto assocTypeDecl =
380+
getOwnAssociatedTypeDecl(getProtocol(), memberType->getAssocType());
381+
auto &subst = getTypeWitnessSubstAndDecl(assocTypeDecl, resolver).first;
382+
return subst.getReplacement();
383+
}
384+
385+
ProtocolConformanceRef
386+
NormalProtocolConformance::getAssociatedConformance(Type assocType,
387+
ProtocolDecl *protocol,
388+
LazyResolver *resolver) const {
389+
assert(assocType->isTypeParameter() &&
390+
"associated type must be a type parameter");
391+
392+
#ifndef NDEBUG
393+
bool foundInRequirements = false;
394+
for (auto &reqt :
395+
getProtocol()->getRequirementSignature()->getRequirements()) {
396+
if (reqt.getKind() == RequirementKind::Conformance &&
397+
reqt.getFirstType()->isEqual(assocType) &&
398+
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() == protocol) {
399+
foundInRequirements = true;
400+
break;
401+
}
402+
}
403+
assert(foundInRequirements &&
404+
"requested conformance was not a direct requirement of the protocol");
405+
#endif
406+
407+
auto type = assocType->getCanonicalType();
408+
409+
if (isa<GenericTypeParamType>(type)) {
410+
assert(type->isEqual(getProtocol()->getSelfInterfaceType()) &&
411+
"type parameter in protocol was not Self");
412+
auto conf = getInheritedConformance(protocol);
413+
assert(conf && "inherited conformances cannot be abstract");
414+
return ProtocolConformanceRef(conf);
415+
}
416+
417+
auto memberType = cast<DependentMemberType>(type);
418+
419+
// For now, NormalProtocolConformance does not store indirect associations.
420+
assert(memberType.getBase()->isEqual(getProtocol()->getSelfInterfaceType()) &&
421+
"dependent member in protocol was not rooted in Self");
422+
423+
auto assocTypeDecl =
424+
getOwnAssociatedTypeDecl(getProtocol(), memberType->getAssocType());
425+
auto &subst = getTypeWitnessSubstAndDecl(assocTypeDecl, resolver).first;
426+
427+
// Scan the conformances for the exact conformance.
428+
// TODO: should we allow indirect conformances for convenience of use?
429+
for (auto &conf : subst.getConformances()) {
430+
if (conf.getRequirement() == protocol)
431+
return conf;
432+
}
433+
llvm_unreachable("missing conformance to protocol");
434+
}
435+
339436
/// Retrieve the value witness corresponding to the given requirement.
340437
Witness NormalProtocolConformance::getWitness(ValueDecl *requirement,
341438
LazyResolver *resolver) const {

0 commit comments

Comments
 (0)