//===--- APINotesReader.cpp - API Notes Reader ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the \c APINotesReader class that reads source
// API notes data providing additional information about source code as
// a separate input, such as the non-nil/nilable annotations for
// method parameters.
//
//===----------------------------------------------------------------------===//
#include "clang/APINotes/APINotesReader.h"
#include "APINotesFormat.h"
#include "clang/APINotes/Types.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/OnDiskHashTable.h"

namespace clang {
namespace api_notes {
using namespace llvm::support;

namespace {
/// Deserialize a version tuple.
llvm::VersionTuple ReadVersionTuple(const uint8_t *&Data) {
  uint8_t NumVersions = (*Data++) & 0x03;

  unsigned Major = endian::readNext<uint32_t, llvm::endianness::little>(Data);
  if (NumVersions == 0)
    return llvm::VersionTuple(Major);

  unsigned Minor = endian::readNext<uint32_t, llvm::endianness::little>(Data);
  if (NumVersions == 1)
    return llvm::VersionTuple(Major, Minor);

  unsigned Subminor =
      endian::readNext<uint32_t, llvm::endianness::little>(Data);
  if (NumVersions == 2)
    return llvm::VersionTuple(Major, Minor, Subminor);

  unsigned Build = endian::readNext<uint32_t, llvm::endianness::little>(Data);
  return llvm::VersionTuple(Major, Minor, Subminor, Build);
}

/// An on-disk hash table whose data is versioned based on the Swift version.
template <typename Derived, typename KeyType, typename UnversionedDataType>
class VersionedTableInfo {
public:
  using internal_key_type = KeyType;
  using external_key_type = KeyType;
  using data_type =
      llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>;
  using hash_value_type = size_t;
  using offset_type = unsigned;

  internal_key_type GetInternalKey(external_key_type Key) { return Key; }

  external_key_type GetExternalKey(internal_key_type Key) { return Key; }

  static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
    return LHS == RHS;
  }

  static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
    unsigned KeyLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    unsigned DataLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    return {KeyLength, DataLength};
  }

  static data_type ReadData(internal_key_type Key, const uint8_t *Data,
                            unsigned Length) {
    unsigned NumElements =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    data_type Result;
    Result.reserve(NumElements);
    for (unsigned i = 0; i != NumElements; ++i) {
      auto version = ReadVersionTuple(Data);
      const auto *DataBefore = Data;
      (void)DataBefore;
      auto UnversionedData = Derived::readUnversioned(Key, Data);
      assert(Data != DataBefore &&
             "Unversioned data reader didn't move pointer");
      Result.push_back({version, UnversionedData});
    }
    return Result;
  }
};

/// Read serialized CommonEntityInfo.
void ReadCommonEntityInfo(const uint8_t *&Data, CommonEntityInfo &Info) {
  uint8_t UnavailableBits = *Data++;
  Info.Unavailable = (UnavailableBits >> 1) & 0x01;
  Info.UnavailableInSwift = UnavailableBits & 0x01;
  if ((UnavailableBits >> 2) & 0x01)
    Info.setSwiftPrivate(static_cast<bool>((UnavailableBits >> 3) & 0x01));

  unsigned MsgLength =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  Info.UnavailableMsg =
      std::string(reinterpret_cast<const char *>(Data),
                  reinterpret_cast<const char *>(Data) + MsgLength);
  Data += MsgLength;

  unsigned SwiftNameLength =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  Info.SwiftName =
      std::string(reinterpret_cast<const char *>(Data),
                  reinterpret_cast<const char *>(Data) + SwiftNameLength);
  Data += SwiftNameLength;
}

/// Read serialized CommonTypeInfo.
void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) {
  ReadCommonEntityInfo(Data, Info);

  unsigned SwiftBridgeLength =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  if (SwiftBridgeLength > 0) {
    Info.setSwiftBridge(std::string(reinterpret_cast<const char *>(Data),
                                    SwiftBridgeLength - 1));
    Data += SwiftBridgeLength - 1;
  }

  unsigned ErrorDomainLength =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  if (ErrorDomainLength > 0) {
    Info.setNSErrorDomain(std::optional<std::string>(std::string(
        reinterpret_cast<const char *>(Data), ErrorDomainLength - 1)));
    Data += ErrorDomainLength - 1;
  }
}

/// Used to deserialize the on-disk identifier table.
class IdentifierTableInfo {
public:
  using internal_key_type = llvm::StringRef;
  using external_key_type = llvm::StringRef;
  using data_type = IdentifierID;
  using hash_value_type = uint32_t;
  using offset_type = unsigned;

  internal_key_type GetInternalKey(external_key_type Key) { return Key; }

  external_key_type GetExternalKey(internal_key_type Key) { return Key; }

  hash_value_type ComputeHash(internal_key_type Key) {
    return llvm::djbHash(Key);
  }

  static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
    return LHS == RHS;
  }

  static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
    unsigned KeyLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    unsigned DataLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    return {KeyLength, DataLength};
  }

  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    return llvm::StringRef(reinterpret_cast<const char *>(Data), Length);
  }

  static data_type ReadData(internal_key_type key, const uint8_t *Data,
                            unsigned Length) {
    return endian::readNext<uint32_t, llvm::endianness::little>(Data);
  }
};

