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.
This commit is contained in:
John McCall
2017-03-01 18:18:23 -05:00
parent 6707eb00f2
commit fe7915d09e
22 changed files with 668 additions and 348 deletions

View File

@@ -336,6 +336,103 @@ void NormalProtocolConformance::setTypeWitness(
TypeWitnesses[assocType] = std::make_pair(substitution, typeDecl);
}
/// TypeWitnesses is keyed by the protocol's own declarations, but
/// DependentMemberTypes will sometimes store a base protocol's declaration.
/// Map to the derived declaration if possible.
static AssociatedTypeDecl *getOwnAssociatedTypeDecl(ProtocolDecl *protocol,
AssociatedTypeDecl *assoc) {
// Fast path.
if (assoc->getProtocol() == protocol) return assoc;
// Search the protocol.
for (auto member : protocol->getMembers()) {
if (auto memberAssoc = dyn_cast<AssociatedTypeDecl>(member)) {
if (memberAssoc->getName() == assoc->getName()) {
return memberAssoc;
}
}
}
// Just assume this is fine.
return assoc;
}
Type NormalProtocolConformance::getAssociatedType(Type assocType,
LazyResolver *resolver) const {
assert(assocType->isTypeParameter() &&
"associated type must be a type parameter");
// Fast path.
auto type = assocType->getCanonicalType();
if (isa<GenericTypeParamType>(type)) {
assert(type->isEqual(getProtocol()->getSelfInterfaceType()) &&
"type parameter in protocol was not Self");
return getType();
}
auto memberType = cast<DependentMemberType>(type);
// TODO: make this handle multiple levels of dependent member type.
assert(memberType.getBase()->isEqual(getProtocol()->getSelfInterfaceType()) &&
"dependent member in protocol was not rooted in Self");
auto assocTypeDecl =
getOwnAssociatedTypeDecl(getProtocol(), memberType->getAssocType());
auto &subst = getTypeWitnessSubstAndDecl(assocTypeDecl, resolver).first;
return subst.getReplacement();
}
ProtocolConformanceRef
NormalProtocolConformance::getAssociatedConformance(Type assocType,
ProtocolDecl *protocol,
LazyResolver *resolver) const {
assert(assocType->isTypeParameter() &&
"associated type must be a type parameter");
#ifndef NDEBUG
bool foundInRequirements = false;
for (auto &reqt :
getProtocol()->getRequirementSignature()->getRequirements()) {
if (reqt.getKind() == RequirementKind::Conformance &&
reqt.getFirstType()->isEqual(assocType) &&
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() == protocol) {
foundInRequirements = true;
break;
}
}
assert(foundInRequirements &&
"requested conformance was not a direct requirement of the protocol");
#endif
auto type = assocType->getCanonicalType();
if (isa<GenericTypeParamType>(type)) {
assert(type->isEqual(getProtocol()->getSelfInterfaceType()) &&
"type parameter in protocol was not Self");
auto conf = getInheritedConformance(protocol);
assert(conf && "inherited conformances cannot be abstract");
return ProtocolConformanceRef(conf);
}
auto memberType = cast<DependentMemberType>(type);
// For now, NormalProtocolConformance does not store indirect associations.
assert(memberType.getBase()->isEqual(getProtocol()->getSelfInterfaceType()) &&
"dependent member in protocol was not rooted in Self");
auto assocTypeDecl =
getOwnAssociatedTypeDecl(getProtocol(), memberType->getAssocType());
auto &subst = getTypeWitnessSubstAndDecl(assocTypeDecl, resolver).first;
// Scan the conformances for the exact conformance.
// TODO: should we allow indirect conformances for convenience of use?
for (auto &conf : subst.getConformances()) {
if (conf.getRequirement() == protocol)
return conf;
}
llvm_unreachable("missing conformance to protocol");
}
/// Retrieve the value witness corresponding to the given requirement.
Witness NormalProtocolConformance::getWitness(ValueDecl *requirement,
LazyResolver *resolver) const {