Skip to content

Commit b816977

Browse files
authored
[APINotes] Support fields of C/C++ structs
This allows annotating fields of C/C++ structs using API Notes. Previously API Notes supported Objective-C properties, but not fields. rdar://131548377
1 parent 907c7eb commit b816977

File tree

12 files changed

+381
-13
lines changed

12 files changed

+381
-13
lines changed

clang/include/clang/APINotes/APINotesReader.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ class APINotesReader {
141141
ObjCSelectorRef Selector,
142142
bool IsInstanceMethod);
143143

144+
/// Look for information regarding the given field of a C struct.
145+
///
146+
/// \param Name The name of the field.
147+
///
148+
/// \returns information about the field, if known.
149+
VersionedInfo<FieldInfo> lookupField(ContextID CtxID, llvm::StringRef Name);
150+
144151
/// Look for information regarding the given C++ method in the given C++ tag
145152
/// context.
146153
///

clang/include/clang/APINotes/APINotesWriter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ class APINotesWriter {
8686
void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
8787
const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion);
8888

89+
/// Add information about a specific C record field.
90+
///
91+
/// \param CtxID The context in which this field resides, i.e. a C/C++ tag.
92+
/// \param Name The name of the field.
93+
/// \param Info Information about this field.
94+
void addField(ContextID CtxID, llvm::StringRef Name, const FieldInfo &Info,
95+
llvm::VersionTuple SwiftVersion);
96+
8997
/// Add information about a global variable.
9098
///
9199
/// \param Name The name of this global variable.

clang/include/clang/APINotes/Types.h

Lines changed: 6 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/C++ record field.
660+
class FieldInfo : public VariableInfo {
661+
public:
662+
FieldInfo() {}
663+
};
664+
659665
/// Describes API notes data for a C++ method.
660666
class CXXMethodInfo : public FunctionInfo {
661667
public:

clang/lib/APINotes/APINotesFormat.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
2424
/// API notes file minor version number.
2525
///
2626
/// When the format changes IN ANY WAY, this number should be incremented.
27-
const uint16_t VERSION_MINOR = 29; // SwiftConformsTo
27+
const uint16_t VERSION_MINOR = 30; // fields
2828

2929
const uint8_t kSwiftCopyable = 1;
3030
const uint8_t kSwiftNonCopyable = 2;
@@ -72,6 +72,10 @@ enum BlockID {
7272
/// used in other tables.
7373
OBJC_SELECTOR_BLOCK_ID,
7474

75+
/// The fields data block, which maps names fields of C records to
76+
/// information about the field.
77+
FIELD_BLOCK_ID,
78+
7579
/// The global variables data block, which maps global variable names to
7680
/// information about the global variable.
7781
GLOBAL_VARIABLE_BLOCK_ID,
@@ -199,6 +203,20 @@ using CXXMethodDataLayout =
199203
>;
200204
} // namespace cxx_method_block
201205

206+
namespace field_block {
207+
enum {
208+
FIELD_DATA = 1,
209+
};
210+
211+
using FieldDataLayout =
212+
llvm::BCRecordLayout<FIELD_DATA, // record ID
213+
llvm::BCVBR<16>, // table offset within the blob (see
214+
// below)
215+
llvm::BCBlob // map from C (context id, name)
216+
// tuples to C field information
217+
>;
218+
} // namespace field_block
219+
202220
namespace objc_selector_block {
203221
enum {
204222
OBJC_SELECTOR_DATA = 1,

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,28 @@ class ObjCPropertyTableInfo
299299
}
300300
};
301301

302+
/// Used to deserialize the on-disk C record field table.
303+
class FieldTableInfo
304+
: public VersionedTableInfo<FieldTableInfo, SingleDeclTableKey, FieldInfo> {
305+
public:
306+
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
307+
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
308+
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
309+
return {CtxID, NameID};
310+
}
311+
312+
hash_value_type ComputeHash(internal_key_type Key) {
313+
return static_cast<size_t>(Key.hashValue());
314+
}
315+
316+
static FieldInfo readUnversioned(internal_key_type Key,
317+
const uint8_t *&Data) {
318+
FieldInfo Info;
319+
ReadVariableInfo(Data, Info);
320+
return Info;
321+
}
322+
};
323+
302324
/// Read serialized ParamInfo.
303325
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
304326
ReadVariableInfo(Data, Info);
@@ -653,6 +675,12 @@ class APINotesReader::Implementation {
653675
/// The Objective-C property table.
654676
std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable;
655677

678+
using SerializedFieldTable =
679+
llvm::OnDiskIterableChainedHashTable<FieldTableInfo>;
680+
681+
/// The C record field table.
682+
std::unique_ptr<SerializedFieldTable> FieldTable;
683+
656684
using SerializedObjCMethodTable =
657685
llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;
658686

@@ -720,6 +748,8 @@ class APINotesReader::Implementation {
720748
llvm::SmallVectorImpl<uint64_t> &Scratch);
721749
bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
722750
llvm::SmallVectorImpl<uint64_t> &Scratch);
751+
bool readFieldBlock(llvm::BitstreamCursor &Cursor,
752+
llvm::SmallVectorImpl<uint64_t> &Scratch);
723753
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
724754
llvm::SmallVectorImpl<uint64_t> &Scratch);
725755
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
@@ -1252,6 +1282,81 @@ bool APINotesReader::Implementation::readCXXMethodBlock(
12521282
return false;
12531283
}
12541284

