Skip to content

[Runtime] Adjust "conforming type" based on a given conformance descriptor #20564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions stdlib/public/runtime/AnyHashableSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct HashableConformanceEntry {
// FIXME(performance): consider merging this cache into the regular
// protocol conformance cache.
static ConcurrentMap<HashableConformanceEntry, /*Destructor*/ false>
HashableConformances;
HashableConformances;

template<bool KnownToConformToHashable>
LLVM_ATTRIBUTE_ALWAYS_INLINE
Expand All @@ -80,16 +80,18 @@ static const Metadata *findHashableBaseTypeImpl(const Metadata *type) {
HashableConformances.find(HashableConformanceKey{type})) {
return entry->baseTypeThatConformsToHashable;
}
if (!KnownToConformToHashable &&
!swift_conformsToProtocol(type, &HashableProtocolDescriptor)) {

auto witnessTable =
swift_conformsToProtocol(type, &HashableProtocolDescriptor);
if (!KnownToConformToHashable && !witnessTable) {
// Don't cache the negative response because we don't invalidate
// this cache when a new conformance is loaded dynamically.
return nullptr;
}
// By this point, `type` is known to conform to `Hashable`.

const auto *conformance = witnessTable->Description;
const Metadata *baseTypeThatConformsToHashable =
findConformingSuperclass(type, &HashableProtocolDescriptor);
findConformingSuperclass(type, conformance);
HashableConformances.getOrInsert(HashableConformanceKey{type},
baseTypeThatConformsToHashable);
return baseTypeThatConformsToHashable;
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4124,7 +4124,7 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
// For a class, chase the superclass chain up until we hit the
// type that specified the conformance.
auto originalConformingType = findConformingSuperclass(conformingType,
protocol);
conformance);
SubstGenericParametersFromMetadata substitutions(originalConformingType);
assocTypeMetadata = _getTypeByMangledName(mangledName, substitutions);
}
Expand Down
10 changes: 5 additions & 5 deletions stdlib/public/runtime/Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,11 @@ class TypeInfo {
ProtocolDescriptorRef protocol,
const WitnessTable **conformance);

/// Given a type that we know conforms to the given protocol, find the
/// superclass that introduced the conformance.
const Metadata *findConformingSuperclass(const Metadata *type,
const ProtocolDescriptor *protocol);

/// Given a type that we know can be used with the given conformance, find
/// the superclass that introduced the conformance.
const Metadata *findConformingSuperclass(
const Metadata *type,
const ProtocolConformanceDescriptor *conformance);
} // end namespace swift

#endif /* SWIFT_RUNTIME_PRIVATE_H */
147 changes: 77 additions & 70 deletions stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,42 +456,74 @@ searchInConformanceCache(const Metadata *type,
return ConformanceCacheResult::cacheMiss();
}

