mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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?");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user