/// Used to deserialize the on-disk table of Objective-C classes and C++
/// namespaces.
class ContextIDTableInfo {
public:
  using internal_key_type = ContextTableKey;
  using external_key_type = internal_key_type;
  using data_type = unsigned;
  using hash_value_type = size_t;
  using offset_type = unsigned;

  internal_key_type GetInternalKey(external_key_type Key) { return Key; }

  external_key_type GetExternalKey(internal_key_type Key) { return Key; }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
    return LHS == RHS;
  }

  static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
    unsigned KeyLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    unsigned DataLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    return {KeyLength, DataLength};
  }

  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto ParentCtxID =
        endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto ContextKind =
        endian::readNext<uint8_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return {ParentCtxID, ContextKind, NameID};
  }

  static data_type ReadData(internal_key_type Key, const uint8_t *Data,
                            unsigned Length) {
    return endian::readNext<uint32_t, llvm::endianness::little>(Data);
  }
};

/// Used to deserialize the on-disk Objective-C property table.
class ContextInfoTableInfo
    : public VersionedTableInfo<ContextInfoTableInfo, unsigned, ContextInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    return endian::readNext<uint32_t, llvm::endianness::little>(Data);
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(llvm::hash_value(Key));
  }

  static ContextInfo readUnversioned(internal_key_type Key,
                                     const uint8_t *&Data) {
    ContextInfo Info;
    ReadCommonTypeInfo(Data, Info);
    uint8_t Payload = *Data++;

    if (Payload & 0x01)
      Info.setHasDesignatedInits(true);
    Payload = Payload >> 1;

    if (Payload & 0x4)
      Info.setDefaultNullability(static_cast<NullabilityKind>(Payload & 0x03));
    Payload >>= 3;

    if (Payload & (1 << 1))
      Info.setSwiftObjCMembers(Payload & 1);
    Payload >>= 2;

    if (Payload & (1 << 1))
      Info.setSwiftImportAsNonGeneric(Payload & 1);

    return Info;
  }
};

/// Read serialized VariableInfo.
void ReadVariableInfo(const uint8_t *&Data, VariableInfo &Info) {
  ReadCommonEntityInfo(Data, Info);
  if (*Data++) {
    Info.setNullabilityAudited(static_cast<NullabilityKind>(*Data));
  }
  ++Data;

  auto TypeLen = endian::readNext<uint16_t, llvm::endianness::little>(Data);
  Info.setType(std::string(Data, Data + TypeLen));
  Data += TypeLen;
}

/// Used to deserialize the on-disk Objective-C property table.
class ObjCPropertyTableInfo
    : public VersionedTableInfo<ObjCPropertyTableInfo,
                                std::tuple<uint32_t, uint32_t, uint8_t>,
                                ObjCPropertyInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    char IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data);
    return {ClassID, NameID, IsInstance};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(llvm::hash_value(Key));
  }

  static ObjCPropertyInfo readUnversioned(internal_key_type Key,
                                          const uint8_t *&Data) {
    ObjCPropertyInfo Info;
    ReadVariableInfo(Data, Info);
    uint8_t Flags = *Data++;
    if (Flags & (1 << 0))
      Info.setSwiftImportAsAccessors(Flags & (1 << 1));
    return Info;
  }
};

/// Used to deserialize the on-disk C record field table.
class FieldTableInfo
    : public VersionedTableInfo<FieldTableInfo, SingleDeclTableKey, FieldInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return {CtxID, NameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static FieldInfo readUnversioned(internal_key_type Key,
                                   const uint8_t *&Data) {
    FieldInfo Info;
    ReadVariableInfo(Data, Info);
    return Info;
  }
};

/// Read serialized ParamInfo.
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
  ReadVariableInfo(Data, Info);

  uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
  if (auto RawConvention = Payload & 0x7) {
    auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1);
    Info.setRetainCountConvention(Convention);
  }
  Payload >>= 3;
  if (Payload & 0x01)
    Info.setLifetimebound(Payload & 0x02);
  Payload >>= 2;
  if (Payload & 0x01)
    Info.setNoEscape(Payload & 0x02);
  Payload >>= 2;
  assert(Payload == 0 && "Bad API notes");
}

/// Read serialized FunctionInfo.
void ReadFunctionInfo(const uint8_t *&Data, FunctionInfo &Info) {
  ReadCommonEntityInfo(Data, Info);

  uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
  if (auto RawConvention = Payload & 0x7) {
    auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1);
    Info.setRetainCountConvention(Convention);
  }
  Payload >>= 3;
  Info.NullabilityAudited = Payload & 0x1;
  Payload >>= 1;
  assert(Payload == 0 && "Bad API notes");

  Info.NumAdjustedNullable =
      endian::readNext<uint8_t, llvm::endianness::little>(Data);
  Info.NullabilityPayload =
      endian::readNext<uint64_t, llvm::endianness::little>(Data);

  unsigned NumParams =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  while (NumParams > 0) {
    ParamInfo pi;
    ReadParamInfo(Data, pi);
    Info.Params.push_back(pi);
    --NumParams;
  }

  unsigned ResultTypeLen =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  Info.ResultType = std::string(Data, Data + ResultTypeLen);
  Data += ResultTypeLen;

  unsigned SwiftReturnOwnershipLength =
      endian::readNext<uint16_t, llvm::endianness::little>(Data);
  Info.SwiftReturnOwnership = std::string(reinterpret_cast<const char *>(Data),
                                          reinterpret_cast<const char *>(Data) +
                                              SwiftReturnOwnershipLength);
  Data += SwiftReturnOwnershipLength;
}

