mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
(Note that this registry isn't fully enabled yet; it's built so that
we can test it, but has not yet taken over the primary task of
managing conformances from the existing system).
The conformance registry tracks all of the protocols to which a
particular nominal type conforms, including those for which
conformance was explicitly specified, implied by other explicit
conformances, inherited from a superclass, or synthesized by the
implementation.
The conformance registry is a lazily-built data structure designed for
multi-file support (which has been a problematic area for protocol
conformances). It allows one to query for the conformances of a type
to a particular protocol, enumerate all protocols to which a type
conforms, and enumerate all of the conformances that are associated
with a particular declaration context (important to eliminate
duplicated witness tables).
The conformance registry diagnoses conflicts and ambiguities among
different conformances of the same type to the same protocol. There
are three common cases where we'll see a diagnostic:
1) Redundant explicit conformance of a type to a protocol:
protocol P { }
struct X : P { }
extension X : P { } // error: redundant explicit conformance
2) Explicit conformance to a protocol that collides with an inherited
conformance:
protocol P { }
class Super : P { }
class Sub : Super, P { } // error: redundant explicit conformance
3) Ambiguous placement of an implied conformance:
protocol P1 { }
protocol P2 : P1 { }
protocol P3 : P1 { }
struct Y { }
extension Y : P2 { }
extension Y : P3 { } // error: ambiguous implied conformance to 'P1'
This happens when two different explicit conformances (here, P2 and
P3) placed on different declarations (e.g., two extensions, or the
original definition and other extension) both imply the same
conformance (P1), and neither of the explicit conformances imply
each other. We require the user to explicitly specify the ambiguous
conformance to break the ambiguity and associate the witness table
with a specific context.
Swift SVN r26067
1802 lines
66 KiB
C++
1802 lines
66 KiB
C++
//===--- ProtocolConformance.cpp - AST Protocol Conformance -----*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the protocol conformance data structures.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/Substitution.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/TypeWalker.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
|
|
using namespace swift;
|
|
|
|
void *ProtocolConformance::operator new(size_t bytes, ASTContext &context,
|
|
AllocationArena arena,
|
|
unsigned alignment) {
|
|
return context.Allocate(bytes, alignment, arena);
|
|
|
|
}
|
|
|
|
#define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \
|
|
switch (getKind()) { \
|
|
case ProtocolConformanceKind::Normal: \
|
|
static_assert(&ProtocolConformance::Method != \
|
|
&NormalProtocolConformance::Method, \
|
|
"Must override NormalProtocolConformance::" #Method); \
|
|
return cast<NormalProtocolConformance>(this)->Method Args; \
|
|
case ProtocolConformanceKind::Specialized: \
|
|
static_assert(&ProtocolConformance::Method != \
|
|
&SpecializedProtocolConformance::Method, \
|
|
"Must override SpecializedProtocolConformance::" #Method); \
|
|
return cast<SpecializedProtocolConformance>(this)->Method Args; \
|
|
case ProtocolConformanceKind::Inherited: \
|
|
static_assert(&ProtocolConformance::Method != \
|
|
&InheritedProtocolConformance::Method, \
|
|
"Must override InheritedProtocolConformance::" #Method); \
|
|
return cast<InheritedProtocolConformance>(this)->Method Args; \
|
|
} \
|
|
llvm_unreachable("bad ProtocolConformanceKind");
|
|
|
|
/// Get the protocol being conformed to.
|
|
ProtocolDecl *ProtocolConformance::getProtocol() const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getProtocol, ())
|
|
}
|
|
|
|
DeclContext *ProtocolConformance::getDeclContext() const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getDeclContext, ())
|
|
}
|
|
|
|
/// Retrieve the state of this conformance.
|
|
ProtocolConformanceState ProtocolConformance::getState() const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getState, ())
|
|
}
|
|
|
|
bool
|
|
ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(hasTypeWitness, (assocType, resolver));
|
|
}
|
|
|
|
const Substitution &
|
|
ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitness, (assocType, resolver))
|
|
}
|
|
|
|
Type
|
|
ProtocolConformance::getTypeWitnessByName(Type type,
|
|
ProtocolConformance *conformance,
|
|
Identifier name,
|
|
LazyResolver *resolver) {
|
|
// For an archetype, retrieve the nested type with the appropriate
|
|
// name. There are no conformance tables.
|
|
if (auto archetype = type->getAs<ArchetypeType>()) {
|
|
return archetype->getNestedTypeValue(name);
|
|
}
|
|
|
|
// Find the named requirement.
|
|
AssociatedTypeDecl *assocType = nullptr;
|
|
auto members = conformance->getProtocol()->lookupDirect(name);
|
|
for (auto member : members) {
|
|
assocType = dyn_cast<AssociatedTypeDecl>(member);
|
|
if (assocType)
|
|
break;
|
|
}
|
|
|
|
if (!assocType)
|
|
return nullptr;
|
|
|
|
assert(conformance && "Missing conformance information");
|
|
if (!conformance->hasTypeWitness(assocType, resolver)) {
|
|
return nullptr;
|
|
}
|
|
return conformance->getTypeWitness(assocType, resolver).getReplacement();
|
|
}
|
|
|
|
ConcreteDeclRef ProtocolConformance::getWitness(ValueDecl *requirement,
|
|
LazyResolver *resolver) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getWitness, (requirement, resolver))
|
|
}
|
|
|
|
const InheritedConformanceMap &
|
|
ProtocolConformance::getInheritedConformances() const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getInheritedConformances, ())
|
|
}
|
|
|
|
/// Determine whether the witness for the given requirement
|
|
/// is either the default definition or was otherwise deduced.
|
|
bool ProtocolConformance::usesDefaultDefinition(ValueDecl *requirement) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(usesDefaultDefinition, (requirement))
|
|
}
|
|
|
|
GenericParamList *ProtocolConformance::getGenericParams() const {
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Inherited:
|
|
case ProtocolConformanceKind::Normal:
|
|
// If we have a normal or inherited protocol conformance, look for its
|
|
// generic parameters.
|
|
return getDeclContext()->getGenericParamsOfContext();
|
|
|
|
case ProtocolConformanceKind::Specialized:
|
|
// If we have a specialized protocol conformance, since we do not support
|
|
// currently partial specialization, we know that it can not have any open
|
|
// type variables.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Type ProtocolConformance::getInterfaceType() const {
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Normal:
|
|
case ProtocolConformanceKind::Inherited:
|
|
return getDeclContext()->getDeclaredInterfaceType();
|
|
|
|
case ProtocolConformanceKind::Specialized:
|
|
// Assume a specialized conformance is fully applied.
|
|
return getType();
|
|
}
|
|
llvm_unreachable("bad ProtocolConformanceKind");
|
|
}
|
|
|
|
GenericSignature *ProtocolConformance::getGenericSignature() const {
|
|
return getDeclContext()->getGenericSignatureOfContext();
|
|
}
|
|
|
|
bool NormalProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
if (TypeWitnesses.find(assocType) != TypeWitnesses.end()) {
|
|
return true;
|
|
}
|
|
if (resolver) {
|
|
resolver->resolveTypeWitness(this, assocType);
|
|
if (TypeWitnesses.find(assocType) != TypeWitnesses.end()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const Substitution &NormalProtocolConformance::getTypeWitness(
|
|
AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
auto known = TypeWitnesses.find(assocType);
|
|
if (known == TypeWitnesses.end()) {
|
|
assert(resolver && "Unable to resolve type witness");
|
|
resolver->resolveTypeWitness(this, assocType);
|
|
known = TypeWitnesses.find(assocType);
|
|
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
|
|
}
|
|
|
|
return known->second;
|
|
}
|
|
|
|
void NormalProtocolConformance::setTypeWitness(
|
|
AssociatedTypeDecl *assocType,
|
|
const Substitution &substitution) const {
|
|
assert(getProtocol() == cast<ProtocolDecl>(assocType->getDeclContext()) &&
|
|
"associated type in wrong protocol");
|
|
assert(TypeWitnesses.count(assocType) == 0 && "Type witness already known");
|
|
assert(!isComplete() && "Conformance already complete?");
|
|
TypeWitnesses[assocType] = substitution;
|
|
}
|
|
|
|
/// Retrieve the value witness corresponding to the given requirement.
|
|
ConcreteDeclRef NormalProtocolConformance::getWitness(
|
|
ValueDecl *requirement,
|
|
LazyResolver *resolver) const {
|
|
assert(!isa<AssociatedTypeDecl>(requirement) && "Request type witness");
|
|
auto known = Mapping.find(requirement);
|
|
if (known == Mapping.end()) {
|
|
assert(resolver && "Unable to resolve witness without resolver");
|
|
resolver->resolveWitness(this, requirement);
|
|
known = Mapping.find(requirement);
|
|
}
|
|
if (known != Mapping.end()) {
|
|
return known->second;
|
|
} else {
|
|
assert(!isComplete() && "Resolver did not resolve requirement");
|
|
return ConcreteDeclRef();
|
|
}
|
|
}
|
|
|
|
void NormalProtocolConformance::setWitness(ValueDecl *requirement,
|
|
ConcreteDeclRef witness) const {
|
|
assert(!isa<AssociatedTypeDecl>(requirement) && "Request type witness");
|
|
assert(getProtocol() == cast<ProtocolDecl>(requirement->getDeclContext()) &&
|
|
"requirement in wrong protocol");
|
|
assert(Mapping.count(requirement) == 0 && "Witness already known");
|
|
assert(!isComplete() && "Conformance already complete?");
|
|
Mapping[requirement] = witness;
|
|
}
|
|
|
|
SpecializedProtocolConformance::SpecializedProtocolConformance(
|
|
Type conformingType,
|
|
ProtocolConformance *genericConformance,
|
|
ArrayRef<Substitution> substitutions)
|
|
: ProtocolConformance(ProtocolConformanceKind::Specialized, conformingType),
|
|
GenericConformance(genericConformance),
|
|
GenericSubstitutions(substitutions)
|
|
{
|
|
assert(genericConformance->getKind() != ProtocolConformanceKind::Specialized);
|
|
}
|
|
|
|
bool SpecializedProtocolConformance::hasTypeWitness(
|
|
AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
return TypeWitnesses.find(assocType) != TypeWitnesses.end() ||
|
|
GenericConformance->hasTypeWitness(assocType, resolver);
|
|
}
|
|
|
|
const Substitution &SpecializedProtocolConformance::getTypeWitness(
|
|
AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
// If we've already created this type witness, return it.
|
|
auto known = TypeWitnesses.find(assocType);
|
|
if (known != TypeWitnesses.end()) {
|
|
return known->second;
|
|
}
|
|
|
|
// Otherwise, perform substitutions to create this witness now.
|
|
TypeSubstitutionMap substitutionMap = GenericConformance->getGenericParams()
|
|
->getSubstitutionMap(GenericSubstitutions);
|
|
|
|
auto &genericWitness
|
|
= GenericConformance->getTypeWitness(assocType, resolver);
|
|
auto conformingDC = getDeclContext();
|
|
auto conformingModule = conformingDC->getParentModule();
|
|
auto specializedType
|
|
= genericWitness.getReplacement().subst(conformingModule,
|
|
substitutionMap,
|
|
/*ignoreMissing=*/false,
|
|
resolver);
|
|
|
|
// If the type witness was unchanged, just copy it directly.
|
|
if (specializedType.getPointer() == genericWitness.getReplacement().getPointer()) {
|
|
TypeWitnesses[assocType] = genericWitness;
|
|
return TypeWitnesses[assocType];
|
|
}
|
|
|
|
// Gather the conformances for the type witness. These should never fail.
|
|
SmallVector<ProtocolConformance *, 4> conformances;
|
|
auto archetype = genericWitness.getArchetype();
|
|
for (auto proto : archetype->getConformsTo()) {
|
|
auto conforms = conformingModule->lookupConformance(specializedType, proto,
|
|
resolver);
|
|
assert((conforms.getInt() == ConformanceKind::Conforms ||
|
|
specializedType->is<TypeVariableType>() ||
|
|
specializedType->is<GenericTypeParamType>() ||
|
|
specializedType->is<DependentMemberType>()) &&
|
|
"Improperly checked substitution");
|
|
conformances.push_back(conforms.getPointer());
|
|
}
|
|
|
|
// Form the substitution.
|
|
auto &ctx = assocType->getASTContext();
|
|
TypeWitnesses[assocType] = Substitution{archetype, specializedType,
|
|
ctx.AllocateCopy(conformances)};
|
|
return TypeWitnesses[assocType];
|
|
}
|
|
|
|
ConcreteDeclRef
|
|
SpecializedProtocolConformance::getWitness(ValueDecl *requirement,
|
|
LazyResolver *resolver) const {
|
|
// FIXME: Apply substitutions here!
|
|
return GenericConformance->getWitness(requirement, resolver);
|
|
}
|
|
|
|
const NormalProtocolConformance *
|
|
ProtocolConformance::getRootNormalConformance() const {
|
|
const ProtocolConformance *C = this;
|
|
while (!isa<NormalProtocolConformance>(C)) {
|
|
switch (C->getKind()) {
|
|
case ProtocolConformanceKind::Normal:
|
|
llvm_unreachable("should have broken out of loop");
|
|
case ProtocolConformanceKind::Inherited:
|
|
C = cast<InheritedProtocolConformance>(C)
|
|
->getInheritedConformance();
|
|
break;
|
|
case ProtocolConformanceKind::Specialized:
|
|
C = cast<SpecializedProtocolConformance>(C)
|
|
->getGenericConformance();
|
|
break;
|
|
}
|
|
}
|
|
return cast<NormalProtocolConformance>(C);
|
|
}
|
|
|
|
ProtocolConformance *ProtocolConformance::subst(Module *module,
|
|
Type substType,
|
|
ArrayRef<Substitution> subs,
|
|
TypeSubstitutionMap &subMap,
|
|
ArchetypeConformanceMap &conformanceMap) {
|
|
if (getType()->isEqual(substType))
|
|
return this;
|
|
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Normal:
|
|
if (substType->isSpecialized()) {
|
|
assert(getType()->isSpecialized()
|
|
&& "substitution mapped non-specialized to specialized?!");
|
|
assert(getType()->getNominalOrBoundGenericNominal()
|
|
== substType->getNominalOrBoundGenericNominal()
|
|
&& "substitution mapped to different nominal?!");
|
|
return module->getASTContext()
|
|
.getSpecializedConformance(substType, this,
|
|
substType->gatherAllSubstitutions(module, nullptr));
|
|
}
|
|
assert(substType->isEqual(getType())
|
|
&& "substitution changed non-specialized type?!");
|
|
return this;
|
|
|
|
case ProtocolConformanceKind::Inherited: {
|
|
// Substitute the base.
|
|
ProtocolConformance *newBase
|
|
= cast<InheritedProtocolConformance>(this)->getInheritedConformance()
|
|
->subst(module, substType, subs, subMap, conformanceMap);
|
|
return module->getASTContext()
|
|
.getInheritedConformance(substType, newBase);
|
|
}
|
|
case ProtocolConformanceKind::Specialized: {
|
|
// Substitute the substitutions in the specialized conformance.
|
|
auto spec = cast<SpecializedProtocolConformance>(this);
|
|
SmallVector<Substitution, 8> newSubs;
|
|
newSubs.reserve(spec->getGenericSubstitutions().size());
|
|
for (auto &sub : spec->getGenericSubstitutions())
|
|
newSubs.push_back(sub.subst(module, subs, subMap, conformanceMap));
|
|
|
|
auto ctxNewSubs = module->getASTContext().AllocateCopy(newSubs);
|
|
|
|
return module->getASTContext()
|
|
.getSpecializedConformance(substType, spec->getGenericConformance(),
|
|
ctxNewSubs);
|
|
}
|
|
}
|
|
llvm_unreachable("bad ProtocolConformanceKind");
|
|
}
|
|
|
|
ProtocolConformance *
|
|
ProtocolConformance::getInheritedConformance(ProtocolDecl *protocol) const {
|
|
// Preserve specialization through this operation by peeling off the
|
|
// substitutions from a specialized conformance so we can apply them later.
|
|
const ProtocolConformance *unspecialized;
|
|
ArrayRef<Substitution> subs;
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Specialized: {
|
|
auto spec = cast<SpecializedProtocolConformance>(this);
|
|
unspecialized = spec->getGenericConformance();
|
|
subs = spec->getGenericSubstitutions();
|
|
break;
|
|
}
|
|
|
|
case ProtocolConformanceKind::Normal:
|
|
case ProtocolConformanceKind::Inherited:
|
|
unspecialized = this;
|
|
break;
|
|
}
|
|
|
|
|
|
ProtocolConformance *foundInherited;
|
|
|
|
// Search for the inherited conformance among our immediate parents.
|
|
auto &inherited = unspecialized->getInheritedConformances();
|
|
auto known = inherited.find(protocol);
|
|
if (known != inherited.end()) {
|
|
foundInherited = known->second;
|
|
goto found_inherited;
|
|
}
|
|
|
|
// If not there, the inherited conformance must be available through one of
|
|
// our parents.
|
|
for (auto &inheritedMapping : inherited)
|
|
if (inheritedMapping.first->inheritsFrom(protocol)) {
|
|
foundInherited = inheritedMapping.second->
|
|
getInheritedConformance(protocol);
|
|
goto found_inherited;
|
|
}
|
|
|
|
llvm_unreachable("Can't find the inherited conformance.");
|
|
|
|
found_inherited:
|
|
|
|
// Specialize the inherited conformance, if necessary.
|
|
if (!subs.empty()) {
|
|
return getType()->getASTContext()
|
|
.getSpecializedConformance(getType(), foundInherited, subs);
|
|
}
|
|
assert((getType()->isEqual(foundInherited->getType()) ||
|
|
foundInherited->getType()->isSuperclassOf(getType(), nullptr))
|
|
&& "inherited conformance does not match type");
|
|
return foundInherited;
|
|
}
|
|
|
|
#pragma mark Protocol conformance lookup
|
|
|
|
class swift::ConformanceLookupTable {
|
|
/// Describes the stage at which a particular nominal type or
|
|
/// extension's conformances has been processed.
|
|
enum class ConformanceStage : unsigned char {
|
|
/// The explicit conformances have been recorded in the lookup table.
|
|
RecordedExplicit,
|
|
|
|
/// Conformances from the superclass have been inherited.
|
|
Inherited,
|
|
|
|
/// The explicit conformances have been expanded out to include
|
|
/// the conformances they imply.
|
|
ExpandedImplied,
|
|
|
|
/// The complete set of conformances have been fully resolved to
|
|
/// assign conformances, diagnose conflicts, etc.
|
|
Resolved,
|
|
};
|
|
|
|
/// The number of conformance stages.
|
|
static const unsigned NumConformanceStages = 4;
|
|
|
|
/// An entry in the last-processed list, which contains a pointer to
|
|
/// the last extension that was processed at a particular stage (or
|
|
/// nullptr if no extensions have been processed) and indicates
|
|
/// whether the nominal type declaration itself has been processed
|
|
/// at that stage.
|
|
typedef llvm::PointerIntPair<ExtensionDecl *, 1, bool> LastProcessedEntry;
|
|
|
|
/// Array indicating how far we have gotten in processing the
|
|
/// nominal type and list of extensions for each stage of
|
|
/// conformance checking.
|
|
LastProcessedEntry LastProcessed[NumConformanceStages];
|
|
|
|
struct ConformanceEntry;
|
|
|
|
/// Describes the "source" of a conformance, indicating where the
|
|
/// conformance came from.
|
|
class ConformanceSource {
|
|
llvm::PointerIntPair<void *, 2, ConformanceEntryKind> Storage;
|
|
|
|
ConformanceSource(void *ptr, ConformanceEntryKind kind)
|
|
: Storage(ptr, kind) { }
|
|
|
|
public:
|
|
/// Create an inherited conformance.
|
|
///
|
|
/// The given class will have an inherited conformance for the
|
|
/// requested protocol.
|
|
static ConformanceSource forInherited(ClassDecl *classDecl) {
|
|
return ConformanceSource(classDecl, ConformanceEntryKind::Inherited);
|
|
}
|
|
|
|
/// Create an explicit conformance.
|
|
///
|
|
/// The given declaration context (nominal type declaration or
|
|
/// extension thereof) explicitly specifies conformance to the
|
|
/// protocol.
|
|
static ConformanceSource forExplicit(DeclContext *dc) {
|
|
return ConformanceSource(dc, ConformanceEntryKind::Explicit);
|
|
}
|
|
|
|
/// Create an implied conformance.
|
|
///
|
|
/// Conformance to the protocol is implied by the given
|
|
/// conformance entry. The chain of conformance entries will
|
|
/// eventually terminate in a non-implied conformance.
|
|
static ConformanceSource forImplied(ConformanceEntry *entry) {
|
|
return ConformanceSource(entry, ConformanceEntryKind::Implied);
|
|
}
|
|
|
|
/// Create a synthesized conformance.
|
|
///
|
|
/// The given nominal type declaration will get a synthesized
|
|
/// conformance to the requested protocol.
|
|
static ConformanceSource forSynthesized(NominalTypeDecl *typeDecl) {
|
|
return ConformanceSource(typeDecl, ConformanceEntryKind::Synthesized);
|
|
}
|
|
|
|
/// Retrieve the kind of conformance formed from this source.
|
|
ConformanceEntryKind getKind() const { return Storage.getInt(); }
|
|
|
|
/// For an inherited conformance, retrieve the class declaration
|
|
/// for the inheriting class.
|
|
ClassDecl *getInheritingClass() const {
|
|
assert(getKind() == ConformanceEntryKind::Inherited);
|
|
return static_cast<ClassDecl *>(Storage.getPointer());
|
|
}
|
|
|
|
/// For an explicit conformance, retrieve the declaration context
|
|
/// that specifies the conformance.
|
|
DeclContext *getExplicitDeclContext() const {
|
|
assert(getKind() == ConformanceEntryKind::Explicit);
|
|
return static_cast<DeclContext *>(Storage.getPointer());
|
|
}
|
|
|
|
/// For a synthesized conformance, retrieve the nominal type decl
|
|
/// that will receive the conformance.
|
|
ConformanceEntry *getImpliedSource() const {
|
|
assert(getKind() == ConformanceEntryKind::Implied);
|
|
return static_cast<ConformanceEntry *>(Storage.getPointer());
|
|
}
|
|
|
|
/// For a synthesized conformance, retrieve the nominal type decl
|
|
/// that will receive the conformance.
|
|
NominalTypeDecl *getSynthesizedDecl() const {
|
|
assert(getKind() == ConformanceEntryKind::Synthesized);
|
|
return static_cast<NominalTypeDecl *>(Storage.getPointer());
|
|
}
|
|
|
|
/// Get the declaration context that this conformance will be
|
|
/// associated with.
|
|
DeclContext *getDeclContext() const;
|
|
};
|
|
|
|
/// An entry in the conformance table.
|
|
struct ConformanceEntry {
|
|
/// The source location within the current context where the
|
|
/// protocol conformance was specified.
|
|
SourceLoc Loc;
|
|
|
|
/// If this conformance entry has been superseded, the conformance
|
|
/// that superseded it.
|
|
ConformanceEntry *SupersededBy = nullptr;
|
|
|
|
/// The source of this conformance entry , which is either a
|
|
/// DeclContext (for an explicitly-specified conformance) or a
|
|
/// link to the conformance that implied this conformance.
|
|
ConformanceSource Source;
|
|
|
|
/// Either the protocol to be resolved or the resolved protocol conformance.
|
|
llvm::PointerUnion<ProtocolDecl *, ProtocolConformance *> Conformance;
|
|
|
|
ConformanceEntry(SourceLoc loc, ProtocolDecl *protocol,
|
|
ConformanceSource source)
|
|
: Loc(loc), Source(source), Conformance(protocol) { }
|
|
|
|
/// Retrieve the location at which this conformance was declared
|
|
/// or synthesized.
|
|
SourceLoc getLoc() const { return Loc; }
|
|
|
|
/// Whether this conformance is already "fixed" and cannot be superseded.
|
|
bool isFixed() const {
|
|
// If a conformance has been assigned, it cannot be superseded.
|
|
if (getConformance())
|
|
return true;
|
|
|
|
// Otherwise, only inherited conformances are fixed.
|
|
switch (getKind()) {
|
|
case ConformanceEntryKind::Explicit:
|
|
case ConformanceEntryKind::Implied:
|
|
case ConformanceEntryKind::Synthesized:
|
|
return false;
|
|
|
|
case ConformanceEntryKind::Inherited:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// Whether this protocol conformance was superseded by another
|
|
/// conformance.
|
|
bool isSuperseded() const { return SupersededBy != nullptr; }
|
|
|
|
/// Retrieve the conformance entry that superseded this one.
|
|
ConformanceEntry *getSupersededBy() const { return SupersededBy; }
|
|
|
|
/// Note that this conformance entry was superseded by the given
|
|
/// entry.
|
|
void markSupersededBy(ConformanceLookupTable &table,
|
|
ConformanceEntry *entry,
|
|
bool diagnose);
|
|
|
|
/// Determine the kind of conformance.
|
|
ConformanceEntryKind getKind() const {
|
|
return Source.getKind();
|
|
}
|
|
|
|
/// Retrieve the declaration context associated with this conformance.
|
|
DeclContext *getDeclContext() const {
|
|
return Source.getDeclContext();
|
|
}
|
|
|
|
/// Retrieve the protocol to which this conformance entry refers.
|
|
ProtocolDecl *getProtocol() const {
|
|
if (auto protocol = Conformance.dyn_cast<ProtocolDecl *>())
|
|
return protocol;
|
|
|
|
return Conformance.get<ProtocolConformance *>()->getProtocol();
|
|
}
|
|
|
|
/// Retrieve the conformance for this entry, if it has one.
|
|
ProtocolConformance *getConformance() const {
|
|
return Conformance.dyn_cast<ProtocolConformance *>();
|
|
}
|
|
|
|
/// Retrieve the conformance entry where the conformance was
|
|
/// declared.
|
|
const ConformanceEntry *getDeclaredConformance() const {
|
|
if (Source.getKind() == ConformanceEntryKind::Implied)
|
|
return Source.getImpliedSource()->getDeclaredConformance();
|
|
|
|
return this;
|
|
}
|
|
|
|
/// Retrieve the source location of the place where the
|
|
/// conformance was introduced, e.g., an explicit conformance or
|
|
/// the point at which a subclass inherits a conformance from its
|
|
/// superclass.
|
|
SourceLoc getDeclaredLoc() const {
|
|
if (Source.getKind() == ConformanceEntryKind::Implied)
|
|
return Source.getImpliedSource()->getDeclaredLoc();
|
|
|
|
return Loc;
|
|
}
|
|
|
|
// Only allow allocation of conformance entries using the
|
|
// allocator in ASTContext.
|
|
void *operator new(size_t Bytes, ASTContext &C,
|
|
unsigned Alignment = alignof(ConformanceEntry)) {
|
|
return C.Allocate(Bytes, Alignment);
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_DEPRECATED(
|
|
void dump() const LLVM_ATTRIBUTE_USED,
|
|
"only for use within the debugger");
|
|
void dump(raw_ostream &os, unsigned indent = 0) const;
|
|
};
|
|
|
|
/// The set of conformance entries for a given protocol.
|
|
typedef llvm::TinyPtrVector<ConformanceEntry *> ConformanceEntries;
|
|
|
|
/// The type of the internal conformance table.
|
|
typedef llvm::DenseMap<ProtocolDecl *, ConformanceEntries> ConformanceTable;
|
|
|
|
/// The conformance table.
|
|
ConformanceTable Conformances;
|
|
|
|
typedef llvm::SmallVector<ProtocolDecl *, 2> ProtocolList;
|
|
|
|
/// List of all of the protocols to which a given context declares
|
|
/// conformance, both explicitly and implicitly.
|
|
llvm::DenseMap<DeclContext *, SmallVector<ConformanceEntry *, 4>>
|
|
AllConformances;
|
|
|
|
/// The complete set of diagnostics about erroneously superseded
|
|
/// protocol conformances.
|
|
llvm::SmallDenseMap<DeclContext *, std::vector<ConformanceEntry *> >
|
|
AllSupersededDiagnostics;
|
|
|
|
/// Indicates whether we are visiting the superclass.
|
|
bool VisitingSuperclass = false;
|
|
|
|
/// Add a protocol.
|
|
bool addProtocol(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol, SourceLoc loc,
|
|
ConformanceSource source);
|
|
|
|
/// Add the protocols from the given list.
|
|
void addProtocols(NominalTypeDecl *nominal, ArrayRef<TypeLoc> inherited,
|
|
ConformanceSource source, LazyResolver *resolver);
|
|
|
|
/// Expand the implied conformances for the given DeclContext.
|
|
void expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc,
|
|
LazyResolver *resolver);
|
|
|
|
/// A three-way ordering
|
|
enum class Ordering {
|
|
Before,
|
|
Equivalent,
|
|
After,
|
|
};
|
|
|
|
/// Determine whether the first conformance entry supersedes the
|
|
/// second when determining where to place the conformance.
|
|
///
|
|
/// \param diagnoseSuperseded When one entry is better than another,
|
|
/// whether to diagnose the problem as an error.
|
|
Ordering compareConformances(ConformanceEntry *lhs, ConformanceEntry *rhs,
|
|
bool &diagnoseSuperseded);
|
|
|
|
/// Resolve the set of conformances that will be generated for the
|
|
/// given protocol.
|
|
///
|
|
/// \returns true if any conformance entries were superseded by this
|
|
/// operation.
|
|
bool resolveConformances(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol,
|
|
LazyResolver *resolver);
|
|
|
|
/// Enumerate each of the unhandled contexts (nominal type
|
|
/// declaration or extension) within the given stage.
|
|
///
|
|
/// \param stage The stage to process. Note that it is up to the
|
|
/// caller to ensure that prior stages have already been handled.
|
|
///
|
|
/// \param nominalFunc Function object to be invoked when the
|
|
/// nominal type declaration itself needs to be processed. It takes
|
|
/// the nominal type declaration and its result is ignored.
|
|
///
|
|
/// \param extensionFunc Function object to be invoked with a given
|
|
/// extension needs to be processed. It takes the extension as an
|
|
/// argument and its result is ignored.
|
|
template<typename NominalFunc, typename ExtensionFunc>
|
|
void forEachInStage(ConformanceStage stage,
|
|
NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
NominalFunc nominalFunc,
|
|
ExtensionFunc extensionFunc);
|
|
|
|
/// Inherit the conformances from the given superclass into the
|
|
/// given nominal type.
|
|
///
|
|
/// \param classDecl The class into which the conformances will be
|
|
/// inherited.
|
|
///
|
|
/// \param superclassDecl The superclass from which the conformances
|
|
/// will be inherited.
|
|
///
|
|
/// \param superclassExt If non-null, the superclass extension from
|
|
/// which conformances will be inherited. If null, the conformances
|
|
/// on the superclass declaration itself will be inherited.
|
|
void inheritConformances(ClassDecl *classDecl,
|
|
ClassDecl *superclassDecl,
|
|
ExtensionDecl *superclassExt,
|
|
LazyResolver *resolver);
|
|
|
|
/// Update a lookup table with conformances from newly-added extensions.
|
|
void updateLookupTable(NominalTypeDecl *nominal, ConformanceStage stage,
|
|
LazyResolver *resolver);
|
|
|
|
/// Load all of the protocol conformances for the given (serialized)
|
|
/// declaration context.
|
|
void loadAllConformances(NominalTypeDecl *nominal,
|
|
DeclContext *dc,
|
|
ArrayRef<ProtocolConformance *> conformances);
|
|
|
|
public:
|
|
/// Create a new conformance lookup table.
|
|
ConformanceLookupTable(ASTContext &ctx, NominalTypeDecl *nominal,
|
|
LazyResolver *resolver);
|
|
|
|
/// Destroy the conformance table.
|
|
void destroy();
|
|
|
|
/// Add a synthesized conformance to the lookup table.
|
|
void addSynthesizedConformance(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol);
|
|
|
|
/// Look for conformances to the given protocol.
|
|
///
|
|
/// \param conformances Will be populated with the set of protocol
|
|
/// conformances found for this protocol and nominal type.
|
|
///
|
|
/// \returns true if any conformances were found.
|
|
bool lookupConformance(Module *module,
|
|
NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances);
|
|
|
|
/// Look for all of the conformances within the given declaration context.
|
|
void lookupConformances(NominalTypeDecl *nominal,
|
|
DeclContext *dc,
|
|
LazyResolver *resolver,
|
|
ConformanceLookupKind lookupKind,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances,
|
|
SmallVectorImpl<ConformanceDiagnostic> *diagnostics);
|
|
|
|
/// Retrieve the complete set of protocols to which this nominal
|
|
/// type conforms.
|
|
void getAllProtocols(NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolDecl *> &scratch);
|
|
|
|
/// Retrieve the complete set of protocol conformances for this
|
|
/// nominal type.
|
|
void getAllConformances(NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &scratch);
|
|
|
|
// Only allow allocation of conformance lookup tables using the
|
|
// allocator in ASTContext or by doing a placement new.
|
|
void *operator new(size_t Bytes, ASTContext &C,
|
|
unsigned Alignment = alignof(ConformanceLookupTable)) {
|
|
return C.Allocate(Bytes, Alignment);
|
|
}
|
|
void *operator new(size_t Bytes, void *Mem) {
|
|
assert(Mem);
|
|
return Mem;
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_DEPRECATED(
|
|
void dump() const LLVM_ATTRIBUTE_USED,
|
|
"only for use within the debugger");
|
|
void dump(raw_ostream &os) const;
|
|
};
|
|
|
|
DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const {
|
|
switch (getKind()) {
|
|
case ConformanceEntryKind::Inherited:
|
|
return getInheritingClass();
|
|
|
|
case ConformanceEntryKind::Explicit:
|
|
return getExplicitDeclContext();
|
|
|
|
case ConformanceEntryKind::Implied:
|
|
return getImpliedSource()->Source.getDeclContext();
|
|
|
|
case ConformanceEntryKind::Synthesized:
|
|
return getSynthesizedDecl();
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::ConformanceEntry::markSupersededBy(
|
|
ConformanceLookupTable &table,
|
|
ConformanceEntry *entry,
|
|
bool diagnose) {
|
|
assert(!isSuperseded() && "Already superseded");
|
|
|
|
// Note that we've been superseded.
|
|
SupersededBy = entry;
|
|
|
|
if (diagnose) {
|
|
// Record the problem in the conformance table. We'll
|
|
// diagnose these in semantic analysis.
|
|
table.AllSupersededDiagnostics[getDeclContext()].push_back(this);
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::ConformanceEntry::dump() const {
|
|
dump(llvm::errs());
|
|
}
|
|
|
|
void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os,
|
|
unsigned indent) const {
|
|
os.indent(indent) << "(conformance @" << static_cast<const void *>(this);
|
|
|
|
os << " protocol=";
|
|
getProtocol()->dumpRef(os);
|
|
|
|
if (Loc.isValid()) {
|
|
os << " loc=";
|
|
Loc.dump(getProtocol()->getASTContext().SourceMgr);
|
|
}
|
|
|
|
switch (getKind()) {
|
|
case ConformanceEntryKind::Implied:
|
|
os << " implied_by=@"
|
|
<< static_cast<const void *>(Source.getImpliedSource());
|
|
break;
|
|
|
|
case ConformanceEntryKind::Explicit:
|
|
os << " explicit";
|
|
break;
|
|
|
|
case ConformanceEntryKind::Inherited:
|
|
os << " inherited";
|
|
break;
|
|
|
|
case ConformanceEntryKind::Synthesized:
|
|
os << " synthesized";
|
|
break;
|
|
}
|
|
|
|
if (auto conf = getConformance()) {
|
|
os << " fixed_conformance=@" << static_cast<const void *>(conf);
|
|
}
|
|
|
|
if (SupersededBy)
|
|
os << " superseded_by=@" << static_cast<const void *>(SupersededBy);
|
|
|
|
os << ")\n";
|
|
}
|
|
|
|
ConformanceLookupTable::ConformanceLookupTable(ASTContext &ctx,
|
|
NominalTypeDecl *nominal,
|
|
LazyResolver *resolver) {
|
|
// Register a cleanup with the ASTContext to call the conformance
|
|
// table destructor.
|
|
ctx.addCleanup([this]() {
|
|
this->destroy();
|
|
});
|
|
}
|
|
|
|
void ConformanceLookupTable::destroy() {
|
|
this->~ConformanceLookupTable();
|
|
}
|
|
|
|
template<typename NominalFunc, typename ExtensionFunc>
|
|
void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
|
|
NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
NominalFunc nominalFunc,
|
|
ExtensionFunc extensionFunc) {
|
|
assert(static_cast<unsigned>(stage) < NumConformanceStages &&
|
|
"NumConformanceStages has not been updated");
|
|
LastProcessedEntry &lastProcessed
|
|
= LastProcessed[static_cast<unsigned>(stage)];
|
|
|
|
// Handle the nominal type.
|
|
if (!lastProcessed.getInt()) {
|
|
lastProcessed.setInt(true);
|
|
|
|
if (resolver)
|
|
resolver->resolveDeclSignature(nominal);
|
|
|
|
// If we have conformances we can load, do so.
|
|
if (!nominal->addedLoadedConformances()) {
|
|
nominal->setAddedLoadedConformances();
|
|
loadAllConformances(nominal, nominal, nominal->getConformances());
|
|
}
|
|
|
|
nominalFunc(nominal);
|
|
}
|
|
|
|
// Handle the extensions that we have not yet visited.
|
|
nominal->prepareExtensions();
|
|
for (auto next = lastProcessed.getPointer()
|
|
? lastProcessed.getPointer()->NextExtension.getPointer()
|
|
: nominal->FirstExtension;
|
|
next;
|
|
next = next->NextExtension.getPointer()) {
|
|
lastProcessed.setPointer(next);
|
|
|
|
if (resolver)
|
|
resolver->resolveExtension(next);
|
|
|
|
// If we have conformances we can load, do so.
|
|
// FIXME: We should do this more lazily.
|
|
if (!next->addedLoadedConformances()) {
|
|
next->setAddedLoadedConformances();
|
|
loadAllConformances(nominal, next, next->getConformances());
|
|
}
|
|
|
|
extensionFunc(next);
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
|
|
ClassDecl *superclassDecl,
|
|
ExtensionDecl *superclassExt,
|
|
LazyResolver *resolver) {
|
|
// Local function to return the location of the superclass. This
|
|
// takes a little digging, so compute on first use and cache it.
|
|
SourceLoc superclassLoc;
|
|
auto getSuperclassLoc = [&] {
|
|
if (superclassLoc.isValid())
|
|
return superclassLoc;
|
|
|
|
for (const auto &inherited : classDecl->getInherited()) {
|
|
if (auto inheritedType = inherited.getType()) {
|
|
if (inheritedType->getClassOrBoundGenericClass()) {
|
|
superclassLoc = inherited.getSourceRange().Start;
|
|
return superclassLoc;
|
|
}
|
|
}
|
|
}
|
|
|
|
superclassLoc = superclassDecl->getLoc();
|
|
return superclassLoc;
|
|
};
|
|
|
|
llvm::SmallPtrSet<ProtocolDecl *, 4> protocols;
|
|
auto addInheritedConformance = [&](ConformanceEntry *entry) {
|
|
auto protocol = entry->getProtocol();
|
|
|
|
// Don't add redundant conformances here. This is merely an
|
|
// optimization; resolveConformances() would zap the duplicates
|
|
// anyway.
|
|
if (!protocols.insert(protocol).second)
|
|
return;
|
|
|
|
// Add the inherited entry.
|
|
(void)addProtocol(classDecl, protocol, getSuperclassLoc(),
|
|
ConformanceSource::forInherited(classDecl));
|
|
};
|
|
|
|
// Add inherited conformances.
|
|
DeclContext *superDC = superclassExt;
|
|
if (!superclassExt)
|
|
superDC = superclassDecl;
|
|
|
|
for (auto entry : superclassDecl->ConformanceTable->AllConformances[superDC]){
|
|
addInheritedConformance(entry);
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
|
|
ConformanceStage stage,
|
|
LazyResolver *resolver) {
|
|
switch (stage) {
|
|
case ConformanceStage::RecordedExplicit:
|
|
// Record all of the explicit conformances.
|
|
forEachInStage(stage, nominal, resolver,
|
|
[&](NominalTypeDecl *nominal) {
|
|
if (resolver)
|
|
resolver->resolveInheritanceClause(nominal);
|
|
|
|
addProtocols(nominal, nominal->getInherited(),
|
|
ConformanceSource::forExplicit(nominal),
|
|
resolver);
|
|
},
|
|
[&](ExtensionDecl *ext) {
|
|
if (resolver)
|
|
resolver->resolveInheritanceClause(ext);
|
|
|
|
addProtocols(nominal, ext->getInherited(),
|
|
ConformanceSource::forExplicit(ext),
|
|
resolver);
|
|
});
|
|
break;
|
|
|
|
case ConformanceStage::Inherited:
|
|
updateLookupTable(nominal, ConformanceStage::RecordedExplicit, resolver);
|
|
|
|
// For classes, expand implied conformances of the superclass,
|
|
// because an implied conformance in the superclass is considered
|
|
// "fixed" in the subclass.
|
|
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
|
|
if (resolver)
|
|
resolver->resolveSuperclass(classDecl);
|
|
|
|
if (Type superclass = classDecl->getSuperclass()) {
|
|
if (auto superclassDecl
|
|
= superclass->getClassOrBoundGenericClass()) {
|
|
// Break infinite recursion when visiting ill-formed classes
|
|
// with circular inheritance.
|
|
if (VisitingSuperclass)
|
|
return;
|
|
llvm::SaveAndRestore<bool> visiting(VisitingSuperclass, true);
|
|
|
|
// Resolve the conformances of the superclass.
|
|
superclassDecl->prepareConformanceTable(resolver);
|
|
superclassDecl->ConformanceTable->updateLookupTable(
|
|
superclassDecl,
|
|
ConformanceStage::Resolved,
|
|
resolver);
|
|
|
|
// Expand inherited conformances.
|
|
forEachInStage(stage, superclassDecl, resolver,
|
|
[&](NominalTypeDecl *superclass) {
|
|
inheritConformances(classDecl, superclassDecl,
|
|
nullptr, resolver);
|
|
},
|
|
[&](ExtensionDecl *ext) {
|
|
inheritConformances(classDecl, superclassDecl, ext,
|
|
resolver);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ConformanceStage::ExpandedImplied:
|
|
// Record explicit conformances and import inherited conformances
|
|
// before expanding.
|
|
updateLookupTable(nominal, ConformanceStage::Inherited, resolver);
|
|
|
|
// Expand inherited conformances.
|
|
forEachInStage(stage, nominal, resolver,
|
|
[&](NominalTypeDecl *nominal) {
|
|
expandImpliedConformances(nominal, nominal, resolver);
|
|
},
|
|
[&](ExtensionDecl *ext) {
|
|
expandImpliedConformances(nominal, ext, resolver);
|
|
});
|
|
break;
|
|
|
|
case ConformanceStage::Resolved:
|
|
// Expand inherited conformances so we have the complete set of
|
|
// conformances.
|
|
updateLookupTable(nominal, ConformanceStage::ExpandedImplied, resolver);
|
|
|
|
/// Determine whether any extensions were added that might require
|
|
/// us to compute conformances again.
|
|
bool anyChanged = false;
|
|
forEachInStage(stage, nominal, resolver,
|
|
[&](NominalTypeDecl *nominal) {
|
|
anyChanged = true;
|
|
},
|
|
[&](ExtensionDecl *ext) {
|
|
anyChanged = true;
|
|
});
|
|
|
|
if (anyChanged) {
|
|
// Compute the conformances for each protocol.
|
|
bool anySuperseded = false;
|
|
for (const auto &entry : Conformances) {
|
|
if (resolveConformances(nominal, entry.first, resolver))
|
|
anySuperseded = true;
|
|
}
|
|
|
|
if (anySuperseded) {
|
|
// Update the lists of all conformances to remove superseded
|
|
// conformances.
|
|
for (auto &conformances : AllConformances) {
|
|
conformances.second.erase(
|
|
std::remove_if(conformances.second.begin(),
|
|
conformances.second.end(),
|
|
[&](ConformanceEntry *entry) {
|
|
return entry->isSuperseded();
|
|
}),
|
|
conformances.second.end());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Expand all of the inherited conformances.
|
|
static void expandConformance(ProtocolConformance *conformance,
|
|
llvm::SetVector<ProtocolConformance *> &expanded){
|
|
if (!expanded.insert(conformance))
|
|
return;
|
|
|
|
for (auto &inherited : conformance->getInheritedConformances()) {
|
|
expandConformance(inherited.second, expanded);
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::loadAllConformances(
|
|
NominalTypeDecl *nominal,
|
|
DeclContext *dc,
|
|
ArrayRef<ProtocolConformance*> conformances) {
|
|
// If this declaration context came from source, there's nothing to
|
|
// do here.
|
|
if (dc->getParentSourceFile())
|
|
return;
|
|
|
|
// Expand all of the inherited conformances.
|
|
llvm::SetVector<ProtocolConformance *> expandedConformances;
|
|
for (auto conformance : conformances)
|
|
expandConformance(conformance, expandedConformances);
|
|
|
|
// Add entries for each loaded conformance.
|
|
ASTContext &ctx = nominal->getASTContext();
|
|
auto &dcConformances = AllConformances[dc];
|
|
for (auto conformance : expandedConformances) {
|
|
// Only add conformances from this context. The expanded list
|
|
// might contain inherited conformances from other contexts.
|
|
if (conformance->getDeclContext() != dc)
|
|
continue;
|
|
|
|
auto inherited = dyn_cast<InheritedProtocolConformance>(conformance);
|
|
ConformanceSource source
|
|
= inherited ? ConformanceSource::forInherited(cast<ClassDecl>(nominal))
|
|
: ConformanceSource::forExplicit(dc);
|
|
|
|
auto protocol = conformance->getProtocol();
|
|
ConformanceEntry *entry = new (ctx) ConformanceEntry(SourceLoc(),
|
|
protocol,
|
|
source);
|
|
entry->Conformance = conformance;
|
|
|
|
// Record that this type conforms to the given protocol.
|
|
Conformances[protocol].push_back(entry);
|
|
|
|
// Record this as a conformance within the given declaration
|
|
// context.
|
|
dcConformances.push_back(entry);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Visit the protocols referenced by the given type, which was
|
|
/// uttered at the given location.
|
|
template<typename AddProtocolFunc>
|
|
void visitProtocols(Type type, SourceLoc loc, AddProtocolFunc addProtocol) {
|
|
// Protocol types.
|
|
if (auto protocol = type->getAs<ProtocolType>()) {
|
|
addProtocol(protocol->getDecl(), loc);
|
|
return;
|
|
}
|
|
|
|
// Protocol compositions.
|
|
if (auto composition = type->getAs<ProtocolCompositionType>()) {
|
|
for (auto protocol : composition->getProtocols())
|
|
visitProtocols(protocol, loc, addProtocol);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// Visit the protocols referenced by the given type representation.
|
|
/// Each protocol is provided to the callback along with the source
|
|
/// location where that protocol was named.
|
|
template<typename AddProtocolFunc>
|
|
void visitProtocols(TypeRepr *typeRepr, LazyResolver *resolver,
|
|
AddProtocolFunc addProtocol) {
|
|
// Look through attributed type representations.
|
|
while (auto attr = dyn_cast<AttributedTypeRepr>(typeRepr))
|
|
typeRepr = attr->getTypeRepr();
|
|
|
|
// Handle identifier type representations.
|
|
if (auto ident = dyn_cast<IdentTypeRepr>(typeRepr)) {
|
|
ComponentIdentTypeRepr *component = ident->getComponentRange().back();
|
|
|
|
// If the last component refers to a type, visit the protocols
|
|
// in that type.
|
|
if (component->isBoundType()) {
|
|
visitProtocols(component->getBoundType(), component->getIdLoc(),
|
|
addProtocol);
|
|
return;
|
|
}
|
|
|
|
// If the last component refers to a type declaration, visit the
|
|
// protocols in that type.
|
|
if (component->isBoundDecl()) {
|
|
if (auto typeDecl = dyn_cast<TypeDecl>(component->getBoundDecl())) {
|
|
if (resolver)
|
|
resolver->resolveDeclSignature(typeDecl);
|
|
|
|
visitProtocols(typeDecl->getDeclaredType(), component->getIdLoc(),
|
|
addProtocol);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Recurse into protocol compositions.
|
|
if (auto composition = dyn_cast<ProtocolCompositionTypeRepr>(typeRepr)) {
|
|
for (auto ident : composition->getProtocols())
|
|
visitProtocols(ident, resolver, addProtocol);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConformanceLookupTable::addProtocol(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol, SourceLoc loc,
|
|
ConformanceSource source) {
|
|
DeclContext *dc = source.getDeclContext();
|
|
ASTContext &ctx = dc->getASTContext();
|
|
|
|
// Determine the kind of conformance.
|
|
ConformanceEntryKind kind = source.getKind();
|
|
|
|
// If this entry is synthesized or implied, scan to determine
|
|
// whether there are any explicit better conformances that make this
|
|
// conformance trivially superseded (and, therefore, no worth
|
|
// recording).
|
|
auto &conformanceEntries = Conformances[protocol];
|
|
if (kind == ConformanceEntryKind::Implied ||
|
|
kind == ConformanceEntryKind::Synthesized) {
|
|
for (const auto *existingEntry : conformanceEntries) {
|
|
switch (existingEntry->getKind()) {
|
|
case ConformanceEntryKind::Explicit:
|
|
case ConformanceEntryKind::Inherited:
|
|
return false;
|
|
|
|
case ConformanceEntryKind::Implied:
|
|
// An implied conformance is better than a synthesized one.
|
|
if (kind == ConformanceEntryKind::Synthesized)
|
|
return false;
|
|
break;
|
|
|
|
case ConformanceEntryKind::Synthesized:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build the conformance entry (if it hasn't been built before).
|
|
ConformanceEntry *entry = new (ctx) ConformanceEntry(loc, protocol, source);
|
|
conformanceEntries.push_back(entry);
|
|
|
|
// Record this as a conformance within the given declaration
|
|
// context.
|
|
AllConformances[dc].push_back(entry);
|
|
return true;
|
|
}
|
|
|
|
void ConformanceLookupTable::addProtocols(NominalTypeDecl *nominal,
|
|
ArrayRef<TypeLoc> inherited,
|
|
ConformanceSource source,
|
|
LazyResolver *resolver) {
|
|
// Visit each of the types in the inheritance list to find
|
|
// protocols.
|
|
for (const auto &entry : inherited) {
|
|
if (auto typeRepr = entry.getTypeRepr()) {
|
|
visitProtocols(typeRepr, resolver,
|
|
[&](ProtocolDecl *protocol, SourceLoc loc) {
|
|
addProtocol(nominal, protocol, loc, source);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal,
|
|
DeclContext *dc,
|
|
LazyResolver *resolver) {
|
|
// Note: recursive type-checking implies that that AllConformances
|
|
// may be reallocated during this traversal, so pay the lookup cost
|
|
// during each iteration.
|
|
for (unsigned i = 0; i != AllConformances[dc].size(); ++i) {
|
|
ConformanceEntry *conformanceEntry = AllConformances[dc][i];
|
|
ProtocolDecl *conformingProtocol = conformanceEntry->getProtocol();
|
|
|
|
// Visit the protocols inherited by this protocol, adding them as
|
|
// implied conformances.
|
|
if (resolver)
|
|
resolver->resolveInheritanceClause(dc);
|
|
|
|
// FIXME: It's odd that the inheritance clause isn't loaded at all during
|
|
// deserialization, so we have this weird separate path.
|
|
if (!conformingProtocol->getParentSourceFile()) {
|
|
for (auto protocol : conformingProtocol->getProtocols()) {
|
|
addProtocol(nominal, protocol, SourceLoc(),
|
|
ConformanceSource::forImplied(conformanceEntry));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
for (const auto &entry : conformingProtocol->getInherited()) {
|
|
if (auto typeRepr = entry.getTypeRepr()) {
|
|
visitProtocols(typeRepr, resolver,
|
|
[&](ProtocolDecl *protocol, SourceLoc loc) {
|
|
addProtocol(nominal, protocol, loc,
|
|
ConformanceSource::forImplied(
|
|
conformanceEntry));
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given conformance entry kind can be replaced.
|
|
static bool isReplaceable(ConformanceEntryKind kind) {
|
|
switch (kind) {
|
|
case ConformanceEntryKind::Implied:
|
|
case ConformanceEntryKind::Synthesized:
|
|
return true;
|
|
|
|
case ConformanceEntryKind::Explicit:
|
|
case ConformanceEntryKind::Inherited:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
|
|
ConformanceEntry *lhs,
|
|
ConformanceEntry *rhs,
|
|
bool &diagnoseSuperseded) {
|
|
// If one entry is fixed and the other is not, we have our answer.
|
|
if (lhs->isFixed() != rhs->isFixed()) {
|
|
// If the non-fixed conformance is not replacable, we have a failure to
|
|
// diagnose.
|
|
diagnoseSuperseded = (lhs->isFixed() && !isReplaceable(rhs->getKind())) ||
|
|
(rhs->isFixed() && !isReplaceable(lhs->getKind()));
|
|
|
|
return lhs->isFixed() ? Ordering::Before : Ordering::After;
|
|
}
|
|
|
|
ConformanceEntryKind lhsKind = lhs->getKind();
|
|
ConformanceEntryKind rhsKind = rhs->getKind();
|
|
|
|
if (lhsKind != ConformanceEntryKind::Implied ||
|
|
rhsKind != ConformanceEntryKind::Implied) {
|
|
// If both conformances are non-replaceable, diagnose the
|
|
// superseded one.
|
|
diagnoseSuperseded = !isReplaceable(lhsKind) && !isReplaceable(rhsKind) &&
|
|
!(lhsKind == ConformanceEntryKind::Inherited &&
|
|
rhsKind == ConformanceEntryKind::Inherited);
|
|
|
|
// If we can order by kind, do so.
|
|
if (lhsKind != rhsKind) {
|
|
return (static_cast<unsigned>(lhsKind) < static_cast<unsigned>(rhsKind))
|
|
? Ordering::Before
|
|
: Ordering::After;
|
|
}
|
|
|
|
// We shouldn't get two synthesized conformances. It's not harmful
|
|
// per se, but it's indicative of redundant logic in the frontend.
|
|
assert(lhsKind != ConformanceEntryKind::Synthesized &&
|
|
"Shouldn't ever get two synthesized conformances");
|
|
|
|
// FIXME: Deterministic ordering.
|
|
return Ordering::Before;
|
|
}
|
|
|
|
// Both the left- and right-hand sides are implied, so use the
|
|
// stated explicit conformances to determine where the conformance
|
|
// should go.
|
|
assert(lhsKind == ConformanceEntryKind::Implied &&
|
|
"Expected implied conformance");
|
|
assert(rhsKind == ConformanceEntryKind::Implied &&
|
|
"Expected implied conformance");
|
|
auto lhsExplicit = lhs->getDeclaredConformance();
|
|
auto lhsExplicitProtocol = lhsExplicit->getProtocol();
|
|
auto rhsExplicit = rhs->getDeclaredConformance();
|
|
auto rhsExplicitProtocol = rhsExplicit->getProtocol();
|
|
if (lhsExplicitProtocol != rhsExplicitProtocol) {
|
|
// If the explicit protocol for the left-hand side is implied by
|
|
// the explicit protocol for the right-hand side, the left-hand
|
|
// side supersedes the right-hand side.
|
|
SmallVector<ProtocolDecl *, 4> rhsProtocols;
|
|
for (auto rhsProtocol : rhsExplicitProtocol->getAllProtocols(nullptr,
|
|
rhsProtocols)){
|
|
if (rhsProtocol == lhsExplicitProtocol) {
|
|
diagnoseSuperseded = false;
|
|
return Ordering::Before;
|
|
}
|
|
}
|
|
rhsProtocols.clear();
|
|
|
|
// If the explicit protocol for the right-hand side is implied by
|
|
// the explicit protocol for the left-hand side, the right-hand
|
|
// side supersedes the left-hand side.
|
|
SmallVector<ProtocolDecl *, 4> lhsProtocols;
|
|
for (auto lhsProtocol : lhsExplicitProtocol->getAllProtocols(nullptr,
|
|
lhsProtocols)){
|
|
if (lhsProtocol == rhsExplicitProtocol) {
|
|
diagnoseSuperseded = false;
|
|
return Ordering::After;
|
|
}
|
|
}
|
|
|
|
// There is no ordering between the two explicit conformances. If
|
|
// they land in different contexts, diagnose the problem.
|
|
diagnoseSuperseded = lhs->getDeclContext() != rhs->getDeclContext();
|
|
}
|
|
|
|
// If we get here when lhsExplicitProtocol == rhsExplicitProtocol,
|
|
// supersede without diagnosing. Either it's well-formed because the
|
|
// two implicit conformances come from exactly the same explicit
|
|
// conformance (e.g., due to the protocol inherance graph being a
|
|
// DAG rather than a tree) or we will already be diagnosing the
|
|
// redundant explicit conformance, and don't want to introduce
|
|
// redundant diagnostics.
|
|
|
|
// FIXME: Deterministic ordering.
|
|
return Ordering::Before;
|
|
}
|
|
|
|
bool ConformanceLookupTable::resolveConformances(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol,
|
|
LazyResolver *resolver) {
|
|
// Find any entries that are superseded by other entries.
|
|
ConformanceEntries &entries = Conformances[protocol];
|
|
llvm::SmallPtrSet<DeclContext *, 4> knownConformances;
|
|
bool anySuperseded = false;
|
|
for (auto entry : entries) {
|
|
// If this entry has a conformance associated with it, note that.
|
|
if (entry->getConformance())
|
|
knownConformances.insert(entry->getDeclContext());
|
|
|
|
// If this entry was superseded, move on.
|
|
if (entry->isSuperseded()) {
|
|
anySuperseded = true;
|
|
continue;
|
|
}
|
|
|
|
// Determine whether this entry is superseded by (or supersedes)
|
|
// some other entry.
|
|
for (auto otherEntry : entries) {
|
|
if (entry == otherEntry)
|
|
continue;
|
|
|
|
if (otherEntry->isSuperseded()) {
|
|
anySuperseded = true;
|
|
continue;
|
|
}
|
|
|
|
bool diagnoseSuperseded = false;
|
|
bool doneWithEntry = false;
|
|
switch (compareConformances(entry, otherEntry, diagnoseSuperseded)) {
|
|
case Ordering::Equivalent:
|
|
break;
|
|
|
|
case Ordering::Before:
|
|
otherEntry->markSupersededBy(*this, entry, diagnoseSuperseded);
|
|
anySuperseded = true;
|
|
break;
|
|
|
|
case Ordering::After:
|
|
entry->markSupersededBy(*this, otherEntry, diagnoseSuperseded);
|
|
anySuperseded = true;
|
|
doneWithEntry = true;
|
|
break;
|
|
}
|
|
|
|
if (doneWithEntry)
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If any entries were superseded, remove them now.
|
|
if (anySuperseded) {
|
|
entries.erase(std::remove_if(entries.begin(), entries.end(),
|
|
[&](ConformanceEntry *entry) {
|
|
return entry->isSuperseded();
|
|
}),
|
|
entries.end());
|
|
}
|
|
|
|
// FIXME: Assign protocol conformances for any remaining entries
|
|
// that do not have them.
|
|
|
|
return anySuperseded;
|
|
}
|
|
|
|
void ConformanceLookupTable::addSynthesizedConformance(NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol) {
|
|
addProtocol(nominal, protocol, nominal->getLoc(),
|
|
ConformanceSource::forSynthesized(nominal));
|
|
}
|
|
|
|
bool ConformanceLookupTable::lookupConformance(
|
|
Module *module,
|
|
NominalTypeDecl *nominal,
|
|
ProtocolDecl *protocol,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances) {
|
|
// Update to record all explicit and inherited conformances.
|
|
updateLookupTable(nominal, ConformanceStage::Inherited, resolver);
|
|
|
|
// Look for conformances to this protocol.
|
|
auto known = Conformances.find(protocol);
|
|
if (known == Conformances.end()) {
|
|
// If we didn't find anything, expand implied conformances.
|
|
updateLookupTable(nominal, ConformanceStage::ExpandedImplied, resolver);
|
|
known = Conformances.find(protocol);
|
|
|
|
// We didn't find anything.
|
|
if (known == Conformances.end())
|
|
return false;
|
|
}
|
|
|
|
// Resolve the conformances for this protocol.
|
|
resolveConformances(nominal, protocol, resolver);
|
|
for (auto entry : Conformances[protocol]) {
|
|
// FIXME: Eventually, we'll always build the conformance.
|
|
if (entry->getConformance()) {
|
|
conformances.push_back(entry->getConformance());
|
|
}
|
|
}
|
|
return !conformances.empty();
|
|
}
|
|
|
|
void ConformanceLookupTable::lookupConformances(
|
|
NominalTypeDecl *nominal,
|
|
DeclContext *dc,
|
|
LazyResolver *resolver,
|
|
ConformanceLookupKind lookupKind,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances,
|
|
SmallVectorImpl<ConformanceDiagnostic> *diagnostics) {
|
|
// We need to expand all implied conformances before we can find
|
|
// those conformances that pertain to this declaration context.
|
|
updateLookupTable(nominal, ConformanceStage::ExpandedImplied, resolver);
|
|
|
|
/// Resolve conformances for each of the protocols to which this
|
|
/// declaration may provide a conformance. Only some of these will
|
|
/// result in conformances that are attributed to this declaration
|
|
/// context.
|
|
auto &potentialConformances = AllConformances[dc];
|
|
for (const auto &potential : potentialConformances) {
|
|
resolveConformances(nominal, potential->getProtocol(), resolver);
|
|
}
|
|
|
|
// Remove any superseded conformances from AllConformances.
|
|
potentialConformances.erase(
|
|
std::remove_if(potentialConformances.begin(),
|
|
potentialConformances.end(),
|
|
[&](ConformanceEntry *entry) {
|
|
if (entry->isSuperseded())
|
|
return true;
|
|
|
|
if (lookupKind == ConformanceLookupKind::OnlyExplicit &&
|
|
entry->getKind() != ConformanceEntryKind::Explicit)
|
|
return true;
|
|
|
|
// FIXME: Eventually, we'll always have a conformance.
|
|
if (entry->getConformance())
|
|
conformances.push_back(entry->getConformance());
|
|
return false;
|
|
}),
|
|
potentialConformances.end());
|
|
|
|
// Gather any diagnostics we've produced.
|
|
if (diagnostics) {
|
|
auto knownDiags = AllSupersededDiagnostics.find(dc);
|
|
if (knownDiags != AllSupersededDiagnostics.end()) {
|
|
for (const auto *entry : knownDiags->second) {
|
|
ConformanceEntry *supersededBy = entry->getSupersededBy();
|
|
|
|
diagnostics->push_back({entry->getProtocol(),
|
|
entry->getDeclaredLoc(),
|
|
entry->getKind(),
|
|
entry->getDeclaredConformance()->getProtocol(),
|
|
supersededBy->getDeclContext(),
|
|
supersededBy->getKind(),
|
|
supersededBy->getDeclaredConformance()
|
|
->getProtocol()});
|
|
}
|
|
|
|
// We have transferred these diagnostics; erase them.
|
|
AllSupersededDiagnostics.erase(knownDiags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConformanceLookupTable::getAllProtocols(
|
|
NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolDecl *> &scratch) {
|
|
// We need to expand all implied conformances to find the complete
|
|
// set of protocols to which this nominal type conforms.
|
|
updateLookupTable(nominal, ConformanceStage::ExpandedImplied, resolver);
|
|
|
|
// Gather all of the protocols.
|
|
for (const auto &conformance : Conformances) {
|
|
if (conformance.second.empty())
|
|
continue;
|
|
|
|
scratch.push_back(conformance.first);
|
|
}
|
|
|
|
// FIXME: sort the protocols in some canonical order?
|
|
}
|
|
|
|
void ConformanceLookupTable::getAllConformances(
|
|
NominalTypeDecl *nominal,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &scratch) {
|
|
// We need to expand and resolve all conformances to enumerate them.
|
|
updateLookupTable(nominal, ConformanceStage::Resolved, resolver);
|
|
|
|
// Gather all of the protocols.
|
|
for (const auto &conformance : AllConformances) {
|
|
for (auto entry : conformance.second) {
|
|
// FIXME: Eventually, we'll always have conformances.
|
|
if (entry->getConformance())
|
|
scratch.push_back(entry->getConformance());
|
|
}
|
|
}
|
|
|
|
// FIXME: sort the conformances in some canonical order?
|
|
}
|
|
|
|
void ConformanceLookupTable::dump() const {
|
|
dump(llvm::errs());
|
|
}
|
|
|
|
void ConformanceLookupTable::dump(raw_ostream &os) const {
|
|
for (const auto &dcEntries : AllConformances) {
|
|
os << "Conformances in context:\n";
|
|
dcEntries.first->printContext(os);
|
|
for (auto entry : dcEntries.second) {
|
|
entry->dump(os);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NominalTypeDecl::prepareConformanceTable(LazyResolver *resolver) const {
|
|
if (ConformanceTable)
|
|
return;
|
|
|
|
auto mutableThis = const_cast<NominalTypeDecl *>(this);
|
|
ASTContext &ctx = getASTContext();
|
|
ConformanceTable = new (ctx) ConformanceLookupTable(ctx, mutableThis,
|
|
resolver);
|
|
|
|
// If this type declaration was not parsed from source code or introduced
|
|
// via the Clang importer, don't add any synthesized conformances.
|
|
if (!getParentSourceFile() && !hasClangNode())
|
|
return;
|
|
|
|
// Add any synthesized conformances.
|
|
if (isa<ClassDecl>(this)) {
|
|
if (auto anyObject = getASTContext().getProtocol(
|
|
KnownProtocolKind::AnyObject)) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis, anyObject);
|
|
}
|
|
} else if (auto theEnum = dyn_cast<EnumDecl>(mutableThis)) {
|
|
if (theEnum->isSimpleEnum()) {
|
|
// Simple enumerations conform to Equatable and Hashable.
|
|
if (auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable)) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis, equatable);
|
|
}
|
|
|
|
// Simple enumerations conform to Equatable and Hashable.
|
|
if (auto hashable = getASTContext().getProtocol(
|
|
KnownProtocolKind::Hashable)) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis, hashable);
|
|
}
|
|
}
|
|
|
|
// Enumerations with a raw type conform to RawRepresentable.
|
|
if (resolver)
|
|
resolver->resolveRawType(theEnum);
|
|
if (theEnum->hasRawType()) {
|
|
// Simple enumerations conform to Equatable and Hashable.
|
|
if (auto rawRepresentable = getASTContext().getProtocol(
|
|
KnownProtocolKind::RawRepresentable)) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis,
|
|
rawRepresentable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void NominalTypeDecl::registerSynthesizedConformance(
|
|
ProtocolDecl *protocol,
|
|
LazyResolver *resolver) {
|
|
prepareConformanceTable(resolver);
|
|
ConformanceTable->addSynthesizedConformance(this, protocol);
|
|
}
|
|
|
|
bool NominalTypeDecl::lookupConformance(
|
|
Module *module, ProtocolDecl *protocol,
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances) const {
|
|
prepareConformanceTable(resolver);
|
|
return ConformanceTable->lookupConformance(
|
|
module,
|
|
const_cast<NominalTypeDecl *>(this),
|
|
protocol,
|
|
resolver,
|
|
conformances);
|
|
}
|
|
|
|
ArrayRef<ProtocolDecl *> NominalTypeDecl::getAllProtocols(
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolDecl *> &scratch) const {
|
|
prepareConformanceTable(resolver);
|
|
scratch.clear();
|
|
ConformanceTable->getAllProtocols(const_cast<NominalTypeDecl *>(this),
|
|
resolver,
|
|
scratch);
|
|
return scratch;
|
|
}
|
|
|
|
ArrayRef<ProtocolConformance *>
|
|
NominalTypeDecl::getAllConformances(
|
|
LazyResolver *resolver,
|
|
SmallVectorImpl<ProtocolConformance *> &scratch) const
|
|
{
|
|
prepareConformanceTable(resolver);
|
|
scratch.clear();
|
|
ConformanceTable->getAllConformances(const_cast<NominalTypeDecl *>(this),
|
|
resolver,
|
|
scratch);
|
|
return scratch;
|
|
}
|
|
|
|
|
|
ArrayRef<ProtocolConformance *>
|
|
DeclContext::getLocalConformances(
|
|
LazyResolver *resolver,
|
|
ConformanceLookupKind lookupKind,
|
|
SmallVectorImpl<ProtocolConformance *> &scratch,
|
|
SmallVectorImpl<ConformanceDiagnostic> *diagnostics) const
|
|
{
|
|
// Dig out the nominal type.
|
|
NominalTypeDecl *nominal = isNominalTypeOrNominalTypeExtensionContext();
|
|
if (!nominal)
|
|
return { };
|
|
|
|
// Update to record all potential conformances.
|
|
nominal->prepareConformanceTable(resolver);
|
|
scratch.clear();
|
|
nominal->ConformanceTable->lookupConformances(
|
|
nominal,
|
|
const_cast<DeclContext *>(this),
|
|
resolver,
|
|
lookupKind,
|
|
scratch,
|
|
diagnostics);
|
|
return scratch;
|
|
}
|