Skip to content

Commit 0293db5

Browse files
committed
Support @objc(CustomName) on extensions
This now specifies a category name that’s used in TBDGen, IRGen, and PrintAsClang. There are also now category name conflict diagnostics; these subsume some @implementation diagnostics. (It turns out there was already a check for @objc(CustomName) to make sure it wasn’t a selector!)
1 parent 07b2b9a commit 0293db5

18 files changed

+363
-32
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5064,6 +5064,10 @@ class ClassDecl final : public NominalTypeDecl {
50645064
llvm::TinyPtrVector<Decl *>
50655065
getImportedObjCCategory(Identifier name) const;
50665066

5067+
/// Record the presence of an extension that may become an Objective-C
5068+
/// category.
5069+
void recordObjCCategory(ExtensionDecl *ext);
5070+
50675071
// Implement isa/cast/dyncast/etc.
50685072
static bool classof(const Decl *D) {
50695073
return D->getKind() == DeclKind::Class;

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,10 @@ WARNING(api_pattern_attr_ignored, none,
136136
(StringRef, StringRef))
137137

138138
ERROR(objc_implementation_two_impls, none,
139-
"duplicate implementation of Objective-C %select{|category %0 on }0"
140-
"%kind1",
141-
(Identifier, Decl *))
142-
139+
"duplicate implementation of imported %kind0",
140+
(Decl *))
143141
NOTE(previous_objc_implementation, none,
144-
"previously implemented by extension here", ())
142+
"previously implemented here", ())
145143

146144
NOTE(macro_not_imported_unsupported_operator, none, "operator not supported in macro arithmetic", ())
147145
NOTE(macro_not_imported_unsupported_named_operator, none, "operator '%0' not supported in macro arithmetic", (StringRef))

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6450,6 +6450,12 @@ ERROR(objc_redecl_same,none,
64506450
"previous declaration with the same Objective-C selector",
64516451
(unsigned, DeclName, unsigned, DeclName, ObjCSelector))
64526452

6453+
ERROR(objc_redecl_category_name,none,
6454+
"%select{|imported }0extension with Objective-C category name %2 "
6455+
"conflicts with previous %select{|imported }1extension with the same "
6456+
"category name",
6457+
(bool, bool, Identifier))
6458+
64536459
ERROR(objc_override_other,none,
64546460
OBJC_DIAG_SELECT " with Objective-C selector %4 conflicts with "
64556461
OBJC_DIAG_SELECT_2 " from superclass %5 with the same Objective-C "

include/swift/AST/SourceFile.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,16 @@ class SourceFile final : public FileUnit {
359359
/// List of Objective-C member conflicts we have found during type checking.
360360
llvm::SetVector<ObjCMethodConflict> ObjCMethodConflicts;
361361

362+
struct ObjCCategoryConflict {
363+
NominalTypeDecl *typeDecl;
364+
Identifier name;
365+
366+
ObjCCategoryConflict(NominalTypeDecl *typeDecl, Identifier name)
367+
: typeDecl(typeDecl), name(name)
368+
{}
369+
};
370+
llvm::SetVector<ObjCCategoryConflict> ObjCCategoryConflicts;
371+
362372
/// List of attributes added by access notes, used to emit remarks for valid
363373
/// ones.
364374
llvm::DenseMap<ValueDecl *, std::vector<DeclAttribute *>>
@@ -876,6 +886,25 @@ struct DenseMapInfo<swift::SourceFile::ObjCMethodConflict> {
876886
}
877887
};
878888

889+
template<>
890+
struct DenseMapInfo<swift::SourceFile::ObjCCategoryConflict> {
891+
using ObjCCategoryConflict = swift::SourceFile::ObjCCategoryConflict;
892+
893+
static inline ObjCCategoryConflict getEmptyKey() {
894+
return { nullptr, DenseMapInfo<swift::Identifier>::getEmptyKey() };
895+
}
896+
static inline ObjCCategoryConflict getTombstoneKey() {
897+
return { nullptr, DenseMapInfo<swift::Identifier>::getTombstoneKey() };
898+
}
899+
static inline unsigned getHashValue(ObjCCategoryConflict a) {
900+
return hash_combine(hash_value(a.typeDecl),
901+
DenseMapInfo<swift::Identifier>::getHashValue(a.name));
902+
}
903+
static bool isEqual(ObjCCategoryConflict a, ObjCCategoryConflict b) {
904+
return a.typeDecl == b.typeDecl && a.name == b.name;
905+
}
906+
};
907+
879908
}
880909

881910

lib/AST/Decl.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3718,12 +3718,9 @@ Identifier ExtensionDecl::getObjCCategoryName() const {
37183718
}
37193719

37203720
// Fall back to @_objcImplementation attribute.
3721-
if (auto attr =
3721+
if (auto implAttr =
37223722
getAttrs().getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true)) {
3723-
if (!attr->isCategoryNameInvalid())
3724-
return attr->CategoryName;
3725-
3726-
return Identifier();
3723+
return implAttr->CategoryName;
37273724
}
37283725

37293726
// Not a category, evidently.

lib/AST/NameLookup.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,8 @@ void NominalTypeDecl::addedExtension(ExtensionDecl *ext) {
15271527
assert(table);
15281528

15291529
table->addExtension(ext);
1530+
if (auto CD = dyn_cast<ClassDecl>(this))
1531+
CD->recordObjCCategory(ext);
15301532
}
15311533