/// Used to deserialize the on-disk Objective-C method table.
class ObjCMethodTableInfo
    : public VersionedTableInfo<ObjCMethodTableInfo,
                                std::tuple<uint32_t, uint32_t, uint8_t>,
                                ObjCMethodInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto SelectorID =
        endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data);
    return {ClassID, SelectorID, IsInstance};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(llvm::hash_value(Key));
  }

  static ObjCMethodInfo readUnversioned(internal_key_type Key,
                                        const uint8_t *&Data) {
    ObjCMethodInfo Info;
    uint8_t Payload = *Data++;
    bool HasSelf = Payload & 0x01;
    Payload >>= 1;
    Info.RequiredInit = Payload & 0x01;
    Payload >>= 1;
    Info.DesignatedInit = Payload & 0x01;
    Payload >>= 1;
    assert(Payload == 0 && "Unable to fully decode 'Payload'.");

    ReadFunctionInfo(Data, Info);
    if (HasSelf) {
      Info.Self = ParamInfo{};
      ReadParamInfo(Data, *Info.Self);
    }
    return Info;
  }
};

/// Used to deserialize the on-disk Objective-C selector table.
class ObjCSelectorTableInfo {
public:
  using internal_key_type = StoredObjCSelector;
  using external_key_type = internal_key_type;
  using data_type = SelectorID;
  using hash_value_type = unsigned;
  using offset_type = unsigned;

  internal_key_type GetInternalKey(external_key_type Key) { return Key; }

  external_key_type GetExternalKey(internal_key_type Key) { return Key; }

  hash_value_type ComputeHash(internal_key_type Key) {
    return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Key);
  }

  static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
    return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(LHS, RHS);
  }

  static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
    unsigned KeyLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    unsigned DataLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    return {KeyLength, DataLength};
  }

  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    internal_key_type Key;
    Key.NumArgs = endian::readNext<uint16_t, llvm::endianness::little>(Data);
    unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t);
    for (unsigned i = 0; i != NumIdents; ++i) {
      Key.Identifiers.push_back(
          endian::readNext<uint32_t, llvm::endianness::little>(Data));
    }
    return Key;
  }

  static data_type ReadData(internal_key_type Key, const uint8_t *Data,
                            unsigned Length) {
    return endian::readNext<uint32_t, llvm::endianness::little>(Data);
  }
};

/// Used to deserialize the on-disk global variable table.
class GlobalVariableTableInfo
    : public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey,
                                GlobalVariableInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return {CtxID, NameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static GlobalVariableInfo readUnversioned(internal_key_type Key,
                                            const uint8_t *&Data) {
    GlobalVariableInfo Info;
    ReadVariableInfo(Data, Info);
    return Info;
  }
};

/// Used to deserialize the on-disk global function table.
class GlobalFunctionTableInfo
    : public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey,
                                GlobalFunctionInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return {CtxID, NameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static GlobalFunctionInfo readUnversioned(internal_key_type Key,
                                            const uint8_t *&Data) {
    GlobalFunctionInfo Info;
    ReadFunctionInfo(Data, Info);
    return Info;
  }
};

/// Used to deserialize the on-disk C++ method table.
class CXXMethodTableInfo
    : public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey,
                                CXXMethodInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return {CtxID, NameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static CXXMethodInfo readUnversioned(internal_key_type Key,
                                       const uint8_t *&Data) {
    CXXMethodInfo Info;

    uint8_t Payload = *Data++;
    bool HasThis = Payload & 0x01;
    Payload >>= 1;
    assert(Payload == 0 && "Unable to fully decode 'Payload'.");

    ReadFunctionInfo(Data, Info);
    if (HasThis) {
      Info.This = ParamInfo{};
      ReadParamInfo(Data, *Info.This);
    }
    return Info;
  }
};

/// Used to deserialize the on-disk enumerator table.
class EnumConstantTableInfo
    : public VersionedTableInfo<EnumConstantTableInfo, uint32_t,
                                EnumConstantInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    return NameID;
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(llvm::hash_value(Key));
  }

  static EnumConstantInfo readUnversioned(internal_key_type Key,
                                          const uint8_t *&Data) {
    EnumConstantInfo Info;
    ReadCommonEntityInfo(Data, Info);
    return Info;
  }
};

