[AST] Compute conditional requirements in a conformance.

This allows determining which requirements make a conformance conditional; as
in, which requirements aren't known as part of the type itself.

Additionally, use this to assert that a few builtin protocols aren't
conditionally-conformed-to, something we won't support for now.
This commit is contained in:
Huon Wilson
2017-09-14 13:54:23 -07:00
parent fd07426743
commit 945f723d59
9 changed files with 203 additions and 4 deletions

View File

@@ -285,6 +285,63 @@ AbstractStorageDecl *ProtocolConformance::getBehaviorDecl() const {
return getRootNormalConformance()->getBehaviorDecl();
}
ArrayRef<Requirement> ProtocolConformance::getConditionalRequirements() const {
CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirements, ());
}
ArrayRef<Requirement>
ProtocolConformanceRef::getConditionalRequirements() const {
if (isConcrete())
return getConcrete()->getConditionalRequirements();
else
// An abstract conformance is never conditional: any conditionality in the
// concrete types that will eventually pass through this at runtime is
// completely pre-checked and packaged up.
return {};
}
void NormalProtocolConformance::differenceAndStoreConditionalRequirements() {
assert(ConditionalRequirements.size() == 0 &&
"should not recompute conditional requirements");
auto &ctxt = getProtocol()->getASTContext();
auto DC = getDeclContext();
// Only conformances in extensions can be conditional
if (!isa<ExtensionDecl>(DC))
return;
auto typeSig = DC->getAsNominalTypeOrNominalTypeExtensionContext()
->getGenericSignature();
auto extensionSig = DC->getGenericSignatureOfContext();
// If the type is generic, the extension should be too, and vice versa.
assert((bool)typeSig == (bool)extensionSig &&
"unexpected generic-ness mismatch on conformance");
if (!typeSig)
return;
auto canExtensionSig = extensionSig->getCanonicalSignature();
auto canTypeSig = typeSig->getCanonicalSignature();
if (canTypeSig == canExtensionSig)
return;
// The extension signature should be a superset of the type signature, meaning
// every thing in the type signature either is included too or is implied by
// something else. The most important bit is having the same type
// parameters. (NB. if/when Swift gets parameterized extensions, this needs to
// change.)
assert(canTypeSig.getGenericParams() == canExtensionSig.getGenericParams());
auto mod = DC->getParentModule();
// Find the requirements in the extension that aren't proved by the original
// type, these are the ones that make the conformance conditional.
SmallVector<Requirement, 4> reqs;
for (auto requirement : canExtensionSig->getRequirements()) {
if (!canTypeSig->isRequirementSatisfied(requirement))
reqs.push_back(requirement);
}
ConditionalRequirements = ctxt.AllocateCopy(reqs);
}
void NormalProtocolConformance::setSignatureConformances(
ArrayRef<ProtocolConformanceRef> conformances) {
auto &ctx = getProtocol()->getASTContext();
@@ -635,11 +692,24 @@ SpecializedProtocolConformance::SpecializedProtocolConformance(
GenericSubstitutions(substitutions)
{
assert(genericConformance->getKind() != ProtocolConformanceKind::Specialized);
// Substitute the conditional requirements so that they're phrased in terms of
// the specialized types, not the conformance-declaring decl's types.
auto subMap = getSubstitutionMap();
SmallVector<Requirement, 4> newReqs;
for (auto oldReq : GenericConformance->getConditionalRequirements()) {
newReqs.push_back(*oldReq.subst(subMap));
}
auto &ctxt = getProtocol()->getASTContext();
ConditionalRequirements = ctxt.AllocateCopy(newReqs);
}
SubstitutionMap SpecializedProtocolConformance::getSubstitutionMap() const {
auto *genericSig = GenericConformance->getGenericSignature();
return genericSig->getSubstitutionMap(GenericSubstitutions);
if (genericSig)
return genericSig->getSubstitutionMap(GenericSubstitutions);
return SubstitutionMap();
}
bool SpecializedProtocolConformance::hasTypeWitness(