| //===--- ProtocolInfo.h - Abstract protocol witness layout ------*- 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 types for representing the abstract layout of a |
| // protocol. |
| // |
| // SWIFT_ENABLE_TENSORFLOW (this whole file has been significantly modified) |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_IRGEN_PROTOCOLINFO_H |
| #define SWIFT_IRGEN_PROTOCOLINFO_H |
| |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/ProtocolAssociations.h" |
| |
| #include "swift/IRGen/ValueWitness.h" |
| #include "WitnessIndex.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/Support/TrailingObjects.h" |
| |
| namespace swift { |
| class CanType; |
| class ProtocolConformance; |
| |
| namespace irgen { |
| class IRGenModule; |
| class TypeInfo; |
| |
| /// A witness to a specific element of a protocol. Every |
| /// ProtocolTypeInfo stores one of these for each requirement |
| /// introduced by the protocol. |
| class WitnessTableEntry { |
| enum WitnessKind { |
| PlaceholderKind, |
| OutOfLineBaseKind, |
| MethodKind, |
| AssociatedTypeKind, |
| AssociatedConformanceKind |
| }; |
| |
| struct OutOfLineBaseWitness { |
| ProtocolDecl *Protocol; |
| }; |
| |
| struct MethodWitness { |
| SILDeclRef Witness; |
| }; |
| |
| struct AssociatedTypeWitness { |
| AssociatedTypeDecl *Association; |
| }; |
| |
| struct AssociatedConformanceWitness { |
| TypeBase *AssociatedType; |
| ProtocolDecl *Protocol; |
| }; |
| |
| WitnessKind Kind; |
| union { |
| OutOfLineBaseWitness OutOfLineBaseEntry; |
| MethodWitness MethodEntry; |
| AssociatedTypeWitness AssociatedTypeEntry; |
| AssociatedConformanceWitness AssociatedConformanceEntry; |
| }; |
| |
| WitnessTableEntry(WitnessKind Kind) : Kind(Kind) {} |
| |
| public: |
| static WitnessTableEntry forPlaceholder() { |
| return WitnessTableEntry(WitnessKind::PlaceholderKind); |
| } |
| |
| static WitnessTableEntry forOutOfLineBase(ProtocolDecl *proto) { |
| assert(proto != nullptr); |
| WitnessTableEntry entry(WitnessKind::OutOfLineBaseKind); |
| entry.OutOfLineBaseEntry = {proto}; |
| return entry; |
| } |
| |
| /// Is this a base-protocol entry? |
| bool isBase() const { return Kind == WitnessKind::OutOfLineBaseKind; } |
| |
| bool matchesBase(ProtocolDecl *proto) const { |
| assert(proto != nullptr); |
| return isBase() && OutOfLineBaseEntry.Protocol == proto; |
| } |
| |
| /// Given that this is a base-protocol entry, is the table |
| /// "out of line"? |
| bool isOutOfLineBase() const { |
| assert(isBase()); |
| return true; |
| } |
| |
| ProtocolDecl *getBase() const { |
| assert(isBase()); |
| return OutOfLineBaseEntry.Protocol; |
| } |
| |
| static WitnessTableEntry forFunction(SILDeclRef declRef) { |
| assert(!declRef.isNull()); |
| WitnessTableEntry entry(WitnessKind::MethodKind); |
| entry.MethodEntry = {declRef}; |
| return entry; |
| } |
| |
| bool isFunction() const { |
| return Kind == WitnessKind::MethodKind; |
| } |
| |
| bool matchesFunction(SILDeclRef declRef) const { |
| return isFunction() && MethodEntry.Witness == declRef; |
| } |
| |
| SILDeclRef getFunction() const { |
| assert(isFunction()); |
| return MethodEntry.Witness; |
| } |
| |
| static WitnessTableEntry forAssociatedType(AssociatedType ty) { |
| WitnessTableEntry entry(WitnessKind::AssociatedTypeKind); |
| entry.AssociatedTypeEntry = {ty.getAssociation()}; |
| return entry; |
| } |
| |
| bool isAssociatedType() const { |
| return Kind == WitnessKind::AssociatedTypeKind; |
| } |
| |
| bool matchesAssociatedType(AssociatedType assocType) const { |
| return isAssociatedType() && |
| AssociatedTypeEntry.Association == assocType.getAssociation(); |
| } |
| |
| AssociatedTypeDecl *getAssociatedType() const { |
| assert(isAssociatedType()); |
| return AssociatedTypeEntry.Association; |
| } |
| |
| static WitnessTableEntry forAssociatedConformance( |
| AssociatedConformance conf) { |
| WitnessTableEntry entry(WitnessKind::AssociatedConformanceKind); |
| entry.AssociatedConformanceEntry = {conf.getAssociation().getPointer(), |
| conf.getAssociatedRequirement()}; |
| return entry; |
| } |
| |
| bool isAssociatedConformance() const { |
| return Kind == WitnessKind::AssociatedConformanceKind; |
| } |
| |
| bool matchesAssociatedConformance(const AssociatedConformance &conf) const { |
| return isAssociatedConformance() && |
| AssociatedConformanceEntry.AssociatedType == |
| conf.getAssociation().getPointer() && |
| AssociatedConformanceEntry.Protocol == conf.getAssociatedRequirement(); |
| } |
| |
| CanType getAssociatedConformancePath() const { |
| assert(isAssociatedConformance()); |
| return CanType(AssociatedConformanceEntry.AssociatedType); |
| } |
| |
| ProtocolDecl *getAssociatedConformanceRequirement() const { |
| assert(isAssociatedConformance()); |
| return AssociatedConformanceEntry.Protocol; |
| } |
| |
| friend bool operator==(WitnessTableEntry left, WitnessTableEntry right) { |
| if (left.Kind != right.Kind) |
| return false; |
| switch (left.Kind) { |
| case WitnessKind::PlaceholderKind: |
| return true; |
| case WitnessKind::OutOfLineBaseKind: |
| return left.OutOfLineBaseEntry.Protocol == |
| right.OutOfLineBaseEntry.Protocol; |
| case WitnessKind::MethodKind: |
| return left.MethodEntry.Witness == right.MethodEntry.Witness; |
| case WitnessKind::AssociatedTypeKind: |
| return left.AssociatedTypeEntry.Association == |
| right.AssociatedTypeEntry.Association; |
| case WitnessKind::AssociatedConformanceKind: |
| return left.AssociatedConformanceEntry.AssociatedType == |
| right.AssociatedConformanceEntry.AssociatedType && |
| left.AssociatedConformanceEntry.Protocol == |
| right.AssociatedConformanceEntry.Protocol; |
| } |
| } |
| }; |
| |
| /// Describes the information available in a ProtocolInfo. |
| /// |
| /// Each kind includes the information of the kinds before it. |
| enum class ProtocolInfoKind : uint8_t { |
| RequirementSignature, |
| Full |
| }; |
| |
| /// An abstract description of a protocol. |
| class ProtocolInfo final : |
| private llvm::TrailingObjects<ProtocolInfo, WitnessTableEntry> { |
| friend TrailingObjects; |
| friend class TypeConverter; |
| |
| /// The number of table entries in this protocol layout. |
| unsigned NumTableEntries; |
| |
| ProtocolInfoKind Kind; |
| |
| ProtocolInfoKind getKind() const { |
| return Kind; |
| } |
| |
| ProtocolInfo(ArrayRef<WitnessTableEntry> table, ProtocolInfoKind kind) |
| : NumTableEntries(table.size()), Kind(kind) { |
| std::uninitialized_copy(table.begin(), table.end(), |
| getTrailingObjects<WitnessTableEntry>()); |
| } |
| |
| static std::unique_ptr<ProtocolInfo> create(ArrayRef<WitnessTableEntry> table, |
| ProtocolInfoKind kind); |
| |
| public: |
| /// The number of witness slots in a conformance to this protocol; |
| /// in other words, the size of the table in words. |
| unsigned getNumWitnesses() const { |
| assert(getKind() == ProtocolInfoKind::Full); |
| return NumTableEntries; |
| } |
| |
| /// Return all of the entries in this protocol witness table. |
| /// |
| /// The addresses of the entries in this array can be passed to |
| /// getBaseWitnessIndex/getNonBaseWitnessIndex, below. |
| ArrayRef<WitnessTableEntry> getWitnessEntries() const { |
| return {getTrailingObjects<WitnessTableEntry>(), NumTableEntries}; |
| } |
| |
| /// Given the address of a witness entry from this PI for a base protocol |
| /// conformance, return its witness index. |
| WitnessIndex getBaseWitnessIndex(const WitnessTableEntry *witness) const { |
| assert(witness && witness->isBase()); |
| auto entries = getWitnessEntries(); |
| assert(entries.begin() <= witness && witness < entries.end() && |
| "argument witness entry does not belong to this ProtocolInfo"); |
| if (witness->isOutOfLineBase()) { |
| return WitnessIndex(witness - entries.begin(), false); |
| } else { |
| return WitnessIndex(0, true); |
| } |
| } |
| |
| /// Given the address of a witness entry from this PI for a non-base |
| /// witness, return its witness index. |
| WitnessIndex getNonBaseWitnessIndex(const WitnessTableEntry *witness) const { |
| assert(witness && !witness->isBase()); |
| auto entries = getWitnessEntries(); |
| assert(entries.begin() <= witness && witness < entries.end()); |
| return WitnessIndex(witness - entries.begin(), false); |
| } |
| |
| /// Return the witness index for the protocol conformance pointer |
| /// for the given base protocol requirement. |
| WitnessIndex getBaseIndex(ProtocolDecl *protocol) const { |
| for (auto &witness : getWitnessEntries()) { |
| if (witness.matchesBase(protocol)) |
| return getBaseWitnessIndex(&witness); |
| } |
| llvm_unreachable("didn't find entry for base"); |
| } |
| |
| /// Return the witness index for the witness function for the given |
| /// function requirement. |
| WitnessIndex getFunctionIndex(SILDeclRef declRef) const { |
| assert(getKind() >= ProtocolInfoKind::Full); |
| for (auto &witness : getWitnessEntries()) { |
| if (witness.matchesFunction(declRef)) |
| return getNonBaseWitnessIndex(&witness); |
| } |
| llvm_unreachable("didn't find entry for function"); |
| } |
| |
| /// Return the witness index for the type metadata access function |
| /// for the given associated type. |
| WitnessIndex getAssociatedTypeIndex(IRGenModule &IGM, |
| AssociatedType assocType) const; |
| |
| /// Return the witness index for the protocol witness table access |
| /// function for the given associated protocol conformance. |
| WitnessIndex |
| getAssociatedConformanceIndex(const AssociatedConformance &conf) const { |
| for (auto &witness : getWitnessEntries()) { |
| if (witness.matchesAssociatedConformance(conf)) |
| return getNonBaseWitnessIndex(&witness); |
| } |
| llvm_unreachable("didn't find entry for associated conformance"); |
| } |
| }; |
| |
| /// Detail about how an object conforms to a protocol. |
| class ConformanceInfo { |
| virtual void anchor(); |
| public: |
| virtual ~ConformanceInfo() = default; |
| virtual llvm::Value *getTable(IRGenFunction &IGF, |
| llvm::Value **conformingMetadataCache) const = 0; |
| /// Try to get this table as a constant pointer. This might just |
| /// not be supportable at all. |
| virtual llvm::Constant *tryGetConstantTable(IRGenModule &IGM, |
| CanType conformingType) const = 0; |
| }; |
| |
| } // end namespace irgen |
| } // end namespace swift |
| |
| #endif |