Skip to content

Commit

Permalink
[APINotes] Support annotating C++ methods
Browse files Browse the repository at this point in the history
This adds support for adding Clang attributes to C++ methods declared
within C++ records by using API Notes.

For instance:
```
Tags:
- Name: IntWrapper
  Methods:
  - Name: getIncremented
    Availability: none
```

This is the first instance of something within a C++ record being
annotated with API Notes, so it adds the necessary infra to make a C++
record an "API Notes context".

Notably this does not add support for nested C++ tags. That will be
added in a follow-up patch.

rdar://131387880
  • Loading branch information
egorzhdan authored Jul 19, 2024
1 parent 007aa6d commit 8a79dc7
Show file tree
Hide file tree
Showing 15 changed files with 465 additions and 79 deletions.
21 changes: 21 additions & 0 deletions clang/include/clang/APINotes/APINotesReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,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<CXXMethodInfo> lookupCXXMethod(ContextID CtxID,
llvm::StringRef Name);

/// Look for information regarding the given global variable.
///
/// \param Name The name of the global variable.
Expand All @@ -166,6 +176,17 @@ class APINotesReader {
/// \returns information about the enumerator, if known.
VersionedInfo<EnumConstantInfo> 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<ContextID>
lookupTagID(llvm::StringRef Name,
std::optional<Context> ParentCtx = std::nullopt);

/// Look for information regarding the given tag
/// (struct/union/enum/C++ class).
///
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/APINotes/APINotesWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/APINotes/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,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:
Expand Down Expand Up @@ -789,6 +795,7 @@ enum class ContextKind : uint8_t {
ObjCClass = 0,
ObjCProtocol = 1,
Namespace = 2,
Tag = 3,
};

struct Context {
Expand Down
29 changes: 29 additions & 0 deletions clang/lib/APINotes/APINotesFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -181,6 +185,20 @@ using ObjCMethodDataLayout =
>;
} // namespace objc_method_block

namespace cxx_method_block {
enum {
CXX_METHOD_DATA = 1,
};

using CXXMethodDataLayout =
llvm::BCRecordLayout<CXX_METHOD_DATA, // record ID
llvm::BCVBR<16>, // 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,
Expand Down Expand Up @@ -269,6 +287,17 @@ struct ContextTableKey {
: parentContextID(parentContextID), contextKind(contextKind),
contextID(contextID) {}

ContextTableKey(std::optional<ContextID> ParentContextID, ContextKind Kind,
uint32_t ContextID)
: parentContextID(ParentContextID ? ParentContextID->Value : -1),
contextKind(static_cast<uint8_t>(Kind)), contextID(ContextID) {}

ContextTableKey(std::optional<Context> 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(
std::tuple{parentContextID, contextKind, contextID});
Expand Down
149 changes: 149 additions & 0 deletions clang/lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,29 @@ class GlobalFunctionTableInfo
}
};

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

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

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

/// Used to deserialize the on-disk enumerator table.
class EnumConstantTableInfo
: public VersionedTableInfo<EnumConstantTableInfo, uint32_t,
Expand Down Expand Up @@ -630,6 +653,12 @@ class APINotesReader::Implementation {
/// The Objective-C method table.
std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;

using SerializedCXXMethodTable =
llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;

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

using SerializedObjCSelectorTable =
llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;

Expand Down Expand Up @@ -683,6 +712,8 @@ class APINotesReader::Implementation {
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
Expand Down Expand Up @@ -1140,6 +1171,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock(
return false;
}

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

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

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

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

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

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

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

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

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

return false;
}

bool APINotesReader::Implementation::readObjCSelectorBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
Expand Down Expand Up @@ -1692,6 +1798,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)) {
Expand Down Expand Up @@ -1911,6 +2025,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
return {Implementation->SwiftVersion, *Known};
}

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

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

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

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

auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalVariableInfo> {
Expand Down Expand Up @@ -1965,6 +2096,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
return {Implementation->SwiftVersion, *Known};
}

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

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

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

return ContextID(*KnownID);
}

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

0 comments on commit 8a79dc7

Please sign in to comment.