[AST] Teach NormalProtocolConformance to resolve known, synthesized witnesses.

There are a number of type witnesses that are introduced by the Clang
importer. Immediately resolve those witnesses *without* going through
the type checker, because there are cases (i.e., deserialized SIL)
where the conformance is created but there is no type checker around.

Fixes rdar://problem/30364905, rdar://problem/31053701,
rdar://problem/31565413 / SR-4565.
This commit is contained in:
Doug Gregor
2017-04-13 14:25:02 -07:00
parent 25290bd537
commit c8b435757d
3 changed files with 121 additions and 2 deletions

View File

@@ -398,6 +398,91 @@ bool NormalProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
return false;
}
/// Directly resolve type witnesses that are known to the compiler because they
/// were synthesized by the compiler.
///
/// FIXME: This is a hack to work around the fact that we don't have a
/// TypeChecker when we need one.
///
/// \returns true if we resolved the type witness.
static bool resolveKnownTypeWitness(NormalProtocolConformance *conformance,
AssociatedTypeDecl *assocType) {
auto nominal = conformance->getType()->getAnyNominal();
if (!nominal) return false;
if (!nominal->hasClangNode()) return false;
auto proto = conformance->getProtocol();
auto knownKind = proto->getKnownProtocolKind();
if (!knownKind) return false;
auto &ctx = nominal->getASTContext();
// Local function to handle resolution via lookup directly into the nominal
// type.
auto resolveViaLookup = [&] {
for (auto member : nominal->lookupDirect(assocType->getFullName())) {
auto memberType = dyn_cast<TypeDecl>(member);
if (!memberType) continue;
if (memberType->getDeclContext() != nominal) continue;
conformance->setTypeWitness(assocType,
nominal->mapTypeIntoContext(
memberType->getDeclaredInterfaceType()),
memberType);
return true;
}
return false;
};
// RawRepresentable.RawValue.
if (*knownKind == KnownProtocolKind::RawRepresentable) {
assert(assocType->getName() == ctx.Id_RawValue);
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
// First, try to resolve via lookup, so we get the declaration.
if (resolveViaLookup()) return true;
// Otherwise, use the raw type.
if (enumDecl->hasRawType()) {
conformance->setTypeWitness(assocType, enumDecl->getRawType(), nullptr);
return true;
}
return false;
}
// All other cases resolve via lookup.
return resolveViaLookup();
}
// OptionSet.Element.
if (*knownKind == KnownProtocolKind::OptionSet) {
assert(assocType->getName() == ctx.Id_Element);
return resolveViaLookup();
}
// _ObjectiveCBridgeable._ObjectiveCType
if (*knownKind == KnownProtocolKind::ObjectiveCBridgeable) {
assert(assocType->getName() == ctx.Id_ObjectiveCType);
return resolveViaLookup();
}
// _BridgedStoredNSError.Code
if (*knownKind == KnownProtocolKind::BridgedStoredNSError) {
assert(assocType->getName() == ctx.Id_Code);
return resolveViaLookup();
}
// ErrorCodeProtocol._ErrorType.
if (*knownKind == KnownProtocolKind::ErrorCodeProtocol) {
assert(assocType->getName() == ctx.Id_ErrorType);
return resolveViaLookup();
}
return false;
}
std::pair<Type, TypeDecl *>
NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const {
@@ -407,8 +492,11 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
auto known = TypeWitnesses.find(assocType);
if (known == TypeWitnesses.end()) {
PrettyStackTraceRequirement trace("resolving", this, assocType);
assert(resolver && "Unable to resolve type witness");
resolver->resolveTypeWitness(this, assocType);
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
assocType)) {
assert(resolver && "Unable to resolve type witness");
resolver->resolveTypeWitness(this, assocType);
}
known = TypeWitnesses.find(assocType);
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
}