Skip to content

Commit 8a79dc7

Browse files
authored
[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
1 parent 007aa6d commit 8a79dc7

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
@@ -141,6 +141,16 @@ class APINotesReader {
141141
ObjCSelectorRef Selector,
142142
bool IsInstanceMethod);
143143

144+
/// Look for information regarding the given C++ method in the given C++ tag
145+
/// context.
146+
///
147+
/// \param CtxID The ID that references the parent context, i.e. a C++ tag.
148+
/// \param Name The name of the C++ method we're looking for.
149+
///
150+
/// \returns Information about the method, if known.
151+
VersionedInfo<CXXMethodInfo> lookupCXXMethod(ContextID CtxID,
152+
llvm::StringRef Name);
153+
144154
/// Look for information regarding the given global variable.
145155
///
146156
/// \param Name The name of the global variable.
@@ -166,6 +176,17 @@ class APINotesReader {
166176
/// \returns information about the enumerator, if known.
167177
VersionedInfo<EnumConstantInfo> lookupEnumConstant(llvm::StringRef Name);
168178

179+
/// Look for the context ID of the given C++ tag.
180+
///
181+
/// \param Name The name of the tag we're looking for.
182+
/// \param ParentCtx The context in which this tag is declared, e.g. a C++
183+
/// namespace.
184+
///
185+
/// \returns The ID, if known.
186+
std::optional<ContextID>
187+
lookupTagID(llvm::StringRef Name,
188+
std::optional<Context> ParentCtx = std::nullopt);
189+
169190
/// Look for information regarding the given tag
170191
/// (struct/union/enum/C++ class).
171192
///

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
@@ -656,6 +656,12 @@ class GlobalFunctionInfo : public FunctionInfo {
656656
GlobalFunctionInfo() {}
657657
};
658658

659+
/// Describes API notes data for a C++ method.
660+
class CXXMethodInfo : public FunctionInfo {
661+
public:
662+
CXXMethodInfo() {}
663+
};
664+
659665
/// Describes API notes data for an enumerator.
660666
class EnumConstantInfo : public CommonEntityInfo {
661667
public:
@@ -789,6 +795,7 @@ enum class ContextKind : uint8_t {
789795
ObjCClass = 0,
790796
ObjCProtocol = 1,
791797
Namespace = 2,
798+
Tag = 3,
792799
};
793800

794801
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,
@@ -630,6 +653,12 @@ class APINotesReader::Implementation {
630653
/// The Objective-C method table.
631654
std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
632655

656+
using SerializedCXXMethodTable =
657+
llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;
658+
659+
/// The C++ method table.
660+
std::unique_ptr<SerializedCXXMethodTable> CXXMethodTable;
661+
633662
using SerializedObjCSelectorTable =
634663
llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
635664

@@ -683,6 +712,8 @@ class APINotesReader::Implementation {
683712
llvm::SmallVectorImpl<uint64_t> &Scratch);
684713
bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
685714
llvm::SmallVectorImpl<uint64_t> &Scratch);
715+
bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
716+
llvm::SmallVectorImpl<uint64_t> &Scratch);
686717
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
687718
llvm::SmallVectorImpl<uint64_t> &Scratch);
688719
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
@@ -1140,6 +1171,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock(
11401171
return false;
11411172
}
11421173

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

1801+
case CXX_METHOD_BLOCK_ID:
1802+
if (!HasValidControlBlock ||
1803+
Implementation->readCXXMethodBlock(Cursor, Scratch)) {
1804+
Failed = true;
1805+
return;
1806+
}
1807+
break;
1808+
16951809
case OBJC_SELECTOR_BLOCK_ID:
16961810
if (!HasValidControlBlock ||
16971811
Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
@@ -1911,6 +2025,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
19112025
return {Implementation->SwiftVersion, *Known};
19122026
}
19132027

2028+
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
2029+
-> VersionedInfo<CXXMethodInfo> {
2030+
if (!Implementation->CXXMethodTable)
2031+
return std::nullopt;
2032+
2033+
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
2034+
if (!NameID)
2035+
return std::nullopt;
2036+
2037+
auto Known = Implementation->CXXMethodTable->find(
2038+
SingleDeclTableKey(CtxID.Value, *NameID));
2039+
if (Known == Implementation->CXXMethodTable->end())
2040+
return std::nullopt;
2041+
2042+
return {Implementation->SwiftVersion, *Known};
2043+
}
2044+
19142045
auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
19152046
std::optional<Context> Ctx)
19162047
-> VersionedInfo<GlobalVariableInfo> {
@@ -1965,6 +2096,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
19652096
return {Implementation->SwiftVersion, *Known};
19662097
}
19672098

2099+
auto APINotesReader::lookupTagID(llvm::StringRef Name,
2100+
std::optional<Context> ParentCtx)
2101+
-> std::optional<ContextID> {
2102+
if (!Implementation->ContextIDTable)
2103+
return std::nullopt;
2104+
2105+
std::optional<IdentifierID> TagID = Implementation->getIdentifier(Name);
2106+
if (!TagID)
2107+
return std::nullopt;
2108+
2109+
auto KnownID = Implementation->ContextIDTable->find(
2110+
ContextTableKey(ParentCtx, ContextKind::Tag, *TagID));
2111+
if (KnownID == Implementation->ContextIDTable->end())
2112+
return std::nullopt;
2113+
2114+
return ContextID(*KnownID);
2115+
}
2116+
19682117
auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional<Context> Ctx)
19692118
-> VersionedInfo<TagInfo> {
19702119
if (!Implementation->TagTable)

0 commit comments

Comments
 (0)