Skip to content

Commit 61d27bf

Browse files
committed
[APINotes] Initial support for tags and typedefs within namespaces
This makes it possible to apply an attribute to a C++ struct declared within a C++ namespace via API Notes. The current implementation will fail with an assertion if a nested namespace has the same name as its parent namespace. This will be addressed in a follow-up commit. rdar://113403829
1 parent 1f55f19 commit 61d27bf

File tree

10 files changed

+150
-33
lines changed

10 files changed

+150
-33
lines changed

clang/include/clang/APINotes/APINotesReader.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,16 @@ class APINotesReader {
197197
/// \param name The name of the tag.
198198
///
199199
/// \returns information about the tag, if known.
200-
VersionedInfo<TagInfo> lookupTag(llvm::StringRef name);
200+
VersionedInfo<TagInfo> lookupTag(std::optional<Context> context,
201+
llvm::StringRef name);
201202

202203
/// Look for information regarding the given typedef.
203204
///
204205
/// \param name The name of the typedef.
205206
///
206207
/// \returns information about the typedef, if known.
207-
VersionedInfo<TypedefInfo> lookupTypedef(llvm::StringRef name);
208+
VersionedInfo<TypedefInfo> lookupTypedef(std::optional<Context> context,
209+
llvm::StringRef name);
208210

209211
/// Look for the context ID of the given C++ namespace.
210212
///

clang/include/clang/APINotes/APINotesWriter.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ class APINotesWriter {
105105
///
106106
/// \param name The name of this tag.
107107
/// \param info Information about this tag.
108-
void addTag(llvm::StringRef name, const TagInfo &info,
109-
llvm::VersionTuple swiftVersion);
108+
void addTag(std::optional<Context> context, llvm::StringRef name,
109+
const TagInfo &info, llvm::VersionTuple swiftVersion);
110110

111111
/// Add information about a typedef.
112112
///
113113
/// \param name The name of this typedef.
114114
/// \param info Information about this typedef.
115-
void addTypedef(llvm::StringRef name, const TypedefInfo &info,
116-
llvm::VersionTuple swiftVersion);
115+
void addTypedef(std::optional<Context> context, llvm::StringRef name,
116+
const TypedefInfo &info, llvm::VersionTuple swiftVersion);
117117

118118
/// Add module options
119119
void addModuleOptions(ModuleOptions opts);

clang/include/clang/APINotes/Types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,13 @@ enum class ContextKind : uint8_t {
750750
Namespace = 2
751751
};
752752

753+
struct Context {
754+
ContextID id;
755+
ContextKind kind;
756+
757+
Context(ContextID id, ContextKind kind) : id(id), kind(kind) {}
758+
};
759+
753760
/// A temporary reference to an Objective-C selector, suitable for
754761
/// referencing selector data on the stack.
755762
///

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,14 @@ namespace {
485485

486486
/// Used to deserialize the on-disk tag table.
487487
class TagTableInfo
488-
: public VersionedTableInfo<TagTableInfo, unsigned, TagInfo> {
488+
: public VersionedTableInfo<
489+
TagTableInfo, std::tuple<uint32_t, uint8_t, uint32_t>, TagInfo> {
489490
public:
490491
static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
492+
auto contextID = endian::readNext<uint32_t, little, unaligned>(data);
493+
auto contextKind = endian::readNext<uint8_t, little, unaligned>(data);
491494
auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
492-
return nameID;
495+
return {contextID, contextKind, nameID};
493496
}
494497

495498
static TagInfo readUnversioned(internal_key_type key,
@@ -513,11 +516,15 @@ namespace {
513516

514517
/// Used to deserialize the on-disk typedef table.
515518
class TypedefTableInfo
516-
: public VersionedTableInfo<TypedefTableInfo, unsigned, TypedefInfo> {
519+
: public VersionedTableInfo<TypedefTableInfo,
520+
std::tuple<uint32_t, uint8_t, uint32_t>,
521+
TypedefInfo> {
517522
public:
518523
static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
524+
auto contextID = endian::readNext<uint32_t, little, unaligned>(data);
525+
auto contextKind = endian::readNext<uint8_t, little, unaligned>(data);
519526
auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
520-
return nameID;
527+
return {contextID, contextKind, nameID};
521528
}
522529

523530
static TypedefInfo readUnversioned(internal_key_type key,
@@ -1970,22 +1977,30 @@ auto APINotesReader::lookupEnumConstant(StringRef name)
19701977
return { Impl.SwiftVersion, *known };
19711978
}
19721979

1973-
auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo<TagInfo> {
1980+
auto APINotesReader::lookupTag(std::optional<Context> context, StringRef name)
1981+
-> VersionedInfo<TagInfo> {
19741982
if (!Impl.TagTable)
19751983
return None;
19761984

19771985
Optional<IdentifierID> nameID = Impl.getIdentifier(name);
19781986
if (!nameID)
19791987
return None;
19801988

1981-
auto known = Impl.TagTable->find(*nameID);
1989+
std::tuple<uint32_t, uint8_t, uint32_t> key;
1990+
if (context)
1991+
key = {context->id.Value, (uint8_t)context->kind, *nameID};
1992+
else
1993+
key = {-1, (uint8_t)-1, *nameID};
1994+
1995+
auto known = Impl.TagTable->find(key);
19821996
if (known == Impl.TagTable->end())
19831997
return None;
19841998

19851999
return { Impl.SwiftVersion, *known };
19862000
}
19872001

1988-
auto APINotesReader::lookupTypedef(StringRef name)
2002+
auto APINotesReader::lookupTypedef(std::optional<Context> context,
2003+
StringRef name)
19892004
-> VersionedInfo<TypedefInfo> {
19902005
if (!Impl.TypedefTable)
19912006
return None;
@@ -1994,7 +2009,13 @@ auto APINotesReader::lookupTypedef(StringRef name)
19942009
if (!nameID)
19952010
return None;
19962011

1997-
auto known = Impl.TypedefTable->find(*nameID);
2012+
std::tuple<uint32_t, uint8_t, uint32_t> key;
2013+
if (context)
2014+
key = {context->id.Value, (uint8_t)context->kind, *nameID};
2015+
else
2016+
key = {-1, (uint8_t)-1, *nameID};
2017+
2018+
auto known = Impl.TypedefTable->find(key);
19982019
if (known == Impl.TypedefTable->end())
19992020
return None;
20002021

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,15 @@ class APINotesWriter::Implementation {
111111

112112
/// Information about tags.
113113
///
114-
/// Indexed by the identifier ID.
115-
llvm::DenseMap<unsigned,
114+
/// Indexed by the context ID, contextKind, identifier ID.
115+
llvm::DenseMap<std::tuple<uint32_t, uint8_t, uint32_t>,
116116
llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
117117
Tags;
118118

119119
/// Information about typedefs.
120120
///
121-
/// Indexed by the identifier ID.
122-
llvm::DenseMap<unsigned,
121+
/// Indexed by the context ID, contextKind, identifier ID.
122+
llvm::DenseMap<std::tuple<uint32_t, uint8_t, uint32_t>,
123123
llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
124124
Typedefs;
125125

@@ -1050,16 +1050,20 @@ void APINotesWriter::Implementation::writeEnumConstantBlock(
10501050
namespace {
10511051
template<typename Derived, typename UnversionedDataType>
10521052
class CommonTypeTableInfo
1053-
: public VersionedTableInfo<Derived, unsigned, UnversionedDataType> {
1053+
: public VersionedTableInfo<Derived,
1054+
std::tuple<uint32_t, uint8_t, uint32_t>,
1055+
UnversionedDataType> {
10541056
public:
10551057
using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
10561058

10571059
unsigned getKeyLength(key_type_ref) {
1058-
return sizeof(IdentifierID);
1060+
return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(IdentifierID);
10591061
}
10601062
void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
10611063
endian::Writer writer(out, little);
1062-
writer.write<IdentifierID>(key);
1064+
writer.write<uint32_t>(std::get<0>(key));
1065+
writer.write<uint8_t>(std::get<1>(key));
1066+
writer.write<IdentifierID>(std::get<2>(key));
10631067
}
10641068

10651069
unsigned getUnversionedInfoSize(const UnversionedDataType &info) {
@@ -1323,16 +1327,28 @@ void APINotesWriter::addEnumConstant(llvm::StringRef name,
13231327
Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info});
13241328
}
13251329

1326-
void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info,
1330+
void APINotesWriter::addTag(std::optional<Context> context,
1331+
llvm::StringRef name, const TagInfo &info,
13271332
VersionTuple swiftVersion) {
13281333
IdentifierID tagID = Impl.getIdentifier(name);
1329-
Impl.Tags[tagID].push_back({swiftVersion, info});
1334+
std::tuple<uint32_t, uint8_t, uint32_t> key;
1335+
if (context)
1336+
key = {context->id.Value, (uint8_t)context->kind, tagID};
1337+
else
1338+
key = {-1, (uint8_t)-1, tagID};
1339+
Impl.Tags[key].push_back({swiftVersion, info});
13301340
}
13311341

1332-
void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info,
1342+
void APINotesWriter::addTypedef(std::optional<Context> context,
1343+
llvm::StringRef name, const TypedefInfo &info,
13331344
VersionTuple swiftVersion) {
13341345
IdentifierID typedefID = Impl.getIdentifier(name);
1335-
Impl.Typedefs[typedefID].push_back({swiftVersion, info});
1346+
std::tuple<uint32_t, uint8_t, uint32_t> key;
1347+
if (context)
1348+
key = {context->id.Value, (uint8_t)context->kind, typedefID};
1349+
else
1350+
key = {-1, (uint8_t)-1, typedefID};
1351+
Impl.Typedefs[key].push_back({swiftVersion, info});
13361352
}
13371353

13381354
void APINotesWriter::addModuleOptions(ModuleOptions opts) {

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,23 @@ namespace {
997997
}
998998
}
999999

1000-
void convertTopLevelItems(const TopLevelItems &items,
1000+
void convertNamespaceContext(const Namespace &ns,
1001+
VersionTuple swiftVersion) {
1002+
// Write the namespace.
1003+
ObjCContextInfo cInfo;
1004+
1005+
if (convertCommon(ns, cInfo, ns.Name))
1006+
return;
1007+
1008+
ContextID clID = Writer->addObjCContext(ns.Name, ContextKind::Namespace,
1009+
cInfo, swiftVersion);
1010+
1011+
convertTopLevelItems(Context(clID, ContextKind::Namespace), ns.Items,
1012+
swiftVersion);
1013+
}
1014+
1015+
void convertTopLevelItems(std::optional<Context> context,
1016+
const TopLevelItems &items,
10011017
VersionTuple swiftVersion) {
10021018
// Write all classes.
10031019
llvm::StringSet<> knownClasses;
@@ -1023,6 +1039,18 @@ namespace {
10231039
convertContext(pr, ContextKind::ObjCProtocol, swiftVersion);
10241040
}
10251041

1042+
// Write all namespaces.
1043+
llvm::StringSet<> knownNamespaces;
1044+
for (const auto &ns : items.Namespaces) {
1045+
// Check for duplicate namespace definitions.
1046+
if (!knownNamespaces.insert(ns.Name).second) {
1047+
emitError("multiple definitions of namespace '" + ns.Name + "'");
1048+
continue;
1049+
}
1050+
1051+
convertNamespaceContext(ns, swiftVersion);
1052+
}
1053+
10261054
// Write all global variables.
10271055
llvm::StringSet<> knownGlobals;
10281056
for (const auto &global : items.Globals) {
@@ -1131,7 +1159,7 @@ namespace {
11311159
tagInfo.setFlagEnum(t.FlagEnum);
11321160
}
11331161

1134-
Writer->addTag(t.Name, tagInfo, swiftVersion);
1162+
Writer->addTag(context, t.Name, tagInfo, swiftVersion);
11351163
}
11361164

11371165
// Write all typedefs.
@@ -1148,7 +1176,7 @@ namespace {
11481176
continue;
11491177
typedefInfo.SwiftWrapper = t.SwiftType;
11501178

1151-
Writer->addTypedef(t.Name, typedefInfo, swiftVersion);
1179+
Writer->addTypedef(context, t.Name, typedefInfo, swiftVersion);
11521180
}
11531181
}
11541182

@@ -1159,7 +1187,8 @@ namespace {
11591187
Writer = &writer;
11601188

11611189
// Write the top-level items.
1162-
convertTopLevelItems(TheModule.TopLevel, VersionTuple());
1190+
convertTopLevelItems(/* context */ std::nullopt, TheModule.TopLevel,
1191+
VersionTuple());
11631192

11641193
if (TheModule.SwiftInferImportAsMember) {
11651194
ModuleOptions opts;
@@ -1169,7 +1198,8 @@ namespace {
11691198

11701199
// Convert the versioned information.
11711200
for (const auto &versioned : TheModule.SwiftVersions) {
1172-
convertTopLevelItems(versioned.Items, versioned.Version);
1201+
convertTopLevelItems(/* context */ std::nullopt, versioned.Items,
1202+
versioned.Version);
11731203
}
11741204

11751205
if (!ErrorOccured)

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -812,8 +812,19 @@ void Sema::ProcessAPINotes(Decl *D) {
812812

813813
// Globals.
814814
if (D->getDeclContext()->isFileContext() ||
815+
D->getDeclContext()->isNamespace() ||
815816
D->getDeclContext()->isExternCContext() ||
816817
D->getDeclContext()->isExternCXXContext()) {
818+
std::optional<api_notes::Context> APINotesContext;
819+
if (auto NamespaceContext = dyn_cast<NamespaceDecl>(D->getDeclContext())) {
820+
for (auto Reader :
821+
APINotes.findAPINotes(NamespaceContext->getLocation())) {
822+
if (auto id = Reader->lookupNamespaceID(NamespaceContext->getName())) {
823+
APINotesContext = {*id, api_notes::ContextKind::Namespace};
824+
}
825+
}
826+
}
827+
817828
// Global variables.
818829
if (auto VD = dyn_cast<VarDecl>(D)) {
819830
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
@@ -883,7 +894,7 @@ void Sema::ProcessAPINotes(Decl *D) {
883894
}
884895

885896
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
886-
auto Info = Reader->lookupTag(LookupName);
897+
auto Info = Reader->lookupTag(APINotesContext, LookupName);
887898
ProcessVersionedAPINotes(*this, Tag, Info);
888899
}
889900

@@ -893,7 +904,7 @@ void Sema::ProcessAPINotes(Decl *D) {
893904
// Typedefs
894905
if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
895906
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
896-
auto Info = Reader->lookupTypedef(Typedef->getName());
907+
auto Info = Reader->lookupTypedef(APINotesContext, Typedef->getName());
897908
ProcessVersionedAPINotes(*this, Typedef, Info);
898909
}
899910

clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.apinotes

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,16 @@ Enumerators:
1212
SwiftName: red
1313
Tags:
1414
- Name: NSSomeEnumOptions
15-
SwiftName: SomeEnum.Options
15+
SwiftName: SomeEnum.Options
16+
- Name: char_box
17+
SwiftName: InWrongContext
18+
Namespaces:
19+
- Name: Namespace1
20+
Tags:
21+
- Name: char_box
22+
SwiftName: CharBox
23+
Namespaces:
24+
- Name: Nested1
25+
Tags:
26+
- Name: char_box
27+
SwiftName: NestedCharBox

clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@ typedef NS_OPTIONS(NSUInteger, NSSomeEnumOptions) {
2121
NSSomeEnumWithGreen,
2222
NSSomeEnumWithBlue,
2323
};
24+
25+
namespace Namespace1 {
26+
struct char_box { char c; };
27+
28+
namespace Nested1 {
29+
struct char_box { char c; };
30+
} // namespace Nested1
31+
} // namespace Namespace1

clang/test/APINotes/objcxx-swift-name.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -x objective-c++
33
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter SomeClass -x objective-c++ | FileCheck %s
44
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter "(anonymous)" -x objective-c++ | FileCheck -check-prefix=CHECK-ANONYMOUS-ENUM %s
5+
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-NAMESPACE %s
6+
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -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
57

68
#import <CXXInteropKit/CXXInteropKit.h>
79

@@ -21,3 +23,11 @@
2123
// CHECK-ANONYMOUS-ENUM: Dumping (anonymous):
2224
// CHECK-ANONYMOUS-ENUM-NEXT: EnumDecl {{.+}} imported in CXXInteropKit <undeserialized declarations> 'NSSomeEnumOptions':'unsigned long'
2325
// CHECK-ANONYMOUS-ENUM-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "SomeEnum.Options"
26+
27+
// CHECK-STRUCT-IN-NAMESPACE: Dumping Namespace1::char_box:
28+
// CHECK-STRUCT-IN-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in CXXInteropKit <undeserialized declarations> struct char_box
29+
// CHECK-STRUCT-IN-NAMESPACE: SwiftNameAttr {{.+}} <<invalid sloc>> "CharBox"
30+
31+
// CHECK-STRUCT-IN-NESTED-NAMESPACE: Dumping Namespace1::Nested1::char_box:
32+
// CHECK-STRUCT-IN-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in CXXInteropKit <undeserialized declarations> struct char_box
33+
// CHECK-STRUCT-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <<invalid sloc>> "NestedCharBox"

0 commit comments

Comments
 (0)