Skip to content

Commit 0aae94b

Browse files
committed
[APINotes] Support annotating C++ methods
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 (cherry picked from commit 8a79dc7)
1 parent 549fc30 commit 0aae94b

File tree

15 files changed

+465
-79
lines changed

15 files changed

+465
-79
lines changed

clang/include/clang/APINotes/APINotesReader.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ class APINotesReader {
148148
ObjCSelectorRef Selector,
149149
bool IsInstanceMethod);
150150

151+
/// Look for information regarding the given C++ method in the given C++ tag
152+
/// context.
153+
///
154+
/// \param CtxID The ID that references the parent context, i.e. a C++ tag.
155+
/// \param Name The name of the C++ method we're looking for.
156+
///
157+
/// \returns Information about the method, if known.
158+
VersionedInfo<CXXMethodInfo> lookupCXXMethod(ContextID CtxID,
159+
llvm::StringRef Name);
160+
151161
/// Look for information regarding the given global variable.
152162
///
153163
/// \param Name The name of the global variable.
@@ -173,6 +183,17 @@ class APINotesReader {
173183
/// \returns information about the enumerator, if known.
174184
VersionedInfo<EnumConstantInfo> lookupEnumConstant(llvm::StringRef Name);
175185

186+
/// Look for the context ID of the given C++ tag.
187+
///
188+
/// \param Name The name of the tag we're looking for.
189+
/// \param ParentCtx The context in which this tag is declared, e.g. a C++
190+
/// namespace.
191+
///
192+
/// \returns The ID, if known.
193+
std::optional<ContextID>
194+
lookupTagID(llvm::StringRef Name,
195+
std::optional<Context> ParentCtx = std::nullopt);
196+
176197
/// Look for information regarding the given tag
177198
/// (struct/union/enum/C++ class).
178199
///

clang/include/clang/APINotes/APINotesWriter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class APINotesWriter {
7878
bool IsInstanceMethod, const ObjCMethodInfo &Info,
7979
llvm::VersionTuple SwiftVersion);
8080

81+
/// Add information about a specific C++ method.
82+
///
83+
/// \param CtxID The context in which this method resides, i.e. a C++ tag.
84+
/// \param Name The name of the method.
85+
/// \param Info Information about this method.
86+
void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
87+
const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion);
88+
8189
/// Add information about a global variable.
8290
///
8391
/// \param Name The name of this global variable.

clang/include/clang/APINotes/Types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,12 @@ class GlobalFunctionInfo : public FunctionInfo {
659659
GlobalFunctionInfo() {}
660660
};
661661

