blob: d197b1ca7dd42be458638325bf7b8edc9d8e2fa2 [file] [log] [blame]
//===--- 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.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_IRGEN_PROTOCOLINFO_H
#define SWIFT_IRGEN_PROTOCOLINFO_H
#include "swift/AST/Decl.h"
#include "ValueWitness.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/TrailingObjects.h"
namespace swift {
class CanType;
class Decl;
class ProtocolConformance;
class ProtocolDecl;
namespace irgen {
class ConformanceInfo; // private to GenProto.cpp
class IRGenModule;
class TypeInfo;
/// A class which encapsulates an index into a witness table.
class WitnessIndex {
unsigned Value : 31;
unsigned IsPrefix : 1;
public:
WitnessIndex() = default;
WitnessIndex(ValueWitness index) : Value(unsigned(index)) {}
explicit WitnessIndex(unsigned index, bool isPrefix)
: Value(index), IsPrefix(isPrefix) {}
unsigned getValue() const { return Value; }
bool isPrefix() const { return IsPrefix; }
};
/// A witness to a specific element of a protocol. Every
/// ProtocolTypeInfo stores one of these for each declaration in the
/// protocol.
///
/// The structure of a witness varies by the type of declaration:
/// - a function requires a single witness, the function;
/// - a variable requires two witnesses, a getter and a setter;
/// - a subscript requires two witnesses, a getter and a setter;
/// - a type requires a pointer to the metadata for that type and
/// to witness tables for each of the protocols it obeys.
class WitnessTableEntry {
Decl *Member;
WitnessIndex BeginIndex;
WitnessTableEntry(Decl *member, WitnessIndex begin)
: Member(member), BeginIndex(begin) {}
public:
WitnessTableEntry() = default;
Decl *getMember() const {
return Member;
}
static WitnessTableEntry forPrefixBase(ProtocolDecl *proto) {
return WitnessTableEntry(proto, WitnessIndex(0, /*isPrefix=*/ true));
}
static WitnessTableEntry forOutOfLineBase(ProtocolDecl *proto,
WitnessIndex index) {
return WitnessTableEntry(proto, index);
}
/// Is this a base-protocol entry?
bool isBase() const { return isa<ProtocolDecl>(Member); }
/// Is the table for this base-protocol entry "out of line",
/// i.e. there is a witness which indirectly points to it, or is
/// it a prefix of the layout of this protocol?
bool isOutOfLineBase() const {
assert(isBase());
return !BeginIndex.isPrefix();
}
/// Return the index at which to find the table for this
/// base-protocol entry.
WitnessIndex getOutOfLineBaseIndex() const {
assert(isOutOfLineBase());
return BeginIndex;
}
static WitnessTableEntry forFunction(AbstractFunctionDecl *func,
WitnessIndex index) {
return WitnessTableEntry(func, index);
}
bool isFunction() const { return isa<AbstractFunctionDecl>(Member); }
WitnessIndex getFunctionIndex() const {
assert(isFunction());
return BeginIndex;
}
static WitnessTableEntry forAssociatedType(AssociatedTypeDecl *ty,
WitnessIndex index) {
return WitnessTableEntry(ty, index);
}
bool isAssociatedType() const { return isa<AssociatedTypeDecl>(Member); }
WitnessIndex getAssociatedTypeIndex() const {
assert(isAssociatedType());
return BeginIndex;
}
WitnessIndex
getAssociatedTypeWitnessTableIndex(ProtocolDecl *target) const {
assert(!BeginIndex.isPrefix());
auto index = BeginIndex.getValue() + 1;
for (auto protocol :
cast<AssociatedTypeDecl>(Member)->getConformingProtocols()) {
if (protocol == target) {
return WitnessIndex(index, false);
}
index++;
}
llvm_unreachable("protocol not in direct conformance list?");
}
};
/// An abstract description of a protocol.
class ProtocolInfo final :
private llvm::TrailingObjects<ProtocolInfo, WitnessTableEntry> {
friend TrailingObjects;
/// A singly-linked-list of all the protocols that have been laid out.
const ProtocolInfo *NextConverted;
friend class TypeConverter;
/// The number of witnesses in the protocol.
unsigned NumWitnesses;
/// The number of table entries in this protocol layout.
unsigned NumTableEntries;
/// A table of all the conformances we've needed so far for this
/// protocol. We expect this to be quite small for most protocols.
mutable llvm::SmallDenseMap<const ProtocolConformance*, ConformanceInfo*, 2>
Conformances;
ProtocolInfo(unsigned numWitnesses, ArrayRef<WitnessTableEntry> table)
: NumWitnesses(numWitnesses), NumTableEntries(table.size()) {
std::uninitialized_copy(table.begin(), table.end(),
getTrailingObjects<WitnessTableEntry>());
}
static ProtocolInfo *create(unsigned numWitnesses,
ArrayRef<WitnessTableEntry> table);
public:
const ConformanceInfo &getConformance(IRGenModule &IGM,
ProtocolDecl *protocol,
const ProtocolConformance *conf) const;
/// The number of witness slots in a conformance to this protocol;
/// in other words, the size of the table in words.
unsigned getNumWitnesses() const {
return NumWitnesses;
}
ArrayRef<WitnessTableEntry> getWitnessEntries() const {
return {getTrailingObjects<WitnessTableEntry>(), NumTableEntries};
}
const WitnessTableEntry &getWitnessEntry(Decl *member) const {
// FIXME: do a binary search if the number of witnesses is large
// enough.
for (auto &witness : getWitnessEntries())
if (witness.getMember() == member)
return witness;
llvm_unreachable("didn't find entry for member!");
}
~ProtocolInfo();
};
} // end namespace irgen
} // end namespace swift
#endif