Skip to content

Commit fe7915d

Browse files
committed
Rework a number of SIL and IRGen witness-table abstractions
to correctly handle generalized protocol requirements. The major missing pieces here are that the conformance search algorithms in both the AST (type substitution) and IRGen (witness table reference emission) need to be rewritten to back-track requirement sources, and the AST needs to actually represent this stuff in NormalProtocolConformances instead of just doing ???. The new generality isn't tested yet; I'm looking into that, but I wanted to get the abstractions in place first.
1 parent 6707eb0 commit fe7915d

22 files changed

+666
-346
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)