/// Used to deserialize the on-disk tag table.
class TagTableInfo
    : public VersionedTableInfo<TagTableInfo, SingleDeclTableKey, TagInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto NameID =
        endian::readNext<IdentifierID, llvm::endianness::little>(Data);
    return {CtxID, NameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static TagInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) {
    TagInfo Info;

    uint8_t Payload = *Data++;
    if (Payload & 1)
      Info.setFlagEnum(Payload & 2);
    Payload >>= 2;
    if (Payload > 0)
      Info.EnumExtensibility =
          static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);

    uint8_t Copyable =
        endian::readNext<uint8_t, llvm::endianness::little>(Data);
    if (Copyable == kSwiftConforms || Copyable == kSwiftDoesNotConform)
      Info.setSwiftCopyable(std::optional(Copyable == kSwiftConforms));
    uint8_t Escapable =
        endian::readNext<uint8_t, llvm::endianness::little>(Data);
    if (Escapable == kSwiftConforms || Escapable == kSwiftDoesNotConform)
      Info.setSwiftEscapable(std::optional(Escapable == kSwiftConforms));

    unsigned ImportAsLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    if (ImportAsLength > 0) {
      Info.SwiftImportAs =
          std::string(reinterpret_cast<const char *>(Data), ImportAsLength - 1);
      Data += ImportAsLength - 1;
    }
    unsigned RetainOpLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    if (RetainOpLength > 0) {
      Info.SwiftRetainOp =
          std::string(reinterpret_cast<const char *>(Data), RetainOpLength - 1);
      Data += RetainOpLength - 1;
    }
    unsigned ReleaseOpLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    if (ReleaseOpLength > 0) {
      Info.SwiftReleaseOp = std::string(reinterpret_cast<const char *>(Data),
                                        ReleaseOpLength - 1);
      Data += ReleaseOpLength - 1;
    }
    unsigned DefaultOwnershipLength =
        endian::readNext<uint16_t, llvm::endianness::little>(Data);
    if (DefaultOwnershipLength > 0) {
      Info.SwiftDefaultOwnership = std::string(
          reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1);
      Data += DefaultOwnershipLength - 1;
    }
    if (unsigned ConformanceLength =
            endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
      Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data),
                                          ConformanceLength - 1);
      Data += ConformanceLength - 1;
    }

    ReadCommonTypeInfo(Data, Info);
    return Info;
  }
};

/// Used to deserialize the on-disk typedef table.
class TypedefTableInfo
    : public VersionedTableInfo<TypedefTableInfo, SingleDeclTableKey,
                                TypedefInfo> {
public:
  static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
    auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
    auto nameID =
        endian::readNext<IdentifierID, llvm::endianness::little>(Data);
    return {CtxID, nameID};
  }

  hash_value_type ComputeHash(internal_key_type Key) {
    return static_cast<size_t>(Key.hashValue());
  }

  static TypedefInfo readUnversioned(internal_key_type Key,
                                     const uint8_t *&Data) {
    TypedefInfo Info;

    uint8_t Payload = *Data++;
    if (Payload > 0)
      Info.SwiftWrapper = static_cast<SwiftNewTypeKind>((Payload & 0x3) - 1);

    ReadCommonTypeInfo(Data, Info);
    return Info;
  }
};
} // end anonymous namespace

class APINotesReader::Implementation {
public:
  /// The input buffer for the API notes data.
  llvm::MemoryBuffer *InputBuffer;

  /// The Swift version to use for filtering.
  llvm::VersionTuple SwiftVersion;

  /// The name of the module that we read from the control block.
  std::string ModuleName;

  // The size and modification time of the source file from
  // which this API notes file was created, if known.
  std::optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime;

  using SerializedIdentifierTable =
      llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>;

  /// The identifier table.
  std::unique_ptr<SerializedIdentifierTable> IdentifierTable;

  using SerializedContextIDTable =
      llvm::OnDiskIterableChainedHashTable<ContextIDTableInfo>;

  /// The Objective-C / C++ context ID table.
  std::unique_ptr<SerializedContextIDTable> ContextIDTable;

  using SerializedContextInfoTable =
      llvm::OnDiskIterableChainedHashTable<ContextInfoTableInfo>;

  /// The Objective-C context info table.
  std::unique_ptr<SerializedContextInfoTable> ContextInfoTable;

  using SerializedObjCPropertyTable =
      llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>;

  /// The Objective-C property table.
  std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable;

  using SerializedFieldTable =
      llvm::OnDiskIterableChainedHashTable<FieldTableInfo>;

  /// The C record field table.
  std::unique_ptr<SerializedFieldTable> FieldTable;

  using SerializedObjCMethodTable =
      llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;

  /// The Objective-C method table.
  std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;

  using SerializedCXXMethodTable =
      llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;

  /// The C++ method table.
  std::unique_ptr<SerializedCXXMethodTable> CXXMethodTable;

  using SerializedObjCSelectorTable =
      llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;

  /// The Objective-C selector table.
  std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable;

  using SerializedGlobalVariableTable =
      llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>;

  /// The global variable table.
  std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable;

  using SerializedGlobalFunctionTable =
      llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>;

  /// The global function table.
  std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable;

  using SerializedEnumConstantTable =
      llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>;

  /// The enumerator table.
  std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable;

  using SerializedTagTable = llvm::OnDiskIterableChainedHashTable<TagTableInfo>;

  /// The tag table.
  std::unique_ptr<SerializedTagTable> TagTable;

  using SerializedTypedefTable =
      llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>;