15321534
void NominalTypeDecl::addedMember(Decl *member) {
@@ -2015,8 +2017,11 @@ void NominalTypeDecl::prepareLookupTable() {
20152017
}
20162018

20172019
// Note: this calls prepareExtensions()
2020+
auto CD = dyn_cast<ClassDecl>(this);
20182021
for (auto e : getExtensions()) {
20192022
table->addExtension(e);
2023+
if (CD)
2024+
CD->recordObjCCategory(e);
20202025
}
20212026

20222027
// Any extensions added after this point will add their members to the
@@ -2262,6 +2267,27 @@ void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
22622267
vec.push_back(method);
22632268
}
22642269

2270+
void ClassDecl::recordObjCCategory(ExtensionDecl *ext) {
2271+
Identifier name = ext->getObjCCategoryName();
2272+
if (name.empty())
2273+
return;
2274+
2275+
// Use a linear scan on the assumption that there aren't very many extensions.
2276+
for (auto *other : getExtensions()) {
2277+
if (other != ext && other->getObjCCategoryName() == name
2278+
// @implementations don't conflict with their interfaces
2279+
&& !((ext->hasClangNode() && other->isObjCImplementation())
2280+
|| (ext->isObjCImplementation() && other->hasClangNode()))) {
2281+
2282+
// Collision.
2283+
if (auto *sf = ext->getParentSourceFile()) {
2284+
sf->ObjCCategoryConflicts.insert({ this, name });
2285+
}
2286+
break;
2287+
}
2288+
}
2289+
}
2290+
22652291
static bool missingExplicitImportForMemberDecl(const DeclContext *dc,
22662292
ValueDecl *decl) {
22672293
// Only require explicit imports for members when MemberImportVisibility is

lib/ClangImporter/ClangImporter.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5984,10 +5984,15 @@ constructResult(const llvm::TinyPtrVector<Decl *> &interfaces,
59845984
auto attr = extraImpl->getAttrs().getAttribute<ObjCImplementationAttr>();
59855985
attr->setCategoryNameInvalid();
59865986

5987-
diags.diagnose(attr->getLocation(), diag::objc_implementation_two_impls,
5988-
categoryName, diagnoseOn)
5989-
.fixItRemove(attr->getRangeWithAt());
5990-
diags.diagnose(impls.front(), diag::previous_objc_implementation);
5987+
// @objc @implementations for categories are diagnosed as category
5988+
// conflicts, so we're only concerned with main class bodies and
5989+
// non-category implementations here.
5990+
if (categoryName.empty() || !isa<ExtensionDecl>(impls.front())) {
5991+
diags.diagnose(attr->getLocation(), diag::objc_implementation_two_impls,
5992+
diagnoseOn)
5993+
.fixItRemove(attr->getRangeWithAt());
5994+
diags.diagnose(impls.front(), diag::previous_objc_implementation);
5995+
}
59915996
}
59925997
}
59935998

lib/IRGen/GenClass.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,8 @@ namespace {
11381138
return pair.second;
11391139
}
11401140

1141-
std::optional<StringRef> getObjCImplCategoryName() const {
1142-
if (!TheExtension || !TheExtension->isObjCImplementation())
1141+
std::optional<StringRef> getCustomCategoryName() const {
1142+
if (!TheExtension)
11431143
return std::nullopt;
11441144
assert(!TheExtension->hasClangNode());
11451145
auto ident = TheExtension->getObjCCategoryName();
@@ -1426,8 +1426,8 @@ namespace {
14261426
void buildCategoryName(SmallVectorImpl<char> &s) {
14271427
llvm::raw_svector_ostream os(s);
14281428

1429-
if (auto implementationCategoryName = getObjCImplCategoryName()) {
1430-
os << *implementationCategoryName;
1429+
if (auto customCategoryName = getCustomCategoryName()) {
1430+
os << *customCategoryName;
14311431
return;
14321432
}
14331433

@@ -2449,8 +2449,8 @@ namespace {
24492449
os << "@";
24502450
TheExtension->getLoc().print(os, IGM.Context.SourceMgr);
24512451
}
2452-
if (auto name = getObjCImplCategoryName()) {
2453-
os << " objc_impl_category_name=" << *name;
2452+
if (auto name = getCustomCategoryName()) {
2453+
os << " custom_category_name=" << *name;
24542454
}
24552455

24562456
if (HasNonTrivialConstructor)

lib/IRGen/TBDGen.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,10 @@ class APIGenRecorder final : public APIRecorder {
829829
void buildCategoryName(const ExtensionDecl *ext, const ClassDecl *cls,
830830
SmallVectorImpl<char> &s) {
831831
llvm::raw_svector_ostream os(s);
832+
if (!ext->getObjCCategoryName().empty()) {
833+
os << ext->getObjCCategoryName();
834+
return;
835+
}
832836
ModuleDecl *module = ext->getParentModule();
833837
os << module->getName();
834838
unsigned categoryCount = CategoryCounts[{cls, module}]++;

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,10 @@ class DeclAndTypePrinter::Implementation
403403
os << "\n";
404404
os << "@interface " << getNameForObjC(baseClass);
405405
maybePrintObjCGenericParameters(baseClass);
406-
os << " (SWIFT_EXTENSION(" << ED->getModuleContext()->getName() << "))";
406+
if (ED->getObjCCategoryName().empty())
407+
os << " (SWIFT_EXTENSION(" << ED->getModuleContext()->getName() << "))";
408+
else
409+
os << " (" << ED->getObjCCategoryName() << ")";
407410
printProtocols(ED->getLocalProtocols(ConformanceLookupKind::OnlyExplicit));
408411
os << "\n";
409412
printMembers(ED->getMembers());

0 commit comments

Comments
 (0)