/// Checks if a given candidate is a type itself, one of its
/// superclasses or a related generic type.
///
/// This check is supposed to use the same logic that is used
/// by searchInConformanceCache.
///
/// \param candidate Pointer to a Metadata or a NominalTypeDescriptor.
///
static
bool isRelatedType(const Metadata *type, const void *candidate,
bool candidateIsMetadata) {
namespace {
/// Describes a protocol conformance "candidate" that can be checked
/// against the
class ConformanceCandidate {
const void *candidate;
bool candidateIsMetadata;

while (true) {
// Check whether the types match.
if (candidateIsMetadata && type == candidate)
return true;
public:
ConformanceCandidate() : candidate(0), candidateIsMetadata(false) { }

ConformanceCandidate(const ProtocolConformanceDescriptor &conformance)
: ConformanceCandidate()
{
if (auto metadata = conformance.getCanonicalTypeMetadata()) {
candidate = metadata;
candidateIsMetadata = true;
return;
}

// Check whether the nominal type descriptors match.
if (!candidateIsMetadata) {
const auto *description = type->getTypeContextDescriptor();
auto candidateDescription =
static_cast<const TypeContextDescriptor *>(candidate);
if (description && equalContexts(description, candidateDescription))
return true;
if (auto description = conformance.getTypeContextDescriptor()) {
candidate = description;
candidateIsMetadata = false;
return;
}
}

// If there is a superclass, look there.
if (auto superclass = _swift_class_getSuperclass(type)) {
type = superclass;
continue;
/// Retrieve the conforming type as metadata, or NULL if the candidate's
/// conforming type is described in another way (e.g., a nominal type
/// descriptor).
const Metadata *getConformingTypeAsMetadata() const {
return candidateIsMetadata ? static_cast<const Metadata *>(candidate)
: nullptr;
}

break;
}
/// Whether the conforming type exactly matches the conformance candidate.
bool matches(const Metadata *conformingType) const {
// Check whether the types match.
if (candidateIsMetadata && conformingType == candidate)
return true;

return false;
// Check whether the nominal type descriptors match.
if (!candidateIsMetadata) {
const auto *description = conformingType->getTypeContextDescriptor();
auto candidateDescription =
static_cast<const TypeContextDescriptor *>(candidate);
if (description && equalContexts(description, candidateDescription))
return true;
}

return false;
}

/// Retrieve the type that matches the conformance candidate, which may
/// be a superclass of the given type. Returns null if this type does not
/// match this conformance.
const Metadata *getMatchingType(const Metadata *conformingType) const {
while (conformingType) {
// Check for a match.
if (matches(conformingType))
return conformingType;

// Look for a superclass.
conformingType = _swift_class_getSuperclass(conformingType);
}

return nullptr;
}
};
}

static const WitnessTable *
Expand Down Expand Up @@ -541,38 +573,18 @@ swift_conformsToProtocolImpl(const Metadata * const type,
for (const auto &record : section) {
auto &descriptor = *record.get();

// If the record applies to a specific type, cache it.
if (auto metadata = descriptor.getCanonicalTypeMetadata()) {
auto P = descriptor.getProtocol();

// Look for an exact match.
if (protocol != P)
continue;

if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true))
continue;

// Record the witness table.
recordWitnessTable(descriptor, metadata);

// TODO: "Nondependent witness table" probably deserves its own flag.
// An accessor function might still be necessary even if the witness table
// can be shared.
} else if (descriptor.getTypeKind()
== TypeReferenceKind::DirectNominalTypeDescriptor ||
descriptor.getTypeKind()
== TypeReferenceKind::IndirectNominalTypeDescriptor) {
auto R = descriptor.getTypeContextDescriptor();
auto P = descriptor.getProtocol();

// Look for an exact match.
if (protocol != P)
continue;
// We only care about conformances for this protocol.
if (descriptor.getProtocol() != protocol)
continue;

if (!isRelatedType(type, R, /*candidateIsMetadata=*/false))
continue;
// If there's a matching type, record the positive result.
ConformanceCandidate candidate(descriptor);
if (candidate.getMatchingType(type)) {
const Metadata *matchingType = candidate.getConformingTypeAsMetadata();
if (!matchingType)
matchingType = type;

recordWitnessTable(descriptor, type);
recordWitnessTable(descriptor, matchingType);
}
}
}
Expand Down Expand Up @@ -692,18 +704,13 @@ bool swift::_checkGenericRequirements(
}

const Metadata *swift::findConformingSuperclass(
const Metadata *type,
const ProtocolDescriptor *protocol) {
const Metadata *conformingType = type;
while (true) {
const Metadata *superclass = _swift_class_getSuperclass(conformingType);
if (!superclass)
break;
if (!swift_conformsToProtocol(superclass, protocol))
break;
conformingType = superclass;
}
const Metadata *type,
const ProtocolConformanceDescriptor *conformance) {
// Figure out which type we're looking for.
ConformanceCandidate candidate(*conformance);

const Metadata *conformingType = candidate.getMatchingType(type);
assert(conformingType);
return conformingType;
}

Expand Down