[serialization] Add support for generic requirements.

Same-type requirements aren't tested yet because there's currently no
support for associated types.

This includes an improvement to BCRecordLayout: array elements can be
passed inline, and the static checks that the data count matches the
field count will take this into account.

Swift SVN r5984
This commit is contained in:
Jordan Rose
2013-07-04 00:15:06 +00:00
parent 61efdb5ff7
commit 197b696ce6
4 changed files with 130 additions and 11 deletions

View File

@@ -298,6 +298,17 @@ namespace impl {
out.EmitRecordWithAbbrev(abbrCode, buffer);
}
template <typename BufferTy, typename FirstData, typename ...RestData>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, FirstData firstData,
RestData... restData) {
std::array<FirstData, 1+sizeof...(restData)> arrayData{ {
firstData,
restData...
} };
emit(out, buffer, abbrCode, arrayData);
}
template <typename BufferTy>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, Nothing_t) {
@@ -344,11 +355,47 @@ namespace impl {
template <typename ElementTy, typename DataTy>
static void read(ArrayRef<ElementTy> buffer, DataTy &data) = delete;
};
/// A type trait whose \c type field is the last of its template parameters.
template<typename First, typename ...Rest>
struct last_type {
using type = typename last_type<Rest...>::type;
};
template<typename Last>
struct last_type<Last> {
using type = Last;
};
/// A type trait whose \c value field is \c true if the last type is BCBlob.
template<typename ...Types>
using has_blob = std::is_same<BCBlob, typename last_type<int, Types...>::type>;
/// A type trait whose \c value field is \c true if the given type is a
/// BCArray (of any element kind).
template <typename T>
struct is_array {
private:
template <typename E>
static bool check(BCArray<E> *);
static int check(...);
public:
typedef bool value_type;
static constexpr bool value =
!std::is_same<decltype(check((T*)nullptr)),
decltype(check(false))>::value;
};
/// A type trait whose \c value field is \c true if the last type is a
/// BCArray (of any element kind).
template<typename ...Types>
using has_array = is_array<typename last_type<int, Types...>::type>;
} // end namespace impl
/// Represents a single bitcode record type.
///
/// This class template is meant to be instantiated and then given a name,
/// This class template is meant to be instantiated and then given a name,
/// so that from then on that name can be used
template<typename IDField, typename... Fields>
class BCGenericRecordLayout {
@@ -386,12 +433,15 @@ public:
/// Emit a record identified by \p abbrCode to bitstream reader \p out, using
/// \p buffer for scratch space.
///
/// Note that even fixed arguments must be specified here. Currently, arrays
/// and blobs can only be passed as StringRefs.
/// Note that even fixed arguments must be specified here. Blobs are passed
/// as StringRefs, while arrays can be passed inline, as aggregates, or as
/// pre-encoded StringRef data. Skipped values and empty arrays should use
/// the special Nothing value.
template <typename BufferTy, typename... Data>
static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, unsigned recordID, Data... data) {
static_assert(sizeof...(data) <= sizeof...(Fields),
static_assert(sizeof...(data) <= sizeof...(Fields) ||
impl::has_array<Fields...>::value,
"Too many record elements");
static_assert(sizeof...(data) >= sizeof...(Fields),
"Too few record elements");
@@ -407,11 +457,10 @@ public:
/// in the buffer and should be handled separately by the caller.
template <typename BufferTy, typename... Data>
static void readRecord(BufferTy buffer, Data &... data) {
// Weaker bounds checks here: a trailing blob is not decoded through the
// layout.
static_assert(sizeof...(data) <= sizeof...(Fields),
"Too many record elements");
static_assert(sizeof...(data)+1 >= sizeof...(Fields),
static_assert(sizeof...(Fields) <=
sizeof...(data) + impl::has_blob<Fields...>::value,
"Too few record elements");
return impl::BCRecordCoding<Fields...>::read(llvm::makeArrayRef(buffer),
data...);

View File

@@ -216,7 +216,47 @@ GenericParamList *ModuleFile::maybeReadGenericParams(DeclContext *DC) {
}
break;
}
case GENERIC_REQUIREMENT: {
uint8_t rawKind;
ArrayRef<uint64_t> rawTypeIDs;
GenericRequirementLayout::readRecord(scratch, rawKind, rawTypeIDs);
switch (rawKind) {
case GenericRequirementKind::Conformance: {
assert(rawTypeIDs.size() == 2);
TypeLoc subject, constraint;
{
BCOffsetRAII restoreInnerOffset(DeclTypeCursor);
subject = TypeLoc::withoutLoc(getType(rawTypeIDs[0]));
constraint = TypeLoc::withoutLoc(getType(rawTypeIDs[1]));
}
requirements.push_back(Requirement::getConformance(subject,
SourceLoc(),
constraint));
break;
}
case GenericRequirementKind::SameType: {
assert(rawTypeIDs.size() == 2);
TypeLoc first, second;
{
BCOffsetRAII restoreInnerOffset(DeclTypeCursor);
first = TypeLoc::withoutLoc(getType(rawTypeIDs[0]));
second = TypeLoc::withoutLoc(getType(rawTypeIDs[1]));
}
requirements.push_back(Requirement::getSameType(first,
SourceLoc(),
second));
break;
}
default:
// Unknown requirement kind. Drop the requirement and continue, but log
// an error so that we don't actually try to generate code.
error();
}
}
default:
// This record is not part of the GenericParamList.
shouldContinue = false;
break;
}

View File

@@ -86,6 +86,14 @@ enum OperatorKind : uint8_t {
static_assert(sizeof(OperatorKind) <= sizeof(TypeID),
"too many operator kinds");
// These IDs must \em not be renumbered or reordered without incrementing
// VERSION_MAJOR.
enum GenericRequirementKind : uint8_t {
Conformance = 0,
SameType
};
using GenericRequirementKindField = BCFixed<1>;
/// The various types of blocks that can occur within a serialized Swift
/// module.
///
@@ -425,6 +433,12 @@ namespace decls_block {
DeclIDField // Typealias
>;
using GenericRequirementLayout = BCRecordLayout<
GENERIC_REQUIREMENT,
GenericRequirementKindField, // requirement kind
BCArray<TypeIDField> // types involved (currently always two)
>;
using XRefLayout = BCRecordLayout<
XREF,
XRefKindField, // reference kind

View File

@@ -386,6 +386,7 @@ void Serializer::writeBlockInfoBlock() {
RECORD(decls_block, GENERIC_PARAM_LIST);
RECORD(decls_block, GENERIC_PARAM);
RECORD(decls_block, GENERIC_REQUIREMENT);
RECORD(decls_block, XREF);
RECORD(decls_block, DECL_CONTEXT);
@@ -549,10 +550,6 @@ bool Serializer::writeGenericParams(const GenericParamList *genericParams) {
if (!genericParams)
return true;
// FIXME: Handle generic requirements.
if (!genericParams->getRequirements().empty())
return false;
SmallVector<TypeID, 8> archetypeIDs;
for (auto archetype : genericParams->getAllArchetypes())
archetypeIDs.push_back(addTypeRef(archetype));
@@ -567,6 +564,24 @@ bool Serializer::writeGenericParams(const GenericParamList *genericParams) {
addDeclRef(next.getDecl()));
}
abbrCode = DeclTypeAbbrCodes[GenericRequirementLayout::Code];
for (auto next : genericParams->getRequirements()) {
switch (next.getKind()) {
case RequirementKind::Conformance:
GenericRequirementLayout::emitRecord(Out, ScratchRecord, abbrCode,
GenericRequirementKind::Conformance,
addTypeRef(next.getSubject()),
addTypeRef(next.getConstraint()));
break;
case RequirementKind::SameType:
GenericRequirementLayout::emitRecord(Out, ScratchRecord, abbrCode,
GenericRequirementKind::SameType,
addTypeRef(next.getFirstType()),
addTypeRef(next.getSecondType()));
break;
}
}
return true;
}
@@ -1112,6 +1127,7 @@ void Serializer::writeAllDeclsAndTypes() {
registerDeclTypeAbbr<GenericParamListLayout>();
registerDeclTypeAbbr<GenericParamLayout>();
registerDeclTypeAbbr<GenericRequirementLayout>();
registerDeclTypeAbbr<XRefLayout>();
registerDeclTypeAbbr<DeclContextLayout>();
}