662+
/// Describes API notes data for a C++ method.
663+
class CXXMethodInfo : public FunctionInfo {
664+
public:
665+
CXXMethodInfo() {}
666+
};
667+
662668
/// Describes API notes data for an enumerator.
663669
class EnumConstantInfo : public CommonEntityInfo {
664670
public:
@@ -792,6 +798,7 @@ enum class ContextKind : uint8_t {
792798
ObjCClass = 0,
793799
ObjCProtocol = 1,
794800
Namespace = 2,
801+
Tag = 3,
795802
};
796803

797804
struct Context {

clang/lib/APINotes/APINotesFormat.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ enum BlockID {
6363
/// about the method.
6464
OBJC_METHOD_BLOCK_ID,
6565

66+
/// The C++ method data block, which maps C++ (context id, method name) pairs
67+
/// to information about the method.
68+
CXX_METHOD_BLOCK_ID,
69+
6670
/// The Objective-C selector data block, which maps Objective-C
6771
/// selector names (# of pieces, identifier IDs) to the selector ID
6872
/// used in other tables.
@@ -181,6 +185,20 @@ using ObjCMethodDataLayout =
181185
>;
182186
} // namespace objc_method_block
183187

188+
namespace cxx_method_block {
189+
enum {
190+
CXX_METHOD_DATA = 1,
191+
};
192+
193+
using CXXMethodDataLayout =
194+
llvm::BCRecordLayout<CXX_METHOD_DATA, // record ID
195+
llvm::BCVBR<16>, // table offset within the blob (see
196+
// below)
197+
llvm::BCBlob // map from C++ (context id, name)
198+
// tuples to C++ method information
199+
>;
200+
} // namespace cxx_method_block
201+
184202
namespace objc_selector_block {
185203
enum {
186204
OBJC_SELECTOR_DATA = 1,
@@ -269,6 +287,17 @@ struct ContextTableKey {
269287
: parentContextID(parentContextID), contextKind(contextKind),
270288
contextID(contextID) {}
271289

290+
ContextTableKey(std::optional<ContextID> ParentContextID, ContextKind Kind,
291+
uint32_t ContextID)
292+
: parentContextID(ParentContextID ? ParentContextID->Value : -1),
293+
contextKind(static_cast<uint8_t>(Kind)), contextID(ContextID) {}
294+
295+
ContextTableKey(std::optional<Context> ParentContext, ContextKind Kind,
296+
uint32_t ContextID)
297+
: ContextTableKey(ParentContext ? std::make_optional(ParentContext->id)
298+
: std::nullopt,
299+
Kind, ContextID) {}
300+
272301
llvm::hash_code hashValue() const {
273302
return llvm::hash_value(
274303
std::tuple{parentContextID, contextKind, contextID});

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,29 @@ class GlobalFunctionTableInfo
473473
}
474474
};
475475

476+
/// Used to deserialize the on-disk C++ method table.
477+
class CXXMethodTableInfo
478+
: public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey,
479+
CXXMethodInfo> {
480+
public:
481+
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
482+
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
483+
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
484+
return {CtxID, NameID};
485+
}
486+
487+
hash_value_type ComputeHash(internal_key_type Key) {
488+
return static_cast<size_t>(Key.hashValue());
489+
}
490+
491+
static CXXMethodInfo readUnversioned(internal_key_type Key,
492+
const uint8_t *&Data) {
493+
CXXMethodInfo Info;
494+
ReadFunctionInfo(Data, Info);
495+
return Info;
496+
}
497+
};
498+
476499
/// Used to deserialize the on-disk enumerator table.
477500
class EnumConstantTableInfo
478501
: public VersionedTableInfo<EnumConstantTableInfo, uint32_t,
@@ -633,6 +656,12 @@ class APINotesReader::Implementation {
633656
/// The Objective-C method table.
634657
std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
635658

659+
using SerializedCXXMethodTable =
660+
llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;
661+
662+
/// The C++ method table.
663+
std::unique_ptr<SerializedCXXMethodTable> CXXMethodTable;
664+
636665
using SerializedObjCSelectorTable =
637666
llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
638667

@@ -686,6 +715,8 @@ class APINotesReader::Implementation {
686715
llvm::SmallVectorImpl<uint64_t> &Scratch);
687716
bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
688717
llvm::SmallVectorImpl<uint64_t> &Scratch);
718+
bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
719+
llvm::SmallVectorImpl<uint64_t> &Scratch);
689720
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
690721
llvm::SmallVectorImpl<uint64_t> &Scratch);
691722
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
@@ -1144,6 +1175,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock(
11441175
return false;
11451176
}
11461177

1178+
bool APINotesReader::Implementation::readCXXMethodBlock(
1179+
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
1180+
if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID))
1181+
return true;
1182+
1183+
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
1184+
if (!MaybeNext) {
1185+
// FIXME this drops the error on the floor.
1186+
consumeError(MaybeNext.takeError());
1187+
return false;
1188+
}
1189+
llvm::BitstreamEntry Next = MaybeNext.get();
1190+
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
1191+
if (Next.Kind == llvm::BitstreamEntry::Error)
1192+
return true;
1193+
1194+
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
1195+
// Unknown sub-block, possibly for use by a future version of the
1196+
// API notes format.
1197+
if (Cursor.SkipBlock())
1198+
return true;
1199+
1200+
MaybeNext = Cursor.advance();
1201+
if (!MaybeNext) {
1202+
// FIXME this drops the error on the floor.
1203+
consumeError(MaybeNext.takeError());
1204+
return false;
1205+
}
1206+
Next = MaybeNext.get();
1207+
continue;
1208+
}
1209+
1210+
Scratch.clear();
1211+
llvm::StringRef BlobData;
1212+
llvm::Expected<unsigned> MaybeKind =
1213+
Cursor.readRecord(Next.ID, Scratch, &BlobData);
1214+
if (!MaybeKind) {
1215+
// FIXME this drops the error on the floor.
1216+
consumeError(MaybeKind.takeError());
1217+
return false;
1218+
}
1219+
unsigned Kind = MaybeKind.get();
1220+
switch (Kind) {
1221+
case cxx_method_block::CXX_METHOD_DATA: {
1222+
// Already saw C++ method table.
1223+
if (CXXMethodTable)
1224+
return true;
1225+
1226+
uint32_t tableOffset;
1227+
cxx_method_block::CXXMethodDataLayout::readRecord(Scratch, tableOffset);
1228+
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
1229+
1230+
CXXMethodTable.reset(SerializedCXXMethodTable::Create(
1231+
base + tableOffset, base + sizeof(uint32_t), base));
1232+
break;
1233+
}
1234+
1235+
default:
1236+
// Unknown record, possibly for use by a future version of the
1237+
// module format.
1238+
break;
1239+
}
1240+
1241+
MaybeNext = Cursor.advance();
1242+
if (!MaybeNext) {
1243+
// FIXME this drops the error on the floor.
1244+
consumeError(MaybeNext.takeError());
1245+
return false;
1246+
}
1247+
Next = MaybeNext.get();
1248+
}
1249+
1250+
return false;
1251+
}
1252+
11471253
bool APINotesReader::Implementation::readObjCSelectorBlock(
11481254
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
11491255
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
@@ -1696,6 +1802,14 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer,
16961802
}
16971803
break;
16981804