  /// The typedef table.
  std::unique_ptr<SerializedTypedefTable> TypedefTable;

  /// Retrieve the identifier ID for the given string, or an empty
  /// optional if the string is unknown.
  std::optional<IdentifierID> getIdentifier(llvm::StringRef Str);

  /// Retrieve the selector ID for the given selector, or an empty
  /// optional if the string is unknown.
  std::optional<SelectorID> getSelector(ObjCSelectorRef Selector);

  bool readControlBlock(llvm::BitstreamCursor &Cursor,
                        llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readIdentifierBlock(llvm::BitstreamCursor &Cursor,
                           llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readContextBlock(llvm::BitstreamCursor &Cursor,
                        llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readObjCPropertyBlock(llvm::BitstreamCursor &Cursor,
                             llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
                           llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
                          llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readFieldBlock(llvm::BitstreamCursor &Cursor,
                      llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
                             llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
                               llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor,
                               llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readEnumConstantBlock(llvm::BitstreamCursor &Cursor,
                             llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readTagBlock(llvm::BitstreamCursor &Cursor,
                    llvm::SmallVectorImpl<uint64_t> &Scratch);
  bool readTypedefBlock(llvm::BitstreamCursor &Cursor,
                        llvm::SmallVectorImpl<uint64_t> &Scratch);
};

std::optional<IdentifierID>
APINotesReader::Implementation::getIdentifier(llvm::StringRef Str) {
  if (!IdentifierTable)
    return std::nullopt;

  if (Str.empty())
    return IdentifierID(0);

  auto Known = IdentifierTable->find(Str);
  if (Known == IdentifierTable->end())
    return std::nullopt;

  return *Known;
}

std::optional<SelectorID>
APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) {
  if (!ObjCSelectorTable || !IdentifierTable)
    return std::nullopt;

  // Translate the identifiers.
  StoredObjCSelector Key;
  Key.NumArgs = Selector.NumArgs;
  for (auto Ident : Selector.Identifiers) {
    if (auto IdentID = getIdentifier(Ident)) {
      Key.Identifiers.push_back(*IdentID);
    } else {
      return std::nullopt;
    }
  }

  auto Known = ObjCSelectorTable->find(Key);
  if (Known == ObjCSelectorTable->end())
    return std::nullopt;

  return *Known;
}

bool APINotesReader::Implementation::readControlBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID))
    return true;

  bool SawMetadata = false;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();

  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown metadata sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();

    switch (Kind) {
    case control_block::METADATA:
      // Already saw metadata.
      if (SawMetadata)
        return true;

      if (Scratch[0] != VERSION_MAJOR || Scratch[1] != VERSION_MINOR)
        return true;

      SawMetadata = true;
      break;

    case control_block::MODULE_NAME:
      ModuleName = BlobData.str();
      break;

    case control_block::MODULE_OPTIONS:
      break;

    case control_block::SOURCE_FILE:
      SourceFileSizeAndModTime = {Scratch[0], Scratch[1]};
      break;

    default:
      // Unknown metadata record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return !SawMetadata;
}

bool APINotesReader::Implementation::readIdentifierBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();

  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case identifier_block::IDENTIFIER_DATA: {
      // Already saw identifier table.
      if (IdentifierTable)
        return true;

      uint32_t tableOffset;
      identifier_block::IdentifierDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      IdentifierTable.reset(SerializedIdentifierTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readContextBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();

  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case context_block::CONTEXT_ID_DATA: {
      // Already saw Objective-C / C++ context ID table.
      if (ContextIDTable)
        return true;

      uint32_t tableOffset;
      context_block::ContextIDLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      ContextIDTable.reset(SerializedContextIDTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    case context_block::CONTEXT_INFO_DATA: {
      // Already saw Objective-C / C++ context info table.
      if (ContextInfoTable)
        return true;

      uint32_t tableOffset;
      context_block::ContextInfoLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      ContextInfoTable.reset(SerializedContextInfoTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readObjCPropertyBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();

  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case objc_property_block::OBJC_PROPERTY_DATA: {
      // Already saw Objective-C property table.
      if (ObjCPropertyTable)
        return true;

      uint32_t tableOffset;
      objc_property_block::ObjCPropertyDataLayout::readRecord(Scratch,
                                                              tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      ObjCPropertyTable.reset(SerializedObjCPropertyTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readObjCMethodBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case objc_method_block::OBJC_METHOD_DATA: {
      // Already saw Objective-C method table.
      if (ObjCMethodTable)
        return true;

      uint32_t tableOffset;
      objc_method_block::ObjCMethodDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      ObjCMethodTable.reset(SerializedObjCMethodTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readCXXMethodBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case cxx_method_block::CXX_METHOD_DATA: {
      // Already saw C++ method table.
      if (CXXMethodTable)
        return true;

      uint32_t tableOffset;
      cxx_method_block::CXXMethodDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      CXXMethodTable.reset(SerializedCXXMethodTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readFieldBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(FIELD_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case field_block::FIELD_DATA: {
      // Already saw field table.
      if (FieldTable)
        return true;

      uint32_t tableOffset;
      field_block::FieldDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      FieldTable.reset(SerializedFieldTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readObjCSelectorBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case objc_selector_block::OBJC_SELECTOR_DATA: {
      // Already saw Objective-C selector table.
      if (ObjCSelectorTable)
        return true;

      uint32_t tableOffset;
      objc_selector_block::ObjCSelectorDataLayout::readRecord(Scratch,
                                                              tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      ObjCSelectorTable.reset(SerializedObjCSelectorTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readGlobalVariableBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case global_variable_block::GLOBAL_VARIABLE_DATA: {
      // Already saw global variable table.
      if (GlobalVariableTable)
        return true;

      uint32_t tableOffset;
      global_variable_block::GlobalVariableDataLayout::readRecord(Scratch,
                                                                  tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      GlobalVariableTable.reset(SerializedGlobalVariableTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readGlobalFunctionBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case global_function_block::GLOBAL_FUNCTION_DATA: {
      // Already saw global function table.
      if (GlobalFunctionTable)
        return true;

      uint32_t tableOffset;
      global_function_block::GlobalFunctionDataLayout::readRecord(Scratch,
                                                                  tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      GlobalFunctionTable.reset(SerializedGlobalFunctionTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readEnumConstantBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case enum_constant_block::ENUM_CONSTANT_DATA: {
      // Already saw enumerator table.
      if (EnumConstantTable)
        return true;

      uint32_t tableOffset;
      enum_constant_block::EnumConstantDataLayout::readRecord(Scratch,
                                                              tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      EnumConstantTable.reset(SerializedEnumConstantTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readTagBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(TAG_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case tag_block::TAG_DATA: {
      // Already saw tag table.
      if (TagTable)
        return true;

      uint32_t tableOffset;
      tag_block::TagDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      TagTable.reset(SerializedTagTable::Create(base + tableOffset,
                                                base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

bool APINotesReader::Implementation::readTypedefBlock(
    llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
  if (Cursor.EnterSubBlock(TYPEDEF_BLOCK_ID))
    return true;

  llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
  if (!MaybeNext) {
    // FIXME this drops the error on the floor.
    consumeError(MaybeNext.takeError());
    return false;
  }
  llvm::BitstreamEntry Next = MaybeNext.get();
  while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
    if (Next.Kind == llvm::BitstreamEntry::Error)
      return true;

    if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
      // Unknown sub-block, possibly for use by a future version of the
      // API notes format.
      if (Cursor.SkipBlock())
        return true;

      MaybeNext = Cursor.advance();
      if (!MaybeNext) {
        // FIXME this drops the error on the floor.
        consumeError(MaybeNext.takeError());
        return false;
      }
      Next = MaybeNext.get();
      continue;
    }

    Scratch.clear();
    llvm::StringRef BlobData;
    llvm::Expected<unsigned> MaybeKind =
        Cursor.readRecord(Next.ID, Scratch, &BlobData);
    if (!MaybeKind) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeKind.takeError());
      return false;
    }
    unsigned Kind = MaybeKind.get();
    switch (Kind) {
    case typedef_block::TYPEDEF_DATA: {
      // Already saw typedef table.
      if (TypedefTable)
        return true;

      uint32_t tableOffset;
      typedef_block::TypedefDataLayout::readRecord(Scratch, tableOffset);
      auto base = reinterpret_cast<const uint8_t *>(BlobData.data());

      TypedefTable.reset(SerializedTypedefTable::Create(
          base + tableOffset, base + sizeof(uint32_t), base));
      break;
    }

    default:
      // Unknown record, possibly for use by a future version of the
      // module format.
      break;
    }

    MaybeNext = Cursor.advance();
    if (!MaybeNext) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeNext.takeError());
      return false;
    }
    Next = MaybeNext.get();
  }

  return false;
}

APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer,
                               llvm::VersionTuple SwiftVersion, bool &Failed)
    : Implementation(new class Implementation) {
  Failed = false;

  // Initialize the input buffer.
  Implementation->InputBuffer = InputBuffer;
  Implementation->SwiftVersion = SwiftVersion;
  llvm::BitstreamCursor Cursor(*Implementation->InputBuffer);

  // Validate signature.
  for (auto byte : API_NOTES_SIGNATURE) {
    if (Cursor.AtEndOfStream()) {
      Failed = true;
      return;
    }
    if (llvm::Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead =
            Cursor.Read(8)) {
      if (maybeRead.get() != byte) {
        Failed = true;
        return;
      }
    } else {
      // FIXME this drops the error on the floor.
      consumeError(maybeRead.takeError());
      Failed = true;
      return;
    }
  }

  // Look at all of the blocks.
  bool HasValidControlBlock = false;
  llvm::SmallVector<uint64_t, 64> Scratch;
  while (!Cursor.AtEndOfStream()) {
    llvm::Expected<llvm::BitstreamEntry> MaybeTopLevelEntry = Cursor.advance();
    if (!MaybeTopLevelEntry) {
      // FIXME this drops the error on the floor.
      consumeError(MaybeTopLevelEntry.takeError());
      Failed = true;
      return;
    }
    llvm::BitstreamEntry TopLevelEntry = MaybeTopLevelEntry.get();

    if (TopLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
      break;

    switch (TopLevelEntry.ID) {
    case llvm::bitc::BLOCKINFO_BLOCK_ID:
      if (!Cursor.ReadBlockInfoBlock()) {
        Failed = true;
        break;
      }
      break;

    case CONTROL_BLOCK_ID:
      // Only allow a single control block.
      if (HasValidControlBlock ||
          Implementation->readControlBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }

      HasValidControlBlock = true;
      break;

    case IDENTIFIER_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readIdentifierBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case OBJC_CONTEXT_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readContextBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }

      break;

    case OBJC_PROPERTY_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readObjCPropertyBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case OBJC_METHOD_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readObjCMethodBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case CXX_METHOD_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readCXXMethodBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case FIELD_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readFieldBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case OBJC_SELECTOR_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case GLOBAL_VARIABLE_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readGlobalVariableBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case GLOBAL_FUNCTION_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readGlobalFunctionBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case ENUM_CONSTANT_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readEnumConstantBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case TAG_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readTagBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    case TYPEDEF_BLOCK_ID:
      if (!HasValidControlBlock ||
          Implementation->readTypedefBlock(Cursor, Scratch)) {
        Failed = true;
        return;
      }
      break;

    default:
      // Unknown top-level block, possibly for use by a future version of the
      // module format.
      if (Cursor.SkipBlock()) {
        Failed = true;
        return;
      }
      break;
    }
  }

  if (!Cursor.AtEndOfStream()) {
    Failed = true;
    return;
  }
}

APINotesReader::~APINotesReader() { delete Implementation->InputBuffer; }

std::unique_ptr<APINotesReader>
APINotesReader::Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
                       llvm::VersionTuple SwiftVersion) {
  bool Failed = false;
  std::unique_ptr<APINotesReader> Reader(
      new APINotesReader(InputBuffer.release(), SwiftVersion, Failed));
  if (Failed)
    return nullptr;

  return Reader;
}

template <typename T>
APINotesReader::VersionedInfo<T>::VersionedInfo(
    llvm::VersionTuple Version,
    llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> R)
    : Results(std::move(R)) {

  assert(!Results.empty());
  assert(llvm::is_sorted(
      Results,
      [](const std::pair<llvm::VersionTuple, T> &left,
         const std::pair<llvm::VersionTuple, T> &right) -> bool {
        // The comparison function should be reflective, and with expensive
        // checks we can get callbacks basically checking that lambda(a,a) is
        // false. We could still check that we do not find equal elements when
        // left!=right.
        assert((&left == &right || left.first != right.first) &&
               "two entries for the same version");
        return left.first < right.first;
      }));

  Selected = std::nullopt;
  for (unsigned i = 0, n = Results.size(); i != n; ++i) {
    if (!Version.empty() && Results[i].first >= Version) {
      // If the current version is "4", then entries for 4 are better than
      // entries for 5, but both are valid. Because entries are sorted, we get
      // that behavior by picking the first match.
      Selected = i;
      break;
    }
  }

  // If we didn't find a match but we have an unversioned result, use the
  // unversioned result. This will always be the first entry because we encode
  // it as version 0.
  if (!Selected && Results[0].first.empty())
    Selected = 0;
}

auto APINotesReader::lookupObjCClassID(llvm::StringRef Name)
    -> std::optional<ContextID> {
  if (!Implementation->ContextIDTable)
    return std::nullopt;

  std::optional<IdentifierID> ClassID = Implementation->getIdentifier(Name);
  if (!ClassID)
    return std::nullopt;

  // ObjC classes can't be declared in C++ namespaces, so use -1 as the global
  // context.
  auto KnownID = Implementation->ContextIDTable->find(
      ContextTableKey(-1, (uint8_t)ContextKind::ObjCClass, *ClassID));
  if (KnownID == Implementation->ContextIDTable->end())
    return std::nullopt;

  return ContextID(*KnownID);
}

auto APINotesReader::lookupObjCClassInfo(llvm::StringRef Name)
    -> VersionedInfo<ContextInfo> {
  if (!Implementation->ContextInfoTable)
    return std::nullopt;

  std::optional<ContextID> CtxID = lookupObjCClassID(Name);
  if (!CtxID)
    return std::nullopt;

  auto KnownInfo = Implementation->ContextInfoTable->find(CtxID->Value);
  if (KnownInfo == Implementation->ContextInfoTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *KnownInfo};
}

auto APINotesReader::lookupObjCProtocolID(llvm::StringRef Name)
    -> std::optional<ContextID> {
  if (!Implementation->ContextIDTable)
    return std::nullopt;

  std::optional<IdentifierID> classID = Implementation->getIdentifier(Name);
  if (!classID)
    return std::nullopt;

  // ObjC classes can't be declared in C++ namespaces, so use -1 as the global
  // context.
  auto KnownID = Implementation->ContextIDTable->find(
      ContextTableKey(-1, (uint8_t)ContextKind::ObjCProtocol, *classID));
  if (KnownID == Implementation->ContextIDTable->end())
    return std::nullopt;

  return ContextID(*KnownID);
}

auto APINotesReader::lookupObjCProtocolInfo(llvm::StringRef Name)
    -> VersionedInfo<ContextInfo> {
  if (!Implementation->ContextInfoTable)
    return std::nullopt;

  std::optional<ContextID> CtxID = lookupObjCProtocolID(Name);
  if (!CtxID)
    return std::nullopt;

  auto KnownInfo = Implementation->ContextInfoTable->find(CtxID->Value);
  if (KnownInfo == Implementation->ContextInfoTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *KnownInfo};
}

auto APINotesReader::lookupObjCProperty(ContextID CtxID, llvm::StringRef Name,
                                        bool IsInstance)
    -> VersionedInfo<ObjCPropertyInfo> {
  if (!Implementation->ObjCPropertyTable)
    return std::nullopt;

  std::optional<IdentifierID> PropertyID = Implementation->getIdentifier(Name);
  if (!PropertyID)
    return std::nullopt;

  auto Known = Implementation->ObjCPropertyTable->find(
      std::make_tuple(CtxID.Value, *PropertyID, (char)IsInstance));
  if (Known == Implementation->ObjCPropertyTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
                                      bool IsInstanceMethod)
    -> VersionedInfo<ObjCMethodInfo> {
  if (!Implementation->ObjCMethodTable)
    return std::nullopt;

  std::optional<SelectorID> SelID = Implementation->getSelector(Selector);
  if (!SelID)
    return std::nullopt;

  auto Known = Implementation->ObjCMethodTable->find(
      ObjCMethodTableInfo::internal_key_type{CtxID.Value, *SelID,
                                             IsInstanceMethod});
  if (Known == Implementation->ObjCMethodTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupField(ContextID CtxID, llvm::StringRef Name)
    -> VersionedInfo<FieldInfo> {
  if (!Implementation->FieldTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  auto Known = Implementation->FieldTable->find(
      SingleDeclTableKey(CtxID.Value, *NameID));
  if (Known == Implementation->FieldTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
    -> VersionedInfo<CXXMethodInfo> {
  if (!Implementation->CXXMethodTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  auto Known = Implementation->CXXMethodTable->find(
      SingleDeclTableKey(CtxID.Value, *NameID));
  if (Known == Implementation->CXXMethodTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
                                          std::optional<Context> Ctx)
    -> VersionedInfo<GlobalVariableInfo> {
  if (!Implementation->GlobalVariableTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  SingleDeclTableKey Key(Ctx, *NameID);

  auto Known = Implementation->GlobalVariableTable->find(Key);
  if (Known == Implementation->GlobalVariableTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name,
                                          std::optional<Context> Ctx)
    -> VersionedInfo<GlobalFunctionInfo> {
  if (!Implementation->GlobalFunctionTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  SingleDeclTableKey Key(Ctx, *NameID);

  auto Known = Implementation->GlobalFunctionTable->find(Key);
  if (Known == Implementation->GlobalFunctionTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
    -> VersionedInfo<EnumConstantInfo> {
  if (!Implementation->EnumConstantTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  auto Known = Implementation->EnumConstantTable->find(*NameID);
  if (Known == Implementation->EnumConstantTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupTagID(llvm::StringRef Name,
                                 std::optional<Context> ParentCtx)
    -> std::optional<ContextID> {
  if (!Implementation->ContextIDTable)
    return std::nullopt;

  std::optional<IdentifierID> TagID = Implementation->getIdentifier(Name);
  if (!TagID)
    return std::nullopt;

  auto KnownID = Implementation->ContextIDTable->find(
      ContextTableKey(ParentCtx, ContextKind::Tag, *TagID));
  if (KnownID == Implementation->ContextIDTable->end())
    return std::nullopt;

  return ContextID(*KnownID);
}

auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional<Context> Ctx)
    -> VersionedInfo<TagInfo> {
  if (!Implementation->TagTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  SingleDeclTableKey Key(Ctx, *NameID);

  auto Known = Implementation->TagTable->find(Key);
  if (Known == Implementation->TagTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupTypedef(llvm::StringRef Name,
                                   std::optional<Context> Ctx)
    -> VersionedInfo<TypedefInfo> {
  if (!Implementation->TypedefTable)
    return std::nullopt;

  std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
  if (!NameID)
    return std::nullopt;

  SingleDeclTableKey Key(Ctx, *NameID);

  auto Known = Implementation->TypedefTable->find(Key);
  if (Known == Implementation->TypedefTable->end())
    return std::nullopt;

  return {Implementation->SwiftVersion, *Known};
}

auto APINotesReader::lookupNamespaceID(
    llvm::StringRef Name, std::optional<ContextID> ParentNamespaceID)
    -> std::optional<ContextID> {
  if (!Implementation->ContextIDTable)
    return std::nullopt;

  std::optional<IdentifierID> NamespaceID = Implementation->getIdentifier(Name);
  if (!NamespaceID)
    return std::nullopt;

  uint32_t RawParentNamespaceID =
      ParentNamespaceID ? ParentNamespaceID->Value : -1;
  auto KnownID = Implementation->ContextIDTable->find(
      {RawParentNamespaceID, (uint8_t)ContextKind::Namespace, *NamespaceID});
  if (KnownID == Implementation->ContextIDTable->end())
    return std::nullopt;

  return ContextID(*KnownID);
}

} // namespace api_notes
} // namespace clang