1285+
bool APINotesReader::Implementation::readFieldBlock(
1286+
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
1287+
if (Cursor.EnterSubBlock(FIELD_BLOCK_ID))
1288+
return true;
1289+
1290+
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
1291+
if (!MaybeNext) {
1292+
// FIXME this drops the error on the floor.
1293+
consumeError(MaybeNext.takeError());
1294+
return false;
1295+
}
1296+
llvm::BitstreamEntry Next = MaybeNext.get();
1297+
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
1298+
if (Next.Kind == llvm::BitstreamEntry::Error)
1299+
return true;
1300+
1301+
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
1302+
// Unknown sub-block, possibly for use by a future version of the
1303+
// API notes format.
1304+
if (Cursor.SkipBlock())
1305+
return true;
1306+
1307+
MaybeNext = Cursor.advance();
1308+
if (!MaybeNext) {
1309+
// FIXME this drops the error on the floor.
1310+
consumeError(MaybeNext.takeError());
1311+
return false;
1312+
}
1313+
Next = MaybeNext.get();
1314+
continue;
1315+
}
1316+
1317+
Scratch.clear();
1318+
llvm::StringRef BlobData;
1319+
llvm::Expected<unsigned> MaybeKind =
1320+
Cursor.readRecord(Next.ID, Scratch, &BlobData);
1321+
if (!MaybeKind) {
1322+
// FIXME this drops the error on the floor.
1323+
consumeError(MaybeKind.takeError());
1324+
return false;
1325+
}
1326+
unsigned Kind = MaybeKind.get();
1327+
switch (Kind) {
1328+
case field_block::FIELD_DATA: {
1329+
// Already saw field table.
1330+
if (FieldTable)
1331+
return true;
1332+
1333+
uint32_t tableOffset;
1334+
field_block::FieldDataLayout::readRecord(Scratch, tableOffset);
1335+
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
1336+
1337+
FieldTable.reset(SerializedFieldTable::Create(
1338+
base + tableOffset, base + sizeof(uint32_t), base));
1339+
break;
1340+
}
1341+
1342+
default:
1343+
// Unknown record, possibly for use by a future version of the
1344+
// module format.
1345+
break;
1346+
}
1347+
1348+
MaybeNext = Cursor.advance();
1349+
if (!MaybeNext) {
1350+
// FIXME this drops the error on the floor.
1351+
consumeError(MaybeNext.takeError());
1352+
return false;
1353+
}
1354+
Next = MaybeNext.get();
1355+
}
1356+
1357+
return false;
1358+
}
1359+
12551360
bool APINotesReader::Implementation::readObjCSelectorBlock(
12561361
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
12571362
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
@@ -1812,6 +1917,14 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer,
18121917
}
18131918
break;
18141919

1920+
case FIELD_BLOCK_ID:
1921+
if (!HasValidControlBlock ||
1922+
Implementation->readFieldBlock(Cursor, Scratch)) {
1923+
Failed = true;
1924+
return;
1925+
}
1926+
break;
1927+
18151928
case OBJC_SELECTOR_BLOCK_ID:
18161929
if (!HasValidControlBlock ||
18171930
Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
@@ -2031,6 +2144,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
20312144
return {Implementation->SwiftVersion, *Known};
20322145
}
20332146

