Skip to content

Commit 07b9fe9

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 4ba2f6e commit 07b9fe9

21 files changed

+416
-32
lines changed

include/swift/AST/Decl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4199,6 +4199,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
41994199
/// Retrieve the set of extensions of this type.
42004200
ExtensionRange getExtensions();
42014201

4202+
/// Retrieve the extension most recently added to this type. Helpful to
4203+
/// determine if an extension has been added.
4204+
ExtensionDecl *getLastExtension() const {
4205+
return LastExtension;
4206+
}
4207+
42024208
/// Special-behaviour flags passed to lookupDirect()
42034209
enum class LookupDirectFlags {
42044210
/// Whether to include @_implements members.
@@ -5064,6 +5070,11 @@ class ClassDecl final : public NominalTypeDecl {
50645070
llvm::TinyPtrVector<Decl *>
50655071
getImportedObjCCategory(Identifier name) const;
50665072

5073+
/// Return a map of category names to extensions with that category name,
5074+
/// whether imported or otherwise.
5075+
llvm::DenseMap<Identifier, llvm::TinyPtrVector<ExtensionDecl *>>
5076+
getObjCCategoryNameMap();
5077+
50675078
// Implement isa/cast/dyncast/etc.
50685079
static bool classof(const Decl *D) {
50695080
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/NameLookupRequests.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,39 @@ class LookupIntrinsicRequest
970970
bool isCached() const { return true; }
971971
};
972972

973+
using ObjCCategoryNameMap =
974+
llvm::DenseMap<Identifier, llvm::TinyPtrVector<ExtensionDecl *>>;
975+
976+
/// Generate a map of all known extensions of the given class that have an
977+
/// explicit category name. This request does not force clang categories that
978+
/// haven't been imported already, but it will generate a new map if new
979+
/// categories have been imported since the cached value was generated.
980+
///
981+
/// \seeAlso ClassDecl::getObjCCategoryNameMap()
982+
class ObjCCategoryNameMapRequest
983+
: public SimpleRequest<ObjCCategoryNameMapRequest,
984+
ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *),
985+
RequestFlags::Cached> {
986+
public:
987+
using SimpleRequest::SimpleRequest;
988+
989+
// Convenience to automatically extract `lastExtension`.
990+
ObjCCategoryNameMapRequest(ClassDecl *classDecl)
991+
: ObjCCategoryNameMapRequest(classDecl, classDecl->getLastExtension())
992+
{}
993+
994+
private:
995+
friend SimpleRequest;
996+
997+
// Evaluation.
998+
ObjCCategoryNameMap evaluate(Evaluator &evaluator,
999+
ClassDecl *classDecl,
1000+
ExtensionDecl *lastExtension) const;
1001+
1002+
public:
1003+
bool isCached() const { return true; }
1004+
};
1005+
9731006
#define SWIFT_TYPEID_ZONE NameLookup
9741007
#define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def"
9751008
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/NameLookupTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,5 @@ SWIFT_REQUEST(NameLookup, ImplementsAttrProtocolRequest,
116116
ProtocolDecl *(const ImplementsAttr *, DeclContext *), Cached, NoLocationInfo)
117117
SWIFT_REQUEST(NameLookup, LookupIntrinsicRequest,
118118
FuncDecl *(ModuleDecl *, Identifier), Cached, NoLocationInfo)
119+
SWIFT_REQUEST(NameLookup, ObjCCategoryNameMapRequest,
120+
ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *), Cached, NoLocationInfo)

include/swift/AST/SourceFile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,10 @@ 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+
/// Categories (extensions with explicit @objc names) declared in this
363+
/// source file. They need to be checked for conflicts after type checking.
364+
llvm::TinyPtrVector<ExtensionDecl *> ObjCCategories;
365+
362366
/// List of attributes added by access notes, used to emit remarks for valid
363367
/// ones.
364368
llvm::DenseMap<ValueDecl *, std::vector<DeclAttribute *>>

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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,6 +2262,35 @@ void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
22622262
vec.push_back(method);
22632263
}
22642264

2265+
ObjCCategoryNameMap
2266+
ObjCCategoryNameMapRequest::evaluate(Evaluator &evaluator,
2267+
ClassDecl *classDecl,
2268+
ExtensionDecl *lastExtension) const {
2269+
// The purpose of the `lastExtension` parameter is to bake something into the
2270+
// request that will change when another extension is added. This ensures that
2271+
// if more extensions are added later, we don't return an outdated version of
2272+
// the extension map by accident.
2273+
2274+
ObjCCategoryNameMap results;
2275+
2276+
for (auto ext : classDecl->getExtensions()) {
2277+
auto catName = ext->getObjCCategoryName();
2278+
if (!catName.empty())
2279+
results[catName].push_back(ext);
2280+
2281+
if (ext == lastExtension)
2282+
break;
2283+
}
2284+
2285+
return results;
2286+
}
2287+
2288+
ObjCCategoryNameMap ClassDecl::getObjCCategoryNameMap() {
2289+
return evaluateOrDefault(getASTContext().evaluator,
2290+
ObjCCategoryNameMapRequest{this},
2291+
ObjCCategoryNameMap());
2292+
}
2293+
22652294
static bool missingExplicitImportForMemberDecl(const DeclContext *dc,
22662295
ValueDecl *decl) {
22672296
// 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)

0 commit comments

Comments
 (0)