From b4b3a9bf92597e9ea361f7f8dd3453097b00e607 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 2 Oct 2016 21:01:55 -0700 Subject: [PATCH 1/4] [API Notes] Add Swift versioning to API notes files Extend the API notes format with support for specifying API notes at a particular Swift version. A single API notes file has "unversioned" information, followed optionally by more information about the various classes/functions/etc. at a specific Swift version. The intent is to allow API notes to provide backward-compatibility information Parse Swift-versioned API notes, store version API notes in the binary format, and ensure that it round-trips. For now, Clang still only adds attributes based on the unversioned information. This is the first step of rdar://problem/28455809. (cherry picked from commit 16d41c34b0feb962d6974b311597944e74b7027b) --- include/clang/APINotes/APINotesReader.h | 53 +- include/clang/APINotes/APINotesWriter.h | 46 +- include/clang/Basic/VersionTuple.h | 35 ++ lib/APINotes/APINotesFormat.h | 21 +- lib/APINotes/APINotesReader.cpp | 635 ++++++++++++----------- lib/APINotes/APINotesWriter.cpp | 636 +++++++++++++----------- lib/APINotes/APINotesYAMLCompiler.cpp | 355 ++++++++----- lib/Sema/SemaAPINotes.cpp | 16 +- test/APINotes/Inputs/roundtrip.apinotes | 19 + 9 files changed, 1049 insertions(+), 767 deletions(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index aa88bac0626..593e8174d4e 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -17,6 +17,7 @@ #define LLVM_CLANG_API_NOTES_READER_H #include "clang/APINotes/Types.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -65,21 +66,33 @@ class APINotesReader { /// Retrieve the module options ModuleOptions getModuleOptions() const; + /// Look for the context ID of the given Objective-C class. + /// + /// \param name The name of the class we're looking for. + /// + /// \returns The ID, if known. + Optional lookupObjCClassID(StringRef name); + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. /// - /// \returns The ID and information about the class, if known. - Optional> - lookupObjCClass(StringRef name); + /// \returns The information about the class, if known. + Optional lookupObjCClassInfo(StringRef name); + + /// Look for the context ID of the given Objective-C protocol. + /// + /// \param name The name of the protocol we're looking for. + /// + /// \returns The ID of the protocol, if known. + Optional lookupObjCProtocolID(StringRef name); /// Look for information regarding the given Objective-C protocol. /// /// \param name The name of the protocol we're looking for. /// - /// \returns The ID and information about the protocol, if known. - Optional> - lookupObjCProtocol(StringRef name); + /// \returns The information about the protocol, if known. + Optional lookupObjCProtocolInfo(StringRef name); /// Look for information regarding the given Objective-C property in /// the given context. @@ -88,6 +101,7 @@ class APINotesReader { /// \param name The name of the property we're looking for. /// \param isInstance Whether we are looking for an instance property (vs. /// a class property). + /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. Optional lookupObjCProperty(ContextID contextID, @@ -149,39 +163,48 @@ class APINotesReader { /// Visit an Objective-C class. virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C protocol. virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C method. virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info); + const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C property. virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Visit a global variable. virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info); + const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Visit a global function. virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info); + const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Visit an enumerator. virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info); + const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Visit a tag. - virtual void visitTag(StringRef name, const TagInfo &info); + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Visit a typedef. - virtual void visitTypedef(StringRef name, const TypedefInfo &info); + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 4bf3ce13716..62defc1f944 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -16,6 +16,7 @@ #ifndef LLVM_CLANG_API_NOTES_WRITER_H #define LLVM_CLANG_API_NOTES_WRITER_H +#include "clang/Basic/VersionTuple.h" #include "clang/APINotes/Types.h" namespace llvm { @@ -46,23 +47,17 @@ class APINotesWriter { /// Write the API notes data to the given stream. void writeToStream(llvm::raw_ostream &os); - /// Add information about a specific Objective-C class. + /// Add information about a specific Objective-C class or protocol. /// - /// \param name The name of this class. - /// \param info Information about this class. + /// \param name The name of this class/protocol. + /// \param isClass Whether this is a class (vs. a protocol). + /// \param info Information about this class/protocol. /// - /// \returns the ID of the class, which can be used to add properties and - /// methods to the class. - ContextID addObjCClass(StringRef name, const ObjCContextInfo &info); - - /// Add information about a specific Objective-C protocol. - /// - /// \param name The name of this protocol. - /// \param info Information about this protocol. - /// - /// \returns the ID of the protocol, which can be used to add properties and - /// methods to the protocol. - ContextID addObjCProtocol(StringRef name, const ObjCContextInfo &info); + /// \returns the ID of the class or protocol, which can be used to add + /// properties and methods to the class/protocol. + ContextID addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C property. /// @@ -71,7 +66,8 @@ class APINotesWriter { /// \param info Information about this property. void addObjCProperty(ContextID contextID, StringRef name, bool isInstanceProperty, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C method. /// @@ -81,37 +77,43 @@ class APINotesWriter { /// (vs. a class method). /// \param info Information about this method. void addObjCMethod(ContextID contextID, ObjCSelectorRef selector, - bool isInstanceMethod, const ObjCMethodInfo &info); + bool isInstanceMethod, const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Add information about a global variable. /// /// \param name The name of this global variable. /// \param info Information about this global variable. - void addGlobalVariable(StringRef name, const GlobalVariableInfo &info); + void addGlobalVariable(StringRef name, const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Add information about a global function. /// /// \param name The name of this global function. /// \param info Information about this global function. - void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Add information about an enumerator. /// /// \param name The name of this enumerator. /// \param info Information about this enumerator. - void addEnumConstant(StringRef name, const EnumConstantInfo &info); + void addEnumConstant(StringRef name, const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. /// \param info Information about this tag. - void addTag(StringRef name, const TagInfo &info); + void addTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Add information about a typedef. /// /// \param name The name of this typedef. /// \param info Information about this typedef. - void addTypedef(StringRef name, const TypedefInfo &info); + void addTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); /// Add module options void addModuleOptions(ModuleOptions opts); diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h index da3b01903ed..07315f008cd 100644 --- a/include/clang/Basic/VersionTuple.h +++ b/include/clang/Basic/VersionTuple.h @@ -17,6 +17,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/DenseMapInfo.h" #include #include @@ -70,6 +71,9 @@ class VersionTuple { return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; } + /// Whether this is a non-empty version tuple. + explicit operator bool () const { return !empty(); } + /// \brief Retrieve the major version number. unsigned getMajor() const { return Major; } @@ -165,4 +169,35 @@ class VersionTuple { raw_ostream& operator<<(raw_ostream &Out, const VersionTuple &V); } // end namespace clang + +namespace llvm { + // Provide DenseMapInfo for version tuples. + template<> + struct DenseMapInfo { + static inline clang::VersionTuple getEmptyKey() { + return clang::VersionTuple(0x7FFFFFFF); + } + static inline clang::VersionTuple getTombstoneKey() { + return clang::VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const clang::VersionTuple& value) { + unsigned result = value.getMajor(); + if (auto minor = value.getMinor()) + result = combineHashValue(result, *minor); + if (auto subminor = value.getSubminor()) + result = combineHashValue(result, *subminor); + if (auto build = value.getBuild()) + result = combineHashValue(result, *build); + + return result; + } + + static bool isEqual(const clang::VersionTuple &lhs, + const clang::VersionTuple &rhs) { + return lhs == rhs; + } + }; + +} // end namespace llvm + #endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 542f90831a5..9b68cf29238 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 15; // source file info +const uint16_t VERSION_MINOR = 16; // versioned API notes. using IdentifierID = PointerEmbeddedInt; using IdentifierIDField = BCVBR<16>; @@ -60,8 +60,8 @@ enum BlockID { /// The identifier data block, which maps identifier strings to IDs. IDENTIFIER_BLOCK_ID, - /// The Objective-C class data block, which maps Objective-C class - /// names to information about the class. + /// The Objective-C context data block, which contains information about + /// Objective-C classes and protocols. OBJC_CONTEXT_BLOCK_ID, /// The Objective-C property data block, which maps Objective-C @@ -147,13 +147,20 @@ namespace identifier_block { namespace objc_context_block { enum { - OBJC_CONTEXT_DATA = 1, + OBJC_CONTEXT_ID_DATA = 1, + OBJC_CONTEXT_INFO_DATA = 2, }; - using ObjCContextDataLayout = BCRecordLayout< - OBJC_CONTEXT_DATA, // record ID + using ObjCContextIDLayout = BCRecordLayout< + OBJC_CONTEXT_ID_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) - BCBlob // map from ObjC class names (as IDs) to ObjC class information + BCBlob // map from ObjC class names/protocol (as IDs) to context IDs + >; + + using ObjCContextInfoLayout = BCRecordLayout< + OBJC_CONTEXT_INFO_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC context IDs to context information. >; } diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 0c93756a005..71e1e411602 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -28,6 +28,77 @@ using namespace llvm::support; using namespace llvm; namespace { + /// Deserialize a version tuple. + VersionTuple readVersionTuple(const uint8_t *&data) { + uint8_t numVersions = (*data++) & 0x03; + + unsigned major = endian::readNext(data); + if (numVersions == 0) + return VersionTuple(major); + + unsigned minor = endian::readNext(data); + if (numVersions == 1) + return VersionTuple(major, minor); + + unsigned subminor = endian::readNext(data); + if (numVersions == 2) + return VersionTuple(major, minor, subminor); + + unsigned build = endian::readNext(data); + return VersionTuple(major, minor, subminor, build); + } + + /// An on-disk hash table whose data is versioned based on the Swift version. + template + class VersionedTableInfo { + public: + using internal_key_type = KeyType; + using external_key_type = KeyType; + using data_type = SmallVector, 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; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + unsigned numElements = endian::readNext(data); + data_type result; + result.reserve(numElements); + for (unsigned i = 0; i != numElements; ++i) { + auto version = readVersionTuple(data); + auto dataBefore = data; (void)data; + 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++; @@ -109,12 +180,12 @@ namespace { }; /// Used to deserialize the on-disk Objective-C class table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: // identifier ID, is-protocol using internal_key_type = std::pair; using external_key_type = internal_key_type; - using data_type = std::pair; + using data_type = unsigned; using hash_value_type = size_t; using offset_type = unsigned; @@ -150,16 +221,35 @@ namespace { static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { - data_type result; - result.first = endian::readNext(data); - readCommonTypeInfo(data, result.second); - if (*data++) { - result.second.setDefaultNullability(static_cast(*data)); - } - ++data; - result.second.setHasDesignatedInits(*data++); - - return result; + return endian::readNext(data); + } + }; + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo + { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return endian::readNext(data); + } + + static ObjCContextInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + ObjCContextInfo info; + readCommonTypeInfo(data, info); + uint8_t payload = *data++; + + if (payload & 0x01) + info.setHasDesignatedInits(true); + payload = payload >> 1; + + if (payload & 0x4) + info.setDefaultNullability(static_cast(payload&0x03)); + + return info; } }; @@ -173,38 +263,12 @@ namespace { } /// Used to deserialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + class ObjCPropertyTableInfo + : public VersionedTableInfo, + ObjCPropertyInfo> + { public: - // (context ID, name ID, isInstance) - using internal_key_type = std::tuple; - using external_key_type = internal_key_type; - using data_type = ObjCPropertyInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto nameID = endian::readNext(data); @@ -212,8 +276,8 @@ namespace { return std::make_tuple(classID, nameID, isInstance); } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCPropertyInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCPropertyInfo info; readVariableInfo(data, info); return info; @@ -248,40 +312,11 @@ namespace { } /// Used to deserialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using internal_key_type = std::tuple; - using external_key_type = internal_key_type; - using data_type = ObjCMethodInfo; - 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 llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto selectorID = endian::readNext(data); @@ -289,13 +324,18 @@ namespace { return internal_key_type{ classID, selectorID, isInstance }; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCMethodInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCMethodInfo info; + uint8_t payload = *data++; + info.Required = payload & 0x01; + payload >>= 1; + info.DesignatedInit = payload & 0x01; + payload >>= 1; + info.FactoryAsInit = payload & 0x03; + payload >>= 2; + readFunctionInfo(data, info); - info.DesignatedInit = endian::readNext(data); - info.FactoryAsInit = endian::readNext(data); - info.Required = endian::readNext(data); return info; } }; @@ -350,44 +390,17 @@ namespace { }; /// Used to deserialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalVariableInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static GlobalVariableInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalVariableInfo info; readVariableInfo(data, info); return info; @@ -395,44 +408,17 @@ namespace { }; /// Used to deserialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalFunctionInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static GlobalFunctionInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalFunctionInfo info; readFunctionInfo(data, info); return info; @@ -440,44 +426,17 @@ namespace { }; /// Used to deserialize the on-disk enumerator table. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = EnumConstantInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static EnumConstantInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { EnumConstantInfo info; readCommonEntityInfo(data, info); return info; @@ -485,44 +444,16 @@ namespace { }; /// Used to deserialize the on-disk tag table. - class TagTableInfo { + class TagTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TagInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static TagInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TagInfo info; readCommonTypeInfo(data, info); return info; @@ -530,44 +461,16 @@ namespace { }; /// Used to deserialize the on-disk typedef table. - class TypedefTableInfo { + class TypedefTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TypedefInfo; - 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(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static TypedefInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TypedefInfo info; readCommonTypeInfo(data, info); return info; @@ -602,11 +505,17 @@ class APINotesReader::Implementation { /// The identifier table. std::unique_ptr IdentifierTable; - using SerializedObjCContextTable = - llvm::OnDiskIterableChainedHashTable; + using SerializedObjCContextIDTable = + llvm::OnDiskIterableChainedHashTable; - /// The Objective-C context table. - std::unique_ptr ObjCContextTable; + /// The Objective-C context ID table. + std::unique_ptr ObjCContextIDTable; + + using SerializedObjCContextInfoTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C context info table. + std::unique_ptr ObjCContextInfoTable; using SerializedObjCPropertyTable = llvm::OnDiskIterableChainedHashTable; @@ -867,19 +776,36 @@ bool APINotesReader::Implementation::readObjCContextBlock( StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { - case objc_context_block::OBJC_CONTEXT_DATA: { - // Already saw Objective-C class table. - if (ObjCContextTable) + case objc_context_block::OBJC_CONTEXT_ID_DATA: { + // Already saw Objective-C context ID table. + if (ObjCContextIDTable) return true; uint32_t tableOffset; - objc_context_block::ObjCContextDataLayout::readRecord(scratch, tableOffset); + objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); - ObjCContextTable.reset( - SerializedObjCContextTable::Create(base + tableOffset, - base + sizeof(uint32_t), - base)); + ObjCContextIDTable.reset( + SerializedObjCContextIDTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + case objc_context_block::OBJC_CONTEXT_INFO_DATA: { + // Already saw Objective-C context info table. + if (ObjCContextInfoTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextInfoLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(blobData.data()); + + ObjCContextInfoTable.reset( + SerializedObjCContextInfoTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); break; } @@ -1509,38 +1435,78 @@ ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } -auto APINotesReader::lookupObjCClass(StringRef name) - -> Optional> { - if (!Impl.ObjCContextTable) +namespace { + template + Optional getUnversioned( + const SmallVectorImpl>& array) { + for (const auto &versioned : array) { + if (!versioned.first) return versioned.second; + } + return None; + } +} + +auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional { + if (!Impl.ObjCContextIDTable) return None; Optional classID = Impl.getIdentifier(name); if (!classID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\0'}); - if (known == Impl.ObjCContextTable->end()) + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'}); + if (knownID == Impl.ObjCContextIDTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return ContextID(*knownID); } -auto APINotesReader::lookupObjCProtocol(StringRef name) - -> Optional> { - if (!Impl.ObjCContextTable) +auto APINotesReader::lookupObjCClassInfo(StringRef name) + -> Optional { + if (!Impl.ObjCContextInfoTable) return None; - Optional classID = Impl.getIdentifier(name); - if (!classID) + Optional contextID = lookupObjCClassID(name); + if (!contextID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\1'}); - if (known == Impl.ObjCContextTable->end()) + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return getUnversioned(*knownInfo); +} + +auto APINotesReader::lookupObjCProtocolID(StringRef name) + -> Optional { + if (!Impl.ObjCContextIDTable) + return None; + + Optional classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\1'}); + if (knownID == Impl.ObjCContextIDTable->end()) + return None; + + return ContextID(*knownID); +} + +auto APINotesReader::lookupObjCProtocolInfo(StringRef name) + -> Optional { + if (!Impl.ObjCContextInfoTable) + return None; + + Optional contextID = lookupObjCProtocolID(name); + if (!contextID) + return None; + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) + return None; + + return getUnversioned(*knownInfo); } Optional APINotesReader::lookupObjCProperty( @@ -1560,7 +1526,7 @@ Optional APINotesReader::lookupObjCProperty( if (known == Impl.ObjCPropertyTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupObjCMethod( @@ -1580,7 +1546,7 @@ Optional APINotesReader::lookupObjCMethod( if (known == Impl.ObjCMethodTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupGlobalVariable( @@ -1596,7 +1562,7 @@ Optional APINotesReader::lookupGlobalVariable( if (known == Impl.GlobalVariableTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupGlobalFunction( @@ -1612,7 +1578,7 @@ Optional APINotesReader::lookupGlobalFunction( if (known == Impl.GlobalFunctionTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupEnumConstant(StringRef name) { @@ -1627,7 +1593,7 @@ Optional APINotesReader::lookupEnumConstant(StringRef name) { if (known == Impl.EnumConstantTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupTag(StringRef name) { @@ -1642,7 +1608,7 @@ Optional APINotesReader::lookupTag(StringRef name) { if (known == Impl.TagTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional APINotesReader::lookupTypedef(StringRef name) { @@ -1657,48 +1623,61 @@ Optional APINotesReader::lookupTypedef(StringRef name) { if (known == Impl.TypedefTable->end()) return None; - return *known; + return getUnversioned(*known); } APINotesReader::Visitor::~Visitor() { } -void APINotesReader::Visitor::visitObjCClass(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCProtocol(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCMethod(ContextID contextID, - StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info) { } +void APINotesReader::Visitor::visitObjCClass( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } -void APINotesReader::Visitor::visitObjCProperty(ContextID contextID, - StringRef name, - bool isInstance, - const ObjCPropertyInfo &info) { } +void APINotesReader::Visitor::visitObjCProtocol( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCMethod( + ContextID contextID, + StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCProperty( + ContextID contextID, + StringRef name, + bool isInstance, + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalVariable( StringRef name, - const GlobalVariableInfo &info) { } + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalFunction( StringRef name, - const GlobalFunctionInfo &info) { } + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitEnumConstant( StringRef name, - const EnumConstantInfo &info) { } + const EnumConstantInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTag( StringRef name, - const TagInfo &info) { } + const TagInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTypedef( StringRef name, - const TypedefInfo &info) { } + const TypedefInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we @@ -1717,15 +1696,22 @@ void APINotesReader::visit(Visitor &visitor) { } // Visit classes and protocols. - if (Impl.ObjCContextTable) { - for (auto key : Impl.ObjCContextTable->keys()) { + if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) { + for (auto key : Impl.ObjCContextIDTable->keys()) { auto name = identifiers[key.first]; - auto info = *Impl.ObjCContextTable->find(key); - - if (key.second) - visitor.visitObjCProtocol(ContextID(info.first), name, info.second); - else - visitor.visitObjCClass(ContextID(info.first), name, info.second); + auto contextID = *Impl.ObjCContextIDTable->find(key); + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID); + if (knownInfo == Impl.ObjCContextInfoTable->end()) continue; + + for (const auto &versioned : *knownInfo) { + if (key.second) + visitor.visitObjCProtocol(ContextID(contextID), name, + versioned.second, versioned.first); + else + visitor.visitObjCClass(ContextID(contextID), name, versioned.second, + versioned.first); + } } } @@ -1754,8 +1740,9 @@ void APINotesReader::visit(Visitor &visitor) { for (auto key : Impl.ObjCMethodTable->keys()) { ContextID contextID(std::get<0>(key)); const auto &selector = selectors[std::get<1>(key)]; - auto info = *Impl.ObjCMethodTable->find(key); - visitor.visitObjCMethod(contextID, selector, std::get<2>(key), info); + for (const auto &versioned : *Impl.ObjCMethodTable->find(key)) + visitor.visitObjCMethod(contextID, selector, std::get<2>(key), + versioned.second, versioned.first); } } @@ -1765,8 +1752,10 @@ void APINotesReader::visit(Visitor &visitor) { ContextID contextID(std::get<0>(key)); auto name = identifiers[std::get<1>(key)]; char isInstance = std::get<2>(key); - auto info = *Impl.ObjCPropertyTable->find(key); - visitor.visitObjCProperty(contextID, name, isInstance, info); + for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) { + visitor.visitObjCProperty(contextID, name, isInstance, versioned.second, + versioned.first); + } } } @@ -1774,8 +1763,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalFunctionTable) { for (auto key : Impl.GlobalFunctionTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalFunctionTable->find(key); - visitor.visitGlobalFunction(name, info); + for (const auto &versioned : *Impl.GlobalFunctionTable->find(key)) + visitor.visitGlobalFunction(name, versioned.second, versioned.first); } } @@ -1783,8 +1772,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalVariableTable) { for (auto key : Impl.GlobalVariableTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalVariableTable->find(key); - visitor.visitGlobalVariable(name, info); + for (const auto &versioned : *Impl.GlobalVariableTable->find(key)) + visitor.visitGlobalVariable(name, versioned.second, versioned.first); } } @@ -1792,8 +1781,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.EnumConstantTable) { for (auto key : Impl.EnumConstantTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.EnumConstantTable->find(key); - visitor.visitEnumConstant(name, info); + for (const auto &versioned : *Impl.EnumConstantTable->find(key)) + visitor.visitEnumConstant(name, versioned.second, versioned.first); } } @@ -1801,8 +1790,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TagTable->find(key); - visitor.visitTag(name, info); + for (const auto &versioned : *Impl.TagTable->find(key)) + visitor.visitTag(name, versioned.second, versioned.first); } } @@ -1810,8 +1799,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TypedefTable) { for (auto key : Impl.TypedefTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TypedefTable->find(key); - visitor.visitTypedef(name, info); + for (const auto &versioned : *Impl.TypedefTable->find(key)) + visitor.visitTypedef(name, versioned.second, versioned.first); } } } diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 2df5e76f7c8..36c504afc34 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -24,12 +24,18 @@ #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/DataTypes.h" #include #include using namespace clang; using namespace api_notes; using namespace llvm::support; +namespace { + template using VersionedSmallVector = + SmallVector, 1>; +} + class APINotesWriter::Implementation { /// Mapping from strings to identifier IDs. llvm::StringMap IdentifierIDs; @@ -56,7 +62,8 @@ class APINotesWriter::Implementation { /// for a class (0) or protocol (1) and provides both the context ID and /// information describing the context within that module. llvm::DenseMap, - std::pair> ObjCContexts; + std::pair>> + ObjCContexts; /// Mapping from context IDs to the identifier ID holding the name. llvm::DenseMap ObjCContextNames; @@ -65,40 +72,56 @@ class APINotesWriter::Implementation { /// /// Indexed by the context ID, property name, and whether this is an /// instance property. - llvm::DenseMap, ObjCPropertyInfo> + llvm::DenseMap, + llvm::SmallVector, + 1>> ObjCProperties; /// Information about Objective-C methods. /// /// Indexed by the context ID, selector ID, and Boolean (stored as a /// char) indicating whether this is a class or instance method. - llvm::DenseMap, ObjCMethodInfo> + llvm::DenseMap, + llvm::SmallVector, 1>> ObjCMethods; /// Information about global variables. /// /// Indexed by the identifier ID. - llvm::DenseMap GlobalVariables; + llvm::DenseMap, + 1>> + GlobalVariables; /// Information about global functions. /// /// Indexed by the identifier ID. - llvm::DenseMap GlobalFunctions; + llvm::DenseMap, + 1>> + GlobalFunctions; /// Information about enumerators. /// /// Indexed by the identifier ID. - llvm::DenseMap EnumConstants; + llvm::DenseMap, + 1>> + EnumConstants; /// Information about tags. /// /// Indexed by the identifier ID. - llvm::DenseMap Tags; + llvm::DenseMap, 1>> + Tags; /// Information about typedefs. /// /// Indexed by the identifier ID. - llvm::DenseMap Typedefs; + llvm::DenseMap, 1>> + Typedefs; /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { @@ -194,7 +217,7 @@ void APINotesWriter::Implementation::writeBlockInfoBlock( BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); BLOCK(OBJC_CONTEXT_BLOCK); - BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_DATA); + BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA); BLOCK(OBJC_PROPERTY_BLOCK); BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); @@ -336,18 +359,15 @@ namespace { } /// Used to serialize the on-disk Objective-C context table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: using key_type = std::pair; // identifier ID, is-protocol using key_type_ref = key_type; - using data_type = std::pair; + using data_type = unsigned; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; - /// The number of bytes in a data entry. - static const unsigned dataBytes = 3; - hash_value_type ComputeHash(key_type_ref key) { return static_cast(llvm::hash_value(key)); } @@ -356,9 +376,7 @@ namespace { key_type_ref key, data_type_ref data) { uint32_t keyLength = sizeof(uint32_t) + 1; - uint32_t dataLength = sizeof(uint32_t) - + getCommonTypeInfoSize(data.second) - + dataBytes; + uint32_t dataLength = sizeof(uint32_t); endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -374,58 +392,92 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); - writer.write(data.first); - - emitCommonTypeInfo(out, data.second); - - // FIXME: Inefficient representation. - uint8_t bytes[dataBytes] = { 0, 0, 0 }; - if (auto nullable = data.second.getDefaultNullability()) { - bytes[0] = 1; - bytes[1] = static_cast(*nullable); - } else { - // Nothing to do. - } - bytes[2] = data.second.hasDesignatedInits(); - - out.write(reinterpret_cast(bytes), dataBytes); + writer.write(data); } }; } // end anonymous namespace -void APINotesWriter::Implementation::writeObjCContextBlock( - llvm::BitstreamWriter &writer) { - BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); +namespace { + /// Retrieve the serialized size of the given VersionTuple, for use in + /// on-disk hash tables. + unsigned getVersionTupleSize(const VersionTuple &version) { + unsigned size = sizeof(uint8_t) + /*major*/sizeof(uint32_t); + if (version.getMinor()) size += sizeof(uint32_t); + if (version.getSubminor()) size += sizeof(uint32_t); + if (version.getBuild()) size += sizeof(uint32_t); + return size; + } - if (ObjCContexts.empty()) - return; + /// Emit a serialized representation of a version tuple. + void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { + endian::Writer writer(out); - llvm::SmallString<4096> hashTableBlob; - uint32_t tableOffset; - { - llvm::OnDiskChainedHashTableGenerator generator; - for (auto &entry : ObjCContexts) - generator.insert(entry.first, entry.second); + // First byte contains the number of components beyond the 'major' + // component. + uint8_t descriptor; + if (version.getBuild()) descriptor = 3; + else if (version.getSubminor()) descriptor = 2; + else if (version.getMinor()) descriptor = 1; + else descriptor = 0; + assert(!version.usesUnderscores() && "Not a serializable version"); + writer.write(descriptor); + + // Write the components. + writer.write(version.getMajor()); + if (auto minor = version.getMinor()) + writer.write(*minor); + if (auto subminor = version.getSubminor()) + writer.write(*subminor); + if (auto build = version.getBuild()) + writer.write(*build); + } - llvm::raw_svector_ostream blobStream(hashTableBlob); - // Make sure that no bucket is at offset 0 - endian::Writer(blobStream).write(0); - tableOffset = generator.Emit(blobStream); + /// Localized helper to make a type dependent, thwarting template argument + /// deduction. + template + struct MakeDependent { + typedef T Type; + }; + + /// Determine the size of an array of versioned information, + template + unsigned getVersionedInfoSize( + const SmallVectorImpl> &infoArray, + llvm::function_ref::Type&)> + getInfoSize) { + unsigned result = sizeof(uint16_t); // # of elements + for (const auto &element : infoArray) { + result += getVersionTupleSize(element.first); + result += getInfoSize(element.second); + } + + return result; } - objc_context_block::ObjCContextDataLayout layout(writer); - layout.emit(ScratchRecord, tableOffset, hashTableBlob); -} + /// Emit versioned information. + template + void emitVersionedInfo( + raw_ostream &out, + const SmallVectorImpl> &infoArray, + llvm::function_ref::Type& info)> + emitInfo) { + endian::Writer writer(out); + writer.write(infoArray.size()); + for (const auto &element : infoArray) { + emitVersionTuple(out, element.first); + emitInfo(out, element.second); + } + } -namespace { /// Retrieve the serialized size of the given VariableInfo, for use in /// on-disk hash tables. - static unsigned getVariableInfoSize(const VariableInfo &info) { + unsigned getVariableInfoSize(const VariableInfo &info) { return 2 + getCommonEntityInfoSize(info); } /// Emit a serialized representation of the variable information. - static void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { + void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { emitCommonEntityInfo(out, info); uint8_t bytes[2] = { 0, 0 }; @@ -439,32 +491,96 @@ namespace { out.write(reinterpret_cast(bytes), 2); } - /// Used to serialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + /// On-dish hash table info key base for handling versioned data. + template + class VersionedTableInfo { + Derived &asDerived() { + return *static_cast(this); + } + + const Derived &asDerived() const { + return *static_cast(this); + } + public: - // (class ID, name ID, isInstance) - using key_type = std::tuple; + using key_type = KeyType; using key_type_ref = key_type; - using data_type = ObjCPropertyInfo; + using data_type = + SmallVector, 1>; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); + return llvm::hash_value(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); - uint32_t dataLength = getVariableInfoSize(data); + uint32_t keyLength = asDerived().getKeyLength(key); + uint32_t dataLength = getVersionedInfoSize(data, + [this](const UnversionedDataType &unversionedInfo) { + return asDerived().getUnversionedInfoSize(unversionedInfo); + }); + endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); return { keyLength, dataLength }; } + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitVersionedInfo(out, data, + [this](llvm::raw_ostream &out, + const UnversionedDataType &unversionedInfo) { + asDerived().emitUnversionedInfo(out, unversionedInfo); + }); + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + unsigned getUnversionedInfoSize(const ObjCContextInfo &info) { + return getCommonTypeInfoSize(info) + 1; + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCContextInfo &info) { + emitCommonTypeInfo(out, info); + + uint8_t payload = 0; + if (auto nullable = info.getDefaultNullability()) { + payload = (0x01 << 2) | static_cast(*nullable); + } + payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0); + out << payload; + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo + : public VersionedTableInfo, + ObjCPropertyInfo> { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); + } + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer writer(out); writer.write(std::get<0>(key)); @@ -472,13 +588,61 @@ namespace { writer.write(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace +void APINotesWriter::Implementation::writeObjCContextBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); + + if (ObjCContexts.empty()) + return; + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.first, entry.second.first); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextIDLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.second.first, entry.second.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextInfoLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } +} + void APINotesWriter::Implementation::writeObjCPropertyBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3); @@ -536,31 +700,13 @@ namespace { } /// Used to serialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using key_type = std::tuple; - using key_type_ref = key_type; - using data_type = ObjCMethodInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + 1; - uint32_t dataLength = getFunctionInfoSize(data) + 3; - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + 1; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -570,16 +716,18 @@ namespace { writer.write(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) { + return 1 + getFunctionInfoSize(info); + } + void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { + uint8_t payload = info.FactoryAsInit << 2; + payload = (payload | info.DesignatedInit) << 1; + payload = (payload | info.Required); endian::Writer writer(out); + writer.write(payload); - // FIXME: Inefficient representation - writer.write(data.DesignatedInit); - writer.write(data.FactoryAsInit); - writer.write(data.Required); + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -678,28 +826,13 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( namespace { /// Used to serialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalVariableInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getVariableInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref key) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -707,9 +840,13 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalVariableInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace @@ -740,28 +877,13 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock( namespace { /// Used to serialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalFunctionInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_value(key); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getFunctionInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -769,9 +891,13 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) { + return getFunctionInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalFunctionInfo &info) { + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -803,28 +929,13 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( namespace { /// Used to serialize the on-disk global enum constant. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = EnumConstantInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getCommonEntityInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -832,9 +943,12 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonEntityInfo(out, data); + unsigned getUnversionedInfoSize(const EnumConstantInfo &info) { + return getCommonEntityInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) { + emitCommonEntityInfo(out, info); } }; } // end anonymous namespace @@ -864,41 +978,32 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( } namespace { - /// Used to serialize the on-disk tag table. - class TagTableInfo { + template + class CommonTypeTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TagInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } + using key_type_ref = typename CommonTypeTableInfo::key_type_ref; - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(IdentifierID); } - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer writer(out); writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); + unsigned getUnversionedInfoSize(const UnversionedDataType &info) { + return getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const UnversionedDataType &info) { + emitCommonTypeInfo(out, info); } }; + + /// Used to serialize the on-disk tag table. + class TagTableInfo : public CommonTypeTableInfo { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTagBlock( @@ -927,40 +1032,8 @@ void APINotesWriter::Implementation::writeTagBlock( namespace { /// Used to serialize the on-disk typedef table. - class TypedefTableInfo { - public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TypedefInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; - } - - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer writer(out); - writer.write(key); - } - - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); - } - }; + class TypedefTableInfo + : public CommonTypeTableInfo { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTypedefBlock( @@ -1033,106 +1106,115 @@ void APINotesWriter::writeToStream(raw_ostream &os) { Impl.writeToStream(os); } -ContextID APINotesWriter::addObjCClass(StringRef name, - const ObjCContextInfo &info) { - IdentifierID classID = Impl.getIdentifier(name); +ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); - std::pair key(classID, 0); + std::pair key(nameID, isClass ? 0 : 1); auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { + if (known == Impl.ObjCContexts.end()) { unsigned nextID = Impl.ObjCContexts.size() + 1; + VersionedSmallVector emptyVersionedInfo; known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) + std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo))) .first; - Impl.ObjCContextNames[nextID] = classID; + Impl.ObjCContextNames[nextID] = nameID; } - return ContextID(known->second.first); -} - -ContextID APINotesWriter::addObjCProtocol(StringRef name, - const ObjCContextInfo &info) { - IdentifierID protocolID = Impl.getIdentifier(name); - - std::pair key(protocolID, 1); - auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { - unsigned nextID = Impl.ObjCContexts.size() + 1; - - known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) - .first; - - Impl.ObjCContextNames[nextID] = protocolID; + // Add this version information. + auto &versionedVec = known->second.second; + bool found = false; + for (auto &versioned : versionedVec){ + if (versioned.first == swiftVersion) { + versioned.second |= info; + found = true; + break; + } } + if (!found) + versionedVec.push_back({swiftVersion, info}); + return ContextID(known->second.first); } + void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.ObjCProperties.count(std::make_tuple(contextID.Value, nameID, isInstance))); - Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] = info; + Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] + .push_back({swiftVersion, info}); } void APINotesWriter::addObjCMethod(ContextID contextID, ObjCSelectorRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { SelectorID selectorID = Impl.getSelector(selector); auto key = std::tuple{ contextID.Value, selectorID, isInstanceMethod}; - assert(!Impl.ObjCMethods.count(key)); - Impl.ObjCMethods[key] = info; + Impl.ObjCMethods[key].push_back({swiftVersion, info}); // If this method is a designated initializer, update the class to note that // it has designated initializers. if (info.DesignatedInit) { assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value], (char)0})); - Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] - .second.setHasDesignatedInits(true); + auto &versionedVec = + Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] + .second; + bool found = false; + for (auto &versioned : versionedVec) { + if (versioned.first == swiftVersion) { + versioned.second.setHasDesignatedInits(true); + found = true; + break; + } + } + + if (!found) { + versionedVec.push_back({swiftVersion, ObjCContextInfo()}); + versionedVec.back().second.setHasDesignatedInits(true); + } } } void APINotesWriter::addGlobalVariable(llvm::StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { IdentifierID variableID = Impl.getIdentifier(name); - assert(!Impl.GlobalVariables.count(variableID)); - Impl.GlobalVariables[variableID] = info; + Impl.GlobalVariables[variableID].push_back({swiftVersion, info}); } void APINotesWriter::addGlobalFunction(llvm::StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.GlobalFunctions.count(nameID)); - Impl.GlobalFunctions[nameID] = info; + Impl.GlobalFunctions[nameID].push_back({swiftVersion, info}); } void APINotesWriter::addEnumConstant(llvm::StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { IdentifierID enumConstantID = Impl.getIdentifier(name); - assert(!Impl.EnumConstants.count(enumConstantID)); - Impl.EnumConstants[enumConstantID] = info; + Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info}); } -void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { IdentifierID tagID = Impl.getIdentifier(name); - assert(!Impl.Tags.count(tagID)); - Impl.Tags[tagID] = info; + Impl.Tags[tagID].push_back({swiftVersion, info}); } -void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { IdentifierID typedefID = Impl.getIdentifier(name); - assert(!Impl.Typedefs.count(typedefID)); - Impl.Typedefs[typedefID] = info; + Impl.Typedefs[typedefID].push_back({swiftVersion, info}); } void APINotesWriter::addModuleOptions(ModuleOptions opts) { diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 0d30731194c..f15099e4677 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -14,6 +14,7 @@ #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/Types.h" #include "clang/APINotes/APINotesWriter.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/SourceMgr.h" @@ -262,9 +263,7 @@ namespace { }; typedef std::vector TypedefsSeq; - struct Module { - StringRef Name; - AvailabilityItem Availability; + struct TopLevelItems { ClassesSeq Classes; ClassesSeq Protocols; FunctionsSeq Functions; @@ -272,6 +271,20 @@ namespace { EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; + }; + + struct Versioned { + VersionTuple Version; + TopLevelItems Items; + }; + + typedef std::vector VersionedSeq; + + struct Module { + StringRef Name; + AvailabilityItem Availability; + TopLevelItems TopLevel; + VersionedSeq SwiftVersions; llvm::Optional SwiftInferImportAsMember = {llvm::None}; @@ -291,6 +304,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) +LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned) namespace llvm { namespace yaml { @@ -335,6 +349,24 @@ namespace llvm { } }; + template <> + struct ScalarTraits { + static void output(const VersionTuple &value, void*, + llvm::raw_ostream &out) { + out << value; + } + static StringRef input(StringRef scalar, void*, VersionTuple &value) { + if (value.tryParse(scalar)) + return "not a version number in the form XX.YY"; + + // Canonicalize on '.' as a separator. + value.UseDotAsSeparator(); + return StringRef(); + } + + static bool mustQuote(StringRef) { return false; } + }; + template <> struct MappingTraits { static void mapping(IO &io, Param& p) { @@ -460,6 +492,24 @@ namespace llvm { } }; + static void mapTopLevelItems(IO &io, TopLevelItems &i) { + io.mapOptional("Classes", i.Classes); + io.mapOptional("Protocols", i.Protocols); + io.mapOptional("Functions", i.Functions); + io.mapOptional("Globals", i.Globals); + io.mapOptional("Enumerators", i.EnumConstants); + io.mapOptional("Tags", i.Tags); + io.mapOptional("Typedefs", i.Typedefs); + } + + template <> + struct MappingTraits { + static void mapping(IO &io, Versioned& v) { + io.mapRequired("Version", v.Version); + mapTopLevelItems(io, v.Items); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Module& m) { @@ -467,13 +517,10 @@ namespace llvm { io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); - io.mapOptional("Classes", m.Classes); - io.mapOptional("Protocols", m.Protocols); - io.mapOptional("Functions", m.Functions); - io.mapOptional("Globals", m.Globals); - io.mapOptional("Enumerators", m.EnumConstants); - io.mapOptional("Tags", m.Tags); - io.mapOptional("Typedefs", m.Typedefs); + + mapTopLevelItems(io, m.TopLevel); + + io.mapOptional("SwiftVersions", m.SwiftVersions); } }; } @@ -624,7 +671,8 @@ namespace { // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, - ContextID classID, StringRef className) { + ContextID classID, StringRef className, + VersionTuple swiftVersion) { ObjCMethodInfo mInfo; if (convertCommon(meth, mInfo, meth.Selector)) @@ -662,10 +710,11 @@ namespace { // Write it. Writer->addObjCMethod(classID, selectorRef, meth.Kind == MethodKind::Instance, - mInfo); + mInfo, swiftVersion); } - void convertContext(const Class &cl, bool isClass) { + void convertContext(const Class &cl, bool isClass, + VersionTuple swiftVersion) { // Write the class. ObjCContextInfo cInfo; @@ -675,8 +724,8 @@ namespace { if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : - Writer->addObjCProtocol(cl.Name, cInfo); + ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, + swiftVersion); // Write all methods. llvm::StringMap> knownMethods; @@ -693,7 +742,7 @@ namespace { } known = true; - convertMethod(method, clID, cl.Name); + convertMethod(method, clID, cl.Name, swiftVersion); } // Write all properties. @@ -726,51 +775,45 @@ namespace { pInfo.setNullabilityAudited(*prop.Nullability); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, - *prop.Kind == MethodKind::Instance, pInfo); + *prop.Kind == MethodKind::Instance, pInfo, + swiftVersion); } else { // Add both instance and class properties with this name. - Writer->addObjCProperty(clID, prop.Name, true, pInfo); - Writer->addObjCProperty(clID, prop.Name, false, pInfo); + Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion); + Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion); } } } - bool convertModule() { - if (!isAvailable(TheModule.Availability)) - return false; - - // Set up the writer. - // FIXME: This is kindof ugly. - APINotesWriter writer(TheModule.Name, SourceFile); - Writer = &writer; - + void convertTopLevelItems(const TopLevelItems &items, + VersionTuple swiftVersion) { // Write all classes. llvm::StringSet<> knownClasses; - for (const auto &cl : TheModule.Classes) { + for (const auto &cl : items.Classes) { // Check for duplicate class definitions. if (!knownClasses.insert(cl.Name).second) { emitError("multiple definitions of class '" + cl.Name + "'"); continue; } - convertContext(cl, /*isClass*/ true); + convertContext(cl, /*isClass*/ true, swiftVersion); } // Write all protocols. llvm::StringSet<> knownProtocols; - for (const auto &pr : TheModule.Protocols) { + for (const auto &pr : items.Protocols) { // Check for duplicate protocol definitions. if (!knownProtocols.insert(pr.Name).second) { emitError("multiple definitions of protocol '" + pr.Name + "'"); continue; } - convertContext(pr, /*isClass*/ false); + convertContext(pr, /*isClass*/ false, swiftVersion); } // Write all global variables. llvm::StringSet<> knownGlobals; - for (const auto &global : TheModule.Globals) { + for (const auto &global : items.Globals) { // Check for duplicate global variables. if (!knownGlobals.insert(global.Name).second) { emitError("multiple definitions of global variable '" + @@ -786,12 +829,12 @@ namespace { info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); - Writer->addGlobalVariable(global.Name, info); + Writer->addGlobalVariable(global.Name, info, swiftVersion); } // Write all global functions. llvm::StringSet<> knownFunctions; - for (const auto &function : TheModule.Functions) { + for (const auto &function : items.Functions) { // Check for duplicate global functions. if (!knownFunctions.insert(function.Name).second) { emitError("multiple definitions of global function '" + @@ -810,12 +853,12 @@ namespace { function.NullabilityOfRet, info, function.Name); - Writer->addGlobalFunction(function.Name, info); + Writer->addGlobalFunction(function.Name, info, swiftVersion); } // Write all enumerators. llvm::StringSet<> knownEnumConstants; - for (const auto &enumConstant : TheModule.EnumConstants) { + for (const auto &enumConstant : items.EnumConstants) { // Check for duplicate enumerators if (!knownEnumConstants.insert(enumConstant.Name).second) { emitError("multiple definitions of enumerator '" + @@ -829,12 +872,12 @@ namespace { convertAvailability(enumConstant.Availability, info, enumConstant.Name); info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; - Writer->addEnumConstant(enumConstant.Name, info); + Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); } // Write all tags. llvm::StringSet<> knownTags; - for (const auto &t : TheModule.Tags) { + for (const auto &t : items.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { emitError("multiple definitions Of tag '" + t.Name + "'"); @@ -845,12 +888,12 @@ namespace { if (convertCommonType(t, tagInfo, t.Name)) continue; - Writer->addTag(t.Name, tagInfo); + Writer->addTag(t.Name, tagInfo, swiftVersion); } // Write all typedefs. llvm::StringSet<> knownTypedefs; - for (const auto &t : TheModule.Typedefs) { + for (const auto &t : items.Typedefs) { // Check for duplicate typedef definitions. if (!knownTypedefs.insert(t.Name).second) { emitError("multiple definitions of typedef '" + t.Name + "'"); @@ -860,9 +903,22 @@ namespace { TypedefInfo typedefInfo; if (convertCommonType(t, typedefInfo, t.Name)) continue; - - Writer->addTypedef(t.Name, typedefInfo); + + Writer->addTypedef(t.Name, typedefInfo, swiftVersion); } + } + + bool convertModule() { + if (!isAvailable(TheModule.Availability)) + return false; + + // Set up the writer. + // FIXME: This is kindof ugly. + APINotesWriter writer(TheModule.Name, SourceFile); + Writer = &writer; + + // Write the top-level items. + convertTopLevelItems(TheModule.TopLevel, VersionTuple()); if (TheModule.SwiftInferImportAsMember) { ModuleOptions opts; @@ -870,6 +926,12 @@ namespace { Writer->addModuleOptions(opts); } + // Convert the versioned information. + for (const auto &versioned : TheModule.SwiftVersions) { + convertTopLevelItems(versioned.Items, versioned.Version); + + } + if (!ErrorOccured) Writer->writeToStream(OS); @@ -934,10 +996,34 @@ namespace { /// The module we're building. Module TheModule; + /// A known context, which tracks what we know about a context ID. + struct KnownContext { + /// Whether this is a protocol (vs. a class). + bool isProtocol; + + /// The indices into the top-level items for this context at each + /// Swift version. + SmallVector, 1> indices; + + Class &getContext(const VersionTuple &swiftVersion, + TopLevelItems &items) { + ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes; + + for (auto &index : indices) { + if (index.first == swiftVersion) + return seq[index.second]; + } + + indices.push_back({swiftVersion, seq.size()}); + seq.push_back(Class()); + return seq.back(); + } + }; + /// A mapping from context ID to a pair (index, is-protocol) that indicates /// the index of that class or protocol in the global "classes" or /// "protocols" list. - llvm::DenseMap> knownContexts; + llvm::DenseMap knownContexts; /// Copy a string into allocated memory so it does disappear on us. StringRef copyString(StringRef string) { @@ -1015,30 +1101,46 @@ namespace { } } + TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) { + if (!swiftVersion) return TheModule.TopLevel; + + for (auto &versioned : TheModule.SwiftVersions) { + if (versioned.Version == swiftVersion) + return versioned.Items; + } + + TheModule.SwiftVersions.push_back(Versioned()); + TheModule.SwiftVersions.back().Version = swiftVersion; + return TheModule.SwiftVersions.back().Items; + } + public: virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Classes.size(), false }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = false; - // Add the class. - TheModule.Classes.push_back(Class()); - handleObjCContext(TheModule.Classes.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Protocols.size(), true }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = true; - // Add the protocol. - TheModule.Protocols.push_back(Class()); - handleObjCContext(TheModule.Protocols.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { Method method; method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; @@ -1051,16 +1153,15 @@ namespace { method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Methods.push_back(method); - else - TheModule.Classes[known.first].Methods.push_back(method); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Methods.push_back(method); } virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { Property property; property.Name = name; property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; @@ -1071,15 +1172,14 @@ namespace { property.Nullability = *nullability; } - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Properties.push_back(property); - else - TheModule.Classes[known.first].Properties.push_back(property); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Properties.push_back(property); } virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { Function function; function.Name = name; handleCommon(function, info); @@ -1088,11 +1188,13 @@ namespace { handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); - TheModule.Functions.push_back(function); + auto &items = getTopLevelItems(swiftVersion); + items.Functions.push_back(function); } virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { GlobalVariable global; global.Name = name; handleCommon(global, info); @@ -1102,30 +1204,37 @@ namespace { global.Nullability = *nullability; } - TheModule.Globals.push_back(global); + auto &items = getTopLevelItems(swiftVersion); + items.Globals.push_back(global); } virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { EnumConstant enumConstant; enumConstant.Name = name; handleCommon(enumConstant, info); - TheModule.EnumConstants.push_back(enumConstant); + auto &items = getTopLevelItems(swiftVersion); + items.EnumConstants.push_back(enumConstant); } - virtual void visitTag(StringRef name, const TagInfo &info) { + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { Tag tag; tag.Name = name; handleCommonType(tag, info); - TheModule.Tags.push_back(tag); + auto &items = getTopLevelItems(swiftVersion); + items.Tags.push_back(tag); } - virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { Typedef td; td.Name = name; handleCommonType(td, info); - TheModule.Typedefs.push_back(td); + auto &items = getTopLevelItems(swiftVersion); + items.Typedefs.push_back(td); } /// Retrieve the module. @@ -1138,38 +1247,16 @@ static unsigned flattenPropertyKind(llvm::Optional kind) { return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; } -bool api_notes::decompileAPINotes(std::unique_ptr input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - - DecompileVisitor decompileVisitor; - reader->visit(decompileVisitor); - - // Sort the data in the module, because the API notes reader doesn't preserve - // order. - auto &module = decompileVisitor.getModule(); - - // Set module name. - module.Name = reader->getModuleName(); - - // Set module options - auto opts = reader->getModuleOptions(); - if (opts.SwiftInferImportAsMember) - module.SwiftInferImportAsMember = true; - +/// Sort the items in the given block of "top-level" items. +static void sortTopLevelItems(TopLevelItems &items) { // Sort classes. - std::sort(module.Classes.begin(), module.Classes.end(), + std::sort(items.Classes.begin(), items.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort protocols. - std::sort(module.Protocols.begin(), module.Protocols.end(), + std::sort(items.Protocols.begin(), items.Protocols.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); @@ -1180,52 +1267,90 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, std::sort(record.Properties.begin(), record.Properties.end(), [](const Property &lhs, const Property &rhs) -> bool { return lhs.Name < rhs.Name || - (lhs.Name == rhs.Name && - flattenPropertyKind(lhs.Kind) < - flattenPropertyKind(rhs.Kind)); + (lhs.Name == rhs.Name && + flattenPropertyKind(lhs.Kind) < + flattenPropertyKind(rhs.Kind)); }); // Sort methods. std::sort(record.Methods.begin(), record.Methods.end(), [](const Method &lhs, const Method &rhs) -> bool { return lhs.Selector < rhs.Selector || - (lhs.Selector == rhs.Selector && - static_cast(lhs.Kind) - < static_cast(rhs.Kind)); + (lhs.Selector == rhs.Selector && + static_cast(lhs.Kind) + < static_cast(rhs.Kind)); }); }; - std::for_each(module.Classes.begin(), module.Classes.end(), sortMembers); - std::for_each(module.Protocols.begin(), module.Protocols.end(), sortMembers); + std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers); + std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers); // Sort functions. - std::sort(module.Functions.begin(), module.Functions.end(), + std::sort(items.Functions.begin(), items.Functions.end(), [](const Function &lhs, const Function &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort global variables. - std::sort(module.Globals.begin(), module.Globals.end(), + std::sort(items.Globals.begin(), items.Globals.end(), [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort enum constants. - std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + std::sort(items.EnumConstants.begin(), items.EnumConstants.end(), [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort tags. - std::sort(module.Tags.begin(), module.Tags.end(), + std::sort(items.Tags.begin(), items.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort typedefs. - std::sort(module.Typedefs.begin(), module.Typedefs.end(), + std::sort(items.Typedefs.begin(), items.Typedefs.end(), [](const Typedef &lhs, const Typedef &rhs) -> bool { return lhs.Name < rhs.Name; }); +} + +bool api_notes::decompileAPINotes(std::unique_ptr input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + + DecompileVisitor decompileVisitor; + reader->visit(decompileVisitor); + + // Sort the data in the module, because the API notes reader doesn't preserve + // order. + auto &module = decompileVisitor.getModule(); + + // Set module name. + module.Name = reader->getModuleName(); + + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + + // Sort the top-level items. + sortTopLevelItems(module.TopLevel); + + // Sort the Swift versions. + std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(), + [](const Versioned &lhs, const Versioned &rhs) -> bool { + return lhs.Version < rhs.Version; + }); + + // Sort the top-level items within each Swift version. + for (auto &versioned : module.SwiftVersions) + sortTopLevelItems(versioned.Items); // Output the YAML representation. Output yout(os); diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 29861d140f2..4a666ccbd94 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -352,8 +352,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCClass(Class->getName())) { - ::ProcessAPINotes(*this, Class, Info->second); + if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { + ::ProcessAPINotes(*this, Class, *Info); } } @@ -363,8 +363,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, Info->second); + if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { + ::ProcessAPINotes(*this, Protocol, *Info); } } @@ -414,8 +414,8 @@ void Sema::ProcessAPINotes(Decl *D) { auto GetContext = [&](api_notes::APINotesReader *Reader) -> Optional { if (auto Protocol = dyn_cast(ObjCContainer)) { - if (auto Found = Reader->lookupObjCProtocol(Protocol->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) + return *Found; return None; } @@ -442,8 +442,8 @@ void Sema::ProcessAPINotes(Decl *D) { } if (auto Class = dyn_cast(ObjCContainer)) { - if (auto Found = Reader->lookupObjCClass(Class->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCClassID(Class->getName())) + return *Found; return None; diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 10ac249817d..c0e91f93fde 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -146,3 +146,22 @@ Typedefs: SwiftName: Typedef SwiftBridge: '' NSErrorDomain: '' +SwiftVersions: + - Version: 3.0 + Classes: + - Name: NSCell + Availability: available + AvailabilityMsg: '' + SwiftPrivate: false + SwiftName: NSBox + SwiftBridge: '' + NSErrorDomain: '' + Methods: + - Selector: init + MethodKind: Instance + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + SwiftPrivate: true + SwiftName: '' + DesignatedInit: true From 847455db5437610576dbbf4e34d2c4e426dfcf08 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Oct 2016 00:12:26 -0700 Subject: [PATCH 2/4] [Modules] Fix module hash computation when module file extensions are involved. Fix an egregious error in with modules and module file extensions, where only the hash code of the module file extension and sysroots would affect the module cache, leading to module file collisions. The effect of this error was likely masked by the old client of module file extensions (Swift) embedding Clang version information. (cherry picked from commit 977557058d1208e7d099569d3327c00c1aa4b1cf) --- lib/Frontend/CompilerInvocation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index bf657706beb..6e5a90d1006 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2502,7 +2502,9 @@ std::string CompilerInvocation::getModuleHash() const { // Extend the signature with the module file extensions. const FrontendOptions &frontendOpts = getFrontendOpts(); for (const auto &ext : frontendOpts.ModuleFileExtensions) { - code = ext->hashExtension(code); + code = hash_combine(code, ext->hashExtension(code)); + } + } // Darwin-specific hack: if we have a sysroot, use the contents and From 37a2480b26b48b6032d17adbf1246b84bc42d9eb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Oct 2016 00:12:34 -0700 Subject: [PATCH 3/4] [API Notes] Add a command-line option for Swift version selection. Introduce -fapinotes-swift-version=XX.YY, which sets the Swift version to use when applying versioned API notes to the Clang AST. When set, a versioned API note that matches the provided Swift version will take precedence over an unversioned API note. More of rdar://problem/28455809. (cherry picked from commit e48790bfa156772574ef81db98f79da29eebe445) --- include/clang/APINotes/APINotesManager.h | 9 ++ include/clang/APINotes/APINotesOptions.h | 4 + include/clang/APINotes/APINotesReader.h | 84 ++++++++++++--- include/clang/Driver/Options.td | 3 + lib/APINotes/APINotesManager.cpp | 7 +- lib/APINotes/APINotesReader.cpp | 102 +++++++++++------- lib/APINotes/APINotesYAMLCompiler.cpp | 2 +- lib/Frontend/CompilerInstance.cpp | 3 + lib/Frontend/CompilerInvocation.cpp | 20 +++- .../APINotes/SomeKit.apinotes | 9 ++ test/APINotes/nullability.m | 12 ++- 11 files changed, 195 insertions(+), 60 deletions(-) diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index a300c14ee9d..2adc29c0bf4 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -16,6 +16,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Module.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -55,6 +56,9 @@ class APINotesManager { /// source file from which an entity was declared. bool ImplicitAPINotes; + /// The Swift version to use when interpreting versioned API notes. + VersionTuple SwiftVersion; + /// API notes readers for the current module. /// /// There can be up to two of these, one for public headers and one @@ -109,6 +113,11 @@ class APINotesManager { APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Set the Swift version to use when filtering API notes. + void setSwiftVersion(VersionTuple swiftVersion) { + SwiftVersion = swiftVersion; + } + /// Load the API notes for the current module. /// /// \param module The current module. diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h index 01c7513a1d3..24bb9134b21 100644 --- a/include/clang/APINotes/APINotesOptions.h +++ b/include/clang/APINotes/APINotesOptions.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H #define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#include "clang/Basic/VersionTuple.h" #include #include @@ -23,6 +24,9 @@ namespace clang { /// notes are found and handled. class APINotesOptions { public: + /// The Swift version which should be used for API notes. + VersionTuple SwiftVersion; + /// The set of search paths where we API notes can be found for /// particular modules. /// diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 593e8174d4e..a09bcc76df1 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -33,7 +33,7 @@ class APINotesReader { Implementation &Impl; APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, - bool &failed); + VersionTuple swiftVersion, bool &failed); public: /// Create a new API notes reader from the given member buffer, which @@ -41,14 +41,16 @@ class APINotesReader { /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr - get(std::unique_ptr inputBuffer); + get(std::unique_ptr inputBuffer, + VersionTuple swiftVersion); /// Create a new API notes reader from the given member buffer, which /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr - getUnmanaged(llvm::MemoryBuffer *inputBuffer); + getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion); ~APINotesReader(); @@ -66,6 +68,56 @@ class APINotesReader { /// Retrieve the module options ModuleOptions getModuleOptions() const; + /// Captures the completed versioned information for a particular part of + /// API notes, including both unversioned API notes and each versioned API + /// note for that particular entity. + template + class VersionedInfo { + /// The complete set of results. + SmallVector, 1> Results; + + /// The index of the result that is the "selected" set based on the desired + /// Swift version, or \c Results.size() if nothing matched. + unsigned Selected; + + public: + /// Form an empty set of versioned information. + VersionedInfo(llvm::NoneType) : Selected(0) { } + + /// Form a versioned info set given the desired version and a set of + /// results. + VersionedInfo(VersionTuple version, + SmallVector, 1> results); + + /// Determine whether there is a result that should be applied directly + /// to the AST. + explicit operator bool() const { return Selected != size(); } + + /// Retrieve the information to apply directly to the AST. + const T& operator*() const { + assert(*this && "No result to apply directly"); + return (*this)[Selected].second; + } + + /// Retrieve the selected index in the result set. + Optional getSelected() const { + if (Selected == Results.size()) return None; + return Selected; + } + + /// Return the number of versioned results we know about. + unsigned size() const { return Results.size(); } + + /// Access all versioned results. + const std::pair *begin() const { return Results.begin(); } + const std::pair *end() const { return Results.end(); } + + /// Access a specific versioned result. + const std::pair &operator[](unsigned index) const { + return Results[index]; + } + }; + /// Look for the context ID of the given Objective-C class. /// /// \param name The name of the class we're looking for. @@ -78,7 +130,7 @@ class APINotesReader { /// \param name The name of the class we're looking for. /// /// \returns The information about the class, if known. - Optional lookupObjCClassInfo(StringRef name); + VersionedInfo lookupObjCClassInfo(StringRef name); /// Look for the context ID of the given Objective-C protocol. /// @@ -92,7 +144,7 @@ class APINotesReader { /// \param name The name of the protocol we're looking for. /// /// \returns The information about the protocol, if known. - Optional lookupObjCProtocolInfo(StringRef name); + VersionedInfo lookupObjCProtocolInfo(StringRef name); /// Look for information regarding the given Objective-C property in /// the given context. @@ -104,9 +156,9 @@ class APINotesReader { /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. - Optional lookupObjCProperty(ContextID contextID, - StringRef name, - bool isInstance); + VersionedInfo lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance); /// Look for information regarding the given Objective-C method in /// the given context. @@ -116,30 +168,30 @@ class APINotesReader { /// \param isInstanceMethod Whether we are looking for an instance method. /// /// \returns Information about the method, if known. - Optional lookupObjCMethod(ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod); + VersionedInfo lookupObjCMethod(ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod); /// Look for information regarding the given global variable. /// /// \param name The name of the global variable. /// /// \returns information about the global variable, if known. - Optional lookupGlobalVariable(StringRef name); + VersionedInfo lookupGlobalVariable(StringRef name); /// Look for information regarding the given global function. /// /// \param name The name of the global function. /// /// \returns information about the global function, if known. - Optional lookupGlobalFunction(StringRef name); + VersionedInfo lookupGlobalFunction(StringRef name); /// Look for information regarding the given enumerator. /// /// \param name The name of the enumerator. /// /// \returns information about the enumerator, if known. - Optional lookupEnumConstant(StringRef name); + VersionedInfo lookupEnumConstant(StringRef name); /// Look for information regarding the given tag /// (struct/union/enum/C++ class). @@ -147,14 +199,14 @@ class APINotesReader { /// \param name The name of the tag. /// /// \returns information about the tag, if known. - Optional lookupTag(StringRef name); + VersionedInfo lookupTag(StringRef name); /// Look for information regarding the given typedef. /// /// \param name The name of the typedef. /// /// \returns information about the typedef, if known. - Optional lookupTypedef(StringRef name); + VersionedInfo lookupTypedef(StringRef name); /// Visitor used when walking the contents of the API notes file. class Visitor { diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index cbd2a4a7343..c22106b023d 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -551,6 +551,9 @@ def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group">, HelpText<"Specify the API notes cache path">; +def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, + Group, Flags<[CC1Option]>, MetaVarName<"">, + HelpText<"Specify the Swift version to use when filtering API notes">; def fblocks : Flag<["-"], "fblocks">, Group, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp index 834d7e1f72f..1622f8f4067 100644 --- a/lib/APINotes/APINotesManager.cpp +++ b/lib/APINotes/APINotesManager.cpp @@ -153,7 +153,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { if (!buffer) return nullptr; // Load the binary form. - return APINotesReader::getUnmanaged(buffer); + return APINotesReader::getUnmanaged(buffer, SwiftVersion); } // If we haven't pruned the API notes cache yet during this execution, do @@ -184,7 +184,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { // Load the file contents. if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Load the file. - if (auto reader = APINotesReader::get(std::move(buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()), + SwiftVersion)) { bool outOfDate = false; if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) { if (sizeAndModTime->first != apiNotesFile->getSize() || @@ -272,7 +273,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { } // Load the binary form we just compiled. - auto reader = APINotesReader::get(std::move(compiledBuffer)); + auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion); assert(reader && "Could not load the API notes we just generated?"); return reader; } diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 71e1e411602..dda721e4112 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -486,6 +486,9 @@ class APINotesReader::Implementation { /// Whether we own the input buffer. bool OwnsInputBuffer; + /// The Swift version to use for filtering. + VersionTuple SwiftVersion; + /// The reader attached to \c InputBuffer. llvm::BitstreamReader InputReader; @@ -1252,6 +1255,7 @@ bool APINotesReader::Implementation::readTypedefBlock( APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, + VersionTuple swiftVersion, bool &failed) : Impl(*new Implementation) { @@ -1260,6 +1264,7 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, // Initialize the input buffer. Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; + Impl.SwiftVersion = swiftVersion; Impl.InputReader.init( reinterpret_cast(Impl.InputBuffer->getBufferStart()), reinterpret_cast(Impl.InputBuffer->getBufferEnd())); @@ -1399,11 +1404,12 @@ APINotesReader::~APINotesReader() { } std::unique_ptr -APINotesReader::get(std::unique_ptr inputBuffer) { +APINotesReader::get(std::unique_ptr inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1411,11 +1417,12 @@ APINotesReader::get(std::unique_ptr inputBuffer) { } std::unique_ptr -APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer) { +APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1435,15 +1442,31 @@ ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } -namespace { - template - Optional getUnversioned( - const SmallVectorImpl>& array) { - for (const auto &versioned : array) { - if (!versioned.first) return versioned.second; +template +APINotesReader::VersionedInfo::VersionedInfo( + VersionTuple version, + SmallVector, 1> results) + : Results(std::move(results)) { + + // Look for an exact version match. + Optional unversioned; + Selected = Results.size(); + for (unsigned i = 0, n = Results.size(); i != n; ++i) { + if (Results[i].first == version) { + Selected = i; + break; + } + + if (!Results[i].first) { + assert(!unversioned && "Two unversioned entries?"); + unversioned = i; } - return None; } + + // If we didn't find a match but we have an unversioned result, use the + // unversioned result. + if (Selected == Results.size() && unversioned) + Selected = *unversioned; } auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional { @@ -1462,7 +1485,7 @@ auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional { } auto APINotesReader::lookupObjCClassInfo(StringRef name) - -> Optional { + -> VersionedInfo { if (!Impl.ObjCContextInfoTable) return None; @@ -1474,7 +1497,7 @@ auto APINotesReader::lookupObjCClassInfo(StringRef name) if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - return getUnversioned(*knownInfo); + return { Impl.SwiftVersion, *knownInfo }; } auto APINotesReader::lookupObjCProtocolID(StringRef name) @@ -1494,7 +1517,7 @@ auto APINotesReader::lookupObjCProtocolID(StringRef name) } auto APINotesReader::lookupObjCProtocolInfo(StringRef name) - -> Optional { + -> VersionedInfo { if (!Impl.ObjCContextInfoTable) return None; @@ -1506,13 +1529,14 @@ auto APINotesReader::lookupObjCProtocolInfo(StringRef name) if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - return getUnversioned(*knownInfo); + return { Impl.SwiftVersion, *knownInfo }; } -Optional APINotesReader::lookupObjCProperty( - ContextID contextID, - StringRef name, - bool isInstance) { + +auto APINotesReader::lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance) + -> VersionedInfo { if (!Impl.ObjCPropertyTable) return None; @@ -1526,13 +1550,14 @@ Optional APINotesReader::lookupObjCProperty( if (known == Impl.ObjCPropertyTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupObjCMethod( - ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod) { +auto APINotesReader::lookupObjCMethod( + ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod) + -> VersionedInfo { if (!Impl.ObjCMethodTable) return None; @@ -1546,11 +1571,12 @@ Optional APINotesReader::lookupObjCMethod( if (known == Impl.ObjCMethodTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupGlobalVariable( - StringRef name) { +auto APINotesReader::lookupGlobalVariable( + StringRef name) + -> VersionedInfo { if (!Impl.GlobalVariableTable) return None; @@ -1562,11 +1588,11 @@ Optional APINotesReader::lookupGlobalVariable( if (known == Impl.GlobalVariableTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupGlobalFunction( - StringRef name) { +auto APINotesReader::lookupGlobalFunction(StringRef name) + -> VersionedInfo { if (!Impl.GlobalFunctionTable) return None; @@ -1578,10 +1604,11 @@ Optional APINotesReader::lookupGlobalFunction( if (known == Impl.GlobalFunctionTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupEnumConstant(StringRef name) { +auto APINotesReader::lookupEnumConstant(StringRef name) + -> VersionedInfo { if (!Impl.EnumConstantTable) return None; @@ -1593,10 +1620,10 @@ Optional APINotesReader::lookupEnumConstant(StringRef name) { if (known == Impl.EnumConstantTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupTag(StringRef name) { +auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo { if (!Impl.TagTable) return None; @@ -1608,10 +1635,11 @@ Optional APINotesReader::lookupTag(StringRef name) { if (known == Impl.TagTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupTypedef(StringRef name) { +auto APINotesReader::lookupTypedef(StringRef name) + -> VersionedInfo { if (!Impl.TypedefTable) return None; @@ -1623,7 +1651,7 @@ Optional APINotesReader::lookupTypedef(StringRef name) { if (known == Impl.TypedefTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } APINotesReader::Visitor::~Visitor() { } diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index f15099e4677..8f1e5a29d4e 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -1318,7 +1318,7 @@ static void sortTopLevelItems(TopLevelItems &items) { bool api_notes::decompileAPINotes(std::unique_ptr input, llvm::raw_ostream &os) { // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); + auto reader = APINotesReader::get(std::move(input), VersionTuple()); if (!reader) { llvm::errs() << "not a well-formed API notes binary file\n"; return true; diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index a68d87b4186..d3b1df0faef 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -540,6 +540,9 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + // Set up API notes. + TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion); + // If we're building a module and are supposed to load API notes, // notify the API notes manager. if (auto currentModule = getPreprocessor().getCurrentModule()) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 6e5a90d1006..c1635d55dcb 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1473,8 +1473,14 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } -static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args, + DiagnosticsEngine &diags) { using namespace options; + if (const Arg *A = Args.getLastArg(OPT_fapinotes_swift_version)) { + if (Opts.SwiftVersion.tryParse(A->getValue())) + diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) Opts.ModuleSearchPaths.push_back(A->getValue()); } @@ -2369,7 +2375,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); - ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the @@ -2505,6 +2511,16 @@ std::string CompilerInvocation::getModuleHash() const { code = hash_combine(code, ext->hashExtension(code)); } + // Extend the signature with the SWift version for API notes. + const APINotesOptions &apiNotesOpts = getAPINotesOpts(); + if (apiNotesOpts.SwiftVersion) { + code = hash_combine(code, apiNotesOpts.SwiftVersion.getMajor()); + if (auto minor = apiNotesOpts.SwiftVersion.getMinor()) + code = hash_combine(code, *minor); + if (auto subminor = apiNotesOpts.SwiftVersion.getSubminor()) + code = hash_combine(code, *subminor); + if (auto build = apiNotesOpts.SwiftVersion.getBuild()) + code = hash_combine(code, *build); } // Darwin-specific hack: if we have a sysroot, use the contents and diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index e79a210e39a..9c855c6dc6a 100644 --- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -31,3 +31,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index fc149b465ea..c11cea5bd52 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,12 +1,22 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 + #import int main() { A *a; - [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#if SWIFT_VERSION_3_0 + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A * _Nullable'}} + [a transform: 0 integer: 0]; +#else + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A *'}} + [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#endif [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} [A setNonnullAInstance: 0]; // no warning From 15be1756fd0f2875b64ba1c5402c37258cc329e4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Oct 2016 00:39:43 -0700 Subject: [PATCH 4/4] [API Notes] Look for framework API notes in Headers/PrivateHeaders. Rather than relying on the addition of a new "top-level" directory within a framework, have frameworks put API notes alongside the headers. (cherry picked from commit 1237d45515611f604812d85758bf0bf0fea927e1) --- lib/APINotes/APINotesManager.cpp | 16 ++++--- .../Headers/SomeKit.apinotes | 42 +++++++++++++++++++ .../Headers/SomeKitExplicitNullability.h | 5 +++ .../PrivateHeaders/SomeKit_private.apinotes | 15 +++++++ .../Headers/SomeOtherKit.apinotes | 8 ++++ 5 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes create mode 100644 test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp index 1622f8f4067..5d50d3cd1dd 100644 --- a/lib/APINotes/APINotesManager.cpp +++ b/lib/APINotes/APINotesManager.cpp @@ -396,14 +396,20 @@ bool APINotesManager::loadCurrentModuleAPINotes( }; if (module->IsFramework) { - // For frameworks, we search in the "APINotes" subdirectory. + // For frameworks, we search in the "Headers" or "PrivateHeaders" + // subdirectory. llvm::SmallString<128> path; path += module->Directory->getName(); - llvm::sys::path::append(path, "APINotes"); - if (auto apinotesDir = fileMgr.getDirectory(path)) { + unsigned pathLen = path.size(); + + llvm::sys::path::append(path, "Headers"); + if (auto apinotesDir = fileMgr.getDirectory(path)) tryAPINotes(apinotesDir, /*wantPublic=*/true); - tryAPINotes(apinotesDir, /*wantPublic=*/false); - } + + path.resize(pathLen); + llvm::sys::path::append(path, "PrivateHeaders"); + if (auto privateAPINotesDir = fileMgr.getDirectory(path)) + tryAPINotes(privateAPINotesDir, /*wantPublic=*/false); } else { tryAPINotes(module->Directory, /*wantPublic=*/true); tryAPINotes(module->Directory, /*wantPublic=*/false); diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes new file mode 100644 index 00000000000..9c855c6dc6a --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -0,0 +1,42 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + PropertyKind: Instance + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: nonnullAInstance + PropertyKind: Instance + Nullability: N + - Name: nonnullAClass + PropertyKind: Class + Nullability: N + - Name: nonnullABoth + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h new file mode 100644 index 00000000000..40be241eb93 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h @@ -0,0 +1,5 @@ +@interface A(ExplicitNullabilityProperties) +@property (nonatomic, readwrite, retain, nonnull) A *explicitNonnullInstance; +@property (nonatomic, readwrite, retain, nullable) A *explicitNullableInstance; +@end + diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes new file mode 100644 index 00000000000..28ede9dfa25 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes @@ -0,0 +1,15 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: internalProperty + Nullability: N +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes new file mode 100644 index 00000000000..2ad546b8f8b --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes @@ -0,0 +1,8 @@ +Name: SomeOtherKit +Classes: + - Name: A + Methods: + - Selector: "methodA" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this"