1805+
case CXX_METHOD_BLOCK_ID:
1806+
if (!HasValidControlBlock ||
1807+
Implementation->readCXXMethodBlock(Cursor, Scratch)) {
1808+
Failed = true;
1809+
return;
1810+
}
1811+
break;
1812+
16991813
case OBJC_SELECTOR_BLOCK_ID:
17001814
if (!HasValidControlBlock ||
17011815
Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
@@ -1923,6 +2037,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
19232037
return {Implementation->SwiftVersion, *Known};
19242038
}
19252039

2040+
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
2041+
-> VersionedInfo<CXXMethodInfo> {
2042+
if (!Implementation->CXXMethodTable)
2043+
return std::nullopt;
2044+
2045+
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
2046+
if (!NameID)
2047+
return std::nullopt;
2048+
2049+
auto Known = Implementation->CXXMethodTable->find(
2050+
SingleDeclTableKey(CtxID.Value, *NameID));
2051+
if (Known == Implementation->CXXMethodTable->end())
2052+
return std::nullopt;
2053+
2054+
return {Implementation->SwiftVersion, *Known};
2055+
}
2056+
19262057
auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
19272058
std::optional<Context> Ctx)
19282059
-> VersionedInfo<GlobalVariableInfo> {
@@ -1977,6 +2108,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
19772108
return {Implementation->SwiftVersion, *Known};
19782109
}
19792110

2111+
auto APINotesReader::lookupTagID(llvm::StringRef Name,
2112+
std::optional<Context> ParentCtx)
2113+
-> std::optional<ContextID> {
2114+
if (!Implementation->ContextIDTable)
2115+
return std::nullopt;
2116+
2117+
std::optional<IdentifierID> TagID = Implementation->getIdentifier(Name);
2118+
if (!TagID)
2119+
return std::nullopt;
2120+
2121+
auto KnownID = Implementation->ContextIDTable->find(
2122+
ContextTableKey(ParentCtx, ContextKind::Tag, *TagID));
2123+
if (KnownID == Implementation->ContextIDTable->end())
2124+
return std::nullopt;
2125+
2126+
return ContextID(*KnownID);
2127+
}
2128+
19802129
auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional<Context> Ctx)
19812130
-> VersionedInfo<TagInfo> {
19822131
if (!Implementation->TagTable)

0 commit comments

Comments
 (0)