diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index b62055a76a09d..6cc8a2fe66b09 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -148,6 +148,16 @@ class APINotesReader { ObjCSelectorRef Selector, bool IsInstanceMethod); + /// Look for information regarding the given C++ method in the given C++ tag + /// context. + /// + /// \param CtxID The ID that references the parent context, i.e. a C++ tag. + /// \param Name The name of the C++ method we're looking for. + /// + /// \returns Information about the method, if known. + VersionedInfo lookupCXXMethod(ContextID CtxID, + llvm::StringRef Name); + /// Look for information regarding the given global variable. /// /// \param Name The name of the global variable. @@ -173,6 +183,17 @@ class APINotesReader { /// \returns information about the enumerator, if known. VersionedInfo lookupEnumConstant(llvm::StringRef Name); + /// Look for the context ID of the given C++ tag. + /// + /// \param Name The name of the tag we're looking for. + /// \param ParentCtx The context in which this tag is declared, e.g. a C++ + /// namespace. + /// + /// \returns The ID, if known. + std::optional + lookupTagID(llvm::StringRef Name, + std::optional ParentCtx = std::nullopt); + /// Look for information regarding the given tag /// (struct/union/enum/C++ class). /// diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index f2c2d0815b196..1bb5dfc793594 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -78,6 +78,14 @@ class APINotesWriter { bool IsInstanceMethod, const ObjCMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a specific C++ method. + /// + /// \param CtxID The context in which this method resides, i.e. a C++ tag. + /// \param Name The name of the method. + /// \param Info Information about this method. + void addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a global variable. /// /// \param Name The name of this global variable. diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index d00058f9f8857..e03e366ad763d 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -659,6 +659,12 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() {} }; +/// Describes API notes data for a C++ method. +class CXXMethodInfo : public FunctionInfo { +public: + CXXMethodInfo() {} +}; + /// Describes API notes data for an enumerator. class EnumConstantInfo : public CommonEntityInfo { public: @@ -792,6 +798,7 @@ enum class ContextKind : uint8_t { ObjCClass = 0, ObjCProtocol = 1, Namespace = 2, + Tag = 3, }; struct Context { diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index e3aa76df8316c..cd6456dbe37b2 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,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 = 26; // SwiftCopyable +const uint16_t VERSION_MINOR = 27; // SingleDeclTableKey const uint8_t kSwiftCopyable = 1; const uint8_t kSwiftNonCopyable = 2; @@ -63,6 +63,10 @@ enum BlockID { /// about the method. OBJC_METHOD_BLOCK_ID, + /// The C++ method data block, which maps C++ (context id, method name) pairs + /// to information about the method. + CXX_METHOD_BLOCK_ID, + /// The Objective-C selector data block, which maps Objective-C /// selector names (# of pieces, identifier IDs) to the selector ID /// used in other tables. @@ -181,6 +185,20 @@ using ObjCMethodDataLayout = >; } // namespace objc_method_block +namespace cxx_method_block { +enum { + CXX_METHOD_DATA = 1, +}; + +using CXXMethodDataLayout = + llvm::BCRecordLayout, // table offset within the blob (see + // below) + llvm::BCBlob // map from C++ (context id, name) + // tuples to C++ method information + >; +} // namespace cxx_method_block + namespace objc_selector_block { enum { OBJC_SELECTOR_DATA = 1, @@ -269,11 +287,16 @@ struct ContextTableKey { : parentContextID(parentContextID), contextKind(contextKind), contextID(contextID) {} - ContextTableKey(std::optional context, IdentifierID nameID) - : parentContextID(context ? context->id.Value : (uint32_t)-1), - contextKind(context ? static_cast(context->kind) - : static_cast(-1)), - contextID(nameID) {} + ContextTableKey(std::optional ParentContextID, ContextKind Kind, + uint32_t ContextID) + : parentContextID(ParentContextID ? ParentContextID->Value : -1), + contextKind(static_cast(Kind)), contextID(ContextID) {} + + ContextTableKey(std::optional ParentContext, ContextKind Kind, + uint32_t ContextID) + : ContextTableKey(ParentContext ? std::make_optional(ParentContext->id) + : std::nullopt, + Kind, ContextID) {} llvm::hash_code hashValue() const { return llvm::hash_value( @@ -286,6 +309,32 @@ inline bool operator==(const ContextTableKey &lhs, const ContextTableKey &rhs) { lhs.contextKind == rhs.contextKind && lhs.contextID == rhs.contextID; } +/// A stored Objective-C or C++ declaration, represented by the ID of its parent +/// context, and the name of the declaration. +struct SingleDeclTableKey { + uint32_t parentContextID; + uint32_t nameID; + + SingleDeclTableKey() : parentContextID(-1), nameID(-1) {} + + SingleDeclTableKey(uint32_t ParentContextID, uint32_t NameID) + : parentContextID(ParentContextID), nameID(NameID) {} + + SingleDeclTableKey(std::optional ParentCtx, IdentifierID NameID) + : parentContextID(ParentCtx ? ParentCtx->id.Value + : static_cast(-1)), + nameID(NameID) {} + + llvm::hash_code hashValue() const { + return llvm::hash_value(std::make_pair(parentContextID, nameID)); + } +}; + +inline bool operator==(const SingleDeclTableKey &lhs, + const SingleDeclTableKey &rhs) { + return lhs.parentContextID == rhs.parentContextID && lhs.nameID == rhs.nameID; +} + } // namespace api_notes } // namespace clang @@ -341,6 +390,29 @@ template <> struct DenseMapInfo { return lhs == rhs; } }; + +template <> struct DenseMapInfo { + static inline clang::api_notes::SingleDeclTableKey getEmptyKey() { + return clang::api_notes::SingleDeclTableKey(); + } + + static inline clang::api_notes::SingleDeclTableKey getTombstoneKey() { + return clang::api_notes::SingleDeclTableKey{ + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()}; + } + + static unsigned + getHashValue(const clang::api_notes::SingleDeclTableKey &value) { + return value.hashValue(); + } + + static bool isEqual(const clang::api_notes::SingleDeclTableKey &lhs, + const clang::api_notes::SingleDeclTableKey &rhs) { + return lhs == rhs; + } +}; + } // namespace llvm #endif diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index ceb9fb10db8bf..7475e63c90845 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -429,15 +429,13 @@ class ObjCSelectorTableInfo { /// Used to deserialize the on-disk global variable table. class GlobalVariableTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -454,15 +452,13 @@ class GlobalVariableTableInfo /// Used to deserialize the on-disk global function table. class GlobalFunctionTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -477,6 +473,29 @@ class GlobalFunctionTableInfo } }; +/// Used to deserialize the on-disk C++ method table. +class CXXMethodTableInfo + : public VersionedTableInfo { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext(Data); + auto NameID = endian::readNext(Data); + return {CtxID, NameID}; + } + + hash_value_type ComputeHash(internal_key_type Key) { + return static_cast(Key.hashValue()); + } + + static CXXMethodInfo readUnversioned(internal_key_type Key, + const uint8_t *&Data) { + CXXMethodInfo Info; + ReadFunctionInfo(Data, Info); + return Info; + } +}; + /// Used to deserialize the on-disk enumerator table. class EnumConstantTableInfo : public VersionedTableInfo { + : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -563,16 +580,14 @@ class TagTableInfo /// Used to deserialize the on-disk typedef table. class TypedefTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto nameID = endian::readNext(Data); - return {CtxID, ContextKind, nameID}; + return {CtxID, nameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -641,6 +656,12 @@ class APINotesReader::Implementation { /// The Objective-C method table. std::unique_ptr ObjCMethodTable; + using SerializedCXXMethodTable = + llvm::OnDiskIterableChainedHashTable; + + /// The C++ method table. + std::unique_ptr CXXMethodTable; + using SerializedObjCSelectorTable = llvm::OnDiskIterableChainedHashTable; @@ -694,6 +715,8 @@ class APINotesReader::Implementation { llvm::SmallVectorImpl &Scratch); bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); + bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor, + llvm::SmallVectorImpl &Scratch); bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor, @@ -1152,6 +1175,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock( return false; } +bool APINotesReader::Implementation::readCXXMethodBlock( + llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { + if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID)) + return true; + + llvm::Expected MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + llvm::BitstreamEntry Next = MaybeNext.get(); + while (Next.Kind != llvm::BitstreamEntry::EndBlock) { + if (Next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (Next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (Cursor.SkipBlock()) + return true; + + MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + Next = MaybeNext.get(); + continue; + } + + Scratch.clear(); + llvm::StringRef BlobData; + llvm::Expected MaybeKind = + Cursor.readRecord(Next.ID, Scratch, &BlobData); + if (!MaybeKind) { + // FIXME this drops the error on the floor. + consumeError(MaybeKind.takeError()); + return false; + } + unsigned Kind = MaybeKind.get(); + switch (Kind) { + case cxx_method_block::CXX_METHOD_DATA: { + // Already saw C++ method table. + if (CXXMethodTable) + return true; + + uint32_t tableOffset; + cxx_method_block::CXXMethodDataLayout::readRecord(Scratch, tableOffset); + auto base = reinterpret_cast(BlobData.data()); + + CXXMethodTable.reset(SerializedCXXMethodTable::Create( + base + tableOffset, base + sizeof(uint32_t), base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + Next = MaybeNext.get(); + } + + return false; +} + bool APINotesReader::Implementation::readObjCSelectorBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) @@ -1704,6 +1802,14 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer, } break; + case CXX_METHOD_BLOCK_ID: + if (!HasValidControlBlock || + Implementation->readCXXMethodBlock(Cursor, Scratch)) { + Failed = true; + return; + } + break; + case OBJC_SELECTOR_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCSelectorBlock(Cursor, Scratch)) { @@ -1931,6 +2037,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, return {Implementation->SwiftVersion, *Known}; } +auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name) + -> VersionedInfo { + if (!Implementation->CXXMethodTable) + return std::nullopt; + + std::optional NameID = Implementation->getIdentifier(Name); + if (!NameID) + return std::nullopt; + + auto Known = Implementation->CXXMethodTable->find( + SingleDeclTableKey(CtxID.Value, *NameID)); + if (Known == Implementation->CXXMethodTable->end()) + return std::nullopt; + + return {Implementation->SwiftVersion, *Known}; +} + auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { @@ -1941,7 +2064,7 @@ auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalVariableTable->find(Key); if (Known == Implementation->GlobalVariableTable->end()) @@ -1960,7 +2083,7 @@ auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalFunctionTable->find(Key); if (Known == Implementation->GlobalFunctionTable->end()) @@ -1985,6 +2108,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name) return {Implementation->SwiftVersion, *Known}; } +auto APINotesReader::lookupTagID(llvm::StringRef Name, + std::optional ParentCtx) + -> std::optional { + if (!Implementation->ContextIDTable) + return std::nullopt; + + std::optional TagID = Implementation->getIdentifier(Name); + if (!TagID) + return std::nullopt; + + auto KnownID = Implementation->ContextIDTable->find( + ContextTableKey(ParentCtx, ContextKind::Tag, *TagID)); + if (KnownID == Implementation->ContextIDTable->end()) + return std::nullopt; + + return ContextID(*KnownID); +} + auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->TagTable) @@ -1994,7 +2135,7 @@ auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional Ctx) if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->TagTable->find(Key); if (Known == Implementation->TagTable->end()) @@ -2013,7 +2154,7 @@ auto APINotesReader::lookupTypedef(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->TypedefTable->find(Key); if (Known == Implementation->TypedefTable->end()) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 0068270060d6b..2428f0a96829d 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -72,22 +72,29 @@ class APINotesWriter::Implementation { llvm::SmallVector, 1>> ObjCMethods; + /// Information about C++ methods. + /// + /// Indexed by the context ID and name ID. + llvm::DenseMap, 1>> + CXXMethods; + /// Mapping from selectors to selector ID. llvm::DenseMap SelectorIDs; /// Information about global variables. /// - /// Indexed by the context ID, contextKind, identifier ID. + /// Indexed by the context ID, identifier ID. llvm::DenseMap< - ContextTableKey, + SingleDeclTableKey, llvm::SmallVector, 1>> GlobalVariables; /// Information about global functions. /// - /// Indexed by the context ID, contextKind, identifier ID. + /// Indexed by the context ID, identifier ID. llvm::DenseMap< - ContextTableKey, + SingleDeclTableKey, llvm::SmallVector, 1>> GlobalFunctions; @@ -100,15 +107,15 @@ class APINotesWriter::Implementation { /// Information about tags. /// - /// Indexed by the context ID, contextKind, identifier ID. - llvm::DenseMap, 1>> Tags; /// Information about typedefs. /// - /// Indexed by the context ID, contextKind, identifier ID. - llvm::DenseMap, 1>> Typedefs; @@ -152,6 +159,7 @@ class APINotesWriter::Implementation { void writeContextBlock(llvm::BitstreamWriter &Stream); void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream); void writeObjCMethodBlock(llvm::BitstreamWriter &Stream); + void writeCXXMethodBlock(llvm::BitstreamWriter &Stream); void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream); void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream); void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream); @@ -183,6 +191,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) { writeContextBlock(Stream); writeObjCPropertyBlock(Stream); writeObjCMethodBlock(Stream); + writeCXXMethodBlock(Stream); writeObjCSelectorBlock(Stream); writeGlobalVariableBlock(Stream); writeGlobalFunctionBlock(Stream); @@ -772,6 +781,34 @@ class ObjCMethodTableInfo emitFunctionInfo(OS, OMI); } }; + +/// Used to serialize the on-disk C++ method table. +class CXXMethodTableInfo + : public VersionedTableInfo { +public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t); + } + + void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { + llvm::support::endian::Writer writer(OS, llvm::endianness::little); + writer.write(Key.parentContextID); + writer.write(Key.nameID); + } + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(key.hashValue()); + } + + unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) { + return getFunctionInfoSize(OMI); + } + + void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) { + emitFunctionInfo(OS, OMI); + } +}; } // namespace void APINotesWriter::Implementation::writeObjCMethodBlock( @@ -801,6 +838,33 @@ void APINotesWriter::Implementation::writeObjCMethodBlock( } } +void APINotesWriter::Implementation::writeCXXMethodBlock( + llvm::BitstreamWriter &Stream) { + llvm::BCBlockRAII Scope(Stream, CXX_METHOD_BLOCK_ID, 3); + + if (CXXMethods.empty()) + return; + + { + llvm::SmallString<4096> HashTableBlob; + uint32_t Offset; + { + llvm::OnDiskChainedHashTableGenerator Generator; + for (auto &MD : CXXMethods) + Generator.insert(MD.first, MD.second); + + llvm::raw_svector_ostream BlobStream(HashTableBlob); + // Make sure that no bucket is at offset 0 + llvm::support::endian::write(BlobStream, 0, + llvm::endianness::little); + Offset = Generator.Emit(BlobStream); + } + + cxx_method_block::CXXMethodDataLayout CXXMethodData(Stream); + CXXMethodData.emit(Scratch, Offset, HashTableBlob); + } +} + namespace { /// Used to serialize the on-disk Objective-C selector table. class ObjCSelectorTableInfo { @@ -872,18 +936,17 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( namespace { /// Used to serialize the on-disk global variable table. class GlobalVariableTableInfo - : public VersionedTableInfo { public: unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); + return sizeof(uint32_t) + sizeof(uint32_t); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -986,18 +1049,17 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) { /// Used to serialize the on-disk global function table. class GlobalFunctionTableInfo - : public VersionedTableInfo { public: unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); + return sizeof(uint32_t) + sizeof(uint32_t); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -1098,20 +1160,20 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( namespace { template class CommonTypeTableInfo - : public VersionedTableInfo { + : public VersionedTableInfo { public: using key_type_ref = typename CommonTypeTableInfo::key_type_ref; using hash_value_type = typename CommonTypeTableInfo::hash_value_type; unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(IdentifierID); + return sizeof(uint32_t) + sizeof(IdentifierID); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -1353,12 +1415,20 @@ void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, } } +void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const CXXMethodInfo &Info, + VersionTuple SwiftVersion) { + IdentifierID NameID = Implementation->getIdentifier(Name); + SingleDeclTableKey Key(CtxID.Value, NameID); + Implementation->CXXMethods[Key].push_back({SwiftVersion, Info}); +} + void APINotesWriter::addGlobalVariable(std::optional Ctx, llvm::StringRef Name, const GlobalVariableInfo &Info, VersionTuple SwiftVersion) { IdentifierID VariableID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, VariableID); + SingleDeclTableKey Key(Ctx, VariableID); Implementation->GlobalVariables[Key].push_back({SwiftVersion, Info}); } @@ -1367,7 +1437,7 @@ void APINotesWriter::addGlobalFunction(std::optional Ctx, const GlobalFunctionInfo &Info, VersionTuple SwiftVersion) { IdentifierID NameID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, NameID); + SingleDeclTableKey Key(Ctx, NameID); Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info}); } @@ -1381,7 +1451,7 @@ void APINotesWriter::addEnumConstant(llvm::StringRef Name, void APINotesWriter::addTag(std::optional Ctx, llvm::StringRef Name, const TagInfo &Info, VersionTuple SwiftVersion) { IdentifierID TagID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, TagID); + SingleDeclTableKey Key(Ctx, TagID); Implementation->Tags[Key].push_back({SwiftVersion, Info}); } @@ -1389,7 +1459,7 @@ void APINotesWriter::addTypedef(std::optional Ctx, llvm::StringRef Name, const TypedefInfo &Info, VersionTuple SwiftVersion) { IdentifierID TypedefID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, TypedefID); + SingleDeclTableKey Key(Ctx, TypedefID); Implementation->Typedefs[Key].push_back({SwiftVersion, Info}); } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index cc37a771a56c3..c3bc3d25f2ba0 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -533,6 +533,7 @@ struct Tag { std::optional FlagEnum; std::optional EnumConvenienceKind; std::optional SwiftCopyable; + FunctionsSeq Methods; }; typedef std::vector TagsSeq; @@ -567,6 +568,7 @@ template <> struct MappingTraits { IO.mapOptional("FlagEnum", T.FlagEnum); IO.mapOptional("EnumKind", T.EnumConvenienceKind); IO.mapOptional("SwiftCopyable", T.SwiftCopyable); + IO.mapOptional("Methods", T.Methods); } }; } // namespace yaml @@ -987,6 +989,96 @@ class YAMLConverter { TheNamespace.Items, SwiftVersion); } + void convertFunction(const Function &Function, FunctionInfo &FI) { + convertAvailability(Function.Availability, FI, Function.Name); + FI.setSwiftPrivate(Function.SwiftPrivate); + FI.SwiftName = std::string(Function.SwiftName); + convertParams(Function.Params, FI); + convertNullability(Function.Nullability, Function.NullabilityOfRet, FI, + Function.Name); + FI.ResultType = std::string(Function.ResultType); + FI.setRetainCountConvention(Function.RetainCountConvention); + } + + void convertTagContext(std::optional ParentContext, const Tag &T, + VersionTuple SwiftVersion) { + TagInfo TI; + std::optional ParentContextID = + ParentContext ? std::optional(ParentContext->id) + : std::nullopt; + convertCommonType(T, TI, T.Name); + + if ((T.SwiftRetainOp || T.SwiftReleaseOp) && !T.SwiftImportAs) { + emitError(llvm::Twine("should declare SwiftImportAs to use " + "SwiftRetainOp and SwiftReleaseOp (for ") + + T.Name + ")"); + return; + } + if (T.SwiftReleaseOp.has_value() != T.SwiftRetainOp.has_value()) { + emitError(llvm::Twine("should declare both SwiftReleaseOp and " + "SwiftRetainOp (for ") + + T.Name + ")"); + return; + } + + if (T.SwiftImportAs) + TI.SwiftImportAs = T.SwiftImportAs; + if (T.SwiftRetainOp) + TI.SwiftRetainOp = T.SwiftRetainOp; + if (T.SwiftReleaseOp) + TI.SwiftReleaseOp = T.SwiftReleaseOp; + + if (T.SwiftCopyable) + TI.setSwiftCopyable(T.SwiftCopyable); + + if (T.EnumConvenienceKind) { + if (T.EnumExtensibility) { + emitError( + llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + + T.Name + ")"); + return; + } + if (T.FlagEnum) { + emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + + T.Name + ")"); + return; + } + switch (*T.EnumConvenienceKind) { + case EnumConvenienceAliasKind::None: + TI.EnumExtensibility = EnumExtensibilityKind::None; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFOptions: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(true); + break; + case EnumConvenienceAliasKind::CFClosedEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Closed; + TI.setFlagEnum(false); + break; + } + } else { + TI.EnumExtensibility = T.EnumExtensibility; + TI.setFlagEnum(T.FlagEnum); + } + + Writer.addTag(ParentContext, T.Name, TI, SwiftVersion); + + ContextInfo CI; + auto TagCtxID = Writer.addContext(ParentContextID, T.Name, ContextKind::Tag, + CI, SwiftVersion); + + for (const auto &CXXMethod : T.Methods) { + CXXMethodInfo MI; + convertFunction(CXXMethod, MI); + Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion); + } + } + void convertTopLevelItems(std::optional Ctx, const TopLevelItems &TLItems, VersionTuple SwiftVersion) { @@ -1063,14 +1155,7 @@ class YAMLConverter { } GlobalFunctionInfo GFI; - convertAvailability(Function.Availability, GFI, Function.Name); - GFI.setSwiftPrivate(Function.SwiftPrivate); - GFI.SwiftName = std::string(Function.SwiftName); - convertParams(Function.Params, GFI); - convertNullability(Function.Nullability, Function.NullabilityOfRet, GFI, - Function.Name); - GFI.ResultType = std::string(Function.ResultType); - GFI.setRetainCountConvention(Function.RetainCountConvention); + convertFunction(Function, GFI); Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion); } @@ -1101,68 +1186,7 @@ class YAMLConverter { continue; } - TagInfo TI; - convertCommonType(Tag, TI, Tag.Name); - - if ((Tag.SwiftRetainOp || Tag.SwiftReleaseOp) && !Tag.SwiftImportAs) { - emitError(llvm::Twine("should declare SwiftImportAs to use " - "SwiftRetainOp and SwiftReleaseOp (for ") + - Tag.Name + ")"); - continue; - } - if (Tag.SwiftReleaseOp.has_value() != Tag.SwiftRetainOp.has_value()) { - emitError(llvm::Twine("should declare both SwiftReleaseOp and " - "SwiftRetainOp (for ") + - Tag.Name + ")"); - continue; - } - - if (Tag.SwiftImportAs) - TI.SwiftImportAs = Tag.SwiftImportAs; - if (Tag.SwiftRetainOp) - TI.SwiftRetainOp = Tag.SwiftRetainOp; - if (Tag.SwiftReleaseOp) - TI.SwiftReleaseOp = Tag.SwiftReleaseOp; - - if (Tag.SwiftCopyable) - TI.setSwiftCopyable(Tag.SwiftCopyable); - - if (Tag.EnumConvenienceKind) { - if (Tag.EnumExtensibility) { - emitError( - llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + - Tag.Name + ")"); - continue; - } - if (Tag.FlagEnum) { - emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + - Tag.Name + ")"); - continue; - } - switch (*Tag.EnumConvenienceKind) { - case EnumConvenienceAliasKind::None: - TI.EnumExtensibility = EnumExtensibilityKind::None; - TI.setFlagEnum(false); - break; - case EnumConvenienceAliasKind::CFEnum: - TI.EnumExtensibility = EnumExtensibilityKind::Open; - TI.setFlagEnum(false); - break; - case EnumConvenienceAliasKind::CFOptions: - TI.EnumExtensibility = EnumExtensibilityKind::Open; - TI.setFlagEnum(true); - break; - case EnumConvenienceAliasKind::CFClosedEnum: - TI.EnumExtensibility = EnumExtensibilityKind::Closed; - TI.setFlagEnum(false); - break; - } - } else { - TI.EnumExtensibility = Tag.EnumExtensibility; - TI.setFlagEnum(Tag.FlagEnum); - } - - Writer.addTag(Ctx, Tag.Name, TI, SwiftVersion); + convertTagContext(Ctx, Tag, SwiftVersion); } // Write all typedefs. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 3482f3741fce6..055e66a0c3486 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -546,6 +546,13 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, Metadata); } +/// Process API notes for a C++ method. +static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method, + const api_notes::CXXMethodInfo &Info, + VersionedInfoMetadata Metadata) { + ProcessAPINotes(S, (FunctionOrMethod)Method, Info, Metadata); +} + /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, const api_notes::GlobalFunctionInfo &Info, @@ -782,13 +789,9 @@ void Sema::ProcessAPINotes(Decl *D) { if (!D) return; - // Globals. - if (D->getDeclContext()->isFileContext() || - D->getDeclContext()->isNamespace() || - D->getDeclContext()->isExternCContext() || - D->getDeclContext()->isExternCXXContext()) { - std::optional APINotesContext; - if (auto NamespaceContext = dyn_cast(D->getDeclContext())) { + auto GetNamespaceContext = + [&](DeclContext *DC) -> std::optional { + if (auto NamespaceContext = dyn_cast(DC)) { for (auto Reader : APINotes.findAPINotes(NamespaceContext->getLocation())) { // Retrieve the context ID for the parent namespace of the decl. @@ -811,11 +814,20 @@ void Sema::ProcessAPINotes(Decl *D) { break; } if (NamespaceID) - APINotesContext = api_notes::Context( - *NamespaceID, api_notes::ContextKind::Namespace); + return api_notes::Context(*NamespaceID, + api_notes::ContextKind::Namespace); } } + return std::nullopt; + }; + // Globals. + if (D->getDeclContext()->isFileContext() || + D->getDeclContext()->isNamespace() || + D->getDeclContext()->isExternCContext() || + D->getDeclContext()->isExternCXXContext()) { + std::optional APINotesContext = + GetNamespaceContext(D->getDeclContext()); // Global variables. if (auto VD = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { @@ -1001,4 +1013,24 @@ void Sema::ProcessAPINotes(Decl *D) { return; } } + + if (auto CXXRecord = dyn_cast(D->getDeclContext())) { + auto GetRecordContext = [&](api_notes::APINotesReader *Reader) + -> std::optional { + auto ParentContext = GetNamespaceContext(CXXRecord->getDeclContext()); + if (auto Found = Reader->lookupTagID(CXXRecord->getName(), ParentContext)) + return *Found; + + return std::nullopt; + }; + + if (auto CXXMethod = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetRecordContext(Reader)) { + auto Info = Reader->lookupCXXMethod(*Context, CXXMethod->getName()); + ProcessVersionedAPINotes(*this, CXXMethod, Info); + } + } + } + } } diff --git a/clang/test/APINotes/Inputs/Headers/Methods.apinotes b/clang/test/APINotes/Inputs/Headers/Methods.apinotes new file mode 100644 index 0000000000000..0fa6991a51ff4 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/Methods.apinotes @@ -0,0 +1,8 @@ +--- +Name: Methods +Tags: +- Name: IntWrapper + Methods: + - Name: getIncremented + Availability: none + AvailabilityMsg: "oh no" diff --git a/clang/test/APINotes/Inputs/Headers/Methods.h b/clang/test/APINotes/Inputs/Headers/Methods.h new file mode 100644 index 0000000000000..f46fe31533e5d --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/Methods.h @@ -0,0 +1,14 @@ +struct IntWrapper { + int value; + + IntWrapper getIncremented() const { return {value + 1}; } +}; + +// TODO: support nested tags +struct Outer { + struct Inner { + int value; + + Inner getDecremented() const { return {value - 1}; } + }; +}; diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes index e9da36787b638..68073932d600e 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes @@ -38,6 +38,9 @@ Namespaces: Tags: - Name: char_box SwiftName: NestedCharBox + Methods: + - Name: methodInNestedNamespace + SwiftName: swiftMethodInNestedNamespace() Namespaces: - Name: Namespace1 Tags: diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.h b/clang/test/APINotes/Inputs/Headers/Namespaces.h index 6a79e996be86c..e996b8ffa6b6e 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.h +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.h @@ -9,6 +9,7 @@ namespace Nested1 { void funcInNestedNamespace(int i); struct char_box { char c; + void methodInNestedNamespace(); }; } diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index d515169184f4f..faf6042c78d57 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -24,6 +24,10 @@ module BrokenTypes { header "BrokenTypes.h" } +module Methods { + header "Methods.h" +} + module ModuleWithWrongCase { header "ModuleWithWrongCase.h" } diff --git a/clang/test/APINotes/methods.cpp b/clang/test/APINotes/methods.cpp new file mode 100644 index 0000000000000..692f750ed66c7 --- /dev/null +++ b/clang/test/APINotes/methods.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -x c++ +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter IntWrapper::getIncremented -x c++ | FileCheck --check-prefix=CHECK-METHOD %s + +#include "Methods.h" + +// CHECK-METHOD: Dumping IntWrapper::getIncremented: +// CHECK-METHOD-NEXT: CXXMethodDecl {{.+}} getIncremented +// CHECK-METHOD: UnavailableAttr {{.+}} <> "oh no" diff --git a/clang/test/APINotes/namespaces.cpp b/clang/test/APINotes/namespaces.cpp index c19eee565c2da..a6517a324b9c5 100644 --- a/clang/test/APINotes/namespaces.cpp +++ b/clang/test/APINotes/namespaces.cpp @@ -9,6 +9,7 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested2::varInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-ANOTHER-GLOBAL-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::funcInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-FUNC-IN-NESTED-NAMESPACE %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box::methodInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-METHOD-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::Namespace1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter varInInlineNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-GLOBAL-IN-INLINE-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter funcInInlineNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-FUNC-IN-INLINE-NAMESPACE %s @@ -55,6 +56,10 @@ // CHECK-STRUCT-IN-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces struct char_box // CHECK-STRUCT-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "NestedCharBox" +// CHECK-METHOD-IN-NESTED-NAMESPACE: Dumping Namespace1::Nested1::char_box::methodInNestedNamespace: +// CHECK-METHOD-IN-NESTED-NAMESPACE-NEXT: CXXMethodDecl {{.+}} imported in Namespaces methodInNestedNamespace +// CHECK-METHOD-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "swiftMethodInNestedNamespace()" + // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE: Dumping Namespace1::Nested1::Namespace1::char_box: // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces struct char_box // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "DeepNestedCharBox"