mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Reimplement the witness matching logic used for generic requirements so that it properly models the expectations required of the witness, then captures the results in the AST. The new approach has a number of advantages over the existing hacks: * The constraint solver no longer requires hacks to try to tangle together the innermost archetypes from the requirement with the outer archetypes of the context of the protocol conformance. Instead, we create a synthetic set of archetypes that describes the requirement as it should be matched against witnesses. This eliminates the infamous 'SelfTypeVar' hack. * The type checker no longer records substitutions involving a weird mix of archetypes from different contexts (see above), so it's actually plausible to reason about the substitutions of a witness. A new `Witness` class contains the declaration, substitutions, and all other information required to interpret the witness. * SILGen now uses the substitution information for witnesses when building witness thunks, rather than computing all of it from scratch. ``substSelfTypeIntoProtocolRequirementType()` is now gone (absorbed into the type checker, and improved from there), and the witness-thunk emission code is simpler. A few other bits of SILGen got simpler because the substitutions can now be trusted. * Witness matching and thunk generation involving generic requirements and nested generics now works, based on some work @slavapestov was already doing in this area. * The AST verifier can now verify the archetypes that occur in witness substitutions. * Although it's not in this commit, the `Witness` structure is suitable for complete (de-)serialization, unlike the weird mix of archetypes previously present. Fixes rdar://problem/24079818 and cleans up an area that's been messy and poorly understood for a very, very long time.
773 lines
28 KiB
C++
773 lines
28 KiB
C++
//===--- ProtocolConformance.cpp - AST Protocol Conformance ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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 "ConformanceLookupTable.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/Substitution.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/TypeWalker.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
|
|
using namespace swift;
|
|
|
|
Witness::Witness(ValueDecl *decl, ArrayRef<Substitution> substitutions,
|
|
GenericSignature *syntheticSig,
|
|
GenericEnvironment *syntheticEnv,
|
|
SubstitutionMap reqToSynthesizedEnvMap) {
|
|
auto &ctx = decl->getASTContext();
|
|
|
|
auto declRef = ConcreteDeclRef(ctx, decl, substitutions);
|
|
auto storedMem = ctx.Allocate(sizeof(StoredWitness), alignof(StoredWitness));
|
|
auto stored =
|
|
new (storedMem) StoredWitness{declRef, syntheticSig, syntheticEnv,
|
|
std::move(reqToSynthesizedEnvMap)};
|
|
ctx.addDestructorCleanup(*stored);
|
|
|
|
storage = stored;
|
|
}
|
|
|
|
void Witness::dump() const { dump(llvm::errs()); }
|
|
|
|
void Witness::dump(llvm::raw_ostream &out) const {
|
|
// FIXME: Implement!
|
|
}
|
|
|
|
ProtocolConformanceRef::ProtocolConformanceRef(ProtocolDecl *protocol,
|
|
ProtocolConformance *conf) {
|
|
assert(protocol != nullptr &&
|
|
"cannot construct ProtocolConformanceRef with null protocol");
|
|
if (conf) {
|
|
assert(protocol == conf->getProtocol() && "protocol conformance mismatch");
|
|
Union = conf;
|
|
} else {
|
|
Union = protocol;
|
|
}
|
|
}
|
|
|
|
ProtocolDecl *ProtocolConformanceRef::getRequirement() const {
|
|
if (isConcrete()) {
|
|
return getConcrete()->getProtocol();
|
|
} else {
|
|
return getAbstract();
|
|
}
|
|
}
|
|
|
|
ProtocolConformanceRef
|
|
ProtocolConformanceRef::getInherited(ProtocolDecl *parent) const {
|
|
assert((getRequirement() == parent ||
|
|
getRequirement()->inheritsFrom(parent)) &&
|
|
"not a parent of this protocol");
|
|
|
|
if (parent == getRequirement())
|
|
return *this;
|
|
|
|
// For an abstract requirement, simply produce a new abstract requirement
|
|
// for the parent.
|
|
if (isAbstract()) {
|
|
return ProtocolConformanceRef(parent);
|
|
}
|
|
|
|
// Navigate concrete conformances.
|
|
if (isConcrete()) {
|
|
return ProtocolConformanceRef(
|
|
getConcrete()->getInheritedConformance(parent));
|
|
}
|
|
|
|
llvm_unreachable("unhandled ProtocolConformanceRef");
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
std::pair<const Substitution &, TypeDecl *>
|
|
ProtocolConformance::getTypeWitnessSubstAndDecl(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessSubstAndDecl,
|
|
(assocType, resolver))
|
|
}
|
|
|
|
const Substitution &
|
|
ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
return getTypeWitnessSubstAndDecl(assocType, resolver).first;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
Witness 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(AssociatedTypeDecl *requirement) const {
|
|
CONFORMANCE_SUBCLASS_DISPATCH(usesDefaultDefinition, (requirement))
|
|
}
|
|
|
|
GenericEnvironment *ProtocolConformance::getGenericEnvironment() 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()->getGenericEnvironmentOfContext();
|
|
|
|
case ProtocolConformanceKind::Specialized:
|
|
// If we have a specialized protocol conformance, since we do not support
|
|
// currently partial specialization, we know that it cannot have any open
|
|
// type variables.
|
|
//
|
|
// FIXME: We could return a meaningful GenericEnvironment here
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
GenericSignature *ProtocolConformance::getGenericSignature() const {
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Inherited:
|
|
case ProtocolConformanceKind::Normal:
|
|
// If we have a normal or inherited protocol conformance, look for its
|
|
// generic signature.
|
|
return getDeclContext()->getGenericSignatureOfContext();
|
|
|
|
case ProtocolConformanceKind::Specialized:
|
|
// If we have a specialized protocol conformance, since we do not support
|
|
// currently partial specialization, we know that it cannot have any open
|
|
// type variables.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool ProtocolConformance::isBehaviorConformance() const {
|
|
return getRootNormalConformance()->isBehaviorConformance();
|
|
}
|
|
|
|
AbstractStorageDecl *ProtocolConformance::getBehaviorDecl() const {
|
|
return getRootNormalConformance()->getBehaviorDecl();
|
|
}
|
|
|
|
void NormalProtocolConformance::resolveLazyInfo() const {
|
|
assert(Resolver);
|
|
assert(isComplete());
|
|
|
|
auto *resolver = Resolver;
|
|
auto *mutableThis = const_cast<NormalProtocolConformance *>(this);
|
|
mutableThis->Resolver = nullptr;
|
|
mutableThis->setState(ProtocolConformanceState::Incomplete);
|
|
resolver->finishNormalConformance(mutableThis, ResolverContextData);
|
|
mutableThis->setState(ProtocolConformanceState::Complete);
|
|
}
|
|
|
|
void NormalProtocolConformance::setLazyLoader(LazyMemberLoader *resolver,
|
|
uint64_t contextData) {
|
|
assert(!Resolver && "already has a resolver");
|
|
Resolver = resolver;
|
|
ResolverContextData = contextData;
|
|
}
|
|
|
|
bool NormalProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
if (Resolver)
|
|
resolveLazyInfo();
|
|
|
|
if (TypeWitnesses.find(assocType) != TypeWitnesses.end()) {
|
|
return true;
|
|
}
|
|
if (resolver) {
|
|
resolver->resolveTypeWitness(this, assocType);
|
|
if (TypeWitnesses.find(assocType) != TypeWitnesses.end()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::pair<const Substitution &, TypeDecl *>
|
|
NormalProtocolConformance::getTypeWitnessSubstAndDecl(
|
|
AssociatedTypeDecl *assocType,
|
|
LazyResolver *resolver) const {
|
|
if (Resolver)
|
|
resolveLazyInfo();
|
|
|
|
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,
|
|
TypeDecl *typeDecl) const {
|
|
assert(getProtocol() == cast<ProtocolDecl>(assocType->getDeclContext()) &&
|
|
"associated type in wrong protocol");
|
|
assert(TypeWitnesses.count(assocType) == 0 && "Type witness already known");
|
|
assert((!isComplete() || isInvalid()) && "Conformance already complete?");
|
|
TypeWitnesses[assocType] = std::make_pair(substitution, typeDecl);
|
|
}
|
|
|
|
/// Retrieve the value witness corresponding to the given requirement.
|
|
Witness NormalProtocolConformance::getWitness(ValueDecl *requirement,
|
|
LazyResolver *resolver) const {
|
|
assert(!isa<AssociatedTypeDecl>(requirement) && "Request type witness");
|
|
if (Resolver)
|
|
resolveLazyInfo();
|
|
|
|
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() || isInvalid()) &&
|
|
"Resolver did not resolve requirement");
|
|
return Witness();
|
|
}
|
|
}
|
|
|
|
void NormalProtocolConformance::setWitness(ValueDecl *requirement,
|
|
Witness 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() || isInvalid()) && "Conformance already complete?");
|
|
Mapping[requirement] = witness;
|
|
}
|
|
|
|
SpecializedProtocolConformance::SpecializedProtocolConformance(
|
|
Type conformingType,
|
|
ProtocolConformance *genericConformance,
|
|
ArrayRef<Substitution> substitutions)
|
|
: ProtocolConformance(ProtocolConformanceKind::Specialized, conformingType,
|
|
// FIXME: interface type should be passed in.
|
|
// assumes specialized conformance is always fully
|
|
// 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);
|
|
}
|
|
|
|
std::pair<const Substitution &, TypeDecl *>
|
|
SpecializedProtocolConformance::getTypeWitnessSubstAndDecl(
|
|
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.
|
|
auto conformingDC = getDeclContext();
|
|
auto conformingModule = conformingDC->getParentModule();
|
|
|
|
auto *genericEnv = GenericConformance->getGenericEnvironment();
|
|
auto *genericSig = GenericConformance->getGenericSignature();
|
|
|
|
auto substitutionMap = genericEnv->getSubstitutionMap(
|
|
conformingModule, genericSig, GenericSubstitutions);
|
|
|
|
auto genericWitnessAndDecl
|
|
= GenericConformance->getTypeWitnessSubstAndDecl(assocType, resolver);
|
|
|
|
auto &genericWitness = genericWitnessAndDecl.first;
|
|
auto *typeDecl = genericWitnessAndDecl.second;
|
|
|
|
// Apply the substitution we computed above
|
|
auto specializedType
|
|
= genericWitness.getReplacement().subst(substitutionMap, None);
|
|
|
|
// If the type witness was unchanged, just copy it directly.
|
|
if (specializedType.getPointer() == genericWitness.getReplacement().getPointer()) {
|
|
TypeWitnesses[assocType] = genericWitnessAndDecl;
|
|
return TypeWitnesses[assocType];
|
|
}
|
|
|
|
// Gather the conformances for the type witness. These should never fail.
|
|
// FIXME: We should just be able to use the SubstitutionMap from above,
|
|
// but we have no way to force inherited conformances to be filled in
|
|
// through that mechanism.
|
|
SmallVector<ProtocolConformanceRef, 4> conformances;
|
|
for (auto proto : assocType->getConformingProtocols()) {
|
|
auto conforms = conformingModule->lookupConformance(specializedType, proto,
|
|
resolver);
|
|
assert((conforms ||
|
|
specializedType->isTypeVariableOrMember() ||
|
|
specializedType->isTypeParameter() ||
|
|
specializedType->hasError() ||
|
|
specializedType->getCanonicalType()->hasError()) &&
|
|
"Improperly checked substitution");
|
|
conformances.push_back(conforms ? *conforms
|
|
: ProtocolConformanceRef(proto));
|
|
}
|
|
|
|
// Form the substitution.
|
|
auto &ctx = assocType->getASTContext();
|
|
TypeWitnesses[assocType] = std::make_pair(
|
|
Substitution{specializedType,
|
|
ctx.AllocateCopy(conformances)},
|
|
typeDecl);
|
|
return TypeWitnesses[assocType];
|
|
}
|
|
|
|
Witness
|
|
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);
|
|
}
|
|
|
|
bool ProtocolConformance::isVisibleFrom(const DeclContext *dc) const {
|
|
// FIXME: Implement me!
|
|
return true;
|
|
}
|
|
|
|
ProtocolConformance *ProtocolConformance::subst(Module *module,
|
|
Type substType,
|
|
const SubstitutionMap &subMap) const {
|
|
if (getType()->isEqual(substType))
|
|
return const_cast<ProtocolConformance *>(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,
|
|
const_cast<ProtocolConformance *>(this),
|
|
substType->gatherAllSubstitutions(module, nullptr));
|
|
}
|
|
assert(substType->isEqual(getType())
|
|
&& "substitution changed non-specialized type?!");
|
|
return const_cast<ProtocolConformance *>(this);
|
|
|
|
case ProtocolConformanceKind::Inherited: {
|
|
// Substitute the base.
|
|
auto inheritedConformance
|
|
= cast<InheritedProtocolConformance>(this)->getInheritedConformance();
|
|
ProtocolConformance *newBase;
|
|
if (inheritedConformance->getType()->isSpecialized()) {
|
|
newBase = inheritedConformance->subst(module, substType, subMap);
|
|
} else {
|
|
newBase = inheritedConformance;
|
|
}
|
|
|
|
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, subMap));
|
|
|
|
auto ctxNewSubs = module->getASTContext().AllocateCopy(newSubs);
|
|
|
|
return module->getASTContext()
|
|
.getSpecializedConformance(substType, spec->getGenericConformance(),
|
|
ctxNewSubs);
|
|
}
|
|
}
|
|
llvm_unreachable("bad ProtocolConformanceKind");
|
|
}
|
|
|
|
ProtocolConformance *
|
|
ProtocolConformance::getInheritedConformance(ProtocolDecl *protocol) const {
|
|
auto &C = getProtocol()->getASTContext();
|
|
// Preserve specialization and class inheritance through this operation by
|
|
// reapplying them to the conformance we find.
|
|
switch (getKind()) {
|
|
case ProtocolConformanceKind::Specialized: {
|
|
auto spec = cast<SpecializedProtocolConformance>(this);
|
|
auto inherited = spec->getGenericConformance()
|
|
->getInheritedConformance(protocol);
|
|
assert(inherited->getType()->isEqual(spec->getGenericConformance()->getType())
|
|
&& "inherited conformance doesn't match type?!");
|
|
|
|
auto subs = spec->getGenericSubstitutions();
|
|
|
|
auto *conformingDC = spec->getDeclContext();
|
|
auto *conformingModule = conformingDC->getParentModule();
|
|
|
|
auto *sig = conformingDC->getGenericSignatureOfContext();
|
|
auto *env = conformingDC->getGenericEnvironmentOfContext();
|
|
|
|
auto subMap = env->getSubstitutionMap(conformingModule, sig, subs);
|
|
|
|
auto r = inherited->subst(conformingModule, getType(), subMap);
|
|
assert(getType()->isEqual(r->getType())
|
|
&& "substitution didn't produce conformance for same type?!");
|
|
return r;
|
|
}
|
|
|
|
case ProtocolConformanceKind::Inherited: {
|
|
auto classInherited = cast<InheritedProtocolConformance>(this);
|
|
auto protoInherited = classInherited->getInheritedConformance()
|
|
->getInheritedConformance(protocol);
|
|
assert(protoInherited->getType()->isEqual(
|
|
classInherited->getInheritedConformance()->getType())
|
|
&& "inherited conformance doesn't match type?!");
|
|
return C.getInheritedConformance(classInherited->getType(),
|
|
protoInherited);
|
|
}
|
|
|
|
case ProtocolConformanceKind::Normal:
|
|
// For a normal conformance, do the inheritance lookup.
|
|
break;
|
|
}
|
|
|
|
// Search for the inherited conformance among our immediate parents.
|
|
auto &inherited = getInheritedConformances();
|
|
auto known = inherited.find(protocol);
|
|
if (known != inherited.end())
|
|
return known->second;
|
|
|
|
// If not there, the inherited conformance must be available through one of
|
|
// our parents.
|
|
for (auto &inheritedMapping : inherited)
|
|
if (inheritedMapping.first->inheritsFrom(protocol))
|
|
return inheritedMapping.second->getInheritedConformance(protocol);
|
|
|
|
// The conformance must not be complete; resolve the inherited conformance
|
|
// and try again.
|
|
assert(!isComplete() && "Missing inherited mapping in conformance");
|
|
assert(C.getLazyResolver() && "Need a lazy resolver");
|
|
return C.getLazyResolver()->resolveInheritedConformance(
|
|
getRootNormalConformance(), protocol);
|
|
}
|
|
|
|
#pragma mark Protocol conformance lookup
|
|
void NominalTypeDecl::prepareConformanceTable() const {
|
|
if (ConformanceTable)
|
|
return;
|
|
|
|
auto mutableThis = const_cast<NominalTypeDecl *>(this);
|
|
ASTContext &ctx = getASTContext();
|
|
auto resolver = ctx.getLazyResolver();
|
|
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->hasOnlyCasesWithoutAssociatedValues()) {
|
|
// Simple enumerations conform to Equatable.
|
|
if (auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable)) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis, equatable);
|
|
}
|
|
|
|
// Simple enumerations conform to 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()) {
|
|
if (auto rawRepresentable = ctx.getProtocol(KnownProtocolKind::RawRepresentable)) {
|
|
|
|
// The presence of a raw type is an explicit declaration that
|
|
// the compiler should derive a RawRepresentable conformance.
|
|
auto conformance = ctx.getConformance(mutableThis->getDeclaredTypeInContext(), rawRepresentable,
|
|
mutableThis->getNameLoc(), mutableThis->getInnermostDeclContext(),
|
|
ProtocolConformanceState::Incomplete);
|
|
ConformanceTable->registerProtocolConformance(conformance);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add protocols for any synthesized protocol attributes.
|
|
for (auto attr : getAttrs()) {
|
|
if (auto synthesizedProto = dyn_cast<SynthesizedProtocolAttr>(attr)) {
|
|
if (auto proto = getASTContext().getProtocol(
|
|
synthesizedProto->getProtocolKind())) {
|
|
ConformanceTable->addSynthesizedConformance(mutableThis, proto);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool NominalTypeDecl::lookupConformance(
|
|
Module *module, ProtocolDecl *protocol,
|
|
SmallVectorImpl<ProtocolConformance *> &conformances) const {
|
|
prepareConformanceTable();
|
|
return ConformanceTable->lookupConformance(
|
|
module,
|
|
const_cast<NominalTypeDecl *>(this),
|
|
protocol,
|
|
getASTContext().getLazyResolver(),
|
|
conformances);
|
|
}
|
|
|
|
SmallVector<ProtocolDecl *, 2> NominalTypeDecl::getAllProtocols() const {
|
|
prepareConformanceTable();
|
|
SmallVector<ProtocolDecl *, 2> result;
|
|
ConformanceTable->getAllProtocols(const_cast<NominalTypeDecl *>(this),
|
|
getASTContext().getLazyResolver(),
|
|
result);
|
|
return result;
|
|
}
|
|
|
|
SmallVector<ProtocolConformance *, 2> NominalTypeDecl::getAllConformances(
|
|
bool sorted) const
|
|
{
|
|
prepareConformanceTable();
|
|
SmallVector<ProtocolConformance *, 2> result;
|
|
ConformanceTable->getAllConformances(const_cast<NominalTypeDecl *>(this),
|
|
getASTContext().getLazyResolver(),
|
|
sorted,
|
|
result);
|
|
return result;
|
|
}
|
|
|
|
void NominalTypeDecl::getImplicitProtocols(
|
|
SmallVectorImpl<ProtocolDecl *> &protocols) {
|
|
prepareConformanceTable();
|
|
ConformanceTable->getImplicitProtocols(this, protocols);
|
|
}
|
|
|
|
void NominalTypeDecl::registerProtocolConformance(
|
|
ProtocolConformance *conformance) {
|
|
prepareConformanceTable();
|
|
ConformanceTable->registerProtocolConformance(conformance);
|
|
}
|
|
|
|
ArrayRef<ValueDecl *>
|
|
NominalTypeDecl::getSatisfiedProtocolRequirementsForMember(
|
|
const ValueDecl *member,
|
|
bool sorted) const {
|
|
assert(member->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext()
|
|
== this);
|
|
assert(!isa<ProtocolDecl>(this));
|
|
prepareConformanceTable();
|
|
return ConformanceTable->getSatisfiedProtocolRequirementsForMember(member,
|
|
const_cast<NominalTypeDecl *>(this),
|
|
getASTContext().getLazyResolver(),
|
|
sorted);
|
|
}
|
|
|
|
SmallVector<ProtocolDecl *, 2>
|
|
DeclContext::getLocalProtocols(
|
|
ConformanceLookupKind lookupKind,
|
|
SmallVectorImpl<ConformanceDiagnostic> *diagnostics,
|
|
bool sorted) const
|
|
{
|
|
SmallVector<ProtocolDecl *, 2> result;
|
|
|
|
// Dig out the nominal type.
|
|
NominalTypeDecl *nominal = getAsNominalTypeOrNominalTypeExtensionContext();
|
|
if (!nominal)
|
|
return result;
|
|
|
|
// Update to record all potential conformances.
|
|
nominal->prepareConformanceTable();
|
|
nominal->ConformanceTable->lookupConformances(
|
|
nominal,
|
|
const_cast<DeclContext *>(this),
|
|
getASTContext().getLazyResolver(),
|
|
lookupKind,
|
|
&result,
|
|
nullptr,
|
|
diagnostics);
|
|
|
|
// Sort if required.
|
|
if (sorted) {
|
|
llvm::array_pod_sort(result.begin(), result.end(),
|
|
&ProtocolType::compareProtocols);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
SmallVector<ProtocolConformance *, 2>
|
|
DeclContext::getLocalConformances(
|
|
ConformanceLookupKind lookupKind,
|
|
SmallVectorImpl<ConformanceDiagnostic> *diagnostics,
|
|
bool sorted) const
|
|
{
|
|
SmallVector<ProtocolConformance *, 2> result;
|
|
|
|
// Dig out the nominal type.
|
|
NominalTypeDecl *nominal = getAsNominalTypeOrNominalTypeExtensionContext();
|
|
if (!nominal)
|
|
return result;
|
|
|
|
// Protocols don't have conformances.
|
|
if (isa<ProtocolDecl>(nominal))
|
|
return { };
|
|
|
|
// Update to record all potential conformances.
|
|
nominal->prepareConformanceTable();
|
|
nominal->ConformanceTable->lookupConformances(
|
|
nominal,
|
|
const_cast<DeclContext *>(this),
|
|
nominal->getASTContext().getLazyResolver(),
|
|
lookupKind,
|
|
nullptr,
|
|
&result,
|
|
diagnostics);
|
|
|
|
// If requested, sort the results.
|
|
if (sorted) {
|
|
llvm::array_pod_sort(result.begin(), result.end(),
|
|
&ConformanceLookupTable::compareProtocolConformances);
|
|
}
|
|
|
|
return result;
|
|
}
|