blob: bcd3f5cd961b1e41bc3a3cfa26fb055732cd2033 [file] [log] [blame]
//===--- ConformanceLookupTable - Conformance Lookup Table ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ConformanceLookupTable class, which manages protocol
// conformances for a given nominal type. Most clients should not access this
// table directly; rather, they should go through the NominalTypeDecl or
// DeclContext entry points.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_CONFORMANCE_LOOKUP_TABLE_H
#define SWIFT_AST_CONFORMANCE_LOOKUP_TABLE_H
#include "swift/AST/DeclContext.h"
#include "swift/AST/TypeLoc.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/SourceLoc.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SetVector.h"
#include <unordered_map>
namespace swift {
class ExtensionDecl;
class ModuleDecl;
/// Keeps track of the protocols to which a particular nominal type conforms.
///
/// This table is a lower-level detail that clients should generally not
/// access directly. Rather, one should use the protocol- and
/// conformance-centric entry points in \c NominalTypeDecl and \c DeclContext.
class ConformanceLookupTable {
/// Describes the stage at which a particular nominal type or
/// extension's conformances has been processed.
enum class ConformanceStage : uint8_t {
/// 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 each
/// nominal type and list of extensions for each stage of
/// conformance checking.
///
/// Uses std::unordered_map instead of DenseMap so that stable interior
/// references can be taken.
std::unordered_map<NominalTypeDecl *,
std::array<LastProcessedEntry, NumConformanceStages>>
LastProcessed;
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(); }
/// Retrieve kind of the conformance for ranking purposes.
///
/// The only difference between the ranking kind and the kind is
/// that implied conformances originating from a synthesized
/// conformance are considered to be synthesized (which has a
/// lower ranking).
ConformanceEntryKind getRankingKind() const {
switch (auto kind = getKind()) {
case ConformanceEntryKind::Explicit:
case ConformanceEntryKind::Inherited:
case ConformanceEntryKind::Synthesized:
return kind;
case ConformanceEntryKind::Implied:
return (getImpliedSource()->getDeclaredConformance()->getKind()
== ConformanceEntryKind::Synthesized)
? ConformanceEntryKind::Synthesized
: ConformanceEntryKind::Implied;
}
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
}
/// 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;
}
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
}
/// 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();
}
/// Determine the kind of conformance for ranking purposes.
ConformanceEntryKind getRankingKind() const {
return Source.getRankingKind();
}
/// 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;
/// 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));
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::MapVector<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::MapVector<DeclContext *, SmallVector<ConformanceEntry *, 4>>
AllConformances;
/// The complete set of diagnostics about erroneously superseded
/// protocol conformances.
llvm::SmallDenseMap<DeclContext *, std::vector<ConformanceEntry *> >
AllSupersededDiagnostics;
/// Associates a conforming decl to its protocol conformance decls.
llvm::DenseMap<const ValueDecl *, llvm::TinyPtrVector<ValueDecl *>>
ConformingDeclMap;
/// Indicates whether we are visiting the superclass.
bool VisitingSuperclass = false;
/// Add a protocol.
bool addProtocol(ProtocolDecl *protocol, SourceLoc loc,
ConformanceSource source);
/// Add the protocols from the given list.
void addInheritedProtocols(
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
ConformanceSource source);
/// Expand the implied conformances for the given DeclContext.
void expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc);
/// 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(ProtocolDecl *protocol);
/// Retrieve the declaration context that provides the
/// (non-inherited) conformance described by the given conformance
/// entry.
DeclContext *getConformingContext(NominalTypeDecl *nominal,
ConformanceEntry *entry);
/// Resolve the given conformance entry to an actual protocol conformance.
ProtocolConformance *getConformance(NominalTypeDecl *nominal,
ConformanceEntry *entry);
/// 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,
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);
/// Update a lookup table with conformances from newly-added extensions.
void updateLookupTable(NominalTypeDecl *nominal, ConformanceStage stage);
/// Load all of the protocol conformances for the given (serialized)
/// declaration context.
void loadAllConformances(DeclContext *dc,
ArrayRef<ProtocolConformance *> conformances);
public:
/// Create a new conformance lookup table.
ConformanceLookupTable(ASTContext &ctx);
/// Destroy the conformance table.
void destroy();
/// Add a synthesized conformance to the lookup table.
void addSynthesizedConformance(NominalTypeDecl *nominal,
ProtocolDecl *protocol);
/// Register an externally-supplied protocol conformance.
void registerProtocolConformance(ProtocolConformance *conformance,
bool synthesized = false);
/// 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(ModuleDecl *module,
NominalTypeDecl *nominal,
ProtocolDecl *protocol,
SmallVectorImpl<ProtocolConformance *> &conformances);
/// Look for all of the conformances within the given declaration context.
void lookupConformances(NominalTypeDecl *nominal,
DeclContext *dc,
ConformanceLookupKind lookupKind,
SmallVectorImpl<ProtocolDecl *> *protocols,
SmallVectorImpl<ProtocolConformance *> *conformances,
SmallVectorImpl<ConformanceDiagnostic> *diagnostics);
/// Retrieve the complete set of protocols to which this nominal
/// type conforms.
void getAllProtocols(NominalTypeDecl *nominal,
SmallVectorImpl<ProtocolDecl *> &scratch);
/// Retrieve the complete set of protocol conformances for this
/// nominal type.
void getAllConformances(NominalTypeDecl *nominal,
bool sorted,
SmallVectorImpl<ProtocolConformance *> &scratch);
/// Retrieve the protocols that would be implicitly synthesized.
/// FIXME: This is a hack, because it's the wrong question to ask. It
/// skips over the possibility that there is an explicit conformance
/// somewhere.
void getImplicitProtocols(NominalTypeDecl *nominal,
SmallVectorImpl<ProtocolDecl *> &protocols);
/// Returns the protocol requirements that \c Member conforms to.
ArrayRef<ValueDecl *>
getSatisfiedProtocolRequirementsForMember(const ValueDecl *member,
NominalTypeDecl *nominal,
bool sorted);
// 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));
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;
/// Compare two protocol conformances to place them in some canonical order.
static int compareProtocolConformances(ProtocolConformance * const *lhsPtr,
ProtocolConformance * const *rhsPtr);
};
}
#endif /* SWIFT_AST_CONFORMANCE_LOOKUP_TABLE_H */