2147+
auto APINotesReader::lookupField(ContextID CtxID, llvm::StringRef Name)
2148+
-> VersionedInfo<FieldInfo> {
2149+
if (!Implementation->FieldTable)
2150+
return std::nullopt;
2151+
2152+
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
2153+
if (!NameID)
2154+
return std::nullopt;
2155+
2156+
auto Known = Implementation->FieldTable->find(
2157+
SingleDeclTableKey(CtxID.Value, *NameID));
2158+
if (Known == Implementation->FieldTable->end())
2159+
return std::nullopt;
2160+
2161+
return {Implementation->SwiftVersion, *Known};
2162+
}
2163+
20342164
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
20352165
-> VersionedInfo<CXXMethodInfo> {
20362166
if (!Implementation->CXXMethodTable)

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ class APINotesWriter::Implementation {
6262
llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, 1>>
6363
ObjCProperties;
6464

65+
/// Information about C record fields.
66+
///
67+
/// Indexed by the context ID and name ID.
68+
llvm::DenseMap<SingleDeclTableKey,
69+
llvm::SmallVector<std::pair<VersionTuple, FieldInfo>, 1>>
70+
Fields;
71+
6572
/// Information about Objective-C methods.
6673
///
6774
/// Indexed by the context ID, selector ID, and Boolean (stored as a char)
@@ -158,6 +165,7 @@ class APINotesWriter::Implementation {
158165
void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream);
159166
void writeObjCMethodBlock(llvm::BitstreamWriter &Stream);
160167
void writeCXXMethodBlock(llvm::BitstreamWriter &Stream);
168+
void writeFieldBlock(llvm::BitstreamWriter &Stream);
161169
void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream);
162170
void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream);
163171
void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream);
@@ -190,6 +198,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) {
190198
writeObjCPropertyBlock(Stream);
191199
writeObjCMethodBlock(Stream);
192200
writeCXXMethodBlock(Stream);
201+
writeFieldBlock(Stream);
193202
writeObjCSelectorBlock(Stream);
194203
writeGlobalVariableBlock(Stream);
195204
writeGlobalFunctionBlock(Stream);
@@ -858,6 +867,62 @@ void APINotesWriter::Implementation::writeCXXMethodBlock(
858867
}
859868
}
860869

870+
namespace {
871+
/// Used to serialize the on-disk C field table.
872+
class FieldTableInfo
873+
: public VersionedTableInfo<FieldTableInfo, SingleDeclTableKey, FieldInfo> {
874+
public:
875+
unsigned getKeyLength(key_type_ref) {
876+
return sizeof(uint32_t) + sizeof(uint32_t);
877+
}
878+
879+
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
880+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
881+
writer.write<uint32_t>(Key.parentContextID);
882+
writer.write<uint32_t>(Key.nameID);
883+
}
884+
885+
hash_value_type ComputeHash(key_type_ref key) {
886+
return static_cast<size_t>(key.hashValue());
887+
}
888+
889+
unsigned getUnversionedInfoSize(const FieldInfo &FI) {
890+
return getVariableInfoSize(FI);
891+
}
892+
893+
void emitUnversionedInfo(raw_ostream &OS, const FieldInfo &FI) {
894+
emitVariableInfo(OS, FI);
895+
}
896+
};
897+
} // namespace
898+
899+
void APINotesWriter::Implementation::writeFieldBlock(
900+
llvm::BitstreamWriter &Stream) {
901+
llvm::BCBlockRAII Scope(Stream, FIELD_BLOCK_ID, 3);
902+
903+
if (Fields.empty())
904+
return;
905+
906+
{
907+
llvm::SmallString<4096> HashTableBlob;
908+
uint32_t Offset;
909+
{
910+
llvm::OnDiskChainedHashTableGenerator<FieldTableInfo> Generator;
911+
for (auto &FD : Fields)
912+
Generator.insert(FD.first, FD.second);
913+
914+
llvm::raw_svector_ostream BlobStream(HashTableBlob);
915+
// Make sure that no bucket is at offset 0
916+
llvm::support::endian::write<uint32_t>(BlobStream, 0,
917+
llvm::endianness::little);
918+
Offset = Generator.Emit(BlobStream);
919+
}
920+
921+
field_block::FieldDataLayout FieldData(Stream);
922+
FieldData.emit(Scratch, Offset, HashTableBlob);
923+
}
924+
}
925+
861926
namespace {
862927
/// Used to serialize the on-disk Objective-C selector table.
863928
class ObjCSelectorTableInfo {
@@ -1423,6 +1488,14 @@ void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
14231488
Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
14241489
}
14251490

1491+
void APINotesWriter::addField(ContextID CtxID, llvm::StringRef Name,
1492+
const FieldInfo &Info,
1493+
VersionTuple SwiftVersion) {
1494+
IdentifierID NameID = Implementation->getIdentifier(Name);
1495+
SingleDeclTableKey Key(CtxID.Value, NameID);
1496+
Implementation->Fields[Key].push_back({SwiftVersion, Info});
1497+
}
1498+
14261499
void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
14271500
llvm::StringRef Name,
14281501
const GlobalVariableInfo &Info,

0 commit comments

Comments
 (0)